From 4fd8c57af3deae789c054354d1decba58375a31b Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Wed, 4 Mar 2026 11:36:48 +0700 Subject: [PATCH] feat: Implement precise unit factor calculation for BOM-related stock moves and add a script to fix existing MO quantity decimals. --- fix_mo_decimals.py | 49 ++++++++++++++++++++++++++++++++++++++++++++ models/__init__.py | 2 ++ models/stock_move.py | 22 ++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 fix_mo_decimals.py create mode 100644 models/stock_move.py diff --git a/fix_mo_decimals.py b/fix_mo_decimals.py new file mode 100644 index 0000000..b0419f9 --- /dev/null +++ b/fix_mo_decimals.py @@ -0,0 +1,49 @@ +import xmlrpc.client +import ssl + +url = "https://trialerp.mapan.co.id" +username = 'suherdy.yacob' +api_key = '27aa1dfc979d5507973b190018329d0690400c66' +selected_db = 'mapangroup_trial_o19' +context = ssl._create_unverified_context() + +try: + common = xmlrpc.client.ServerProxy(f'{url}/xmlrpc/2/common', context=context) + uid = common.authenticate(selected_db, username, api_key, {}) + models = xmlrpc.client.ServerProxy('{}/xmlrpc/2/object'.format(url), context=context) + + # Search for all draft or confirmed MOs to recalculate quantities based on true BOM ratios. + mo_ids = models.execute_kw(selected_db, uid, api_key, 'mrp.production', 'search', [[('state', 'in', ['draft', 'confirmed'])]]) + + print(f"Found {len(mo_ids)} active MOs.") + + for mo_id in mo_ids: + mo = models.execute_kw(selected_db, uid, api_key, 'mrp.production', 'read', [[mo_id]], {'fields': ['name', 'product_qty', 'bom_id', 'move_raw_ids']})[0] + if not mo.get('bom_id'): + continue + + print(f"Checking MO {mo['name']}...") + bom = models.execute_kw(selected_db, uid, api_key, 'mrp.bom', 'read', [[mo['bom_id'][0]]], {'fields': ['product_qty', 'bom_line_ids']})[0] + bom_qty = bom['product_qty'] + + # Read BOM lines to get exact ratios + bom_lines = models.execute_kw(selected_db, uid, api_key, 'mrp.bom.line', 'read', [bom['bom_line_ids']], {'fields': ['product_qty']}) + bom_ratios = {bl['id']: bl['product_qty'] / bom_qty for bl in bom_lines} + + moves = models.execute_kw(selected_db, uid, api_key, 'stock.move', 'read', [mo['move_raw_ids']], {'fields': ['bom_line_id', 'product_uom_qty']}) + + for move in moves: + if move.get('bom_line_id'): + bom_line_id = move['bom_line_id'][0] + if bom_line_id in bom_ratios: + correct_qty = bom_ratios[bom_line_id] * mo['product_qty'] + current_qty = move.get('product_uom_qty') or 0.0 + + if abs(correct_qty - current_qty) > 0.0001: + print(f" - Fixing move {move['id']}: qty {current_qty} -> exact integer {correct_qty}") + models.execute_kw(selected_db, uid, api_key, 'stock.move', 'write', [[move['id']], {'product_uom_qty': correct_qty}]) + + print("Finished checking MOs.") + +except Exception as e: + print("Error:", e) diff --git a/models/__init__.py b/models/__init__.py index 81dfa6d..c3dd1ba 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -3,4 +3,6 @@ from . import mrp_production from . import mrp_bom from . import mrp_production_schedule from . import mrp_packaging +from . import stock_move + diff --git a/models/stock_move.py b/models/stock_move.py new file mode 100644 index 0000000..07e361d --- /dev/null +++ b/models/stock_move.py @@ -0,0 +1,22 @@ +from odoo import api, models + +class StockMove(models.Model): + _inherit = 'stock.move' + + @api.depends('product_uom_qty', 'raw_material_production_id', 'raw_material_production_id.product_qty', 'raw_material_production_id.qty_producing') + def _compute_unit_factor(self): + super()._compute_unit_factor() + for move in self: + mo = move.raw_material_production_id or move.production_id + # Prevent 3-decimal rounding drift if the move comes from a BOM Line! + if mo and mo.bom_id and move.bom_line_id: + # Use the exact, unrounded mathematical ratio directly from BOM line and UoMs. + bom_line = move.bom_line_id + bom = mo.bom_id + + # Fetch quantities directly converting to product matching formats without DB truncation + line_qty = bom_line.product_uom_id._compute_quantity(bom_line.product_qty, bom_line.product_id.uom_id, round=False) + bom_qty = bom.product_uom_id._compute_quantity(bom.product_qty, bom.product_tmpl_id.uom_id, round=False) + + if bom_qty: + move.unit_factor = line_qty / bom_qty