From dcd84cc71e2e0051d7a301b3b8c3e2ebf733ba8e Mon Sep 17 00:00:00 2001
From: Paul Morelle <pmo@odoo.com>
Date: Mon, 30 Aug 2021 11:20:47 +0000
Subject: [PATCH] [FIX] account_edi*: retrieve records within allowed companies

_retrieve_{partner,product,tax} methods were not taking into account a
scope of a company, and methods like _import_ubl or _import_facturx
could end up on creating an invoice with a company mismatch between the
company of the journal and the company of the partner, product or tax.

With this commit, these methods will consider the allowed_company_ids
context value, and calling code should set it to an acceptable value.

OPW-2560795

closes odoo/odoo#75738

Signed-off-by: Laurent Smet <smetl@users.noreply.github.com>
---
 .../account_edi/models/account_edi_format.py  | 21 ++++++++++++-------
 .../models/account_edi_format.py              | 10 +++++----
 .../test_file/test_facturx.xml                |  3 +++
 .../account_edi_facturx/tests/test_facturx.py | 18 ++++++++++++++++
 .../models/account_edi_format.py              |  7 ++++---
 5 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/addons/account_edi/models/account_edi_format.py b/addons/account_edi/models/account_edi_format.py
index 1dce2c580df0..68ae2c9532c7 100644
--- a/addons/account_edi/models/account_edi_format.py
+++ b/addons/account_edi/models/account_edi_format.py
@@ -497,7 +497,10 @@ class AccountEdiFormat(models.Model):
             if value is not None:
                 domains.append(domain)
 
-        domain = expression.OR(domains)
+        domain = expression.AND([
+            expression.OR(domains),
+            [('company_id', 'in', [False, self.env.company.id])],
+        ])
         return self.env['res.partner'].search(domain, limit=1)
 
     def _retrieve_product(self, name=None, default_code=None, barcode=None):
@@ -517,7 +520,10 @@ class AccountEdiFormat(models.Model):
             if value is not None:
                 domains.append([domain])
 
-        domain = expression.OR(domains)
+        domain = expression.AND([
+            expression.OR(domains),
+            [('company_id', 'in', [False, self.env.company.id])],
+        ])
         return self.env['product.product'].search(domain, limit=1)
 
     def _retrieve_tax(self, amount, type_tax_use):
@@ -527,12 +533,11 @@ class AccountEdiFormat(models.Model):
         :param type_tax_use:    The type of the tax.
         :returns:               A tax or an empty recordset if not found.
         '''
-        domains = [
-            [('amount', '=', float(amount))],
-            [('type_tax_use', '=', type_tax_use)]
-        ]
-
-        return self.env['account.tax'].search(expression.AND(domains), order='sequence ASC', limit=1)
+        return self.env['account.tax'].search([
+            ('amount', '=', float(amount)),
+            ('type_tax_use', '=', type_tax_use),
+            ('company_id', '=', self.env.company.id),
+        ], limit=1)
 
     def _retrieve_currency(self, code):
         '''Search all currencies and find one that matches the code.
diff --git a/addons/account_edi_facturx/models/account_edi_format.py b/addons/account_edi_facturx/models/account_edi_format.py
index 60f74b82e1db..8b3a1f684f7f 100644
--- a/addons/account_edi_facturx/models/account_edi_format.py
+++ b/addons/account_edi_facturx/models/account_edi_format.py
@@ -193,9 +193,11 @@ class AccountEdiFormat(models.Model):
         # self could be a single record (editing) or be empty (new).
         with Form(invoice.with_context(default_move_type=default_move_type,
                                        account_predictive_bills_disable_prediction=True)) as invoice_form:
+            self_ctx = self.with_company(invoice.company_id)
+
             # Partner (first step to avoid warning 'Warning! You must first select a partner.').
             partner_type = invoice_form.journal_id.type == 'purchase' and 'SellerTradeParty' or 'BuyerTradeParty'
