Kerndatensatz Senologie
0.9.0 - ci-build

Kerndatensatz Senologie - Local Development build (v0.9.0) built by the FHIR (HL7® FHIR® Standard) Build Tools. See the Directory of published versions

StructureMap: SenologieToIRegMeldung

Official URL: https://www.senologie.org/fhir/StructureMap/SenologieToIRegMeldung Version: 0.9.0
Draft as of 2026-05-04 Computable Name: SenologieToIRegMeldung

title: Senologie FHIR Bundle to IRegG Brustimplantat-Meldung (Orchestrator) status: draft

map "https://www.senologie.org/fhir/StructureMap/SenologieToIRegMeldung" = "SenologieToIRegMeldung"

// title: Senologie FHIR Bundle to IRegG Brustimplantat-Meldung (Orchestrator)
// status: draft

uses "http://hl7.org/fhir/StructureDefinition/Bundle" alias Bundle as source
uses "http://hl7.org/fhir/StructureDefinition/Patient" alias Patient as source
uses "http://hl7.org/fhir/StructureDefinition/Encounter" alias Encounter as source
uses "http://hl7.org/fhir/StructureDefinition/Procedure" alias Procedure as source
uses "http://hl7.org/fhir/StructureDefinition/Device" alias Device as source
uses "http://hl7.org/fhir/StructureDefinition/Condition" alias Condition as source
uses "http://hl7.org/fhir/StructureDefinition/Observation" alias Observation as source
uses "http://hl7.org/fhir/StructureDefinition/Organization" alias Organization as source
uses "https://www.senologie.org/fhir/StructureDefinition/ireg-brustimplantat-meldung" alias IRegMeldung as target

imports "https://www.senologie.org/fhir/StructureMap/SenologieToIRegPatient"
imports "https://www.senologie.org/fhir/StructureMap/SenologieToIRegOperation"
imports "https://www.senologie.org/fhir/StructureMap/SenologieToIRegEntlassung"

