====== 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).