This commit is contained in:
dfuhrmann3 2026-07-06 12:53:48 +02:00
parent 3e59cd188e
commit 75c9d113bc
1 changed files with 17 additions and 7 deletions

24
main.py
View File

@ -69,7 +69,7 @@ class InvoiceMetadata(BaseModel):
invoice_number: str = Field(description="Die Rechnungsnummer nach 'Nummer:' (z.B. R26-219)") invoice_number: str = Field(description="Die Rechnungsnummer nach 'Nummer:' (z.B. R26-219)")
invoice_date: str = Field(description="Das Rechnungsdatum nach 'Datum:' im Format YYYY-MM-DD") invoice_date: str = Field(description="Das Rechnungsdatum nach 'Datum:' im Format YYYY-MM-DD")
delivery_date: str = Field(description="Das Lieferdatum nach 'Lieferdatum:' im Format YYYY-MM-DD") delivery_date: str = Field(description="Das Lieferdatum nach 'Lieferdatum:' im Format YYYY-MM-DD")
buyer_reference: str = Field(description="Der exakte Wert nach 'Ihr Auftrag:' (z.B. 05.06.2026-Herr.Knospe)") buyer_reference: str = Field(default="", description="Käuferreferenz/Leitweg-ID: NUR eintragen, wenn im Text explizit ein Wert nach 'Ihr Auftrag:' o.ä. als eigene Referenz des Käufers/Empfängers steht (z.B. 05.06.2026-Herr.Knospe). NIEMALS die Nummer einer Auftragsbestätigung/Bestellung hier eintragen - die gehört ausschließlich in order_id. Wenn keine separate Käuferreferenz im Text erkennbar ist: leer lassen.")
recipient_name: str = Field(description="Vollständige Firma des Kunden/Empfängers (z.B. Stadtwerke Hennigsdorf GmbH)") recipient_name: str = Field(description="Vollständige Firma des Kunden/Empfängers (z.B. Stadtwerke Hennigsdorf GmbH)")
recipient_street: str = Field(description="Straße und Hausnummer des Kunden") recipient_street: str = Field(description="Straße und Hausnummer des Kunden")
recipient_zip: str = Field(description="Postleitzahl (PLZ) des Kunden") recipient_zip: str = Field(description="Postleitzahl (PLZ) des Kunden")
@ -215,7 +215,12 @@ def generate_compliant_xrechnung(data):
ET.SubElement(root, f"{{{CBC}}}Note").text = str(data["invoice_note"]).strip() ET.SubElement(root, f"{{{CBC}}}Note").text = str(data["invoice_note"]).strip()
ET.SubElement(root, f"{{{CBC}}}DocumentCurrencyCode").text = "EUR" ET.SubElement(root, f"{{{CBC}}}DocumentCurrencyCode").text = "EUR"
ET.SubElement(root, f"{{{CBC}}}BuyerReference").text = str(data["buyer_reference"]) # BT-10 Käuferreferenz: NUR eintragen, wenn tatsächlich ein Wert vorliegt. Für private/
# geschäftliche Empfänger (B2B) ist dieses Feld optional (0..1); nur bei Behörden (B2G)
# ist eine echte Leitweg-ID zwingend (BR-DE-15) - die kommt dann aber vom Empfänger selbst
# und wird nicht erfunden.
if data.get("buyer_reference") and str(data["buyer_reference"]).strip() != "":
ET.SubElement(root, f"{{{CBC}}}BuyerReference").text = str(data["buyer_reference"]).strip()
if data.get("service_start_date") and data.get("service_end_date"): if data.get("service_start_date") and data.get("service_end_date"):
period_elem = ET.SubElement(root, f"{{{CAC}}}InvoicePeriod") period_elem = ET.SubElement(root, f"{{{CAC}}}InvoicePeriod")
@ -241,8 +246,6 @@ def generate_compliant_xrechnung(data):
supplier = ET.SubElement(root, f"{{{CAC}}}AccountingSupplierParty") supplier = ET.SubElement(root, f"{{{CAC}}}AccountingSupplierParty")
party_supp = ET.SubElement(supplier, f"{{{CAC}}}Party") party_supp = ET.SubElement(supplier, f"{{{CAC}}}Party")
ET.SubElement(party_supp, f"{{{CBC}}}EndpointID", {"schemeID": "EM"}).text = "office@scitech-gmbh.de" ET.SubElement(party_supp, f"{{{CBC}}}EndpointID", {"schemeID": "EM"}).text = "office@scitech-gmbh.de"
party_name_supp = ET.SubElement(party_supp, f"{{{CAC}}}PartyName")
ET.SubElement(party_name_supp, f"{{{CBC}}}Name").text = "scitech it solutions GmbH"
addr_supp = ET.SubElement(party_supp, f"{{{CAC}}}PostalAddress") addr_supp = ET.SubElement(party_supp, f"{{{CAC}}}PostalAddress")
ET.SubElement(addr_supp, f"{{{CBC}}}StreetName").text = "Hermann-Schumann-Str. 4" ET.SubElement(addr_supp, f"{{{CBC}}}StreetName").text = "Hermann-Schumann-Str. 4"
@ -270,8 +273,6 @@ def generate_compliant_xrechnung(data):
customer = ET.SubElement(root, f"{{{CAC}}}AccountingCustomerParty") customer = ET.SubElement(root, f"{{{CAC}}}AccountingCustomerParty")
party_cust = ET.SubElement(customer, f"{{{CAC}}}Party") party_cust = ET.SubElement(customer, f"{{{CAC}}}Party")
ET.SubElement(party_cust, f"{{{CBC}}}EndpointID", {"schemeID": "EM"}).text = str(data["recipient_email"]) if data.get("recipient_email") else "rechnung@kunde.de" ET.SubElement(party_cust, f"{{{CBC}}}EndpointID", {"schemeID": "EM"}).text = str(data["recipient_email"]) if data.get("recipient_email") else "rechnung@kunde.de"
party_name_cust = ET.SubElement(party_cust, f"{{{CAC}}}PartyName")
ET.SubElement(party_name_cust, f"{{{CBC}}}Name").text = str(data["recipient_name"])
addr_cust = ET.SubElement(party_cust, f"{{{CAC}}}PostalAddress") addr_cust = ET.SubElement(party_cust, f"{{{CAC}}}PostalAddress")
ET.SubElement(addr_cust, f"{{{CBC}}}StreetName").text = str(data["recipient_street"]) ET.SubElement(addr_cust, f"{{{CBC}}}StreetName").text = str(data["recipient_street"])
ET.SubElement(addr_cust, f"{{{CBC}}}CityName").text = str(data["recipient_city"]) ET.SubElement(addr_cust, f"{{{CBC}}}CityName").text = str(data["recipient_city"])
@ -292,6 +293,12 @@ def generate_compliant_xrechnung(data):
payee_acc = ET.SubElement(payment_means, f"{{{CAC}}}PayeeFinancialAccount") payee_acc = ET.SubElement(payment_means, f"{{{CAC}}}PayeeFinancialAccount")
ET.SubElement(payee_acc, f"{{{CBC}}}ID").text = str(data["iban"]).replace(" ", "") ET.SubElement(payee_acc, f"{{{CBC}}}ID").text = str(data["iban"]).replace(" ", "")
# BT-20 Zahlungsbedingungen (Freitext) - für menschliche Lesbarkeit, analog zur
# CII/ZUGFeRD-Fassung. BR-CO-25 ist bereits durch das gesetzte cbc:DueDate (BT-9) erfüllt;
# dieser Text ist zusätzliche Klarstellung, keine Pflicht.
payment_terms_ubl = ET.SubElement(root, f"{{{CAC}}}PaymentTerms")
ET.SubElement(payment_terms_ubl, f"{{{CBC}}}Note").text = "Zahlbar innerhalb 14 Tagen ohne Abzug."
tax_total = ET.SubElement(root, f"{{{CAC}}}TaxTotal") tax_total = ET.SubElement(root, f"{{{CAC}}}TaxTotal")
ET.SubElement(tax_total, f"{{{CBC}}}TaxAmount", {"currencyID": "EUR"}).text = f"{data['total_vat_amount']:.2f}" ET.SubElement(tax_total, f"{{{CBC}}}TaxAmount", {"currencyID": "EUR"}).text = f"{data['total_vat_amount']:.2f}"
@ -418,7 +425,10 @@ def generate_compliant_zugferd_cii(data):
ET.SubElement(mon_sum, f"{{{RAM}}}LineTotalAmount").text = f"{item['total_amount']:.2f}" ET.SubElement(mon_sum, f"{{{RAM}}}LineTotalAmount").text = f"{item['total_amount']:.2f}"
header_agree = ET.SubElement(trade_trans, f"{{{RAM}}}ApplicableHeaderTradeAgreement") header_agree = ET.SubElement(trade_trans, f"{{{RAM}}}ApplicableHeaderTradeAgreement")
ET.SubElement(header_agree, f"{{{RAM}}}BuyerReference").text = str(data["buyer_reference"]) # BT-10 Käuferreferenz: NUR eintragen, wenn tatsächlich ein Wert vorliegt (siehe UBL-Generator
# für die ausführliche Begründung).
if data.get("buyer_reference") and str(data["buyer_reference"]).strip() != "":
ET.SubElement(header_agree, f"{{{RAM}}}BuyerReference").text = str(data["buyer_reference"]).strip()
supplier_party = ET.SubElement(header_agree, f"{{{RAM}}}SellerTradeParty") supplier_party = ET.SubElement(header_agree, f"{{{RAM}}}SellerTradeParty")
ET.SubElement(supplier_party, f"{{{RAM}}}Name").text = "scitech it solutions GmbH" ET.SubElement(supplier_party, f"{{{RAM}}}Name").text = "scitech it solutions GmbH"