fix: Recalculate raw move quantities directly from BOM lines and refine decimal cleaning for production order moves and consumed quantities.
This commit is contained in:
parent
4b9efd08a8
commit
696d1a3b90
@ -38,24 +38,25 @@ class MrpProduction(models.Model):
|
||||
return moves
|
||||
|
||||
def _update_raw_moves(self, factor):
|
||||
# Odoo core _update_raw_moves multiplies `old_qty * factor` and rounds UP.
|
||||
# If factor is 0.9799118079... and old_qty is 71435, the math results in 70000.00000000004
|
||||
# Odoo's UP rounding elevates this dust to 70000.001!
|
||||
# We must let Odoo do its thing, but then surgically clean the dust.
|
||||
# We override this to ensure "To Consume" (product_uom_qty) is calculated from BOM line "truth"
|
||||
# rather than multiplying an old (possibly noisy) quantity by a factor.
|
||||
res = super()._update_raw_moves(factor)
|
||||
|
||||
clean_res = []
|
||||
for move, old_qty, new_qty in res:
|
||||
exact_qty = old_qty * factor
|
||||
# Clean floating point microscopic dust (e.g. 70000.0000000004 -> 70000.0)
|
||||
clean_exact_qty = round(exact_qty, 8)
|
||||
if move.bom_line_id and self.bom_id:
|
||||
# Recalculate the clean truth directly from BOM
|
||||
clean_factor = round(self.product_uom_id._compute_quantity(self.product_qty, self.bom_id.product_uom_id, round=False) / self.bom_id.product_qty, 8)
|
||||
line_quantity = round(clean_factor * move.bom_line_id.product_qty, 8)
|
||||
|
||||
from odoo.tools import float_round
|
||||
ideal_qty = float_round(clean_exact_qty, precision_rounding=move.product_uom.rounding, rounding_method='UP')
|
||||
ideal_qty = float_round(line_quantity, precision_rounding=move.product_uom.rounding, rounding_method='UP')
|
||||
|
||||
if move.product_uom_qty != ideal_qty:
|
||||
move.write({'product_uom_qty': ideal_qty})
|
||||
clean_res.append((move, old_qty, ideal_qty))
|
||||
else:
|
||||
clean_res.append((move, old_qty, new_qty))
|
||||
|
||||
return clean_res
|
||||
|
||||
@ -132,18 +133,38 @@ class MrpProduction(models.Model):
|
||||
|
||||
def _clean_lingering_decimals(self):
|
||||
for production in self:
|
||||
if not production.bom_id:
|
||||
continue
|
||||
|
||||
# Pre-calculate clean factor for this production
|
||||
clean_factor = round(production.product_uom_id._compute_quantity(production.product_qty, production.bom_id.product_uom_id, round=False) / production.bom_id.product_qty, 8)
|
||||
|
||||
for move in production.move_raw_ids:
|
||||
rounding = move.product_uom.rounding
|
||||
if move.product_uom_qty:
|
||||
# Clean quantities that moved away from clean decimals by exactly the rounding amount
|
||||
# E.g. 60.030 instead of 60.000 where component increment is 0.010
|
||||
clean_qty = round(move.product_uom_qty, 2)
|
||||
if 0.000001 < abs(move.product_uom_qty - clean_qty) < (rounding * 1.5):
|
||||
move.product_uom_qty = clean_qty
|
||||
if not move.bom_line_id:
|
||||
continue
|
||||
|
||||
# Calculate what the quantity SHOULD be according to BOM and MO Qty
|
||||
line_quantity = round(clean_factor * move.bom_line_id.product_qty, 8)
|
||||
from odoo.tools import float_round
|
||||
ideal_qty = float_round(line_quantity, precision_rounding=move.product_uom.rounding, rounding_method='UP')
|
||||
|
||||
# 1. Fix "To Consume" (product_uom_qty)
|
||||
if abs(move.product_uom_qty - ideal_qty) > 0.000001:
|
||||
move.product_uom_qty = ideal_qty
|
||||
|
||||
# 2. Fix "Consumed" (move_line_ids.quantity)
|
||||
# If the line is FULLY consumed (e.g. qty_producing == product_qty),
|
||||
# then move lines should match the ideal quantity.
|
||||
if production.qty_producing == production.product_qty:
|
||||
for ml in move.move_line_ids:
|
||||
if abs(ml.quantity - ideal_qty) > 0.000001:
|
||||
ml.quantity = ideal_qty
|
||||
else:
|
||||
# Otherwise just round to 2 decimals if it's "close enough" to a clean decimal
|
||||
# but drifted by microscopic dust.
|
||||
for ml in move.move_line_ids:
|
||||
if ml.quantity:
|
||||
clean_done = round(ml.quantity, 2)
|
||||
if 0.000001 < abs(ml.quantity - clean_done) < (rounding * 1.5):
|
||||
if 0.000001 < abs(ml.quantity - clean_done) < (move.product_uom.rounding * 1.5):
|
||||
ml.quantity = clean_done
|
||||
|
||||
def _merge_finished_move_lines(self):
|
||||
|
||||
Loading…
Reference in New Issue
Block a user