-            invoice_form.partner_id = self._retrieve_partner(
+            invoice_form.partner_id = self_ctx._retrieve_partner(
                 name=self._find_value('//ram:' + partner_type + '/ram:Name', tree, namespaces=tree.nsmap),
                 mail=self._find_value('//ram:' + partner_type + '//ram:URIID[@schemeID=\'SMTP\']', tree, namespaces=tree.nsmap),
                 vat=self._find_value('//ram:' + partner_type + '/ram:SpecifiedTaxRegistration/ram:ID', tree, namespaces=tree.nsmap),
@@ -203,7 +205,7 @@ class AccountEdiFormat(models.Model):
 
             # Delivery partner
             if 'partner_shipping_id' in invoice._fields:
-                invoice_form.partner_shipping_id = self._retrieve_partner(
+                invoice_form.partner_shipping_id = self_ctx._retrieve_partner(
                     name=self._find_value('//ram:ShipToTradeParty/ram:Name', tree, namespaces=tree.nsmap),
                     mail=self._find_value('//ram:ShipToTradeParty//ram:URIID[@schemeID=\'SMTP\']', tree, namespaces=tree.nsmap),
                     vat=self._find_value('//ram:ShipToTradeParty/ram:SpecifiedTaxRegistration/ram:ID', tree, namespaces=tree.nsmap),
@@ -267,7 +269,7 @@ class AccountEdiFormat(models.Model):
                         name = _find_value('.//ram:SpecifiedTradeProduct/ram:Name', element)
                         if name:
                             invoice_line_form.name = name
-                        invoice_line_form.product_id = self._retrieve_product(
+                        invoice_line_form.product_id = self_ctx._retrieve_product(
                             default_code=_find_value('.//ram:SpecifiedTradeProduct/ram:SellerAssignedID', element),
                             name=_find_value('.//ram:SpecifiedTradeProduct/ram:Name', element),
                             barcode=_find_value('.//ram:SpecifiedTradeProduct/ram:GlobalID', element)
@@ -303,7 +305,7 @@ class AccountEdiFormat(models.Model):
                         tax_element = element.xpath('.//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:RateApplicablePercent', namespaces=tree.nsmap)
                         invoice_line_form.tax_ids.clear()
                         for eline in tax_element:
-                            tax = self._retrieve_tax(
+                            tax = self_ctx._retrieve_tax(
                                 amount=eline.text,
                                 type_tax_use=invoice_form.journal_id.type
                             )
diff --git a/addons/account_edi_facturx/test_file/test_facturx.xml b/addons/account_edi_facturx/test_file/test_facturx.xml
index ad5d797ad342..0216f86af1c0 100644
--- a/addons/account_edi_facturx/test_file/test_facturx.xml
+++ b/addons/account_edi_facturx/test_file/test_facturx.xml
@@ -42,6 +42,9 @@ Conference room table</ram:Name>
                 <ram:SpecifiedTradeSettlementLineMonetarySummation>
                     <ram:LineTotalAmount currencyID="USD">3210.00</ram:LineTotalAmount>
                 </ram:SpecifiedTradeSettlementLineMonetarySummation>
+                <ram:ApplicableTradeTax>
+                    <ram:RateApplicablePercent>0.0</ram:RateApplicablePercent>
+                </ram:ApplicableTradeTax>
             </ram:SpecifiedLineTradeSettlement>
 
         </ram:IncludedSupplyChainTradeLineItem>
diff --git a/addons/account_edi_facturx/tests/test_facturx.py b/addons/account_edi_facturx/tests/test_facturx.py
index 46729101fbe1..bc0a4c01989b 100644
--- a/addons/account_edi_facturx/tests/test_facturx.py
+++ b/addons/account_edi_facturx/tests/test_facturx.py
@@ -244,3 +244,21 @@ class TestAccountEdiFacturx(AccountEdiTestCommon):
 
         self.assertEqual(invoice.amount_total, 4610)
         self.assertEqual(len(self.env['account.move'].search([])), invoice_count + 1)
+
+    def test_invoice_edi_multicompany(self):
+        # Create taxes that will match the first line of the facturx invoice
+        my_company_id = TestAccountEdiFacturx.company_data['company'].id
+        other_company_id = TestAccountEdiFacturx.company_data_2['company'].id
+
+        common_tax_fields = dict(amount_type='percent', type_tax_use='purchase', amount=0.0)
+        self.env['account.tax'].create([
+            dict(name="OtherCompany Tax", company_id=other_company_id, sequence=10, **common_tax_fields),
+            dict(name="MyCompany Tax",    company_id=my_company_id,    sequence=20, **common_tax_fields),
+        ])
+
+        invoice = self._create_empty_vendor_bill()
+        self.update_invoice_from_file('account_edi_facturx', 'test_file', 'test_facturx.xml', invoice)
+
+        tax_ids = invoice.line_ids.tax_ids
+        self.assertEqual(len(tax_ids), 1)
+        self.assertEqual(tax_ids[0].name, "MyCompany Tax")
diff --git a/addons/account_edi_ubl/models/account_edi_format.py b/addons/account_edi_ubl/models/account_edi_format.py
index 29b93b67a4ab..fcd5f84a78ce 100644
--- a/addons/account_edi_ubl/models/account_edi_format.py
+++ b/addons/account_edi_ubl/models/account_edi_format.py
@@ -58,6 +58,7 @@ class AccountEdiFormat(models.Model):
             return self._find_value(xpath, element, namespaces)
 
         with Form(invoice.with_context(account_predictive_bills_disable_prediction=True)) as invoice_form:
+            self_ctx = self.with_company(invoice.company_id.id)
 
             # Reference
             elements = tree.xpath('//cbc:ID', namespaces=namespaces)
@@ -91,7 +92,7 @@ class AccountEdiFormat(models.Model):
                 invoice_form.invoice_incoterm_id = self.env['account.incoterms'].search([('code', '=', elements[0].text)], limit=1)
 
             # Partner
-            invoice_form.partner_id = self._retrieve_partner(
+            invoice_form.partner_id = self_ctx._retrieve_partner(
                 name=_find_value('//cac:AccountingSupplierParty/cac:Party//cbc:Name'),
                 phone=_find_value('//cac:AccountingSupplierParty/cac:Party//cbc:Telephone'),
                 mail=_find_value('//cac:AccountingSupplierParty/cac:Party//cbc:ElectronicMail'),
@@ -103,7 +104,7 @@ class AccountEdiFormat(models.Model):
             for eline in lines_elements:
                 with invoice_form.invoice_line_ids.new() as invoice_line_form:
                     # Product
-                    invoice_line_form.product_id = self._retrieve_product(
+                    invoice_line_form.product_id = self_ctx._retrieve_product(
                         default_code=_find_value('cac:Item/cac:SellersItemIdentification/cbc:ID', eline),
                         name=_find_value('cac:Item/cbc:Name', eline),
                         barcode=_find_value('cac:Item/cac:StandardItemIdentification/cbc:ID[@schemeID=\'0160\']', eline)
@@ -135,7 +136,7 @@ class AccountEdiFormat(models.Model):
                     tax_element = eline.xpath('cac:TaxTotal/cac:TaxSubtotal', namespaces=namespaces)
                     invoice_line_form.tax_ids.clear()
                     for eline in tax_element:
-                        tax = self._retrieve_tax(
+                        tax = self_ctx._retrieve_tax(
                             amount=_find_value('cbc:Percent', eline),
                             type_tax_use=invoice_form.journal_id.type
                         )
-- 
GitLab