// Known limitation: Sub-groups (MapMeldungskopf, MapFall) use
// `target tgt : BackboneElement` because FML has no syntax to declare the
// specific Logical Model sub-path when a parent group passes a BackboneElement
// slice. The IG Publisher resolves target paths against BackboneElement,
// producing ~20 SM_TARGET_PATH / SM_SOURCE_PATH errors. The element names are
// correct and match the IRegG Brustimplantat Logical Model. These errors are
// cosmetic.
// ============================================================================
// Master Orchestrator: Bundle -> IRegG Brustimplantat-Meldung
// Empfaengt ein FHIR Bundle mit Senologie-Ressourcen (Patient, Procedure,
// Device, Encounter, Condition, Observations) und erzeugt eine vollstaendige
// IRegG-Meldung fuer Brustimplantate.
// Im Gegensatz zur oBDS-Transformation (mehrere Meldungen pro Bundle)
// erzeugt die IRegG-Meldung eine einzelne GEMeldung pro Behandlungsfall.
// ============================================================================
group SenologieToIRegMeldung(source src : Bundle, target tgt : IRegMeldung) {
  // --- Meldungskopf (MEL_*) ---
  src -> tgt.meldung as mel then MapMeldungskopf(src, mel) "CallMapMeldungskopf";
  // --- Fall (FAL_*) ---
  src.entry as entry where resource.is(Encounter) then {
    entry.resource as encounter -> tgt.fall as fall then MapFall(encounter, fall) "CallMapFall";
    // Bundle lookups moved from MapFall
    // Ausloeser: OPS-Kodes
    src.entry as auslEntry where resource.is(Procedure) then {
      auslEntry.resource as procedure then {
        procedure.code as code then {
          code.coding as c where system = 'http://fhir.de/CodeSystem/bfarm/ops' -> fall.ausloeser as ausl then {
            c.code as cd -> ausl.prozedurenSchluessel = cd "SetAusloeserOPS";
          } "MapAusloeserCoding";
        } "MapAusloeserCode";
      } "AusloeserProcContext";
    } "EntryAusloeser";
    // Patientenaufnahme
    src.entry as patEntry where resource.is(Patient) then {
      patEntry.resource as patient -> fall.patientenaufnahme as pat then MapPatientenaufnahme(patient, pat) "CallMapPatientenaufnahme";
      // PAT_Groesse (moved from MapPatientenaufnahme)
      src.entry as ghEntry where resource.is(Observation) and resource.code.coding.exists(code = '8302-2') then {
        ghEntry.resource as obs then {
          obs.value as val then {
            val.value as v -> pat.groesse = truncate(v) "SetGroesse";
          } "MapGroesseValue";
        } "GroesseObsContext";
      } "EntryGroesse";
      // PAT_Gewicht (moved from MapPatientenaufnahme)
      src.entry as gwEntry where resource.is(Observation) and resource.code.coding.exists(code = '29463-7') then {
        gwEntry.resource as obs then {
          obs.value as val then {
            val.value as v -> pat.gewicht = truncate(v) "SetGewicht";
          } "MapGewichtValue";
        } "GewichtObsContext";
      } "EntryGewicht";
    } "EntryPatient";
    // Operationen
    src.entry as opEntry where resource.is(Procedure) and resource.meta.profile.exists($this.contains('senologie-operation') or $this.contains('senologie-brustop')) then {
      opEntry.resource as procedure -> fall.operation as op then MapOperation(procedure, op) "CallMapOperation";
      // Artikelidentifikation via focalDevice
      opEntry.resource as procedure then {
        procedure.focalDevice as fd then {
          fd.manipulated as ref then {
            src.entry as devEntry where resource.is(Device) and (fullUrl = (%ref.reference)) then {
              devEntry.resource as device -> op.artikelidentifikation as art then MapArtikelidentifikation(device, art) "CallMapArtikel";
            } "ResolveDeviceRef";
          } "MapDeviceRef";
        } "MapFocalDevice";
        procedure where focalDevice.exists().not() then {
          src.entry as devEntry where resource.is(Device) and resource.meta.profile.exists($this.contains('senologie-implantat')) then {
            devEntry.resource as device -> op.artikelidentifikation as art then MapArtikelidentifikation(device, art) "CallMapArtikelFallback";
          } "EntryDeviceFallback";
        } "FallbackDevice";
      } "DeviceLookupContext";
      // Zubehoer
      src.entry as zubEntry where resource.is(Device) and resource.meta.profile.exists($this.contains('ireg-zubehoer') or $this.contains('senologie-zubehoer')) then {
        zubEntry.resource as device -> op.zubehoer as zub then MapZubehoer(device, zub) "CallMapZubehoer";
      } "EntryZubehoer";
    } "EntryOperation";
    // Entlassung
    encounter -> fall.entlassung as entl then MapEntlassung(encounter, entl) "CallMapEntlassung";
    // DBI Entlassungsdiagnosen
    src.entry as dbiEntry where resource.is(Condition) then {
      dbiEntry.resource as condition then {
        condition.code as code then {
          code.coding as c where system = 'http://fhir.de/CodeSystem/bfarm/icd-10-gm' -> fall.entlassung as entl then {
            c -> entl.diagnoseBrustimplantat as dbi then {
              c.code as cd -> dbi.icdSchluessel = cd "SetDbiIcdCode";
              condition.bodySite as bs then {
                bs.coding as bsc where system = 'http://snomed.info/sct' then {
                  bsc.code as bscd where $this = '24028007' -> dbi.icdSchluessel = append(cd, ':R') "AppendSeiteRechts";
                  bsc.code as bscd where $this = '7771000' -> dbi.icdSchluessel = append(cd, ':L') "AppendSeiteLinks";
                  bsc.code as bscd where $this = '51440002' -> dbi.icdSchluessel = append(cd, ':B') "AppendSeiteBeidseits";
                } "MapDbiSeiteSCT";
              } "MapDbiSeite";
            } "MapDbiIcd";
          } "WrapDbiEntl";
        } "MapDbiCode";
      } "DbiConditionContext";
    } "EntryDbiCondition";
  } "EntryEncounter";
}

