合并企业版代码(未测试,先提交到测试分支)
This commit is contained in:
7
mrp_workorder/tests/__init__.py
Normal file
7
mrp_workorder/tests/__init__.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from . import test_workorder
|
||||
from . import test_basic
|
||||
from . import test_duplicates
|
||||
from . import test_quality
|
||||
from . import test_dependencies
|
||||
from . import test_tablet_client_action
|
||||
from . import test_consume_tracked_component
|
||||
2231
mrp_workorder/tests/test_basic.py
Normal file
2231
mrp_workorder/tests/test_basic.py
Normal file
File diff suppressed because it is too large
Load Diff
172
mrp_workorder/tests/test_consume_tracked_component.py
Normal file
172
mrp_workorder/tests/test_consume_tracked_component.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from odoo.tests import tagged
|
||||
from odoo.addons.mrp.tests.common_consume_tracked_component import TestConsumeTrackedComponentCommon
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestConsumeTrackedComponent(TestConsumeTrackedComponentCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestConsumeTrackedComponent, cls).setUpClass()
|
||||
|
||||
cls.consume_quality_point = cls.env.ref('mrp_workorder.test_type_register_consumed_materials')
|
||||
|
||||
cls.routing_workcenter_serial = cls.env['mrp.routing.workcenter'].create({
|
||||
'bom_id' : cls.bom_serial.id,
|
||||
'workcenter_id' : cls.workcenter.id,
|
||||
'time_cycle' : 120,
|
||||
'sequence' : 1,
|
||||
'name' : 'Assembly'
|
||||
})
|
||||
|
||||
cls.bom_serial_line_serial = list(filter(lambda boml: boml.tracking == 'serial', cls.bom_serial.bom_line_ids))[0]
|
||||
cls.bom_serial_line_serial.operation_id = cls.routing_workcenter_serial
|
||||
|
||||
cls.quality_point = cls.env['quality.point'].create({
|
||||
'product_ids' : [(4, cls.produced_serial.id)],
|
||||
'operation_id' : cls.routing_workcenter_serial.id,
|
||||
'test_type_id' : cls.consume_quality_point.id,
|
||||
'component_id' : cls.raw_serial.id,
|
||||
'sequence' : 1,
|
||||
'note' : 'Please enter the Raw Serial serial number',
|
||||
'title' : 'Component Registration : Raw Serial',
|
||||
'picking_type_ids' : [(4, cls.picking_type.id)],
|
||||
})
|
||||
|
||||
def test_option_enabled_and_operation_id_and_quality_point(self):
|
||||
"""
|
||||
Auto-consume is enabled
|
||||
There's a BoM.operation_id and a quality_point on the 'serial' component
|
||||
-> Consumption step already exist
|
||||
-> No auto-consumption of the 'serial' component
|
||||
-> Auto-consumption of other components
|
||||
"""
|
||||
self.assertTrue(self.picking_type.use_auto_consume_components_lots)
|
||||
#Create manufacturing order
|
||||
mo_serial = self.create_mo(self.mo_serial_tmpl, 1)
|
||||
mo_serial.action_confirm()
|
||||
|
||||
self.assertTrue(self.raw_serial in mo_serial.workorder_ids.quality_point_ids.component_id)
|
||||
self.assertTrue(self.routing_workcenter_serial in mo_serial.move_raw_ids.operation_id)
|
||||
|
||||
quant = self.create_quant(self.raw_none, 3)
|
||||
quant |= self.create_quant(self.raw_lot, 2)
|
||||
quant |= self.create_quant(self.raw_serial, 1)
|
||||
quant.action_apply_inventory()
|
||||
|
||||
#Quantities are fully reserved (stock.move state is available)
|
||||
mo_serial.action_assign()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.")
|
||||
|
||||
mo_serial.action_generate_serial()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
if mov.has_tracking != 'serial':
|
||||
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
|
||||
else:
|
||||
self.assertEqual(0, mov.quantity_done, "Done quantity shall be equal to 0.")
|
||||
|
||||
def test_option_enabled_and_operation_id(self):
|
||||
"""
|
||||
Auto-consume is enabled
|
||||
There's a BoM.operation_id on the 'serial' component
|
||||
-> Consumption step is created
|
||||
-> No auto-consumption of the 'serial' component
|
||||
-> Auto-consumption of other components
|
||||
"""
|
||||
self.assertTrue(self.picking_type.use_auto_consume_components_lots)
|
||||
|
||||
self.quality_point.active = False
|
||||
|
||||
#Create manufacturing order
|
||||
mo_serial = self.create_mo(self.mo_serial_tmpl, 1)
|
||||
mo_serial.action_confirm()
|
||||
|
||||
self.assertFalse(self.raw_serial in mo_serial.workorder_ids.quality_point_ids.component_id)
|
||||
self.assertTrue(self.routing_workcenter_serial in mo_serial.move_raw_ids.operation_id)
|
||||
|
||||
quant = self.create_quant(self.raw_none, 3)
|
||||
quant |= self.create_quant(self.raw_lot, 2)
|
||||
quant |= self.create_quant(self.raw_serial, 1)
|
||||
quant.action_apply_inventory()
|
||||
|
||||
#Quantities are fully reserved (stock.move state is available)
|
||||
mo_serial.action_assign()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.")
|
||||
|
||||
mo_serial.action_generate_serial()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
if mov.has_tracking != 'serial':
|
||||
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
|
||||
else:
|
||||
self.assertEqual(0, mov.quantity_done, "Done quantity shall be equal to 0.")
|
||||
|
||||
def test_option_enabled_and_quality_point(self):
|
||||
"""
|
||||
Auto-consume is enabled
|
||||
There's a quality_point on the 'serial' component
|
||||
-> Consumption step already exist
|
||||
-> No auto-consumption of the 'serial' component
|
||||
-> Auto-consumption of other components
|
||||
"""
|
||||
self.assertTrue(self.picking_type.use_auto_consume_components_lots)
|
||||
|
||||
self.quality_point.active = True
|
||||
self.bom_serial_line_serial.operation_id = self.env['mrp.routing.workcenter']
|
||||
|
||||
#Create manufacturing order
|
||||
mo_serial = self.create_mo(self.mo_serial_tmpl, 1)
|
||||
mo_serial.action_confirm()
|
||||
|
||||
self.assertTrue(self.raw_serial in mo_serial.workorder_ids.quality_point_ids.component_id)
|
||||
self.assertFalse(self.routing_workcenter_serial in mo_serial.move_raw_ids.operation_id)
|
||||
|
||||
quant = self.create_quant(self.raw_none, 3)
|
||||
quant |= self.create_quant(self.raw_lot, 2)
|
||||
quant |= self.create_quant(self.raw_serial, 1)
|
||||
quant.action_apply_inventory()
|
||||
|
||||
#Quantities are fully reserved (stock.move state is available)
|
||||
mo_serial.action_assign()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.")
|
||||
|
||||
mo_serial.action_generate_serial()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
if mov.has_tracking != 'serial':
|
||||
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
|
||||
else:
|
||||
self.assertEqual(0, mov.quantity_done, "Done quantity shall be equal to 0.")
|
||||
|
||||
def test_option_enabled_only(self):
|
||||
"""
|
||||
Auto-consume is enabled
|
||||
There's neither a BoM.operation_id nor a quality_point on the 'serial' component
|
||||
-> No Consumption step
|
||||
-> Auto-consumption of all components
|
||||
"""
|
||||
self.assertTrue(self.picking_type.use_auto_consume_components_lots)
|
||||
|
||||
self.quality_point.active = False
|
||||
self.bom_serial_line_serial.operation_id = self.env['mrp.routing.workcenter']
|
||||
|
||||
#Create manufacturing order
|
||||
mo_serial = self.create_mo(self.mo_serial_tmpl, 1)
|
||||
mo_serial.action_confirm()
|
||||
|
||||
self.assertFalse(self.raw_serial in mo_serial.workorder_ids.quality_point_ids.component_id)
|
||||
self.assertFalse(self.routing_workcenter_serial in mo_serial.move_raw_ids.operation_id)
|
||||
|
||||
quant = self.create_quant(self.raw_none, 3)
|
||||
quant |= self.create_quant(self.raw_lot, 2)
|
||||
quant |= self.create_quant(self.raw_serial, 1)
|
||||
quant.action_apply_inventory()
|
||||
|
||||
#Quantities are fully reserved (stock.move state is available)
|
||||
mo_serial.action_assign()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
self.assertEqual(mov.product_qty, mov.reserved_availability, "Reserved quantity shall be equal to To Consume quantity.")
|
||||
|
||||
mo_serial.action_generate_serial()
|
||||
for mov in mo_serial.move_raw_ids:
|
||||
self.assertEqual(mov.product_qty, mov.quantity_done, "Done quantity shall be equal to To Consume quantity.")
|
||||
209
mrp_workorder/tests/test_dependencies.py
Normal file
209
mrp_workorder/tests/test_dependencies.py
Normal file
@@ -0,0 +1,209 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import Command
|
||||
from odoo.addons.mrp.tests import common
|
||||
from odoo.tests import Form
|
||||
|
||||
|
||||
class TestWorkOrderDependencies(common.TestMrpCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.wkct1 = cls.env['mrp.workcenter'].create({
|
||||
'name': 'Workcenter#1',
|
||||
})
|
||||
cls.wkct2 = cls.env['mrp.workcenter'].create({
|
||||
'name': 'Workcenter#2',
|
||||
})
|
||||
cls.wkct3 = cls.env['mrp.workcenter'].create({
|
||||
'name': 'Workcenter#3',
|
||||
})
|
||||
cls.finished = cls.env['product.product'].create({
|
||||
'name': 'Finished Product',
|
||||
'type': 'product',
|
||||
})
|
||||
cls.component1 = cls.env['product.product'].create({
|
||||
'name': 'Component#1',
|
||||
'type': 'product',
|
||||
})
|
||||
cls.component2 = cls.env['product.product'].create({
|
||||
'name': 'Component#2',
|
||||
'type': 'product',
|
||||
})
|
||||
cls.bom = cls.env['mrp.bom'].create({
|
||||
'product_id': cls.finished.id,
|
||||
'product_tmpl_id': cls.finished.product_tmpl_id.id,
|
||||
'product_qty': 1.0,
|
||||
'bom_line_ids': [
|
||||
Command.create({'product_id': cls.component1.id, 'product_qty': 1}),
|
||||
Command.create({'product_id': cls.component2.id, 'product_qty': 2})
|
||||
],
|
||||
'operation_ids': [
|
||||
Command.create({'name': 'Operation#A', 'workcenter_id': cls.wkct1.id}),
|
||||
Command.create({'name': 'Operation#B', 'workcenter_id': cls.wkct2.id}),
|
||||
Command.create({'name': 'Operation#C', 'workcenter_id': cls.wkct3.id}),
|
||||
],
|
||||
'allow_operation_dependencies': True,
|
||||
})
|
||||
cls.stock_location = cls.env.ref('stock.stock_location_stock')
|
||||
cls.env['stock.quant']._update_available_quantity(cls.component1, cls.stock_location, 100)
|
||||
cls.env['stock.quant']._update_available_quantity(cls.component2, cls.stock_location, 100)
|
||||
|
||||
def test_parallel_workorders(self):
|
||||
""" Test parallel workorders: bom allowing operation dependencies without any dependency."""
|
||||
|
||||
# Make MO
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.finished
|
||||
mo_form.product_qty = 2.0
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
|
||||
# Check workorders initial state
|
||||
self.assertEqual(mo.workorder_ids[0].state, 'ready', "All workorders should be ready.")
|
||||
self.assertEqual(mo.workorder_ids[1].state, 'ready', "All workorders should be ready.")
|
||||
self.assertEqual(mo.workorder_ids[2].state, 'ready', "All workorders should be ready.")
|
||||
|
||||
def test_stepped_workorders(self):
|
||||
""" Test step-by-step workorders: bom operations are interdependent."""
|
||||
|
||||
# Make 1st workorder depend on 3rd
|
||||
self.bom.operation_ids[0].blocked_by_operation_ids = [Command.link(self.bom.operation_ids[2].id)]
|
||||
|
||||
# Make MO
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.finished
|
||||
mo_form.product_qty = 2.0
|
||||
mo = mo_form.save()
|
||||
mo.action_confirm()
|
||||
wo1, wo2, wo3 = mo.workorder_ids
|
||||
# Check workorders initial state
|
||||
self.assertEqual(wo1.state, 'pending', "Workorder for Operation-A should be Waiting for another WO (the 3rd).")
|
||||
self.assertEqual(wo2.state, 'ready', "Workorder for Operation-B should be ready.")
|
||||
self.assertEqual(wo3.state, 'ready', "Workorder for Operation-C should be ready.")
|
||||
mo.button_plan()
|
||||
# Mark 1st initial WO as done
|
||||
wo2.button_start()
|
||||
wo2.qty_producing = 2
|
||||
wo2.record_production()
|
||||
# Check 3rd WO (not dependent on 1st)
|
||||
self.assertEqual(wo1.state, 'pending', "Workorder for Operation-A should STILL be Waiting for another WO (the 3rd).")
|
||||
# Mark 2nd initial WO as done
|
||||
wo3.button_start()
|
||||
wo3.qty_producing = 2
|
||||
wo3.record_production()
|
||||
# Check dependent WO
|
||||
self.assertEqual(wo1.state, 'ready', "Workorder for Operation-A can be started, as its predecessors are now done.")
|
||||
|
||||
def test_propagate_quantity_on_backorders_with_stepped_workorders(self):
|
||||
"""Create a MO for a product with several work orders.
|
||||
Produce different quantities to test quantity propagation and workorder cancellation.
|
||||
-> Reproduce test_propagate_quantity_on_backorders on stepped workorders
|
||||
"""
|
||||
|
||||
# Make 1st workorder depend on 3rd
|
||||
self.bom.operation_ids[0].blocked_by_operation_ids = [Command.link(self.bom.operation_ids[2].id)]
|
||||
|
||||
# Make MO for 20 products
|
||||
|
||||
mo_form = Form(self.env['mrp.production'])
|
||||
mo_form.product_id = self.finished
|
||||
mo_form.product_qty = 20
|
||||
mo = mo_form.save()
|
||||
|
||||
self.assertEqual(mo.state, 'draft')
|
||||
mo.action_confirm()
|
||||
|
||||
wo_1, wo_2, wo_3 = mo.workorder_ids
|
||||
self.assertEqual(mo.state, 'confirmed')
|
||||
self.assertEqual(wo_1.state, 'pending')
|
||||
self.assertEqual(wo_2.state, 'ready')
|
||||
self.assertEqual(wo_3.state, 'ready')
|
||||
|
||||
# produce 20 / 10 / 5 on workorders 2 / 3 / 1, mark as done & create backorder
|
||||
# mo closed with 5 produced
|
||||
# backorder for 15 created with
|
||||
# - wo5 'cancel' (fully processed)
|
||||
# - wo6 'ready' for 10
|
||||
# - wo4 'pending' for 15
|
||||
|
||||
wo_2.button_start()
|
||||
wo_2.qty_producing = 20
|
||||
self.assertEqual(mo.state, 'progress')
|
||||
wo_2.button_finish()
|
||||
|
||||
wo_3.button_start()
|
||||
wo_3.qty_producing = 10
|
||||
wo_3.button_finish()
|
||||
|
||||
wo_1.button_start()
|
||||
wo_1.qty_producing = 5
|
||||
wo_1.button_finish()
|
||||
|
||||
self.assertEqual(mo.state, 'to_close')
|
||||
mo.button_mark_done()
|
||||
|
||||
bo = self.env['mrp.production.backorder'].create({
|
||||
"mrp_production_backorder_line_ids": [
|
||||
[0, 0, {"mrp_production_id": mo.id, "to_backorder": True}]
|
||||
]
|
||||
})
|
||||
bo.action_backorder()
|
||||
|
||||
self.assertEqual(mo.state, 'done')
|
||||
|
||||
mo_2 = mo.procurement_group_id.mrp_production_ids - mo
|
||||
wo_4, wo_5, wo_6 = mo_2.workorder_ids
|
||||
|
||||
self.assertEqual(wo_4.state, 'pending')
|
||||
self.assertEqual(wo_5.state, 'cancel')
|
||||
self.assertEqual(wo_6.state, 'ready')
|
||||
|
||||
# produce 10 / 5 on workorders 6 / 4, mark as done & create backorder
|
||||
# mo closed with 5 produced
|
||||
# backorder for 10 created with
|
||||
# - wo8 'cancel' (already fully processed)
|
||||
# - wo9 'cancel' (fully processed)
|
||||
# - wo7 'ready' for 10
|
||||
|
||||
wo_6.button_start()
|
||||
wo_6.qty_producing = 10
|
||||
self.assertEqual(mo_2.state, 'progress')
|
||||
wo_6.button_finish()
|
||||
|
||||
wo_4.button_start()
|
||||
wo_4.qty_producing = 5
|
||||
wo_4.button_finish()
|
||||
|
||||
self.assertEqual(mo_2.state, 'to_close')
|
||||
mo_2.button_mark_done()
|
||||
|
||||
bo = self.env['mrp.production.backorder'].create({
|
||||
"mrp_production_backorder_line_ids": [
|
||||
[0, 0, {"mrp_production_id": mo_2.id, "to_backorder": True}]
|
||||
]
|
||||
})
|
||||
bo.action_backorder()
|
||||
|
||||
self.assertEqual(mo_2.state, 'done')
|
||||
|
||||
mo_3 = mo.procurement_group_id.mrp_production_ids - (mo | mo_2)
|
||||
wo_7, wo_8, wo_9 = mo_3.workorder_ids
|
||||
|
||||
self.assertEqual(wo_7.state, 'ready')
|
||||
self.assertEqual(wo_8.state, 'cancel')
|
||||
self.assertEqual(wo_9.state, 'cancel')
|
||||
|
||||
# produce 10 on workorder 7 and finish work
|
||||
|
||||
wo_7.button_start()
|
||||
wo_7.qty_producing = 10
|
||||
self.assertEqual(mo_3.state, 'progress')
|
||||
wo_7.button_finish()
|
||||
|
||||
self.assertEqual(mo_3.state, 'to_close')
|
||||
mo_3.button_mark_done()
|
||||
self.assertEqual(mo_3.state, 'done')
|
||||
204
mrp_workorder/tests/test_duplicates.py
Normal file
204
mrp_workorder/tests/test_duplicates.py
Normal file
@@ -0,0 +1,204 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import Form, common
|
||||
|
||||
|
||||
class TestDuplicateProducts(common.TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestDuplicateProducts, cls).setUpClass()
|
||||
cls.workcenter_1 = cls.env['mrp.workcenter'].create({
|
||||
'name': 'Nuclear Workcenter',
|
||||
'default_capacity': 2,
|
||||
'time_start': 10,
|
||||
'time_stop': 5,
|
||||
'time_efficiency': 80,
|
||||
})
|
||||
# Products and lots
|
||||
cls.painted_boat = cls.env['product.product'].create({
|
||||
'name': 'Painted boat',
|
||||
'type': 'product',
|
||||
'tracking': 'serial'})
|
||||
cls.pb1 = cls.env['stock.lot'].create({
|
||||
'company_id': cls.env.company.id,
|
||||
'product_id': cls.painted_boat.id,
|
||||
'name': 'pb1'})
|
||||
cls.blank_boat = cls.env['product.product'].create({
|
||||
'name': 'Blank Boat',
|
||||
'type': 'product',
|
||||
'tracking': 'serial'})
|
||||
cls.bb1 = cls.env['stock.lot'].create({
|
||||
'company_id': cls.env.company.id,
|
||||
'product_id': cls.blank_boat.id,
|
||||
'name': 'bb1'})
|
||||
cls.painting = cls.env['product.product'].create({
|
||||
'name': 'Color Painting',
|
||||
'type': 'product',
|
||||
'tracking': 'lot'})
|
||||
cls.p1 = cls.env['stock.lot'].create({
|
||||
'company_id': cls.env.company.id,
|
||||
'product_id': cls.painting.id,
|
||||
'name': 'p1'})
|
||||
|
||||
# Bill of material
|
||||
cls.bom_boat = cls.env['mrp.bom'].create({
|
||||
'product_tmpl_id': cls.painted_boat.product_tmpl_id.id,
|
||||
'product_qty': 1.0})
|
||||
cls.operation_1 = cls.env['mrp.routing.workcenter'].create({
|
||||
'name': 'Gift Wrap Maching',
|
||||
'workcenter_id': cls.workcenter_1.id,
|
||||
'bom_id': cls.bom_boat.id,
|
||||
'time_cycle': 15,
|
||||
'sequence': 1,
|
||||
})
|
||||
cls.env['mrp.bom.line'].create({
|
||||
'product_id': cls.blank_boat.id,
|
||||
'product_qty': 1.0,
|
||||
'bom_id': cls.bom_boat.id})
|
||||
# first painting layer
|
||||
cls.env['mrp.bom.line'].create({
|
||||
'product_id': cls.painting.id,
|
||||
'product_qty': 1.0,
|
||||
'bom_id': cls.bom_boat.id})
|
||||
# second painting layer
|
||||
cls.env['mrp.bom.line'].create({
|
||||
'product_id': cls.painting.id,
|
||||
'product_qty': 1.0,
|
||||
'bom_id': cls.bom_boat.id})
|
||||
|
||||
# Update quantities
|
||||
cls.location_1 = cls.env.ref('stock.stock_location_stock')
|
||||
Quant = cls.env['stock.quant']
|
||||
Quant._update_available_quantity(cls.blank_boat, cls.location_1, 1.0, lot_id=cls.bb1)
|
||||
Quant._update_available_quantity(cls.painting, cls.location_1, 10.0, lot_id=cls.p1)
|
||||
|
||||
def test_duplicate_without_point(self):
|
||||
""" Bom with the same tracked product in 2 bom lines"""
|
||||
mrp_order_form = Form(self.env['mrp.production'])
|
||||
mrp_order_form.product_id = self.painted_boat
|
||||
mrp_order_form.product_qty = 1
|
||||
production = mrp_order_form.save()
|
||||
|
||||
production.action_confirm()
|
||||
production.button_plan()
|
||||
self.assertEqual(len(production.workorder_ids), 1, "wrong number of workorders")
|
||||
self.assertEqual(production.workorder_ids.state, 'ready', "workorder state should be 'ready'")
|
||||
self.assertEqual(len(production.workorder_ids.check_ids), 3, "Same components are not merged, should be 3 quality checks")
|
||||
painting_checks = production.workorder_ids.check_ids.filtered(lambda check: check.component_id == self.painting)
|
||||
self.assertEqual(len(painting_checks), 2, "should be 2 quality checks for painting")
|
||||
|
||||
def test_duplicate_with_points(self):
|
||||
""" Bom with the same non tracked product in 2 bom lines and a quality point
|
||||
on this component"""
|
||||
self.painting.tracking = 'none'
|
||||
self.blank_boat.tracking = 'none'
|
||||
self.env['quality.point'].create({
|
||||
'product_ids': [(4, self.painted_boat.id)],
|
||||
'picking_type_ids': [(4, self.env['stock.picking.type'].search([('code', '=', 'mrp_operation')], limit=1).id)],
|
||||
'operation_id': self.operation_1.id,
|
||||
'test_type_id': self.env.ref('mrp_workorder.test_type_register_consumed_materials').id,
|
||||
'component_id': self.painting.id,
|
||||
})
|
||||
self.env['quality.point'].create({
|
||||
'product_ids': [(4, self.painted_boat.id)],
|
||||
'picking_type_ids': [(4, self.env['stock.picking.type'].search([('code', '=', 'mrp_operation')], limit=1).id)],
|
||||
'operation_id': self.operation_1.id,
|
||||
'test_type_id': self.env.ref('mrp_workorder.test_type_register_consumed_materials').id,
|
||||
'component_id': self.blank_boat.id,
|
||||
})
|
||||
self.bom_boat.bom_line_ids.write({'operation_id': self.operation_1.id})
|
||||
|
||||
mrp_order_form = Form(self.env['mrp.production'])
|
||||
mrp_order_form.product_id = self.painted_boat
|
||||
mrp_order_form.product_qty = 1
|
||||
production = mrp_order_form.save()
|
||||
|
||||
production.action_confirm()
|
||||
production.button_plan()
|
||||
self.assertEqual(len(production.workorder_ids), 1, "wrong number of workorders")
|
||||
self.assertEqual(production.workorder_ids.state, 'ready', "workorder state should be 'ready'")
|
||||
self.assertEqual(len(production.workorder_ids.check_ids), 3, "Same components are not merged, should be 3 quality checks")
|
||||
painting_checks = production.workorder_ids.check_ids.filtered(lambda check: check.component_id == self.painting)
|
||||
self.assertEqual(len(painting_checks), 2, "should be 2 quality checks for painting")
|
||||
|
||||
def test_assignation_1(self):
|
||||
""" Bom with the same tracked product in 2 bom lines
|
||||
Plan the workorder before reservign quantities """
|
||||
mrp_order_form = Form(self.env['mrp.production'])
|
||||
mrp_order_form.product_id = self.painted_boat
|
||||
mrp_order_form.product_qty = 1
|
||||
production = mrp_order_form.save()
|
||||
|
||||
production.action_confirm()
|
||||
production.button_plan()
|
||||
self.assertEqual(len(production.workorder_ids), 1, "wrong number of workorders")
|
||||
self.assertEqual(production.workorder_ids.state, 'ready', "workorder state should be 'ready'")
|
||||
self.assertEqual(len(production.workorder_ids.check_ids), 3, "Same components are not merged, should be 3 quality checks")
|
||||
painting_checks = production.workorder_ids.check_ids.filtered(lambda check: check.component_id == self.painting)
|
||||
self.assertEqual(len(painting_checks), 2, "should be 2 quality checks for painting")
|
||||
production.action_assign()
|
||||
self.assertEqual(len(production.workorder_ids.check_ids), 3, "Same components merged, should be 3 quality checks")
|
||||
painting_checks = production.workorder_ids.check_ids.filtered(lambda check: check.component_id == self.painting)
|
||||
self.assertEqual(len(painting_checks), 2, "should be 2 quality checks for painting")
|
||||
|
||||
def test_byproduct_1(self):
|
||||
""" Use the same product as component and as byproduct"""
|
||||
# Required for `byproduct_ids` to be visible in the view
|
||||
self.env.user.groups_id += self.env.ref('mrp.group_mrp_byproducts')
|
||||
bom_form = Form(self.bom_boat)
|
||||
with bom_form.byproduct_ids.new() as bp:
|
||||
bp.product_id = self.painting
|
||||
bp.product_qty = 1.0
|
||||
bom_form.save()
|
||||
self.p2 = self.env['stock.lot'].create({
|
||||
'company_id': self.env.company.id,
|
||||
'product_id': self.painting.id,
|
||||
'name': 'p2'})
|
||||
|
||||
mrp_order_form = Form(self.env['mrp.production'])
|
||||
mrp_order_form.product_id = self.painted_boat
|
||||
mrp_order_form.product_qty = 1
|
||||
production = mrp_order_form.save()
|
||||
|
||||
production.action_confirm()
|
||||
production.button_plan()
|
||||
|
||||
production.workorder_ids[0].button_start()
|
||||
wo_form = Form(production.workorder_ids[0], view='mrp_workorder.mrp_workorder_view_form_tablet')
|
||||
# Components
|
||||
wo_form.finished_lot_id = self.pb1
|
||||
wo = wo_form.save()
|
||||
qc_form = Form(wo.current_quality_check_id, view='mrp_workorder.quality_check_view_form_tablet')
|
||||
qc_form.lot_id = self.bb1
|
||||
qc = qc_form.save()
|
||||
qc._next()
|
||||
# First layer
|
||||
qc_form = Form(wo.current_quality_check_id, view='mrp_workorder.quality_check_view_form_tablet')
|
||||
qc_form.lot_id = self.p1
|
||||
qc = qc_form.save()
|
||||
qc._next()
|
||||
# Second layer
|
||||
qc_form = Form(wo.current_quality_check_id, view='mrp_workorder.quality_check_view_form_tablet')
|
||||
qc_form.lot_id = self.p1
|
||||
qc = qc_form.save()
|
||||
qc._next()
|
||||
qc_form = Form(wo.current_quality_check_id, view='mrp_workorder.quality_check_view_form_tablet')
|
||||
# Byproduct
|
||||
qc_form.lot_id = self.p2
|
||||
qc = qc_form.save()
|
||||
qc._next()
|
||||
wo.do_finish()
|
||||
production.button_mark_done()
|
||||
|
||||
move_paint_raw = production.move_raw_ids.filtered(lambda move: move.product_id == self.painting)
|
||||
self.assertEqual(len(move_paint_raw), 2, 'there should be 2 moves after merge same components')
|
||||
self.assertEqual(move_paint_raw.mapped('state'), ['done', 'done'], 'Moves should be done')
|
||||
self.assertEqual(move_paint_raw.mapped('quantity_done'), [1, 1], 'Consumed quantity should be 2')
|
||||
self.assertEqual(len(move_paint_raw.move_line_ids), 2, 'their should be 2 move lines')
|
||||
self.assertEqual(move_paint_raw.mapped('move_line_ids').mapped('lot_id'), self.p1, 'Wrong lot numbers used')
|
||||
move_paint_finished = production.move_finished_ids.filtered(lambda move: move.product_id == self.painting)
|
||||
self.assertEqual(move_paint_finished.state, 'done', 'Move should be done')
|
||||
self.assertEqual(move_paint_finished.quantity_done, 1, 'Consumed quantity should be 1')
|
||||
self.assertEqual(len(move_paint_finished.move_line_ids), 1, 'their should be 1 move line')
|
||||
self.assertEqual(move_paint_finished.move_line_ids.lot_id, self.p2, 'Wrong lot numbers used')
|
||||
57
mrp_workorder/tests/test_quality.py
Normal file
57
mrp_workorder/tests/test_quality.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import Form
|
||||
from odoo.tests.common import TransactionCase
|
||||
|
||||
|
||||
class TestQuality(TransactionCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.product_1 = cls.env['product.product'].create({'name': 'Table'})
|
||||
cls.product_2 = cls.env['product.product'].create({'name': 'Table top'})
|
||||
cls.product_3 = cls.env['product.product'].create({'name': 'Table leg'})
|
||||
cls.workcenter_1 = cls.env['mrp.workcenter'].create({
|
||||
'name': 'Test Workcenter',
|
||||
'default_capacity': 2,
|
||||
'time_start': 10,
|
||||
'time_stop': 5,
|
||||
'time_efficiency': 80,
|
||||
})
|
||||
cls.bom = cls.env['mrp.bom'].create({
|
||||
'product_id': cls.product_1.id,
|
||||
'product_tmpl_id': cls.product_1.product_tmpl_id.id,
|
||||
'product_uom_id': cls.product_1.uom_id.id,
|
||||
'product_qty': 1.0,
|
||||
'consumption': 'flexible',
|
||||
'operation_ids': [
|
||||
(0, 0, {'name': 'Assembly', 'workcenter_id': cls.workcenter_1.id, 'time_cycle': 15, 'sequence': 1}),
|
||||
],
|
||||
'type': 'normal',
|
||||
'bom_line_ids': [
|
||||
(0, 0, {'product_id': cls.product_2.id, 'product_qty': 1}),
|
||||
(0, 0, {'product_id': cls.product_3.id, 'product_qty': 4})
|
||||
]
|
||||
})
|
||||
|
||||
def test_quality_point_onchange(self):
|
||||
quality_point_form = Form(self.env['quality.point'].with_context(default_product_ids=[self.product_2.id]))
|
||||
# Form should keep the default products set
|
||||
self.assertEqual(len(quality_point_form.product_ids), 1)
|
||||
self.assertEqual(quality_point_form.product_ids[0].id, self.product_2.id)
|
||||
# <field name="operation_id" attrs="{'invisible': [('is_workorder_step', '=', False)]}"/>
|
||||
# @api.depends('operation_id', 'picking_type_ids')
|
||||
# def _compute_is_workorder_step(self):
|
||||
# for quality_point in self:
|
||||
# quality_point.is_workorder_step = quality_point.operation_id or quality_point.picking_type_ids and\
|
||||
# all(pt.code == 'mrp_operation' for pt in quality_point.picking_type_ids)
|
||||
quality_point_form.picking_type_ids.add(
|
||||
self.env['stock.picking.type'].search([('code', '=', 'mrp_operation')], limit=1)
|
||||
)
|
||||
# Select a workorder operation
|
||||
quality_point_form.operation_id = self.bom.operation_ids[0]
|
||||
# Product should be replaced by the product linked to the bom
|
||||
self.assertEqual(len(quality_point_form.product_ids), 1)
|
||||
self.assertEqual(quality_point_form.product_ids[0].id, self.bom.product_id.id)
|
||||
89
mrp_workorder/tests/test_tablet_client_action.py
Normal file
89
mrp_workorder/tests/test_tablet_client_action.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
# from odoo.fields import Markup
|
||||
from odoo.tests import Form, HttpCase, tagged
|
||||
from .test_workorder import TestWorkOrder
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestPickingWorkorderClientAction(TestWorkOrder, HttpCase):
|
||||
def _get_client_action_url(self, workorder_id):
|
||||
action = self.env["ir.actions.actions"]._for_xml_id("mrp_workorder.tablet_client_action")
|
||||
return '/web#action=%s&active_id=%s' % (action['id'], workorder_id)
|
||||
|
||||
def test_add_component(self):
|
||||
self.bom_submarine.bom_line_ids.write({'operation_id': False})
|
||||
self.bom_submarine.operation_ids = False
|
||||
self.bom_submarine.write({
|
||||
'operation_ids': [(0, 0, {
|
||||
'workcenter_id': self.mrp_workcenter_3.id,
|
||||
'name': 'Manual Assembly',
|
||||
'time_cycle': 60,
|
||||
})]
|
||||
})
|
||||
self.bom_submarine.consumption = 'flexible'
|
||||
self.env['stock.lot'].create([{
|
||||
'product_id': self.submarine_pod.id,
|
||||
'name': 'sn1',
|
||||
'company_id': self.env.company.id,
|
||||
}])
|
||||
mrp_order_form = Form(self.env['mrp.production'])
|
||||
mrp_order_form.product_id = self.submarine_pod
|
||||
production = mrp_order_form.save()
|
||||
production.action_confirm()
|
||||
production.action_assign()
|
||||
production.button_plan()
|
||||
self.assertEqual(len(production.workorder_ids.check_ids), 2)
|
||||
wo = production.workorder_ids[0]
|
||||
wo.button_start()
|
||||
extra = self.env['product.product'].create({
|
||||
'name': 'extra',
|
||||
'type': 'product',
|
||||
'tracking': 'lot',
|
||||
})
|
||||
extra_bp = self.env['product.product'].create({
|
||||
'name': 'extra-bp',
|
||||
'type': 'product',
|
||||
'tracking': 'lot',
|
||||
})
|
||||
self.env['stock.lot'].create([{
|
||||
'product_id': extra.id,
|
||||
'name': 'lot1',
|
||||
'company_id': self.env.company.id,
|
||||
}, {
|
||||
'product_id': extra_bp.id,
|
||||
'name': 'lot2',
|
||||
'company_id': self.env.company.id,
|
||||
}])
|
||||
url = self._get_client_action_url(wo.id)
|
||||
self.start_tour(url, 'test_add_component', login='admin', timeout=80)
|
||||
|
||||
def test_add_step(self):
|
||||
""" Add a step as instruction in the tablet view via the 'suggest
|
||||
worksheet improvement' """
|
||||
self.bom_submarine.consumption = 'flexible'
|
||||
self.env['stock.lot'].create([{
|
||||
'product_id': self.submarine_pod.id,
|
||||
'name': 'sn1',
|
||||
'company_id': self.env.company.id,
|
||||
}])
|
||||
mrp_order_form = Form(self.env['mrp.production'])
|
||||
mrp_order_form.product_id = self.submarine_pod
|
||||
production = mrp_order_form.save()
|
||||
production.action_confirm()
|
||||
production.action_assign()
|
||||
production.button_plan()
|
||||
self.assertEqual(len(production.workorder_ids.check_ids), 2)
|
||||
wo = production.workorder_ids[0]
|
||||
wo.button_start()
|
||||
url = self._get_client_action_url(wo.id)
|
||||
|
||||
self.start_tour(url, 'test_add_step', login='admin', timeout=80)
|
||||
# activities = production.bom_id.activity_ids
|
||||
# self.assertEqual(len(activities), 2, 'should be 2 activities')
|
||||
# activity = activities[0]
|
||||
# self.assertEqual(activity.summary, 'BoM feedback Instructions "Cutting Machine" (%s)' % production.name)
|
||||
# self.assertEqual(activity.note, Markup('<span><b>New Step suggested by Mitchell Admin</b><br><b>Reason:</b>why am I adding a step</span>'))
|
||||
# activity = activities[1]
|
||||
# self.assertEqual(activity.summary, 'BoM feedback Register Consumed Materials "Metal cylinder" (%s)' % production.name)
|
||||
# self.assertEqual(activity.note, Markup('<span><b>New Instruction suggested by Mitchell Admin</b><br>False<br><b>Reason: my reason</b></span>'))
|
||||
1536
mrp_workorder/tests/test_workorder.py
Normal file
1536
mrp_workorder/tests/test_workorder.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user