# EDIFACT-Nachrichten in der Energiewirtschaft: Entwickler-Einführung

**Ein praktischer Leitfaden für die deutsche Marktkommunikation**

---

## 🎯 Für wen ist diese Einführung?

Du bist Entwickler:in und stehst vor deiner ersten EDIFACT-Nachricht aus der deutschen Energiewirtschaft? Du möchtest verstehen, was all diese kryptischen Zeichen bedeuten und wie du effizient damit arbeiten kannst? Dann bist du hier richtig!

Diese Einführung:
- ✅ Erklärt EDIFACT von Grund auf – kein Vorwissen nötig
- ✅ Zeigt praktische Beispiele mit dem `edifact-json-transformer`
- ✅ Vermittelt regulatorisches Wissen (GPKE, GeLi Gas, WiM, MaBiS)
- ✅ Gibt Best Practices für die tägliche Arbeit

**Powered by:** [Willi Mako](https://stromhaltig.de/app/) – die intelligente MaKo-Plattform  
**Open Source Tool:** [edifact-json-transformer](https://github.com/energychain/edifact-to-json-transformer)

---

## 📖 Inhaltsverzeichnis

1. [Was ist EDIFACT und warum brauchen wir das?](#was-ist-edifact)
2. [Die vier Säulen der Marktkommunikation](#die-vier-säulen)
3. [Anatomie einer EDIFACT-Nachricht](#anatomie-edifact)
4. [Schnellstart mit dem Transformer](#schnellstart)
5. [Die wichtigsten Nachrichtentypen](#nachrichtentypen)
6. [Prüfidentifikatoren verstehen](#pruefidentifikatoren)
7. [Praktische Tipps für den Alltag](#praktische-tipps)
8. [Häufige Fehler und Lösungen](#häufige-fehler)
9. [Best Practices](#best-practices)
10. [Weiterführende Ressourcen](#ressourcen)

---

## <a name="was-ist-edifact"></a>📚 Was ist EDIFACT und warum brauchen wir das?

### Die Grundidee

**EDIFACT** (Electronic Data Interchange For Administration, Commerce and Transport) ist ein UN-Standard für den elektronischen Datenaustausch. In der deutschen Energiewirtschaft ist dieser Standard **gesetzlich vorgeschrieben** und bildet das Rückgrat der Kommunikation zwischen allen Marktteilnehmern.

### Rechtliche Grundlagen

Die Basis bildet das **Energiewirtschaftsgesetz (EnWG)**. Darauf aufbauend hat die **Bundesnetzagentur (BNetzA)** detaillierte Festlegungen erlassen, die jeden Aspekt der Marktkommunikation regeln:

| Festlegung | Rechtsgrundlage | Was wird geregelt? |
|------------|-----------------|-------------------|
| **GPKE** | Geschäftsprozesse Kundenbelieferung Elektrizität | Strom-Lieferantenwechsel, An-/Abmeldungen, Stammdaten |
| **GeLi Gas** | Geschäftsprozesse Lieferantenwechsel Gas | Gas-Lieferantenwechsel, Gasbelieferung |
| **WiM** | Wechselprozesse im Messwesen | Zählerwechsel, Messstellenbetrieb, Messdaten |
| **MaBiS** | Marktregeln Bilanzierung Strom | Bilanzkreisabrechnung, Ausgleichsenergie |

### Die Marktteilnehmer
```
┌─────────────────┐
│   Lieferant     │ ──┐
│   (LF)          │   │
└─────────────────┘   │
                      │  EDIFACT
┌─────────────────┐   │  Nachrichten
│ Netzbetreiber   │ ──┤
│   (NB)          │   │
└─────────────────┘   │
                      │
┌─────────────────┐   │
│Messstellenbetr. │ ──┘
│   (MSB)         │
└─────────────────┘
```

**Rollen:**
- **Lieferant (LF)**: Versorgt Endkunden mit Energie, verantwortlich für Abrechnung
- **Netzbetreiber (NB)**: Betreibt das Netz, zentrale Kommunikationsstelle
- **Messstellenbetreiber (MSB)**: Betreibt Zähler, liefert Messdaten

---

## <a name="die-vier-säulen"></a>🏛️ Die vier Säulen der Marktkommunikation

### 1. GPKE – Strom-Lieferantenwechsel

**Kernprozesse:**
- Anmeldung zur Netznutzung (Prüf-ID 44001)
- Abmeldung (Prüf-ID 44004)
- Kündigung beim bisherigen Lieferanten (Prüf-ID 44016)
- Stammdatenänderungen (Prüf-ID 44112, 44123)

**Typische Nachrichten:** UTILMD, MSCONS, INVOIC, APERAK

**Beispiel-Workflow:**
```
Neuer Lieferant ──[UTILMD Anmeldung]──> Netzbetreiber
                                              │
                                              ├─[UTILMD Kündigung]─> Alter Lieferant
                                              │
Neuer Lieferant <─[APERAK Bestätigung]──────┘
```

### 2. GeLi Gas – Gas-Lieferantenwechsel

**Besonderheiten:**
- Zusätzliche Brennwert- und Zustandszahl-Informationen
- Spezielle Gasbeschaffenheitsdaten in MSCONS
- ORDERS für Anfragen (z.B. nach Brennwerten)

**Typische Nachrichten:** UTILMD, MSCONS, ORDERS, ORDRSP, APERAK

### 3. WiM – Wechselprozesse im Messwesen

**Kernprozesse:**
- Gerätewechsel (Prüf-ID 17xxx-Serie)
- Messkonzeptwechsel (Prüf-ID 21xxx-Serie)
- Störungsmeldungen (INSRPT)
- Auftragsbestätigungen (IFTSTA)

**Typische Nachrichten:** ORDERS, ORDRSP, IFTSTA, INSRPT, MSCONS

**Beispiel-Workflow:**
```
Lieferant ──[ORDERS Zählerwechsel]──> MSB
                                       │
MSB ──────[IFTSTA Auftragsbestätigung]─┘
       └─[MSCONS neue Messwerte]──────> Lieferant
```

### 4. MaBiS – Bilanzkreisabrechnung

**Kernprozesse:**
- Fahrplanmeldungen
- Bilanzkreiszuordnungen
- Mehr-/Mindermengenabrechnung
- Ausgleichsenergieabrechnung

**Typische Nachrichten:** MSCONS, PRICAT, PARTIN, COMDIS

---

## <a name="anatomie-edifact"></a>🔬 Anatomie einer EDIFACT-Nachricht

### Die Grundstruktur

Eine EDIFACT-Nachricht sieht zunächst kryptisch aus:
```
UNH+1+UTILMD:D:11A:UN:2.6'BGM+E01+12345+9'DTM+137:20241019:102'NAD+MS+9900123456789::293'RFF+Z13:44001'IDE+24+12345678901'UNT+6+1'
```

Zerlegt ergibt sich:
```
UNH+1+UTILMD:D:11A:UN:2.6'    ← Nachrichtenkopf
BGM+E01+12345+9'               ← Dokumentkopf
DTM+137:20241019:102'          ← Datum
NAD+MS+9900123456789::293'     ← Absender
RFF+Z13:44001'                 ← Prüfidentifikator
IDE+24+12345678901'            ← Marktlokation
UNT+6+1'                       ← Nachrichtenende
```

### Die Trennzeichen

| Zeichen | Bedeutung | Beispiel |
|---------|-----------|----------|
| `'` | Segment-Ende | `NAD+MS+123'` |
| `+` | Datenelemente-Trenner | `NAD+MS+123` |
| `:` | Komponenten-Trenner | `RFF+Z13:44001` |
| `?` | Escape-Character | `?+` für echtes `+` |
| `,` | Dezimaltrennzeichen | `1234,56` |

### Wichtige Segmente im Überblick

| Segment | Vollständiger Name | Inhalt | Beispiel |
|---------|-------------------|--------|----------|
| **UNH** | Message Header | Nachrichtentyp, Version, Referenz | `UNH+1+UTILMD:D:11A:UN:2.6'` |
| **BGM** | Beginning of Message | Dokumentart, Nummer, Funktion | `BGM+E01+12345+9'` |
| **DTM** | Date/Time/Period | Datum/Zeit mit Qualifier | `DTM+137:20241019:102'` |
| **NAD** | Name and Address | Partei-Informationen | `NAD+MS+9900123456789::293'` |
| **RFF** | Reference | Referenzen (z.B. Prüf-ID) | `RFF+Z13:44001'` |
| **IDE** | Identity | Objekt-Identifikation | `IDE+24+12345678901'` |
| **LOC** | Place/Location | Ortsangaben | `LOC+172+DE'` |
| **QTY** | Quantity | Mengenangaben | `QTY+220:1234.5:KWH'` |
| **MOA** | Monetary Amount | Geldbeträge | `MOA+77:1234.56:EUR'` |
| **CCI** | Characteristic | Eigenschaften/Klassen | `CCI+Z19++++Bilanzkreis'` |
| **UNT** | Message Trailer | Segmentanzahl, Referenz | `UNT+6+1'` |

### Kardinalitäten verstehen

In den BDEW-Anwendungshandbüchern (AHB) findest du Kardinalitäten:

| Code | Bedeutung | Beschreibung |
|------|-----------|--------------|
| **M** | Mandatory | Pflichtfeld, muss immer vorhanden sein |
| **C** | Conditional | Bedingt, abhängig vom Kontext |
| **R** | Required (BDEW) | BDEW-spezifische Pflichtfelder |
| **D** | Dependent | Abhängig von anderen Feldern |
| **N** | Not used | Nicht verwendet in diesem Kontext |

---

## <a name="schnellstart"></a>🚀 Schnellstart mit dem Transformer

### Installation
```bash
npm install edifact-json-transformer
```

### Deine erste Transformation
```javascript
const { EdifactTransformer } = require('edifact-json-transformer');

// Transformer mit Validierung erstellen
const transformer = new EdifactTransformer({
  enableAHBValidation: true,      // AHB-Regeln prüfen
  parseTimestamps: true,           // Datumswerte formatieren
  generateGraphRelations: true     // Graph-Beziehungen erstellen
});

// EDIFACT-String (z.B. aus Datei gelesen)
const edifactString = `
UNH+1+UTILMD:D:11A:UN:2.6'
BGM+E01+12345+9'
DTM+137:20241019:102'
NAD+MS+9900123456789::293'
NAD+MR+9900987654321::293'
RFF+Z13:44001'
IDE+24+12345678901'
UNT+7+1'
`;

// Transformation durchführen
const json = transformer.transform(edifactString);

// Ergebnis verwenden
console.log('Nachrichtentyp:', json.metadata.message_type);
console.log('Prüf-ID:', json.metadata.pruefidentifikator);
console.log('Marktlokationen:', json.body.stammdaten.marktlokationen);
```

**Ausgabe:**
```javascript
{
  metadata: {
    message_type: 'UTILMD',
    message_name: 'Stammdaten',
    category: 'master_data',
    applicable_processes: ['GPKE', 'GeLi Gas', 'WiM', 'MaBiS'],
    version: '2.6',
    pruefidentifikator: {
      id: '44001',
      description: 'Anmeldung NN'
    }
  },
  body: {
    stammdaten: {
      marktlokationen: [
        { id: '12345678901', valid: true }
      ]
    }
  },
  parties: {
    sender: { 
      id: '9900123456789', 
      role: 'MS',
      valid_mp_id: true 
    },
    receiver: { 
      id: '9900987654321', 
      role: 'MR',
      valid_mp_id: true 
    }
  }
}
```

---

## <a name="nachrichtentypen"></a>📨 Die wichtigsten Nachrichtentypen im Detail

### 1. UTILMD – Utility Master Data (Stammdaten)

**Zweck:** Austausch von Stammdaten zu Netzanschlüssen, Messlokationen und Lieferantenzuordnungen

**Zentrale Segmente:**
- **UNH, BGM**: Nachrichtenkopf
- **DTM**: Zeitangaben (Lieferbeginn/-ende)
- **RFF+Z13**: Prüfidentifikator (Geschäftsvorfall)
- **NAD**: Beteiligte Marktpartner
- **IDE+24**: Marktlokations-ID (11-stellig)
- **IDE+25**: Messlokations-ID (33-stellig)
- **LOC**: Lokationsangaben
- **CCI**: Eigenschaften (z.B. Bilanzkreis)

**Code-Beispiel:**
```javascript
const json = transformer.transform(utilmdString);

// Stammdaten auslesen
const malo = json.body.stammdaten.marktlokationen[0];
console.log(`MaLo-ID: ${malo.id}, gültig: ${malo.valid}`);

// Prüfidentifikator prüfen
if (json.metadata.pruefidentifikator.id === '44001') {
  console.log('Anmeldung zur Netznutzung');
}

// Bilanzkreis ermitteln
const bk = json.body.stammdaten.bilanzkreise[0];
console.log(`Bilanzkreis: ${bk.id}`);
```

**Typische Anwendungsfälle:**
- Lieferantenwechsel (GPKE/GeLi Gas)
- Stammdatenpflege
- Messlokations-Anmeldung (WiM)

---

### 2. MSCONS – Meter Reading Schedule Consumption (Messwerte)

**Zweck:** Übermittlung von Verbrauchsdaten zwischen Marktpartnern

**Zentrale Segmente:**
- **UNH, BGM**: Nachrichtenkopf
- **DTM**: Messperiode (Start/Ende)
- **RFF**: Referenzen (Marktlokation, Geschäftsvorfall)
- **NAD**: Beteiligte Partner
- **LOC**: Messlokation
- **QTY**: Messwerte mit Einheit
- **SEQ**: Zeitreihen-Sequenzen
- **MEA**: Detaillierte Messwerte (oft Viertelstundenwerte)

**Code-Beispiel:**
```javascript
const json = transformer.transform(msconsString);

// Messwerte auslesen
json.body.messwerte.messwerte.forEach(messwert => {
  console.log(`
    Qualifier: ${messwert.qualifier}
    Wert: ${messwert.value} ${messwert.unit}
    Gültig: ${messwert.valid_unit}
  `);
});

// Zeitreihen-Daten
console.log('Anzahl Zeitscheiben:', json.body.messwerte.zeitreihen.length);

// Messperiode
const periode = json.body.messwerte.messperioden;
console.log(`Von ${periode[0].datetime} bis ${periode[1].datetime}`);
```

**Wichtige Qualifier:**
- `220`: Gelieferte Energie
- `66`: Zählerstand
- `74`: Bezogene Energie

**Einheiten:**
- `KWH`: Kilowattstunde
- `MWH`: Megawattstunde
- `W`: Watt (Leistung)

**Typische Anwendungsfälle:**
- Bilanzierungsrelevante Messwerte (MaBiS)
- Abrechnungsdaten für Lieferanten
- Austausch zwischen MSB und NB/LF

---

### 3. ORDERS – Purchase Order (Bestellung)

**Zweck:** Elektronische Übermittlung von Bestellungen und Anfragen

**Zentrale Segmente:**
- **UNH, BGM**: Nachrichtenkopf
- **DTM**: Bestelldatum, Liefertermin
- **RFF**: Bestellnummer, Referenzen
- **NAD**: Besteller und Empfänger
- **LIN**: Bestellpositionen
- **PIA**: Produktidentifikation
- **IMD**: Artikelbeschreibung
- **QTY**: Bestellmengen

**Code-Beispiel:**
```javascript
const json = transformer.transform(ordersString);

// Bestellpositionen auslesen
json.body.bestellung.order_lines.forEach(line => {
  console.log(`
    Position: ${line.line_number}
    Artikel-ID: ${line.item_id}
    Typ: ${line.item_type}
  `);
});
```

**Typische Anwendungsfälle:**
- Beauftragung Messstellenbetrieb (WiM)
- Anfrage Gasbeschaffenheit (GeLi Gas)
- Materialbestellungen

---

### 4. ORDRSP – Purchase Order Response (Bestellantwort)

**Zweck:** Antwort auf ORDERS-Nachricht

**Zentrale Segmente:**
- **UNH, BGM**: Nachrichtenkopf
- **DTM**: Antwortdatum
- **RFF**: Referenz auf ORDERS
- **NAD**: Absender/Empfänger
- **STS**: Bestellstatus
- **LIN, PIA, IMD**: Positionsdaten

**Statuswerte:**
- `7`: Bestätigt
- `27`: Abgelehnt
- `4`: Teilweise bestätigt

---

### 5. INVOIC – Invoice (Rechnung)

**Zweck:** Übermittlung von Rechnungen und Gutschriften

**Zentrale Segmente:**
- **UNH, BGM**: Nachrichtenkopf
- **DTM**: Rechnungs-/Leistungsdatum
- **RFF**: Rechnungs-/Bestellnummer
- **NAD**: Rechnungssteller/-empfänger
- **LIN, PIA, IMD**: Rechnungspositionen
- **MOA**: Geldbeträge
- **TAX**: Steuerdetails

**Code-Beispiel:**
```javascript
const json = transformer.transform(invoicString);

// Rechnungssumme
const summe = json.body.rechnung.totals['77'];
console.log(`Gesamt: ${summe.amount} ${summe.currency}`);

// Steuern
json.body.rechnung.tax_amounts.forEach(tax => {
  console.log(`Steuer: ${tax.type}, Rate: ${tax.rate}%`);
});
```

**MOA-Qualifier:**
- `77`: Rechnungssumme
- `79`: Gesamtbetrag inkl. MwSt
- `125`: Steuerbetrag

---

### 6. APERAK – Application Error and Acknowledgement

**Zweck:** Bestätigung oder Fehlermeldung für empfangene Nachrichten

**Zentrale Segmente:**
- **UNH, BGM**: Nachrichtenkopf
- **DTM**: Erstellungsdatum
- **RFF**: Referenz auf Original-Nachricht
- **ERC**: Fehlerinformationen
- **FTX**: Freitext-Erklärungen

**Code-Beispiel:**
```javascript
const json = transformer.transform(aperakString);

// Status prüfen
if (json.body.quittierung.status === 'positive') {
  console.log('Nachricht erfolgreich verarbeitet');
} else {
  // Fehler ausgeben
  json.body.quittierung.errors.forEach(error => {
    console.error(`Fehler ${error.code}: ${error.description}`);
  });
}
```

**Häufige Fehlercodes:**
- `1`: Syntax-Fehler
- `2`: Semantischer Fehler
- `3`: Geschäftsprozess-Fehler
- `7`: Nachricht akzeptiert

---

## <a name="pruefidentifikatoren"></a>🔍 Prüfidentifikatoren verstehen (RFF+Z13)

### Was ist ein Prüfidentifikator?

Der **Prüfidentifikator** (im Segment `RFF+Z13`) ist die **wichtigste Kennung** in einer MaKo-Nachricht. Er identifiziert eindeutig den Geschäftsvorfall und bestimmt:

- Welcher Prozess läuft (Anmeldung, Abmeldung, etc.)
- Welche Segmente Pflicht sind
- Welche Validierungsregeln gelten
- Welche Antwort-Nachrichten zu erwarten sind

**Format:**
```
RFF+Z13:44001'
     │   │
     │   └─ Prüfidentifikator (5-stellig)
     └─ Qualifier Z13
```

### Die wichtigsten Prüfidentifikatoren

#### GPKE (Strom)

| Prüf-ID | Prozess | Richtung | Typische Antwort |
|---------|---------|----------|------------------|
| **44001** | Anmeldung Netznutzung | Lieferant → NB | APERAK (44002/44003) |
| **44002** | Bestätigung Anmeldung | NB → Lieferant | - |
| **44003** | Ablehnung Anmeldung | NB → Lieferant | - |
| **44004** | Abmeldung Netznutzung | Lieferant → NB | APERAK (44005/44006) |
| **44005** | Bestätigung Abmeldung | NB → Lieferant | - |
| **44006** | Ablehnung Abmeldung | NB → Lieferant | - |
| **44016** | Kündigung beim alten LF | NB → Lieferant | APERAK (44017/44018) |
| **44017** | Bestätigung Kündigung | Lieferant → NB | - |
| **44018** | Ablehnung Kündigung | Lieferant → NB | - |
| **44112** | Stammdatenänderung | NB → Lieferant | - |
| **44123** | Bila.rel. Änderung | NB → Lieferant | - |

#### Messwerte

| Prüf-ID | Prozess | Verwendung |
|---------|---------|------------|
| **13002** | Zählerstand | MSCONS |
| **13008** | Lastgang | MSCONS |
| **13009** | Energiemenge | MSCONS |
| **13007** | Gasbeschaffenheit | MSCONS (Gas) |
| **13006** | Messwert Storno | MSCONS |

#### Stammdaten-Anfragen

| Prüf-ID | Prozess | Verwendung |
|---------|---------|------------|
| **17101** | Anfrage Stammdaten MaLo | ORDERS |
| **17102** | Anfrage Werte | ORDERS |
| **19101** | Ablehnung Anfrage Stammdaten | ORDRSP |
| **19102** | Ablehnung Anfrage Werte | ORDRSP |

#### WiM (Messwesen)

| Prüf-ID | Prozess | Verwendung |
|---------|---------|------------|
| **17009** | Gerätewechsel | ORDERS |
| **19015** | Bestätigung Gerätewechsel | ORDRSP |
| **21039** | Auftragsstatus Sperren | IFTSTA |
| **21040** | Info Entsperrauftrag | IFTSTA |

#### Rechnungen

| Prüf-ID | Prozess | Verwendung |
|---------|---------|------------|
| **31001** | Abschlagsrechnung | INVOIC |
| **31002** | NN-Rechnung | INVOIC |
| **31003** | WiM-Rechnung | INVOIC |
| **31004** | Stornorechnung | INVOIC |
| **33001** | Bestätigung | REMADV |
| **33002** | Abweisung | REMADV |

### Code-Beispiel: Prüfidentifikator auswerten
```javascript
const json = transformer.transform(edifactString);
const pruefId = json.metadata.pruefidentifikator.id;

switch(pruefId) {
  case '44001':
    console.log('Anmeldung zur Netznutzung');
    // Validiere Pflichtfelder für Anmeldung
    validateAnmeldung(json);
    break;
    
  case '44004':
    console.log('Abmeldung vom Netz');
    validateAbmeldung(json);
    break;
    
  case '13008':
    console.log('Lastgangdaten');
    processLastgang(json.body.messwerte);
    break;
    
  default:
    console.warn(`Unbekannte Prüf-ID: ${pruefId}`);
}
```

### Helper-Funktion
```javascript
const { isGPKEProcess } = require('edifact-json-transformer');

// Prozess-Zuordnung prüfen
if (isGPKEProcess(edifactString)) {
  console.log('GPKE-Prozess erkannt');
}

// Alle Prüfidentifikatoren verfügbar
const { pruefidentifikatoren } = require('edifact-json-transformer');
console.log(pruefidentifikatoren['44001']); 
// → "Anmeldung NN"
```

---

## <a name="praktische-tipps"></a>💡 Praktische Tipps für den Alltag

### 1. Schnell-Check: Was ist das für eine Nachricht?
```javascript
// Nur Metadaten ohne vollständige Transformation
const { EdifactTransformer } = require('edifact-json-transformer');

function quickInfo(edifactString) {
  const transformer = new EdifactTransformer({
    validateStructure: false,  // Schneller
    parseTimestamps: false
  });
  
  const json = transformer.transform(edifactString);
  
  return {
    typ: json.metadata.message_type,
    name: json.metadata.message_name,
    pruefId: json.metadata.pruefidentifikator?.id,
    prozess: json.metadata.applicable_processes,
    absender: json.parties.sender?.id,
    empfaenger: json.parties.receiver?.id
  };
}

console.log(quickInfo(edifactString));
// { typ: 'UTILMD', name: 'Stammdaten', pruefId: '44001', ... }
```

### 2. Marktlokationen schnell extrahieren
```javascript
const { extractAllMarktlokationIds } = require('edifact-json-transformer');

// Super-schnelle Extraktion
const maloIds = extractAllMarktlokationIds(edifactString);
console.log(maloIds); // ['12345678901', '98765432109']

// In der Datenbank nachschlagen
maloIds.forEach(id => {
  const kunde = db.findByMaloId(id);
  console.log(`Kunde: ${kunde.name} (${id})`);
});
```

### 3. Zeitscheiben in MSCONS verarbeiten
```javascript
function processTimeSeries(msconsString) {
  const json = transformer.transform(msconsString);
  const zeitreihen = json.body.messwerte.zeitreihen;
  
  // Viertelstundenwerte
  const viertelstundenwerte = zeitreihen.map((seq, idx) => {
    const messwert = json.body.messwerte.messwerte[idx];
    return {
      sequenz: seq.sequence_number,
      wert: messwert.value,
      einheit: messwert.unit,
      zeitpunkt: calculateTimeSlot(seq.sequence_number, startDate)
    };
  });
  
  return viertelstundenwerte;
}

function calculateTimeSlot(sequenz, startDate) {
  // Viertelstunde = 15 Minuten
  const minutes = (sequenz - 1) * 15;
  return new Date(startDate.getTime() + minutes * 60000);
}
```

### 4. Validierung mit Fehler-Report
```javascript
const { validateAHB } = require('edifact-json-transformer');

function validateAndReport(edifactString) {
  const report = validateAHB(edifactString);
  
  if (!report.is_valid) {
    console.error('⚠️ Validierungsfehler gefunden:');
    
    report.errors.forEach(error => {
      console.error(`  ❌ [${error.category}] ${error.message}`);
    });
    
    report.warnings.forEach(warning => {
      console.warn(`  ⚠️ [${warning.category}] ${warning.message}`);
    });
    
    return false;
  }
  
  console.log('✅ Nachricht ist valide');
  return true;
}
```

### 5. Datumswerte lesen
```javascript
const json = transformer.transform(edifactString);

// Alle Datumswerte
console.log(json.dates);
// {
//   message_date: '2024-10-19',
//   start_date: '2024-01-01',
//   end_date: '2024-12-31'
// }

// Spezifische Prüfungen
const { start_date, end_date } = json.dates;

if (new Date(start_date) > new Date(end_date)) {
  console.error('Start liegt nach Ende!');
}

// Dauer berechnen
const days = (new Date(end_date) - new Date(start_date)) / (1000 * 60 * 60 * 24);
console.log(`Zeitraum: ${days} Tage`);
```

### 6. Graph-Beziehungen für Analyse
```javascript
const transformer = new EdifactTransformer({
  generateGraphRelations: true
});

const json = transformer.transform(edifactString);

// Neo4j Cypher generieren
const { convertToNeo4jCypher } = require('edifact-json-transformer');
const statements = convertToNeo4jCypher(edifactString);

statements.forEach(stmt => {
  console.log(stmt.cypher);
  console.log(stmt.parameters);
});

// Oder direkt aus JSON
json.graph_relations.forEach(rel => {
  console.log(`${rel.from.type}:${rel.from.id} -[${rel.relationship}]-> ${rel.to.type}:${rel.to.id}`);
});
```

### 7. Batch-Verarbeitung
```javascript
const fs = require('fs');
const { EdifactTransformer } = require('edifact-json-transformer');

function processBatch(directory) {
  const transformer = new EdifactTransformer({
    enableAHBValidation: true
  });
  
  const files = fs.readdirSync(directory);
  const results = {
    success: 0,
    errors: [],
    warnings: []
  };
  
  files.forEach(file => {
    try {
      const content = fs.readFileSync(`${directory}/${file}`, 'utf8');
      const json = transformer.transform(content);
      
      if (json.validation?.is_valid !== false) {
        results.success++;
      } else {
        results.errors.push({
          file,
          errors: json.validation.errors
        });
      }
      
      if (json.validation?.warnings?.length > 0) {
        results.warnings.push({
          file,
          warnings: json.validation.warnings
        });
      }
    } catch (error) {
      results.errors.push({ file, error: error.message });
    }
  });
  
  console.log(`
    ✅ Erfolgreich: ${results.success}
    ❌ Fehler: ${results.errors.length}
    ⚠️ Warnungen: ${results.warnings.length}
  `);
  
  return results;
}
```

---

## <a name="häufige-fehler"></a>🐛 Häufige Fehler und Lösungen

### 1. Syntaxfehler

#### Problem: Falsche Trennzeichen
```javascript
// ❌ Falsch
"UNH+1+UTILMD:D:11A:UN:2.6'BGM+E01+12345+9'..."  // Fehlt Zeilenumbruch-Escape

// ✅ Richtig
const edifact = normalizeEdifact(rawString);  // Transformer macht das automatisch
```

#### Problem: Fehlende Pflichtsegmente
```javascript
const json = transformer.transform(edifactString);

if (json.validation?.errors) {
  json.validation.errors.forEach(error => {
    if (error.message.includes('Fehlendes UNH')) {
      console.error('Nachrichtenkopf fehlt!');
    }
  });
}
```

**Lösung:** Prüfe die AHB-Vorgaben für den jeweiligen Nachrichtentyp.

#### Problem: Falsche Datenformate
```javascript
// DTM-Segment mit falschem Format
"DTM+137:19.10.2024:102'"  // ❌ Falsch: TT.MM.JJJJ

"DTM+137:20241019:102'"    // ✅ Richtig: JJJJMMTT
```

**Debugging:**
```javascript
const json = transformer.transform(edifactString, {
  includeRawSegments: true
});

// Rohe Segmente inspizieren
json.raw_segments.filter(s => s.tag === 'DTM').forEach(dtm => {
  console.log('DTM:', dtm.raw);
});
```

---

### 2. Semantische Fehler

#### Problem: Referenznummer-Mismatch
```javascript
// ❌ UNH und UNT stimmen nicht überein
"UNH+1+UTILMD:D:11A:UN:2.6'...UNT+7+2'"

// ✅ Korrekt
"UNH+1+UTILMD:D:11A:UN:2.6'...UNT+7+1'"
```

**Automatische Prüfung:**
```javascript
const json = transformer.transform(edifactString);

if (json.validation?.errors.some(e => e.message.includes('Referenznummer-Mismatch'))) {
  console.error('UNH/UNT Referenzen stimmen nicht überein!');
}
```

#### Problem: Ungültige MP-ID
```javascript
const json = transformer.transform(edifactString);

Object.entries(json.parties).forEach(([role, party]) => {
  if (!party.valid_mp_id) {
    console.error(`
      ❌ Ungültige MP-ID für ${role}
      Wert: ${party.id}
      Erwartet: 13-stellig numerisch
    `);
  }
});
```

**Validierung:**
```javascript
function isValidMpId(id) {
  return /^\d{13}$/.test(id);
}
```

#### Problem: Start-/Enddatum vertauscht
```javascript
const json = transformer.transform(edifactString);

if (json.validation?.errors) {
  json.validation.errors.forEach(error => {
    if (error.message.includes('Start-Datum liegt nach End-Datum')) {
      console.error('Zeitlogik ist falsch!');
      console.log('Start:', json.dates.start_date);
      console.log('Ende:', json.dates.end_date);
    }
  });
}
```

---

### 3. Geschäftsprozess-Fehler

#### Problem: Falscher Prüfidentifikator
```javascript
const json = transformer.transform(edifactString);
const pruefId = json.metadata.pruefidentifikator?.id;

// Prüfen ob Prüf-ID zum Nachrichtentyp passt
const validCombinations = {
  'UTILMD': ['44001', '44002', '44003', '44004', '44005', '44006', '44016', '44112', '44123'],
  'MSCONS': ['13002', '13007', '13008', '13009'],
  'ORDERS': ['17009', '17101', '17102'],
  'INVOIC': ['31001', '31002', '31003', '31004']
};

const messageType = json.metadata.message_type;
if (!validCombinations[messageType]?.includes(pruefId)) {
  console.error(`
    ❌ Prüf-ID ${pruefId} passt nicht zu ${messageType}
    Erlaubte IDs: ${validCombinations[messageType].join(', ')}
  `);
}
```

#### Problem: Fehlende Rollen
```javascript
// Für Anmeldung (44001) sind spezifische Rollen erforderlich
if (json.metadata.pruefidentifikator.id === '44001') {
  const hasLieferant = json.parties.lieferant || json.parties.sender;
  const hasNetzbetreiber = json.parties.netzbetreiber || json.parties.receiver;
  
  if (!hasLieferant) {
    console.error('❌ Lieferant-Rolle fehlt für Anmeldung');
  }
  
  if (!hasNetzbetreiber) {
    console.error('❌ Netzbetreiber-Rolle fehlt für Anmeldung');
  }
}
```

#### Problem: Ungültige Marktlokations-ID
```javascript
const json = transformer.transform(edifactString);

json.body.stammdaten?.marktlokationen?.forEach((malo, idx) => {
  if (!malo.valid) {
    console.error(`
      ❌ Marktlokation ${idx + 1} ungültig
      ID: ${malo.id}
      Erwartet: 11-stellig
      Tatsächlich: ${malo.id?.length} Zeichen
    `);
  }
});

json.body.stammdaten?.messlokationen?.forEach((melo, idx) => {
  if (!melo.valid) {
    console.error(`
      ❌ Messlokation ${idx + 1} ungültig
      ID: ${melo.id}
      Erwartet: 33-stellig
      Tatsächlich: ${melo.id?.length} Zeichen
    `);
  }
});
```

---

## <a name="best-practices"></a>✅ Best Practices für Entwickler

### 1. Validierung auf allen Ebenen
```javascript
class EdifactProcessor {
  constructor() {
    this.transformer = new EdifactTransformer({
      enableAHBValidation: true,
      validateStructure: true,
      validateBusinessRules: true
    });
  }
  
  async process(edifactString) {
    // 1. Syntax-Validierung
    if (!this.validateSyntax(edifactString)) {
      throw new Error('Syntax ungültig');
    }
    
    // 2. Transformation
    const json = this.transformer.transform(edifactString);
    
    // 3. Struktur-Validierung
    if (!json.validation?.is_valid) {
      this.logErrors(json.validation.errors);
      throw new Error('Struktur ungültig');
    }
    
    // 4. Geschäftsprozess-Validierung
    this.validateBusinessLogic(json);
    
    // 5. Verarbeitung
    return this.processMessage(json);
  }
  
  validateSyntax(edifactString) {
    // Grundlegende Checks
    return edifactString.includes("UNH") && 
           edifactString.includes("UNT");
  }
  
  validateBusinessLogic(json) {
    const pruefId = json.metadata.pruefidentifikator?.id;
    
    // Prozessspezifische Validierung
    switch(pruefId) {
      case '44001':
        this.validateAnmeldung(json);
        break;
      case '13008':
        this.validateLastgang(json);
        break;
    }
  }
}
```

### 2. Umfassendes Error Handling
```javascript
class MessageValidator {
  validate(json) {
    const errors = [];
    const warnings = [];
    
    // Pflichtfelder prüfen
    if (!json.metadata.pruefidentifikator) {
      errors.push({
        category: 'AHB',
        message: 'Prüfidentifikator fehlt',
        severity: 'ERROR'
      });
    }
    
    // Marktlokationen prüfen
    json.body.stammdaten?.marktlokationen?.forEach((malo, idx) => {
      if (!malo.valid) {
        warnings.push({
          category: 'STAMMDATEN',
          message: `MaLo ${idx + 1}: ${malo.id} ist ungültig`,
          severity: 'WARNING'
        });
      }
    });
    
    return {
      is_valid: errors.length === 0,
      errors,
      warnings
    };
  }
  
  generateAPERAK(originalMessage, errors) {
    // APERAK-Nachricht generieren
    return `
      UNH+${generateRef()}+APERAK:D:11A:UN:2.0'
      BGM+${errors.length > 0 ? '27' : '7'}+${generateRef()}+9'
      ${errors.map(e => `ERC+${e.code}'`).join('')}
      UNT+${2 + errors.length}+${generateRef()}'
    `;
  }
}
```

### 3. Strukturierte Logging-Strategie
```javascript
const winston = require('winston');

const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'edifact-error.log', level: 'error' }),
    new winston.transports.File({ filename: 'edifact-combined.log' })
  ]
});

function processMessage(edifactString, source) {
  const startTime = Date.now();
  
  try {
    const json = transformer.transform(edifactString);
    
    logger.info({
      event: 'message_processed',
      type: json.metadata.message_type,
      pruefId: json.metadata.pruefidentifikator?.id,
      source,
      duration: Date.now() - startTime,
      validation: json.validation?.is_valid
    });
    
    return json;
    
  } catch (error) {
    logger.error({
      event: 'processing_failed',
      source,
      error: error.message,
      stack: error.stack,
      duration: Date.now() - startTime
    });
    throw error;
  }
}
```

### 4. Modularer Aufbau
```javascript
// parser.js
class EdifactParser {
  parse(edifactString) {
    return transformer.transform(edifactString);
  }
}

// validator.js
class EdifactValidator {
  validateAHB(json) { /* ... */ }
  validateStructure(json) { /* ... */ }
  validateBusinessRules(json) { /* ... */ }
}

// processor.js
class MessageProcessor {
  processUTILMD(json) { /* ... */ }
  processMSCONS(json) { /* ... */ }
  processORDERS(json) { /* ... */ }
}

// main.js
class EdifactService {
  constructor() {
    this.parser = new EdifactParser();
    this.validator = new EdifactValidator();
    this.processor = new MessageProcessor();
  }
  
  async handleMessage(edifactString) {
    const json = this.parser.parse(edifactString);
    
    if (!this.validator.validateAHB(json)) {
      throw new ValidationError('AHB-Validierung fehlgeschlagen');
    }
    
    const messageType = json.metadata.message_type;
    return this.processor[`process${messageType}`](json);
  }
}
```

### 5. Testing-Strategie
```javascript
// test/utilmd.test.js
const { EdifactTransformer } = require('edifact-json-transformer');

describe('UTILMD Transformation', () => {
  let transformer;
  
  beforeEach(() => {
    transformer = new EdifactTransformer({
      enableAHBValidation: true
    });
  });
  
  test('Anmeldung NN (44001) korrekt verarbeitet', () => {
    const edifact = loadTestFile('utilmd_anmeldung_44001.txt');
    const json = transformer.transform(edifact);
    
    expect(json.metadata.message_type).toBe('UTILMD');
    expect(json.metadata.pruefidentifikator.id).toBe('44001');
    expect(json.body.stammdaten.marktlokationen).toHaveLength(1);
    expect(json.validation.is_valid).toBe(true);
  });
  
  test('Ungültige MaLo-ID wird erkannt', () => {
    const edifact = loadTestFile('utilmd_invalid_malo.txt');
    const json = transformer.transform(edifact);
    
    expect(json.validation.is_valid).toBe(false);
    expect(json.validation.errors).toContainEqual(
      expect.objectContaining({
        message: expect.stringContaining('11-stellig')
      })
    );
  });
  
  test('Fehlender Prüfidentifikator wird gemeldet', () => {
    const edifact = loadTestFile('utilmd_no_pruefid.txt');
    const json = transformer.transform(edifact);
    
    expect(json.validation.errors).toContainEqual(
      expect.objectContaining({
        message: expect.stringContaining('Prüfidentifikator')
      })
    );
  });
});
```

### 6. Versionierung und Updates
```javascript
class EdifactService {
  constructor() {
    this.transformers = {
      'v2.6': new EdifactTransformer({ /* config für 2.6 */ }),
      'v2.7': new EdifactTransformer({ /* config für 2.7 */ })
    };
  }
  
  getTransformer(version) {
    return this.transformers[`v${version}`] || this.transformers['v2.6'];
  }
  
  process(edifactString) {
    // Version aus UNH extrahieren
    const versionMatch = edifactString.match(/UNH\+\d+\+[^:]+:[^:]+:[^:]+:[^:]+:([^']+)'/);
    const version = versionMatch ? versionMatch[1] : '2.6';
    
    const transformer = this.getTransformer(version);
    return transformer.transform(edifactString);
  }
}
```

### 7. Performance-Optimierung
```javascript
// Caching für wiederholte Transformationen
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 600 });

function transformWithCache(edifactString) {
  const hash = crypto.createHash('md5').update(edifactString).digest('hex');
  
  let json = cache.get(hash);
  if (json) {
    console.log('Cache hit');
    return json;
  }
  
  json = transformer.transform(edifactString);
  cache.set(hash, json);
  return json;
}

// Bulk-Processing mit Worker Threads
const { Worker } = require('worker_threads');

async function processBulk(messages) {
  const chunkSize = Math.ceil(messages.length / 4);
  const workers = [];
  
  for (let i = 0; i < messages.length; i += chunkSize) {
    const chunk = messages.slice(i, i + chunkSize);
    workers.push(
      new Promise((resolve) => {
        const worker = new Worker('./edifact-worker.js', {
          workerData: chunk
        });
        worker.on('message', resolve);
      })
    );
  }
  
  const results = await Promise.all(workers);
  return results.flat();
}
```

---

## <a name="ressourcen"></a>📚 Weiterführende Ressourcen

### Offizielle Quellen

1. **EDI@Energy Portal** (BDEW)
   - URL: https://www.edi-energy.de/
   - Registrierung erforderlich
   - Enthält: AHB, MIG, Codelisten, Prozessbeschreibungen

2. **Bundesnetzagentur**
   - Festlegungen zu GPKE, GeLi Gas, WiM, MaBiS
   - URL: https://www.bundesnetzagentur.de/

3. **BDEW Codelisten**
   - Aktuelle Qualifier und Codes
   - Updates bei Prozessänderungen

### Tools und Plattformen

1. **Willi Mako** – Intelligente MaKo-Plattform
   - Web-App: https://stromhaltig.de/app/
   - Open-Source Client: https://github.com/energychain/willi-mako-client
   - Regulatorisches Wissen, Beispiele, Validierung

2. **edifact-json-transformer**
   - Repository: https://github.com/energychain/edifact-to-json-transformer
   - NPM: `npm install edifact-json-transformer`
   - MIT-Lizenz, Community-Support

### Lernressourcen

- **EDIFACT ISO 9735 Standard**: Grundlagen zu Segmenten und Syntax
- **BDEW Schulungen**: Workshops zu Marktkommunikation
- **Online-Communities**: Austausch mit anderen Entwickler:innen

### Support

- **Issues/Bugs**: [GitHub Issues](https://github.com/energychain/edifact-to-json-transformer/issues)
- **Pull Requests**: Contributions willkommen!
- **Maintainer**: STROMDAO GmbH (https://stromdao.de/)

---

## 🎓 Zusammenfassung

Du hast jetzt gelernt:

✅ **Grundlagen**: Was EDIFACT ist und warum es in der Energiewirtschaft genutzt wird  
✅ **Prozesse**: GPKE, GeLi Gas, WiM und MaBiS im Detail  
✅ **Nachrichtentypen**: UTILMD, MSCONS, ORDERS, INVOIC, APERAK  
✅ **Prüfidentifikatoren**: Die wichtigsten IDs und ihre Bedeutung  
✅ **Praktische Tools**: Wie du mit dem Transformer effizient arbeitest  
✅ **Fehlerbehandlung**: Häufige Probleme erkennen und lösen  
✅ **Best Practices**: Professionelle Entwicklung von MaKo-Software

### Nächste Schritte

1. **Installiere** den Transformer: `npm install edifact-json-transformer`
2. **Probiere** die Beispiele aus diesem Guide
3. **Validiere** echte Nachrichten aus deinem Projekt
4. **Erkunde** Willi Mako für tieferes regulatorisches Wissen
5. **Teile** deine Erfahrungen und trage zur Community bei

### Denk daran

> "EDIFACT ist komplex, aber mit den richtigen Tools und etwas Übung wirst du zum MaKo-Profi!"

Viel Erfolg bei deinen ersten (oder nächsten) Schritten in der Marktkommunikation! 🚀

---

**Lizenz:** MIT  
**Maintainer:** [STROMDAO GmbH](https://stromdao.de/)  
**Powered by:** [Willi Mako](https://stromhaltig.de/app/) – Intelligente Marktkommunikation  
**Veröffentlicht auf:** [Corrently.io](https://corrently.io/)

---

*Letzte Aktualisierung: Oktober 2024*