====== std.edi — EDIFACT / B2B-Datenaustausch ====== **14 Units** für den elektronischen Datenaustausch in Handel, Logistik, Seefracht und Finanzwesen — vom Preiskatalog über Bestellung, Lieferschein und Rechnung bis zur Zoll- und Containermeldung. Alles baut auf ''std.edi.core'' auf; kein externes EDIFACT-Framework, kein fremder Parser. → [[lyx_-_programmiersprache:start|Übersicht]] · [[lyx_-_programmiersprache:units|Standard Library]] · [[lyx_-_programmiersprache:guides:edi|Guide: EDI in der Praxis]] · [[lyx_-_programmiersprache:guides:welche-unit|Welche Unit?]] Alle Beispiele dieser Seite sind mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; die gezeigten Ausgaben sind echte Programmausgaben. ---- ===== Was ist EDIFACT? ===== **UN/EDIFACT** ist der internationale Standard für den Datenaustausch zwischen Unternehmen. Eine Nachricht ist reiner Text aus **Segmenten** — ein Drei-Buchstaben-Tag, dann Datenelemente, abgeschlossen mit dem Segmentzeichen: UNA:+.? 'UNB+UNOA:1+4012345000009+4098765000004+20260813:0930+IC000042'UNH+1+ORDERS:D:01B:UN'...'UNT+2+1'UNZ+1+IC000042' Die Hierarchie ist dreistufig — Umschlag, optionale Gruppe, Nachricht: ^ Ebene ^ Öffnet ^ Schließt ^ Inhalt ^ | Interchange (Umschlag) | UNB | UNZ | Absender, Empfänger, Datum, Interchange-Referenz | | Gruppe (optional) | UNG | UNE | fasst gleichartige Nachrichten zusammen | | Nachricht | UNH | UNT | eine Geschäftsnachricht, z. B. ORDERS | Vorangestellt werden darf ein **UNA**-Segment: neun Bytes, die die Trennzeichen bekanntgeben. Fehlt es, gelten die UNOA-Standardwerte. ^ Rolle ^ Zeichen ^ Code ^ Konstante ^ | Komponententrenner | '':'' | 58 | ''EDI_DEF_COMP'' | | Datenelementtrenner | ''+'' | 43 | ''EDI_DEF_DATA'' | | Dezimalzeichen | ''.'' | 46 | ''EDI_DEF_DEC'' | | Escape (Release) | ''?'' | 63 | ''EDI_DEF_REL'' | | Segmentende | ''%%'%%'' | 39 | ''EDI_DEF_SEG'' | Zeichensätze: ''EDI_CHARSET_UNOA'' (A–Z, 0–9, wenige Sonderzeichen — Vorgabe), ''EDI_CHARSET_UNOB'' (ISO-8859-1), ''EDI_CHARSET_UNOC'' (UTF-8). ---- ===== Units ===== ^ Unit ^ Nachrichten ^ Beschreibung ^ | [[lyx_-_programmiersprache:units:edi:core|std.edi.core]] | UNA/UNB/UNZ/UNG/UNE/UNH/UNT | Umschlag, Segment-Tokenizer, Escape-Behandlung, Sequenznummern, Duplikatserkennung | | [[lyx_-_programmiersprache:units:edi:messages|std.edi.messages]] | CONTRL, APERAK, GENRAL | Quittung, Fehlermeldung, freie Nachricht | | [[lyx_-_programmiersprache:units:edi:catalog|std.edi.catalog]] | PRICAT, PARTIN | Preiskatalog, Partnerstammdaten; GTIN-/GLN-Prüfziffern | | [[lyx_-_programmiersprache:units:edi:partner|std.edi.partner]] | AS2/SFTP-Profile | Partner-Registry mit Nachrichtentyp-Bitmaske | | [[lyx_-_programmiersprache:units:edi:rfq|std.edi.rfq]] | REQOTE, QUOTES | Preisanfrage und Angebot | | [[lyx_-_programmiersprache:units:edi:orders|std.edi.orders]] | ORDERS, ORDRSP, ORDCHG | Bestellung, Auftragsbestätigung, Bestelländerung | | [[lyx_-_programmiersprache:units:edi:invoice|std.edi.invoice]] | INVOIC, INVCON, COMDIS, COACSU | Rechnung, Rechnungsbestätigung, Disput, Saldenabstimmung | | [[lyx_-_programmiersprache:units:edi:shipping|std.edi.shipping]] | DESADV, RECADV | Lieferschein mit SSCC-Hierarchie, Wareneingang | | [[lyx_-_programmiersprache:units:edi:payment|std.edi.payment]] | REMADV, PAYMUL, DIRDEB u. a. (16 Typen) | Zahlungsverkehr; IBAN-/BIC-Prüfung | | [[lyx_-_programmiersprache:units:edi:inventory|std.edi.inventory]] | INVRPT, SLSRPT, SLSFCT | Lagerbestand, Absatzbericht, Absatzprognose | | [[lyx_-_programmiersprache:units:edi:delfor|std.edi.delfor]] | DELFOR, DELJIT | Lieferabruf und JIT-Feinabruf | | [[lyx_-_programmiersprache:units:edi:customs|std.edi.customs]] | CUSCAR, CUSDEC, CUSEXP, CUSRSP | Zoll: Frachtanmeldung, Zollanmeldung, Behördenantwort | | [[lyx_-_programmiersprache:units:edi:seafreight|std.edi.seafreight]] | BAPLIE, MOVINS, COPARN, CODECO, COARRI, COPINO, COPRAR, VERMAS, HANMOV | Seefracht: Stauplan, Containermeldungen, VGM (SOLAS) | | [[lyx_-_programmiersprache:units:edi:transport|std.edi.transport]] | IFTMIN, IFTMBC, IFTMCS, IFTMAN, IFCSUM, IFTSTA, IFTFCC, IFTDGN | Multimodaler Transport: Auftrag, Status, Gefahrgut | Jede Fachunit zieht ''std.edi.core'' mit — ''import std.edi.orders;'' genügt. ---- ===== Designprinzipien ===== * **Zero-Copy-Parsing** — Lesefunktionen setzen Zeiger in den Originalpuffer. Jedes Textfeld ist ein Paar aus Zeiger (''…_PTR'' bzw. ''…_GTIN'') und Länge (''…LEN''); der Puffer muss leben, solange die Struct benutzt wird. * **Caller alloziert** — jede Struct-Größe ist als ''EDI_*_SIZE'' exportiert. Der Aufrufer legt an, nullt und gibt frei. * **Festkomma statt Fließkomma** — Beträge, Preise und Steuersätze sind ''int64 × 100'': ''1999'' = 19,99 €, ''1900'' = 19,00 % Steuer. Kein ''f64'' im Zahlungsverkehr. * **Prüfziffern eingebaut** — GTIN (8/12/13/14), GLN (13), SSCC (18), IBAN (MOD-97), BIC (8/11), ISO-6346-Container, UN-Gefahrgutnummer. * **Kein Netzwerk** — Nachrichten sind Puffer. AS2, SFTP oder OFTP2 laufen über ''std.net''; ''std.edi.partner'' verwaltet nur die Verbindungsparameter. ---- ===== Schnellstart — Interchange schreiben und wieder einlesen ===== import std.edi.core; import std.alloc; import std.string; // Zero-Copy-Felder sind nicht nullterminiert: terminierte Kopie fuer die Ausgabe. fn Feld(ptr: int64, len: int64): pchar { var b: int64 := alloc(len + 1); var i: int64 := 0; while (i < len) { poke8(b + i, peek8(ptr + i)); i := i + 1; } poke8(b + len, 0); return b as pchar; } fn main(): int64 { var ctx: int64 := alloc(EDI_CTX_SIZE); EdiCtxInit(ctx); poke64(ctx + EDI_CTX_SENDER, "4012345000009"c as int64); poke64(ctx + EDI_CTX_SNDLEN, 13); poke64(ctx + EDI_CTX_RCVR, "4098765000004"c as int64); poke64(ctx + EDI_CTX_RCVLEN, 13); poke64(ctx + EDI_CTX_ICREF, "IC000042"c as int64); poke64(ctx + EDI_CTX_ICREFLEN, 8); poke64(ctx + EDI_CTX_DATE, "20260813"c as int64); poke64(ctx + EDI_CTX_TIME, "0930"c as int64); var out: int64 := alloc(4096); var pos: int64 := 0; pos += EdiUnaWrite(ctx, out + pos, 4096 - pos); pos += EdiUnbWrite(ctx, out + pos, 4096 - pos); pos += EdiUnhWrite(ctx, "1"c, "ORDERS"c, out + pos, 4096 - pos); // ... hier stehen die Fachsegmente, z. B. aus EdiOrdersWrite ... pos += EdiUntWrite(ctx, 2, "1"c, out + pos, 4096 - pos); pos += EdiUnzWrite(ctx, 1, out + pos, 4096 - pos); PrintLn(Feld(out, pos)); // Wieder einlesen: UNA gibt die Trennzeichen vor var una: int64 := alloc(EDI_UNA_SIZE); var start: int64 := 0; if (EdiUnaRead(out, pos, una) == EDI_OK) { start := 9; } var sctx: int64 := alloc(EDI_SEGCTX_SIZE); EdiSegCtxInit(sctx, una, start); var seg: int64 := alloc(EDI_SEG_SIZE); // EdiUnbRead erwartet GENAU ein Segment - erst mit dem Iterator holen. var ctx2: int64 := alloc(EDI_CTX_SIZE); EdiCtxInit(ctx2); if (EdiSegmentNext(out, pos, sctx, seg) == 1) { EdiUnbRead(peek64(seg + EDI_SEG_PTR), peek64(seg + EDI_SEG_LEN), ctx2); } PrintLn(StrConcat("Absender: ", Feld(peek64(ctx2 + EDI_CTX_SENDER), peek64(ctx2 + EDI_CTX_SNDLEN)))); PrintLn(StrConcat("ICR: ", Feld(peek64(ctx2 + EDI_CTX_ICREF), peek64(ctx2 + EDI_CTX_ICREFLEN)))); var n: int64 := 1; while (EdiSegmentNext(out, pos, sctx, seg) == 1) { n += 1; PrintLn(StrConcat(" Segment: ", Feld(peek64(seg + EDI_SEG_PTR), 3))); } PrintLn(StrConcat("Segmente: ", IntToStr(n))); return 0; } UNA:+.? 'UNB+UNOA:1+4012345000009+4098765000004+20260813:0930+IC000042'UNH+1+ORDERS:D:01B:UN'UNT+2+1'UNZ+1+IC000042' Absender: 4012345000009 ICR: IC000042 Segment: UNH Segment: UNT Segment: UNZ Segmente: 4 Die Fachunits erzeugen **nur den Nachrichtenrumpf** (BGM, DTM, NAD, LIN …). Der Umschlag kommt immer aus ''std.edi.core'': UNA + UNB + UNH davor, UNT + UNZ dahinter. ---- ===== Prüfziffern über alle Units ===== import std.edi.catalog; import std.edi.shipping; import std.edi.payment; import std.edi.seafreight; import std.edi.transport; import std.string; import std.alloc; fn J(v: int64): pchar { if (v == 1) { return "gueltig"; } return "ungueltig"; } fn main(): int64 { PrintLn(StrConcat("GTIN-13 4012345678901: ", J(EdiGtinCheck("4012345678901"c, 13)))); PrintLn(StrConcat("GLN 4012345000009: ", J(EdiGlnCheck("4012345000009"c, 13)))); var sscc: int64 := alloc(19); EdiSsccGenerate("34012345000000001"c, sscc); PrintLn(StrConcat(StrConcat("SSCC-18 ", sscc as pchar), StrConcat(": ", J(EdiSsccCheck(sscc as pchar, 18))))); PrintLn(StrConcat("IBAN DE89370400440532013000: ", J(EdiIbanCheck("DE89370400440532013000"c, 22)))); PrintLn(StrConcat("BIC COBADEFFXXX: ", J(EdiBicCheck("COBADEFFXXX"c, 11)))); PrintLn(StrConcat("Container MSKU1234565: ", J(EdiContainerCheck("MSKU1234565"c, 11)))); PrintLn(StrConcat("UN-Nummer 1203: ", J(EdiUnNumberCheck("1203"c, 4)))); return 0; } GTIN-13 4012345678901: gueltig GLN 4012345000009: gueltig SSCC-18 340123450000000017: gueltig IBAN DE89370400440532013000: gueltig BIC COBADEFFXXX: gueltig Container MSKU1234565: gueltig UN-Nummer 1203: gueltig ^ Prüfung ^ Unit ^ Länge ^ Verfahren ^ | ''EdiGtinCheck'' | catalog | 8, 12, 13 oder 14 | GS1 Mod-10 | | ''EdiGlnCheck'' | catalog | genau 13 | GS1 Mod-10 | | ''EdiSsccCheck'' / ''EdiSsccGenerate'' | shipping | 18 (Generator: 17 → 18) | GS1 Mod-10 | | ''EdiIbanCheck'' | payment | ≥ 4 | ISO 13616, MOD-97 | | ''EdiBicCheck'' | payment | 8 oder 11 | Formatprüfung ISO 9362 | | ''EdiContainerCheck'' | seafreight | 11 | ISO 6346 | | ''EdiUnNumberCheck'' | transport | genau 4 | UN-Gefahrgutnummer | Alle liefern ''1'' = gültig, ''0'' = ungültig. **Keine Write-Funktion prüft von sich aus** — Prüfziffern sind Sache des Aufrufers. ---- ===== Fehlercodes ===== ^ Konstante ^ Wert ^ Bedeutung ^ | ''EDI_OK'' | 0 | erfolgreich | | ''EDI_ERR_TRUNC'' | 1 | Nachricht/Puffer abgeschnitten | | ''EDI_ERR_NOUNA'' | 2 | kein UNA-Segment — Standardtrennzeichen gelten | | ''EDI_ERR_SYNTAX'' | 3 | Segment nicht lesbar | | ''EDI_ERR_DUP'' | 4 | Interchange-Referenz schon gesehen | | ''EDI_ERR_OVERFLOW'' | 5 | Zielpuffer zu klein / Partner-Slots voll | | ''EDI_ERR_PARTNER'' | 6 | Partner nicht gefunden | ''EDI_ERR_NOUNA'' ist **kein Fehler im engeren Sinn**: fehlt UNA, füllt ''EdiUnaRead'' die Standardtrennzeichen ein und die Verarbeitung läuft weiter — nur der Startversatz ist dann 0 statt 9. Die ''…Write''-Funktionen melden Überlauf mit **''-1''**; ihr Rückgabewert ist sonst die Zahl geschriebener Bytes. ---- ===== Typische Nachrichtenflüsse ===== **Beschaffung** Käufer Lieferant REQOTE (Anfrage) → ← QUOTES (Angebot) ORDERS (Bestellung) → ← ORDRSP (Bestaetigung) ORDCHG (Aenderung) → ← DESADV (Lieferavis, SSCC) RECADV (Wareneingang) → ← INVOIC (Rechnung) REMADV (Zahlungsavis) → **Seefracht-Container** Verlader Terminal / Carrier COPARN (Anmeldung) → ← CODECO (Gate-In / Gate-Out) VERMAS (VGM, SOLAS) → ← BAPLIE (Stauplan) ← COARRI (Ankunft) ← COPINO (Abholfreigabe) **Quittungen** — auf jede eingehende Nachricht antwortet CONTRL (Syntaxquittung) oder APERAK (fachliche Fehlermeldung), siehe [[lyx_-_programmiersprache:units:edi:messages|std.edi.messages]]. ---- ===== Fallstricke ===== * **''EdiUnbRead'' will genau ein Segment.** Wer den ganzen Interchange übergibt, bekommt zu lange Felder zurück (die Interchange-Referenz enthält dann das nächste Segment). Erst ''EdiSegmentNext'', dann ''EdiUnbRead'' mit ''EDI_SEG_PTR''/''EDI_SEG_LEN''. * **''msgRef'' und ''msgType'' sind Zeiger, keine Zahlen.** ''EdiUnhWrite(ctx, "1"c, "ORDERS"c, …)'' — eine echte Zahl an dieser Stelle führt zum Absturz, weil die Funktion sie als Adresse liest. Dasselbe gilt für ''msgRef'' in ''EdiUntWrite'' und ''grpRef''/''msgType'' in ''EdiUngWrite''. * **Startversatz nicht vergessen.** Mit UNA beginnt der Nachrichtenteil bei Byte 9, ohne UNA bei 0. Der Rückgabewert von ''EdiUnaRead'' sagt, welcher Fall vorliegt. * **Beträge sind ×100.** ''MOA+77:776.76'' kommt als ''77676'' zurück. Wer den Wert direkt anzeigt, zeigt das Hundertfache. * **Duplikatsring ist begrenzt**: 16 Partner × 128 Interchange-Referenzen. Darüber hinaus gilt eine alte Referenz wieder als neu — für die Dauerhaltung eine eigene Tabelle führen. * **Zeiger überleben den Puffer nicht.** Wird der Empfangspuffer wiederverwendet, zeigen alle Struct-Felder ins Leere. ---- **Weiterführend:** [[lyx_-_programmiersprache:guides:edi|Guide: EDI in der Praxis]] · [[lyx_-_programmiersprache:units:edi:core|std.edi.core]] · [[lyx_-_programmiersprache:units:net|std.net]] · [[lyx_-_programmiersprache:sprache:rohspeicher|Rohspeicher]] ===== Quelldateien ===== Alle Units liegen unter ''std/edi/'': ''core.lyx'', ''messages.lyx'', ''catalog.lyx'', ''partner.lyx'', ''rfq.lyx'', ''orders.lyx'', ''invoice.lyx'', ''shipping.lyx'', ''payment.lyx'', ''inventory.lyx'', ''delfor.lyx'', ''customs.lyx'', ''seafreight.lyx'', ''transport.lyx''. Letzte Aktualisierung: 2026-08-13 — Trennzeichen-Tabelle repariert (war eine einspaltige Liste ohne Kopf), Schnellstart durch ein lauffähiges Programm ersetzt (die alten Aufrufe hatten falsche Signaturen), Umschlag-Hierarchie, Prüfziffer-Übersicht, Fehlercodes und Fallstricke ergänzt. Geprüft gegen ''std/edi/*.lyx'' und ''lyxc 1.0.17K''. Codebeispiele geprüft: gegen **lyxc 1.2.5C** übersetzt (Prüflauf 2026-09-08 über die gesamte Doku: 574 Vollprogramme, 0 echte Fehler; zusätzlich 5159 Aufrufe gegen die ''pub fn''-Signaturen in ''aurum/std'' gehalten, 0 Abweichungen).