[IMP] uom: unique reference UoM per category

Users can configure multiple references in a same
category. This might cause problems during conversion
and does not make sense.
Since this is a "late" constraint, we define it in
python to avoid conflict with existing data.
We allow user to have multiple unactive reference, but
force to have exactly one active. So when a new UoM
category is created, user have to encode the reference
unit first, and then the bigger/smaller ones.

This commit also update stock tests, since they were
redefining new reference UoM in the same category, and
finally, this commit provides some tests for this
uniqueness and custom UoM behavior.

Task #1820076
This commit is contained in:
jem-odoo
2018-04-09 10:13:18 +02:00
parent 2d32ff4bb8
commit e0ed50a724
6 changed files with 85 additions and 11 deletions
+4 -2
View File
@@ -37,7 +37,8 @@ class TestSaleMrpFlow(common.TransactionCase):
p.route_ids.add(r)
return p.save()
self.uom_kg = self.UoM.create({
self.uom_kg = self.env['uom.uom'].search([('category_id', '=', self.categ_kgm.id), ('uom_type', '=', 'reference')], limit=1)
self.uom_kg.write({
'name': 'Test-KG',
'category_id': self.categ_kgm.id,
'factor_inv': 1,
@@ -50,7 +51,8 @@ class TestSaleMrpFlow(common.TransactionCase):
'uom_type': 'smaller',
'factor': 1000.0,
'rounding': 0.001})
self.uom_unit = self.UoM.create({
self.uom_unit = self.env['uom.uom'].search([('category_id', '=', self.categ_unit.id), ('uom_type', '=', 'reference')], limit=1)
self.uom_unit.write({
'name': 'Test-Unit',
'category_id': self.categ_unit.id,
'factor': 1,
+4 -2
View File
@@ -45,7 +45,8 @@ class TestStockCommon(common.TransactionCase):
self.productE = self.ProductObj.create({'name': 'Product E', 'type': 'product'})
# Configure unit of measure.
self.uom_kg = self.UomObj.create({
self.uom_kg = self.env['uom.uom'].search([('category_id', '=', self.categ_kgm), ('uom_type', '=', 'reference')], limit=1)
self.uom_kg.write({
'name': 'Test-KG',
'category_id': self.categ_kgm,
'factor_inv': 1,
@@ -71,7 +72,8 @@ class TestStockCommon(common.TransactionCase):
'factor': 100000.0,
'rounding': 0.001})
# Check Unit
self.uom_unit = self.UomObj.create({
self.uom_unit = self.env['uom.uom'].search([('category_id', '=', self.categ_unit), ('uom_type', '=', 'reference')], limit=1)
self.uom_unit.write({
'name': 'Test-Unit',
'category_id': self.categ_unit,
'factor': 1,
@@ -15,7 +15,8 @@ class TestStockLandedCostsRounding(TestStockLandedCostsCommon):
self._load('stock_account', 'test', 'stock_valuation_account.xml')
# Define undivisible units
product_uom_unit_round_1 = self.env['uom.uom'].create({
product_uom_unit_round_1 = self.env.ref('uom.product_uom_unit')
product_uom_unit_round_1.write({
'category_id': self.ref('uom.product_uom_categ_unit'),
'name': 'Undivisible Unit(s)',
'factor': 1.0,
+5 -5
View File
@@ -51,17 +51,17 @@
<field name="factor" eval="1000"/>
<field name="uom_type">smaller</field>
</record>
<record id="product_uom_day" model="uom.uom">
<field name="name">Day(s)</field>
<field eval="uom_categ_wtime" name="category_id"/>
<field name="factor" eval="1.0"/>
</record>
<record id="product_uom_hour" model="uom.uom">
<field name="name">Hour(s)</field>
<field eval="uom_categ_wtime" name="category_id"/>
<field name="factor" eval="8.0"/>
<field name="uom_type">smaller</field>
</record>
<record id="product_uom_day" model="uom.uom">
<field name="name">Day(s)</field>
<field eval="uom_categ_wtime" name="category_id"/>
<field name="factor" eval="1.0"/>
</record>
<record id="product_uom_ton" model="uom.uom">
<field name="category_id" ref="product_uom_categ_kgm"/>
<!-- 'tonne' is the most common spelling in english-speaking countries,
+22 -1
View File
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, tools, models, _
from odoo.exceptions import UserError
from odoo.exceptions import UserError, ValidationError
class UoMCategory(models.Model):
@@ -66,6 +66,27 @@ class UoM(models.Model):
if self.uom_type == 'reference':
self.factor = 1
@api.constrains('category_id', 'uom_type', 'active')
def _check_category_reference_uniqueness(self):
""" Force the existence of only one UoM reference per category
NOTE: this is a constraint on the all table. This might not be a good practice, but this is
not possible to do it in SQL directly.
"""
category_ids = self.mapped('category_id').ids
self._cr.execute("""
SELECT C.id AS category_id, count(U.id) AS uom_count
FROM uom_category C
LEFT JOIN uom_uom U ON C.id = U.category_id AND uom_type = 'reference'
WHERE C.id IN %s
AND U.active = 't'
GROUP BY C.id
""", (tuple(category_ids),))
for uom_data in self._cr.dictfetchall():
if uom_data['uom_count'] == 0:
raise ValidationError(_("UoM category %s should have a reference unit of measure. If you just created a new category, please record the 'reference' unit first.") % (self.env['uom.category'].browse(uom_data['category_id']).name,))
if uom_data['uom_count'] > 1:
raise ValidationError(_("UoM category %s should only have one reference unit of measure.") % (self.env['uom.category'].browse(uom_data['category_id']).name,))
@api.model
def create(self, values):
if 'factor_inv' in values:
+48
View File
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests.common import TransactionCase
from odoo.exceptions import ValidationError
class TestUom(TransactionCase):
@@ -47,3 +48,50 @@ class TestUom(TransactionCase):
qty = self.uom_unit._compute_quantity(2, product_uom)
self.assertEquals(qty, 1, "Converted quantity should be rounded up.")
def test_30_reference_uniqueness(self):
""" Check the uniqueness of the reference UoM in a category """
time_category = self.env['uom.category'].search([('measure_type', '=', 'time')], limit=1)
with self.assertRaises(ValidationError):
self.env['uom.uom'].create({
'name': 'Second Time Reference',
'factor_inv': 1,
'uom_type': 'reference',
'rounding': 1.0,
'category_id': time_category.id
})
def test_40_custom_uom(self):
""" A custom UoM is an UoM in a category without measurement type. It should behave like a normal UoM """
category = self.env['uom.category'].create({
'name': 'Custom UoM category',
})
# create the reference
self.env['uom.uom'].create({
'name': 'Reference UoM of my category',
'factor_inv': 1,
'uom_type': 'reference',
'rounding': 1.0,
'category_id': category.id
})
# we can create another UoM now
self.env['uom.uom'].create({
'name': 'Bigger UoM of my category',
'factor_inv': 42,
'uom_type': 'bigger',
'rounding': 0.5,
'category_id': category.id
})
# we can not create a second reference in custom category
with self.assertRaises(ValidationError):
self.env['uom.uom'].create({
'name': 'Second Time Reference',
'factor_inv': 1,
'uom_type': 'reference',
'rounding': 1.0,
'category_id': category.id
})