-
-
Notifications
You must be signed in to change notification settings - Fork 55
feat: generate EN 16931 compatible invoices #3159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds EN 16931 (European standard e-invoicing format) XML generation and embeds it into PDF invoices using Factur-X format. The changes enable compliance with European e-invoicing standards by generating structured invoice data alongside the visual PDF representation.
Key changes:
- Integration of PyCheval library for EN 16931 XML generation and Factur-X for PDF embedding
- New
get_en_16931_xml()andgenerate_en_16931_xml()methods to create standardized e-invoice XML - Modified PDF generation to embed EN 16931 XML using Factur-X format
- Fixed bug in Money S3 XML generation where country was referenced incorrectly
Reviewed Changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| weblate_web/invoices/models.py | Adds EN 16931 XML generation methods, embeds e-invoice in PDF, fixes country code reference in Money S3 XML |
| weblate_web/invoices/tests.py | Adds validation for generated EN 16931 XML using drafthorse validator |
| requirements.txt | Adds drafthorse, factur-x, and PyCheval dependencies for e-invoicing support |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| billed_total=Money( | ||
| self.total_discount, self.get_currency_display() | ||
| ), | ||
| tax_rate=self.vat_rate, |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The discount line item should use self.vat_rate or None for consistency with other line items (line 740). When vat_rate is 0 (falsy), the tax_rate should be None, not 0.
| tax_rate=self.vat_rate, | |
| tax_rate=self.vat_rate or None, |
weblate_web/invoices/models.py
Outdated
| pdf_bytes = generate_from_binary( | ||
| buffer.getvalue(), generate_xml(self.get_en_16931_xml()) | ||
| ) | ||
| # pdf_bytes = embed_invoice_in_pdf(buffer, self.get_en_16931_xml()) |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commented-out code should be removed. If this is an alternative implementation that might be needed later, it should be documented in a TODO comment or issue tracker instead.
| # pdf_bytes = embed_invoice_in_pdf(buffer, self.get_en_16931_xml()) |
weblate_web/invoices/models.py
Outdated
| line_items=line_items, | ||
| type_code=type_code, | ||
| seller=TradeParty( | ||
| name="Weblate s.r.o", |
Copilot
AI
Nov 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Company name is missing a period after 'r.o'. It should be 'Weblate s.r.o.' (with a trailing period) for proper Czech company designation.
| name="Weblate s.r.o", | |
| name="Weblate s.r.o.", |
❌ 38 Tests Failed:
View the top 3 failed test(s) by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
ad6ee0e to
a5473ae
Compare
This is a proof of concept, I had to use several libraries to make it work. Outstanding issues: - The VAT validation fails because of zfutura/pycheval#30 - We really need full validation to be part of the tests, needs to be investigated. - There are definitely issues with generated XML.
This is a proof of concept, I had to use several libraries to make it work.
Outstanding issues: