====== std.edi.invoice ====== Fakturierung per EDIFACT: Rechnungen, Gutschriften, Streitfälle und Kontoauszüge. ''EdiInvoicWrite'' berechnet Netto-, Steuer- und Bruttosummen aus den Positionen selbst — Summenwerte im Header werden ignoriert und überschrieben. * **INVOIC** (BGM 380=Rechnung, 381=Gutschrift, 383=Belastung, 386=Vorauszahlungsanforderung) * **INVCON** (BGM 44) — Dauerrechnung / Rahmenvertrag * **COMDIS** (BGM 34) — Handelsstreit mit Freitext-Begründung * **COACSU** (BGM 36) — Kontoauszug (Netto- und Bruttosaldo) Steuertypen: ^ Konstante ^ Code ^ Satz ^ | ''EDI_INVOIC_TAX_S'' | S | 19 % Standard | | ''EDI_INVOIC_TAX_AA'' | AA | 7 % Ermäßigt | | ''EDI_INVOIC_TAX_Z'' | Z | 0 % Nullsatz | | ''EDI_INVOIC_TAX_E'' | E | 0 % Steuerfrei | import std.edi.invoice; import std.alloc; import std.string; fn Feld(p: int64, l: int64): pchar { var b: int64 := alloc(l + 1); var i: int64 := 0; while (i < l) { poke8(b + i, peek8(p + i)); i := i + 1; } poke8(b + l, 0); return b as pchar; } // Betrag in Cent als "123.45" ausgeben fn Geld(cent: int64): pchar { var r: int64 := cent - (cent / 100) * 100; var nk: pchar := IntToStr(r); if (r < 10) { nk := StrConcat("0", nk); } return StrConcat(StrConcat(IntToStr(cent / 100), ","), nk); } fn main(): int64 { var hdr: int64 := alloc(EDI_INVOIC_HDR_SIZE); var i: int64 := 0; while (i < EDI_INVOIC_HDR_SIZE) { poke8(hdr + i, 0); i := i + 1; } poke64(hdr + EDI_INVOIC_HDR_NUM, "RE-2026-4711"c as int64); poke64(hdr + EDI_INVOIC_HDR_NUMLEN, 12); poke64(hdr + EDI_INVOIC_HDR_DATE, "20260813"c as int64); poke64(hdr + EDI_INVOIC_HDR_DATELEN, 8); poke64(hdr + EDI_INVOIC_HDR_BGMQUAL, 380); // 380 = Rechnung poke64(hdr + EDI_INVOIC_HDR_SUPPLIER, "4098765000004"c as int64); poke64(hdr + EDI_INVOIC_HDR_SUPLEN, 13); poke64(hdr + EDI_INVOIC_HDR_BUYER, "4012345000009"c as int64); poke64(hdr + EDI_INVOIC_HDR_BUYLEN, 13); poke64(hdr + EDI_INVOIC_HDR_CURRENCY, "EUR"c as int64); poke64(hdr + EDI_INVOIC_HDR_CURLEN, 3); var lines: int64 := alloc(2 * EDI_INVOIC_LINE_SIZE); i := 0; while (i < 2 * EDI_INVOIC_LINE_SIZE) { poke8(lines + i, 0); i := i + 1; } poke64(lines + EDI_INVOIC_LINE_GTIN, "04012345678901"c as int64); poke64(lines + EDI_INVOIC_LINE_GTINLEN, 14); poke64(lines + EDI_INVOIC_LINE_QTY, 24); poke64(lines + EDI_INVOIC_LINE_PRICE, 1999); poke64(lines + EDI_INVOIC_LINE_NETAMT, 24 * 1999); poke64(lines + EDI_INVOIC_LINE_TAXTYPE, EDI_INVOIC_TAX_S); poke64(lines + EDI_INVOIC_LINE_LINENO, 1); var l1: int64 := lines + EDI_INVOIC_LINE_SIZE; poke64(l1 + EDI_INVOIC_LINE_GTIN, "04012345678918"c as int64); poke64(l1 + EDI_INVOIC_LINE_GTINLEN, 14); poke64(l1 + EDI_INVOIC_LINE_QTY, 6); poke64(l1 + EDI_INVOIC_LINE_PRICE, 4950); poke64(l1 + EDI_INVOIC_LINE_NETAMT, 6 * 4950); poke64(l1 + EDI_INVOIC_LINE_TAXTYPE, EDI_INVOIC_TAX_AA); poke64(l1 + EDI_INVOIC_LINE_LINENO, 2); var out: int64 := alloc(4096); var n: int64 := EdiInvoicWrite(hdr, lines, 2, out, 4096); PrintLn(Feld(out, n)); var hdr2: int64 := alloc(EDI_INVOIC_HDR_SIZE); var lin2: int64 := alloc(8 * EDI_INVOIC_LINE_SIZE); var cnt: int64 := EdiInvoicRead(out, n, hdr2, lin2, 8); PrintLn(StrConcat("Positionen: ", IntToStr(cnt))); PrintLn(StrConcat("Netto: ", Geld(peek64(hdr2 + EDI_INVOIC_HDR_NETTOTAL)))); PrintLn(StrConcat("Steuer: ", Geld(peek64(hdr2 + EDI_INVOIC_HDR_TAXTOTAL)))); PrintLn(StrConcat("Brutto: ", Geld(peek64(hdr2 + EDI_INVOIC_HDR_GROSSTOTAL)))); PrintLn(StrConcat("Validierung: ", IntToStr(EdiInvoicValidate(hdr2, lin2, cnt)))); return 0; } BGM+380+RE-2026-4711+9'DTM+137:20260813:102'NAD+SU+4098765000004::9'NAD+BY+4012345000009::9'CUX+2:EUR:4'LIN+1++04012345678901:SRV'QTY+47:24'PRI+AAA:19.99:CP'MOA+203:479.76'TAX+7+VAT+++:::S'LIN+2++04012345678918:SRV'QTY+47:6'PRI+AAA:49.50:CP'MOA+203:297.00'TAX+7+VAT+++:::AA'UNS+S'TAX+7+VAT+++:::S'MOA+125:479.76'MOA+124:91.15'TAX+7+VAT+++:::AA'MOA+125:297.00'MOA+124:20.79'MOA+77:776.76'MOA+176:111.94'MOA+9:888.70'CNT+2:2' Positionen: 2 Netto: 776,76 Steuer: 111,94 Brutto: 888,70 Validierung: 0 Die Steuer wird **je Steuersatz getrennt** ausgewiesen (''MOA+125'' Nettoteilbetrag, ''MOA+124'' Steuerbetrag), erst danach folgen die Gesamtsummen ''MOA+77'' (netto), ''MOA+176'' (Steuer) und ''MOA+9'' (brutto). ---- ===== Imports ===== * ''std.edi.core'' * ''std.alloc'' ---- ===== Structs ===== ==== EdiInvoicHdr (EDI_INVOIC_HDR_SIZE = 112 Bytes) ==== ^ Offset-Konstante ^ Inhalt ^ | ''EDI_INVOIC_HDR_NUM / EDI_INVOIC_HDR_NUMLEN'' | Rechnungsnummer | | ''EDI_INVOIC_HDR_DATE / EDI_INVOIC_HDR_DATELEN'' | Rechnungsdatum | | ''EDI_INVOIC_HDR_BGMQUAL'' | BGM-Qualifier: 380=Rechnung, 381=Gutschrift, 383=Belastung, 386=Vorauszahlung | | ''EDI_INVOIC_HDR_SUPPLIER / EDI_INVOIC_HDR_SUPLEN'' | NAD+SU Lieferant (Zeiger/Länge) | | ''EDI_INVOIC_HDR_BUYER / EDI_INVOIC_HDR_BUYLEN'' | NAD+BY Käufer | | ''EDI_INVOIC_HDR_CURRENCY / EDI_INVOIC_HDR_CURLEN'' | Währungscode (z. B. "EUR") | | ''EDI_INVOIC_HDR_NETTOTAL'' | Nettobetrag × 100 (MOA+77) — wird von Write neu berechnet | | ''EDI_INVOIC_HDR_TAXTOTAL'' | Steuerbetrag × 100 (MOA+176) — wird von Write neu berechnet | | ''EDI_INVOIC_HDR_GROSSTOTAL'' | Bruttobetrag × 100 (MOA+9) — wird von Write neu berechnet | ==== EdiInvoicLine (EDI_INVOIC_LINE_SIZE = 56 Bytes) ==== ^ Offset-Konstante ^ Inhalt ^ | ''EDI_INVOIC_LINE_GTIN / EDI_INVOIC_LINE_GTINLEN'' | GTIN-Zeiger und -Länge | | ''EDI_INVOIC_LINE_QTY'' | Menge | | ''EDI_INVOIC_LINE_PRICE'' | Einzelpreis × 100 | | ''EDI_INVOIC_LINE_NETAMT'' | Zeilenbetrag × 100 (Qty × Price) | | ''EDI_INVOIC_LINE_TAXTYPE'' | Steuertyp: ''EDI_INVOIC_TAX_*'' | | ''EDI_INVOIC_LINE_LINENO'' | Zeilennummer | ==== EdiInvconHdr (EDI_INVCON_HDR_SIZE = 72 Bytes) ==== ^ Offset-Konstante ^ Inhalt ^ | ''EDI_INVCON_HDR_CONTRACTNUM / EDI_INVCON_HDR_NUMLEN'' | Vertragsnummer | | ''EDI_INVCON_HDR_DATE / EDI_INVCON_HDR_DATELEN'' | Vertrags-/Abrechnungsdatum | | ''EDI_INVCON_HDR_INVOICEREF / EDI_INVCON_HDR_REFLEN'' | Referenz auf Ausgangsrechnung | | ''EDI_INVCON_HDR_AMOUNT / EDI_INVCON_HDR_CURRENCY'' | Betrag × 100 + Währungszeiger | ==== EdiComdisHdr (EDI_COMDIS_HDR_SIZE = 72 Bytes) ==== ^ Offset-Konstante ^ Inhalt ^ | ''EDI_COMDIS_HDR_DISPUTEREF'' | Streitfall-Referenz | | ''EDI_COMDIS_HDR_INVOICEREF'' | Bezogene Rechnungsnummer | | ''EDI_COMDIS_HDR_AMOUNT'' | Strittiger Betrag × 100 | | ''EDI_COMDIS_HDR_REASON / EDI_COMDIS_HDR_REASONLEN'' | Freitext-Begründung | ==== EdiCoacsuHdr (EDI_COACSU_HDR_SIZE = 64 Bytes) ==== ^ Offset-Konstante ^ Inhalt ^ | ''EDI_COACSU_HDR_ACCOUNTREF'' | Konto-Referenz | | ''EDI_COACSU_HDR_NETBALANCE'' | Nettosaldo × 100 | | ''EDI_COACSU_HDR_GROSSBALANCE'' | Bruttosaldo × 100 | | ''EDI_COACSU_HDR_CURRENCY'' | Währungszeiger | ---- ===== Funktionen ===== ==== INVOIC ==== ^ Signatur ^ Beschreibung ^ | ''EdiInvoicWrite(hdr: int64, lines: int64, lineCount: int64, out: int64, outMax: int64): int64'' | Schreibt INVOIC. Netto/Steuer/Brutto werden aus den Positionen neu berechnet. | | ''EdiInvoicRead(buf: int64, bufLen: int64, hdr: int64, lines: int64, maxLines: int64): int64'' | Parst INVOIC; gibt Zeilenanzahl zurück. | | ''EdiInvoicValidate(hdr: int64, lines: int64, lineCount: int64): int64'' | Prüft Berechnungsintegrität. Gibt 0=ok, 1=Nettofehler, 3=Bruttofehler. Toleranz 1 Cent. | ==== INVCON ==== ^ Signatur ^ Beschreibung ^ | ''EdiInvconWrite(hdr: int64, out: int64, outMax: int64): int64'' | Schreibt INVCON (Dauerrechnung) | | ''EdiInvconRead(buf: int64, bufLen: int64, hdr: int64): int64'' | Parst INVCON; gibt 1=ok, 0=Fehler | ==== COMDIS ==== ^ Signatur ^ Beschreibung ^ | ''EdiComdisWrite(hdr: int64, out: int64, outMax: int64): int64'' | Schreibt COMDIS (Handelsstreit) | | ''EdiComdisRead(buf: int64, bufLen: int64, hdr: int64): int64'' | Parst COMDIS | ==== COACSU ==== ^ Signatur ^ Beschreibung ^ | ''EdiCoacsuWrite(hdr: int64, out: int64, outMax: int64): int64'' | Schreibt COACSU (Kontoauszug) | | ''EdiCoacsuRead(buf: int64, bufLen: int64, hdr: int64): int64'' | Parst COACSU | ---- ===== Codebeispiel — eingehende Rechnung prüfen ===== import std.edi.invoice; import std.alloc; import std.string; fn main(): int64 { var msg: pchar := "BGM+380+RE-2026-4711+9'DTM+137:20260813:102'NAD+SU+4098765000004::9'NAD+BY+4012345000009::9'CUX+2:EUR:4'LIN+1++04012345678901:SRV'QTY+47:24'PRI+AAA:19.99:CP'MOA+203:479.76'TAX+7+VAT+++:::S'UNS+S'MOA+77:479.76'MOA+176:91.15'MOA+9:570.91'CNT+1:1'"c; var hdr: int64 := alloc(EDI_INVOIC_HDR_SIZE); var lines: int64 := alloc(64 * EDI_INVOIC_LINE_SIZE); var count: int64 := EdiInvoicRead(msg as int64, StrLen(msg), hdr, lines, 64); var rc: int64 := EdiInvoicValidate(hdr, lines, count); if (rc == 0) { PrintLn(StrConcat("Rechnung rechnerisch korrekt, Positionen: ", IntToStr(count))); PrintLn(StrConcat("Brutto in Cent: ", IntToStr(peek64(hdr + EDI_INVOIC_HDR_GROSSTOTAL)))); } else { if (rc == 1) { PrintLn("Nettosumme stimmt nicht"); } if (rc == 3) { PrintLn("Bruttosumme stimmt nicht"); } } free(lines, 64 * EDI_INVOIC_LINE_SIZE); free(hdr, EDI_INVOIC_HDR_SIZE); return 0; } Rechnung rechnerisch korrekt, Positionen: 1 Brutto in Cent: 57091 ''EdiInvoicValidate'' liefert ''0'' bei Übereinstimmung, ''1'' bei falscher Nettosumme und ''3'' bei falscher Bruttosumme; Toleranz ist jeweils 1 Cent. ---- ===== Hinweise ===== * **EdiInvoicWrite berechnet Summen selbst**: Header-Felder NETTOTAL/TAXTOTAL/GROSSTOTAL werden vor dem Schreiben aus den Zeilen neu berechnet. Vorab gesetzte Werte werden ignoriert. * **Steuertyp-Codes**: Der Typ ist eine interne Konstante (''EDI_INVOIC_TAX_S'' = 1). Intern wird daraus ''S'' (Standard), ''AA'' (Ermäßigt) etc. im EDIFACT-Format. * **BGM 380 vs 381**: Für Gutschriften BGM-Qualifier auf 381 setzen; alle anderen Felder identisch. * **''NETAMT'' muss der Aufrufer setzen** — ''EdiInvoicWrite'' schreibt ''MOA+203'' aus diesem Feld und summiert es; ''qty × price'' rechnet die Bibliothek dafür **nicht** nach. Ein leeres ''NETAMT'' ergibt eine Rechnung über 0,00 €. * **Beträge sind ×100**: ''888.70'' im EDIFACT-Text kommt als ''88870'' zurück. * **Structs selbst nullen**, bevor Felder gesetzt werden — ''EdiInvoicWrite'' liest jedes Feld. ---- ===== Quelldatei ===== ^ Unit ^ Datei ^ | ''std.edi.invoice'' | ''std/edi/invoice.lyx'' | Letzte Aktualisierung: 2026-08-13 — beide Beispiele lauffähig gemacht und ausgeführt (lyxc 1.0.17K), echte EDIFACT-Ausgabe samt Steueraufteilung je Satz ergänzt, Rückgabewerte von ''EdiInvoicValidate'' (0/1/3) und die Rolle von ''NETAMT'' klargestellt 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).