edi-energy 0.19.0

EDI@Energy EDIFACT parser and validator for the German energy market
Documentation
//! BDEW segment layouts for the user segments EDI@Energy messages carry.
//!
//! `edifact-rs` ships the ISO 9735 service segments (`UNB`…`UNZ`, `UCI`…`UCD`)
//! because the syntax standard fixes them. The user segments below come from
//! the separately-licensed UN/EDIFACT directory, so they are declared here.
//!
//! # These follow the MIG, not the directory
//!
//! The two differ, and for EDI@Energy the MIG wins:
//!
//! - **An unused element keeps its slot.** A MIG may mark a composite *nicht
//!   benutzt*, but it cannot renumber the ones after it — `C502` is unused in
//!   `CCI` and still occupies element 2, which is why the Merkmal sits at
//!   element 3 (`CCI+15++BI1'`).
//! - **A composite may be restricted.** `C240` carries only DE 7037 and `C901`
//!   only DE 9321 in the BDEW profile; the directory defines more.
//! - **`STS` is polymorphic in its Statuskategorie.** DE 9015 selects whether
//!   `C555` or `C556` carries the value, and the MIG marks the other unused in
//!   each case. Each data element still resolves to exactly one slot, so one
//!   definition addresses all three — but *which* is populated is a runtime
//!   question. See [`crate::messages::segments::Sts::code`].
//! - **A composite may repeat.** `C556` occurs up to three times in `STS`
//!   (`STS+7++E01+ZW4+E03'`), so DE 9013 addresses the *first* occurrence and
//!   the later ones are read positionally.
//!
//! Positions are **one-based**, as `ElementRef`/`ComponentRef` require;
//! `element_slot`/`component_slot` return the zero-based index the accessors
//! use.
//!
//! Sources: BDEW EDI@Energy `UTILMD MIG Strom S2.2`, `UTILMD MIG Gas G1.2`,
//! `MSCONS MIG 2.5`, `ORDERS MIG 1.4c`, `INVOIC MIG 2.8e`, `APERAK MIG 2.2`.

use edifact_rs::{ComponentRef, ElementRef, SegmentDefinition, Status};

use Status::{Conditional as C, Mandatory as M};

// ── composite data elements ───────────────────────────────────────────────────

/// C002 — Dokumenten-/Nachrichtenname (`BGM`).
const C002: &[ComponentRef] = &[ComponentRef::new(1, "1001", C)];

/// C106 — Dokumenten-/Nachrichten-Identifikation (`BGM`).
const C106: &[ComponentRef] = &[ComponentRef::new(1, "1004", C)];

/// C507 — Datum/Uhrzeit/Zeitspanne (`DTM`).
const C507: &[ComponentRef] = &[
    ComponentRef::new(1, "2005", M),
    ComponentRef::new(2, "2380", C),
    ComponentRef::new(3, "2379", C),
];

/// C082 — Identifikation des Beteiligten (`NAD`).
const C082: &[ComponentRef] = &[
    ComponentRef::new(1, "3039", M),
    ComponentRef::new(2, "1131", C),
    ComponentRef::new(3, "3055", C),
];

/// C058 — Name und Anschrift (`NAD`). Five interchangeable free-text lines.
const C058: &[ComponentRef] = &[ComponentRef::repeated(1, "3124", C, 5)];

/// C080 — Name des Beteiligten (`NAD`).
/// C080 — Name des Beteiligten (`NAD`). Five interchangeable name components
/// and, after them, the **Namensformat** DE 3045 at component 6: `Z01` Struktur
/// von Personennamen, `Z02` Struktur der Firmenbezeichnung. It is what says
/// whether the five parts are (Nachname, Vorname, …) or a company name, and the
/// UTILMD AHB marks it Muss wherever a `NAD` carries a Kundenname.
const C080: &[ComponentRef] = &[
    ComponentRef::repeated(1, "3036", M, 5),
    ComponentRef::new(6, "3045", C),
];

