476 lines
22 KiB
Python
476 lines
22 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo.tests import Form
|
||
|
from odoo.tests import common
|
||
|
from odoo.exceptions import ValidationError
|
||
|
|
||
|
|
||
|
class TestMrpByProduct(common.TransactionCase):
|
||
|
|
||
|
@classmethod
|
||
|
def setUpClass(cls):
|
||
|
super().setUpClass()
|
||
|
cls.MrpBom = cls.env['mrp.bom']
|
||
|
cls.warehouse = cls.env.ref('stock.warehouse0')
|
||
|
route_manufacture = cls.warehouse.manufacture_pull_id.route_id.id
|
||
|
route_mto = cls.warehouse.mto_pull_id.route_id.id
|
||
|
cls.uom_unit_id = cls.env.ref('uom.product_uom_unit').id
|
||
|
def create_product(name, route_ids=[]):
|
||
|
return cls.env['product.product'].create({
|
||
|
'name': name,
|
||
|
'type': 'product',
|
||
|
'route_ids': route_ids})
|
||
|
|
||
|
# Create product A, B, C.
|
||
|
# --------------------------
|
||
|
cls.product_a = create_product('Product A', route_ids=[(6, 0, [route_manufacture, route_mto])])
|
||
|
cls.product_b = create_product('Product B', route_ids=[(6, 0, [route_manufacture, route_mto])])
|
||
|
cls.product_c_id = create_product('Product C', route_ids=[]).id
|
||
|
cls.bom_byproduct = cls.MrpBom.create({
|
||
|
'product_tmpl_id': cls.product_a.product_tmpl_id.id,
|
||
|
'product_qty': 1.0,
|
||
|
'type': 'normal',
|
||
|
'product_uom_id': cls.uom_unit_id,
|
||
|
'bom_line_ids': [(0, 0, {'product_id': cls.product_c_id, 'product_uom_id': cls.uom_unit_id, 'product_qty': 2})],
|
||
|
'byproduct_ids': [(0, 0, {'product_id': cls.product_b.id, 'product_uom_id': cls.uom_unit_id, 'product_qty': 1})]
|
||
|
})
|
||
|
cls.produced_serial = cls.env['product.product'].create({
|
||
|
'name': 'Produced Serial',
|
||
|
'type': 'product',
|
||
|
'tracking': 'serial',
|
||
|
})
|
||
|
cls.sn_1 = cls.env['stock.lot'].create({
|
||
|
'name': 'Serial_01',
|
||
|
'product_id': cls.produced_serial.id
|
||
|
})
|
||
|
cls.sn_2 = cls.env['stock.lot'].create({
|
||
|
'name': 'Serial_02',
|
||
|
'product_id': cls.produced_serial.id
|
||
|
})
|
||
|
|
||
|
def test_00_mrp_byproduct(self):
|
||
|
""" Test by product with production order."""
|
||
|
# Create BOM for product B
|
||
|
# ------------------------
|
||
|
bom_product_b = self.MrpBom.create({
|
||
|
'product_tmpl_id': self.product_b.product_tmpl_id.id,
|
||
|
'product_qty': 1.0,
|
||
|
'type': 'normal',
|
||
|
'product_uom_id': self.uom_unit_id,
|
||
|
'bom_line_ids': [(0, 0, {'product_id': self.product_c_id, 'product_uom_id': self.uom_unit_id, 'product_qty': 2})]
|
||
|
})
|
||
|
|
||
|
# Create production order for product A
|
||
|
# -------------------------------------
|
||
|
|
||
|
mnf_product_a_form = Form(self.env['mrp.production'])
|
||
|
mnf_product_a_form.product_id = self.product_a
|
||
|
mnf_product_a_form.bom_id = self.bom_byproduct
|
||
|
mnf_product_a_form.product_qty = 2.0
|
||
|
mnf_product_a = mnf_product_a_form.save()
|
||
|
mnf_product_a.action_confirm()
|
||
|
|
||
|
# I confirm the production order.
|
||
|
self.assertEqual(mnf_product_a.state, 'confirmed', 'Production order should be in state confirmed')
|
||
|
|
||
|
# Now I check the stock moves for the byproduct I created in the bill of material.
|
||
|
# This move is created automatically when I confirmed the production order.
|
||
|
moves = mnf_product_a.move_raw_ids | mnf_product_a.move_finished_ids
|
||
|
self.assertTrue(moves, 'No moves are created !')
|
||
|
|
||
|
# I consume and produce the production of products.
|
||
|
# I create record for selecting mode and quantity of products to produce.
|
||
|
mo_form = Form(mnf_product_a)
|
||
|
mo_form.qty_producing = 2.00
|
||
|
mnf_product_a = mo_form.save()
|
||
|
# I finish the production order.
|
||
|
self.assertEqual(len(mnf_product_a.move_raw_ids), 1, "Wrong consume move on production order.")
|
||
|
consume_move_c = mnf_product_a.move_raw_ids
|
||
|
by_product_move = mnf_product_a.move_finished_ids.filtered(lambda x: x.product_id.id == self.product_b.id)
|
||
|
# Check sub production produced quantity...
|
||
|
self.assertEqual(consume_move_c.product_uom_qty, 4, "Wrong consumed quantity of product c.")
|
||
|
self.assertEqual(by_product_move.product_uom_qty, 2, "Wrong produced quantity of sub product.")
|
||
|
|
||
|
mnf_product_a._post_inventory()
|
||
|
|
||
|
# I see that stock moves of External Hard Disk including Headset USB are done now.
|
||
|
self.assertFalse(any(move.state != 'done' for move in moves), 'Moves are not done!')
|
||
|
|
||
|
def test_01_mrp_byproduct(self):
|
||
|
self.env["stock.quant"].create({
|
||
|
"product_id": self.product_c_id,
|
||
|
"location_id": self.warehouse.lot_stock_id.id,
|
||
|
"quantity": 4,
|
||
|
})
|
||
|
bom_product_a = self.MrpBom.create({
|
||
|
'product_tmpl_id': self.product_a.product_tmpl_id.id,
|
||
|
'product_qty': 1.0,
|
||
|
'type': 'normal',
|
||
|
'product_uom_id': self.uom_unit_id,
|
||
|
'bom_line_ids': [(0, 0, {'product_id': self.product_c_id, 'product_uom_id': self.uom_unit_id, 'product_qty': 2})]
|
||
|
})
|
||
|
mnf_product_a_form = Form(self.env['mrp.production'])
|
||
|
mnf_product_a_form.product_id = self.product_a
|
||
|
mnf_product_a_form.bom_id = bom_product_a
|
||
|
mnf_product_a_form.product_qty = 2.0
|
||
|
mnf_product_a = mnf_product_a_form.save()
|
||
|
mnf_product_a.action_confirm()
|
||
|
self.assertEqual(mnf_product_a.state, "confirmed")
|
||
|
mnf_product_a.move_raw_ids._action_assign()
|
||
|
mnf_product_a.move_raw_ids.picked = True
|
||
|
mnf_product_a.move_raw_ids._action_done()
|
||
|
self.assertEqual(mnf_product_a.state, "progress")
|
||
|
mnf_product_a.qty_producing = 2
|
||
|
mnf_product_a.button_mark_done()
|
||
|
self.assertTrue(mnf_product_a.move_finished_ids)
|
||
|
self.assertEqual(mnf_product_a.state, "done")
|
||
|
|
||
|
def test_change_product(self):
|
||
|
""" Create a production order for a specific product with a BoM. Then change the BoM and the finished product for
|
||
|
other ones and check the finished product of the first mo did not became a byproduct of the second one."""
|
||
|
# Create BOM for product A with product B as component
|
||
|
bom_product_a = self.MrpBom.create({
|
||
|
'product_tmpl_id': self.product_a.product_tmpl_id.id,
|
||
|
'product_qty': 1.0,
|
||
|
'type': 'normal',
|
||
|
'product_uom_id': self.uom_unit_id,
|
||
|
'bom_line_ids': [(0, 0, {'product_id': self.product_b.id, 'product_uom_id': self.uom_unit_id, 'product_qty': 2})],
|
||
|
})
|
||
|
|
||
|
bom_product_a_2 = self.MrpBom.create({
|
||
|
'product_tmpl_id': self.product_b.product_tmpl_id.id,
|
||
|
'product_qty': 1.0,
|
||
|
'type': 'normal',
|
||
|
'product_uom_id': self.uom_unit_id,
|
||
|
'bom_line_ids': [(0, 0, {'product_id': self.product_c_id, 'product_uom_id': self.uom_unit_id, 'product_qty': 2})],
|
||
|
})
|
||
|
# Create production order for product A
|
||
|
# -------------------------------------
|
||
|
|
||
|
mnf_product_a_form = Form(self.env['mrp.production'])
|
||
|
mnf_product_a_form.product_id = self.product_a
|
||
|
mnf_product_a_form.bom_id = bom_product_a
|
||
|
mnf_product_a_form.product_qty = 1.0
|
||
|
mnf_product_a = mnf_product_a_form.save()
|
||
|
mnf_product_a_form = Form(mnf_product_a)
|
||
|
mnf_product_a_form.bom_id = bom_product_a_2
|
||
|
mnf_product_a = mnf_product_a_form.save()
|
||
|
self.assertEqual(mnf_product_a.move_raw_ids.product_id.id, self.product_c_id)
|
||
|
self.assertFalse(mnf_product_a.move_byproduct_ids)
|
||
|
|
||
|
def test_default_uom(self):
|
||
|
""" Tests the `uom_id` on the byproduct gets set automatically while creating a byproduct with a product,
|
||
|
without the need to call an onchange or to set the uom manually in the create.
|
||
|
"""
|
||
|
# Set a specific UOM on the byproduct on purpose to make sure it's not just a default on the unit UOM
|
||
|
# that makes the test pass.
|
||
|
self.product_b.product_tmpl_id.uom_id = self.env.ref('uom.product_uom_dozen')
|
||
|
bom = self.MrpBom.create({
|
||
|
'product_tmpl_id': self.product_a.product_tmpl_id.id,
|
||
|
'product_qty': 1.0,
|
||
|
'type': 'normal',
|
||
|
'byproduct_ids': [(0, 0, {'product_id': self.product_b.id, 'product_qty': 1})]
|
||
|
})
|
||
|
self.assertEqual(bom.byproduct_ids.product_uom_id, self.env.ref('uom.product_uom_dozen'))
|
||
|
|
||
|
def test_finished_and_byproduct_moves(self):
|
||
|
"""
|
||
|
Tests the behavior of the `create` override in the model `mrp.production`
|
||
|
regarding the values for the fields `move_finished_ids` and `move_byproduct_ids`.
|
||
|
The behavior is a bit tricky, because the moves included in `move_byproduct_ids`
|
||
|
are included in the `move_finished_ids`. `move_byproduct_ids` is a subset of `move_finished_ids`.
|
||
|
|
||
|
So, when creating a manufacturing order, whether:
|
||
|
- Only `move_finished_ids` is passed, containing both the finished product and the by-products of the BOM,
|
||
|
- Only `move_byproduct_ids` is passed, only containing the by-products of the BOM,
|
||
|
- Both `move_finished_ids` and `move_byproduct_ids` are passed,
|
||
|
holding the product finished and the byproducts respectively
|
||
|
At the end, in the created manufacturing order
|
||
|
`move_finished_ids` must contain both the finished product, and the by-products,
|
||
|
`move_byproduct_ids` must contain only the by-products.
|
||
|
|
||
|
Besides, the code shouldn't raise an error
|
||
|
because only one of the two `move_finished_ids`, `move_byproduct_ids` is provided.
|
||
|
|
||
|
In addition, the test voluntary sets a different produced quantity
|
||
|
for the finished product and the by-products moves than defined in the BOM
|
||
|
as it's the point to manually pass the `move_finished_ids` and `move_byproduct_ids`
|
||
|
when creating a manufacturing order, set different values than the defaults, in this case
|
||
|
a different produced quantity than the defaults from the BOM.
|
||
|
"""
|
||
|
bom_product_a = self.MrpBom.create({
|
||
|
'product_tmpl_id': self.product_a.product_tmpl_id.id,
|
||
|
'product_qty': 1.0,
|
||
|
'type': 'normal',
|
||
|
'bom_line_ids': [(0, 0, {
|
||
|
'product_id': self.product_c_id, 'product_uom_id': self.uom_unit_id, 'product_qty': 2.0
|
||
|
})],
|
||
|
'byproduct_ids': [(0, 0, {
|
||
|
'product_id': self.product_b.id, 'product_uom_id': self.uom_unit_id, 'product_qty': 1.0
|
||
|
})]
|
||
|
})
|
||
|
for expected_finished_qty, expected_byproduct_qty, values in [
|
||
|
# Only `move_finished_ids` passed, containing both the finished product and the by-product
|
||
|
(3.0, 4.0, {
|
||
|
'move_finished_ids': [
|
||
|
(0, 0, {
|
||
|
'product_id': self.product_a.id,
|
||
|
'product_uom_qty': 3.0,
|
||
|
'location_id': self.product_a.property_stock_production,
|
||
|
'location_dest_id': self.warehouse.lot_stock_id.id,
|
||
|
}),
|
||
|
(0, 0, {
|
||
|
'product_id': self.product_b.id,
|
||
|
'product_uom_qty': 4.0,
|
||
|
'location_id': self.product_a.property_stock_production,
|
||
|
'location_dest_id': self.warehouse.lot_stock_id.id,
|
||
|
}),
|
||
|
],
|
||
|
}),
|
||
|
# Only `move_byproduct_ids` passed, containing the by-product move only
|
||
|
(2.0, 4.0, {
|
||
|
'move_byproduct_ids': [
|
||
|
(0, 0, {
|
||
|
'product_id': self.product_b.id,
|
||
|
'product_uom_qty': 4.0,
|
||
|
'location_id': self.product_a.property_stock_production,
|
||
|
'location_dest_id': self.warehouse.lot_stock_id.id,
|
||
|
}),
|
||
|
],
|
||
|
}),
|
||
|
# Both `move_finished_ids` and `move_byproduct_ids` passed,
|
||
|
# containing respectively the finished product and the by-product
|
||
|
(3.0, 4.0, {
|
||
|
'move_finished_ids': [
|
||
|
(0, 0, {
|
||
|
'product_id': self.product_a.id,
|
||
|
'product_uom_qty': 3.0,
|
||
|
'location_id': self.product_a.property_stock_production,
|
||
|
'location_dest_id': self.warehouse.lot_stock_id.id,
|
||
|
}),
|
||
|
],
|
||
|
'move_byproduct_ids': [
|
||
|
(0, 0, {
|
||
|
'product_id': self.product_b.id,
|
||
|
'product_uom_qty': 4.0,
|
||
|
'location_id': self.product_a.property_stock_production,
|
||
|
'location_dest_id': self.warehouse.lot_stock_id.id,
|
||
|
}),
|
||
|
],
|
||
|
}),
|
||
|
]:
|
||
|
mo = self.env['mrp.production'].create({
|
||
|
'product_id': self.product_a.id,
|
||
|
'bom_id': bom_product_a.id,
|
||
|
'product_qty': 2.0,
|
||
|
**values,
|
||
|
})
|
||
|
self.assertEqual(mo.move_finished_ids.product_id, self.product_a + self.product_b)
|
||
|
self.assertEqual(mo.move_byproduct_ids.product_id, self.product_b)
|
||
|
|
||
|
finished_move = mo.move_finished_ids.filtered(lambda x: x.product_id == self.product_a)
|
||
|
self.assertEqual(
|
||
|
finished_move.product_uom_qty, expected_finished_qty, "Wrong produced quantity of finished product."
|
||
|
)
|
||
|
|
||
|
by_product_move = mo.move_finished_ids.filtered(lambda x: x.product_id == self.product_b)
|
||
|
self.assertEqual(
|
||
|
by_product_move.product_uom_qty, expected_byproduct_qty, "Wrong produced quantity of by-product."
|
||
|
)
|
||
|
|
||
|
# Also check the produced quantity of the by-product through `move_byproduct_ids`
|
||
|
self.assertEqual(
|
||
|
mo.move_byproduct_ids.product_uom_qty, expected_byproduct_qty, "Wrong produced quantity of by-product."
|
||
|
)
|
||
|
|
||
|
def test_byproduct_putaway(self):
|
||
|
"""
|
||
|
Test the byproducts are dispatched correctly with putaway rules. We have
|
||
|
a byproduct P and two sublocations L01, L02 with a capacity constraint:
|
||
|
max 2 x P by location. There is already 1 x P at L01. Process a MO with
|
||
|
2 x P as byproducts. They should be redirected to L02
|
||
|
"""
|
||
|
|
||
|
self.stock_location = self.env.ref('stock.stock_location_stock')
|
||
|
stor_category = self.env['stock.storage.category'].create({
|
||
|
'name': 'Super Storage Category',
|
||
|
'max_weight': 1000,
|
||
|
'product_capacity_ids': [(0, 0, {
|
||
|
'product_id': self.product_b.id,
|
||
|
'quantity': 2,
|
||
|
})]
|
||
|
})
|
||
|
shelf1_location = self.env['stock.location'].create({
|
||
|
'name': 'shelf1',
|
||
|
'usage': 'internal',
|
||
|
'location_id': self.stock_location.id,
|
||
|
'storage_category_id': stor_category.id,
|
||
|
})
|
||
|
shelf2_location = self.env['stock.location'].create({
|
||
|
'name': 'shelf2',
|
||
|
'usage': 'internal',
|
||
|
'location_id': self.stock_location.id,
|
||
|
'storage_category_id': stor_category.id,
|
||
|
})
|
||
|
self.env['stock.putaway.rule'].create({
|
||
|
'product_id': self.product_b.id,
|
||
|
'location_in_id': self.stock_location.id,
|
||
|
'location_out_id': self.stock_location.id,
|
||
|
'storage_category_id': stor_category.id,
|
||
|
})
|
||
|
self.env['stock.putaway.rule'].create({
|
||
|
'product_id': self.product_a.id,
|
||
|
'location_in_id': self.stock_location.id,
|
||
|
'location_out_id': shelf2_location.id,
|
||
|
})
|
||
|
|
||
|
self.env['stock.quant']._update_available_quantity(self.product_b, shelf1_location, 1)
|
||
|
|
||
|
mo_form = Form(self.env['mrp.production'])
|
||
|
mo_form.product_id = self.product_a
|
||
|
mo_form.bom_id = self.bom_byproduct
|
||
|
mo_form.product_qty = 2.0
|
||
|
mo = mo_form.save()
|
||
|
mo.action_confirm()
|
||
|
mo_form = Form(mo)
|
||
|
mo_form.qty_producing = 2.00
|
||
|
mo = mo_form.save()
|
||
|
|
||
|
mo._post_inventory()
|
||
|
byproduct_move_line = mo.move_byproduct_ids.move_line_ids
|
||
|
finished_move_line = mo.move_finished_ids.filtered(lambda m: m.product_id == self.product_a).move_line_ids
|
||
|
self.assertEqual(byproduct_move_line.location_dest_id, shelf2_location)
|
||
|
self.assertEqual(finished_move_line.location_dest_id, shelf2_location)
|
||
|
|
||
|
def test_check_byproducts_cost_share(self):
|
||
|
"""
|
||
|
Test that byproducts with total cost_share > 100% or a cost_share < 0%
|
||
|
will throw a ValidationError
|
||
|
"""
|
||
|
# Create new MO
|
||
|
mo_form = Form(self.env['mrp.production'])
|
||
|
mo_form.product_id = self.product_a
|
||
|
mo_form.product_qty = 2.0
|
||
|
mo = mo_form.save()
|
||
|
|
||
|
# Create product
|
||
|
self.product_d = self.env['product.product'].create({
|
||
|
'name': 'Product D',
|
||
|
'type': 'product'})
|
||
|
self.product_e = self.env['product.product'].create({
|
||
|
'name': 'Product E',
|
||
|
'type': 'product'})
|
||
|
|
||
|
# Create byproduct
|
||
|
byproduct_1 = self.env['stock.move'].create({
|
||
|
'name': 'By Product 1',
|
||
|
'product_id': self.product_d.id,
|
||
|
'product_uom': self.ref('uom.product_uom_unit'),
|
||
|
'production_id': mo.id,
|
||
|
'location_id': self.ref('stock.stock_location_stock'),
|
||
|
'location_dest_id': self.ref('stock.stock_location_output'),
|
||
|
})
|
||
|
byproduct_2 = self.env['stock.move'].create({
|
||
|
'name': 'By Product 2',
|
||
|
'product_id': self.product_e.id,
|
||
|
'product_uom': self.ref('uom.product_uom_unit'),
|
||
|
'production_id': mo.id,
|
||
|
'location_id': self.ref('stock.stock_location_stock'),
|
||
|
'location_dest_id': self.ref('stock.stock_location_output'),
|
||
|
})
|
||
|
|
||
|
# Update byproduct has cost share > 100%
|
||
|
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||
|
byproduct_1.cost_share = 120
|
||
|
mo.write({'move_byproduct_ids': [(4, byproduct_1.id)]})
|
||
|
|
||
|
# Update byproduct has cost share < 0%
|
||
|
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||
|
byproduct_1.cost_share = -10
|
||
|
mo.write({'move_byproduct_ids': [(4, byproduct_1.id)]})
|
||
|
|
||
|
# Update byproducts have total cost share > 100%
|
||
|
with self.assertRaises(ValidationError), self.cr.savepoint():
|
||
|
byproduct_1.cost_share = 60
|
||
|
byproduct_2.cost_share = 70
|
||
|
mo.write({'move_byproduct_ids': [(6, 0, [byproduct_1.id, byproduct_2.id])]})
|
||
|
|
||
|
def test_check_byproducts_cost_share_02(self):
|
||
|
"""
|
||
|
Test that byproducts with total cost_share < 100% with a cancelled moves will don't throw a ValidationError
|
||
|
"""
|
||
|
self.bom_byproduct.byproduct_ids[0].cost_share = 70
|
||
|
self.bom_byproduct.byproduct_ids[0].product_qty = 2
|
||
|
mo = self.env["mrp.production"].create({
|
||
|
'product_id': self.product_a.id,
|
||
|
'product_qty': 1.0,
|
||
|
'bom_id': self.bom_byproduct.id,
|
||
|
})
|
||
|
mo.action_confirm()
|
||
|
self.assertEqual(mo.state, 'confirmed')
|
||
|
mo_form = Form(mo)
|
||
|
mo_form.qty_producing = 1
|
||
|
mo = mo_form.save()
|
||
|
self.assertEqual(mo.state, 'to_close')
|
||
|
mo.button_mark_done()
|
||
|
self.assertEqual(mo.state, 'done')
|
||
|
|
||
|
def test_01_check_byproducts_update(self):
|
||
|
"""
|
||
|
Test that check byproducts update in stock move should also reflect in stock move line(Product moves).
|
||
|
"""
|
||
|
# Create new MO
|
||
|
mo_form = Form(self.env['mrp.production'])
|
||
|
mo_form.product_id = self.product_a
|
||
|
mo_form.product_qty = 1.0
|
||
|
mo = mo_form.save()
|
||
|
mo.action_confirm()
|
||
|
|
||
|
mo.move_byproduct_ids.write({'product_id': self.product_c_id})
|
||
|
mo.button_mark_done()
|
||
|
self.assertEqual(mo.move_byproduct_ids.product_id, mo.move_byproduct_ids.move_line_ids.product_id)
|
||
|
|
||
|
def test_02_check_byproducts_update(self):
|
||
|
"""
|
||
|
Case 2: Update Product From Tracked Product to Non Tracked Product.
|
||
|
"""
|
||
|
self.bom_byproduct.byproduct_ids[0].product_id = self.produced_serial.id
|
||
|
self.bom_byproduct.byproduct_ids[0].product_qty = 2
|
||
|
mo = self.env["mrp.production"].create({
|
||
|
'product_id': self.product_a.id,
|
||
|
'product_qty': 1.0,
|
||
|
'bom_id': self.bom_byproduct.id,
|
||
|
})
|
||
|
mo.action_confirm()
|
||
|
|
||
|
mo.move_byproduct_ids.lot_ids = [(4, self.sn_1.id)]
|
||
|
mo.move_byproduct_ids.lot_ids = [(4, self.sn_2.id)]
|
||
|
|
||
|
self.assertEqual(len(mo.move_byproduct_ids.move_line_ids), 2)
|
||
|
|
||
|
mo.move_byproduct_ids.write({'product_id': self.product_c_id})
|
||
|
|
||
|
mo.button_mark_done()
|
||
|
self.assertEqual(len(mo.move_byproduct_ids.move_line_ids), 1)
|
||
|
self.assertEqual(mo.move_byproduct_ids.product_id, mo.move_byproduct_ids.move_line_ids.product_id)
|
||
|
|
||
|
def test_03_check_byproducts_update(self):
|
||
|
"""
|
||
|
Case 3: Update Product From Non Tracked Product to Tracked Product.
|
||
|
"""
|
||
|
mo_form = Form(self.env['mrp.production'])
|
||
|
mo_form.product_id = self.product_a
|
||
|
mo_form.product_qty = 2.0
|
||
|
mo = mo_form.save()
|
||
|
mo.action_confirm()
|
||
|
|
||
|
mo.move_byproduct_ids.write({'product_id': self.produced_serial.id})
|
||
|
|
||
|
mo.move_byproduct_ids.lot_ids = [(4, self.sn_1.id)]
|
||
|
mo.move_byproduct_ids.lot_ids = [(4, self.sn_2.id)]
|
||
|
|
||
|
mo.button_mark_done()
|
||
|
self.assertEqual(len(mo.move_byproduct_ids.move_line_ids), 2)
|
||
|
self.assertEqual(mo.move_byproduct_ids.product_id, mo.move_byproduct_ids.move_line_ids.product_id)
|