Skip to content

Commit

Permalink
add qty_base to sml, refactor code
Browse files Browse the repository at this point in the history
  • Loading branch information
yostashiro committed Sep 21, 2024
1 parent 88e3356 commit afbb94d
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 56 deletions.
26 changes: 18 additions & 8 deletions stock_valuation_fifo_lot/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
19 changes: 8 additions & 11 deletions stock_valuation_fifo_lot/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 12 additions & 5 deletions stock_valuation_fifo_lot/models/stock_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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(
[
Expand Down
50 changes: 18 additions & 32 deletions stock_valuation_fifo_lot/models/stock_move_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions stock_valuation_fifo_lot/views/stock_move_line_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@
</xpath>
<xpath expr="//field[@name='product_uom_id']" position="after">
<field name="company_currency_id" invisible="1" />
<field
name="qty_base"
sum="Qty Base"
groups="stock.group_stock_manager"
optional="show"
/>
<field
name="qty_consumed"
sum="Qty Consumed"
Expand Down

0 comments on commit afbb94d

Please sign in to comment.