-
Notifications
You must be signed in to change notification settings - Fork 0
[17.0][FIX] account_invoice_facturx: fix Factur-X 4.x schematron errors #1
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: 17.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,11 +2,20 @@ | |
| # @author: Alexis de Lattre <alexis.delattre@akretion.com> | ||
| # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). | ||
|
|
||
| from unittest.mock import patch | ||
|
|
||
| from facturx import get_facturx_level | ||
| from lxml import etree | ||
|
|
||
| from odoo.exceptions import UserError | ||
| from odoo.tests.common import TransactionCase | ||
|
|
||
| RAM_NS = ( | ||
| "urn:un:unece:uncefact:data:standard:" | ||
| "ReusableAggregateBusinessInformationEntity:100" | ||
| ) | ||
| NSMAP = {"ram": RAM_NS} | ||
|
|
||
|
|
||
| class TestFacturXInvoice(TransactionCase): | ||
| @classmethod | ||
|
|
@@ -16,6 +25,14 @@ def setUpClass(cls): | |
| cls.company = cls.env.ref("base.main_company") | ||
| cls.product1 = cls.env.ref("product.product_product_4") | ||
| cls.product2 = cls.env.ref("product.product_product_1") | ||
| cls.env.user.partner_id.email = "billing@example.com" | ||
| cls.proprietary_bank = cls.env["res.partner.bank"].create( | ||
| { | ||
| "partner_id": cls.company.partner_id.id, | ||
| "acc_number": "ACC-FACTURX-0001", | ||
| "acc_type": "bank", | ||
| } | ||
| ) | ||
| sale_taxes = cls.env["account.tax"].search( | ||
| [ | ||
| ("company_id", "=", cls.company.id), | ||
|
|
@@ -37,6 +54,7 @@ def setUpClass(cls): | |
| "move_type": "out_invoice", | ||
| "partner_id": cls.env.ref("base.res_partner_2").id, | ||
| "currency_id": cls.company.currency_id.id, | ||
| "partner_bank_id": cls.proprietary_bank.id, | ||
| "invoice_line_ids": [ | ||
| ( | ||
| 0, | ||
|
|
@@ -60,6 +78,14 @@ def setUpClass(cls): | |
| } | ||
| ) | ||
| cls.invoice.action_post() | ||
| cls.invoice.partner_bank_id = cls.proprietary_bank | ||
|
tendil marked this conversation as resolved.
|
||
|
|
||
| def _generate_xml_root(self, invoice=None, level="en16931"): | ||
| invoice = invoice or self.invoice | ||
| self.company.write({"facturx_level": level}) | ||
| xml_bytes, fx_level = invoice.generate_facturx_xml() | ||
| self.assertEqual(fx_level, level) | ||
| return etree.fromstring(xml_bytes) | ||
|
|
||
| def test_deep_customer_invoice(self): | ||
| # Bug in Basic XSD: missing CountrySubDivisionName | ||
|
|
@@ -91,3 +117,52 @@ def test_deep_customer_invoice(self): | |
| xml_root = etree.fromstring(xml_bytes) | ||
| facturx_level = get_facturx_level(xml_root) | ||
| self.assertEqual(facturx_level, level) | ||
|
|
||
| def test_email_uriid_has_no_schemeid(self): | ||
| xml_root = self._generate_xml_root(level="en16931") | ||
| uriid_nodes = xml_root.xpath( | ||
| "//ram:DefinedTradeContact/" | ||
| "ram:EmailURIUniversalCommunication/" | ||
| "ram:URIID", | ||
| namespaces=NSMAP, | ||
| ) | ||
| self.assertTrue(uriid_nodes, "Expected seller email URIID in EN16931 XML") | ||
| self.assertEqual(uriid_nodes[0].text, "billing@example.com") | ||
| self.assertNotIn("schemeID", uriid_nodes[0].attrib) | ||
|
|
||
| def test_credit_transfer_uses_proprietary_id_for_non_iban_account(self): | ||
| xml_root = self._generate_xml_root(level="en16931") | ||
| proprietary_nodes = xml_root.xpath( | ||
| "//ram:SpecifiedTradeSettlementPaymentMeans/" | ||
| "ram:PayeePartyCreditorFinancialAccount/" | ||
| "ram:ProprietaryID", | ||
| namespaces=NSMAP, | ||
| ) | ||
| iban_nodes = xml_root.xpath( | ||
| "//ram:SpecifiedTradeSettlementPaymentMeans/" | ||
| "ram:PayeePartyCreditorFinancialAccount/" | ||
| "ram:IBANID", | ||
| namespaces=NSMAP, | ||
| ) | ||
|
|
||
| self.assertTrue( | ||
| proprietary_nodes, | ||
| "Expected ProprietaryID for non-IBAN creditor account", | ||
| ) | ||
| self.assertEqual( | ||
| proprietary_nodes[0].text, | ||
| self.proprietary_bank.sanitized_acc_number, | ||
| ) | ||
| self.assertFalse(iban_nodes, "IBANID should not be generated for non-IBAN bank") | ||
|
|
||
|
Comment on lines
+133
to
+157
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Add a companion regression for the IBAN branch. This change rewires both sides of the identifier selection, but the new coverage only asserts the non-IBAN path. A small IBAN fixture/assertion would keep 🤖 Prompt for AI Agents |
||
| def test_credit_transfer_requires_account_identifier(self): | ||
| invoice = self.invoice.copy(default={"partner_bank_id": False}) | ||
| invoice.action_post() | ||
|
Comment on lines
+159
to
+160
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clear
Suggested tightening invoice = self.invoice.copy(default={"partner_bank_id": False})
invoice.action_post()
+ invoice.partner_bank_id = FalseBased on learnings: In Odoo 17, 🤖 Prompt for AI Agents |
||
|
|
||
| self.company.write({"facturx_level": "en16931"}) | ||
| with patch( | ||
| "odoo.addons.account_invoice_facturx.models.account_move.xml_check_xsd" | ||
| ) as xml_check_xsd: | ||
| with self.assertRaises(UserError): | ||
| invoice.generate_facturx_xml() | ||
| xml_check_xsd.assert_not_called() | ||
Uh oh!
There was an error while loading. Please reload this page.