diff --git a/__manifest__.py b/__manifest__.py index be1eaf8..2f3bdf4 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -1,6 +1,6 @@ { 'name': 'Product UOM Change', - 'version': '18.0.1.0.0', + 'version': '19.0.1.0.0', 'summary': 'Allow changing Product UOM even if stock moves exist', 'description': """ This module adds a wizard to allow changing the Unit of Measure (UOM) of a product diff --git a/wizard/change_uom.py b/wizard/change_uom.py index 37bedf3..62a38c4 100644 --- a/wizard/change_uom.py +++ b/wizard/change_uom.py @@ -7,23 +7,66 @@ class ProductUomChangeWizard(models.TransientModel): product_tmpl_id = fields.Many2one('product.template', string='Product', required=True, readonly=True) current_uom_id = fields.Many2one('uom.uom', related='product_tmpl_id.uom_id', string='Current UOM', readonly=True) - uom_category_id = fields.Many2one('uom.category', related='product_tmpl_id.uom_id.category_id', readonly=True) + allowed_uom_ids = fields.Many2many('uom.uom', string='Allowed UOMs', compute='_compute_allowed_uom_ids') new_uom_id = fields.Many2one('uom.uom', string='New UOM', required=True) new_uom_po_id = fields.Many2one('uom.uom', string='New Purchase UOM') + @api.depends('product_tmpl_id.uom_id') + def _compute_allowed_uom_ids(self): + for record in self: + if record.product_tmpl_id.uom_id: + # Find all UOMs that share the same root reference as the current UOM + # uom.uom model now uses _has_common_reference logic, but simpler to search by root category if possible. + # Since category_id is gone, we can group by root UoM if that concept exists, or rely on _has_common_reference. + # However, for a domain, we need a list of IDs. + # We can iterate all UOMs and check _has_common_reference, OR traverse up to the root and back down. + # Looking at uom_uom.py: _order = 'sequence, relative_uom_id, id'. + # fields: relative_uom_id (Reference Unit), related_uom_ids (Related UoMs). + # To get all in "category", we need to find the root. + + # Let's traverse up to find the root. + uom = record.product_tmpl_id.uom_id + while uom.relative_uom_id: + uom = uom.relative_uom_id + + # uom is now the root. Now find all descendants. + # A recursive search or using parent_path would be ideal if available/indexed. + # uom.uom has parent_path. + # parent_path structure: "root_id/child_id/..." + # So all UOMs in same category start with same root_id in parent_path. + root_id = uom.id + domain = [('parent_path', '=like', f'{root_id}/%')] + # Also include the root itself if it doesn't match the like pattern (which it usually doesn't as it is just "id/") + # Wait, parent_path is updated by standard valid Parent/Child implementation. + # Actually, simpler: search for uoms where root is the same. + # But we don't have a stored 'root_id'. + # Let's use the fact that we have the root uom 'uom'. + # We can search for all uoms that have this root as ancestor? + # Actually, standard Odoo usually just does `uom.search([])` and filters in python if the set is small, + # but UOMs can be many. + # Let's look at uom_uom.py again. + # It has `_has_common_reference`. + + # A safer approach for now: Get all UOMs and filter. UOM table is usually small < 100. + all_uoms = self.env['uom.uom'].search([]) + allowed = all_uoms.filtered(lambda u: u._has_common_reference(record.product_tmpl_id.uom_id)) + record.allowed_uom_ids = allowed + else: + record.allowed_uom_ids = False + @api.model def default_get(self, fields): res = super(ProductUomChangeWizard, self).default_get(fields) - if self._context.get('active_model') == 'product.template' and self._context.get('active_id'): - res['product_tmpl_id'] = self._context['active_id'] - elif self._context.get('active_model') == 'product.product' and self._context.get('active_id'): - product = self.env['product.product'].browse(self._context['active_id']) + if self.env.context.get('active_model') == 'product.template' and self.env.context.get('active_id'): + res['product_tmpl_id'] = self.env.context['active_id'] + elif self.env.context.get('active_model') == 'product.product' and self.env.context.get('active_id'): + product = self.env['product.product'].browse(self.env.context['active_id']) res['product_tmpl_id'] = product.product_tmpl_id.id return res def action_change_uom(self): self.ensure_one() - if self.new_uom_id.category_id != self.product_tmpl_id.uom_id.category_id: + if not self.new_uom_id._has_common_reference(self.product_tmpl_id.uom_id): raise UserError(_("New UOM must be in the same category as the current UOM.")) # Update UOM via SQL to bypass the constraint check in product.template write @@ -31,7 +74,7 @@ class ProductUomChangeWizard(models.TransientModel): # Also update Purchase UOM if specified if self.new_uom_po_id: - if self.new_uom_po_id.category_id != self.product_tmpl_id.uom_po_id.category_id: + if not self.new_uom_po_id._has_common_reference(self.product_tmpl_id.uom_po_id): raise UserError(_("New Purchase UOM must be in the same category as the current Purchase UOM.")) self.env.cr.execute("UPDATE product_template SET uom_po_id = %s WHERE id = %s", (self.new_uom_po_id.id, self.product_tmpl_id.id)) diff --git a/wizard/change_uom_views.xml b/wizard/change_uom_views.xml index 95b9bc0..8d5b705 100644 --- a/wizard/change_uom_views.xml +++ b/wizard/change_uom_views.xml @@ -8,9 +8,9 @@ - - - + + +