Skip to content
Browse files

[IMP] mrp: block usage of duplicate serial number

With this commit, the manufacturer will now be noticed earlier in
the process if he produced/consumed a serial number already produced/consumed in a
previous production. Before this commit, an error was triggered as
well but only at the very last step of the production.
This leaded to two issues :
 - The user had to unlock-edit-lock to update the wrong serial number
 - On large production, it was difficult to figure out which was/were
   the product(s) to fix.

The search on previous production is done each time the produce wizard is closed
or once the production is done on a workorder

Task 2002133
  • Loading branch information...
Whenrow committed Oct 7, 2019
1 parent eaa0223 commit a26f6c64299cb02dcc6a3041d833aef5b71514a0
@@ -299,6 +299,18 @@ def _strict_consumption_check(self):
if float_compare(qty_done, qty_to_consume, precision_rounding=rounding) != 0:
raise UserError(_('You should consume the quantity of %s defined in the BoM. If you want to consume more or less components, change the consumption setting on the BoM.') % lines[0]

def _check_sn_uniquiness(self):
""" Alert the user if the serial number as already been produced """
if self.product_tracking == 'serial' and self.finished_lot_id:
sml = self.env['stock.move.line'].search_count([
('lot_id', '=',,
('location_id.usage', '=', 'production'),
('qty_done', '=', 1),
('state', '=', 'done')
if sml:
raise UserError(_('This serial number for product %s has already been produced' %

class MrpAbstractWorkorderLine(models.AbstractModel):
_name = "mrp.abstract.workorder.line"
@@ -446,6 +458,26 @@ def _create_extra_move_lines(self):

return vals_list

def _check_line_sn_uniquiness(self):
""" Alert the user if the line serial number as already been consumed/produced """
if self.product_tracking == 'serial' and self.lot_id:
domain = [
('lot_id', '=',,
('qty_done', '=', 1),
('state', '=', 'done')
# Adapt domain and error message in case of component or byproduct
if self[self._get_raw_workorder_inverse_name()]:
domain.append(('location_dest_id.usage', '=', 'production'))
message = _('The serial number used for component %s has already been consumed' %
domain.append(('location_id.usage', '=', 'production'))
message = _('The serial number used for byproduct %s has already been produced' %
sml = self.env['stock.move.line'].search_count(domain)
if sml:
raise UserError(message)

def _unreserve_order(self):
""" Unreserve line with lower reserved quantity first """
@@ -384,10 +384,13 @@ def record_production(self):
return True

if float_compare(self.qty_producing, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
raise UserError(_('Please set the quantity you are currently producing. It should be different from zero.'))

if 'check_id' not in self:
for line in self.raw_workorder_line_ids | self.finished_workorder_line_ids:
# If last work order, then post lots used
if not self.next_work_order_id:
@@ -137,6 +137,7 @@ def _record_production(self):
move_id = self.env['stock.move'].create(values)
line.move_id =

# because of an ORM limitation (fields on transient models are not
# recomputed by updates in non-transient models), the related fields on
# this model are not recomputed by the creations above
@@ -146,6 +147,8 @@ def _record_production(self):
quantity = self.qty_producing
if float_compare(quantity, 0, precision_rounding=self.product_uom_id.rounding) <= 0:
raise UserError(_("The production order for '%s' has no quantity specified.") % self.product_id.display_name)

if self.production_id.state == 'confirmed':

0 comments on commit a26f6c6

Please sign in to comment.
You can’t perform that action at this time.