Files
odoo_source/addons/mrp/tests/test_smp.py
T
JF Aubert f11fcf6bb4 [REF] mrp: split productions
There is 2 major issues with the production of multiple serials number
- Performance issue
- Duplicated code with classic backorder mechanism

The performance issues exist since backorders were create one by one
and `stock.move` and `stock.move.line` are always recomputed. They
are not created in batch neither.

_generate_backorders is removed and replace by _split_productions. The
functionality are the same. Technicaly it does the maximum in batch,
first it creates all the `mrp.production` then all the `stock.move`
and finaly, it splits the existing `stock.move.line` among the new
`stock.move`
It means that the reservation is not recompute anymore during a
backorder process and will remain the same than the splitted production
order.

Performance metrics (10 components):

	|	2   |	10	100	 1000
before	|   0.47s   |  2.84s | 32.25s | 580.53s
-----------------------------------------------
after	|   0.13s   |  0.36s |  2.60s |  35.17s

task 2633369

Part-of: odoo/odoo#77254
2021-12-22 17:55:49 +00:00

125 lines
6.2 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.mrp.tests.common import TestMrpCommon
from odoo.tests import Form
class TestMrpSerialMassProduce(TestMrpCommon):
def test_smp_serial(self):
"""Create a MO for a product not tracked by serial number.
The smp wizard should not open.
"""
mo = self.generate_mo()[0]
self.assertEqual(mo.state, 'confirmed')
res = mo.action_serial_mass_produce_wizard()
self.assertFalse(res)
def test_smp_produce_all(self):
"""Create a MO for a product tracked by serial number.
Open the smp wizard, generate all serial numbers to produce all quantities.
"""
mo = self.generate_mo(tracking_final='serial')[0]
count = mo.product_qty
# Make some stock and reserve
for product in mo.move_raw_ids.product_id:
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': product.id,
'inventory_quantity': 100,
'location_id': mo.location_src_id.id,
})._apply_inventory()
mo.action_assign()
# Open the wizard
action = mo.action_serial_mass_produce_wizard()
wizard = Form(self.env['stock.assign.serial'].with_context(**action['context']))
# Let the wizard generate all serial numbers
wizard.next_serial_number = "sn#1"
wizard.next_serial_count = count
action = wizard.save().generate_serial_numbers_production()
# Reload the wizard to apply generated serial numbers
wizard = Form(self.env['stock.assign.serial'].browse(action['res_id']))
wizard.save().apply()
# Initial MO should have a backorder-sequenced name and be in to_close state
self.assertTrue("-001" in mo.name)
self.assertEqual(mo.state, "to_close")
# Each generated serial number should have its own mo
self.assertEqual(len(mo.procurement_group_id.mrp_production_ids), count)
# Check generated serial numbers
self.assertEqual(mo.procurement_group_id.mrp_production_ids.lot_producing_id.mapped('name'), ["sn#1", "sn#2", "sn#3", "sn#4", "sn#5"])
def test_smp_produce_all_but_one(self):
"""Create a MO for a product tracked by serial number.
Open the smp wizard, generate all but one serial numbers and create a back order.
"""
mo = self.generate_mo(tracking_final='serial')[0]
count = mo.product_qty
# Make some stock and reserve
for product in mo.move_raw_ids.product_id:
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': product.id,
'inventory_quantity': 100,
'location_id': mo.location_src_id.id,
})._apply_inventory()
mo.action_assign()
action = mo.action_serial_mass_produce_wizard()
wizard = Form(self.env['stock.assign.serial'].with_context(**action['context']))
wizard.next_serial_number = "sn#1"
wizard.next_serial_count = count - 1
action = wizard.save().generate_serial_numbers_production()
# Reload the wizard to create backorder (applying generated serial numbers)
wizard = Form(self.env['stock.assign.serial'].browse(action['res_id']))
wizard.save().create_backorder()
# Last MO in sequence is the backorder
bo = mo.procurement_group_id.mrp_production_ids[-1]
self.assertEqual(bo.backorder_sequence, count)
self.assertEqual(bo.state, "confirmed")
def test_smp_produce_complex(self):
"""Create a MO for a product tracked by serial number
and with complex components (serial and multiple lots).
Open the smp wizard, generate all serial numbers to produce all quantities.
Check lot splitting.
"""
mo, dummy, dummy, product_to_use_1, product_to_use_2 = self.generate_mo(tracking_final='serial', tracking_base_1='lot', tracking_base_2='serial', qty_final=3, qty_base_1=2, qty_base_2=1)
count = mo.product_qty
# Make some stock and reserve
for _ in range(2): # 2 lots of 3 to satisfy the need and check lot splitting
lot = self.env['stock.lot'].create({
'product_id': product_to_use_1.id,
'company_id': self.env.company.id,
})
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': product_to_use_1.id,
'inventory_quantity': 3,
'location_id': mo.location_src_id.id,
'lot_id': lot.id,
})._apply_inventory()
for _ in range(3): # 3 serial numbers
lot = self.env['stock.lot'].create({
'product_id': product_to_use_2.id,
'company_id': self.env.company.id,
})
self.env['stock.quant'].with_context(inventory_mode=True).create({
'product_id': product_to_use_2.id,
'inventory_quantity': 1,
'location_id': mo.location_src_id.id,
'lot_id': lot.id,
})._apply_inventory()
mo.action_assign()
# Open the wizard
action = mo.action_serial_mass_produce_wizard()
wizard = Form(self.env['stock.assign.serial'].with_context(**action['context']))
# Let the wizard generate all serial numbers
wizard.next_serial_number = "sn#1"
wizard.next_serial_count = count
action = wizard.save().generate_serial_numbers_production()
# Reload the wizard to apply generated serial numbers
wizard = Form(self.env['stock.assign.serial'].browse(action['res_id']))
wizard.save().apply()
# 1st & 3rd MO in sequence should have only 1 move lines (1 lot) for product_to_use_1 (2nd in bom)
self.assertEqual(mo.procurement_group_id.mrp_production_ids[0].move_raw_ids[1].move_lines_count, 1)
self.assertEqual(mo.procurement_group_id.mrp_production_ids[2].move_raw_ids[1].move_lines_count, 1)
# 2nd MO should have 2 move lines (2 different lots) for product_to_use_1
self.assertEqual(mo.procurement_group_id.mrp_production_ids[1].move_raw_ids[1].move_lines_count, 2)