From 53fe7e9fa60c287f4bf8c6d4b70a054038bb8aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 29 Jun 2016 11:33:59 +0200 Subject: [PATCH] [TESTS] mrp and various: updating tests --- addons/mrp/tests/__init__.py | 5 +- addons/mrp/tests/common.py | 85 ++++- addons/mrp/tests/test_bom.py | 147 ++++++-- addons/mrp/tests/test_order.py | 336 ++++++++++++++++- addons/mrp/tests/test_order_process.py | 140 -------- addons/mrp/tests/test_procurement.py | 107 ++++++ addons/mrp/tests/test_stock.py | 101 ++++++ addons/mrp/tests/test_workorder_operation.py | 337 ++++++++++-------- .../mrp_byproduct/tests/test_mrp_byproduct.py | 89 +++-- addons/product/tests/common.py | 101 ++++-- addons/sale_mrp/tests/__init__.py | 2 +- addons/sale_mrp/tests/test_move_explode.py | 21 -- addons/sale_mrp/tests/test_sale_mrp_flow.py | 3 +- addons/stock/tests/common2.py | 3 +- addons/stock/tests/test_warehouse.py | 4 +- 15 files changed, 1046 insertions(+), 435 deletions(-) delete mode 100644 addons/mrp/tests/test_order_process.py create mode 100644 addons/mrp/tests/test_procurement.py create mode 100644 addons/mrp/tests/test_stock.py diff --git a/addons/mrp/tests/__init__.py b/addons/mrp/tests/__init__.py index 44dc0c6b4ce..dfa765c7f49 100644 --- a/addons/mrp/tests/__init__.py +++ b/addons/mrp/tests/__init__.py @@ -2,5 +2,6 @@ import test_bom import test_order -import test_order_process -import test_workorder_operation \ No newline at end of file +import test_stock +import test_workorder_operation +import test_procurement \ No newline at end of file diff --git a/addons/mrp/tests/common.py b/addons/mrp/tests/common.py index 90869b123e5..5f2fc1e2451 100644 --- a/addons/mrp/tests/common.py +++ b/addons/mrp/tests/common.py @@ -13,7 +13,12 @@ class TestMrpCommon(common2.TestStockCommon): user_group_mrp_user = cls.env.ref('mrp.group_mrp_user') user_group_mrp_manager = cls.env.ref('mrp.group_mrp_manager') - # User Data: stock user and stock manager + # Update demo products + (cls.product_2 | cls.product_3 | cls.product_4 | cls.product_5 | cls.product_6 | cls.product_7 | cls.product_8).write({ + 'type': 'product', + }) + + # User Data: mrp user and mrp manager Users = cls.env['res.users'].with_context({'no_reset_password': True, 'mail_create_nosubscribe': True}) cls.user_mrp_user = Users.create({ 'name': 'Hilda Ferachwal', @@ -28,21 +33,73 @@ class TestMrpCommon(common2.TestStockCommon): 'notify_email': 'none', 'groups_id': [(6, 0, [user_group_mrp_manager.id])]}) + cls.workcenter_1 = cls.env['mrp.workcenter'].create({ + 'name': 'Nuclear Workcenter', + 'capacity': 2, + 'time_start': 10, + 'time_stop': 5, + 'time_efficiency': 80, + }) + cls.routing_1 = cls.env['mrp.routing'].create({ + 'name': 'Simple Line', + }) + cls.routing_2 = cls.env['mrp.routing'].create({ + 'name': 'Complicated Line', + }) + cls.operation_1 = cls.env['mrp.routing.workcenter'].create({ + 'name': 'Gift Wrap Maching', + 'workcenter_id': cls.workcenter_1.id, + 'routing_id': cls.routing_1.id, + 'time_cycle': 15, + 'sequence': 1, + }) + cls.operation_2 = cls.env['mrp.routing.workcenter'].create({ + 'name': 'Cutting Machine', + 'workcenter_id': cls.workcenter_1.id, + 'routing_id': cls.routing_2.id, + 'time_cycle': 12, + 'sequence': 1, + }) + cls.operation_3 = cls.env['mrp.routing.workcenter'].create({ + 'name': 'Weld Machine', + 'workcenter_id': cls.workcenter_1.id, + 'routing_id': cls.routing_2.id, + 'time_cycle': 18, + 'sequence': 2, + }) + cls.bom_1 = 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, + 'product_id': cls.product_4.id, + 'product_tmpl_id': cls.product_4.product_tmpl_id.id, + 'product_uom_id': cls.uom_unit.id, + 'product_qty': 4.0, + 'routing_id': cls.routing_2.id, 'type': 'normal', 'bom_line_ids': [ - (0, 0, {'product_id': cls.product_2.id}), - (0, 0, {'product_id': cls.product_3.id}) + (0, 0, {'product_id': cls.product_2.id, 'product_qty': 2}), + (0, 0, {'product_id': cls.product_1.id, 'product_qty': 4}) ]}) - - cls.production_1 = cls.env['mrp.production'].create({ - 'name': 'MO-Test', - 'product_id': cls.product_1.id, + cls.bom_2 = cls.env['mrp.bom'].create({ + 'product_id': cls.product_5.id, + 'product_tmpl_id': cls.product_5.product_tmpl_id.id, + 'product_uom_id': cls.product_5.uom_id.id, 'product_qty': 1.0, - 'bom_id': cls.bom_1.id, - 'product_uom_id': cls.product_1.uom_id.id, - }) + 'routing_id': cls.routing_1.id, + 'type': 'phantom', + 'sequence': 2, + 'bom_line_ids': [ + (0, 0, {'product_id': cls.product_4.id, 'product_qty': 2}), + (0, 0, {'product_id': cls.product_3.id, 'product_qty': 3}) + ]}) + cls.bom_3 = cls.env['mrp.bom'].create({ + 'product_id': cls.product_6.id, + 'product_tmpl_id': cls.product_6.product_tmpl_id.id, + 'product_uom_id': cls.uom_dozen.id, + 'product_qty': 2.0, + 'routing_id': cls.routing_2.id, + 'type': 'normal', + 'bom_line_ids': [ + (0, 0, {'product_id': cls.product_5.id, 'product_qty': 2}), + (0, 0, {'product_id': cls.product_4.id, 'product_qty': 8}), + (0, 0, {'product_id': cls.product_2.id, 'product_qty': 12}) + ]}) diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py index 7f9d30f9133..4d54f23df0a 100644 --- a/addons/mrp/tests/test_bom.py +++ b/addons/mrp/tests/test_bom.py @@ -1,34 +1,137 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import exceptions from odoo.addons.mrp.tests.common import TestMrpCommon class TestBoM(TestMrpCommon): - def test_basic(self): - # make the production order - production = self.production_1 + def test_explode(self): + boms, lines = self.bom_1.explode(self.product_4, 3) + self.assertEqual(set([bom[0].id for bom in boms]), set(self.bom_1.ids)) + self.assertEqual(set([line[0].id for line in lines]), set(self.bom_1.bom_line_ids.ids)) - # # compute production order data - # production.action_compute() + boms, lines = self.bom_3.explode(self.product_6, 3) + self.assertEqual(set([bom[0].id for bom in boms]), set((self.bom_2 | self.bom_3).ids)) + self.assertEqual( + set([line[0].id for line in lines]), + set((self.bom_2 | self.bom_3).mapped('bom_line_ids').filtered(lambda line: not line.child_bom_id or line.child_bom_id.type != 'phantom').ids)) - # # confirm production - # production.signal_workflow('button_confirm') - self.assertEqual(production.state, 'confirmed') - - # reserve product - # production.force_production() - - # produce product - produce_wizard = self.env['mrp.product.produce'].with_context({ - 'active_id': production.id, - 'active_ids': [production.id], - }).create({ - 'product_qty': 1.0, + def test_variants(self): + test_bom = self.env['mrp.bom'].create({ + 'product_id': self.product_7.id, + 'product_tmpl_id': self.product_7.product_tmpl_id.id, + 'product_uom_id': self.uom_unit.id, + 'product_qty': 4.0, + 'routing_id': self.routing_2.id, + 'type': 'normal', }) - # produce_wizard.on_change_qty() - produce_wizard.do_produce() + test_bom_l1 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom.id, + 'product_id': self.product_2.id, + 'product_qty': 2, + }) + test_bom_l2 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom.id, + 'product_id': self.product_3.id, + 'product_qty': 2, + 'attribute_value_ids': [(4, self.prod_attr1_v1.id)], + }) + test_bom_l3 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom.id, + 'product_id': self.product_4.id, + 'product_qty': 2, + 'attribute_value_ids': [(4, self.prod_attr1_v2.id)], + }) + boms, lines = test_bom.explode(self.product_7, 4) + self.assertIn(test_bom, [b[0]for b in boms]) + self.assertIn(test_bom_l1, [l[0] for l in lines]) + self.assertNotIn(test_bom_l2, [l[0] for l in lines]) + self.assertNotIn(test_bom_l3, [l[0] for l in lines]) - # check production - # self.assertEqual(production.state, 'done') + boms, lines = test_bom.explode(self.product_7_1, 4) + self.assertIn(test_bom, [b[0]for b in boms]) + self.assertIn(test_bom_l1, [l[0] for l in lines]) + self.assertIn(test_bom_l2, [l[0] for l in lines]) + self.assertNotIn(test_bom_l3, [l[0] for l in lines]) + + boms, lines = test_bom.explode(self.product_7_2, 4) + self.assertIn(test_bom, [b[0]for b in boms]) + self.assertIn(test_bom_l1, [l[0] for l in lines]) + self.assertNotIn(test_bom_l2, [l[0] for l in lines]) + self.assertIn(test_bom_l3, [l[0] for l in lines]) + + def test_multi_level_variants(self): + tmp_picking_type = self.env['stock.picking.type'].create({ + 'name': 'Manufacturing', + 'code': 'mrp_operation', + 'sequence_id': self.env['ir.sequence'].search([('code', '=', 'mrp.production')], limit=1).id, + }) + test_bom_1 = self.env['mrp.bom'].create({ + 'product_tmpl_id': self.product_5.product_tmpl_id.id, + 'product_uom_id': self.product_5.uom_id.id, + 'product_qty': 1.0, + 'routing_id': self.routing_1.id, + 'type': 'phantom' + }) + test_bom_1_l1 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom_1.id, + 'product_id': self.product_3.id, + 'product_qty': 3, + }) + + test_bom_2 = self.env['mrp.bom'].create({ + 'product_id': self.product_7.id, + 'product_tmpl_id': self.product_7.product_tmpl_id.id, + 'product_uom_id': self.uom_unit.id, + 'product_qty': 4.0, + 'routing_id': self.routing_2.id, + 'type': 'normal', + }) + test_bom_2_l1 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom_2.id, + 'product_id': self.product_2.id, + 'product_qty': 2, + }) + test_bom_2_l2 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom_2.id, + 'product_id': self.product_5.id, + 'product_qty': 2, + 'attribute_value_ids': [(4, self.prod_attr1_v1.id)], + }) + test_bom_2_l3 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom_2.id, + 'product_id': self.product_5.id, + 'product_qty': 2, + 'attribute_value_ids': [(4, self.prod_attr1_v2.id)], + }) + test_bom_2_l4 = self.env['mrp.bom.line'].create({ + 'bom_id': test_bom_2.id, + 'product_id': self.product_4.id, + 'product_qty': 2, + }) + + # check product > product_tmpl + boms, lines = test_bom_2.explode(self.product_7_1, 4) + self.assertEqual(set((test_bom_2 | self.bom_2).ids), set([b[0].id for b in boms])) + self.assertEqual(set((test_bom_2_l1 | test_bom_2_l4 | self.bom_2.bom_line_ids).ids), set([l[0].id for l in lines])) + + # check sequence priority + test_bom_1.write({'sequence': 1}) + boms, lines = test_bom_2.explode(self.product_7_1, 4) + self.assertEqual(set((test_bom_2 | test_bom_1).ids), set([b[0].id for b in boms])) + self.assertEqual(set((test_bom_2_l1 | test_bom_2_l4 | test_bom_1.bom_line_ids).ids), set([l[0].id for l in lines])) + + # check with another picking_type + test_bom_1.write({'picking_type_id': self.warehouse_1.manu_type_id.id}) + self.bom_2.write({'picking_type_id': tmp_picking_type.id}) + test_bom_2.write({'picking_type_id': tmp_picking_type.id}) + boms, lines = test_bom_2.explode(self.product_7_1, 4) + self.assertEqual(set((test_bom_2 | self.bom_2).ids), set([b[0].id for b in boms])) + self.assertEqual(set((test_bom_2_l1 | test_bom_2_l4 | self.bom_2.bom_line_ids).ids), set([l[0].id for l in lines])) + + # check recursion + test_bom_2_l3.write({'attribute_value_ids': [(6, 0, [self.prod_attr1_v1.id])]}) + with self.assertRaises(exceptions.UserError): + test_bom_2.explode(self.product_7_1, 4) diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 5b534e5d560..172078ae656 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -1,27 +1,333 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from datetime import datetime, timedelta + +from odoo.fields import Datetime as Dt from odoo.addons.mrp.tests.common import TestMrpCommon class TestMrpOrder(TestMrpCommon): def test_access_rights_manager(self): - production = self.production_1.sudo(self.user_mrp_manager) - # production.action_compute() - # production.signal_workflow('button_confirm') - production.action_cancel() - self.assertEqual(production.state, 'cancel') - production.unlink() + man_order = self.env['mrp.production'].sudo(self.user_mrp_manager).create({ + 'name': 'Stick-0', + 'product_id': self.product_4.id, + 'product_uom_id': self.product_4.uom_id.id, + 'product_qty': 5.0, + 'bom_id': self.bom_1.id, + 'location_src_id': self.location_1.id, + 'location_dest_id': self.warehouse_1.wh_output_stock_loc_id.id, + }) + man_order.action_cancel() + self.assertEqual(man_order.state, 'cancel', "Production order should be in cancel state.") + man_order.unlink() def test_access_rights_user(self): - production = self.production_1.sudo(self.user_mrp_user) - # production.action_compute() - # production.signal_workflow('button_confirm') - production.action_cancel() - self.assertEqual(production.state, 'cancel') - production.unlink() + man_order = self.env['mrp.production'].sudo(self.user_mrp_user).create({ + 'name': 'Stick-0', + 'product_id': self.product_4.id, + 'product_uom_id': self.product_4.uom_id.id, + 'product_qty': 5.0, + 'bom_id': self.bom_1.id, + 'location_src_id': self.location_1.id, + 'location_dest_id': self.warehouse_1.wh_output_stock_loc_id.id, + }) + man_order.action_cancel() + self.assertEqual(man_order.state, 'cancel', "Production order should be in cancel state.") + man_order.unlink() - def test_flow(self): - production = self.production_1.sudo(self.user_mrp_user) - # production.action_compute() + def test_basic(self): + """ Basic order test: no routing (thus no workorders), no lot """ + inventory = self.env['stock.inventory'].create({ + 'name': 'Initial inventory', + 'filter': 'partial', + 'line_ids': [(0, 0, { + 'product_id': self.product_1.id, + 'product_uom_id': self.product_1.uom_id.id, + 'product_qty': 500, + 'location_id': self.warehouse_1.lot_stock_id.id + }), (0, 0, { + 'product_id': self.product_2.id, + 'product_uom_id': self.product_2.uom_id.id, + 'product_qty': 500, + 'location_id': self.warehouse_1.lot_stock_id.id + })] + }) + inventory.action_done() + + test_date_planned = datetime.now() - timedelta(days=1) + test_quantity = 2.0 + self.bom_1.routing_id = False + man_order = self.env['mrp.production'].sudo(self.user_mrp_user).create({ + 'name': 'Stick-0', + 'product_id': self.product_4.id, + 'product_uom_id': self.product_4.uom_id.id, + 'product_qty': test_quantity, + 'bom_id': self.bom_1.id, + 'date_planned_start': test_date_planned, + 'location_src_id': self.location_1.id, + 'location_dest_id': self.warehouse_1.wh_output_stock_loc_id.id, + }) + self.assertEqual(man_order.state, 'confirmed', "Production order should be in confirmed state.") + + # check production move + production_move = man_order.move_finished_ids + self.assertEqual(production_move.date, Dt.to_string(test_date_planned)) + self.assertEqual(production_move.product_id, self.product_4) + self.assertEqual(production_move.product_uom, man_order.product_uom_id) + self.assertEqual(production_move.product_qty, man_order.product_qty) + self.assertEqual(production_move.location_id, self.product_4.property_stock_production) + self.assertEqual(production_move.location_dest_id, man_order.location_dest_id) + + # check consumption moves + for move in man_order.move_raw_ids: + self.assertEqual(move.date, Dt.to_string(test_date_planned)) + first_move = man_order.move_raw_ids.filtered(lambda move: move.product_id == self.product_2) + self.assertEqual(first_move.product_qty, test_quantity / self.bom_1.product_qty * self.product_4.uom_id.factor_inv * 2) + first_move = man_order.move_raw_ids.filtered(lambda move: move.product_id == self.product_1) + self.assertEqual(first_move.product_qty, test_quantity / self.bom_1.product_qty * self.product_4.uom_id.factor_inv * 4) + + # waste some material, create a scrap + # scrap = self.env['stock.scrap'].with_context( + # active_model='mrp.production', active_id=man_order.id + # ).create({}) + # scrap = self.env['stock.scrap'].create({ + # 'production_id': man_order.id, + # 'product_id': first_move.product_id.id, + # 'product_uom_id': first_move.product_uom.id, + # 'scrap_qty': 5.0, + # }) + # check created scrap + + + # procurements = self.env['procurement.order'].search([('move_dest_id', 'in', man_order.move_raw_ids.ids)]) + # print procurements + # procurements = self.env['procurement.order'].search([('production_id', '=', man_order.id)]) + # print procurements + # for proc in self.env['procurement.order'].browse(procurements): + # date_planned = self.mrp_production_test1.date_planned + # if proc.product_id.type not in ('product', 'consu'): + # continue + # if proc.product_id.id == order_line.product_id.id: + # self.assertEqual(proc.date_planned, date_planned, "Planned date does not correspond") + # # procurement state should be `confirmed` at this stage, except if procurement_jit is installed, in which + # # case it could already be in `running` or `exception` state (not enough stock) + # expected_states = ('confirmed', 'running', 'exception') + # self.assertEqual(proc.state in expected_states, 'Procurement state is `%s` for %s, expected one of %s' % (proc.state, proc.product_id.name, expected_states)) + + # Change production quantity + qty_wizard = self.env['change.production.qty'].create({ + 'mo_id': man_order.id, + 'product_qty': 3.0, + }) + # qty_wizard.change_prod_qty() + + # # I check qty after changed in production order. + # #self.assertEqual(self.mrp_production_test1.product_qty, 3, "Qty is not changed in order.") + # move = self.mrp_production_test1.move_finished_ids[0] + # self.assertEqual(move.product_qty, self.mrp_production_test1.product_qty, "Qty is not changed in move line.") + + # # I run scheduler. + # self.env['procurement.order'].run_scheduler() + + # # The production order is Waiting Goods, will force production which should set consume lines as available + # self.mrp_production_test1.button_plan() + # # I check that production order in ready state after forcing production. + + # #self.assertEqual(self.mrp_production_test1.availability, 'assigned', 'Production order availability should be set as available') + + # produce product + produce_wizard = self.env['mrp.product.produce'].sudo(self.user_mrp_user).with_context({ + 'active_id': man_order.id, + 'active_ids': [man_order.id], + }).create({ + 'product_qty': 1.0, + }) + produce_wizard.do_produce() + + # man_order.button_mark_done() + man_order.button_mark_done() + self.assertEqual(man_order.state, 'done', "Production order should be in done state.") + + def test_explode_from_order(self): + # + # bom3 produces 2 Dozen of Doors (p6), aka 24 + # To produce 24 Units of Doors (p6) + # - 2 Units of Tools (p5) -> need 4 + # - 8 Dozen of Sticks (p4) -> need 16 + # - 12 Units of Wood (p2) -> need 24 + # bom2 produces 1 Unit of Sticks (p4) + # To produce 1 Unit of Sticks (p4) + # - 2 Dozen of Sticks (p4) -> need 8 + # - 3 Dozen of Stones (p3) -> need 12 + + # Update capacity, start time, stop time, and time efficiency. + # ------------------------------------------------------------ + self.workcenter_1.write({'capacity': 1, 'time_start': 0, 'time_stop': 0, 'time_efficiency': 100}) + + # Set manual time cycle 20 and 10. + # -------------------------------- + self.operation_1.write({'time_cycle_manual': 20}) + (self.operation_2 | self.operation_3).write({'time_cycle_manual': 10}) + + man_order = self.env['mrp.production'].create({ + 'name': 'MO-Test', + 'product_id': self.product_6.id, + 'product_uom_id': self.product_6.uom_id.id, + 'product_qty': 48, + 'bom_id': self.bom_3.id, + }) + + # reset quantities + self.env['stock.change.product.qty'].create({ + 'product_id': self.product_1.id, + 'new_quantity': 0.0, + 'location_id': self.warehouse_1.lot_stock_id.id, + }).change_product_qty() + + (self.product_2 | self.product_4).write({ + 'tracking': 'lot', + }) + # assign consume material + man_order.action_assign() + self.assertEqual(man_order.availability, 'waiting', "Production order should be in waiting state.") + + # check consume materials of manufacturing order + self.assertEqual(len(man_order.move_raw_ids), 4, "Consume material lines are not generated proper.") + product_2_consume_moves = man_order.move_raw_ids.filtered(lambda x: x.product_id == self.product_2) + product_3_consume_moves = man_order.move_raw_ids.filtered(lambda x: x.product_id == self.product_3) + product_4_consume_moves = man_order.move_raw_ids.filtered(lambda x: x.product_id == self.product_4) + product_5_consume_moves = man_order.move_raw_ids.filtered(lambda x: x.product_id == self.product_5) + consume_qty_2 = product_2_consume_moves.product_uom_qty + self.assertEqual(consume_qty_2, 24.0, "Consume material quantity of Wood should be 24 instead of %s" % str(consume_qty_2)) + consume_qty_3 = product_3_consume_moves.product_uom_qty + self.assertEqual(consume_qty_3, 12.0, "Consume material quantity of Stone should be 12 instead of %s" % str(consume_qty_3)) + self.assertEqual(len(product_4_consume_moves), 2, "Consume move are not generated proper.") + for consume_moves in product_4_consume_moves: + consume_qty_4 = consume_moves.product_uom_qty + self.assertIn(consume_qty_4, [8.0, 16.0], "Consume material quantity of Stick should be 8 or 16 instead of %s" % str(consume_qty_4)) + self.assertFalse(product_5_consume_moves, "Move should not create for phantom bom") + + # create required lots + lot_product_2 = self.env['stock.production.lot'].create({'product_id': self.product_2.id}) + lot_product_4 = self.env['stock.production.lot'].create({'product_id': self.product_4.id}) + + # refuel stock + inventory = self.env['stock.inventory'].create({ + 'name': 'Inventory For Product C', + 'filter': 'partial', + 'line_ids': [(0, 0, { + 'product_id': self.product_2.id, + 'product_uom_id': self.product_2.uom_id.id, + 'product_qty': 30, + 'prod_lot_id': lot_product_2.id, + 'location_id': self.ref('stock.stock_location_14') + }), (0, 0, { + 'product_id': self.product_3.id, + 'product_uom_id': self.product_3.uom_id.id, + 'product_qty': 60, + 'location_id': self.ref('stock.stock_location_14') + }), (0, 0, { + 'product_id': self.product_4.id, + 'product_uom_id': self.product_4.uom_id.id, + 'product_qty': 60, + 'prod_lot_id': lot_product_4.id, + 'location_id': self.ref('stock.stock_location_14') + })] + }) + inventory.prepare_inventory() + inventory.action_done() + + # re-assign consume material + man_order.action_assign() + + # Check production order status after assign. + self.assertEqual(man_order.availability, 'assigned', "Production order should be in assigned state.") + # Plan production order. + man_order.button_plan() + + # check workorders + # - main bom: Door: 2 operations + # operation 1: Cutting + # operation 2: Welding, waiting for the previous one + # - kit bom: Stone Tool: 1 operation + # operation 1: Gift Wrapping + workorders = man_order.workorder_ids + kit_wo = man_order.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_1) + door_wo_1 = man_order.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_2) + door_wo_2 = man_order.workorder_ids.filtered(lambda wo: wo.operation_id == self.operation_3) + for workorder in workorders: + self.assertEqual(workorder.workcenter_id, self.workcenter_1, "Workcenter does not match.") + self.assertEqual(kit_wo.state, 'ready', "Workorder should be in ready state.") + self.assertEqual(door_wo_1.state, 'ready', "Workorder should be in ready state.") + self.assertEqual(door_wo_2.state, 'pending', "Workorder should be in pending state.") + self.assertEqual(kit_wo.duration_expected, 80, "Workorder duration should be 80 instead of %s." % str(kit_wo.duration_expected)) + self.assertEqual(door_wo_1.duration_expected, 20, "Workorder duration should be 20 instead of %s." % str(door_wo_1.duration_expected)) + self.assertEqual(door_wo_2.duration_expected, 20, "Workorder duration should be 20 instead of %s." % str(door_wo_2.duration_expected)) + + # subbom: kit for stone tools + kit_wo.button_start() + finished_lot = self.env['stock.production.lot'].create({'product_id': man_order.product_id.id}) + kit_wo.write({ + 'final_lot_id': finished_lot.id, + 'qty_producing': 48 + }) + kit_wo.record_production() + + self.assertEqual(kit_wo.state, 'done', "Workorder should be in done state.") + + # first operation of main bom + finished_lot = self.env['stock.production.lot'].create({'product_id': man_order.product_id.id}) + door_wo_1.write({ + 'final_lot_id': finished_lot.id, + 'qty_producing': 48 + }) + door_wo_1.record_production() + self.assertEqual(door_wo_1.state, 'done', "Workorder should be in done state.") + + # second operation of main bom + self.assertEqual(door_wo_2.state, 'ready', "Workorder should be in ready state.") + door_wo_2.record_production() + self.assertEqual(door_wo_2.state, 'done', "Workorder should be in done state.") + + def test_production_avialability(self): + """ + Test availability of production order. + """ + self.bom_3.bom_line_ids.filtered(lambda x: x.product_id == self.product_5).unlink() + self.bom_3.bom_line_ids.filtered(lambda x: x.product_id == self.product_4).unlink() + + production_2 = self.env['mrp.production'].create({ + 'name': 'MO-Test001', + 'product_id': self.product_6.id, + 'product_qty': 5.0, + 'bom_id': self.bom_3.id, + 'product_uom_id': self.product_6.uom_id.id, + }) + production_2.action_assign() + + # check sub product availability state is waiting + self.assertEqual(production_2.availability, 'waiting', 'Production order should be availability for waiting state') + + # Update Inventory + inventory_wizard = self.env['stock.change.product.qty'].create({ + 'product_id': self.product_2.id, + 'new_quantity': 2.0, + }) + inventory_wizard.change_product_qty() + + production_2.action_assign() + # check sub product availability state is partially available + self.assertEqual(production_2.availability, 'partially_available', 'Production order should be availability for partially available state') + + # Update Inventory + inventory_wizard = self.env['stock.change.product.qty'].create({ + 'product_id': self.product_2.id, + 'new_quantity': 5.0, + }) + inventory_wizard.change_product_qty() + + production_2.action_assign() + # check sub product availability state is assigned + self.assertEqual(production_2.availability, 'assigned', 'Production order should be availability for assigned state') diff --git a/addons/mrp/tests/test_order_process.py b/addons/mrp/tests/test_order_process.py deleted file mode 100644 index 5c7c95a849f..00000000000 --- a/addons/mrp/tests/test_order_process.py +++ /dev/null @@ -1,140 +0,0 @@ - -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from openerp.tests import common - - -class TestOrderProcess(common.TransactionCase): - - def test_00_order_process(self): - self.res_users_mrp_manager = self.env.ref('mrp.group_mrp_manager') - # self.group_account_user = self.env.ref('account.group_account_user') - self.main_company = self.env.ref('base.main_company') - self.group_mrp_user = self.env.ref('mrp.group_mrp_user') - - self.res_users_mrp_manager = self.env['res.users'].create({ - 'company_id': self.main_company.id, - 'name': 'MRP Manager', - 'login': 'mam', - 'password': 'mam', - 'email': 'mrp_manager@yourcompany.com', - 'groups_id': [(6, 0, [self.res_users_mrp_manager.id])] # self.group_account_user.id - }) - - # Create a user as 'MRP User' - # I added groups for MRP User. - self.res_users_mrp_user = self.env['res.users'].create({ - 'company_id': self.main_company.id, - 'name': 'MRP User', - 'login': 'mau', - 'password': 'mau', - 'email': 'mrp_user@yourcompany.com', - 'groups_id': [(6, 0, [self.group_mrp_user.id])] - }) - - self.mrp_production_test1 = self.env['mrp.production'].sudo(self.res_users_mrp_user.id).create({ - 'product_id': self.env.ref('product.product_product_3').id, - 'product_qty': 5.0, - 'product_uom_id': self.env.ref('product.product_product_3').uom_id.id, - 'location_src_id': self.env.ref('stock.stock_location_14').id, - 'location_dest_id': self.env.ref('stock.stock_location_output').id, - 'bom_id': self.env.ref('mrp.mrp_bom_kit').id, - 'routing_id': self.env.ref('mrp.mrp_routing_1').id - }) - - # I compute the production order. - # self.mrp_production_test1.action_compute() - - # I check production lines after compute. - # self.assertEqual(len(self.mrp_production_test1.product_lines), 7) - - # Now I check workcenter lines. - from openerp.tools import float_compare - - def assert_equals(value1, value2, msg, float_compare=float_compare): - assert float_compare(value1, value2, precision_digits=2) == 0, msg - # self.assertTrue(len(self.mrp_production_test1.workcenter_lines)) - - - # I check details of Produce Move of Production Order to trace Final Product. - - self.assertEqual(self.mrp_production_test1.state, 'confirmed', "Production order should be confirmed.") - self.assertTrue(self.mrp_production_test1.move_finished_ids, "Trace Record is not created for Final Product.") - move = self.mrp_production_test1.move_finished_ids[0] - source_location_id = self.mrp_production_test1.product_id.property_stock_production.id - self.assertEqual(move.date, self.mrp_production_test1.date_planned, "Planned date is not correspond.") - self.assertEqual(move.product_id.id, self.mrp_production_test1.product_id.id, "Product does not correspond.") - self.assertEqual(move.product_uom.id, self.mrp_production_test1.product_uom_id.id, "UOM does not correspond.") - self.assertEqual(move.product_qty, self.mrp_production_test1.product_qty, "Qty does not correspond.") - self.assertEqual(move.location_id.id, source_location_id, "Source Location does not correspond.") - self.assertEqual(move.location_dest_id.id, self.mrp_production_test1.location_dest_id.id, "Destination Location does not correspond.") - routing_loc = None - if self.mrp_production_test1.bom_id.routing_id and self.mrp_production_test1.bom_id.routing_id.location_id: - routing_loc = self.mrp_production_test1.routing_id.location_id.id - date_planned = self.mrp_production_test1.date_planned - for move_line in self.mrp_production_test1.move_raw_ids: - self.assertEqual(move_line.date, date_planned, "Planned date does not correspond in 'To consume line'.") - # I consume raw materials and put one material in scrap location due to waste it. - for move in self.mrp_production_test1.move_raw_ids: - if move.product_id.id == self.env.ref("product.product_product_6").id: - self.env['stock.scrap'].with_context(active_model='mrp.production', active_id=self.mrp_production_test1.id).create({'product_id': move.product_id.id, 'scrap_qty': 5.0, 'product_uom_id': move.product_uom.id}) - - # I check procurements have been generated for every consume line - - # order = self.env["mrp.production"].browse(self.env.ref("mrp_production_test1")) - move_line_ids = [x.id for x in self.mrp_production_test1.move_raw_ids] - procurements = self.env['procurement.order'].search([('move_dest_id', 'in', move_line_ids)]) - for proc in self.env['procurement.order'].browse(procurements): - date_planned = self.mrp_production_test1.date_planned - if proc.product_id.type not in ('product', 'consu'): - continue - if proc.product_id.id == order_line.product_id.id: - self.assertEqual(proc.date_planned, date_planned, "Planned date does not correspond") - # procurement state should be `confirmed` at this stage, except if procurement_jit is installed, in which - # case it could already be in `running` or `exception` state (not enough stock) - expected_states = ('confirmed', 'running', 'exception') - self.assertEqual(proc.state in expected_states, 'Procurement state is `%s` for %s, expected one of %s' % (proc.state, proc.product_id.name, expected_states)) - - # I change production qty with 3 PC Assemble SC349. - ctx = dict(self.env.context) - ctx.update({'active_id': self.mrp_production_test1.id}) - - - #TODO: fix change production qty - #self.mrp_production_qty = self.env['change.production.qty'].with_context(ctx).create({ - # 'product_qty': 3.0 - #}) - - #self.mrp_production_qty.change_prod_qty() - - # I check qty after changed in production order. - #self.assertEqual(self.mrp_production_test1.product_qty, 3, "Qty is not changed in order.") - move = self.mrp_production_test1.move_finished_ids[0] - self.assertEqual(move.product_qty, self.mrp_production_test1.product_qty, "Qty is not changed in move line.") - - # I run scheduler. - self.env['procurement.order'].run_scheduler() - - # The production order is Waiting Goods, will force production which should set consume lines as available - self.mrp_production_test1.button_plan() - # I check that production order in ready state after forcing production. - - #self.assertEqual(self.mrp_production_test1.availability, 'assigned', 'Production order availability should be set as available') - - # I produce product. - ctx.update({'active_id': self.mrp_production_test1.id}) - - self.mrp_product_produce1 = self.env['mrp.product.produce'].with_context(ctx).create({}) - - qty = self.mrp_product_produce1.product_qty - self.mrp_product_produce1.do_produce() - - # I check production order after produced. - # self.assertEqual(self.mrp_production_test1.state, 'planned', "Production order should be in planned state.") - - def rounding(f, r): - import math - if not r: - return f - return math.ceil(f / r) * r diff --git a/addons/mrp/tests/test_procurement.py b/addons/mrp/tests/test_procurement.py new file mode 100644 index 00000000000..bc212050db5 --- /dev/null +++ b/addons/mrp/tests/test_procurement.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.mrp.tests.common import TestMrpCommon + + +class TestProcurement(TestMrpCommon): + + def test_procurement(self): + """This test case when create production order check procurement is create""" + # Update BOM + self.bom_3.bom_line_ids.filtered(lambda x: x.product_id == self.product_5).unlink() + self.bom_1.bom_line_ids.filtered(lambda x: x.product_id == self.product_1).unlink() + + # Update route + self.warehouse = self.env.ref('stock.warehouse0') + route_manufacture = self.warehouse.manufacture_pull_id.route_id.id + route_mto = self.warehouse.mto_pull_id.route_id.id + self.product_4.write({'route_ids': [(6, 0, [route_manufacture, route_mto])]}) + + # Create production order + # ------------------------- + # Product6 Unit 24 + # Product4 8 Dozen + # Product2 12 Unit + # ----------------------- + + production_product_6 = self.env['mrp.production'].create({ + 'name': 'MO/Test-00002', + 'product_id': self.product_6.id, + 'product_qty': 24, + 'bom_id': self.bom_3.id, + 'product_uom_id': self.product_6.uom_id.id, + }) + production_product_6.action_assign() + + # check production state is Confirmed + self.assertEqual(production_product_6.state, 'confirmed', 'Production order should be for Confirmed state') + + # Check procurement for product 4 created or not. + procurement = self.env['procurement.order'].search([('group_id', '=', production_product_6.procurement_group_id.id), ('product_id', '=', self.product_4.id)]) + self.assertTrue(procurement, 'No procurement are created !') + self.assertEqual(procurement.state, 'running', 'Procurement order should be in state running') + + produce_product_4 = procurement.production_id + # produce product + self.assertEqual(produce_product_4.availability, 'waiting', "Consume material not available") + + # Create production order + # ------------------------- + # Product 4 96 Unit + # Product2 48 Unit + # --------------------- + # Update Inventory + inventory_wizard = self.env['stock.change.product.qty'].create({ + 'product_id': self.product_2.id, + 'new_quantity': 48, + }) + inventory_wizard.change_product_qty() + produce_product_4.action_assign() + self.assertEqual(produce_product_4.product_qty, 8, "Wrong quantity of finish product.") + self.assertEqual(produce_product_4.product_uom_id, self.uom_dozen, "Wrong quantity of finish product.") + self.assertEqual(produce_product_4.availability, 'assigned', "Consume material not available") + + # produce product4 + # --------------- + + produce_wizard = self.env['mrp.product.produce'].with_context({ + 'active_id': produce_product_4.id, + 'active_ids': [produce_product_4.id], + }).create({ + 'product_qty': produce_product_4.product_qty, + }) + produce_wizard.do_produce() + produce_product_4.post_inventory() + # Check procurement and Production state for product 4. + produce_product_4.button_mark_done() + self.assertEqual(produce_product_4.state, 'done', 'Production order should be in state done') + self.assertEqual(procurement.state, 'done', 'Procurement order should be in state done') + + # Produce product 6 + # ------------------ + + # Update Inventory + inventory_wizard = self.env['stock.change.product.qty'].create({ + 'product_id': self.product_2.id, + 'new_quantity': 12, + }) + inventory_wizard.change_product_qty() + production_product_6.action_assign() + + # ------------------------------------ + + self.assertEqual(production_product_6.availability, 'assigned', "Consume material not available") + produce_wizard = self.env['mrp.product.produce'].with_context({ + 'active_id': production_product_6.id, + 'active_ids': [production_product_6.id], + }).create({ + 'product_qty': production_product_6.product_qty, + }) + produce_wizard.do_produce() + + production_product_6.post_inventory() + # Check procurement and Production state for product 6. + production_product_6.button_mark_done() + self.assertEqual(production_product_6.state, 'done', 'Production order should be in state done') + self.assertEqual(self.product_6.qty_available, 24, 'Wrong quantity available of finished product.') diff --git a/addons/mrp/tests/test_stock.py b/addons/mrp/tests/test_stock.py new file mode 100644 index 00000000000..ddd4deb4341 --- /dev/null +++ b/addons/mrp/tests/test_stock.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import common +from odoo.exceptions import except_orm + + +class TestWarehouse(common.TestMrpCommon): + + def test_manufacturing_route(self): + warehouse_1_stock_manager = self.warehouse_1.sudo(self.user_stock_manager) + manu_rule = self.env['procurement.rule'].search([ + ('action', '=', 'manufacture'), + ('warehouse_id', '=', self.warehouse_1.id)]) + self.assertEqual(self.warehouse_1.manufacture_pull_id, manu_rule) + manu_route = manu_rule.route_id + self.assertIn(manu_route, warehouse_1_stock_manager._get_all_routes()) + warehouse_1_stock_manager.write({ + 'manufacture_to_resupply': False + }) + self.assertFalse(self.warehouse_1.manufacture_pull_id) + self.assertFalse(self.warehouse_1.manu_type_id.active) + self.assertNotIn(manu_route, warehouse_1_stock_manager._get_all_routes()) + warehouse_1_stock_manager.write({ + 'manufacture_to_resupply': True + }) + manu_rule = self.env['procurement.rule'].search([ + ('action', '=', 'manufacture'), + ('warehouse_id', '=', self.warehouse_1.id)]) + self.assertEqual(self.warehouse_1.manufacture_pull_id, manu_rule) + self.assertTrue(self.warehouse_1.manu_type_id.active) + self.assertIn(manu_route, warehouse_1_stock_manager._get_all_routes()) + + def test_manufacturing_scrap(self): + """ + Testing to do a scrap of consumed material. + """ + + # Update demo products + (self.product_4 | self.product_2).write({ + 'tracking': 'lot', + }) + + # Update Bill Of Material to remove product with phantom bom. + self.bom_3.bom_line_ids.filtered(lambda x: x.product_id == self.product_5).unlink() + + # Create Inventory Adjustment For Stick and Stone Tools with lot. + lot_product_4 = self.env['stock.production.lot'].create({ + 'name': '0000000000001', + 'product_id': self.product_4.id, + }) + lot_product_2 = self.env['stock.production.lot'].create({ + 'name': '0000000000002', + 'product_id': self.product_2.id, + }) + + stock_inv_product_4 = self.env['stock.inventory'].create({ + 'name': 'Stock Inventory for Stick', + 'filter': 'product', + 'product_id': self.product_4.id, + 'line_ids': [ + (0, 0, {'product_id': self.product_4.id, 'product_uom_id': self.product_4.uom_id.id, 'product_qty': 8, 'prod_lot_id': lot_product_4.id, 'location_id': self.ref('stock.stock_location_14')}), + ]}) + + stock_inv_product_2 = self.env['stock.inventory'].create({ + 'name': 'Stock Inventory for Stone Tools', + 'filter': 'product', + 'product_id': self.product_2.id, + 'line_ids': [ + (0, 0, {'product_id': self.product_2.id, 'product_uom_id': self.product_2.uom_id.id, 'product_qty': 12, 'prod_lot_id': lot_product_2.id, 'location_id': self.ref('stock.stock_location_14')}) + ]}) + (stock_inv_product_4 | stock_inv_product_2).prepare_inventory() + (stock_inv_product_4 | stock_inv_product_2).action_done() + + #Create Manufacturing order. + production_3 = self.env['mrp.production'].create({ + 'name': 'MO-Test003', + 'product_id': self.product_6.id, + 'product_qty': 12, + 'bom_id': self.bom_3.id, + 'product_uom_id': self.product_6.uom_id.id, + }) + production_3.action_assign() + + # Check Manufacturing order's availability. + self.assertEqual(production_3.availability, 'assigned', "Production order's availability should be Available.") + + location_id = production_3.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) and production_3.location_src_id.id or production_3.location_dest_id.id, + + # Scrap Product Wood without lot to check assert raise ?. + with self.assertRaises(except_orm): + self.env['stock.scrap'].with_context(active_model='mrp.production', active_id=production_3.id).create({'product_id': self.product_2.id, 'scrap_qty': 1.0, 'product_uom_id': self.product_2.uom_id.id, 'location_id': location_id, 'production_id': production_3.id}) + + # Scrap Product Wood with lot. + self.env['stock.scrap'].with_context(active_model='mrp.production', active_id=production_3.id).create({'product_id': self.product_2.id, 'scrap_qty': 1.0, 'product_uom_id': self.product_2.uom_id.id, 'location_id': location_id, 'lot_id': lot_product_2.id, 'production_id': production_3.id}) + + #Check scrap move is created for this production order. + #TODO: should check with scrap objects link in between + +# scrap_move = production_3.move_raw_ids.filtered(lambda x: x.product_id == self.product_2 and x.scrapped) +# self.assertTrue(scrap_move, "There are no any scrap move created for production order.") diff --git a/addons/mrp/tests/test_workorder_operation.py b/addons/mrp/tests/test_workorder_operation.py index 10bc62dd4a9..b773f1fcca7 100644 --- a/addons/mrp/tests/test_workorder_operation.py +++ b/addons/mrp/tests/test_workorder_operation.py @@ -6,42 +6,9 @@ class TestWorkOrderProcess(common.TransactionCase): def setUp(self): super(TestWorkOrderProcess, self).setUp() - self.MrpProductProduce = self.env['mrp.product.produce'] - self.Product = self.env['product.product'] - self.Lot = self.env['stock.production.lot'] - self.StockMoveLot = self.env['stock.move.lots'] - self.MrpBom = self.env['mrp.bom'] - self.MrpBomLine = self.env['mrp.bom.line'] - self.Inventory = self.env['stock.inventory'] - self.InventoryLine = self.env['stock.inventory.line'] self.source_location_id = self.ref('stock.stock_location_14') self.warehouse = self.env.ref('stock.warehouse0') - def create_product(self, name, uom_id, route_ids=None): - return self.Product.create({ - 'name': name, - 'type': 'product', - 'tracking': 'lot', - 'uom_id': uom_id, - 'uom_po_id': uom_id, - 'route_ids': route_ids}) - - def create_bom_lines(self, bom_id, product_id, qty, uom_id): - self.MrpBomLine.create({ - 'product_id': product_id, - 'product_qty': qty, - 'bom_id': bom_id, - 'product_uom_id': uom_id}) - - def create_inventory_line(self, inventory, product, qty, lot_id=False): - self.InventoryLine.create({ - 'inventory_id': inventory.id, - 'product_id': product.id, - 'product_uom_id': product.uom_id.id, - 'product_qty': qty, - 'prod_lot_id': lot_id, - 'location_id': self.source_location_id}) - def test_00_workorder_process(self): """ Testing consume quants and produced quants with workorder """ dining_table = self.env.ref("mrp.product_product_computer_desk") @@ -53,7 +20,8 @@ class TestWorkOrderProcess(common.TransactionCase): 'product_id': dining_table.id, 'product_qty': 1.0, 'product_uom_id': dining_table.uom_id.id, - 'bom_id': self.ref("mrp.mrp_bom_desk")}) + 'bom_id': self.ref("mrp.mrp_bom_desk") + }) # Set tracking lot on finish and consume products. dining_table.tracking = 'lot' @@ -62,26 +30,41 @@ class TestWorkOrderProcess(common.TransactionCase): product_bolt.tracking = "lot" # Initial inventory of product sheet, lags and bolt - lot_sheet = self.Lot.create({'product_id': product_table_sheet.id}) - lot_leg = self.Lot.create({'product_id': product_table_leg.id}) - lot_bolt = self.Lot.create({'product_id': product_bolt.id}) + lot_sheet = self.env['stock.production.lot'].create({'product_id': product_table_sheet.id}) + lot_leg = self.env['stock.production.lot'].create({'product_id': product_table_leg.id}) + lot_bolt = self.env['stock.production.lot'].create({'product_id': product_bolt.id}) # Initialize inventory # -------------------- - inventory = self.Inventory.create({ + inventory = self.env['stock.inventory'].create({ 'name': 'Inventory Product Table', - 'filter': 'partial'}) - inventory.prepare_inventory() - self.assertFalse(inventory.line_ids, "Inventory line should not created.") - self.create_inventory_line(inventory, product_table_sheet, 20, lot_sheet.id) - self.create_inventory_line(inventory, product_table_leg, 20, lot_leg.id) - self.create_inventory_line(inventory, product_bolt, 20, lot_bolt.id) + 'filter': 'partial', + 'line_ids': [(0, 0, { + 'product_id': product_table_sheet.id, + 'product_uom_id': product_table_sheet.uom_id.id, + 'product_qty': 20, + 'prod_lot_id': lot_sheet.id, + 'location_id': self.source_location_id + }), (0, 0, { + 'product_id': product_table_leg.id, + 'product_uom_id': product_table_leg.uom_id.id, + 'product_qty': 20, + 'prod_lot_id': lot_leg.id, + 'location_id': self.source_location_id + }), (0, 0, { + 'product_id': product_bolt.id, + 'product_uom_id': product_bolt.uom_id.id, + 'product_qty': 20, + 'prod_lot_id': lot_bolt.id, + 'location_id': self.source_location_id + })] + }) inventory.action_done() # Create work order production_table.button_plan() # Check Work order created or not - self.assertEqual(len(production_table.workorder_ids), 3, "Workorder should be created 3 instead of %s"%(len(production_table.workorder_ids))) + self.assertEqual(len(production_table.workorder_ids), 3) # --------------------------------------------------------- # Process all workorder and check it state. @@ -89,8 +72,8 @@ class TestWorkOrderProcess(common.TransactionCase): workorders = production_table.workorder_ids self.assertEqual(workorders[0].state, 'ready', "First workorder state should be ready.") - self.assertEqual(workorders[1].state, 'pending', "Workorder state should be pending.") - self.assertEqual(workorders[2].state, 'pending', "Workorder state should be pending.") + self.assertEqual(workorders[1].state, 'pending') + self.assertEqual(workorders[2].state, 'pending') # -------------------------------------------------------------- # Process cutting operation... @@ -100,11 +83,11 @@ class TestWorkOrderProcess(common.TransactionCase): workorders[0].write({'final_lot_id': finished_lot.id}) workorders[0].button_start() workorders[0].active_move_lot_ids[0].write({'lot_id': lot_sheet.id, 'quantity_done': 1}) - self.assertEqual(workorders[0].state, 'progress', "First workorder state should be Inprogress.") + self.assertEqual(workorders[0].state, 'progress') workorders[0].record_production() - self.assertEqual(workorders[0].state, 'done', "Cutting process should be done.") + self.assertEqual(workorders[0].state, 'done') move_table_sheet = production_table.move_raw_ids.filtered(lambda x : x.product_id == product_table_sheet) - self.assertEqual(move_table_sheet.quantity_done, 1, "Wrong consumed quantity of raw materials.") + self.assertEqual(move_table_sheet.quantity_done, 1) # -------------------------------------------------------------- # Process drilling operation ... @@ -114,8 +97,8 @@ class TestWorkOrderProcess(common.TransactionCase): workorders[1].active_move_lot_ids[0].write({'lot_id': lot_leg.id, 'quantity_done': 4}) workorders[1].record_production() move_leg = production_table.move_raw_ids.filtered(lambda x : x.product_id == product_table_leg) - self.assertEqual(workorders[1].state, 'done', "Drilling process should be done.") - self.assertEqual(move_leg.quantity_done, 4, "Wrong consumed quantity of raw materials.") + self.assertEqual(workorders[1].state, 'done') + self.assertEqual(move_leg.quantity_done, 4) # -------------------------------------------------------------- # Process fitting operation ... @@ -128,8 +111,8 @@ class TestWorkOrderProcess(common.TransactionCase): move_lot.write({'lot_id': lot_bolt.id, 'quantity_done': 4}) move_table_bolt = production_table.move_raw_ids.filtered(lambda x : x.product_id.id == product_bolt.id) workorders[2].record_production() - self.assertEqual(workorders[2].state, 'done', "Fitting process should done.") - self.assertEqual(move_table_bolt.quantity_done, 4, "Wrong consumed quantity of raw materials.") + self.assertEqual(workorders[2].state, 'done') + self.assertEqual(move_table_bolt.quantity_done, 4) # ----------------------------------------- # Post inventory of manufacturing order @@ -141,20 +124,20 @@ class TestWorkOrderProcess(common.TransactionCase): # ---------------------------------------- # Check consume quants and produce quants. # ---------------------------------------- - self.assertEqual(sum(move_table_sheet.quant_ids.mapped('qty')) , 1, "Wrong quantity of consumed product %s"% move_table_sheet.product_id.name) - self.assertEqual(sum(move_leg.quant_ids.mapped('qty')), 4, "Wrong quantity of consumed product %s"% move_leg.product_id.name) - self.assertEqual(sum(move_table_bolt.quant_ids.mapped('qty')), 4, "Wrong quantity of consumed product %s"% move_table_bolt.product_id.name) + self.assertEqual(sum(move_table_sheet.quant_ids.mapped('qty')), 1, "Wrong quantity of consumed product %s" % move_table_sheet.product_id.name) + self.assertEqual(sum(move_leg.quant_ids.mapped('qty')), 4, "Wrong quantity of consumed product %s" % move_leg.product_id.name) + self.assertEqual(sum(move_table_bolt.quant_ids.mapped('qty')), 4, "Wrong quantity of consumed product %s" % move_table_bolt.product_id.name) consume_quants = move_table_sheet.quant_ids + move_leg.quant_ids + move_table_bolt.quant_ids # Check for produced quant correctly linked with consumed quants or not. - finish_move = production_table.move_finished_ids.filtered(lambda x : x.product_id.id == dining_table.id) + finish_move = production_table.move_finished_ids.filtered(lambda x: x.product_id.id == dining_table.id) finished_quant = finish_move.quant_ids[0] for quant in consume_quants: - self.assertEqual(len(quant.produced_quant_ids), 1, "Wrong produced quants on consumed quant.") - self.assertEqual(quant.produced_quant_ids[0].lot_id.id, finished_lot.id, "Wrong produced quant lot on consumed quant.") - self.assertEqual(quant.produced_quant_ids[0].id, finished_quant.id, "Wrong produced quants of consume material.") + self.assertEqual(len(quant.produced_quant_ids), 1) + self.assertEqual(quant.produced_quant_ids[0].lot_id.id, finished_lot.id) + self.assertEqual(quant.produced_quant_ids[0].id, finished_quant.id) # ------------------------------------------ # Check finished quants with consumed quant. @@ -170,17 +153,35 @@ class TestWorkOrderProcess(common.TransactionCase): # Create new product charger and keybord # -------------------------------------- - product_charger = self.create_product('Charger', unit) - product_keybord = self.create_product('Usb Keybord', unit) + product_charger = self.env['product.product'].create({ + 'name': 'Charger', + 'type': 'product', + 'tracking': 'lot', + 'uom_id': unit, + 'uom_po_id': unit}) + product_keybord = self.env['product.product'].create({ + 'name': 'Usb Keybord', + 'type': 'product', + 'tracking': 'lot', + 'uom_id': unit, + 'uom_po_id': unit}) # Create bill of material for customized laptop. - bom_custom_laptop = self.MrpBom.create({ - 'product_tmpl_id': custom_laptop.product_tmpl_id.id, - 'product_qty': 10, - 'product_uom_id': unit}) - self.create_bom_lines(bom_custom_laptop.id, product_charger.id, 20, unit) - self.create_bom_lines(bom_custom_laptop.id, product_keybord.id, 20, unit) + bom_custom_laptop = self.env['mrp.bom'].create({ + 'product_tmpl_id': custom_laptop.product_tmpl_id.id, + 'product_qty': 10, + 'product_uom_id': unit, + 'bom_line_ids': [(0, 0, { + 'product_id': product_charger.id, + 'product_qty': 20, + 'product_uom_id': unit + }), (0, 0, { + 'product_id': product_keybord.id, + 'product_qty': 20, + 'product_uom_id': unit + })] + }) # Create production order for customize laptop. @@ -195,34 +196,45 @@ class TestWorkOrderProcess(common.TransactionCase): # Check production order status of availablity - self.assertEqual(mo_custom_laptop.availability, 'waiting', "Production order should be in waiting state.") + self.assertEqual(mo_custom_laptop.availability, 'waiting') # -------------------------------------------------- # Set inventory for rawmaterial charger and keybord # -------------------------------------------------- - lot_charger = self.Lot.create({'product_id': product_charger.id}) - lot_keybord = self.Lot.create({'product_id': product_keybord.id}) + lot_charger = self.env['stock.production.lot'].create({'product_id': product_charger.id}) + lot_keybord = self.env['stock.production.lot'].create({'product_id': product_keybord.id}) # Initialize Inventory # -------------------- - inventory = self.Inventory.create({ + inventory = self.env['stock.inventory'].create({ 'name': 'Inventory Product Table', - 'filter': 'partial'}) - inventory.prepare_inventory() - self.assertFalse(inventory.line_ids, "Inventory line should not created.") - self.create_inventory_line(inventory, product_charger, 20, lot_charger.id) - self.create_inventory_line(inventory, product_keybord, 20, lot_keybord.id) + 'filter': 'partial', + 'line_ids': [(0, 0, { + 'product_id': product_charger.id, + 'product_uom_id': product_charger.uom_id.id, + 'product_qty': 20, + 'prod_lot_id': lot_charger.id, + 'location_id': self.source_location_id + }), (0, 0, { + 'product_id': product_keybord.id, + 'product_uom_id': product_keybord.uom_id.id, + 'product_qty': 20, + 'prod_lot_id': lot_keybord.id, + 'location_id': self.source_location_id + })] + }) + # inventory.prepare_inventory() inventory.action_done() # Check consumed move status mo_custom_laptop.action_assign() - self.assertEqual( mo_custom_laptop.availability, 'assigned', "Production order should be in assigned state.") + self.assertEqual( mo_custom_laptop.availability, 'assigned') # Check current status of raw materials. for move in mo_custom_laptop.move_raw_ids: - self.assertEqual(move.product_uom_qty, 20, "Wrong consume quantity of raw material %s"% (move.product_id.name)) - self.assertEqual(move.quantity_done, 0, "Wrong produced quantity on raw material %s"% (move.product_id.name)) + self.assertEqual(move.product_uom_qty, 20, "Wrong consume quantity of raw material %s: %s instead of %s" % (move.product_id.name, move.product_uom_qty, 20)) + self.assertEqual(move.quantity_done, 0, "Wrong produced quantity on raw material %s: %s instead of %s" % (move.product_id.name, move.quantity_done, 0)) # ----------------- # Start production @@ -231,41 +243,41 @@ class TestWorkOrderProcess(common.TransactionCase): # Produce 6 Unit of custom laptop will consume ( 12 Unit of keybord and 12 Unit of charger) context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id} - product_consume = self.MrpProductProduce.with_context(context).create({'product_qty': 6.00}) - laptop_lot_001 = self.Lot.create({'product_id': custom_laptop.id}) + product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 6.00}) + laptop_lot_001 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) product_consume.lot_id = laptop_lot_001.id product_consume.consume_line_ids.write({'quantity_done': 12}) product_consume.do_produce() # Check consumed move after produce 6 quantity of customized laptop. for move in mo_custom_laptop.move_raw_ids: - self.assertEqual(move.quantity_done, 12, "Wrong produced quantity on raw material %s"% (move.product_id.name)) - self.assertEqual(len(mo_custom_laptop.move_raw_ids), 2, "Wrong raw material moves of production order.") + self.assertEqual(move.quantity_done, 12, "Wrong produced quantity on raw material %s" % (move.product_id.name)) + self.assertEqual(len(mo_custom_laptop.move_raw_ids), 2) mo_custom_laptop.post_inventory() - self.assertEqual(len(mo_custom_laptop.move_raw_ids), 4, "Wrong raw material moves of production order.") + self.assertEqual(len(mo_custom_laptop.move_raw_ids), 4) # Check done move and confirmed move quantity. - charger_done_move = mo_custom_laptop.move_raw_ids.filtered(lambda x : x.product_id.id == product_charger.id and x.state == 'done') - keybord_done_move = mo_custom_laptop.move_raw_ids.filtered(lambda x : x.product_id.id == product_keybord.id and x.state == 'done') - self.assertEquals(charger_done_move.product_uom_qty, 12, "Wrong consumed raw material charger.") - self.assertEquals(keybord_done_move.product_uom_qty, 12, "Wrong consumed raw material keybord") + charger_done_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state == 'done') + keybord_done_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state == 'done') + self.assertEquals(charger_done_move.product_uom_qty, 12) + self.assertEquals(keybord_done_move.product_uom_qty, 12) # Produce remaining 4 quantity # ---------------------------- # Produce 4 Unit of custom laptop will consume ( 8 Unit of keybord and 8 Unit of charger). context = {"active_ids": [mo_custom_laptop.id], "active_id": mo_custom_laptop.id} - product_consume = self.MrpProductProduce.with_context(context).create({'product_qty': 4.00}) - laptop_lot_002 = self.Lot.create({'product_id': custom_laptop.id}) + product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 4.00}) + laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) product_consume.lot_id = laptop_lot_002.id - self.assertEquals(len(product_consume.consume_line_ids), 2, "Wrong lots linked with move.") + self.assertEquals(len(product_consume.consume_line_ids), 2) product_consume.consume_line_ids.write({'quantity_done': 8}) product_consume.do_produce() - charger_move = mo_custom_laptop.move_raw_ids.filtered(lambda x : x.product_id.id == product_charger.id and x.state != 'done') - keybord_move = mo_custom_laptop.move_raw_ids.filtered(lambda x : x.product_id.id == product_keybord.id and x.state !='done') - self.assertEquals(charger_move.quantity_done, 8, "Wrong consumed quantity of %s"% charger_move.product_id.name) - self.assertEquals(keybord_move.quantity_done, 8, "Wrong consumed quantity of %s"% keybord_move.product_id.name) + charger_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state != 'done') + keybord_move = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state !='done') + self.assertEquals(charger_move.quantity_done, 8, "Wrong consumed quantity of %s" % charger_move.product_id.name) + self.assertEquals(keybord_move.quantity_done, 8, "Wrong consumed quantity of %s" % keybord_move.product_id.name) # Post Inventory of production order. mo_custom_laptop.post_inventory() @@ -277,8 +289,8 @@ class TestWorkOrderProcess(common.TransactionCase): # Finished move quants of production order - finshed_quant_lot_001 = mo_custom_laptop.move_finished_ids.filtered(lambda x : x.product_id.id == custom_laptop.id and x.product_uom_qty==6).mapped('quant_ids') - finshed_quant_lot_002 = mo_custom_laptop.move_finished_ids.filtered(lambda x : x.product_id.id == custom_laptop.id and x.product_uom_qty==4).mapped('quant_ids') + finshed_quant_lot_001 = mo_custom_laptop.move_finished_ids.filtered(lambda x: x.product_id.id == custom_laptop.id and x.product_uom_qty==6).mapped('quant_ids') + finshed_quant_lot_002 = mo_custom_laptop.move_finished_ids.filtered(lambda x: x.product_id.id == custom_laptop.id and x.product_uom_qty==4).mapped('quant_ids') # -------------------------------- # Check consume and produce quants @@ -286,34 +298,31 @@ class TestWorkOrderProcess(common.TransactionCase): # Check consumed quants of lot1 for consume_quant in finshed_quant_lot_001[0].consumed_quant_ids: - self.assertEqual(consume_quant.qty, 12, "Wrong quantity of quants of consume quants") - self.assertEqual(consume_quant.produced_quant_ids[0].lot_id.id , finshed_quant_lot_001[0].lot_id.id , "Wrong produced quant lot on consume quants") - self.assertEqual(consume_quant.produced_quant_ids[0].id , finshed_quant_lot_001[0].id , "Wrong produced quant on consume quants") + self.assertEqual(consume_quant.qty, 12) + self.assertEqual(consume_quant.produced_quant_ids[0].lot_id.id, finshed_quant_lot_001[0].lot_id.id) + self.assertEqual(consume_quant.produced_quant_ids[0].id, finshed_quant_lot_001[0].id) - self.assertEqual(len(finshed_quant_lot_001[0].consumed_quant_ids), 2, "Wrong consumed quant linked with produced quant for lot %s "%laptop_lot_001.name) + self.assertEqual(len(finshed_quant_lot_001[0].consumed_quant_ids), 2, "Wrong consumed quant linked with produced quant for lot %s " % laptop_lot_001.name) # Check total no of quants linked with produced quants. - self.assertEqual(len(finshed_quant_lot_002[0].consumed_quant_ids), 2, "Wrong consumed quant linked with produced quant for lot %s "%laptop_lot_002.name) + self.assertEqual(len(finshed_quant_lot_002[0].consumed_quant_ids), 2, "Wrong consumed quant linked with produced quant for lot %s " % laptop_lot_002.name) # Check consumed quants of lot2 for consume_quant in finshed_quant_lot_002[0].consumed_quant_ids: - self.assertEqual(consume_quant.qty, 8, "Wrong quantity of quants of consume quants") - self.assertEqual(consume_quant.produced_quant_ids[0].lot_id.id , finshed_quant_lot_002[0].lot_id.id , "Wrong produced quant lot on consume quants") - self.assertEqual(consume_quant.produced_quant_ids[0].id , finshed_quant_lot_002[0].id , "Wrong produced quant on consume quants") + self.assertEqual(consume_quant.qty, 8) + self.assertEqual(consume_quant.produced_quant_ids[0].lot_id.id, finshed_quant_lot_002[0].lot_id.id) + self.assertEqual(consume_quant.produced_quant_ids[0].id, finshed_quant_lot_002[0].id) # Check total quantity consumed of charger, keybord # -------------------------------------------------- - charger_quants = mo_custom_laptop.move_raw_ids.filtered(lambda x : x.product_id.id == product_charger.id and x.state == 'done').mapped('quant_ids') - keybord_moves = mo_custom_laptop.move_raw_ids.filtered(lambda x : x.product_id.id == product_keybord.id and x.state == 'done').mapped('quant_ids') - self.assertEqual(sum(charger_quants.mapped('qty')) , 20 , "Wrong consumed quants quantity of charger.") - self.assertEqual(sum(keybord_moves.mapped('qty')) , 20 , "Wrong consumed quants quantity of keybord.") + charger_quants = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_charger.id and x.state == 'done').mapped('quant_ids') + keybord_moves = mo_custom_laptop.move_raw_ids.filtered(lambda x: x.product_id.id == product_keybord.id and x.state == 'done').mapped('quant_ids') + self.assertEqual(sum(charger_quants.mapped('qty')), 20) + self.assertEqual(sum(keybord_moves.mapped('qty')), 20) def test_02_different_uom_on_bomlines(self): """ Testing bill of material with diffrent unit of measure.""" - - - route_manufacture = self.warehouse.manufacture_pull_id.route_id.id route_mto = self.warehouse.mto_pull_id.route_id.id unit = self.ref("product.product_uom_unit") @@ -321,9 +330,25 @@ class TestWorkOrderProcess(common.TransactionCase): kg = self.ref("product.product_uom_kgm") gm = self.ref("product.product_uom_gram") # Create Product A, B, C - product_A = self.create_product('Product A', dozen, [(6, 0, [route_manufacture, route_mto])]) - product_B = self.create_product('Product B', dozen) - product_C = self.create_product('Product C', kg) + product_A = self.env['product.product'].create({ + 'name': 'Product A', + 'type': 'product', + 'tracking': 'lot', + 'uom_id': dozen, + 'uom_po_id': dozen, + 'route_ids': [(6, 0, [route_manufacture, route_mto])]}) + product_B = self.env['product.product'].create({ + 'name': 'Product B', + 'type': 'product', + 'tracking': 'lot', + 'uom_id': dozen, + 'uom_po_id': dozen}) + product_C = self.env['product.product'].create({ + 'name': 'Product C', + 'type': 'product', + 'tracking': 'lot', + 'uom_id': kg, + 'uom_po_id': kg}) # Bill of materials # ----------------- @@ -334,13 +359,20 @@ class TestWorkOrderProcess(common.TransactionCase): # Product C 600 gram # ----------------------------------- - bom_a = self.MrpBom.create({ - 'product_tmpl_id': product_A.product_tmpl_id.id, - 'product_qty': 2, - 'product_uom_id': unit}) - self.create_bom_lines(bom_a.id, product_B.id, 4, unit) - self.create_bom_lines(bom_a.id, product_C.id, 600, gm) - #create_bom_lines(bom_a.id, product_d.id, 4, self.uom_unit.id, 'make_to_order') + bom_a = self.env['mrp.bom'].create({ + 'product_tmpl_id': product_A.product_tmpl_id.id, + 'product_qty': 2, + 'product_uom_id': unit, + 'bom_line_ids': [(0, 0, { + 'product_id': product_B.id, + 'product_qty': 4, + 'product_uom_id': unit + }), (0, 0, { + 'product_id': product_C.id, + 'product_qty': 600, + 'product_uom_id': gm + })] + }) # Create production order with product A 10 Unit. # ----------------------------------------------- @@ -351,30 +383,41 @@ class TestWorkOrderProcess(common.TransactionCase): 'product_uom_id': unit, 'bom_id': bom_a.id}) - move_product_b = mo_custom_product.move_raw_ids.filtered(lambda x : x.product_id == product_B) - move_product_c = mo_custom_product.move_raw_ids.filtered(lambda x : x.product_id == product_C) + move_product_b = mo_custom_product.move_raw_ids.filtered(lambda x: x.product_id == product_B) + move_product_c = mo_custom_product.move_raw_ids.filtered(lambda x: x.product_id == product_C) # Check move correctly created or not. - self.assertEqual(move_product_b.product_uom_qty, 20 , "Wrong consume quantity of raw materials.") - self.assertEqual(move_product_b.product_uom.id, unit, "Wrong unit of measure in rawmaterial.") - self.assertEqual(move_product_c.product_uom_qty, 3000, "Wrong consume quantity of raw materials.") - self.assertEqual(move_product_c.product_uom.id, gm, "Wrong unit of measure in rawmaterial.") + self.assertEqual(move_product_b.product_uom_qty, 20) + self.assertEqual(move_product_b.product_uom.id, unit) + self.assertEqual(move_product_c.product_uom_qty, 3000) + self.assertEqual(move_product_c.product_uom.id, gm) # Lot create for product B and product C # --------------------------------------- - lot_a = self.Lot.create({'product_id': product_A.id}) - lot_b = self.Lot.create({'product_id': product_B.id}) - lot_c = self.Lot.create({'product_id': product_C.id}) + lot_a = self.env['stock.production.lot'].create({'product_id': product_A.id}) + lot_b = self.env['stock.production.lot'].create({'product_id': product_B.id}) + lot_c = self.env['stock.production.lot'].create({'product_id': product_C.id}) # Inventory Update # ---------------- - inventory = self.Inventory.create({ + inventory = self.env['stock.inventory'].create({ 'name': 'Inventory Product B and C', - 'filter': 'partial'}) - inventory.prepare_inventory() - self.assertFalse(inventory.line_ids, "Inventory line should not created.") - self.create_inventory_line(inventory, product_B, 3, lot_b.id) - self.create_inventory_line(inventory, product_C, 3, lot_c.id) + 'filter': 'partial', + 'line_ids': [(0, 0, { + 'product_id': product_B.id, + 'product_uom_id': product_B.uom_id.id, + 'product_qty': 3, + 'prod_lot_id': lot_b.id, + 'location_id': self.source_location_id + }), (0, 0, { + 'product_id': product_C.id, + 'product_uom_id': product_C.uom_id.id, + 'product_qty': 3, + 'prod_lot_id': lot_c.id, + 'location_id': self.source_location_id + })] + }) + # inventory.prepare_inventory() inventory.action_done() # Start Production ... @@ -382,10 +425,10 @@ class TestWorkOrderProcess(common.TransactionCase): mo_custom_product.action_assign() context = {"active_ids": [mo_custom_product.id], "active_id": mo_custom_product.id} - product_consume = self.MrpProductProduce.with_context(context).create({'product_qty': 10}) - # laptop_lot_002 = self.Lot.create({'product_id': custom_laptop.id}) + product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 10}) + # laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) product_consume.lot_id = lot_a.id - self.assertEquals(len(product_consume.consume_line_ids), 2, "Wrong move lot linked to produce wizard.") + self.assertEquals(len(product_consume.consume_line_ids), 2) product_consume.consume_line_ids.filtered(lambda x : x.product_id == product_C).write({'quantity_done': 3000}) product_consume.consume_line_ids.filtered(lambda x : x.product_id == product_B).write({'quantity_done': 20}) product_consume.do_produce() @@ -393,8 +436,8 @@ class TestWorkOrderProcess(common.TransactionCase): # Check correct quant linked with move or not # ------------------------------------------- - self.assertEqual(len(move_product_b.quant_ids), 1, "Wrong quants on move of product B") - self.assertEqual(len(move_product_c.quant_ids), 1, "Wrong quants on move of product C") - self.assertEqual(move_product_b.quant_ids.qty, move_product_b.product_qty , "Wrong quantity on quants of product B") - self.assertEqual(move_product_c.quant_ids.qty, 3 , "Wrong quantity on quants of product B") - self.assertEqual(move_product_c.quant_ids.product_uom_id.id, kg , "Wrong quantity on quants of product B") + self.assertEqual(len(move_product_b.quant_ids), 1) + self.assertEqual(len(move_product_c.quant_ids), 1) + self.assertEqual(move_product_b.quant_ids.qty, move_product_b.product_qty) + self.assertEqual(move_product_c.quant_ids.qty, 3) + self.assertEqual(move_product_c.quant_ids.product_uom_id.id, kg) diff --git a/addons/mrp_byproduct/tests/test_mrp_byproduct.py b/addons/mrp_byproduct/tests/test_mrp_byproduct.py index e9dc45b07c3..457dc719dd2 100644 --- a/addons/mrp_byproduct/tests/test_mrp_byproduct.py +++ b/addons/mrp_byproduct/tests/test_mrp_byproduct.py @@ -8,52 +8,79 @@ class TestMrpByProduct(common.TransactionCase): def setUp(self): super(TestMrpByProduct, self).setUp() - self.product_28 = self.env.ref('product.product_delivery_02') - self.product_33 = self.env.ref('product.product_delivery_01') - self.product_uom = self.env.ref('product.product_uom_unit') - self.location_src = self.env.ref('stock.stock_location_stock') - self.mrp_action = self.env.ref('mrp.menu_mrp_production_action') + self.MrpBom = self.env['mrp.bom'] + self.warehouse = self.env.ref('stock.warehouse0') + route_manufacture = self.warehouse.manufacture_pull_id.route_id.id + route_mto = self.warehouse.mto_pull_id.route_id.id + self.uom_unit_id = self.ref('product.product_uom_unit') + def create_product(name, route_ids=[]): + return self.env['product.product'].create({ + 'name': name, + 'type': 'product', + 'route_ids': route_ids}) + + # Create product A, B, C. + # -------------------------- + self.product_a = create_product('Product A', route_ids=[(6, 0, [route_manufacture, route_mto])]) + self.product_b = create_product('Product B', route_ids=[(6, 0, [route_manufacture, route_mto])]) + self.product_c_id = create_product('Product C', route_ids=[]).id def test_00_mrp_byproduct(self): - # I add a sub product in Bill of material for product External Hard Disk. - bom_hardisk = self.env['mrp.bom'].create({'product_tmpl_id': self.product_28.product_tmpl_id.id, - 'product_uom_id': self.product_uom.id, - 'sub_products': [(0, 0, {'product_id': self.product_33.id, - 'product_uom_id': self.product_uom.id, - 'product_qty': 2.0})]}) - # I create a production order for External Hard Disk - mnf_hardisk = self.env['mrp.production'].create({'product_id': self.product_28.id, - 'product_qty': 2.0, - 'product_uom_id': self.product_uom.id, - 'bom_id': bom_hardisk.id, - 'location_src_id': self.location_src.id}) + """ 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 BOM for product A and set byproduct product B + 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})], + 'sub_products': [(0, 0, {'product_id': self.product_b.id, 'product_uom_id': self.uom_unit_id, 'product_qty': 1})] + }) + + # Create production order for product A + # ------------------------------------- + + mnf_product_a = self.env['mrp.production'].create({ + 'product_id': self.product_a.id, + 'product_qty': 2.0, + 'product_uom_id': self.uom_unit_id, + 'bom_id': bom_product_a.id, + }) # I compute the data of production order - context = {"active_ids": [mnf_hardisk.id], "active_id": mnf_hardisk.id} - # mnf_hardisk.with_context(context).action_compute() + context = {"active_model": "mrp.production", "active_ids": [mnf_product_a.id], "active_id": mnf_product_a.id} # I confirm the production order. - # mnf_hardisk.signal_workflow('button_confirm') - - # I confirm the production order. - self.assertEqual(mnf_hardisk.state, 'confirmed', 'Production order should be in state confirmed') + 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 = self.env['stock.move'].search([('product_id', '=', self.product_33.id)]) + 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. product_consume = self.env['mrp.product.produce'].with_context(context).create({'product_qty': 2.00}) # I finish the production order. - context = {"active_model": "mrp.production", "active_ids": [mnf_hardisk.id], "active_id": mnf_hardisk.id} + self.assertEqual(len(mnf_product_a.move_raw_ids), 1, "Wrong consume move on production order.") product_consume.do_produce() - mnf_hardisk.post_inventory() + 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. - #moves = self.Move.search([('origin', '=', mnf_hardisk.name), ('product_id', 'in', [self.product_28.id, self.product_33.id])]) - #for move in moves: - # self.assertEqual(move.state, 'done', 'Moves are not done!') - - \ No newline at end of file + self.assertFalse(any(move.state != 'done' for move in moves), 'Moves are not done!') diff --git a/addons/product/tests/common.py b/addons/product/tests/common.py index fac4c1dba74..007caf4d6bd 100644 --- a/addons/product/tests/common.py +++ b/addons/product/tests/common.py @@ -17,51 +17,76 @@ class TestProductCommon(common.SavepointCase): # Product environment related data Uom = cls.env['product.uom'] - categ_unit = cls.env.ref('product.product_uom_categ_unit') - weight_unit = cls.env.ref('product.product_uom_categ_kgm') - cls.uom_unit = Uom.create({ - 'name': 'TestUnit', - 'category_id': categ_unit.id, - 'factor_inv': 1.0, - 'factor': 1.0, - 'uom_type': 'reference', - 'rounding': 0.000001}) - cls.uom_kunit = Uom.create({ - 'name': 'KTestUnit', - 'category_id': categ_unit.id, - 'factor_inv': 1000.0, - 'factor': 0.001, - 'uom_type': 'bigger', - 'rounding': 0.001}) - cls.uom_munit = Uom.create({ - 'name': 'mTestUnit', - 'category_id': categ_unit.id, - 'factor_inv': 0.001, - 'factor': 1000.0, + cls.uom_unit = cls.env.ref('product.product_uom_unit') + cls.uom_dozen = cls.env.ref('product.product_uom_dozen') + cls.uom_dunit = Uom.create({ + 'name': 'DeciUnit', + 'category_id': cls.uom_unit.category_id.id, + 'factor_inv': 0.1, + 'factor': 10.0, 'uom_type': 'smaller', 'rounding': 0.001}) - cls.uom_weight = Uom.create({ - 'name': 'TestWeight', - 'category_id': weight_unit.id, - 'factor_inv': 1.0, - 'factor': 1.0, - 'uom_type': 'reference', - 'rounding': 0.000001 - }) + cls.uom_weight = cls.env.ref('product.product_uom_kgm') Product = cls.env['product.product'] - cls.product_1 = Product.create({ - 'name': 'TestProduct 1', + cls.product_0 = Product.create({ + 'name': 'Work', + 'type': 'service', 'uom_id': cls.uom_unit.id, 'uom_po_id': cls.uom_unit.id}) + cls.product_1 = Product.create({ + 'name': 'Courage', + 'type': 'consu', + 'uom_id': cls.uom_dunit.id, + 'uom_po_id': cls.uom_dunit.id}) + cls.product_2 = Product.create({ - 'name': 'TestProduct 2', + 'name': 'Wood', 'uom_id': cls.uom_unit.id, 'uom_po_id': cls.uom_unit.id}) cls.product_3 = Product.create({ - 'name': 'TestProduct 3 (KTestUnit)', - 'uom_id': cls.uom_kunit.id, - 'uom_po_id': cls.uom_kunit.id}) + 'name': 'Stone', + 'uom_id': cls.uom_dozen.id, + 'uom_po_id': cls.uom_dozen.id}) + cls.product_4 = Product.create({ - 'name': 'TestProduct 4 (mTestUnit)', - 'uom_id': cls.uom_munit.id, - 'uom_po_id': cls.uom_munit.id}) + 'name': 'Stick', + 'uom_id': cls.uom_dozen.id, + 'uom_po_id': cls.uom_dozen.id}) + cls.product_5 = Product.create({ + 'name': 'Stone Tools', + 'uom_id': cls.uom_unit.id, + 'uom_po_id': cls.uom_unit.id}) + + cls.product_6 = Product.create({ + 'name': 'Door', + 'uom_id': cls.uom_unit.id, + 'uom_po_id': cls.uom_unit.id}) + + cls.prod_att_1 = cls.env['product.attribute'].create({'name': 'Color'}) + cls.prod_attr1_v1 = cls.env['product.attribute.value'].create({'name': 'red', 'attribute_id': cls.prod_att_1.id}) + cls.prod_attr1_v2 = cls.env['product.attribute.value'].create({'name': 'blue', 'attribute_id': cls.prod_att_1.id}) + + cls.product_7_template = cls.env['product.template'].create({ + 'name': 'Sofa', + 'uom_id': cls.uom_unit.id, + 'uom_po_id': cls.uom_unit.id, + 'attribute_line_ids': [(0, 0, { + 'attribute_id': cls.prod_att_1.id, + })] + }) + cls.product_7 = Product.create({ + 'product_tmpl_id': cls.product_7_template.id, + }) + cls.product_7_1 = Product.create({ + 'product_tmpl_id': cls.product_7_template.id, + 'attribute_value_ids': [(6, 0, [cls.prod_attr1_v1.id])], + }) + cls.product_7_2 = Product.create({ + 'product_tmpl_id': cls.product_7_template.id, + 'attribute_value_ids': [(6, 0, [cls.prod_attr1_v2.id])], + }) + + cls.product_8 = Product.create({ + 'name': 'House', + 'uom_id': cls.uom_unit.id, + 'uom_po_id': cls.uom_unit.id}) diff --git a/addons/sale_mrp/tests/__init__.py b/addons/sale_mrp/tests/__init__.py index 5441d7d9b8d..1b510b23429 100644 --- a/addons/sale_mrp/tests/__init__.py +++ b/addons/sale_mrp/tests/__init__.py @@ -2,4 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import test_move_explode -# from . import test_sale_mrp_flow +from . import test_sale_mrp_flow diff --git a/addons/sale_mrp/tests/test_move_explode.py b/addons/sale_mrp/tests/test_move_explode.py index b24f19ed329..14e947bb971 100644 --- a/addons/sale_mrp/tests/test_move_explode.py +++ b/addons/sale_mrp/tests/test_move_explode.py @@ -56,24 +56,3 @@ class TestMoveExplode(common.TransactionCase): #self.assertEqual(len(move_ids), len(bom_component_length[0])) # We should have same amount of move as the component in the phatom bom #self.assertEqual(len(move_ids), 5) - - # def test_00_bom_find(self): - # """Check that _bom_find searches the bom corresponding to the properties passed or takes the bom with the smallest - # sequence.""" - # bom = self.MrpBom._bom_find(product_tmpl_id=self.template.id, product_id=None, properties=[self.mrp_property.id]) - # self.assertEqual(bom, self.bom_prop) - - # def test_00_bom_find(self): - # """Check that _bom_find searches the bom corresponding to the properties passed or takes the bom with the smallest - # sequence.""" - # res = self.MrpBom._bom_find(product_tmpl=self.template) - # self.assertEqual(res.id, self.bom_prop.id) - - - # def test_00_explode(self): - # """Check that explode only takes the lines with the right properties.""" - # bom = self.bom_prop_line - # product = self.product_bom_prop - # res = bom.explode_data(product, 1, properties=self.mrp_property) - # res = set([p['product_id'] for p in res[0]]) - # self.assertEqual(res, set([self.product_A.id, self.product_B.id])) diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py index 9fd80c67bb7..74d1da1e8f8 100644 --- a/addons/sale_mrp/tests/test_sale_mrp_flow.py +++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py @@ -340,7 +340,8 @@ class TestSaleMrpFlow(common.TransactionCase): """ Test delivered quantity on SO based on delivered quantity in pickings.""" # intial so self.partner = self.env.ref('base.res_partner_1') - self.product = self.env.ref('product.product_product_5') + self.product = self.env.ref('mrp.product_product_build_kit') + self.product.invoice_policy = 'delivery' so_vals = { 'partner_id': self.partner.id, 'partner_invoice_id': self.partner.id, diff --git a/addons/stock/tests/common2.py b/addons/stock/tests/common2.py index b12774f7840..c6654dec903 100644 --- a/addons/stock/tests/common2.py +++ b/addons/stock/tests/common2.py @@ -80,7 +80,8 @@ class TestStockCommon(common.TestProductCommon): 'name': 'TestLocation1', 'posx': 3, 'location_id': cls.warehouse_1.lot_stock_id.id, - }) + }) + # Existing data cls.existing_inventories = cls.env['stock.inventory'].search([]) cls.existing_quants = cls.env['stock.quant'].search([]) diff --git a/addons/stock/tests/test_warehouse.py b/addons/stock/tests/test_warehouse.py index 048392f5085..6e71ec74276 100644 --- a/addons/stock/tests/test_warehouse.py +++ b/addons/stock/tests/test_warehouse.py @@ -19,7 +19,7 @@ class TestInventory(TestStockCommon): self.assertEqual(len(inventory.line_ids), 1) self.assertEqual(inventory.line_ids.theoretical_qty, 50.0) self.assertEqual(inventory.line_ids.product_id, self.product_1) - self.assertEqual(inventory.line_ids.product_uom_id, self.uom_unit) + self.assertEqual(inventory.line_ids.product_uom_id, self.product_1.uom_id) # Update the line, set to 35 inventory.line_ids.write({'product_qty': 35.0}) @@ -70,7 +70,7 @@ class TestInventory(TestStockCommon): customer_move = self._create_move(product, self.warehouse_1.lot_stock_id, self.env.ref('stock.stock_location_customers'), product_uom_qty=5) # test move values - self.assertEqual(customer_move.product_uom, self.uom_kunit) + self.assertEqual(customer_move.product_uom, product.uom_id) self.assertEqual(customer_move.location_id, self.warehouse_1.lot_stock_id) self.assertEqual(customer_move.location_dest_id, self.env.ref('stock.stock_location_customers'))