From 96be0eda68fdcc0cc33fcfa381b5ba8d3cb9567a Mon Sep 17 00:00:00 2001 From: Julien Van Roy Date: Thu, 1 Dec 2022 08:41:33 +0000 Subject: [PATCH] [FIX] account_edi_ubl_cii: import customer when uploading an invoice Previously, it was only possible to upload a bill, so the partner_id was read from the vendor in the imported file. Now, it is also possible to upload a file to create an invoice, so we need to be able to decode the customer in the imported file. opw-3072989 closes odoo/odoo#106942 X-original-commit: c52cde5405dcc5f3e48fea9b380a93d4a56467b5 Signed-off-by: Laurent Smet Signed-off-by: Julien Van Roy --- .../i18n/account_edi_ubl_cii.pot | 36 +++++++++++------- .../models/account_edi_xml_cii_facturx.py | 4 +- .../models/account_edi_xml_ubl_20.py | 15 ++++---- .../tests/common.py | 38 ++++++++++++++++++- .../tests/test_xml_cii_fr.py | 3 ++ .../tests/test_xml_ubl_be.py | 3 ++ 6 files changed, 76 insertions(+), 23 deletions(-) diff --git a/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot b/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot index 05069028c31..6ee3e4eeaba 100644 --- a/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot +++ b/addons/account_edi_ubl_cii/i18n/account_edi_ubl_cii.pot @@ -82,27 +82,23 @@ msgid "" msgstr "" #. module: account_edi_ubl_cii -#: code:addons/account_edi_ubl_cii/models/account_edi_common.py:0 #: code:addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py:0 #: code:addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py:0 #, python-format +msgid "Could not retrieve the %s." +msgstr "" + +#. module: account_edi_ubl_cii +#: code:addons/account_edi_ubl_cii/models/account_edi_common.py:0 +#: code:addons/account_edi_ubl_cii/models/account_edi_common.py:0 +#, python-format msgid "Could not retrieve the tax: %s %% for line '%s'." msgstr "" #. module: account_edi_ubl_cii -#: code:addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py:0 -#: code:addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py:0 +#: code:addons/account_edi_ubl_cii/models/account_edi_common.py:0 #, python-format -msgid "" -"Could not retrieve the unit of measure for line with label '%s'. Did you " -"install the inventory app and enabled the 'Units of Measure' option ?" -msgstr "" - -#. module: account_edi_ubl_cii -#: code:addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py:0 -#: code:addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py:0 -#, python-format -msgid "Could not retrieve the vendor." +msgid "Could not retrieve the unit of measure for line with label '%s'." msgstr "" #. module: account_edi_ubl_cii @@ -380,6 +376,13 @@ msgid "" " code (BT-151)." msgstr "" +#. module: account_edi_ubl_cii +#: code:addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py:0 +#: code:addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py:0 +#, python-format +msgid "customer" +msgstr "" + #. module: account_edi_ubl_cii #: model_terms:ir.ui.view,arch_db:account_edi_ubl_cii.account_invoice_pdfa_3_facturx_metadata msgid "external" @@ -409,3 +412,10 @@ msgstr "" #: model_terms:ir.ui.view,arch_db:account_edi_ubl_cii.account_invoice_pdfa_3_facturx_metadata msgid "urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#" msgstr "" + +#. module: account_edi_ubl_cii +#: code:addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py:0 +#: code:addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py:0 +#, python-format +msgid "vendor" +msgstr "" diff --git a/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py b/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py index 21ad9c17322..90c489ec2ce 100644 --- a/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py +++ b/addons/account_edi_ubl_cii/models/account_edi_xml_cii_facturx.py @@ -216,14 +216,14 @@ class AccountEdiXmlCII(models.AbstractModel): # ==== partner_id ==== - partner_type = invoice.journal_id.type == journal.type and 'SellerTradeParty' or 'BuyerTradeParty' + partner_type = invoice.journal_id.type == 'purchase' and 'SellerTradeParty' or 'BuyerTradeParty' invoice.partner_id = self.env['account.edi.format']._retrieve_partner( name=_find_value(f"//ram:{partner_type}/ram:Name"), mail=_find_value(f"//ram:{partner_type}//ram:URIID[@schemeID='SMTP']"), vat=_find_value(f"//ram:{partner_type}/ram:SpecifiedTaxRegistration/ram:ID"), ) if not invoice.partner_id: - logs.append(_("Could not retrieve the vendor.")) + logs.append(_("Could not retrieve the %s.", _("customer") if invoice.is_sale_document() else _("vendor"))) # ==== currency_id ==== diff --git a/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py b/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py index 0b2e60a096e..08074c35ceb 100644 --- a/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py +++ b/addons/account_edi_ubl_cii/models/account_edi_xml_ubl_20.py @@ -465,12 +465,12 @@ class AccountEdiXmlUBL20(models.AbstractModel): partner = self._import_retrieve_info_from_map( tree, - self._import_retrieve_partner_map(journal), + self._import_retrieve_partner_map(self.env.company, journal.type), ) if partner: invoice.partner_id = partner else: - logs.append(_("Could not retrieve the vendor.")) + logs.append(_("Could not retrieve the %s.", _("customer") if invoice.is_sale_document() else _("vendor"))) # ==== currency_id ==== @@ -615,23 +615,24 @@ class AccountEdiXmlUBL20(models.AbstractModel): return 'refund', 1 return None, None - def _import_retrieve_partner_map(self, company): + def _import_retrieve_partner_map(self, company, move_type='purchase'): + role = "Customer" if move_type == 'sale' else "Supplier" def with_vat(tree, extra_domain): - vat_node = tree.find('.//{*}AccountingSupplierParty/{*}Party//{*}CompanyID') + vat_node = tree.find(f'.//{{*}}Accounting{role}Party/{{*}}Party//{{*}}CompanyID') vat = None if vat_node is None else vat_node.text return self.env['account.edi.format']._retrieve_partner_with_vat(vat, extra_domain) def with_phone_mail(tree, extra_domain): - phone_node = tree.find('.//{*}AccountingSupplierParty/{*}Party//{*}Telephone') - mail_node = tree.find('.//{*}AccountingSupplierParty/{*}Party//{*}ElectronicMail') + phone_node = tree.find(f'.//{{*}}Accounting{role}Party/{{*}}Party//{{*}}Telephone') + mail_node = tree.find(f'.//{{*}}Accounting{role}Party/{{*}}Party//{{*}}ElectronicMail') phone = None if phone_node is None else phone_node.text mail = None if mail_node is None else mail_node.text return self.env['account.edi.format']._retrieve_partner_with_phone_mail(phone, mail, extra_domain) def with_name(tree, extra_domain): - name_node = tree.find('.//{*}AccountingSupplierParty/{*}Party//{*}Name') + name_node = tree.find(f'.//{{*}}Accounting{role}Party/{{*}}Party//{{*}}Name') name = None if name_node is None else name_node.text return self.env['account.edi.format']._retrieve_partner_with_name(name, extra_domain) diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py index c56ec871660..d504bbe8f6f 100644 --- a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py @@ -44,7 +44,7 @@ class TestUBLCommon(AccountEdiTestCommon): def assert_same_invoice(self, invoice1, invoice2, **invoice_kwargs): self.assertEqual(len(invoice1.invoice_line_ids), len(invoice2.invoice_line_ids)) self.assertRecordValues(invoice2, [{ - 'partner_id': invoice1.company_id.partner_id.id, + 'partner_id': invoice1.partner_id.id, 'invoice_date': fields.Date.from_string(invoice1.date), 'currency_id': invoice1.currency_id.id, 'amount_untaxed': invoice1.amount_untaxed, @@ -74,6 +74,7 @@ class TestUBLCommon(AccountEdiTestCommon): Create an account.move directly from an xml file, asserts the invoice obtained is the same as the expected invoice. """ + # /!\ use the same journal as the invoice's one to import the attachment ! invoice.journal_id.create_document_from_attachment(attachment.ids) new_invoice = self.env['account.move'].search([], order='id desc', limit=1) @@ -194,6 +195,41 @@ class TestUBLCommon(AccountEdiTestCommon): return attachment + def _test_import_partner(self, edi_code, filename): + """ + Given an invoice where partner_1 is the vendor and partner_2 is the customer with an EDI attachment. + * Uploading the attachment as an invoice should create an invoice with the buyer = partner_2. + * Uploading the attachment as a vendor bill should create a bill with the vendor = partner_1. + """ + invoice = self._generate_move( + seller=self.partner_1, + buyer=self.partner_2, + move_type='out_invoice', + invoice_line_ids=[{'product_id': self.product_a.id}], + ) + edi_attachment = invoice.edi_document_ids.filtered( + lambda doc: doc.edi_format_id.code == edi_code).attachment_id + self.assertEqual(edi_attachment.name, filename) + edi_etree = self.get_xml_tree_from_string(edi_attachment.raw) + + # Import attachment as an invoice + new_invoice = self.edi_format._create_invoice_from_xml_tree( + filename='test_filename', + tree=edi_etree, + journal=self.env['account.journal'].search( + [('type', '=', 'sale'), ('company_id', '=', self.env.company.id)], limit=1) + ) + self.assertEqual(self.partner_2, new_invoice.partner_id) + + # Import attachment as a vendor bill + new_invoice = self.edi_format._create_invoice_from_xml_tree( + filename='test_filename', + tree=edi_etree, + journal=self.env['account.journal'].search( + [('type', '=', 'purchase'), ('company_id', '=', self.env.company.id)], limit=1) + ) + self.assertEqual(self.partner_1, new_invoice.partner_id) + def _test_encoding_in_attachment(self, edi_code, filename): """ Generate an invoice, assert that the tag '' is present in the attachment diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py index 124471ad7d9..a1365e9ac27 100644 --- a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_cii_fr.py @@ -292,6 +292,9 @@ class TestCIIFR(TestUBLCommon): # Test import #################################################### + def test_import_partner_facturx(self): + self._test_import_partner('facturx_1_0_05', 'factur-x.xml') + def test_import_tax_included(self): """ Tests whether the tax included / tax excluded are correctly decoded when diff --git a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py index e784a3a541a..d91cb0ff125 100644 --- a/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py +++ b/addons/l10n_account_edi_ubl_cii_tests/tests/test_xml_ubl_be.py @@ -220,6 +220,9 @@ class TestUBLBE(TestUBLCommon): # Test import #################################################### + def test_import_partner_ubl(self): + self._test_import_partner('ubl_bis3', 'INV_2017_00002_ubl_bis3.xml') + def test_import_export_invoice_xml(self): """ Test whether the elements only specific to ubl_be are correctly exported