pretix / python-drafthorse

Pure-python ZUGFeRD implementation
Apache License 2.0
81 stars 21 forks source link

Gross invoice example? #47

Open hschneider opened 2 weeks ago

hschneider commented 2 weeks ago

At first thanks for this great package.

Is it possible that someone translates the net invoice example to a gros invoice? A simple invoice with 19% VAT, no tax exemption.

Otherwise its very hard to determine where to enter net values.

I am sure, this will prevent tons of validation errors and questions:

note = IncludedNote()
note.content.add("Test Node 1")
doc.header.notes.add(note)

doc.trade.agreement.seller.name = "Lieferant GmbH"
doc.trade.settlement.payee.name = "Kunde GmbH"

doc.trade.agreement.buyer.name = "Kunde GmbH"
doc.trade.settlement.invoicee.name = "Kunde GmbH"

doc.trade.settlement.currency_code = "EUR"
doc.trade.settlement.payment_means.type_code = "ZZZ"

doc.trade.agreement.seller.address.country_id = "DE"
doc.trade.agreement.seller.address.country_subdivision = "Bayern"

doc.trade.agreement.seller_order.issue_date_time = datetime.now(timezone.utc)
doc.trade.agreement.buyer_order.issue_date_time = datetime.now(timezone.utc)
doc.trade.settlement.advance_payment.received_date = datetime.now(timezone.utc)
doc.trade.agreement.customer_order.issue_date_time = datetime.now(timezone.utc)

li = LineItem()
li.document.line_id = "1"
li.product.name = "Rainbow"
li.agreement.gross.amount = Decimal("999.00")
li.agreement.gross.basis_quantity = (Decimal("1.0000"), "C62")  # C62 == pieces
li.agreement.net.amount = Decimal("999.00")
li.agreement.net.basis_quantity = (Decimal("999.00"), "EUR")
li.delivery.billed_quantity = (Decimal("1.0000"), "C62")  # C62 == pieces
li.settlement.trade_tax.type_code = "VAT"
li.settlement.trade_tax.category_code = "E"
li.settlement.trade_tax.rate_applicable_percent = Decimal("0.00")
li.settlement.monetary_summation.total_amount = Decimal("999.00")
doc.trade.items.add(li)

trade_tax = ApplicableTradeTax()
trade_tax.calculated_amount = Decimal("0.00")
trade_tax.basis_amount = Decimal("999.00")
trade_tax.type_code = "VAT"
trade_tax.category_code = "AE"
trade_tax.exemption_reason_code = 'VATEX-EU-AE'
trade_tax.rate_applicable_percent = Decimal("0.00")
doc.trade.settlement.trade_tax.add(trade_tax)

doc.trade.settlement.monetary_summation.line_total = Decimal("999.00")
doc.trade.settlement.monetary_summation.charge_total = Decimal("0.00")
doc.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")
doc.trade.settlement.monetary_summation.tax_basis_total = Decimal("999.00")
doc.trade.settlement.monetary_summation.tax_total = Decimal("0.00")
doc.trade.settlement.monetary_summation.grand_total = Decimal("999.00")
doc.trade.settlement.monetary_summation.due_amount = Decimal("999.00")

THANKS!

hschneider commented 2 weeks ago

To be more specific, I tried to setup this invoice:

1 x test procuct 59 EUR net. Grand total: 70,21 EUR with 19% VAT inclusive.

This is the code:

 li = LineItem()
        li.document.line_id = "1"
        li.product.name = "TEST"
        li.agreement.gross.amount = Decimal("59.00")
        li.agreement.gross.basis_quantity = (Decimal("1.00"), "H87")  # C62 == pieces
        li.agreement.net.amount = Decimal("59.00")
        li.agreement.net.basis_quantity = (Decimal("59.00"), "EUR")
        li.delivery.billed_quantity = (Decimal("1.00"), "H87")  # C62 == pieces
        li.settlement.trade_tax.type_code = "VAT"
        li.settlement.trade_tax.category_code = "S"
        li.settlement.trade_tax.rate_applicable_percent = Decimal("19.00")
        li.settlement.monetary_summation.total_amount = Decimal("59.00")
        self.docZF.trade.items.add(li)

        trade_tax = ApplicableTradeTax()
        trade_tax.calculated_amount = Decimal("11.21")
        trade_tax.basis_amount = Decimal("59.00")
        trade_tax.type_code = "VAT"
        trade_tax.category_code = "S"
        #trade_tax.exemption_reason_code = 'VATEX-EU-AE'
        trade_tax.rate_applicable_percent = Decimal("19.00")
        self.docZF.trade.settlement.trade_tax.add(trade_tax)

        self.docZF.trade.settlement.monetary_summation.line_total = Decimal("59.00")
        self.docZF.trade.settlement.monetary_summation.charge_total = Decimal("0.00")       # Gebühren
        self.docZF.trade.settlement.monetary_summation.allowance_total = Decimal("0.00")    # Zuschläge
        self.docZF.trade.settlement.monetary_summation.tax_basis_total = Decimal("59.00")
        self.docZF.trade.settlement.monetary_summation.tax_total = Decimal("11.21")
        self.docZF.trade.settlement.monetary_summation.grand_total = Decimal("70.21")
        self.docZF.trade.settlement.monetary_summation.due_amount = Decimal("70.21")

Verifying the PDF, throws this error:

16:45:28.353 [main] INFO  o.m.validator.XMLValidator - FailedAssert 
16:45:28.360 [main] ERROR o.m.validator.ZUGFeRDValidator - Error 4: 
    Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110). from /xslt/ZF_221/FACTUR-X_EXTENDED.xslt)

Valitool says:

[VD-Valitool-59]-Rechenfehler (ggf. wegen Rundungsfehler): MonetarySummation -> GrandTotalAmount = TaxBasisTotalAmount + TaxTotalAmount 59.00 + 0.00 = 59.00 Dies entspricht nicht dem angebenen Wert 70.21

So TaxTotalAmount is read as 0.00. In the code it is set to .settlement.monetary_summation.tax_total = Decimal("11.21")

Did I miss something or did I hit some bug?

raphaelm commented 2 weeks ago

So the problem is that you are trying to do the impossible.

ZUGFeRD 2 is modeled after EN16931. The German version of that norm is freely available at DIN…

… and it only allows net invoices. Gross invoices are impossible to be EN16931 compliant.

Yes, this sucks a lot. Someone has not thought this entire thing through and it has already given me sleepless nights.

ZUGFeRD 2.3 EXTENDED introduces some flexibility on the rounding check, which is however in violation of current EN16931.

hschneider commented 2 weeks ago

Ouch! OK - thanks for the info.

0xD0M1M0 commented 3 days ago

To fix "Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110)" add a currencyID to taxTotalAmount:

VALUE

Reference: https://xeinkauf.de/app/uploads/2024/07/302-XRechnung-2024-06-20.pdf Page 66 / BR-53:

Wenn ein Währungscode für die Umsatzsteuerabrechnung "VAT accounting currency code" (BT-6) angegeben wurde, muss der Umsatzsteuergesamtbetrag in der Abrechnungswährung "Invoice total VAT amount in accounting currency" (BT-111) angegeben werden.

raphaelm commented 3 days ago

That's not the issue at hand

0xD0M1M0 commented 3 days ago

Sorry. I just read the error message and saw a solution.