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