Test
This commit is contained in:
parent
75c9d113bc
commit
076e976298
109
main.py
109
main.py
|
|
@ -69,7 +69,8 @@ class InvoiceMetadata(BaseModel):
|
|||
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")
|
||||
delivery_date: str = Field(description="Das Lieferdatum nach 'Lieferdatum:' im Format YYYY-MM-DD")
|
||||
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.")
|
||||
customer_number: str = Field(default="", description="Kundennummer, wie sie beim Empfänger hinterlegt ist (z.B. der exakte Wert nach 'Kunden-Nr:'). Wird als Käuferreferenz (BT-10) verwendet. Falls im Text keine Kundennummer erkennbar ist: leer lassen.")
|
||||
buyer_reference: str = Field(default="", description="Bestellnummer des KÄUFERS für diesen Auftrag (BT-13): der exakte Wert nach 'Ihr Auftrag:' o.ä. (z.B. 05.06.2026-Herr.Knospe). NICHT die Kundennummer (die gehört in customer_number) und NICHT unsere eigene Auftragsbestätigungsnummer (die gehört in order_id). Wenn im Text keine solche Käufer-Bestellnummer erkennbar ist: leer lassen.")
|
||||
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_zip: str = Field(description="Postleitzahl (PLZ) des Kunden")
|
||||
|
|
@ -78,7 +79,7 @@ class InvoiceMetadata(BaseModel):
|
|||
vat_rate_percent: float = Field(description="Der primäre Umsatzsteuersatz in Prozent (z.B. 19.0)")
|
||||
|
||||
delivery_note_id: str = Field(default="", description="Gefundene Lieferscheinnummer (z.B. LS26-035) VOR Position 1. Sonst leer lassen.")
|
||||
order_id: str = Field(default="", description="Gefundene Nummer der Auftragsbestätigung oder Bestellung (z.B. AB26-080) VOR Position 1. Sonst leer lassen.")
|
||||
order_id: str = Field(default="", description="Gefundene EIGENE Auftragsbestätigungs-/Verkaufsauftragsnummer (BT-14, z.B. AB26-080) VOR Position 1. Das ist unsere eigene Nummer, NICHT die Bestellnummer des Käufers. Sonst leer lassen.")
|
||||
contract_id: str = Field(default="", description="Gefundene Nummer von Vergabeangebot, Vertrag oder Ausschreibung (z.B. AN26-130) VOR Position 1. Sonst leer lassen.")
|
||||
service_start_date: str = Field(default="", description="Startdatum eines explizit genannten Einsatz- oder Leistungszeitraums VOR Position 1 im Format YYYY-MM-DD. Sonst leer.")
|
||||
service_end_date: str = Field(default="", description="Enddatum eines explizit genannten Einsatz- oder Leistungszeitraums VOR Position 1 im Format YYYY-MM-DD. Sonst leer.")
|
||||
|
|
@ -216,20 +217,29 @@ def generate_compliant_xrechnung(data):
|
|||
|
||||
ET.SubElement(root, f"{{{CBC}}}DocumentCurrencyCode").text = "EUR"
|
||||
# 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()
|
||||
# geschäftliche Empfänger (B2B) reicht die Kundennummer, die der Kunde bei euch hat; nur bei
|
||||
# Behörden (B2G) ist eine echte Leitweg-ID vorgesehen - die kommt dann vom Empfänger selbst.
|
||||
if data.get("customer_number") and str(data["customer_number"]).strip() != "":
|
||||
ET.SubElement(root, f"{{{CBC}}}BuyerReference").text = str(data["customer_number"]).strip()
|
||||
|
||||
if data.get("service_start_date") and data.get("service_end_date"):
|
||||
period_elem = ET.SubElement(root, f"{{{CAC}}}InvoicePeriod")
|
||||
ET.SubElement(period_elem, f"{{{CBC}}}StartDate").text = parse_to_strict_xml_date(data["service_start_date"])
|
||||
ET.SubElement(period_elem, f"{{{CBC}}}EndDate").text = parse_to_strict_xml_date(data["service_end_date"])
|
||||
|
||||
if data.get("order_id") and str(data["order_id"]).strip() != "":
|
||||
|
||||
# BT-13 "Purchase order reference" (Bestellnummer des KÄUFERS, z.B. nach "Ihr Auftrag:") und
|
||||
# BT-14 "Sales order reference" (unsere EIGENE Auftrags-/Verkaufsauftragsnummer) leben beide
|
||||
# unter demselben cac:OrderReference-Element. cac:OrderReference/cbc:ID ist laut UBL-Schema
|
||||
# zwingend, sobald OrderReference verwendet wird - liegt keine Käufer-Bestellnummer (BT-13)
|
||||
# vor, aber eine eigene Nummer (BT-14), ist laut Peppol-Spezifikation der Platzhalter "NA"
|
||||
# vorgeschrieben.
|
||||
has_bt13 = data.get("buyer_reference") and str(data["buyer_reference"]).strip() != ""
|
||||
has_bt14 = data.get("order_id") and str(data["order_id"]).strip() != ""
|
||||
if has_bt13 or has_bt14:
|
||||
order_ref = ET.SubElement(root, f"{{{CAC}}}OrderReference")
|
||||
ET.SubElement(order_ref, f"{{{CBC}}}ID").text = str(data["order_id"]).strip()
|
||||
ET.SubElement(order_ref, f"{{{CBC}}}ID").text = str(data["buyer_reference"]).strip() if has_bt13 else "NA"
|
||||
if has_bt14:
|
||||
ET.SubElement(order_ref, f"{{{CBC}}}SalesOrderID").text = str(data["order_id"]).strip()
|
||||
|
||||
if data.get("delivery_note_id") and str(data["delivery_note_id"]).strip() != "":
|
||||
despatch_ref = ET.SubElement(root, f"{{{CAC}}}DespatchDocumentReference")
|
||||
|
|
@ -427,8 +437,8 @@ def generate_compliant_zugferd_cii(data):
|
|||
header_agree = ET.SubElement(trade_trans, f"{{{RAM}}}ApplicableHeaderTradeAgreement")
|
||||
# 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()
|
||||
if data.get("customer_number") and str(data["customer_number"]).strip() != "":
|
||||
ET.SubElement(header_agree, f"{{{RAM}}}BuyerReference").text = str(data["customer_number"]).strip()
|
||||
|
||||
supplier_party = ET.SubElement(header_agree, f"{{{RAM}}}SellerTradeParty")
|
||||
ET.SubElement(supplier_party, f"{{{RAM}}}Name").text = "scitech it solutions GmbH"
|
||||
|
|
@ -478,8 +488,16 @@ def generate_compliant_zugferd_cii(data):
|
|||
)
|
||||
|
||||
if data.get("order_id") and str(data["order_id"]).strip() != "":
|
||||
order_ref = ET.SubElement(header_agree, f"{{{RAM}}}BuyerOrderReferencedDocument")
|
||||
# BT-14 "Sales order reference" - unsere EIGENE Auftrags-/Verkaufsauftragsnummer.
|
||||
# In CII ein eigenständiges Element. Muss laut XSD VOR BuyerOrderReferencedDocument stehen.
|
||||
order_ref = ET.SubElement(header_agree, f"{{{RAM}}}SellerOrderReferencedDocument")
|
||||
ET.SubElement(order_ref, f"{{{RAM}}}IssuerAssignedID").text = str(data["order_id"]).strip()
|
||||
|
||||
if data.get("buyer_reference") and str(data["buyer_reference"]).strip() != "":
|
||||
# BT-13 "Purchase order reference" - die Bestellnummer des KÄUFERS (z.B. nach
|
||||
# "Ihr Auftrag:"), NICHT unsere eigene Nummer (die ist BT-14/order_id oben).
|
||||
buyer_order_ref = ET.SubElement(header_agree, f"{{{RAM}}}BuyerOrderReferencedDocument")
|
||||
ET.SubElement(buyer_order_ref, f"{{{RAM}}}IssuerAssignedID").text = str(data["buyer_reference"]).strip()
|
||||
|
||||
if data.get("contract_id") and str(data["contract_id"]).strip() != "":
|
||||
contract_ref = ET.SubElement(header_agree, f"{{{RAM}}}ContractReferencedDocument")
|
||||
|
|
@ -664,6 +682,7 @@ with col2:
|
|||
"- invoice_date (im Format YYYY-MM-DD)\n"
|
||||
"- delivery_date (im Format YYYY-MM-DD)\n"
|
||||
"- buyer_reference\n"
|
||||
"- customer_number\n"
|
||||
"- recipient_name\n"
|
||||
"- recipient_street\n"
|
||||
"- recipient_zip\n"
|
||||
|
|
@ -727,10 +746,24 @@ with col2:
|
|||
)
|
||||
selected_type_code = inv_type_ui.split(" ")[0]
|
||||
|
||||
inv_num = st.text_input("Rechnungsnummer", value=data.get("invoice_number", ""))
|
||||
inv_date = st.text_input("Rechnungsdatum (YYYY-MM-DD)", value=data.get("invoice_date", ""))
|
||||
inv_delivery = st.text_input("Lieferdatum (YYYY-MM-DD)", value=data.get("delivery_date", ""))
|
||||
buyer_ref = st.text_input("Ihr Auftrag / Referenz", value=data.get("buyer_reference", ""))
|
||||
inv_num = st.text_input("Rechnungsnummer (BT-1)", value=data.get("invoice_number", ""))
|
||||
inv_date = st.text_input("Rechnungsdatum (BT-2, YYYY-MM-DD)", value=data.get("invoice_date", ""))
|
||||
inv_delivery = st.text_input("Lieferdatum (BT-72, YYYY-MM-DD)", value=data.get("delivery_date", ""))
|
||||
cust_num = st.text_input(
|
||||
"Kundennummer / Leitweg-ID (BT-10)",
|
||||
value=data.get("customer_number", ""),
|
||||
help="Eure Kundennummer beim Empfänger (Standardfall für B2B) oder eine Leitweg-ID, falls "
|
||||
"der Empfänger eine Behörde ist. Sonst leer lassen - das Feld wird dann im XML komplett "
|
||||
"weggelassen."
|
||||
)
|
||||
buyer_ref = st.text_input(
|
||||
"Ihr Auftrag / Bestellnummer (BT-13)",
|
||||
value=data.get("buyer_reference", ""),
|
||||
help="Die vom KÄUFER vorgegebene Bestellnummer für diesen Auftrag (z.B. nach 'Ihr Auftrag:'). "
|
||||
"NICHT die Kundennummer (die gehört ins Feld oben) und NICHT die eigene Auftragsbestätigungs-"
|
||||
"/Verkaufsauftragsnummer (die gehört ins Feld 'Auftragsbestätigung / Verkaufsauftrag (BT-14)' "
|
||||
"weiter unten). Sonst leer lassen."
|
||||
)
|
||||
|
||||
# UI-Eingabefelder für die Daten vor Position 1 (BT-22 etc.)
|
||||
st.markdown("---")
|
||||
|
|
@ -739,15 +772,15 @@ with col2:
|
|||
with c_ref1:
|
||||
dn_id = st.text_input("Lieferscheinnummer (BT-16)", value=data.get("delivery_note_id", ""))
|
||||
with c_ref2:
|
||||
o_id = st.text_input("Auftragsbestätigung / Bestellung (BT-13)", value=data.get("order_id", ""))
|
||||
o_id = st.text_input("Auftragsbestätigung / Verkaufsauftrag - unsere Nummer (BT-14)", value=data.get("order_id", ""))
|
||||
with c_ref3:
|
||||
c_id = st.text_input("Vertrag / Vergabeangebot (BT-12)", value=data.get("contract_id", ""))
|
||||
|
||||
c_p1, c_p2 = st.columns(2)
|
||||
with c_p1:
|
||||
s_start = st.text_input("Leistungszeitraum Start (YYYY-MM-DD)", value=data.get("service_start_date", ""))
|
||||
s_start = st.text_input("Leistungszeitraum Start (BT-73, YYYY-MM-DD)", value=data.get("service_start_date", ""))
|
||||
with c_p2:
|
||||
s_end = st.text_input("Leistungszeitraum Ende (YYYY-MM-DD)", value=data.get("service_end_date", ""))
|
||||
s_end = st.text_input("Leistungszeitraum Ende (BT-74, YYYY-MM-DD)", value=data.get("service_end_date", ""))
|
||||
|
||||
i_note = st.text_area("Globale Rechnungserläuterung / Freitext (BT-22)", value=data.get("invoice_note", ""), height=150)
|
||||
st.markdown("---")
|
||||
|
|
@ -756,21 +789,21 @@ with col2:
|
|||
c_s, c_r = st.columns(2)
|
||||
with c_s:
|
||||
st.markdown("**Absender (Verkäufer):**")
|
||||
st.text_input("Firma (Sender)", value="scitech it solutions GmbH", disabled=True)
|
||||
st.text_input("E-Mail (Sender)", value="office@scitech-gmbh.de", disabled=True)
|
||||
st.text_input("Telefon (Sender)", value="+49 3302 4940537", disabled=True)
|
||||
st.text_input("Straße (Sender)", value="Hermann-Schumann-Str. 4", disabled=True)
|
||||
st.text_input("PLZ (Sender)", value="16761", disabled=True)
|
||||
st.text_input("Stadt (Sender)", value="Hennigsdorf", disabled=True)
|
||||
st.text_input("Umsatzsteuer-ID (Sender)", value="DE218436692", disabled=True)
|
||||
st.text_input("Registernummer (Sender)", value="HRB 6058 NP", disabled=True)
|
||||
st.text_input("Firma (Sender) (BT-27)", value="scitech it solutions GmbH", disabled=True)
|
||||
st.text_input("E-Mail (Sender) (BT-34)", value="office@scitech-gmbh.de", disabled=True)
|
||||
st.text_input("Telefon (Sender) (BT-42)", value="+49 3302 4940537", disabled=True)
|
||||
st.text_input("Straße (Sender) (BT-35)", value="Hermann-Schumann-Str. 4", disabled=True)
|
||||
st.text_input("PLZ (Sender) (BT-38)", value="16761", disabled=True)
|
||||
st.text_input("Stadt (Sender) (BT-37)", value="Hennigsdorf", disabled=True)
|
||||
st.text_input("Umsatzsteuer-ID (Sender) (BT-31)", value="DE218436692", disabled=True)
|
||||
st.text_input("Registernummer (Sender) (BT-30)", value="HRB 6058 NP", disabled=True)
|
||||
with c_r:
|
||||
st.markdown("**Empfänger (Kunde):**")
|
||||
r_name = st.text_input("Firma (Kunde)", value=data.get("recipient_name", ""))
|
||||
r_email = st.text_input("E-Mail (Kunde)", value=data.get("recipient_email", ""))
|
||||
r_street = st.text_input("Straße (Kunde)", value=data.get("recipient_street", ""))
|
||||
r_zip = st.text_input("PLZ (Kunde)", value=data.get("recipient_zip", ""))
|
||||
r_city = st.text_input("Stadt (Kunde)", value=data.get("recipient_city", ""))
|
||||
r_name = st.text_input("Firma (Kunde) (BT-44)", value=data.get("recipient_name", ""))
|
||||
r_email = st.text_input("E-Mail (Kunde) (BT-49)", value=data.get("recipient_email", ""))
|
||||
r_street = st.text_input("Straße (Kunde) (BT-50)", value=data.get("recipient_street", ""))
|
||||
r_zip = st.text_input("PLZ (Kunde) (BT-53)", value=data.get("recipient_zip", ""))
|
||||
r_city = st.text_input("Stadt (Kunde) (BT-52)", value=data.get("recipient_city", ""))
|
||||
|
||||
st.markdown("### 3. Positionen")
|
||||
|
||||
|
|
@ -801,11 +834,11 @@ with col2:
|
|||
|
||||
c_qty, c_unit, c_price, c_total = st.columns(4)
|
||||
with c_qty:
|
||||
item_quantity = st.number_input("Menge", value=float(item.get("quantity") or 0.0), format="%.3f", step=0.1, key=f"item_qty_{inv_id}_{idx}")
|
||||
item_quantity = st.number_input("Menge (BT-129)", value=float(item.get("quantity") or 0.0), format="%.3f", step=0.1, key=f"item_qty_{inv_id}_{idx}")
|
||||
with c_unit:
|
||||
item_unit = st.text_input("Einheit", value=item.get("unit", "Stk."), key=f"item_unit_{inv_id}_{idx}")
|
||||
item_unit = st.text_input("Einheit (BT-130)", value=item.get("unit", "Stk."), key=f"item_unit_{inv_id}_{idx}")
|
||||
with c_price:
|
||||
item_price = st.number_input("Einzelpreis (€)", value=float(item.get("unit_price") or 0.0), format="%.2f", key=f"item_price_{inv_id}_{idx}")
|
||||
item_price = st.number_input("Einzelpreis (BT-146, €)", value=float(item.get("unit_price") or 0.0), format="%.2f", key=f"item_price_{inv_id}_{idx}")
|
||||
with c_total:
|
||||
raw_item_total = item_quantity * item_price
|
||||
item_total_amount = kaufmaennisches_runden(raw_item_total)
|
||||
|
|
@ -822,7 +855,7 @@ with col2:
|
|||
})
|
||||
|
||||
st.markdown("---")
|
||||
vat_rate = st.number_input("Steuersatz (%)", value=float(data.get("vat_rate_percent") or 19.0))
|
||||
vat_rate = st.number_input("Steuersatz (BT-152, %)", value=float(data.get("vat_rate_percent") or 19.0))
|
||||
|
||||
computed_net_amount = sum(item["total_amount"] for item in edited_items)
|
||||
raw_vat_amount = computed_net_amount * (vat_rate / 100.0)
|
||||
|
|
@ -846,7 +879,7 @@ with col2:
|
|||
final_invoice_dict = {
|
||||
"invoice_type_code": selected_type_code,
|
||||
"invoice_number": inv_num, "invoice_date": inv_date, "delivery_date": inv_delivery,
|
||||
"buyer_reference": buyer_ref, "recipient_name": r_name, "recipient_email": r_email,
|
||||
"buyer_reference": buyer_ref, "customer_number": cust_num, "recipient_name": r_name, "recipient_email": r_email,
|
||||
"recipient_street": r_street, "recipient_zip": r_zip, "recipient_city": r_city,
|
||||
"items": edited_items, "total_net_amount": computed_net_amount, "total_vat_amount": computed_vat_amount,
|
||||
"vat_rate_percent": vat_rate, "total_gross_amount": computed_gross_amount, "iban": "DE06120700240456207000",
|
||||
|
|
|
|||
Loading…
Reference in New Issue