[FIX] account_edi : search checks for identically named product first
1. If the name of a product on an imported EDI document is a substring of the name of a product on the DB, said product could be used instead of the correct one, depending on their order (i.e. ID) in the DB. Example: There are 2 products named "Placement" and "Displacement" in a DB. A PDF is imported. It contains a product named "Placement". If the "Displacement" product on the DB appears before the "Placement" one, the product on the AML will be set to the "Displacement" one. To fix this, we first check if there is a product identically named to the one in the EDI document. 2. In case a product from the DB is used, if possible, the UoM used should be the one present on the EDI document. Thus there should be a check to ensure that the UoM category on said document matches the one of the product's UoM before assigning it, to avoid a compatibility error. opw-3470969 closes odoo/odoo#143252 X-original-commit: 84911fe007b229b97081a6f5aa298e7b213d43be Signed-off-by: Laurent Smet (las) <las@odoo.com> Co-authored-by: Julien Van Roy <juvr@odoo.com>
This commit is contained in:
co-authored by
Julien Van Roy
parent
15295e317f
commit
027e45596e
@@ -245,17 +245,21 @@ class ProductProduct(models.Model):
|
||||
# cut Sales Description from the name
|
||||
name = name.split('\n')[0]
|
||||
domains = []
|
||||
for value, domain in (
|
||||
(name, ('name', 'ilike', name)),
|
||||
(default_code, ('default_code', '=', default_code)),
|
||||
(barcode, ('barcode', '=', barcode)),
|
||||
):
|
||||
if value is not None:
|
||||
domains.append([domain])
|
||||
if default_code:
|
||||
domains.append([('default_code', '=', default_code)])
|
||||
if barcode:
|
||||
domains.append([('barcode', '=', barcode)])
|
||||
|
||||
domain = expression.AND([
|
||||
expression.OR(domains),
|
||||
self.env['product.product']._check_company_domain(company),
|
||||
extra_domain,
|
||||
])
|
||||
return self.env['product.product'].search(domain, limit=1)
|
||||
# Search for the product with the exact name, then ilike the name
|
||||
name_domains = [('name', '=', name)], [('name', 'ilike', name)] if name else []
|
||||
for name_domain in name_domains:
|
||||
product = self.env['product.product'].search(
|
||||
expression.AND([
|
||||
expression.OR(domains + [name_domain]),
|
||||
self.env['product.product']._check_company_domain(company),
|
||||
]),
|
||||
limit=1,
|
||||
)
|
||||
if product:
|
||||
return product
|
||||
return self.env['product.product']
|
||||
|
||||
@@ -685,10 +685,16 @@ class AccountEdiCommon(models.AbstractModel):
|
||||
|
||||
# Set the values on the line_form
|
||||
invoice_line.quantity = inv_line_vals['quantity']
|
||||
if inv_line_vals.get('product_uom_id'):
|
||||
if not inv_line_vals.get('product_uom_id'):
|
||||
logs.append(
|
||||
_("Could not retrieve the unit of measure for line with label '%s'.", invoice_line.name))
|
||||
elif not invoice_line.product_id:
|
||||
# no product set on the line, no need to check uom compatibility
|
||||
invoice_line.product_uom_id = inv_line_vals['product_uom_id']
|
||||
else:
|
||||
logs.append(_("Could not retrieve the unit of measure for line with label '%s'.", invoice_line.name))
|
||||
elif inv_line_vals['product_uom_id'].category_id == invoice_line.product_id.product_tmpl_id.uom_id.category_id:
|
||||
# needed to check that the uom is compatible with the category of the product
|
||||
invoice_line.product_uom_id = inv_line_vals['product_uom_id']
|
||||
|
||||
invoice_line.price_unit = inv_line_vals['price_unit']
|
||||
invoice_line.discount = inv_line_vals['discount']
|
||||
invoice_line.tax_ids = inv_line_vals['taxes']
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import test_partner_peppol_fields
|
||||
from . import test_ubl_cii
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from lxml import etree
|
||||
from odoo import Command
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestAccountEdiUblCii(AccountTestInvoicingCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls, chart_template_ref=None):
|
||||
super().setUpClass(chart_template_ref=chart_template_ref)
|
||||
|
||||
cls.uom_units = cls.env.ref('uom.product_uom_unit')
|
||||
cls.uom_dozens = cls.env.ref('uom.product_uom_dozen')
|
||||
|
||||
cls.displace_prdct = cls.env['product.product'].create({
|
||||
'name': 'Displacement',
|
||||
'uom_id': cls.uom_units.id,
|
||||
'standard_price': 90.0,
|
||||
})
|
||||
|
||||
cls.place_prdct = cls.env['product.product'].create({
|
||||
'name': 'Placement',
|
||||
'uom_id': cls.uom_units.id,
|
||||
'standard_price': 80.0,
|
||||
})
|
||||
|
||||
def test_import_product(self):
|
||||
line_vals = [
|
||||
{
|
||||
'product_id': self.place_prdct.id,
|
||||
'product_uom_id': self.uom_units.id,
|
||||
'tax_ids': [self.company_data_2['default_tax_sale'].id]
|
||||
}, {
|
||||
'product_id': self.displace_prdct.id,
|
||||
'product_uom_id': self.uom_units.id,
|
||||
'tax_ids': [self.company_data_2['default_tax_sale'].id]
|
||||
}, {
|
||||
'product_id': self.displace_prdct.id,
|
||||
'product_uom_id': self.uom_units.id,
|
||||
'tax_ids': [self.company_data_2['default_tax_sale'].id]
|
||||
}, {
|
||||
'product_id': self.displace_prdct.id,
|
||||
'product_uom_id': self.uom_dozens.id,
|
||||
'tax_ids': [self.company_data_2['default_tax_sale'].id]
|
||||
}
|
||||
]
|
||||
company = self.company_data_2['company']
|
||||
company.country_id = self.env['res.country'].search([('code', '=', 'FR')])
|
||||
company.vat = 'FR23334175221'
|
||||
company.email = 'company@site.ext'
|
||||
company.phone = '+33499999999'
|
||||
company.zip = '78440'
|
||||
|
||||
company.partner_id.ubl_cii_format = 'facturx'
|
||||
company.partner_id.bank_ids = [Command.create({
|
||||
'acc_number': '999999',
|
||||
'partner_id': company.partner_id.id,
|
||||
'acc_holder_name': 'The Chosen One'
|
||||
})]
|
||||
|
||||
invoice = self.env['account.move'].create({
|
||||
'company_id': company.id,
|
||||
'partner_id': company.partner_id.id,
|
||||
'move_type': 'out_invoice',
|
||||
'journal_id': self.company_data_2['default_journal_sale'].id,
|
||||
'invoice_line_ids': [Command.create(vals) for vals in line_vals],
|
||||
})
|
||||
invoice.action_post()
|
||||
|
||||
template = self.env.ref('account.email_template_edi_invoice', raise_if_not_found=False)
|
||||
print_wiz = self.env['account.move.send'].create({
|
||||
'move_ids': invoice.ids,
|
||||
'mail_template_id': template.id
|
||||
})
|
||||
print_wiz.checkbox_download = False
|
||||
print_wiz.checkbox_send_mail = False
|
||||
print_wiz.checkbox_send_by_post = False
|
||||
print_wiz.checkbox_ubl_cii_xml = True
|
||||
print_wiz.action_send_and_print()
|
||||
|
||||
facturx_attachment = invoice.ubl_cii_xml_id
|
||||
xml_tree = etree.fromstring(facturx_attachment.raw)
|
||||
|
||||
# Testing the case where a product on the invoice has a UoM with a different category than the one in the DB
|
||||
wrong_uom_line = xml_tree.findall('./{*}SupplyChainTradeTransaction/{*}IncludedSupplyChainTradeLineItem')[1]
|
||||
wrong_uom_line.find('./{*}SpecifiedLineTradeDelivery/{*}BilledQuantity').attrib['unitCode'] = 'HUR'
|
||||
|
||||
facturx_attachment.raw = etree.tostring(xml_tree)
|
||||
new_invoice = invoice.journal_id._create_document_from_attachment(facturx_attachment.ids)
|
||||
|
||||
self.assertRecordValues(new_invoice.invoice_line_ids, line_vals)
|
||||
Reference in New Issue
Block a user