Skip to content

Commit

Permalink
prod_lot_seq: Fix sequence incrementation opening detailed operations
Browse files Browse the repository at this point in the history
When opening the detailed operations view of a stock move, Odoo is calling
stock.production.lot._get_next_serial to set stock.move.next_serial field
anytime the product is tracked by serial and the move is assigned.

If we use this module with product or global policy, the respective sequence
will therefore be called and incremented anytime this view is open, even
if the picking is not creating lots (e.g. delivery orders or internal transfers)

To avoid incrementing the sequence unnecessarily, we only allow to get the next
sequence number the first time this view is opened and if the move has
to create new serial numbers.
Otherwise, we force the value to be set to stock.move.next_serial field to
an empty string if no serial has to be created through this move, or to the
next_serial value assigned on the first opening of the view.
  • Loading branch information
grindtildeath committed Apr 13, 2023
1 parent 5ec1421 commit b2b1c8d
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 0 deletions.
1 change: 1 addition & 0 deletions product_lot_sequence/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from . import product
from . import stock_production_lot
from . import stock_move
23 changes: 23 additions & 0 deletions product_lot_sequence/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2023 Camptocamp SA
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
from odoo import models


class StockMove(models.Model):
_inherit = "stock.move"

def action_show_details(self):
"""Avoid calling and incrementing the sequence if not needed or already done"""
seq_policy = self.env["stock.production.lot"]._get_sequence_policy()
if seq_policy in ("product", "global"):
# If move is not supposed to assign serial pass empty string for next serial
if not self.display_assign_serial:
return super(
StockMove, self.with_context(force_next_serial="")
).action_show_details()
# If the sequence was already called once, avoid calling it another time
elif self.next_serial:
return super(
StockMove, self.with_context(force_next_serial=self.next_serial)
).action_show_details()
return super().action_show_details()
2 changes: 2 additions & 0 deletions product_lot_sequence/models/stock_production_lot.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ def create(self, vals_list):

@api.model
def _get_next_serial(self, company, product):
if "force_next_serial" in self.env.context:
return self.env.context.get("force_next_serial")
seq_policy = self._get_sequence_policy()
if seq_policy == "product":
seq = product.product_tmpl_id.lot_sequence_id
Expand Down
49 changes: 49 additions & 0 deletions product_lot_sequence/tests/test_product_lot_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ def setUp(self):
super(TestProductLotSequence, self).setUp()
self.product_product = self.env["product.product"]
self.stock_production_lot = self.env["stock.production.lot"]
self.receipt_type = self.env.ref("stock.picking_type_in")
self.delivery_type = self.env.ref("stock.picking_type_out")

def _create_picking(self, picking_type, move_vals_list):
picking_form = Form(self.env["stock.picking"])
picking_form.picking_type_id = picking_type
for move_vals in move_vals_list:
with picking_form.move_ids_without_package.new() as move_form:
move_form.product_id = move_vals.get("product_id")
move_form.product_uom_qty = move_vals.get("product_uom_qty", 1.0)
move_form.product_uom = move_vals.get(
"product_uom", self.env.ref("uom.product_uom_unit")
)
picking = picking_form.save()
picking.action_confirm()
return picking

def test_product_sequence(self):
self.assertEqual(self.stock_production_lot._get_sequence_policy(), "product")
Expand Down Expand Up @@ -88,3 +104,36 @@ def test_lot_onchange_product_id_global(self):
lot_form.product_id = product
lot = lot_form.save()
self.assertEqual(lot.name, next_sequence_number)

def test_open_detailed_operations(self):
self.env["ir.config_parameter"].set_param(
"product_lot_sequence.policy", "global"
)
seq = self.env["ir.sequence"].search([("code", "=", "stock.lot.serial")])
first_next_sequence_number = seq.get_next_char(seq.number_next_actual)
product = self.product_product.create(
{"name": "Test global", "tracking": "serial"}
)
delivery_picking = self._create_picking(
self.delivery_type, [{"product_id": product}]
)
delivery_move = delivery_picking.move_lines
self.assertFalse(delivery_move.next_serial)
delivery_move.action_show_details()
self.assertFalse(delivery_move.next_serial)
self.assertEqual(
seq.get_next_char(seq.number_next_actual), first_next_sequence_number
)
receipt_picking = self._create_picking(
self.receipt_type, [{"product_id": product}]
)
receipt_move = receipt_picking.move_lines
self.assertFalse(receipt_move.next_serial)
receipt_move.action_show_details()
self.assertEqual(receipt_move.next_serial, first_next_sequence_number)
new_next_sequence_number = seq.get_next_char(seq.number_next_actual)
self.assertNotEqual(new_next_sequence_number, first_next_sequence_number)
receipt_move.action_show_details()
self.assertEqual(
new_next_sequence_number, seq.get_next_char(seq.number_next_actual)
)

0 comments on commit b2b1c8d

Please sign in to comment.