diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-04-12 13:37:44 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-04-12 13:37:44 +0200 |
| commit | 0a09a4d8a819ef06db47e798f0465e500004e4c1 (patch) | |
| tree | b9d66d60a1e9b5ff6030be62fff89df410b744ee /addons/mrp_subcontracting/tests | |
| parent | db97bb7f1136dc3ebde9924b79364a698a63765d (diff) | |
| parent | 120359bf0c98191051ac19ae2e5fde53ca7d2ae5 (diff) | |
[MERGE] forward port branch saas-12.3 up to 120359bf0c9
Diffstat (limited to 'addons/mrp_subcontracting/tests')
| -rw-r--r-- | addons/mrp_subcontracting/tests/__init__.py | 4 | ||||
| -rw-r--r-- | addons/mrp_subcontracting/tests/test_subcontracting.py | 525 |
2 files changed, 529 insertions, 0 deletions
diff --git a/addons/mrp_subcontracting/tests/__init__.py b/addons/mrp_subcontracting/tests/__init__.py new file mode 100644 index 00000000000..c8d5649e39d --- /dev/null +++ b/addons/mrp_subcontracting/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import test_subcontracting + diff --git a/addons/mrp_subcontracting/tests/test_subcontracting.py b/addons/mrp_subcontracting/tests/test_subcontracting.py new file mode 100644 index 00000000000..4ed43c5946d --- /dev/null +++ b/addons/mrp_subcontracting/tests/test_subcontracting.py @@ -0,0 +1,525 @@ +# -*- 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 + +from odoo.tests import tagged + +@tagged('post_install', '-at_install') +class TestSubcontractingBasic(TransactionCase): + def test_partner_1(self): + """ Checks the `display_name` of subcontracted partners. """ + main_partner = self.env['res.partner'].create({'name': 'main_partner'}) + + # When a name is set to the subcontracted partner. + subcontractor_partner1 = self.env['res.partner'].create({ + 'name': 'subcontractor_partner', + 'type': 'subcontractor', + 'parent_id': main_partner.id, + }) + self.assertEqual(subcontractor_partner1.display_name, "main_partner, subcontractor_partner, Subcontractor") + + # When a name is not set to the subcontracted partner. + subcontractor_partner1 = self.env['res.partner'].create({ + 'name': '', + 'type': 'subcontractor', + 'parent_id': main_partner.id, + }) + self.assertEqual(subcontractor_partner1.display_name, "main_partner, Subcontractor") + + def test_partner_2(self): + """ Open an existing partner and add a contact of type subcontractor (simple form view). + Checks the customer and supplier locations are set to the subcontracting one onces saved. + """ + that_company = self.env['res.partner'].create({ + 'is_company': True, + 'name': "That Company", + }) + partner_form = Form(that_company) + with partner_form.child_ids.new() as partner: + partner.name = 'That Guy' + partner.type = 'subcontractor' + partner_form.save() + subcontractor = that_company.child_ids + self.assertEquals(subcontractor.property_stock_supplier, self.env.user.company_id.subcontracting_location_id) + self.assertEquals(subcontractor.property_stock_customer, self.env.user.company_id.subcontracting_location_id) + + def test_partner_3(self): + """ Create a new partner, select a parent company, and choose type subcontractor (debug + active). Checks the onchange correctly set the customer and supplier locations to the + subcontracting ones. Change them to another one, save, checks they are rightly applied. + """ + # FIXME sle: this field should not be writeable without the debug mode but it is. ask xmo + group_no_one = self.env.ref('base.group_no_one') + self.env.user.write({'groups_id': [(3, group_no_one.id)]}) + partner_form = Form(self.env['res.partner']) + partner_form.name = 'subcontractor partner' + self.assertNotEqual(partner_form.property_stock_supplier, self.env.user.company_id.subcontracting_location_id) + self.assertNotEqual(partner_form.property_stock_customer, self.env.user.company_id.subcontracting_location_id) + partner_form.type = 'subcontractor' + self.assertEqual(partner_form.property_stock_supplier, self.env.user.company_id.subcontracting_location_id) + self.assertEqual(partner_form.property_stock_customer, self.env.user.company_id.subcontracting_location_id) + subcontracting_location2 = self.env.user.company_id.subcontracting_location_id.copy() + partner_form.property_stock_supplier = subcontracting_location2 + partner_form.property_stock_customer = subcontracting_location2 + partner = partner_form.save() + self.assertEqual(partner.property_stock_supplier, subcontracting_location2) + self.assertEqual(partner.property_stock_customer, subcontracting_location2) + + def test_partner_4(self): + """ Create a new partner, select a parent company, and choose type subcontractor (debug + not active). Checks the customer and supplier locations are set to the subcontracting one + onces saved. + """ + that_company = self.env['res.partner'].create({ + 'is_company': True, + 'name': "That Company", + }) + partner_form = Form(self.env['res.partner']) + partner_form.name = 'That Guy' + partner_form.parent_id = that_company + partner_form.type = 'subcontractor' + subcontractor = partner_form.save() + self.assertEquals(subcontractor.property_stock_supplier, self.env.user.company_id.subcontracting_location_id) + self.assertEquals(subcontractor.property_stock_customer, self.env.user.company_id.subcontracting_location_id) + + def test_partner_5(self): + """ Create a new partner with a child partner of type contact, + then write the type 'subcontractor' on it + """ + that_company = self.env['res.partner'].create({ + 'is_company': True, + 'name': "That Company", + }) + partner_form = Form(that_company) + with partner_form.child_ids.new() as partner: + partner.name = 'That Guy' + partner.type = 'contact' + partner_form.save() + + # We write a new type on the created child partner + subcontractor = that_company.child_ids + self.assertEquals(len(that_company.child_ids), 1) + subcontractor.type = 'subcontractor' + + self.assertEquals(subcontractor.property_stock_supplier, self.env.user.company_id.subcontracting_location_id) + self.assertEquals(subcontractor.property_stock_customer, self.env.user.company_id.subcontracting_location_id) + + def test_subcontracting_location_1(self): + """ Checks the creation and presence of the subcontracting location. """ + self.assertTrue(self.env.user.company_id.subcontracting_location_id) + self.assertTrue(self.env.user.company_id.subcontracting_location_id.active) + company2 = self.env['res.company'].create({'name': 'Test Company'}) + self.assertTrue(company2.subcontracting_location_id) + + +class TestSubcontractingFlows(TransactionCase): + def setUp(self): + super(TestSubcontractingFlows, self).setUp() + # 1: Create a subcontracting partner + main_partner = self.env['res.partner'].create({'name': 'main_partner'}) + self.subcontractor_partner1 = self.env['res.partner'].create({ + 'name': 'subcontractor_partner', + 'type': 'subcontractor', + 'parent_id': main_partner.id, + }) + + # 2. Create a BOM of subcontracting type + self.comp1 = self.env['product.product'].create({ + 'name': 'Component1', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + self.comp2 = self.env['product.product'].create({ + 'name': 'Component2', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + self.finished = self.env['product.product'].create({ + 'name': 'finished', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + bom_form = Form(self.env['mrp.bom']) + bom_form.type = 'subcontract' + bom_form.product_tmpl_id = self.finished.product_tmpl_id + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp1 + bom_line.product_qty = 1 + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp2 + bom_line.product_qty = 1 + self.bom = bom_form.save() + + # Create a BoM for self.comp2 + self.comp2comp = self.env['product.product'].create({ + 'name': 'component for Component2', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + bom_form = Form(self.env['mrp.bom']) + bom_form.product_tmpl_id = self.comp2.product_tmpl_id + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp2comp + bom_line.product_qty = 1 + self.comp2_bom = bom_form.save() + + def test_flow_1(self): + """ Don't tick any route on the components and trigger the creation of the subcontracting + manufacturing order through a receipt picking. Create a reordering rule in the + subcontracting locations for a component and run the scheduler to resupply. Checks if the + resupplying actually works + """ + # Create a receipt picking from the subcontractor + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished + move.product_uom_qty = 1 + picking_receipt = picking_form.save() + picking_receipt.action_confirm() + + # Nothing should be tracked + self.assertFalse(picking_receipt.display_action_record_components) + self.assertFalse(picking_receipt.display_view_subcontracted_move_lines) + + # Check the created manufacturing order + mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]) + self.assertEqual(len(mo), 1) + self.assertEqual(len(mo.picking_ids), 0) + wh = picking_receipt.picking_type_id.warehouse_id + self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id) + self.assertFalse(mo.picking_type_id.active) + + # Create a RR + pg1 = self.env['procurement.group'].create({}) + self.env['stock.warehouse.orderpoint'].create({ + 'name': 'xxx', + 'product_id': self.comp1.id, + 'product_min_qty': 0, + 'product_max_qty': 0, + 'location_id': self.env.user.company_id.subcontracting_location_id.id, + 'group_id': pg1.id, + }) + + # Run the scheduler and check the created picking + self.env['procurement.group'].run_scheduler() + picking = self.env['stock.picking'].search([('group_id', '=', pg1.id)]) + self.assertEqual(len(picking), 1) + self.assertEquals(picking.picking_type_id, wh.out_type_id) + picking_receipt.move_lines.quantity_done = 1 + picking_receipt.button_validate() + self.assertEquals(mo.state, 'done') + + # Now that the picking is done, the details stat button should be visible + self.assertTrue(picking_receipt.display_view_subcontracted_move_lines) + + # Available quantities should be negative at the subcontracting location for each components + avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished, wh.lot_stock_id) + self.assertEquals(avail_qty_comp1, -1) + self.assertEquals(avail_qty_comp2, -1) + self.assertEquals(avail_qty_finished, 1) + + def test_flow_2(self): + """ Tick "Resupply Subcontractor on Order" on the components and trigger the creation of + the subcontracting manufacturing order through a receipt picking. Checks if the resupplying + actually works. + """ + # Tick "resupply subconractor on order" + resupply_sub_on_order_route = self.env['stock.location.route'].search([('name', '=', 'Resupply Subcontractor on Order')]) + (self.comp1 + self.comp2).write({'route_ids': [(4, resupply_sub_on_order_route.id, None)]}) + + # Create a receipt picking from the subcontractor + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished + move.product_uom_qty = 1 + picking_receipt = picking_form.save() + picking_receipt.action_confirm() + + # Nothing should be tracked + self.assertFalse(picking_receipt.display_action_record_components) + self.assertFalse(picking_receipt.display_view_subcontracted_move_lines) + + # Pickings should directly be created + mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]) + self.assertEqual(len(mo.picking_ids), 1) + self.assertEquals(mo.state, 'to_close') + self.assertEqual(len(mo.picking_ids.move_lines), 2) + + picking = mo.picking_ids + wh = picking.picking_type_id.warehouse_id + + # The picking should be a delivery order + self.assertEquals(picking.picking_type_id, wh.out_type_id) + + self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id) + self.assertFalse(mo.picking_type_id.active) + + # No manufacturing order for `self.comp2` + comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)]) + self.assertEqual(len(comp2mo), 0) + + picking_receipt.move_lines.quantity_done = 1 + picking_receipt.button_validate() + self.assertEquals(mo.state, 'done') + + # Now that the picking is done, the details stat button should be visible + self.assertTrue(picking_receipt.display_view_subcontracted_move_lines) + + # Available quantities should be negative at the subcontracting location for each components + avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished, wh.lot_stock_id) + self.assertEquals(avail_qty_comp1, -1) + self.assertEquals(avail_qty_comp2, -1) + self.assertEquals(avail_qty_finished, 1) + + def test_flow_3(self): + """ Tick "Resupply Subcontractor on Order" and "MTO" on the components and trigger the + creation of the subcontracting manufacturing order through a receipt picking. Checks if the + resupplying actually works. One of the component has also "manufacture" set and a BOM + linked. Checks that an MO is created for this one. + """ + # Tick "resupply subconractor on order" + resupply_sub_on_order_route = self.env['stock.location.route'].search([('name', '=', 'Resupply Subcontractor on Order')]) + (self.comp1 + self.comp2).write({'route_ids': [(4, resupply_sub_on_order_route.id, None)]}) + + # Tick "manufacture" and MTO on self.comp2 + mto_route = self.env['stock.location.route'].search([('name', '=', 'Replenish on Order (MTO)')]) + manufacture_route = self.env['stock.location.route'].search([('name', '=', 'Manufacture')]) + self.comp2.write({'route_ids': [(4, manufacture_route.id, None)]}) + self.comp2.write({'route_ids': [(4, mto_route.id, None)]}) + + # Create a receipt picking from the subcontractor + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished + move.product_uom_qty = 1 + picking_receipt = picking_form.save() + picking_receipt.action_confirm() + + # Nothing should be tracked + self.assertFalse(picking_receipt.display_action_record_components) + self.assertFalse(picking_receipt.display_view_subcontracted_move_lines) + + # Pickings should directly be created + mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]) + self.assertEqual(len(mo.picking_ids), 1) + self.assertEquals(mo.state, 'to_close') + self.assertEqual(len(mo.picking_ids.move_lines), 2) + + # The picking should be a delivery order + wh = picking_receipt.picking_type_id.warehouse_id + self.assertEquals(mo.picking_ids.picking_type_id, wh.out_type_id) + + self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id) + self.assertFalse(mo.picking_type_id.active) + + # As well as a manufacturing order for `self.comp2` + comp2mo = self.env['mrp.production'].search([('bom_id', '=', self.comp2_bom.id)]) + self.assertEqual(len(comp2mo), 1) + picking_receipt.move_lines.quantity_done = 1 + picking_receipt.button_validate() + self.assertEquals(mo.state, 'done') + + # Now that the picking is done, the details stat button should be visible + self.assertTrue(picking_receipt.display_view_subcontracted_move_lines) + + # Available quantities should be negative at the subcontracting location for each components + avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished, wh.lot_stock_id) + self.assertEquals(avail_qty_comp1, -1) + self.assertEquals(avail_qty_comp2, -1) + self.assertEquals(avail_qty_finished, 1) + + def test_flow_4(self): + """ Check that the correct BoM is chosen accordingly to the partner + """ + # We create a second partner of type subcontractor + main_partner_2 = self.env['res.partner'].create({'name': 'main_partner'}) + subcontractor_partner2 = self.env['res.partner'].create({ + 'name': 'subcontractor_partner', + 'type': 'subcontractor', + 'parent_id': main_partner_2.id, + }) + + # We create a different BoM for the same product + comp3 = self.env['product.product'].create({ + 'name': 'Component1', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + + bom_form = Form(self.env['mrp.bom']) + bom_form.type = 'subcontract' + bom_form.product_tmpl_id = self.finished.product_tmpl_id + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp1 + bom_line.product_qty = 1 + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = comp3 + bom_line.product_qty = 1 + bom2 = bom_form.save() + + # We assign the second BoM to the new partner + self.bom.write({'subcontractor_ids': [(4, self.subcontractor_partner1.id, None)]}) + bom2.write({'subcontractor_ids': [(4, subcontractor_partner2.id, None)]}) + + # Create a receipt picking from the subcontractor1 + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished + move.product_uom_qty = 1 + picking_receipt1 = picking_form.save() + picking_receipt1.action_confirm() + + # Create a receipt picking from the subcontractor2 + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = subcontractor_partner2 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished + move.product_uom_qty = 1 + picking_receipt2 = picking_form.save() + picking_receipt2.action_confirm() + + mo_pick1 = picking_receipt1.move_lines.mapped('move_orig_ids.production_id') + mo_pick2 = picking_receipt2.move_lines.mapped('move_orig_ids.production_id') + self.assertEquals(len(mo_pick1), 1) + self.assertEquals(len(mo_pick2), 1) + self.assertEquals(mo_pick1.bom_id, self.bom) + self.assertEquals(mo_pick2.bom_id, bom2) + + +class TestSubcontractingTracking(TransactionCase): + def setUp(self): + super(TestSubcontractingTracking, self).setUp() + # 1: Create a subcontracting partner + main_company_1 = self.env['res.partner'].create({'name': 'main_partner'}) + self.subcontractor_partner1 = self.env['res.partner'].create({ + 'name': 'Subcontractor 1', + 'type': 'subcontractor', + 'parent_id': main_company_1.id, + }) + + # 2. Create a BOM of subcontracting type + # 2.1. Comp1 has tracking by lot + self.comp1_sn = self.env['product.product'].create({ + 'name': 'Component1', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + 'tracking': 'serial' + }) + self.comp2 = self.env['product.product'].create({ + 'name': 'Component2', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + }) + + # 2.2. Finished prodcut has tracking by serial number + self.finished_lot = self.env['product.product'].create({ + 'name': 'finished', + 'type': 'product', + 'categ_id': self.env.ref('product.product_category_all').id, + 'tracking': 'lot' + }) + bom_form = Form(self.env['mrp.bom']) + bom_form.type = 'subcontract' + bom_form.product_tmpl_id = self.finished_lot.product_tmpl_id + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp1_sn + bom_line.product_qty = 1 + with bom_form.bom_line_ids.new() as bom_line: + bom_line.product_id = self.comp2 + bom_line.product_qty = 1 + self.bom_tracked = bom_form.save() + + def test_flow_tracked_1(self): + """ This test mimics test_flow_1 but with a BoM that has tracking included in it. + """ + # Create a receipt picking from the subcontractor + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_in') + picking_form.partner_id = self.subcontractor_partner1 + with picking_form.move_ids_without_package.new() as move: + move.product_id = self.finished_lot + move.product_uom_qty = 1 + picking_receipt = picking_form.save() + picking_receipt.action_confirm() + + # We should be able to call the 'record_components' button + self.assertTrue(picking_receipt.display_action_record_components) + self.assertFalse(picking_receipt.display_view_subcontracted_move_lines) + + # Check the created manufacturing order + mo = self.env['mrp.production'].search([('bom_id', '=', self.bom_tracked.id)]) + self.assertEqual(len(mo), 1) + self.assertEquals(mo.state, 'confirmed') + self.assertEqual(len(mo.picking_ids), 0) + wh = picking_receipt.picking_type_id.warehouse_id + self.assertEquals(mo.picking_type_id, wh.subcontracting_type_id) + self.assertFalse(mo.picking_type_id.active) + + # Create a RR + pg1 = self.env['procurement.group'].create({}) + self.env['stock.warehouse.orderpoint'].create({ + 'name': 'xxx', + 'product_id': self.comp1_sn.id, + 'product_min_qty': 0, + 'product_max_qty': 0, + 'location_id': self.env.user.company_id.subcontracting_location_id.id, + 'group_id': pg1.id, + }) + + # Run the scheduler and check the created picking + self.env['procurement.group'].run_scheduler() + picking = self.env['stock.picking'].search([('group_id', '=', pg1.id)]) + self.assertEqual(len(picking), 1) + self.assertEquals(picking.picking_type_id, wh.out_type_id) + + lot_id = self.env['stock.production.lot'].create({ + 'name': 'lot1', + 'product_id': self.finished_lot.id + }) + serial_id = self.env['stock.production.lot'].create({ + 'name': 'lot1', + 'product_id': self.comp1_sn.id + }) + produce_form = Form(self.env['mrp.product.produce'].with_context({ + 'active_id': mo.id, + 'active_ids': [mo.id], + })) + produce_form.final_lot_id = lot_id + produce_form.workorder_line_ids._records[0]['lot_id'] = serial_id.id + wiz_produce = produce_form.save() + wiz_produce.do_produce() + + # We should not be able to call the 'record_components' button + self.assertFalse(picking_receipt.display_action_record_components) + # We should see the move lines stat button + self.assertTrue(picking_receipt.display_view_subcontracted_move_lines) + + picking_receipt.move_lines.quantity_done = 1 + picking_receipt.move_lines.move_line_ids.lot_name = 'lot00001test' + picking_receipt.button_validate() + self.assertEquals(mo.state, 'done') + + # Available quantities should be negative at the subcontracting location for each components + avail_qty_comp1 = self.env['stock.quant']._get_available_quantity(self.comp1_sn, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_comp2 = self.env['stock.quant']._get_available_quantity(self.comp2, self.subcontractor_partner1.property_stock_supplier, allow_negative=True) + avail_qty_finished = self.env['stock.quant']._get_available_quantity(self.finished_lot, wh.lot_stock_id) + self.assertEquals(avail_qty_comp1, -1) + self.assertEquals(avail_qty_comp2, -1) + self.assertEquals(avail_qty_finished, 1) |
