From 1c73b6a0422334fae554de36ac1abe6bdf21a15a Mon Sep 17 00:00:00 2001
From: Julien Van Roy <juvr@odoo.com>
Date: Wed, 30 Nov 2022 10:37:47 +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#106829

X-original-commit: 7eb401e39372bacff7410d7c57a8eb83382c0d58
Signed-off-by: Laurent Smet <las@odoo.com>
Signed-off-by: Julien Van Roy <juvr@odoo.com>
---
 .../i18n/account_edi_ubl_cii.pot              | 30 ++++++++++-----
 .../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, 73 insertions(+), 20 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 b55ada8c4432..5931f2acd72b 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
@@ -81,27 +81,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 tax: %s %% for line '%s'."
+msgid "Could not retrieve the %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
+#: 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 ?"
+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 vendor."
+msgid "Could not retrieve the unit of measure for line with label '%s'."
 msgstr ""
 
 #. module: account_edi_ubl_cii
@@ -382,6 +378,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"
@@ -411,3 +414,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 f460e270dc42..9ccb35f92f71 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
@@ -215,14 +215,14 @@ class AccountEdiXmlCII(models.AbstractModel):
             logs.append(_("The invoice has been converted into a credit note and the quantities have been reverted."))
 
         # ==== partner_id ====
-        partner_type = invoice_form.journal_id.type == journal.type and 'SellerTradeParty' or 'BuyerTradeParty'
+        partner_type = invoice_form.journal_id.type == 'purchase' and 'SellerTradeParty' or 'BuyerTradeParty'
         invoice_form.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_form.partner_id:
-            logs.append(_("Could not retrieve the vendor."))
+            logs.append(_("Could not retrieve the %s.", _("customer") if invoice_form.move_type in ('out_invoice', 'out_refund') 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 9f91397b78ec..509ce5910484 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
@@ -466,12 +466,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_form.partner_id = partner
         else:
-            logs.append(_("Could not retrieve the vendor."))
+            logs.append(_("Could not retrieve the %s.", _("customer") if invoice_form.move_type in ('out_invoice', 'out_refund') else _("vendor")))
 
         # ==== currency_id ====
 
@@ -614,23 +614,24 @@ class AccountEdiXmlUBL20(models.AbstractModel):
             return ('in_refund', 'out_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 6de631042bae..74b572c6697c 100644
--- a/addons/l10n_account_edi_ubl_cii_tests/tests/common.py
+++ b/addons/l10n_account_edi_ubl_cii_tests/tests/common.py
@@ -41,7 +41,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):
         new_invoice = self.edi_format._create_invoice_from_xml_tree(
             xml_filename,
             xml_etree,
+            # /!\ use the same journal as the invoice's one to import the xml !
             invoice.journal_id,
         )
 
@@ -187,3 +188,38 @@ class TestUBLCommon(AccountEdiTestCommon):
         )
 
         return xml_etree, xml_filename
+
+    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)
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 de3c8f065a11..bd961895553b 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
@@ -287,6 +287,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 43dfaca2fe29..7a168ed145df 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
@@ -217,6 +217,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
-- 
GitLab