/// C506 — Referenz (`RFF`).
const C506: &[ComponentRef] = &[
    ComponentRef::new(1, "1153", M),
    ComponentRef::new(2, "1154", C),
];

/// C206 — Identifikationsnummer (`IDE`).
const C206: &[ComponentRef] = &[ComponentRef::new(1, "7402", C)];

/// C517 — Ortsangabe (`LOC`).
const C517: &[ComponentRef] = &[
    ComponentRef::new(1, "3225", C),
    ComponentRef::new(2, "1131", C),
    ComponentRef::new(3, "3055", C),
    ComponentRef::new(4, "3224", C),
];

/// C901 — Anwendungsfehler (`ERC`). BDEW carries only DE 9321.
const C901: &[ComponentRef] = &[ComponentRef::new(1, "9321", M)];

/// C107 — Textreferenz (`FTX`).
const C107: &[ComponentRef] = &[ComponentRef::new(1, "4441", M)];

/// C108 — Text (`FTX`). Five interchangeable lines; the first carries the text.
const C108: &[ComponentRef] = &[ComponentRef::repeated(1, "4440", M, 5)];

/// C186 — Mengenangaben (`QTY`).
const C186: &[ComponentRef] = &[
    ComponentRef::new(1, "6063", M),
    ComponentRef::new(2, "6060", M),
    ComponentRef::new(3, "6411", C),
];

/// C212 — Waren-/Leistungsnummer (`PIA`).
const C212: &[ComponentRef] = &[
    ComponentRef::new(1, "7140", C),
    ComponentRef::new(2, "7143", C),
];

/// C502 — Einzelheiten zu Maßangaben (`CCI`). *Nicht benutzt*, keeps its slot.
const C502: &[ComponentRef] = &[ComponentRef::new(1, "6313", C)];

/// C240 — Merkmalsbeschreibung (`CCI`). BDEW carries only DE 7037.
const C240: &[ComponentRef] = &[ComponentRef::new(1, "7037", M)];

/// C601 — Statuskategorie (`STS`).
const C601: &[ComponentRef] = &[ComponentRef::new(1, "9015", M)];

/// C555 — Status (`STS`). Carries the value for Statuskategorie `Z18` / `10`.
const C555: &[ComponentRef] = &[ComponentRef::new(1, "4405", M)];

/// C556 — Statusanlass (`STS`). Carries the value for Statuskategorie `7` / `Z33`
/// and, on a Bestätigung or Ablehnung, the EBD Antwortcode of Statuskategorie
/// `E01`. DE 1131 names the Codeliste the code is drawn from — the EBD number:
/// `STS+E01++A01:E_0004'`.
const C556: &[ComponentRef] = &[
    ComponentRef::new(1, "9013", M),
    ComponentRef::new(2, "1131", C),
];

/// C056 — Abteilung oder Bearbeiter (`CTA`).
const C056: &[ComponentRef] = &[
    ComponentRef::new(1, "3413", C),
    ComponentRef::new(2, "3412", C),
];

/// C076 — Kommunikationsverbindung (`COM`).
const C076: &[ComponentRef] = &[
    ComponentRef::new(1, "3148", M),
    ComponentRef::new(2, "3155", M),
];

// ── segment definitions ───────────────────────────────────────────────────────

const BGM_ELEMENTS: &[ElementRef] = &[
    ElementRef::composite(1, "C002", C, 1, C002),
    ElementRef::composite(2, "C106", C, 1, C106),
    ElementRef::new(3, "1225", C, 1),
];
/// `BGM` — Beginn der Nachricht.
pub const BGM: SegmentDefinition =
    SegmentDefinition::new("BGM", "Beginn der Nachricht", BGM_ELEMENTS);

