From 64cac68586952a65f03a5ea8ca8d047bc073e39f Mon Sep 17 00:00:00 2001 From: Suherdy Yacob Date: Thu, 8 Jan 2026 13:08:29 +0700 Subject: [PATCH] fix the revaluation bugs --- .../account_move_line.cpython-312.pyc | Bin 6042 -> 11472 bytes models/account_move_line.py | 107 ++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/models/__pycache__/account_move_line.cpython-312.pyc b/models/__pycache__/account_move_line.cpython-312.pyc index d044f39988280cbc4c19674e47ba7a3a59d792e4..81c9cf5a3051de192b69af6aa3e0ed35af826542 100644 GIT binary patch delta 5723 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 delta 826 zcmY*W-%Aux6ux(7b{%(ihqd*`>S`^UrGsYli;_|lg$mM8q0)$9nz_~5IJ4fFF*hU% zf(n9MZY1?kj}bk^zDGoFy#zwkK;%Pzz=By6MBOuMMCak-obMdYIo~<&rXTHA-X@cA z#O-xfEBwlql?k^<3mY@$YJ zs6j4L$Mg+PoA*j;OAvMaBB8V%>Y`U@!6US$icRF2)m=bK4mAnoa%v4E&2a1mLcJhv zICDkaF7Xt*lN%q0BLodRJy-NSqRsod?G?NdpW*2q36kf1&vR_ew=KuG9LSqtxLkt5 zc>-}kx7VU8h-2(0&eAn}!X;@ojI+ zSs*70Ht7pBSv=*gVz#rhPv}Xil{1~U@V50h%H%&}+MXy6mG_zM>S@eV)ftRZ>DzJI z!5Vvpro#E~2YvtYT=wY}iB;ep|&?ks!S zLz)gm(^bTQ!sEl_xLVJ)_wG3r9dk@#h14@tOM|+f(vdm-H;W9?vpj_MZk|I`z!-m* c5bj<@U7yg7ud(R}PA^|rvC7s40zd5W2kHyX;Q#;t diff --git a/models/account_move_line.py b/models/account_move_line.py index 55ef4f9..f3789b1 100755 --- a/models/account_move_line.py +++ b/models/account_move_line.py @@ -3,6 +3,8 @@ from odoo import api, fields, models, _ from odoo.exceptions import UserError +from odoo.tools import float_compare +from datetime import timedelta class AccountMoveLine(models.Model): @@ -157,6 +159,111 @@ class AccountMoveLine(models.Model): if was_locked: po.button_done() + + # --------------------------------------------------------- + # INVENTORY VALUATION UPDATE (AVCO/FIFO FIX) + # --------------------------------------------------------- + # If PO is received, updating price should update Stock Value + if po_line.state in ['purchase', 'done'] and po_line.product_id.type == 'product': + for stock_move in po_line.move_ids.filtered(lambda m: m.state == 'done'): + # Calculate Diff based on NEW Price (updated above) + new_val = price_unit * stock_move.quantity + # Current Value from SVLs + current_val = sum(stock_move.stock_valuation_layer_ids.mapped('value')) + diff = new_val - current_val + + currency = stock_move.company_id.currency_id + if not currency.is_zero(diff): + # 1. Create Correction SVL + svl_vals = { + 'company_id': stock_move.company_id.id, + 'product_id': stock_move.product_id.id, + 'description': _("Valuation correction from Vendor Bill %s") % line.move_id.name, + 'value': diff, + 'quantity': 0, + 'stock_move_id': stock_move.id, + } + svl = self.env['stock.valuation.layer'].create(svl_vals) + + # Backdate SVL + if stock_move.date: + new_date = stock_move.date + timedelta(seconds=1) + self.env.cr.execute("UPDATE stock_valuation_layer SET create_date = %s WHERE id = %s", (new_date, svl.id)) + + # 2. AVCO/FIFO Logic: Update Standard Price and Distribute Value + product = stock_move.product_id + if product.categ_id.property_cost_method in ['average', 'fifo'] and product.quantity_svl > 0: + new_std_price = product.standard_price + (diff / product.quantity_svl) + product.with_context(disable_auto_svl=True).sudo().write({'standard_price': new_std_price}) + + remaining_svls = self.env['stock.valuation.layer'].search([ + ('product_id', '=', product.id), + ('remaining_qty', '>', 0), + ('company_id', '=', stock_move.company_id.id), + ]) + + if remaining_svls: + remaining_qty_total = sum(remaining_svls.mapped('remaining_qty')) + if remaining_qty_total > 0: + remaining_value_to_distribute = diff + remaining_value_unit_cost = remaining_value_to_distribute / remaining_qty_total + + for layer in remaining_svls: + if float_compare(layer.remaining_qty, remaining_qty_total, precision_rounding=product.uom_id.rounding) >= 0: + taken_remaining_value = remaining_value_to_distribute + else: + taken_remaining_value = remaining_value_unit_cost * layer.remaining_qty + + taken_remaining_value = stock_move.company_id.currency_id.round(taken_remaining_value) + layer.sudo().write({'remaining_value': layer.remaining_value + taken_remaining_value}) + + remaining_value_to_distribute -= taken_remaining_value + remaining_qty_total -= layer.remaining_qty + + # 3. Create Accounting Entry + if stock_move.product_id.categ_id.property_valuation == 'real_time': + accounts = stock_move.product_id.product_tmpl_id.get_product_accounts() + acc_expense = accounts.get('expense') + acc_valuation = accounts.get('stock_valuation') + + if acc_expense and acc_valuation: + if diff > 0: + debit_acc = acc_valuation.id + credit_acc = acc_expense.id + amount = diff + else: + debit_acc = acc_expense.id + credit_acc = acc_valuation.id + amount = abs(diff) + + acc_date = stock_move.date.date() if stock_move.date else fields.Date.today() + + move_vals = { + 'journal_id': accounts['stock_journal'].id, + 'company_id': stock_move.company_id.id, + 'ref': _("Revaluation for %s from Bill Edit") % stock_move.product_id.name, + 'date': acc_date, + 'move_type': 'entry', + 'stock_valuation_layer_ids': [(6, 0, [svl.id])], + 'line_ids': [ + (0, 0, { + 'name': _("Valuation Correction - %s") % stock_move.product_id.name, + 'account_id': debit_acc, + 'debit': amount, + 'credit': 0, + 'product_id': stock_move.product_id.id, + }), + (0, 0, { + 'name': _("Valuation Correction - %s") % stock_move.product_id.name, + 'account_id': credit_acc, + 'debit': 0, + 'credit': amount, + 'product_id': stock_move.product_id.id, + }) + ] + } + am = self.env['account.move'].create(move_vals) + am._post() return res