Files
Odoo14Kanjabung/check_bom_vs_moves.py
2026-03-04 19:24:36 +07:00

122 lines
3.7 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Check BoM untuk WH/MO/00012 dan bandingkan dengan stock moves
"""
import xmlrpc.client
API_URL = 'http://localhost:8070'
DB_NAME = 'kanjabung_MRP'
USERNAME = 'admin'
PASSWORD = 'admin'
print("="*80)
print("BOM vs Stock Moves Analysis")
print("="*80)
# Connect
common = xmlrpc.client.ServerProxy(f'{API_URL}/xmlrpc/2/common')
uid = common.authenticate(DB_NAME, USERNAME, PASSWORD, {})
models = xmlrpc.client.ServerProxy(f'{API_URL}/xmlrpc/2/object')
# Find WH/MO/00012
print("\nStep 1: Get MO Details")
mos = models.execute_kw(DB_NAME, uid, PASSWORD, 'mrp.production', 'search_read',
[[('name', '=', 'WH/MO/00012')]],
{'fields': ['id', 'name', 'bom_id', 'product_id', 'product_qty'], 'limit': 1}
)
mo = mos[0]
mo_id = mo['id']
bom_id = mo['bom_id'][0] if mo['bom_id'] else None
print(f"MO: {mo['name']}")
print(f"Product: {mo['product_id'][1]} (Qty: {mo['product_qty']} kg)")
print(f"BoM ID: {mo['bom_id'][1] if mo['bom_id'] else 'None'}")
if not bom_id:
print("\n❌ No BoM assigned")
exit(1)
# Get BoM lines
print("\nStep 2: Get BoM Lines")
bom_lines = models.execute_kw(DB_NAME, uid, PASSWORD, 'mrp.bom.line', 'search_read',
[[('bom_id', '=', bom_id)]],
{
'fields': ['product_id', 'product_qty', 'product_uom_id', 'scada_equipment_id'],
'limit': 100
}
)
print(f"Found {len(bom_lines)} BoM lines:")
print()
bom_pollar = None
for line in bom_lines:
prod = line['product_id']
prod_name = prod[1] if prod else 'Unknown'
qty = line['product_qty']
uom = line['product_uom_id'][1] if line['product_uom_id'] else 'Unknown'
equip = line['scada_equipment_id']
equip_name = equip[1] if equip else 'No Equipment'
print(f"Product: {prod_name}")
print(f" Qty: {qty} {uom}")
print(f" Equipment: {equip_name}")
print()
# Track POLLAR ANGSA
if 'POLLAR' in prod_name.upper() and 'ANGSA' in prod_name.upper():
bom_pollar = {
'product': prod_name,
'qty': qty,
'equipment': equip_name
}
# Compare with stock moves
print("Step 3: Get Stock Moves")
moves = models.execute_kw(DB_NAME, uid, PASSWORD, 'stock.move', 'search_read',
[[('raw_material_production_id', '=', mo_id)]],
{
'fields': ['id', 'product_id', 'product_uom_qty', 'state', 'scada_equipment_id'],
'limit': 100
}
)
pollar_moves = [m for m in moves if 'POLLAR' in m['product_id'][1].upper() and 'ANGSA' in m['product_id'][1].upper()]
print(f"Found {len(pollar_moves)} POLLAR ANGSA stock moves:")
print()
total_moves_qty = 0
for idx, move in enumerate(pollar_moves, 1):
qty = move['product_uom_qty']
state = move['state']
equip_name = move['scada_equipment_id'][1] if move['scada_equipment_id'] else 'No Equipment'
print(f"Move #{idx} (ID: {move['id']}): {qty} kg - state: {state} - {equip_name}")
total_moves_qty += qty
# Analysis
print("\n" + "="*80)
print("ANALYSIS:")
print("="*80)
if bom_pollar:
print(f"\nBoM Line for POLLAR ANGSA:")
print(f" Quantity: {bom_pollar['qty']} kg")
print(f" Equipment: {bom_pollar['equipment']}")
print(f"\nTotal Stock Moves for POLLAR ANGSA: {total_moves_qty} kg")
if len(pollar_moves) > 1:
print(f"\n⚠️ Multiple moves detected!")
print(f" BoM expects: {bom_pollar['qty'] if bom_pollar else '?'} kg")
print(f" Actual moves: {len(pollar_moves)} moves totaling {total_moves_qty} kg")
print(f"\n Possible causes:")
print(f" 1. MO was modified after creation (quantity changed)")
print(f" 2. Manual move splitting")
print(f" 3. Partial reception/return")
print("\nNote: UI displays BoM quantities, API should also group by equipment")
print("="*80)