const DTM_ELEMENTS: &[ElementRef] = &[ElementRef::composite(1, "C507", M, 1, C507)];
/// `DTM` — Datum/Uhrzeit/Zeitspanne.
pub const DTM: SegmentDefinition =
    SegmentDefinition::new("DTM", "Datum/Uhrzeit/Zeitspanne", DTM_ELEMENTS);

const NAD_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "3035", M, 1),
    ElementRef::composite(2, "C082", C, 1, C082),
    ElementRef::composite(3, "C058", C, 1, C058),
    ElementRef::composite(4, "C080", C, 1, C080),
];
/// `NAD` — Name und Adresse.
pub const NAD: SegmentDefinition = SegmentDefinition::new("NAD", "Name und Adresse", NAD_ELEMENTS);

const RFF_ELEMENTS: &[ElementRef] = &[ElementRef::composite(1, "C506", M, 1, C506)];
/// `RFF` — Referenz.
pub const RFF: SegmentDefinition = SegmentDefinition::new("RFF", "Referenz", RFF_ELEMENTS);

const IDE_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "7495", M, 1),
    ElementRef::composite(2, "C206", C, 1, C206),
];
/// `IDE` — Identifikation.
pub const IDE: SegmentDefinition = SegmentDefinition::new("IDE", "Identifikation", IDE_ELEMENTS);

const LOC_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "3227", M, 1),
    ElementRef::composite(2, "C517", C, 1, C517),
];
/// `LOC` — Ortsangabe.
pub const LOC: SegmentDefinition = SegmentDefinition::new("LOC", "Ortsangabe", LOC_ELEMENTS);

const AJT_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "4465", M, 1),
    ElementRef::new(2, "1082", C, 1),
];
/// `AJT` — Einzelheiten zu einer Anpassung/Änderung.
///
/// Two plain data elements, both dense, so no `CANONICAL_ELEMENT_POSITIONS`
/// entry is needed. BDEW repurposes both: DE 4465 carries the **Code des
/// Prüfschritts** of an Entscheidungsbaum and DE 1082 (nominally a Positions-
/// nummer) the **EBD number** that code was published in — see e.g. ORDRSP
/// AHB 1.1b §4.15 and REMADV AHB 1.1 §4.2.
pub const AJT: SegmentDefinition = SegmentDefinition::new(
    "AJT",
    "Einzelheiten zu einer Anpassung/Änderung",
    AJT_ELEMENTS,
);

const ERC_ELEMENTS: &[ElementRef] = &[ElementRef::composite(1, "C901", M, 1, C901)];
/// `ERC` — Anwendungsfehler.
pub const ERC: SegmentDefinition = SegmentDefinition::new("ERC", "Anwendungsfehler", ERC_ELEMENTS);

const FTX_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "4451", M, 1),
    ElementRef::new(2, "4453", C, 1),
    ElementRef::composite(3, "C107", C, 1, C107),
    ElementRef::composite(4, "C108", C, 1, C108),
];
/// `FTX` — Freier Text. The text is `C108` at element 4 (`FTX+AAO+++Text'`).
pub const FTX: SegmentDefinition = SegmentDefinition::new("FTX", "Freier Text", FTX_ELEMENTS);

const QTY_ELEMENTS: &[ElementRef] = &[ElementRef::composite(1, "C186", M, 1, C186)];
/// `QTY` — Mengenangaben.
pub const QTY: SegmentDefinition = SegmentDefinition::new("QTY", "Mengenangaben", QTY_ELEMENTS);

const LIN_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "1082", C, 1),
    ElementRef::new(2, "1229", C, 1),
];
/// `LIN` — Positionsdaten.
pub const LIN: SegmentDefinition = SegmentDefinition::new("LIN", "Positionsdaten", LIN_ELEMENTS);

const PIA_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "4347", M, 1),
    ElementRef::composite(2, "C212", M, 1, C212),
];
/// `PIA` — Zusätzliche Produkt-ID.
pub const PIA: SegmentDefinition =
    SegmentDefinition::new("PIA", "Zusätzliche Produkt-ID", PIA_ELEMENTS);

