Files
odoo_source/addons/product/tests/test_barcode.py
T
Paweł Fertyk b2638565de [IMP] product: make barcode unique only within a company
Some clients use the same barcode for products in different companies,
with different configuration. It is inconvenient for those users to have
to ensure the uniqueness of barcodes across all companies.

This commit will allow products to have duplicated barcodes, as long as
they are assigned to different companies. Products without a company are
treated as belonging to all companies. Product variants require unique barcodes.

The validation needs to be triggered both on barcode and company_id, so
product template (where company_id is stored) was also adjusted.

NOTE: when a barcode is duplicated in 2 companies, an attempt to remove
both the company and the barcode from a product will result in an
error, despite the fact that a product without a company and a barcode
would have a correct configuration. This happens because the barcode
uniqueness check is performed before changing the barcode, but after
changing the company. A workaround for this problem is to remove the
barcode first, save the product, and remove the company.

Task: 3290154
Related task: 3270732

Related: odoo/enterprise#42541
Part-of: odoo/odoo#122511
2023-09-28 10:22:03 +00:00

168 lines
6.5 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.exceptions import ValidationError
from odoo.fields import Command
from odoo.tests import TransactionCase, tagged
@tagged('post_install', '-at_install')
class TestProductBarcode(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env['product.product'].create([
{'name': 'BC1', 'barcode': '1'},
{'name': 'BC2', 'barcode': '2'},
])
cls.size_attribute = cls.env['product.attribute'].create({
'name': 'Size',
'value_ids': [
Command.create({'name': 'SMALL'}),
Command.create({'name': 'LARGE'}),
]
})
cls.size_attribute_s, cls.size_attribute_l = cls.size_attribute.value_ids
cls.template = cls.env['product.template'].create({'name': 'template'})
cls.template.write({
'attribute_line_ids': [Command.create({
'attribute_id': cls.size_attribute.id,
'value_ids': [
Command.link(cls.size_attribute_s.id),
Command.link(cls.size_attribute_l.id),
],
})]
})
def test_blank_barcodes_allowed(self):
"""Makes sure duplicated blank barcodes are allowed."""
for i in range(2):
self.env['product.product'].create({'name': f'BC_{i}'})
def test_false_barcodes_allowed(self):
"""Makes sure duplicated False barcodes are allowed."""
for i in range(2):
self.env['product.product'].create({'name': f'BC_{i}', 'barcode': False})
def test_duplicated_barcode(self):
"""Tests for simple duplication."""
with self.assertRaises(ValidationError):
self.env['product.product'].create({'name': 'BC3', 'barcode': '1'})
def test_duplicated_barcode_in_batch_edit(self):
"""Tests for duplication in batch edits."""
batch = [
{'name': 'BC3', 'barcode': '3'},
{'name': 'BC4', 'barcode': '4'},
]
self.env['product.product'].create(batch)
batch.append({'name': 'BC5', 'barcode': '1'})
with self.assertRaises(ValidationError):
self.env['product.product'].create(batch)
def test_test_duplicated_barcode_error_msg_content(self):
"""Validates the error message shown when duplicated barcodes are found."""
batch = [
{'name': 'BC3', 'barcode': '3'},
{'name': 'BC4', 'barcode': '3'},
{'name': 'BC5', 'barcode': '4'},
{'name': 'BC6', 'barcode': '4'},
{'name': 'BC7', 'barcode': '1'},
]
try:
self.env['product.product'].create(batch)
except ValidationError as exc:
assert 'Barcode "3" already assigned to product(s): BC3, BC4' in exc.args[0]
assert 'Barcode "4" already assigned to product(s): BC5, BC6' in exc.args[0]
assert 'Barcode "1" already assigned to product(s): BC1' in exc.args[0]
def test_delete_package_and_use_its_barcode_in_product(self):
""" Test that the barcode of the package can be used when the package is removed from the product."""
product = self.env['product.product'].create({
'name': 'product',
'packaging_ids': [(0, 0, {
'name': 'packing',
'barcode': '1234',
})]
})
package = product.packaging_ids
self.assertTrue(package.exists())
self.assertEqual(package.barcode, '1234')
product.packaging_ids = False
self.assertFalse(package.exists())
product.barcode = '1234'
def test_duplicated_barcodes_are_allowed_for_different_companies(self):
"""Barcode needs to be unique only withing the same company"""
company_a = self.env.companies[0]
company_b = self.env.companies[1]
allowed_products = [
# Allowed, barcode doesn't exist yet
{'name': 'A1', 'barcode': '3', 'company_id': company_a.id},
# Allowed, barcode exists (A1), but for a different company
{'name': 'A2', 'barcode': '3', 'company_id': company_b.id},
]
forbidden_products = [
# Forbidden, collides with BC1
{'name': 'F1', 'barcode': '1', 'company_id': False},
# Forbidden, collides with BC1
{'name': 'F2', 'barcode': '1', 'company_id': company_a.id},
# Forbidden, collides with BC2
{'name': 'F3', 'barcode': '2', 'company_id': company_b.id},
# Forbidden, collides with A1
{'name': 'F4', 'barcode': '3', 'company_id': company_a.id},
# Forbidden, collides with A2
{'name': 'F5', 'barcode': '3', 'company_id': company_b.id},
# Forbidden, collides with A1 and A2
{'name': 'F6', 'barcode': '3', 'company_id': False},
]
for product in allowed_products:
self.env['product.product'].create(product)
for product in forbidden_products:
with self.assertRaises(ValidationError):
self.env['product.product'].create(product)
def test_duplicated_barcodes_in_product_variants(self):
"""
Create 2 variants with different barcodes and same company.
Assign a duplicated barcode to one of them while changing the company.
Barcode validation should be triggered and a duplicated barcode should be detected.
"""
company_a = self.env.company
company_b = self.env['res.company'].create({'name': 'CB'})
variant_1 = self.template.product_variant_ids[0]
variant_2 = self.template.product_variant_ids[1]
variant_1.barcode = 'barcode_1'
variant_1.company_id = company_a
variant_2.barcode = 'barcode_2'
variant_2.company_id = company_a
with self.assertRaises(ValidationError):
variant_2.write({
'barcode': 'barcode_1',
'company_id': company_b
})
# Variant 1 was not updated
self.assertEqual(variant_2.barcode, 'barcode_2')
self.assertEqual(variant_2.company_id, company_a)
variant_2.write({
'barcode': 'barcode_3',
'company_id': company_b
})
self.assertEqual(variant_1.barcode, 'barcode_1')
self.assertEqual(variant_1.company_id, company_b)
self.assertEqual(variant_2.barcode, 'barcode_3')
self.assertEqual(variant_2.company_id, company_b)