diff --git a/modules/stock/move.py b/modules/stock/move.py index 93baa0131f..133d8922b3 100644 --- a/modules/stock/move.py +++ b/modules/stock/move.py @@ -538,6 +538,19 @@ class Move(Workflow, ModelSQL, ModelView): def on_change_with_product_uom_category(self, name=None): return self.product.default_uom_category if self.product else None + @classmethod + def default_internal_quantity(cls): + # default value before compute of the field + return 0 + + @fields.depends('quantity', 'unit', 'product') + def on_change_with_internal_quantity(self): + pool = Pool() + UoM = pool.get('product.uom') + if self.quantity is not None and self.unit and self.product: + return UoM.compute_qty( + self.unit, self.quantity, self.product.default_uom, round=True) + @fields.depends('from_location') def on_change_with_from_location_name(self, name=None): if self.from_location: @@ -724,13 +737,6 @@ class Move(Workflow, ModelSQL, ModelView): new_cost_price = cost_price return round_price(new_cost_price) - @staticmethod - def _get_internal_quantity(quantity, unit, product): - Uom = Pool().get('product.uom') - internal_quantity = Uom.compute_qty( - unit, quantity, product.default_uom, round=True) - return internal_quantity - def set_effective_date(self): pool = Pool() Date = pool.get('ir.date') @@ -974,33 +980,36 @@ class Move(Workflow, ModelSQL, ModelView): default.setdefault('outcome_moves', None) return super().copy(moves, default=default) + def compute_fields(self, field_names=None): + cls = self.__class__ + values = super().compute_fields(field_names=field_names) + if (not field_names + or (cls.internal_quantity.on_change_with & field_names)): + internal_quantity = self.on_change_with_internal_quantity() + if getattr(self, 'internal_quantity', None) != internal_quantity: + values['internal_quantity'] = internal_quantity + return values + @classmethod def create(cls, vlist): pool = Pool() Product = pool.get('product.product') - Uom = pool.get('product.uom') context = Transaction().context vlist = [x.copy() for x in vlist] # Use ordered dict to optimize cache alignment - products, units = OrderedDict(), OrderedDict() + products = OrderedDict() for vals in vlist: if vals.get('product') is not None: products[vals['product']] = None - if vals.get('unit') is not None: - units[vals['unit']] = None id2product = {p.id: p for p in Product.browse(products.keys())} - id2unit = {u.id: u for u in Uom.browse(units.keys())} for vals in vlist: assert vals.get('state', cls.default_state() ) in ['draft', 'staging'] - product = id2product[int(vals['product'])] - unit = id2unit[int(vals['unit'])] - internal_quantity = cls._get_internal_quantity( - vals['quantity'], unit, product) - vals['internal_quantity'] = internal_quantity - if context.get('_product_replacement', True): - vals['product'] = product.replacement.id + if vals.get('product') is not None: + product = id2product[int(vals['product'])] + if context.get('_product_replacement', True): + vals['product'] = product.replacement.id moves = super().create(vlist) cls.check_period_closed(moves) return moves @@ -1025,28 +1034,17 @@ class Move(Workflow, ModelSQL, ModelView): super().write(*args) - to_write = [] unit_price_update = [] actions = iter(args) for moves, values in zip(actions, actions): if any(f not in cls._allow_modify_closed_period for f in values): cls.check_period_closed(moves) for move in moves: - internal_quantity = cls._get_internal_quantity( - move.quantity, move.unit, move.product) - if (internal_quantity != move.internal_quantity - and internal_quantity - != values.get('internal_quantity')): - to_write.extend(([move], { - 'internal_quantity': internal_quantity, - })) if (move.state == 'done' and ('unit_price' in values or 'currency' in values)): unit_price_update.append(move) - if to_write: - cls.write(*to_write) if unit_price_update: cls.write(unit_price_update, {'unit_price_updated': True}) diff --git a/modules/stock_shipment_measurements/stock.py b/modules/stock_shipment_measurements/stock.py index 81efd60315..31fcba255d 100644 --- a/modules/stock_shipment_measurements/stock.py +++ b/modules/stock_shipment_measurements/stock.py @@ -84,8 +84,10 @@ class Move(metaclass=PoolMeta): "Internal Volume", readonly=True, help="The volume of the moved product in liter.") - @classmethod - def _get_internal_weight(cls, quantity, unit, product): + @fields.depends( + 'quantity', 'unit', 'product', + methods=['on_change_with_internal_quantity']) + def on_change_with_internal_weight(self): pool = Pool() Uom = pool.get('product.uom') ModelData = pool.get('ir.model.data') @@ -94,19 +96,22 @@ class Move(metaclass=PoolMeta): # Use first the weight from product_measurements # as it could include some handling weight - if product.weight is not None: - internal_quantity = cls._get_internal_quantity( - quantity, unit, product) - return Uom.compute_qty( - product.weight_uom, internal_quantity * product.weight, kg, - round=False) - elif unit.category == kg.category: - return Uom.compute_qty(unit, quantity, kg, round=False) - else: - return None + if self.product and self.product.weight is not None: + internal_quantity = self.on_change_with_internal_quantity() + if internal_quantity is not None: + return Uom.compute_qty( + self.product.weight_uom, + internal_quantity * self.product.weight, + kg, + round=False) + elif (self.quantity is not None + and self.unit and self.unit.category == kg.category): + return Uom.compute_qty(self.unit, self.quantity, kg, round=False) - @classmethod - def _get_internal_volume(cls, quantity, unit, product): + @fields.depends( + 'quantity', 'unit', 'product', + methods=['on_change_with_internal_quantity']) + def on_change_with_internal_volume(self): pool = Pool() Uom = pool.get('product.uom') ModelData = pool.get('ir.model.data') @@ -115,61 +120,33 @@ class Move(metaclass=PoolMeta): # Use first the volume from product_measurements # as it could include some handling volume - if product.volume is not None: - internal_quantity = cls._get_internal_quantity( - quantity, unit, product) + if self.product and self.product.volume is not None: + internal_quantity = self.on_change_with_internal_quantity() + if internal_quantity is not None: + return Uom.compute_qty( + self.product.volume_uom, + internal_quantity * self.product.volume, + liter, + round=False) + elif (self.quantity is not None + and self.unit and self.unit.category == liter.category): return Uom.compute_qty( - product.volume_uom, internal_quantity * product.volume, liter, - round=False) - elif unit.category == liter.category: - return Uom.compute_qty(unit, quantity, liter, round=False) - else: - return None + self.unit, self.quantity, liter, round=False) - @classmethod - def create(cls, vlist): - pool = Pool() - Product = pool.get('product.product') - Uom = pool.get('product.uom') - - vlist = [v.copy() for v in vlist] - for values in vlist: - product = Product(values['product']) - unit = Uom(values['unit']) - quantity = values['quantity'] - internal_weight = cls._get_internal_weight(quantity, unit, product) - if internal_weight is not None: + def compute_fields(self, field_names=None): + cls = self.__class__ + values = super().compute_fields(field_names=field_names) + if (not field_names + or (cls.internal_weight.on_change_with & field_names)): + internal_weight = self.on_change_with_internal_weight() + if self.internal_weight != internal_weight: values['internal_weight'] = internal_weight - internal_volume = cls._get_internal_volume(quantity, unit, product) - if internal_volume is not None: + if (not field_names + or (cls.internal_volume.on_change_with & field_names)): + internal_volume = self.on_change_with_internal_volume() + if getattr(self, 'internal_volume', None) != internal_volume: values['internal_volume'] = internal_volume - return super().create(vlist) - - @classmethod - def write(cls, *args): - super().write(*args) - - to_write = [] - actions = iter(args) - for moves, values in zip(actions, actions): - for move in moves: - write = {} - internal_weight = cls._get_internal_weight( - move.quantity, move.unit, move.product) - if (internal_weight is not None - and internal_weight != move.internal_weight - and internal_weight != values.get('internal_weight')): - write['internal_weight'] = internal_weight - internal_volume = cls._get_internal_volume( - move.quantity, move.unit, move.product) - if (internal_volume is not None - and internal_volume != move.internal_volume - and internal_volume != values.get('internal_volume')): - write['internal_volume'] = internal_volume - if write: - to_write.extend(([move], write)) - if to_write: - cls.write(*to_write) + return values @classmethod @ModelView.button