const CCI_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "7059", C, 1),
    ElementRef::composite(2, "C502", C, 1, C502),
    ElementRef::composite(3, "C240", C, 1, C240),
    // DE 4051 „Relevanz des Merkmals, Code" — element **4**, not 3. It is the
    // only element `CCI+Z65` („Umsetzungsgradvorgabe des Produktpakets") uses,
    // so its encoding is `CCI+Z65+++Z01` with C502 and C240 both empty. A
    // three-element definition rejected that message as „CCI has 4 elements,
    // expected between 0 and 3".
    ElementRef::new(4, "4051", C, 1),
];
/// `CCI` — Merkmal/Klasse. `C502` is unused and still occupies element 2.
pub const CCI: SegmentDefinition = SegmentDefinition::new("CCI", "Merkmal/Klasse", CCI_ELEMENTS);

/// C889 — Merkmalswert (`CAV`). `1131` and `3055` are *nicht benutzt* in the
/// BDEW profile and keep their slots, so the value sits at component **4**:
/// `CAV+ZV4:::11XBK-STD-----9`.
const C889: &[ComponentRef] = &[
    ComponentRef::new(1, "7111", C),
    ComponentRef::new(2, "1131", C),
    ComponentRef::new(3, "3055", C),
    ComponentRef::new(4, "7110", C),
];

const CAV_ELEMENTS: &[ElementRef] = &[ElementRef::composite(1, "C889", M, 1, C889)];
/// `CAV` — Merkmalswert. Carries the value of the `CCI` it follows.
pub const CAV: SegmentDefinition = SegmentDefinition::new("CAV", "Merkmalswert", CAV_ELEMENTS);

/// C286 — Information über eine Folge (`SEQ`). BDEW carries only DE 1050.
const C286: &[ComponentRef] = &[ComponentRef::new(1, "1050", M)];

const SEQ_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "1245", C, 1),
    ElementRef::composite(2, "C286", C, 1, C286),
];
/// `SEQ` — Folgeangaben. Opens a `SG8`: `SEQ+Z79+1`.
pub const SEQ: SegmentDefinition = SegmentDefinition::new("SEQ", "Folgeangaben", SEQ_ELEMENTS);

const STS_ELEMENTS: &[ElementRef] = &[
    ElementRef::composite(1, "C601", C, 1, C601),
    ElementRef::composite(2, "C555", C, 1, C555),
    // `C556` occupies three consecutive *element positions* under
    // Statuskategorie `7`: Transaktionsgrund, Ergänzung and Transaktionsgrund
    // für das Lieferende einer befristeten Anmeldung — `STS+7++E01+ZW4+E03'`,
    // the MIG's own example. Only the first is code-addressable (a data element
    // declared at two positions cannot be resolved by code); elements 4 and 5
    // are read positionally by `UtilmdTransaction::transaktionsgrund`.
    ElementRef::composite(3, "C556", C, 1, C556),
];
/// `STS` — Status. Polymorphic in DE 9015; see the module docs.
pub const STS: SegmentDefinition = SegmentDefinition::new("STS", "Status", STS_ELEMENTS);

const CTA_ELEMENTS: &[ElementRef] = &[
    ElementRef::new(1, "3139", C, 1),
    ElementRef::composite(2, "C056", C, 1, C056),
];
/// `CTA` — Ansprechpartner.
pub const CTA: SegmentDefinition = SegmentDefinition::new("CTA", "Ansprechpartner", CTA_ELEMENTS);

const COM_ELEMENTS: &[ElementRef] = &[ElementRef::composite(1, "C076", M, 1, C076)];
/// `COM` — Kommunikationsverbindung.
pub const COM: SegmentDefinition =
    SegmentDefinition::new("COM", "Kommunikationsverbindung", COM_ELEMENTS);