diff --git a/stock_valuation_fifo_lot/hooks.py b/stock_valuation_fifo_lot/hooks.py index 2317c98f..693d6a91 100644 --- a/stock_valuation_fifo_lot/hooks.py +++ b/stock_valuation_fifo_lot/hooks.py @@ -13,22 +13,32 @@ def post_init_hook(cr, registry): svl.lot_ids = svl.stock_move_id.lot_ids if not svl.lot_ids: continue - if svl.quantity <= 0: # Skip outgoing ones + if svl.quantity <= 0: # Skip outgoing svls continue - if svl.product_id.with_company(svl.company_id.id).cost_method != "fifo": + if not svl.product_id._is_fifo(): continue + product_uom = svl.product_id.uom_id + if svl.stock_move_id._is_out(): + # The case where outgoing done qty is reduced + # Let the first move line represent for such adjustments. + ml = svl.stock_move_id.move_line_ids[0] + ml.qty_base += svl.quantity + else: + for ml in svl.stock_move_id.move_line_ids: + ml.qty_base = ml.product_uom_id._compute_quantity( + ml.qty_done, product_uom + ) svl_consumed_qty = svl_consumed_qty_bal = svl.quantity - svl.remaining_qty if not svl_consumed_qty: continue svl_total_value = svl.value + sum(svl.stock_valuation_layer_ids.mapped("value")) svl_consumed_value = svl_total_value - svl.remaining_value - product_uom = svl.product_id.uom_id for ml in svl.stock_move_id.move_line_ids.sorted("id"): - ml_uom = ml.product_uom_id - ml_qty = ml_uom._compute_quantity(ml.qty_done, product_uom) - qty_to_allocate = min(svl_consumed_qty_bal, ml_qty) - ml.qty_consumed += product_uom._compute_quantity(qty_to_allocate, ml_uom) + qty_to_allocate = min(svl_consumed_qty_bal, ml.qty_base) + ml.qty_consumed += qty_to_allocate svl_consumed_qty_bal -= qty_to_allocate ml.value_consumed += svl_consumed_value * qty_to_allocate / svl_consumed_qty - if float_is_zero(svl_consumed_qty_bal, precision_rounding=ml_uom.rounding): + if float_is_zero( + svl_consumed_qty_bal, precision_rounding=product_uom.rounding + ): break diff --git a/stock_valuation_fifo_lot/models/product.py b/stock_valuation_fifo_lot/models/product.py index ab60aec3..0044d66e 100644 --- a/stock_valuation_fifo_lot/models/product.py +++ b/stock_valuation_fifo_lot/models/product.py @@ -13,6 +13,10 @@ class ProductProduct(models.Model): _inherit = "product.product" + def _is_fifo(self): + self.ensure_one() + return self.with_company(self.company_id.id).cost_method == "fifo" + def _get_fifo_candidates_domain(self, company): res = super()._get_fifo_candidates_domain(company) fifo_lot = self.env.context.get("fifo_lot") @@ -58,18 +62,11 @@ def _get_qty_taken_on_candidate(self, qty_to_take_on_candidates, candidate): fifo_lot = self.env.context.get("fifo_lot") if fifo_lot: candidate_ml = candidate._get_unconsumed_in_move_line(fifo_lot) - ml_uom = candidate_ml.product_uom_id - ml_qty_remaining = ml_uom._compute_quantity( - candidate_ml.qty_remaining, candidate_ml.product_id.uom_id - ) - qty_to_take_on_candidates = min(qty_to_take_on_candidates, ml_qty_remaining) - ml_qty_to_take_on_candidates = ( - candidate_ml.product_id.uom_id._compute_quantity( - qty_to_take_on_candidates, ml_uom - ) + qty_to_take_on_candidates = min( + qty_to_take_on_candidates, candidate_ml.qty_remaining ) - candidate_ml.qty_consumed += ml_qty_to_take_on_candidates - candidate_ml.value_consumed += ml_qty_to_take_on_candidates * ( + candidate_ml.qty_consumed += qty_to_take_on_candidates + candidate_ml.value_consumed += qty_to_take_on_candidates * ( candidate.remaining_value / candidate.remaining_qty ) return super()._get_qty_taken_on_candidate(qty_to_take_on_candidates, candidate) diff --git a/stock_valuation_fifo_lot/models/stock_move.py b/stock_valuation_fifo_lot/models/stock_move.py index 4dadf265..85e5ae76 100644 --- a/stock_valuation_fifo_lot/models/stock_move.py +++ b/stock_valuation_fifo_lot/models/stock_move.py @@ -26,16 +26,23 @@ def _create_out_svl(self, forced_quantity=None): return layers def _create_in_svl(self, forced_quantity=None): - """Change product standard price to the first available lot price.""" + correction_ml = self.env.context.get("correction_move_line") + if forced_quantity and correction_ml: + correction_ml.qty_base += forced_quantity + return super()._create_in_svl(forced_quantity=forced_quantity) layers = self.env["stock.valuation.layer"] for move in self: - layers |= super(StockMove, move)._create_in_svl( + layer = super(StockMove, move)._create_in_svl( forced_quantity=forced_quantity ) - product = move.product_id + layers |= layer # Calculate standard price (sorted by lot created date) - if product.cost_method != "fifo" or product.tracking == "none": + product = move.product_id + if not product._is_fifo() or product.tracking == "none": continue + for ml in layer.stock_move_id.move_line_ids: + ml.qty_base = ml.qty_done + # Change product standard price to the first available lot price. product = product.with_context(sort_by="lot_create_date") candidate = product._get_fifo_candidates(move.company_id)[:1] if not candidate: @@ -54,7 +61,7 @@ def _get_price_unit(self): return super()._get_price_unit() if hasattr(self, "purchase_line_id") and self.purchase_line_id: return super()._get_price_unit() - if self.product_id.cost_method == "fifo" and len(self.lot_ids) == 1: + if self.product_id._is_fifo() and len(self.lot_ids) == 1: # Get the most recent incoming move line for the lot. move_line = self.env["stock.move.line"].search( [ diff --git a/stock_valuation_fifo_lot/models/stock_move_line.py b/stock_valuation_fifo_lot/models/stock_move_line.py index 0b32cfd8..816cfd98 100644 --- a/stock_valuation_fifo_lot/models/stock_move_line.py +++ b/stock_valuation_fifo_lot/models/stock_move_line.py @@ -7,6 +7,10 @@ class StockMoveLine(models.Model): _inherit = "stock.move.line" + qty_base = fields.Float( + help="Base quantity for FIFO allocation; this should be equal to the summary " + "of the quantity of the relevant incoming stock valuation layers.", + ) qty_consumed = fields.Float( help="Quantity that has gone out of the inventory for valued incoming moves " "for FIFO products with a lot/serial.", @@ -37,43 +41,15 @@ class StockMoveLine(models.Model): ) @api.depends( - "qty_done", "qty_consumed", "move_id.stock_valuation_layer_ids.remaining_value" + "qty_base", "qty_consumed", "move_id.stock_valuation_layer_ids.remaining_value" ) def _compute_remaining_value(self): for rec in self: - if ( - rec.product_id.with_company(rec.company_id.id).cost_method != "fifo" - or not rec.lot_id - ): - continue - if rec.location_usage in ( - "internal", - "transit", - ) or rec.location_dest_usage not in ("internal", "transit"): - # Specifically for the case where qty_done of the outgoing - # stock move line with done state is reduced, which creates - # a positive stock valuation layer for the outgoing move. - layers = rec.move_id.stock_valuation_layer_ids.filtered( - lambda l: l.remaining_qty > 0 - ) - if not layers: - rec.qty_remaining = 0.0 - rec.value_remaining = 0.0 - continue - rec.qty_remaining = rec.product_id.uom_id._compute_quantity( - sum(layers.mapped("remaining_qty")), rec.product_uom_id - ) - rec.value_remaining = ( - sum(layers.mapped("remaining_value")) - * sum(layers.mapped("remaining_qty")) - / rec.qty_remaining - ) + if not rec.product_id._is_fifo() or not rec.lot_id: continue - rec.qty_remaining = rec.qty_done - rec.qty_consumed + rec.qty_remaining = rec.qty_base - rec.qty_consumed layers = rec.move_id.stock_valuation_layer_ids - remaining_qty = rec.product_id.uom_id._compute_quantity( - sum(layers.mapped("remaining_qty")), rec.product_uom_id - ) + remaining_qty = sum(layers.mapped("remaining_qty")) if not remaining_qty: rec.qty_remaining = 0 rec.value_remaining = 0 @@ -84,6 +60,16 @@ def _compute_remaining_value(self): / remaining_qty ) + def _action_done(self): + res = super()._action_done() + for ml in self.exists(): + if not ml.product_id._is_fifo(): + continue + ml.qty_base = ml.product_uom_id._compute_quantity( + ml.qty_done, ml.product_id.uom_id + ) + return res + def _create_correction_svl(self, move, diff): # Pass the move line as a context value in case qty_done is overridden in a done # transfer, to correctly identify which record should be processed in diff --git a/stock_valuation_fifo_lot/views/stock_move_line_views.xml b/stock_valuation_fifo_lot/views/stock_move_line_views.xml index f59507bb..c3cb4e82 100644 --- a/stock_valuation_fifo_lot/views/stock_move_line_views.xml +++ b/stock_valuation_fifo_lot/views/stock_move_line_views.xml @@ -13,6 +13,12 @@ +