[FIX] stock: allow change of reference unit of measure
closes odoo/odoo#57485 Related: odoo/enterprise#17365 Signed-off-by: Arnold Moyaux <amoyaux@users.noreply.github.com>
This commit is contained in:
@@ -23,4 +23,15 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_uom_categ_form_view" model="ir.ui.view">
|
||||
<field name="name">uom.category.form</field>
|
||||
<field name="model">uom.category</field>
|
||||
<field name="inherit_id" ref="uom.product_uom_categ_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="rounding" position="after">
|
||||
<field name="l10n_ar_afip_code"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -10,4 +10,15 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_uom_categ_form_view_inherit_l10n_in" model="ir.ui.view">
|
||||
<field name="name">uom.category.form</field>
|
||||
<field name="model">uom.category</field>
|
||||
<field name="inherit_id" ref="uom.product_uom_categ_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="category_id" position="after">
|
||||
<field name="l10n_in_code"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
<menuitem
|
||||
action="uom.product_uom_form_action" id="menu_purchase_uom_form_action"
|
||||
parent="purchase.menu_unit_of_measure_in_config_purchase" sequence="5" groups="uom.group_uom"/>
|
||||
parent="purchase.menu_unit_of_measure_in_config_purchase" sequence="5" groups="base.group_no_one"/>
|
||||
|
||||
<menuitem
|
||||
action="uom.product_uom_categ_form_action" id="menu_purchase_uom_categ_form_action"
|
||||
|
||||
@@ -95,9 +95,9 @@
|
||||
<menuitem action="product_template_action" id="menu_product_template_action" parent="product_menu_catalog" sequence="1" active="False"/>
|
||||
<menuitem id="prod_config_main" name="Products" parent="menu_sale_config" sequence="5"/>
|
||||
<menuitem id="menu_products" action="product.product_normal_action_sell" parent="product_menu_catalog" groups="product.group_product_variant" sequence="2" active="False"/>
|
||||
<menuitem id="next_id_16" name="Units of Measure" parent="sale.prod_config_main" sequence="6" groups="uom.group_uom" active="False"/>
|
||||
<menuitem action="uom.product_uom_form_action" id="menu_product_uom_form_action" parent="sale.prod_config_main" sequence="7" groups="uom.group_uom" active="False"/>
|
||||
<menuitem action="uom.product_uom_categ_form_action" id="menu_product_uom_categ_form_action" parent="sale.prod_config_main" sequence="8" groups="uom.group_uom" active="False"/>
|
||||
<menuitem id="next_id_16" name="Units of Measure" parent="menu_sale_config" sequence="6" groups="uom.group_uom" active="False"/>
|
||||
<menuitem action="uom.product_uom_form_action" id="menu_product_uom_form_action" parent="next_id_16" sequence="7" groups="base.group_no_one" active="False"/>
|
||||
<menuitem action="uom.product_uom_categ_form_action" id="menu_product_uom_categ_form_action" parent="next_id_16" sequence="8" active="False"/>
|
||||
<menuitem id="menu_product_pricelist_main" name="Pricelists" parent="product_menu_catalog" action="product.product_pricelist_action2" groups="product.group_product_pricelist" sequence="3" active="False"/>
|
||||
|
||||
<record id="sale_order_view_activity" model="ir.ui.view">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
<menuitem id="menu_stock_uom_form_action" action="uom.product_uom_form_action"
|
||||
name="UoM"
|
||||
parent="product_uom_menu" sequence="35"/>
|
||||
parent="product_uom_menu" sequence="35" groups="base.group_no_one"/>
|
||||
|
||||
<menuitem id="menu_stock_inventory_control" name="Products" parent="menu_stock_root" sequence="4"/>
|
||||
|
||||
|
||||
@@ -13,11 +13,42 @@ class UoMCategory(models.Model):
|
||||
|
||||
name = fields.Char('Unit of Measure Category', required=True, translate=True)
|
||||
|
||||
uom_ids = fields.One2many('uom.uom', 'category_id')
|
||||
reference_uom_id = fields.Many2one('uom.uom', "Reference UoM", store=False, help="Dummy field to keep track of reference uom change")
|
||||
|
||||
@api.onchange('uom_ids')
|
||||
def _onchange_uom_ids(self):
|
||||
if len(self.uom_ids) == 1:
|
||||
self.uom_ids[0].uom_type = 'reference'
|
||||
self.uom_ids[0].factor = 1
|
||||
else:
|
||||
reference_count = sum(uom.uom_type == 'reference' for uom in self.uom_ids)
|
||||
if reference_count == 0 and self._origin.id:
|
||||
return {
|
||||
'warning': {
|
||||
'title': _('Warning!'),
|
||||
'message': _("UoM category %s should have a reference unit of measure.") % self.name
|
||||
}
|
||||
}
|
||||
if self.reference_uom_id:
|
||||
new_reference = self.uom_ids.filtered(lambda o: o.uom_type == 'reference' and o._origin.id != self.reference_uom_id.id)
|
||||
else:
|
||||
new_reference = self.uom_ids.filtered(lambda o: o.uom_type == 'reference' and o._origin.uom_type != 'reference')
|
||||
if new_reference:
|
||||
other_uoms = self.uom_ids - new_reference
|
||||
for uom in other_uoms:
|
||||
uom.factor = uom._origin.factor / (new_reference._origin.factor or 1)
|
||||
if uom.factor > 1:
|
||||
uom.uom_type = 'smaller'
|
||||
else:
|
||||
uom.uom_type = 'bigger'
|
||||
self.reference_uom_id = new_reference._origin.id
|
||||
|
||||
|
||||
class UoM(models.Model):
|
||||
_name = 'uom.uom'
|
||||
_description = 'Product Unit of Measure'
|
||||
_order = "name"
|
||||
_order = "factor DESC, id"
|
||||
|
||||
def _unprotected_uom_xml_ids(self):
|
||||
return [
|
||||
@@ -46,6 +77,8 @@ class UoM(models.Model):
|
||||
('reference', 'Reference Unit of Measure for this category'),
|
||||
('smaller', 'Smaller than the reference Unit of Measure')], 'Type',
|
||||
default='reference', required=True)
|
||||
ratio = fields.Float('Combined Ratio', compute='_compute_ratio', inverse='_set_ratio', store=False)
|
||||
color = fields.Integer('Color', compute='_compute_color')
|
||||
|
||||
_sql_constraints = [
|
||||
('factor_gt_zero', 'CHECK (factor!=0)', 'The conversion ratio for a unit of measure cannot be 0!'),
|
||||
@@ -53,11 +86,48 @@ class UoM(models.Model):
|
||||
('factor_reference_is_one', "CHECK((uom_type = 'reference' AND factor = 1.0) OR (uom_type != 'reference'))", "The reference unit must have a conversion factor equal to 1.")
|
||||
]
|
||||
|
||||
def _check_category_reference_uniqueness(self):
|
||||
for category in self.category_id:
|
||||
if not category.uom_ids:
|
||||
continue
|
||||
reference_count = sum(
|
||||
uom.uom_type == 'reference' for uom in category.uom_ids)
|
||||
if reference_count > 1:
|
||||
raise ValidationError(_("UoM category %s should only have one reference unit of measure.") % category.name)
|
||||
elif reference_count == 0:
|
||||
raise ValidationError(_("UoM category %s should have a reference unit of measure.") % category.name)
|
||||
|
||||
@api.depends('factor')
|
||||
def _compute_factor_inv(self):
|
||||
for uom in self:
|
||||
uom.factor_inv = uom.factor and (1.0 / uom.factor) or 0.0
|
||||
|
||||
@api.depends('uom_type', 'factor')
|
||||
def _compute_ratio(self):
|
||||
for uom in self:
|
||||
if uom.uom_type == 'reference':
|
||||
uom.ratio = 1
|
||||
elif uom.uom_type == 'bigger':
|
||||
uom.ratio = uom.factor_inv
|
||||
else:
|
||||
uom.ratio = uom.factor
|
||||
|
||||
def _set_ratio(self):
|
||||
if self.uom_type == 'reference':
|
||||
self.factor = 1
|
||||
elif self.uom_type == 'bigger':
|
||||
self.factor = 1 / self.ratio
|
||||
else:
|
||||
self.factor = self.ratio
|
||||
|
||||
@api.depends('uom_type')
|
||||
def _compute_color(self):
|
||||
for uom in self:
|
||||
if uom.uom_type == 'reference':
|
||||
uom.color = 7
|
||||
else:
|
||||
uom.color = 0
|
||||
|
||||
@api.onchange('uom_type')
|
||||
def _onchange_uom_type(self):
|
||||
if self.uom_type == 'reference':
|
||||
@@ -80,49 +150,26 @@ class UoM(models.Model):
|
||||
}
|
||||
}
|
||||
|
||||
@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.env['uom.uom'].flush(['category_id', 'uom_type', 'active'])
|
||||
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' AND U.active = 't'
|
||||
WHERE C.id IN %s
|
||||
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.constrains('category_id')
|
||||
def _validate_uom_category(self):
|
||||
for uom in self:
|
||||
reference_uoms = self.env['uom.uom'].search([
|
||||
('category_id', '=', uom.category_id.id),
|
||||
('uom_type', '=', 'reference')])
|
||||
if len(reference_uoms) > 1:
|
||||
raise ValidationError(_("UoM category %s should only have one reference unit of measure.") % (self.category_id.name))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for values in vals_list:
|
||||
if 'factor_inv' in values:
|
||||
factor_inv = values.pop('factor_inv')
|
||||
values['factor'] = factor_inv and (1.0 / factor_inv) or 0.0
|
||||
return super(UoM, self).create(vals_list)
|
||||
res = super(UoM, self).create(vals_list)
|
||||
res._check_category_reference_uniqueness()
|
||||
return res
|
||||
|
||||
def write(self, values):
|
||||
if 'factor_inv' in values:
|
||||
factor_inv = values.pop('factor_inv')
|
||||
values['factor'] = factor_inv and (1.0 / factor_inv) or 0.0
|
||||
return super(UoM, self).write(values)
|
||||
|
||||
res = super(UoM, self).write(values)
|
||||
if ('uom_type' not in values or values['uom_type'] != 'reference') and\
|
||||
not self.env.context.get('allow_to_change_reference'):
|
||||
self._check_category_reference_uniqueness()
|
||||
return res
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_master_data(self):
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<group name="uom_details">
|
||||
<field name="name"/>
|
||||
<field name="category_id"/>
|
||||
<field name="uom_type"/>
|
||||
<field name="uom_type" readonly="1"/>
|
||||
<label for="factor"
|
||||
attrs="{'invisible':[('uom_type','!=','smaller')]}"/>
|
||||
<div attrs="{'invisible':[('uom_type','!=','smaller')]}" class="o_row">
|
||||
@@ -93,7 +93,23 @@
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="reference_uom_id" invisible="1"/>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Units of Measure" name="uom_lines">
|
||||
<field name="uom_ids" force_save="1" context="{'default_uom_type': 'smaller', 'default_category_id': id}">
|
||||
<tree editable="bottom">
|
||||
<field name="name" decoration-bf="uom_type == 'reference'"/>
|
||||
<field name="uom_type" decoration-bf="uom_type == 'reference'"/>
|
||||
<field name="factor" invisible="1"/>
|
||||
<field name="factor_inv" invisible="1"/>
|
||||
<field name="ratio" string="Ratio" attrs="{'readonly': [('uom_type', '=', 'reference')]}" digits="[42,5]"/>
|
||||
<field name="active"/>
|
||||
<field name="rounding" digits="[42, 5]"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
@@ -103,17 +119,30 @@
|
||||
<field name="name">uom.category.tree</field>
|
||||
<field name="model">uom.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Units of Measure categories" editable="bottom">
|
||||
<tree string="Units of Measure categories">
|
||||
<field name="name"/>
|
||||
<field name="uom_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="uom_categ_view_search" model="ir.ui.view">
|
||||
<field name="name">uom.category.view.search</field>
|
||||
<field name="model">uom.category</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search UoM Category">
|
||||
<field name="name"/>
|
||||
<field name="uom_ids"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_uom_categ_form_action" model="ir.actions.act_window">
|
||||
<field name="name">Units of Measure Categories</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">uom.category</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="context">{'allow_to_change_reference': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Add a new unit of measure category
|
||||
|
||||
Reference in New Issue
Block a user