// ============================================================================
// Meldungskopf: Bundle -> meldung (MEL_*)
// Einrichtungskennung, Softwareinfo aus Organization und Bundle-Metadaten
// ============================================================================
group MapMeldungskopf(source src : Bundle, target tgt : BackboneElement) {
  // MEL_IrdIdGesundheitseinrichtung: aus Organization im Bundle
  src.entry as entry where resource.is(Organization) then {
    entry.resource as org then {
      org.identifier as id where system = 'http://fhir.de/sid/ird/ge-kennung' then {
        id.value as v -> tgt.idEinrichtung = v "SetIdEinrichtung";
      } "FilterIrdId";
      // MEL_Bsnr: Betriebsstaettennummer
      org.identifier as id where system = 'https://fhir.de/sid/arge-ik/iknr' then {
        id.value as v -> tgt.bsnr = v "SetBsnr";
      } "FilterBsnr";
    } "OrgContext";
  } "EntryOrganization";
  // MEL_IrdSpezVersion: feste Version der IRegD-Spezifikation
  src -> tgt.irdSpezVersion = '4.1.1' "SetSpezVersion";
  // MEL_SwName, MEL_SwHersteller, MEL_SwVersion: aus Bundle.meta.tag oder fest
  src -> tgt.swName = 'Senologie-KDS FHIR IG' "SetSwName";
  src -> tgt.swHersteller = 'BIH at Charite' "SetSwHersteller";
  src -> tgt.swVersion = '0.1.0' "SetSwVersion";
}

// ============================================================================
// Fall: Encounter -> fall (FAL_*)
// Aufnahmedatum, Art des Aufenthalts, Transfernummer
// ============================================================================
group MapFall(source src : Encounter, target tgt : BackboneElement) {
  // FAL_Aufnahmedatum: aus Encounter.period.start
  src.period as period then {
    period.start as s -> tgt.aufnahmedatum = s "SetAufnahmedatum";
  } "MapAufnahmedatum";
  // FAL_ArtAufenthaltSchluessel: aus Encounter.class
  // vollstationaer = 1, teilstationaer = 2, ambulant = 3
  src.class as cls then {
    cls.code as cd where $this = 'IMP' -> tgt.artAufenthaltSchluessel = '1' "SetAufenthaltStationaer";
    cls.code as cd where $this = 'SS' -> tgt.artAufenthaltSchluessel = '2' "SetAufenthaltTeilstationaer";
    cls.code as cd where $this = 'AMB' -> tgt.artAufenthaltSchluessel = '3' "SetAufenthaltAmbulant";
  } "MapArtAufenthalt";
  // FAL_Transfernummer: aus Encounter.identifier mit Pseudonymisierungssystem
  src.identifier as id where system = 'http://fhir.de/sid/ird/transfernummer' then {
    id.value as v -> tgt.transfernummer = v "SetTransfernummer";
  } "MapTransfernummer";
  // FAL_DatumZeitSatzErstellung: aktuelle Zeit
  src -> tgt.datumZeitSatzErstellung = evaluate(src, now()) "SetSatzErstellung";
  // FAL_Versionsnummer
  src.meta as meta then {
    meta.versionId as v -> tgt.versionsnummer = v "SetVersionsnummer";
  } "MapVersionsnummer";
  // Ausloeser, Patientenaufnahme, Operationen, Entlassung, DBI
  // bundle lookups moved to calling group.
  // Entlassung from Encounter itself (no bundle needed)
  src -> tgt.entlassung as entl then MapEntlassung(src, entl) "CallMapEntlassung";
}