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
|
return moves
|
||||||
|
|
||||||
def _update_raw_moves(self, factor):
|
def _update_raw_moves(self, factor):
|
||||||
# Odoo core _update_raw_moves multiplies `old_qty * factor` and rounds UP.
|
# We override this to ensure "To Consume" (product_uom_qty) is calculated from BOM line "truth"
|
||||||
# If factor is 0.9799118079... and old_qty is 71435, the math results in 70000.00000000004
|
# rather than multiplying an old (possibly noisy) quantity by a factor.
|
||||||
# Odoo's UP rounding elevates this dust to 70000.001!
|
|
||||||
# We must let Odoo do its thing, but then surgically clean the dust.
|
|
||||||
res = super()._update_raw_moves(factor)
|
res = super()._update_raw_moves(factor)
|
||||||
|
|
||||||
clean_res = []
|
clean_res = []
|
||||||
for move, old_qty, new_qty in res:
|
for move, old_qty, new_qty in res:
|
||||||
exact_qty = old_qty * factor
|
if move.bom_line_id and self.bom_id:
|
||||||
# Clean floating point microscopic dust (e.g. 70000.0000000004 -> 70000.0)
|
# Recalculate the clean truth directly from BOM
|
||||||
clean_exact_qty = round(exact_qty, 8)
|
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')
|
from odoo.tools import float_round
|
||||||
|
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})
|
if move.product_uom_qty != ideal_qty:
|
||||||
clean_res.append((move, old_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
|
return clean_res
|
||||||
|
|
||||||
@ -132,18 +133,38 @@ class MrpProduction(models.Model):
|
|||||||
|
|
||||||
def _clean_lingering_decimals(self):
|
def _clean_lingering_decimals(self):
|
||||||
for production in 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:
|
for move in production.move_raw_ids:
|
||||||
rounding = move.product_uom.rounding
|
if not move.bom_line_id:
|
||||||
if move.product_uom_qty:
|
continue
|
||||||
# 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
|
# Calculate what the quantity SHOULD be according to BOM and MO Qty
|
||||||
clean_qty = round(move.product_uom_qty, 2)
|
line_quantity = round(clean_factor * move.bom_line_id.product_qty, 8)
|
||||||
if 0.000001 < abs(move.product_uom_qty - clean_qty) < (rounding * 1.5):
|
from odoo.tools import float_round
|
||||||
move.product_uom_qty = clean_qty
|
ideal_qty = float_round(line_quantity, precision_rounding=move.product_uom.rounding, rounding_method='UP')
|
||||||
for ml in move.move_line_ids:
|
|
||||||
if ml.quantity:
|
# 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:
|
||||||
clean_done = round(ml.quantity, 2)
|
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
|
ml.quantity = clean_done
|
||||||
|
|
||||||
def _merge_finished_move_lines(self):
|
def _merge_finished_move_lines(self):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user