def test_product_produce_uom_2(self): """ Create a bom with a serial tracked component and a pair UoM (2 x unit). The produce wizard should create 2 line with quantity = 1 and UoM = unit for this component. """ unit = self.env.ref("uom.product_uom_unit") categ_unit_id = self.env.ref('uom.product_uom_categ_unit') paire = self.env['uom.uom'].create({ 'name': 'Paire', 'factor_inv': 2, 'uom_type': 'bigger', 'rounding': 0.001, 'category_id': categ_unit_id.id }) binocular = self.env['product.product'].create({ 'name': 'Binocular', 'type': 'product', 'uom_id': unit.id, 'uom_po_id': unit.id }) nocular = self.env['product.product'].create({ 'name': 'Nocular', 'type': 'product', 'tracking': 'serial', 'uom_id': unit.id, 'uom_po_id': unit.id }) bom_binocular = self.env['mrp.bom'].create({ 'product_tmpl_id': binocular.product_tmpl_id.id, 'product_qty': 1, 'product_uom_id': unit.id, 'bom_line_ids': [(0, 0, { 'product_id': nocular.id, 'product_qty': 1, 'product_uom_id': paire.id })] }) mo_form = Form(self.env['mrp.production']) mo_form.product_id = binocular mo_form.bom_id = bom_binocular mo_form.product_uom_id = unit mo_form.product_qty = 1 mo = mo_form.save() mo.action_confirm() self.assertEqual(mo.move_raw_ids.product_uom_qty, 1, 'Quantity should be 1.') self.assertEqual(mo.move_raw_ids.product_uom, paire, 'Move UoM should be "Paire".') # produce product produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) product_produce = produce_form.save() self.assertEqual(product_produce.qty_producing, 1) self.assertEqual(len(product_produce.workorder_line_ids), 2, 'Should be 2 lines since the component tracking is serial and quantity 2.') self.assertEqual(product_produce.workorder_line_ids[0].qty_to_consume, 1, 'Should be 1 unit since the tracking is serial and quantity 2.') self.assertEqual(product_produce.workorder_line_ids[0].product_uom_id, unit, 'Should be the product uom so "unit"') self.assertEqual(product_produce.workorder_line_ids[1].qty_to_consume, 1, 'Should be 1 unit since the tracking is serial and quantity 2.') self.assertEqual(product_produce.workorder_line_ids[1].product_uom_id, unit, 'should be the product uom so "unit"')
def test_rounding(self): """ In previous versions we had rounding and efficiency fields. We check if we can still do the same, but with only the rounding on the UoM""" self.product_6.uom_id.rounding = 1.0 bom_eff = self.env['mrp.bom'].create({'product_id': self.product_6.id, 'product_tmpl_id': self.product_6.product_tmpl_id.id, 'product_qty': 1, 'product_uom_id': self.product_6.uom_id.id, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': self.product_2.id, 'product_qty': 2.03}), (0, 0, {'product_id': self.product_8.id, 'product_qty': 4.16}) ]}) production_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = bom_eff production_form.product_qty = 20 production_form.product_uom_id = self.product_6.uom_id production = production_form.save() production.action_confirm() #Check the production order has the right quantities self.assertEqual(production.move_raw_ids[0].product_qty, 41, 'The quantity should be rounded up') self.assertEqual(production.move_raw_ids[1].product_qty, 84, 'The quantity should be rounded up') # produce product produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': production.id, 'active_ids': [production.id], })) produce_form.qty_producing = 8 produce_wizard = produce_form.save() produce_wizard.do_produce() self.assertEqual(production.move_raw_ids[0].quantity_done, 16, 'Should use half-up rounding when producing') self.assertEqual(production.move_raw_ids[1].quantity_done, 34, 'Should use half-up rounding when producing')
def test_04b_test_planning_date(self): """ Test that workorder are planned at the correct time when setting a start date """ # Remove attendances linked to the calendar, this means that the workcenter is working 24/7 self.env['resource.calendar'].search([]).write({'attendance_ids': [(5, False, False)]}) dining_table = self.env.ref("mrp.product_product_computer_desk") date_start = datetime.now() + timedelta(days=1) production_table_form = Form(self.env['mrp.production']) production_table_form.product_id = dining_table production_table_form.bom_id = self.env.ref("mrp.mrp_bom_desk") production_table_form.product_qty = 1.0 production_table_form.product_uom_id = dining_table.uom_id production_table_form.date_start_wo = date_start production_table = production_table_form.save() production_table.action_confirm() # Create work order production_table.button_plan() workorder = production_table.workorder_ids[0] # Check that the workorder is planned now and that it lasts one hour self.assertEqual(workorder.date_planned_start, date_start, msg="Workorder should be planned tomorrow.") self.assertEqual(workorder.date_planned_finished, date_start + timedelta(hours=1), msg="Workorder should be done one hour later.")
def test_04b_test_planning_date(self): """ Test that workorder are planned at the correct time when setting a start date """ # The workcenter is working 24/7 self.full_availability() dining_table = self.env.ref("mrp.product_product_computer_desk") date_start = datetime.now() + timedelta(days=1) production_table_form = Form(self.env['mrp.production']) production_table_form.product_id = dining_table production_table_form.bom_id = self.env.ref("mrp.mrp_bom_desk") production_table_form.product_qty = 1.0 production_table_form.product_uom_id = dining_table.uom_id production_table_form.date_start_wo = date_start production_table = production_table_form.save() production_table.action_confirm() # Create work order production_table.button_plan() workorder = production_table.workorder_ids[0] # Check that the workorder is planned now and that it lasts one hour self.assertAlmostEqual(workorder.date_planned_start, date_start, delta=timedelta(seconds=1), msg="Workorder should be planned tomorrow.") self.assertAlmostEqual(workorder.date_planned_finished, date_start + timedelta(hours=1), delta=timedelta(seconds=1), msg="Workorder should be done one hour later.")
def test_03b_test_serial_number_defaults(self): """ Check the constraint on the workorder final_lot. The first workorder produces 2/2 units without serial number (serial is only required when you register a component) then the second workorder try to register a serial number. It should be allowed since the first workorder did not specify a seiral number. """ bom = self.env.ref('mrp.mrp_bom_laptop_cust_rout') product = bom.product_tmpl_id.product_variant_id product.tracking = 'serial' lot_1 = self.env['stock.production.lot'].create({ 'product_id': product.id, 'name': 'LOT000001' }) lot_2 = self.env['stock.production.lot'].create({ 'product_id': product.id, 'name': 'LOT000002' }) self.env['stock.production.lot'].create({ 'product_id': product.id, 'name': 'LOT000003' }) mo_form = Form(self.env['mrp.production']) mo_form.product_id = product mo_form.bom_id = bom mo_form.product_qty = 2.0 mo = mo_form.save() mo.action_confirm() mo.button_plan() workorder_0 = mo.workorder_ids[0] workorder_0.record_production() workorder_0.record_production() with self.assertRaises(UserError): workorder_0.record_production() workorder_1 = mo.workorder_ids[1] with Form(workorder_1) as wo: wo.finished_lot_id = lot_1 workorder_1.record_production() self.assertTrue(len(workorder_1.allowed_lots_domain) > 1) with Form(workorder_1) as wo: wo.finished_lot_id = lot_2 workorder_1.record_production() workorder_2 = mo.workorder_ids[2] self.assertEqual(workorder_2.allowed_lots_domain, lot_1 | lot_2) self.assertEqual(workorder_0.finished_workorder_line_ids.qty_done, 2) self.assertFalse(workorder_0.finished_workorder_line_ids.lot_id) self.assertEqual(sum(workorder_1.finished_workorder_line_ids.mapped('qty_done')), 2) self.assertEqual(workorder_1.finished_workorder_line_ids.mapped('lot_id'), lot_1 | lot_2)
def new_mo_laptop(self): form = Form(self.env['mrp.production']) form.product_id = self.laptop form.product_qty = 1 form.bom_id = self.bom_laptop p = form.save() p.action_confirm() p.action_assign() return p
def test_00_production_order_with_accounting(self): self.product_table_sheet.standard_price = 20.0 self.product_table_leg.standard_price = 15.0 self.product_bolt.standard_price = 10.0 self.product_table_leg.tracking = 'none' self.product_table_sheet.tracking = 'none' inventory = self.env['stock.inventory'].create({ 'name': 'Inventory Product Table', 'filter': 'partial', 'line_ids': [(0, 0, { 'product_id': self.product_table_sheet.id, # tracking serial 'product_uom_id': self.product_table_sheet.uom_id.id, 'product_qty': 20, 'location_id': self.source_location_id }), (0, 0, { 'product_id': self.product_table_leg.id, # tracking lot 'product_uom_id': self.product_table_leg.uom_id.id, 'product_qty': 20, 'location_id': self.source_location_id }), (0, 0, { 'product_id': self.product_bolt.id, 'product_uom_id': self.product_bolt.uom_id.id, 'product_qty': 20, 'location_id': self.source_location_id }), (0, 0, { 'product_id': self.product_screw.id, 'product_uom_id': self.product_screw.uom_id.id, 'product_qty': 200000, 'location_id': self.source_location_id }), ] }) inventory.action_validate bom = self.env.ref('mrp.mrp_bom_desk').copy() bom.routing_id = False # TODO: extend the test later with the necessary operations production_table_form = Form(self.env['mrp.production']) production_table_form.product_id = self.dining_table production_table_form.bom_id = bom production_table_form.product_qty = 5.0 production_table = production_table_form.save() production_table.extra_cost = 20 production_table.action_confirm() produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': production_table.id, 'active_ids': [production_table.id], })) produce_form.qty_producing = 1.0 produce_wizard = produce_form.save() produce_wizard.do_produce() production_table.post_inventory() move_value = production_table.move_finished_ids.filtered(lambda x: x.state == "done").value # 1 table head at 20 + 4 table leg at 15 + 4 bolt at 10 + 10 screw at 10 + 1*20 (extra cost) self.assertEqual(move_value, 141, 'Thing should have the correct price')
def test_access_rights_user(self): man_order_form = Form(self.env['mrp.production'].sudo(self.user_mrp_user)) man_order_form.product_id = self.product_4 man_order_form.product_qty = 5.0 man_order_form.bom_id = self.bom_1 man_order_form.location_src_id = self.location_1 man_order_form.location_dest_id = self.warehouse_1.wh_output_stock_loc_id man_order = man_order_form.save() man_order.action_cancel() self.assertEqual(man_order.state, 'cancel', "Production order should be in cancel state.") man_order.unlink()
def test_03_test_serial_number_defaults(self): """ Test that the correct serial number is suggested on consecutive work orders. """ laptop = self.env.ref("product.product_product_25") graphics_card = self.env.ref("product.product_product_24") unit = self.env.ref("uom.product_uom_unit") three_step_routing = self.env.ref("mrp.mrp_routing_1") laptop.tracking = 'serial' bom_laptop = self.env['mrp.bom'].create({ 'product_tmpl_id': laptop.product_tmpl_id.id, 'product_qty': 1, 'product_uom_id': unit.id, 'bom_line_ids': [(0, 0, { 'product_id': graphics_card.id, 'product_qty': 1, 'product_uom_id': unit.id })], 'routing_id': three_step_routing.id }) mo_laptop_form = Form(self.env['mrp.production']) mo_laptop_form.product_id = laptop mo_laptop_form.bom_id = bom_laptop mo_laptop_form.product_qty = 3 mo_laptop = mo_laptop_form.save() mo_laptop.action_confirm() mo_laptop.button_plan() workorders = mo_laptop.workorder_ids self.assertEqual(len(workorders), 3) workorders[0].button_start() serial_a = self.env['stock.production.lot'].create({'product_id': laptop.id}) workorders[0].final_lot_id = serial_a workorders[0].record_production() serial_b = self.env['stock.production.lot'].create({'product_id': laptop.id}) workorders[0].final_lot_id = serial_b workorders[0].record_production() serial_c = self.env['stock.production.lot'].create({'product_id': laptop.id}) workorders[0].final_lot_id = serial_c workorders[0].record_production() self.assertEqual(workorders[0].state, 'done') for workorder in workorders - workorders[0]: self.assertEqual(workorder.final_lot_id, serial_a) workorder.record_production() self.assertEqual(workorder.final_lot_id, serial_b) workorder.record_production() self.assertEqual(workorder.final_lot_id, serial_c) workorder.record_production() self.assertEqual(workorder.state, 'done')
def test_planning_3(self): """ Plan some manufacturing orders with 1 workorder on 1 workcenter the first workorder will be hard set in the future to see if the second one take the free slot before on the calendar calendar after first mo : [ ][mo1] calendar after second mo: [mo2][mo1] """ self.workcenter_1.alternative_workcenter_ids = self.wc_alt_1 | self.wc_alt_2 self.env['mrp.workcenter'].search([]).write({'tz': 'UTC'}) # compute all date in UTC mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product_4 mo_form.bom_id = self.planning_bom mo_form.product_qty = 1 mo_form.date_start_wo = datetime(2019, 5, 13, 14, 0, 0, 0) mo = mo_form.save() start = mo.date_start_wo mo.action_confirm() mo.button_plan() self.assertEqual(mo.workorder_ids[0].workcenter_id, self.wc_alt_2, "wrong workcenter") wo1_start = mo.workorder_ids[0].date_planned_start wo1_stop = mo.workorder_ids[0].date_planned_finished self.assertAlmostEqual(wo1_start, start, delta=timedelta(seconds=10), msg="Wrong plannification") self.assertAlmostEqual(wo1_stop, start + timedelta(minutes=85.58), delta=timedelta(seconds=10), msg="Wrong plannification") # second MO should be plan before as there is a free slot before mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product_4 mo_form.bom_id = self.planning_bom mo_form.product_qty = 1 mo_form.date_start_wo = datetime(2019, 5, 13, 9, 0, 0, 0) mo = mo_form.save() mo.action_confirm() mo.button_plan() self.assertEqual(mo.workorder_ids[0].workcenter_id, self.wc_alt_2, "wrong workcenter") wo1_start = mo.workorder_ids[0].date_planned_start wo1_stop = mo.workorder_ids[0].date_planned_finished self.assertAlmostEqual(wo1_start, datetime(2019, 5, 13, 9, 0, 0, 0), delta=timedelta(seconds=10), msg="Wrong plannification") self.assertAlmostEqual(wo1_stop, datetime(2019, 5, 13, 9, 0, 0, 0) + timedelta(minutes=85.59), delta=timedelta(seconds=10), msg="Wrong plannification")
def test_access_rights_manager(self): """ Checks an MRP manager can create, confirm and cancel a manufacturing order. """ man_order_form = Form(self.env['mrp.production'].sudo(self.user_mrp_manager)) man_order_form.product_id = self.product_4 man_order_form.product_qty = 5.0 man_order_form.bom_id = self.bom_1 man_order_form.location_src_id = self.location_1 man_order_form.location_dest_id = self.warehouse_1.wh_output_stock_loc_id man_order = man_order_form.save() man_order.action_confirm() man_order.action_cancel() self.assertEqual(man_order.state, 'cancel', "Production order should be in cancel state.") man_order.unlink()
def test_planning_1(self): """ Testing planning workorder with alternative workcenters Plan 6 times the same MO, the workorders should be split accross workcenters The 3 workcenters are free, this test plans 3 workorder in a row then three next. The workcenters have not exactly the same parameters (efficiency, start time) so the the last 3 workorder are not dispatched like the 3 first. At the end of the test, the calendars will look like: - calendar wc1 :[mo1][mo4] - calendar wc2 :[mo2 ][mo5 ] - calendar wc3 :[mo3 ][mo6 ]""" self.workcenter_1.alternative_workcenter_ids = self.wc_alt_1 | self.wc_alt_2 workcenters = [self.wc_alt_2, self.wc_alt_1, self.workcenter_1] for i in range(3): # Create an MO for product4 mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product_4 mo_form.bom_id = self.planning_bom mo_form.product_qty = 1 mo = mo_form.save() mo.action_confirm() mo.button_plan() # Check that workcenters change self.assertEqual(mo.workorder_ids.workcenter_id, workcenters[i], "wrong workcenter %d" % i) for i in range(3): # Planning 3 more should choose workcenters in opposite order as # - wc_alt_2 as the best efficiency # - wc_alt_1 take a little less start time # - workcenter_1 is the worst mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product_4 mo_form.bom_id = self.planning_bom mo_form.product_qty = 1 mo = mo_form.save() mo.action_confirm() mo.button_plan() # Check that workcenters change self.assertEqual(mo.workorder_ids.workcenter_id, workcenters[i], "wrong workcenter %d" % i)
def test_planning_4(self): """ Plan a manufacturing orders with 1 workorder on 1 workcenter the workcenter calendar is empty. which means the workcenter is never available. Planning a workorder on it should raise an error""" self.workcenter_1.alternative_workcenter_ids = self.wc_alt_1 | self.wc_alt_2 self.env['resource.calendar'].search([]).write({'attendance_ids': [(5, False, False)]}) mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product_4 mo_form.bom_id = self.planning_bom mo_form.product_qty = 1 mo = mo_form.save() mo.action_confirm() with self.assertRaises(UserError): mo.button_plan()
def test_product_produce_uom(self): """ Produce a finished product tracked by serial number. Set another UoM on the bom. The produce wizard should keep the UoM of the product (unit) and quantity = 1.""" plastic_laminate = self.env.ref('mrp.product_product_plastic_laminate') bom = self.env.ref('mrp.mrp_bom_plastic_laminate') dozen = self.env.ref('uom.product_uom_dozen') unit = self.env.ref('uom.product_uom_unit') plastic_laminate.tracking = 'serial' mo_form = Form(self.env['mrp.production']) mo_form.product_id = plastic_laminate mo_form.bom_id = bom mo_form.product_uom_id = dozen mo_form.product_qty = 1 mo = mo_form.save() final_product_lot = self.env['stock.production.lot'].create({ 'name': 'lot1', 'product_id': plastic_laminate.id, }) mo.action_confirm() mo.action_assign() self.assertEqual(mo.move_raw_ids.product_qty, 12, '12 units should be reserved.') # produce product produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.final_lot_id = final_product_lot product_produce = produce_form.save() self.assertEqual(product_produce.qty_producing, 1) self.assertEqual(product_produce.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.') product_produce.final_lot_id = final_product_lot.id product_produce.do_produce() move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) self.assertEqual(move_line_raw.qty_done, 1) self.assertEqual(move_line_raw.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.') move_line_finished = mo.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) self.assertEqual(move_line_finished.qty_done, 1) self.assertEqual(move_line_finished.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
def test_reuse_unbuilt_usn(self): """ Produce a SN product Unbuilt it Produce a new SN product with same lot """ mo, bom, p_final, p1, p2 = self.generate_mo(qty_base_1=1, qty_base_2=1, qty_final=1, tracking_final='serial') stock_location = self.env.ref('stock.stock_location_stock') self.env['stock.quant']._update_available_quantity(p1, stock_location, 1) self.env['stock.quant']._update_available_quantity(p2, stock_location, 1) mo.action_assign() lot = self.env['stock.lot'].create({ 'name': 'lot1', 'product_id': p_final.id, 'company_id': self.env.company.id, }) mo_form = Form(mo) mo_form.qty_producing = 1.0 mo_form.lot_producing_id = lot mo = mo_form.save() mo.button_mark_done() unbuild_form = Form(self.env['mrp.unbuild']) unbuild_form.mo_id = mo unbuild_form.lot_id = lot unbuild_form.save().action_unbuild() mo_form = Form(self.env['mrp.production']) mo_form.bom_id = bom mo = mo_form.save() mo.action_confirm() with self.assertLogs(level="WARNING") as log_catcher: mo_form = Form(mo) mo_form.qty_producing = 1.0 mo_form.lot_producing_id = lot mo = mo_form.save() _logger.warning('Dummy') self.assertEqual(len(log_catcher.output), 1, "Useless warnings: \n%s" % "\n".join(log_catcher.output[:-1])) mo.button_mark_done() self.assertEqual(mo.state, 'done')
def test_unbuild_account_00(self): """Test when after unbuild, the journal entries are the reversal of the journal entries created when produce the product. """ # build production_form = Form(self.env['mrp.production']) production_form.product_id = self.product_A production_form.bom_id = self.bom production_form.product_qty = 1 production = production_form.save() production.action_confirm() mo_form = Form(production) mo_form.qty_producing = 1 production = mo_form.save() production._post_inventory() production.button_mark_done() # finished product move productA_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product A'), ('credit', '=', 0)]) productA_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product A'), ('debit', '=', 0)]) self.assertEqual(productA_debit_line.account_id, self.stock_valuation_account) self.assertEqual(productA_credit_line.account_id, self.stock_input_account) # component move productB_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product B'), ('credit', '=', 0)]) productB_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'MO%Product B'), ('debit', '=', 0)]) self.assertEqual(productB_debit_line.account_id, self.stock_output_account) self.assertEqual(productB_credit_line.account_id, self.stock_valuation_account) # unbuild res_dict = production.button_unbuild() wizard = Form(self.env[res_dict['res_model']].with_context(res_dict['context'])).save() wizard.action_validate() # finished product move productA_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product A'), ('credit', '=', 0)]) productA_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product A'), ('debit', '=', 0)]) self.assertEqual(productA_debit_line.account_id, self.stock_input_account) self.assertEqual(productA_credit_line.account_id, self.stock_valuation_account) # component move productB_debit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product B'), ('credit', '=', 0)]) productB_credit_line = self.env['account.move.line'].search([('ref', 'ilike', 'UB%Product B'), ('debit', '=', 0)]) self.assertEqual(productB_debit_line.account_id, self.stock_valuation_account) self.assertEqual(productB_credit_line.account_id, self.stock_output_account)
def test_product_produce_uom(self): plastic_laminate = self.env.ref('mrp.product_product_plastic_laminate') bom = self.env.ref('mrp.mrp_bom_plastic_laminate') dozen = self.env.ref('uom.product_uom_dozen') unit = self.env.ref('uom.product_uom_unit') plastic_laminate.tracking = 'serial' mo_form = Form(self.env['mrp.production']) mo_form.product_id = plastic_laminate mo_form.bom_id = bom mo_form.product_uom_id = dozen mo_form.product_qty = 1 mo = mo_form.save() final_product_lot = self.env['stock.production.lot'].create({ 'name': 'lot1', 'product_id': plastic_laminate.id, }) mo.action_confirm() mo.action_assign() self.assertEqual(mo.move_raw_ids.product_qty, 12, '12 units should be reserved.') # produce product produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.final_lot_id = final_product_lot product_produce = produce_form.save() self.assertEqual(product_produce.qty_producing, 1) self.assertEqual(product_produce.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.') product_produce.final_lot_id = final_product_lot.id product_produce.do_produce() move_line_raw = mo.move_raw_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) self.assertEqual(move_line_raw.qty_done, 1) self.assertEqual(move_line_raw.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.') move_line_finished = mo.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.qty_done) self.assertEqual(move_line_finished.qty_done, 1) self.assertEqual(move_line_finished.product_uom_id, unit, 'Should be 1 unit since the tracking is serial.')
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() self.bom_3.ready_to_produce = 'all_available' production_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = self.bom_3 production_form.product_qty = 5.0 production_form.product_uom_id = self.product_6.uom_id production_2 = production_form.save() production_2.action_confirm() production_2.action_assign() # check sub product availability state is waiting self.assertEqual(production_2.reservation_state, 'confirmed', '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.reservation_state, 'confirmed', '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.reservation_state, 'assigned', 'Production order should be availability for assigned state')
def test_workcenter_same_analytic_account(self): """Test when workcenter and MO are using the same analytic account, no duplicated lines will be post. """ # set wc analytic account to be the same of the one on the bom self.workcenter.costs_hour_account_id = self.analytic_account # create a mo mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product mo_form.bom_id = self.bom mo_form.product_qty = 10.0 mo_form.analytic_account_id = self.analytic_account mo = mo_form.save() mo.action_confirm() self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0) # change duration to 60 with mo_form.workorder_ids.edit(0) as line_edit: line_edit.duration = 60.0 mo_form.save() self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -10.0) self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0) # change duration to 120 with mo_form.workorder_ids.edit(0) as line_edit: line_edit.duration = 120.0 mo_form.save() self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0) self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0) # mark as done mo_form.qty_producing = 10.0 mo_form.save() mo.button_mark_done() self.assertEqual(mo.state, 'done') self.assertEqual(mo.workorder_ids.mo_analytic_account_line_id.amount, -20.0) self.assertEqual(len(mo.workorder_ids.wc_analytic_account_line_id), 0)
def generate_mo(self, tracking_final='none', tracking_base_1='none', tracking_base_2='none', qty_final=5, qty_base_1=4, qty_base_2=1, picking_type_id=False, consumption=False): """ This function generate a manufacturing order with one final product and two consumed product. Arguments allows to choose the tracking/qty for each different products. It returns the MO, used bom and the tree products. """ product_to_build = self.env['product.product'].create({ 'name': 'Young Tom', 'type': 'product', 'tracking': tracking_final, }) product_to_use_1 = self.env['product.product'].create({ 'name': 'Botox', 'type': 'product', 'tracking': tracking_base_1, }) product_to_use_2 = self.env['product.product'].create({ 'name': 'Old Tom', 'type': 'product', 'tracking': tracking_base_2, }) bom_1 = self.env['mrp.bom'].create({ 'product_id': product_to_build.id, 'product_tmpl_id': product_to_build.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1.0, 'type': 'normal', 'consumption': consumption if consumption else 'flexible', 'bom_line_ids': [ (0, 0, {'product_id': product_to_use_2.id, 'product_qty': qty_base_2}), (0, 0, {'product_id': product_to_use_1.id, 'product_qty': qty_base_1}) ]}) mo_form = Form(self.env['mrp.production']) if picking_type_id: mo_form.picking_type_id = picking_type_id mo_form.product_id = product_to_build mo_form.bom_id = bom_1 mo_form.product_qty = qty_final mo = mo_form.save() mo.action_confirm() return mo, bom_1, product_to_build, product_to_use_1, product_to_use_2
def test_production_avialability(self): """ Checks the availability of a production order through mutliple calls to `action_assign`. """ 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() self.bom_3.ready_to_produce = 'all_available' production_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = self.bom_3 production_form.product_qty = 5.0 production_form.product_uom_id = self.product_6.uom_id production_2 = production_form.save() production_2.action_confirm() production_2.action_assign() # check sub product availability state is waiting self.assertEqual(production_2.reservation_state, 'confirmed', 'Production order should be availability for waiting state') # Update Inventory self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.product_2.id, 'inventory_quantity': 2.0, 'location_id': self.ref('stock.stock_location_14') }) production_2.action_assign() # check sub product availability state is partially available self.assertEqual(production_2.reservation_state, 'confirmed', 'Production order should be availability for partially available state') # Update Inventory self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.product_2.id, 'inventory_quantity': 5.0, 'location_id': self.ref('stock.stock_location_14') }) production_2.action_assign() # check sub product availability state is assigned self.assertEqual(production_2.reservation_state, 'assigned', 'Production order should be availability for assigned state')
def test_changing_mo_analytic_account(self): """ Check if the MO account analytic lines are correctly updated after the change of the MO account analytic. """ # create a mo mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product mo_form.bom_id = self.bom mo_form.product_qty = 1 mo_form.analytic_account_id = self.analytic_account mo = mo_form.save() mo.action_confirm() self.assertEqual(mo.state, 'confirmed') self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0) # Mark as done wizard_dict = mo.button_mark_done() Form(self.env[(wizard_dict.get('res_model'))].with_context( wizard_dict['context'])).save().process() self.assertEqual(mo.state, 'done') self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 1) # Create a new analytic account new_analytic_account = self.env['account.analytic.account'].create( {'name': 'test_analytic_account_2'}) # Change the MO analytic account mo.analytic_account_id = new_analytic_account self.assertEqual( mo.move_raw_ids.analytic_account_line_id.account_id.id, new_analytic_account.id) #Get the MO analytic account lines mo_analytic_account_lines = mo.move_raw_ids.analytic_account_line_id mo.analytic_account_id = False # Check that the MO analytic account lines are deleted self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0) self.assertFalse(mo_analytic_account_lines.exists()) # Check that the AA lines are recreated correctly if we delete the AA, save the MO, and assign a new one mo.analytic_account_id = self.analytic_account self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 1)
def test_04_test_planning_date(self): """ Test that workorder are planned at the correct time. """ # Remove attendances linked to the calendar, this means that the workcenter is working 24/7 self.env['resource.calendar'].search([]).write({'attendance_ids': [(5, False, False)]}) dining_table = self.env.ref("mrp.product_product_computer_desk") production_table_form = Form(self.env['mrp.production']) production_table_form.product_id = dining_table production_table_form.bom_id = self.env.ref("mrp.mrp_bom_desk") production_table_form.product_qty = 1.0 production_table_form.product_uom_id = dining_table.uom_id production_table = production_table_form.save() production_table.action_confirm() # Create work order production_table.button_plan() workorder = production_table.workorder_ids[0] # Check that the workorder is planned now and that it lasts one hour self.assertAlmostEqual(workorder.date_planned_start, datetime.now(), delta=timedelta(seconds=10), msg="Workorder should be planned now.") self.assertAlmostEqual(workorder.date_planned_finished, datetime.now() + timedelta(hours=1), delta=timedelta(seconds=10), msg="Workorder should be done in an hour.")
def generate_mo(self, tracking_final='none', tracking_base_1='none', tracking_base_2='none', qty_final=5, qty_base_1=4, qty_base_2=1): """ This function generate a manufacturing order with one final product and two consumed product. Arguments allows to choose the tracking/qty for each different products. It returns the MO, used bom and the tree products. """ product_to_build = self.env['product.product'].create({ 'name': 'Young Tom', 'type': 'product', 'tracking': tracking_final, }) product_to_use_1 = self.env['product.product'].create({ 'name': 'Botox', 'type': 'product', 'tracking': tracking_base_1, }) product_to_use_2 = self.env['product.product'].create({ 'name': 'Old Tom', 'type': 'product', 'tracking': tracking_base_2, }) bom_1 = self.env['mrp.bom'].create({ 'product_id': product_to_build.id, 'product_tmpl_id': product_to_build.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': product_to_use_2.id, 'product_qty': qty_base_2}), (0, 0, {'product_id': product_to_use_1.id, 'product_qty': qty_base_1}) ]}) mo_form = Form(self.env['mrp.production']) mo_form.product_id = product_to_build mo_form.bom_id = bom_1 mo_form.product_qty = qty_final mo = mo_form.save() mo.action_confirm() return mo, bom_1, product_to_build, product_to_use_1, product_to_use_2
def test_mo_analytic(self): """Test the amount on analytic line will change when consumed qty of the component changed. """ # create a mo mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product mo_form.bom_id = self.bom mo_form.product_qty = 10.0 mo_form.analytic_account_id = self.analytic_account mo = mo_form.save() mo.action_confirm() self.assertEqual(mo.state, 'confirmed') self.assertEqual(len(mo.move_raw_ids.analytic_account_line_id), 0) # increase qty_producing to 5.0 mo_form = Form(mo) mo_form.qty_producing = 5.0 mo_form.save() self.assertEqual(mo.state, 'progress') self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -50.0) # increase qty_producing to 10.0 mo_form = Form(mo) mo_form.qty_producing = 10.0 mo_form.save() # Hack to bypass test doing strange things mo._set_qty_producing() mo.workorder_ids.button_finish() self.assertEqual(mo.state, 'to_close') self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -100.0) # mark as done mo.button_mark_done() self.assertEqual(mo.state, 'done') self.assertEqual(mo.move_raw_ids.analytic_account_line_id.amount, -100.0)
def test_rounding(self): """ Checks we round up when bringing goods to produce and round half-up when producing. This implementation allows to implement an efficiency notion (see rev 347f140fe63612ee05e). """ self.product_6.uom_id.rounding = 1.0 bom_eff = self.env['mrp.bom'].create({ 'product_id': self.product_6.id, 'product_tmpl_id': self.product_6.product_tmpl_id.id, 'product_qty': 1, 'product_uom_id': self.product_6.uom_id.id, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': self.product_2.id, 'product_qty': 2.03}), (0, 0, {'product_id': self.product_8.id, 'product_qty': 4.16}) ] }) production_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = bom_eff production_form.product_qty = 20 production_form.product_uom_id = self.product_6.uom_id production = production_form.save() production.action_confirm() #Check the production order has the right quantities self.assertEqual(production.move_raw_ids[0].product_qty, 41, 'The quantity should be rounded up') self.assertEqual(production.move_raw_ids[1].product_qty, 84, 'The quantity should be rounded up') # produce product produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': production.id, 'active_ids': [production.id], })) produce_form.qty_producing = 8 produce_wizard = produce_form.save() produce_wizard.do_produce() self.assertEqual(production.move_raw_ids[0].quantity_done, 16, 'Should use half-up rounding when producing') self.assertEqual(production.move_raw_ids[1].quantity_done, 34, 'Should use half-up rounding when producing')
def test_planning_2(self): """ Plan some manufacturing orders with 2 workorders each Batch size of the operation will influence start dates of workorders The first unit to be produced can go the second workorder before finishing to produce the second unit. calendar wc1 : [q1][q2] calendar wc2 : [q1][q2]""" self.workcenter_1.alternative_workcenter_ids = self.wc_alt_1 | self.wc_alt_2 self.planning_bom.routing_id = self.routing_2 # Allow second workorder to start once the first one is not ended yet self.operation_2.batch = 'yes' self.operation_2.batch_size = 1 self.env['mrp.workcenter'].search([]).write({'capacity': 1}) # workcenters work 24/7 self.full_availability() mo_form = Form(self.env['mrp.production']) mo_form.product_id = self.product_4 mo_form.bom_id = self.planning_bom mo_form.product_qty = 2 mo = mo_form.save() mo.action_confirm() plan = datetime.now() mo.button_plan() self.assertEqual(mo.workorder_ids[0].workcenter_id, self.wc_alt_2, "wrong workcenter") self.assertEqual(mo.workorder_ids[1].workcenter_id, self.wc_alt_1, "wrong workcenter") duration1 = self.operation_2.time_cycle * 100.0 / self.wc_alt_2.time_efficiency + self.wc_alt_2.time_start duration2 = 2.0 * self.operation_2.time_cycle * 100.0 / self.wc_alt_1.time_efficiency + self.wc_alt_1.time_start + self.wc_alt_1.time_stop wo2_start = mo.workorder_ids[1].date_planned_start wo2_stop = mo.workorder_ids[1].date_planned_finished wo2_start_theo = self.wc_alt_2.resource_calendar_id.plan_hours(duration1 / 60.0, plan, compute_leaves=False, resource=self.wc_alt_2.resource_id) wo2_stop_theo = self.wc_alt_1.resource_calendar_id.plan_hours(duration2 / 60.0, wo2_start, compute_leaves=False, resource=self.wc_alt_2.resource_id) self.assertAlmostEqual(wo2_start, wo2_start_theo, delta=timedelta(seconds=10), msg="Wrong plannification") self.assertAlmostEqual(wo2_stop, wo2_stop_theo, delta=timedelta(seconds=10), msg="Wrong plannification")
def test_00_production_order_with_accounting(self): self.product_table_sheet.standard_price = 20.0 self.product_table_leg.standard_price = 15.0 self.product_bolt.standard_price = 10.0 self.product_screw.standard_price = 0.1 self.product_table_leg.tracking = 'none' self.product_table_sheet.tracking = 'none' inventory = self.env['stock.inventory'].create({ 'name': 'Inventory Product Table', 'line_ids': [ ( 0, 0, { 'product_id': self.product_table_sheet.id, # tracking serial 'product_uom_id': self.product_table_sheet.uom_id.id, 'product_qty': 20, 'location_id': self.source_location_id }), ( 0, 0, { 'product_id': self.product_table_leg.id, # tracking lot 'product_uom_id': self.product_table_leg.uom_id.id, 'product_qty': 20, 'location_id': self.source_location_id }), (0, 0, { 'product_id': self.product_bolt.id, 'product_uom_id': self.product_bolt.uom_id.id, 'product_qty': 20, 'location_id': self.source_location_id }), (0, 0, { 'product_id': self.product_screw.id, 'product_uom_id': self.product_screw.uom_id.id, 'product_qty': 200000, 'location_id': self.source_location_id }), ] }) inventory.action_validate bom = self.mrp_bom_desk.copy() bom.routing_id = False # TODO: extend the test later with the necessary operations production_table_form = Form(self.env['mrp.production']) production_table_form.product_id = self.dining_table production_table_form.bom_id = bom production_table_form.product_qty = 5.0 production_table = production_table_form.save() production_table.extra_cost = 20 production_table.action_confirm() produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': production_table.id, 'active_ids': [production_table.id], })) produce_form.qty_producing = 1.0 produce_wizard = produce_form.save() produce_wizard.do_produce() production_table.post_inventory() move_value = production_table.move_finished_ids.filtered( lambda x: x.state == "done").stock_valuation_layer_ids.value # 1 table head at 20 + 4 table leg at 15 + 4 bolt at 10 + 10 screw at 10 + 1*20 (extra cost) self.assertEqual(move_value, 141, 'Thing should have the correct price')
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_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = self.bom_3 production_form.product_qty = 24 production_form.product_uom_id = self.product_6.uom_id production_product_6 = production_form.save() production_product_6.action_confirm() 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. # Check it created a purchase order move_raw_product4 = production_product_6.move_raw_ids.filtered( lambda x: x.product_id == self.product_4) produce_product_4 = self.env['mrp.production'].search([ ('product_id', '=', self.product_4.id), ('move_dest_ids', '=', move_raw_product4[0].id) ]) # produce product self.assertEqual(produce_product_4.reservation_state, 'confirmed', "Consume material not available") # Create production order # ------------------------- # Product 4 96 Unit # Product2 48 Unit # --------------------- # Update Inventory self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.product_2.id, 'inventory_quantity': 48, 'location_id': self.warehouse.lot_stock_id.id, }) 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.reservation_state, 'assigned', "Consume material not available") # produce product4 # --------------- produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': produce_product_4.id, 'active_ids': [produce_product_4.id], })) produce_form.qty_producing = produce_product_4.product_qty product_produce = produce_form.save() product_produce.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') # Produce product 6 # ------------------ # Update Inventory self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.product_2.id, 'inventory_quantity': 12, 'location_id': self.warehouse.lot_stock_id.id, }) production_product_6.action_assign() # ------------------------------------ self.assertEqual(production_product_6.reservation_state, 'assigned', "Consume material not available") produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': production_product_6.id, 'active_ids': [production_product_6.id], })) produce_form.qty_producing = production_product_6.product_qty product_produce = produce_form.save() product_produce.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.')
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).action_start() stock_inv_product_2.action_validate() stock_inv_product_4.action_validate() #Create Manufacturing order. production_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = self.bom_3 production_form.product_qty = 12 production_form.product_uom_id = self.product_6.uom_id production_3 = production_form.save() production_3.action_confirm() production_3.action_assign() # Check Manufacturing order's availability. self.assertEqual( production_3.reservation_state, '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 ?. scrap_id = 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 }) with self.assertRaises(except_orm): scrap_id.do_scrap() # 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 })
def test_unbuild_with_routes(self): """ This test creates a MO of a stockable product (Table). A new route for rule QC/Unbuild -> Stock is created with Warehouse -> True. The unbuild order should revert the consumed components into QC/Unbuild location for quality check and then a picking should be generated for transferring components from QC/Unbuild location to stock. """ StockQuant = self.env['stock.quant'] ProductObj = self.env['product.product'] # Create new QC/Unbuild location warehouse = self.env.ref('stock.warehouse0') unbuild_location = self.env['stock.location'].create({ 'name': 'QC/Unbuild', 'usage': 'internal', 'location_id': warehouse.view_location_id.id }) # Create a product route containing a stock rule that will move product from QC/Unbuild location to stock product_route = self.env['stock.location.route'].create({ 'name': 'QC/Unbuild -> Stock', 'warehouse_selectable': True, 'warehouse_ids': [(4, warehouse.id)], 'rule_ids': [(0, 0, { 'name': 'Send Matrial QC/Unbuild -> Stock', 'action': 'push', 'picking_type_id': self.ref('stock.picking_type_internal'), 'location_src_id': unbuild_location.id, 'location_id': self.stock_location.id, })], }) # Create a stockable product and its components finshed_product = ProductObj.create({ 'name': 'Table', 'type': 'product', }) component1 = ProductObj.create({ 'name': 'Table head', 'type': 'product', }) component2 = ProductObj.create({ 'name': 'Table stand', 'type': 'product', }) # Create bom and add components bom = self.env['mrp.bom'].create({ 'product_id': finshed_product.id, 'product_tmpl_id': finshed_product.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': component1.id, 'product_qty': 1}), (0, 0, {'product_id': component2.id, 'product_qty': 1}) ]}) # Set on hand quantity StockQuant._update_available_quantity(component1, self.stock_location, 1) StockQuant._update_available_quantity(component2, self.stock_location, 1) # Create mo mo_form = Form(self.env['mrp.production']) mo_form.product_id = finshed_product mo_form.bom_id = bom mo_form.product_uom_id = finshed_product.uom_id mo_form.product_qty = 1.0 mo = mo_form.save() self.assertEqual(len(mo), 1, 'MO should have been created') mo.action_confirm() mo.action_assign() # Produce the final product produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.qty_producing = 1.0 produce_wizard = produce_form.save() produce_wizard.do_produce() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild self.assertEqual(StockQuant._get_available_quantity(finshed_product, self.stock_location), 1, 'Table should be available in stock') self.assertEqual(StockQuant._get_available_quantity(component1, self.stock_location), 0, 'Table head should not be available in stock') self.assertEqual(StockQuant._get_available_quantity(component2, self.stock_location), 0, 'Table stand should not be available in stock') # --------------------------------------------------- # Unbuild # --------------------------------------------------- # Create an unbuild order of the finished product and set the destination loacation = QC/Unbuild self.env['mrp.unbuild'].create({ 'product_id': finshed_product.id, 'bom_id': bom.id, 'product_qty': 1.0, 'product_uom_id': self.uom_unit.id, 'mo_id': mo.id, 'location_id': self.stock_location.id, 'location_dest_id': unbuild_location.id }).action_unbuild() # Check the available quantity of components and final product in stock self.assertEqual(StockQuant._get_available_quantity(finshed_product, self.stock_location), 0, 'Table should not be available in stock as it is unbuild') self.assertEqual(StockQuant._get_available_quantity(component1, self.stock_location), 0, 'Table head should not be available in stock as it is in QC/Unbuild location') self.assertEqual(StockQuant._get_available_quantity(component2, self.stock_location), 0, 'Table stand should not be available in stock as it is in QC/Unbuild location') # Find new generated picking picking = self.env['stock.picking'].search([('product_id', 'in', [component1.id, component2.id])]) self.assertEqual(picking.location_id.id, unbuild_location.id, 'Wrong source location in picking') self.assertEqual(picking.location_dest_id.id, self.stock_location.id, 'Wrong destination location in picking') # Transfer it for ml in picking.move_ids_without_package: ml.quantity_done = 1 picking.action_done() # Check the available quantity of components and final product in stock self.assertEqual(StockQuant._get_available_quantity(finshed_product, self.stock_location), 0, 'Table should not be available in stock') self.assertEqual(StockQuant._get_available_quantity(component1, self.stock_location), 1, 'Table head should be available in stock as the picking is transferred') self.assertEqual(StockQuant._get_available_quantity(component2, self.stock_location), 1, 'Table stand should be available in stock as the picking is transferred')
def test_basic(self): """ Basic order test: no routing (thus no workorders), no lot """ self.product_1.type = 'product' self.product_2.type = 'product' 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_validate() test_date_planned = Dt.now() - timedelta(days=1) test_quantity = 2.0 self.bom_1.routing_id = False man_order_form = Form(self.env['mrp.production'].sudo(self.user_mrp_user)) man_order_form.product_id = self.product_4 man_order_form.bom_id = self.bom_1 man_order_form.product_uom_id = self.product_4.uom_id man_order_form.product_qty = test_quantity man_order_form.date_planned_start = test_date_planned man_order_form.location_src_id = self.location_1 man_order_form.location_dest_id = self.warehouse_1.wh_output_stock_loc_id man_order = man_order_form.save() self.assertEqual(man_order.state, 'draft', "Production order should be in draft state.") man_order.action_confirm() 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, 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, 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_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': man_order.id, 'active_ids': [man_order.id], })) produce_form.qty_producing = 1.0 produce_wizard = produce_form.save() 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_landed_cost_on_mrp(self): # Initial inventory quants = self.env['stock.quant'].with_context( inventory_mode=True).create({ 'product_id': self.product_component1.id, 'inventory_quantity': 500, 'location_id': self.warehouse_1.lot_stock_id.id, }) quants |= self.env['stock.quant'].with_context( inventory_mode=True).create({ 'product_id': self.product_component2.id, 'inventory_quantity': 500, 'location_id': self.warehouse_1.lot_stock_id.id, }) quants.action_apply_inventory() man_order_form = Form(self.env['mrp.production'].with_user( self.allow_user)) man_order_form.product_id = self.product_refrigerator man_order_form.bom_id = self.bom_refri man_order_form.product_qty = 2.0 man_order = man_order_form.save() self.assertEqual(man_order.state, 'draft', "Production order should be in draft state.") man_order.action_confirm() 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.product_id, self.product_refrigerator) first_move = man_order.move_raw_ids.filtered( lambda move: move.product_id == self.product_component1) self.assertEqual(first_move.product_qty, 6.0) first_move = man_order.move_raw_ids.filtered( lambda move: move.product_id == self.product_component2) self.assertEqual(first_move.product_qty, 2.0) # produce product mo_form = Form(man_order.with_user(self.allow_user)) mo_form.qty_producing = 2 man_order = mo_form.save() man_order.button_mark_done() landed_cost = Form(self.env['stock.landed.cost'].with_user( self.allow_user)).save() landed_cost.target_model = 'manufacturing' # Check domain of the views self.assertTrue(man_order in self.env['mrp.production'].search([( 'move_finished_ids.stock_valuation_layer_ids', '!=', False), ('company_id', '=', landed_cost.company_id.id)])) landed_cost.mrp_production_ids = [(6, 0, [man_order.id])] landed_cost.cost_lines = [(0, 0, { 'product_id': self.landed_cost.id, 'price_unit': 5.0, 'split_method': 'equal' })] landed_cost.button_validate() self.assertEqual(landed_cost.state, 'done') self.assertTrue(landed_cost.account_move_id) # Link to one layer of product_refrigerator self.assertEqual(len(landed_cost.stock_valuation_layer_ids), 1) self.assertEqual(landed_cost.stock_valuation_layer_ids.product_id, self.product_refrigerator) self.assertEqual(landed_cost.stock_valuation_layer_ids.value, 5.0)
def test_tracking_types_on_mo(self): finished_no_track = self._create_product('none') finished_lot = self._create_product('lot') finished_serial = self._create_product('serial') consumed_no_track = self._create_product('none') consumed_lot = self._create_product('lot') consumed_serial = self._create_product('serial') stock_id = self.env.ref('stock.stock_location_stock').id Lot = self.env['stock.lot'] # create inventory quants = self.env['stock.quant'].create({ 'location_id': stock_id, 'product_id': consumed_no_track.id, 'inventory_quantity': 3 }) quants |= self.env['stock.quant'].create({ 'location_id': stock_id, 'product_id': consumed_lot.id, 'inventory_quantity': 3, 'lot_id': Lot.create({ 'name': 'L1', 'product_id': consumed_lot.id, 'company_id': self.env.company.id }).id }) quants |= self.env['stock.quant'].create({ 'location_id': stock_id, 'product_id': consumed_serial.id, 'inventory_quantity': 1, 'lot_id': Lot.create({ 'name': 'S1', 'product_id': consumed_serial.id, 'company_id': self.env.company.id }).id }) quants |= self.env['stock.quant'].create({ 'location_id': stock_id, 'product_id': consumed_serial.id, 'inventory_quantity': 1, 'lot_id': Lot.create({ 'name': 'S2', 'product_id': consumed_serial.id, 'company_id': self.env.company.id }).id }) quants |= self.env['stock.quant'].create({ 'location_id': stock_id, 'product_id': consumed_serial.id, 'inventory_quantity': 1, 'lot_id': Lot.create({ 'name': 'S3', 'product_id': consumed_serial.id, 'company_id': self.env.company.id }).id }) quants.action_apply_inventory() for finished_product in [ finished_no_track, finished_lot, finished_serial ]: bom = self.env['mrp.bom'].create({ 'product_id': finished_product.id, 'product_tmpl_id': finished_product.product_tmpl_id.id, 'product_uom_id': self.env.ref('uom.product_uom_unit').id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, { 'product_id': consumed_no_track.id, 'product_qty': 1 }), (0, 0, { 'product_id': consumed_lot.id, 'product_qty': 1 }), (0, 0, { 'product_id': consumed_serial.id, 'product_qty': 1 }), ], }) mo_form = Form(self.env['mrp.production']) mo_form.product_id = finished_product mo_form.bom_id = bom mo_form.product_uom_id = self.env.ref('uom.product_uom_unit') mo_form.product_qty = 1 mo = mo_form.save() mo.action_confirm() mo.action_assign() # Start MO production mo_form = Form(mo) mo_form.qty_producing = 1 if finished_product.tracking != 'none': mo_form.lot_producing_id = self.env['stock.lot'].create({ 'name': 'Serial or Lot finished', 'product_id': finished_product.id, 'company_id': self.env.company.id }) mo = mo_form.save() details_operation_form = Form( mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo.move_raw_ids[2], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.qty_done = 1 details_operation_form.save() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check results of traceability context = ({ 'active_id': mo.id, 'model': 'mrp.production', }) lines = self.env['stock.traceability.report'].with_context( context).get_lines() self.assertEqual( len(lines), 1, "Should always return 1 line : the final product") final_product = lines[0] self.assertEqual(final_product['unfoldable'], True, "Final product should always be unfoldable") # Find parts of the final products lines = self.env['stock.traceability.report'].get_lines( final_product['id'], **{ 'level': final_product['level'], 'model_id': final_product['model_id'], 'model_name': final_product['model'], }) self.assertEqual( len(lines), 3, "There should be 3 lines. 1 for untracked, 1 for lot, and 1 for serial" ) for line in lines: tracking = line['columns'][1].split(' ')[1] self.assertEqual( line['columns'][-1], "1.00 Units", 'Part with tracking type "%s", should have quantity = 1' % (tracking)) unfoldable = False if tracking == 'none' else True self.assertEqual( line['unfoldable'], unfoldable, 'Parts with tracking type "%s", should have be unfoldable : %s' % (tracking, unfoldable))
def test_unbuild_with_everything_tracked(self): """ This test creates a MO and then creates 3 unbuild orders for the final product. All the products for this test are tracked. It checks the stock state after each order and ensure it is correct. """ mo, bom, p_final, p1, p2 = self.generate_mo(tracking_final='lot', tracking_base_2='lot', tracking_base_1='lot') self.assertEqual(len(mo), 1, 'MO should have been created') lot_final = self.env['stock.production.lot'].create({ 'name': 'lot_final', 'product_id': p_final.id, 'company_id': self.env.company.id, }) lot_1 = self.env['stock.production.lot'].create({ 'name': 'lot_consumed_1', 'product_id': p1.id, 'company_id': self.env.company.id, }) lot_2 = self.env['stock.production.lot'].create({ 'name': 'lot_consumed_2', 'product_id': p2.id, 'company_id': self.env.company.id, }) self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 100, lot_id=lot_1) self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5, lot_id=lot_2) mo.action_assign() # FIXME sle: behavior change mo_form = Form(mo) mo_form.qty_producing = 5.0 mo_form.lot_producing_id = lot_final mo = mo_form.save() details_operation_form = Form( mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.qty_done = 5 details_operation_form.save() details_operation_form = Form( mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.qty_done = 20 details_operation_form.save() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot_final), 5, 'You should have the 5 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot_1), 80, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_2), 0, 'You should have consumed all the 5 product in stock') # --------------------------------------------------- # unbuild # --------------------------------------------------- x = Form(self.env['mrp.unbuild']) with self.assertRaises(AssertionError): x.product_id = p_final x.bom_id = bom x.product_qty = 3 x.save() with self.assertRaises(AssertionError): x.product_id = p_final x.bom_id = bom x.product_qty = 3 x.save() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot_final), 5, 'You should have consumed 3 final product in stock') with self.assertRaises(AssertionError): x.product_id = p_final x.bom_id = bom x.mo_id = mo x.product_qty = 3 x.save() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot_final), 5, 'You should have consumed 3 final product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.mo_id = mo x.product_qty = 3 x.lot_id = lot_final x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot_final), 2, 'You should have consumed 3 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot_1), 92, 'You should have 92 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_2), 3, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.mo_id = mo x.product_qty = 2 x.lot_id = lot_final x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot_final), 0, 'You should have 0 finalproduct in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot_1), 100, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_2), 5, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.mo_id = mo x.product_qty = 5 x.lot_id = lot_final x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot_final, allow_negative=True), -5, 'You should have negative quantity for final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot_1), 120, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_2), 10, 'You should have consumed all the 5 product in stock')
def test_02_different_uom_on_bomlines(self): """ Testing bill of material with different 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("uom.product_uom_unit") dozen = self.ref("uom.product_uom_dozen") kg = self.ref("uom.product_uom_kgm") gm = self.ref("uom.product_uom_gram") # Create Product A, B, C 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 # ----------------- #=================================== # Product A 1 Unit # Product B 4 Unit # Product C 600 gram # ----------------------------------- 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. # ----------------------------------------------- mo_custom_product_form = Form(self.env['mrp.production']) mo_custom_product_form.product_id = product_A mo_custom_product_form.bom_id = bom_a mo_custom_product_form.product_qty = 10.0 mo_custom_product_form.product_uom_id = self.env.ref("uom.product_uom_unit") mo_custom_product = mo_custom_product_form.save() 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) 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.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.env['stock.inventory'].create({ 'name': 'Inventory Product B and C', '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.action_start() inventory.action_validate() # Start Production ... # -------------------- mo_custom_product.action_confirm() mo_custom_product.action_assign() context = {"active_ids": [mo_custom_product.id], "active_id": mo_custom_product.id} produce_form = Form(self.env['mrp.product.produce'].with_context(context)) produce_form.qty_producing = 10.00 produce_form.final_lot_id = lot_a product_consume = produce_form.save() # laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) self.assertEquals(len(product_consume.workorder_line_ids), 2) product_consume.workorder_line_ids.filtered(lambda x: x.product_id == product_C).write({'qty_done': 3000}) product_consume.workorder_line_ids.filtered(lambda x: x.product_id == product_B).write({'qty_done': 20}) product_consume.do_produce() mo_custom_product.post_inventory()
def test_unbuild_with_comnsumed_lot(self): """ This test creates a MO and then creates 3 unbuild orders for the final product. Only once of the two consumed product is tracked by lot. It checks the stock state after each order and ensure it is correct. """ mo, bom, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot') self.assertEqual(len(mo), 1, 'MO should have been created') lot = self.env['stock.production.lot'].create({ 'name': 'lot1', 'product_id': p1.id, 'company_id': self.env.company.id, }) self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 100, lot_id=lot) self.env['stock.quant']._update_available_quantity( p2, self.stock_location, 5) mo.action_assign() for ml in mo.move_raw_ids.mapped('move_line_ids'): if ml.product_id.tracking != 'none': self.assertEqual(ml.lot_id, lot, 'Wrong reserved lot.') # FIXME sle: behavior change mo_form = Form(mo) mo_form.qty_producing = 5.0 mo = mo_form.save() details_operation_form = Form( mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.lot_id = lot ml.qty_done = 20 details_operation_form.save() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 5, 'You should have the 5 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot), 80, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 0, 'You should have consumed all the 5 product in stock') # --------------------------------------------------- # unbuild # --------------------------------------------------- x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 3 unbuild_order = x.save() # This should fail since we do not provide the MO that we wanted to unbuild. (without MO we do not know which consumed lot we have to restore) with self.assertRaises(UserError): unbuild_order.action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 5, 'You should have consumed 3 final product in stock') unbuild_order.mo_id = mo.id unbuild_order.action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 2, 'You should have consumed 3 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot), 92, 'You should have 92 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 3, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.mo_id = mo x.product_qty = 2 x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 0, 'You should have 0 finalproduct in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot), 100, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 5, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.mo_id = mo x.product_qty = 5 x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, allow_negative=True), -5, 'You should have negative quantity for final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location, lot_id=lot), 120, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 10, 'You should have consumed all the 5 product in stock')
def test_01_without_workorder(self): """ Testing consume quants and produced quants without workorder """ unit = self.ref("uom.product_uom_unit") custom_laptop = self.env.ref("product.product_product_27") custom_laptop.tracking = 'lot' # Create new product charger and keybord # -------------------------------------- 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.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. mo_custom_laptop_form = Form(self.env['mrp.production']) mo_custom_laptop_form.product_id = custom_laptop mo_custom_laptop_form.bom_id = bom_custom_laptop mo_custom_laptop_form.product_qty = 10.0 mo_custom_laptop_form.product_uom_id = self.env.ref("uom.product_uom_unit") mo_custom_laptop = mo_custom_laptop_form.save() mo_custom_laptop.action_confirm() # Assign component to production order. mo_custom_laptop.action_assign() # Check production order status of availablity self.assertEqual(mo_custom_laptop.reservation_state, 'confirmed') # -------------------------------------------------- # Set inventory for rawmaterial charger and keybord # -------------------------------------------------- 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.env['stock.inventory'].create({ 'name': 'Inventory Product Table', '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.action_start() inventory.action_validate() # Check consumed move status mo_custom_laptop.action_assign() self.assertEqual(mo_custom_laptop.reservation_state, '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: %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 # ----------------- # 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_form = Form(self.env['mrp.product.produce'].with_context(context)) product_form.qty_producing = 6.00 laptop_lot_001 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) product_form.final_lot_id = laptop_lot_001 product_consume = product_form.save() product_consume.workorder_line_ids[0].qty_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) mo_custom_laptop.post_inventory() 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) 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} produce_form = Form(self.env['mrp.product.produce'].with_context(context)) produce_form.qty_producing = 4.00 laptop_lot_002 = self.env['stock.production.lot'].create({'product_id': custom_laptop.id}) produce_form.final_lot_id = laptop_lot_002 product_consume = produce_form.save() self.assertEquals(len(product_consume.workorder_line_ids), 2) product_consume.workorder_line_ids[0].qty_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) # Post Inventory of production order. mo_custom_laptop.post_inventory()
def test_00_workorder_process(self): """ Testing consume quants and produced quants with workorder """ dining_table = self.env.ref("mrp.product_product_computer_desk") product_table_sheet = self.env.ref('mrp.product_product_computer_desk_head') product_table_leg = self.env.ref('mrp.product_product_computer_desk_leg') product_bolt = self.env.ref('mrp.product_product_computer_desk_bolt') product_screw = self.env.ref('mrp.product_product_computer_desk_screw') self.env['stock.move'].search([('product_id', 'in', [product_bolt.id, product_screw.id])])._do_unreserve() (product_bolt + product_screw).write({'type': 'product'}) self.env.ref("mrp.mrp_bom_desk").consumption = 'flexible' production_table_form = Form(self.env['mrp.production']) production_table_form.product_id = dining_table production_table_form.bom_id = self.env.ref("mrp.mrp_bom_desk") production_table_form.product_qty = 1.0 production_table_form.product_uom_id = dining_table.uom_id production_table = production_table_form.save() production_table.action_confirm() # Set tracking lot on finish and consume products. dining_table.tracking = 'lot' product_table_sheet.tracking = 'lot' product_table_leg.tracking = 'lot' product_bolt.tracking = "lot" # Initial inventory of product sheet, lags and bolt 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.env['stock.inventory'].create({ 'name': 'Inventory Product Table', '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 }), (0, 0, { 'product_id': product_screw.id, 'product_uom_id': product_screw.uom_id.id, 'product_qty': 20, 'location_id': self.source_location_id })] }) inventory.action_validate() # Create work order production_table.button_plan() # Check Work order created or not self.assertEqual(len(production_table.workorder_ids), 1) # --------------------------------------------------------- # Process all workorder and check it state. # ---------------------------------------------------------- workorder = production_table.workorder_ids[0] self.assertEqual(workorder.state, 'ready', "workorder state should be ready.") # -------------------------------------------------------------- # Process assembly line # --------------------------------------------------------- finished_lot =self.env['stock.production.lot'].create({'product_id': production_table.product_id.id}) workorder.write({'final_lot_id': finished_lot.id}) workorder.button_start() for workorder_line_id in workorder.workorder_line_ids: if workorder_line_id.product_id.id == product_bolt.id: workorder_line_id.write({'lot_id': lot_bolt.id, 'qty_done': 1}) if workorder_line_id.product_id.id == product_table_sheet.id: workorder_line_id.write({'lot_id': lot_sheet.id, 'qty_done': 1}) if workorder_line_id.product_id.id == product_table_leg.id: workorder_line_id.write({'lot_id': lot_leg.id, 'qty_done': 1}) self.assertEqual(workorder.state, 'progress') workorder.record_production() self.assertEqual(workorder.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) # --------------------------------------------------------------- # Check consume quants and produce quants after posting inventory # --------------------------------------------------------------- production_table.button_mark_done() self.assertEqual(product_screw.qty_available, 10) self.assertEqual(product_bolt.qty_available, 19) self.assertEqual(product_table_leg.qty_available, 19) self.assertEqual(product_table_sheet.qty_available, 19)
def test_00b_workorder_process(self): """ Testing consume quants and produced quants with workorder """ dining_table = self.env.ref("mrp.product_product_computer_desk") product_table_sheet = self.env.ref('mrp.product_product_computer_desk_head') product_table_leg = self.env.ref('mrp.product_product_computer_desk_leg') product_bolt = self.env.ref('mrp.product_product_computer_desk_bolt') self.env['stock.move'].search([('product_id', '=', product_bolt.id)])._do_unreserve() product_bolt.type = 'product' bom = self.env['mrp.bom'].browse(self.ref("mrp.mrp_bom_desk")) bom.routing_id = self.ref('mrp.mrp_routing_1') bom.consumption = 'flexible' bom.bom_line_ids.filtered(lambda p: p.product_id == product_table_sheet).operation_id = bom.routing_id.operation_ids[0] bom.bom_line_ids.filtered(lambda p: p.product_id == product_table_leg).operation_id = bom.routing_id.operation_ids[1] bom.bom_line_ids.filtered(lambda p: p.product_id == product_bolt).operation_id = bom.routing_id.operation_ids[2] production_table_form = Form(self.env['mrp.production']) production_table_form.product_id = dining_table production_table_form.bom_id = bom production_table_form.product_qty = 2.0 production_table_form.product_uom_id = dining_table.uom_id production_table = production_table_form.save() # Set tracking lot on finish and consume products. dining_table.tracking = 'lot' product_table_sheet.tracking = 'lot' product_table_leg.tracking = 'lot' product_bolt.tracking = "lot" production_table.action_confirm() # Initial inventory of product sheet, lags and bolt 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.env['stock.inventory'].create({ 'name': 'Inventory Product Table', '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_validate() # Create work order production_table.button_plan() # Check Work order created or not self.assertEqual(len(production_table.workorder_ids), 3) # --------------------------------------------------------- # Process all workorder and check it state. # ---------------------------------------------------------- workorders = production_table.workorder_ids self.assertEqual(workorders[0].state, 'ready', "First workorder state should be ready.") self.assertEqual(workorders[1].state, 'pending') self.assertEqual(workorders[2].state, 'pending') # -------------------------------------------------------------- # Process cutting operation... # --------------------------------------------------------- finished_lot = self.env['stock.production.lot'].create({'product_id': production_table.product_id.id}) workorders[0].write({'final_lot_id': finished_lot.id, 'qty_producing': 1.0}) workorders[0].button_start() workorders[0].workorder_line_ids[0].write({'lot_id': lot_sheet.id, 'qty_done': 1}) self.assertEqual(workorders[0].state, 'progress') workorders[0].record_production() move_table_sheet = production_table.move_raw_ids.filtered(lambda p: p.product_id == product_table_sheet) self.assertEqual(move_table_sheet.quantity_done, 1) # -------------------------------------------------------------- # Process drilling operation ... # --------------------------------------------------------- workorders[1].button_start() workorders[1].qty_producing = 1.0 workorders[1].workorder_line_ids[0].write({'lot_id': lot_leg.id, 'qty_done': 4}) workorders[1].record_production() move_leg = production_table.move_raw_ids.filtered(lambda p: p.product_id == product_table_leg) #self.assertEqual(workorders[1].state, 'done') self.assertEqual(move_leg.quantity_done, 4) # -------------------------------------------------------------- # Process fitting operation ... # --------------------------------------------------------- workorders[2].button_start() workorders[2].qty_producing = 1.0 move_lot = workorders[2].workorder_line_ids[0] move_lot.write({'lot_id': lot_bolt.id, 'qty_done': 4}) move_table_bolt = production_table.move_raw_ids.filtered(lambda p: p.product_id.id == product_bolt.id) workorders[2].record_production() self.assertEqual(move_table_bolt.quantity_done, 4) # Change the quantity of the production order to 1 wiz = self.env['change.production.qty'].create({'mo_id': production_table.id , 'product_qty': 1.0}) wiz.change_prod_qty() # --------------------------------------------------------------- # Check consume quants and produce quants after posting inventory # --------------------------------------------------------------- production_table.post_inventory() self.assertEqual(sum(move_table_sheet.mapped('quantity_done')), 1, "Wrong quantity of consumed product %s" % move_table_sheet.product_id.name) self.assertEqual(sum(move_leg.mapped('quantity_done')), 4, "Wrong quantity of consumed product %s" % move_leg.product_id.name) self.assertEqual(sum(move_table_bolt.mapped('quantity_done')), 4, "Wrong quantity of consumed product %s" % move_table_bolt.product_id.name)
def test_procurement_3(self): warehouse = self.env['stock.warehouse'].search([], limit=1) warehouse.write({'reception_steps': 'three_steps'}) self.env['stock.location']._parent_store_compute() warehouse.reception_route_id.rule_ids.filtered( lambda p: p.location_src_id == warehouse.wh_input_stock_loc_id and p.location_id == warehouse.wh_qc_stock_loc_id).write( {'procure_method': 'make_to_stock'}) finished_product = self.env['product.product'].create({ 'name': 'Finished Product', 'type': 'product', }) component = self.env['product.product'].create({ 'name': 'Component', 'type': 'product', 'route_ids': [(4, warehouse.mto_pull_id.route_id.id)] }) self.env['stock.quant']._update_available_quantity( component, warehouse.wh_input_stock_loc_id, 100) bom = self.env['mrp.bom'].create({ 'product_id': finished_product.id, 'product_tmpl_id': finished_product.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [(0, 0, { 'product_id': component.id, 'product_qty': 1.0 })] }) mo_form = Form(self.env['mrp.production']) mo_form.product_id = finished_product mo_form.bom_id = bom mo_form.product_qty = 5 mo_form.product_uom_id = finished_product.uom_id mo_form.location_src_id = warehouse.lot_stock_id mo = mo_form.save() mo.action_confirm() pickings = self.env['stock.picking'].search([('product_id', '=', component.id)]) self.assertEqual(len(pickings), 2.0) picking_input_to_qc = pickings.filtered( lambda p: p.location_id == warehouse.wh_input_stock_loc_id) picking_qc_to_stock = pickings - picking_input_to_qc self.assertTrue(picking_input_to_qc) self.assertTrue(picking_qc_to_stock) picking_input_to_qc.action_assign() self.assertEqual(picking_input_to_qc.state, 'assigned') picking_input_to_qc.move_line_ids.write({'qty_done': 5.0}) picking_input_to_qc.action_done() picking_qc_to_stock.action_assign() self.assertEqual(picking_qc_to_stock.state, 'assigned') picking_qc_to_stock.move_line_ids.write({'qty_done': 3.0}) self.env['stock.backorder.confirmation'].create({ 'pick_ids': [(4, picking_qc_to_stock.id)] }).process_cancel_backorder() self.assertEqual(picking_qc_to_stock.state, 'done') mo.action_assign() self.assertEqual(mo.move_raw_ids.reserved_availability, 3.0) produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.qty_producing = 3.0 produce_wizard = produce_form.save() produce_wizard.do_produce() self.assertEqual(mo.move_raw_ids.quantity_done, 3.0) picking_qc_to_stock.move_line_ids.qty_done = 5.0 self.assertEqual(mo.move_raw_ids.reserved_availability, 5.0) self.assertEqual(mo.move_raw_ids.quantity_done, 3.0)
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).action_start() stock_inv_product_2.action_validate() stock_inv_product_4.action_validate() #Create Manufacturing order. production_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = self.bom_3 production_form.product_qty = 12 production_form.product_uom_id = self.product_6.uom_id production_3 = production_form.save() production_3.action_confirm() production_3.action_assign() # Check Manufacturing order's availability. self.assertEqual(production_3.reservation_state, '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 ?. scrap_id = 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}) with self.assertRaises(except_orm): scrap_id.do_scrap() # 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})
def test_tracking_on_byproducts(self): product_final = self.env['product.product'].create({ 'name': 'Finished Product', 'type': 'product', 'tracking': 'serial', }) product_1 = self.env['product.product'].create({ 'name': 'Raw 1', 'type': 'product', 'tracking': 'serial', }) product_2 = self.env['product.product'].create({ 'name': 'Raw 2', 'type': 'product', 'tracking': 'serial', }) byproduct_1 = self.env['product.product'].create({ 'name': 'Byproduct 1', 'type': 'product', 'tracking': 'serial', }) byproduct_2 = self.env['product.product'].create({ 'name': 'Byproduct 2', 'type': 'product', 'tracking': 'serial', }) bom_1 = self.env['mrp.bom'].create({ 'product_id': product_final.id, 'product_tmpl_id': product_final.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1.0, 'consumption': 'flexible', 'type': 'normal', 'bom_line_ids': [(0, 0, { 'product_id': product_1.id, 'product_qty': 1 }), (0, 0, { 'product_id': product_2.id, 'product_qty': 1 })], 'byproduct_ids': [(0, 0, { 'product_id': byproduct_1.id, 'product_qty': 1, 'product_uom_id': byproduct_1.uom_id.id }), (0, 0, { 'product_id': byproduct_2.id, 'product_qty': 1, 'product_uom_id': byproduct_2.uom_id.id })] }) mo_form = Form(self.env['mrp.production']) mo_form.product_id = product_final mo_form.bom_id = bom_1 mo_form.product_qty = 2 mo = mo_form.save() mo.action_confirm() mo_form = Form(mo) mo_form.lot_producing_id = self.env['stock.lot'].create({ 'product_id': product_final.id, 'name': 'Final_lot_1', 'company_id': self.env.company.id, }) mo = mo_form.save() details_operation_form = Form( mo.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_1.id, 'name': 'Raw_1_lot_1', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo.move_raw_ids[1], view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_2.id, 'name': 'Raw_2_lot_1', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo.move_finished_ids.filtered( lambda m: m.product_id == byproduct_1), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_1.id, 'name': 'Byproduct_1_lot_1', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo.move_finished_ids.filtered( lambda m: m.product_id == byproduct_2), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_2.id, 'name': 'Byproduct_2_lot_1', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() action = mo.button_mark_done() backorder = Form(self.env['mrp.production.backorder'].with_context( **action['context'])) backorder.save().action_backorder() mo_backorder = mo.procurement_group_id.mrp_production_ids[-1] mo_form = Form(mo_backorder) mo_form.lot_producing_id = self.env['stock.lot'].create({ 'product_id': product_final.id, 'name': 'Final_lot_2', 'company_id': self.env.company.id, }) mo_form.qty_producing = 1 mo_backorder = mo_form.save() details_operation_form = Form( mo_backorder.move_raw_ids.filtered( lambda m: m.product_id == product_1), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_1.id, 'name': 'Raw_1_lot_2', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo_backorder.move_raw_ids.filtered( lambda m: m.product_id == product_2), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': product_2.id, 'name': 'Raw_2_lot_2', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo_backorder.move_finished_ids.filtered( lambda m: m.product_id == byproduct_1), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_1.id, 'name': 'Byproduct_1_lot_2', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() details_operation_form = Form( mo_backorder.move_finished_ids.filtered( lambda m: m.product_id == byproduct_2), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.new() as ml: ml.lot_id = self.env['stock.lot'].create({ 'product_id': byproduct_2.id, 'name': 'Byproduct_2_lot_2', 'company_id': self.env.company.id, }) ml.qty_done = 1 details_operation_form.save() mo_backorder.button_mark_done() # self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 4) # self.assertEqual(len(mo.move_finished_ids.mapped('move_line_ids')), 6) mo = mo | mo_backorder raw_move_lines = mo.move_raw_ids.mapped('move_line_ids') raw_line_raw_1_lot_1 = raw_move_lines.filtered( lambda ml: ml.lot_id.name == 'Raw_1_lot_1') self.assertEqual( set(raw_line_raw_1_lot_1.produce_line_ids.lot_id.mapped('name')), set(['Final_lot_1', 'Byproduct_1_lot_1', 'Byproduct_2_lot_1'])) raw_line_raw_2_lot_1 = raw_move_lines.filtered( lambda ml: ml.lot_id.name == 'Raw_2_lot_1') self.assertEqual( set(raw_line_raw_2_lot_1.produce_line_ids.lot_id.mapped('name')), set(['Final_lot_1', 'Byproduct_1_lot_1', 'Byproduct_2_lot_1'])) finished_move_lines = mo.move_finished_ids.mapped('move_line_ids') finished_move_line_lot_1 = finished_move_lines.filtered( lambda ml: ml.lot_id.name == 'Final_lot_1') self.assertEqual( finished_move_line_lot_1.consume_line_ids.filtered( lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) finished_move_line_lot_2 = finished_move_lines.filtered( lambda ml: ml.lot_id.name == 'Final_lot_2') raw_line_raw_1_lot_2 = raw_move_lines.filtered( lambda ml: ml.lot_id.name == 'Raw_1_lot_2') raw_line_raw_2_lot_2 = raw_move_lines.filtered( lambda ml: ml.lot_id.name == 'Raw_2_lot_2') self.assertEqual(finished_move_line_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2) byproduct_move_line_1_lot_1 = finished_move_lines.filtered( lambda ml: ml.lot_id.name == 'Byproduct_1_lot_1') self.assertEqual( byproduct_move_line_1_lot_1.consume_line_ids.filtered( lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) byproduct_move_line_1_lot_2 = finished_move_lines.filtered( lambda ml: ml.lot_id.name == 'Byproduct_1_lot_2') self.assertEqual(byproduct_move_line_1_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2) byproduct_move_line_2_lot_1 = finished_move_lines.filtered( lambda ml: ml.lot_id.name == 'Byproduct_2_lot_1') self.assertEqual( byproduct_move_line_2_lot_1.consume_line_ids.filtered( lambda l: l.qty_done), raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) byproduct_move_line_2_lot_2 = finished_move_lines.filtered( lambda ml: ml.lot_id.name == 'Byproduct_2_lot_2') self.assertEqual(byproduct_move_line_2_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)
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_form = Form(self.env['mrp.production']) man_order_form.product_id = self.product_6 man_order_form.bom_id = self.bom_3 man_order_form.product_qty = 48 man_order_form.product_uom_id = self.product_6.uom_id man_order = man_order_form.save() # reset quantities self.product_1.type = "product" 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': 'none', }) # assign consume material man_order.action_confirm() man_order.action_assign() self.assertEqual(man_order.reservation_state, 'confirmed', "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.action_start() inventory.action_validate() # re-assign consume material man_order.action_assign() # Check production order status after assign. self.assertEqual(man_order.reservation_state, '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_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_form = Form(self.env['mrp.production']) man_order_form.product_id = self.product_6 man_order_form.bom_id = self.bom_3 man_order_form.product_qty = 48 man_order_form.product_uom_id = self.product_6.uom_id man_order = man_order_form.save() # reset quantities self.product_1.type = "product" 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': 'none', }) # assign consume material man_order.action_confirm() man_order.action_assign() self.assertEqual(man_order.reservation_state, 'confirmed', "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.action_start() inventory.action_validate() # re-assign consume material man_order.action_assign() # Check production order status after assign. self.assertEqual(man_order.reservation_state, '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_basic(self): """ Checks a basic manufacturing order: no routing (thus no workorders), no lot and consume strictly what's needed. """ self.product_1.type = 'product' self.product_2.type = 'product' 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_validate() test_date_planned = Dt.now() - timedelta(days=1) test_quantity = 2.0 self.bom_1.routing_id = False man_order_form = Form(self.env['mrp.production'].sudo(self.user_mrp_user)) man_order_form.product_id = self.product_4 man_order_form.bom_id = self.bom_1 man_order_form.product_uom_id = self.product_4.uom_id man_order_form.product_qty = test_quantity man_order_form.date_planned_start = test_date_planned man_order_form.location_src_id = self.location_1 man_order_form.location_dest_id = self.warehouse_1.wh_output_stock_loc_id man_order = man_order_form.save() self.assertEqual(man_order.state, 'draft', "Production order should be in draft state.") man_order.action_confirm() 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, 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, 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) # produce product produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': man_order.id, 'active_ids': [man_order.id], })) produce_form.qty_producing = 1.0 produce_wizard = produce_form.save() produce_wizard.do_produce() man_order.button_mark_done() self.assertEqual(man_order.state, 'done', "Production order should be in done state.")
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_form = Form(self.env['mrp.production']) production_form.product_id = self.product_6 production_form.bom_id = self.bom_3 production_form.product_qty = 24 production_form.product_uom_id = self.product_6.uom_id production_product_6 = production_form.save() production_product_6.action_confirm() 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. # Check it created a purchase order move_raw_product4 = production_product_6.move_raw_ids.filtered(lambda x: x.product_id == self.product_4) produce_product_4 = self.env['mrp.production'].search([('product_id', '=', self.product_4.id), ('move_dest_ids', '=', move_raw_product4[0].id)]) # produce product self.assertEqual(produce_product_4.reservation_state, 'confirmed', "Consume material not available") # Create production order # ------------------------- # Product 4 96 Unit # Product2 48 Unit # --------------------- # Update Inventory self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.product_2.id, 'inventory_quantity': 48, 'location_id': self.warehouse.lot_stock_id.id, }) 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.reservation_state, 'assigned', "Consume material not available") # produce product4 # --------------- produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': produce_product_4.id, 'active_ids': [produce_product_4.id], })) produce_form.qty_producing = produce_product_4.product_qty product_produce = produce_form.save() product_produce.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') # Produce product 6 # ------------------ # Update Inventory self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.product_2.id, 'inventory_quantity': 12, 'location_id': self.warehouse.lot_stock_id.id, }) production_product_6.action_assign() # ------------------------------------ self.assertEqual(production_product_6.reservation_state, 'assigned', "Consume material not available") produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': production_product_6.id, 'active_ids': [production_product_6.id], })) produce_form.qty_producing = production_product_6.product_qty product_produce = produce_form.save() product_produce.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.')
def test_unbuild_standart(self): """ This test creates a MO and then creates 3 unbuild orders for the final product. None of the products for this test are tracked. It checks the stock state after each order and ensure it is correct. """ mo, bom, p_final, p1, p2 = self.generate_mo() self.assertEqual(len(mo), 1, 'MO should have been created') self.env['stock.quant']._update_available_quantity( p1, self.stock_location, 100) self.env['stock.quant']._update_available_quantity( p2, self.stock_location, 5) mo.action_assign() mo_form = Form(mo) mo_form.qty_producing = 5.0 mo = mo_form.save() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 5, 'You should have the 5 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 80, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 0, 'You should have consumed all the 5 product in stock') # --------------------------------------------------- # unbuild # --------------------------------------------------- x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 3 x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 2, 'You should have consumed 3 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 92, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 3, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 2 x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 0, 'You should have 0 finalproduct in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 100, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 5, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 5 x.save().action_unbuild() # Check quantity in stock after last unbuild. self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, allow_negative=True), -5, 'You should have negative quantity for final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 120, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 10, 'You should have consumed all the 5 product in stock')
def test_landed_cost_on_mrp(self): inventory = self.env['stock.inventory'].create({ 'name': 'Initial inventory', 'line_ids': [(0, 0, { 'product_id': self.product_component1.id, 'product_uom_id': self.product_component1.uom_id.id, 'product_qty': 500, 'location_id': self.warehouse_1.lot_stock_id.id }), (0, 0, { 'product_id': self.product_component2.id, 'product_uom_id': self.product_component2.uom_id.id, 'product_qty': 500, 'location_id': self.warehouse_1.lot_stock_id.id })] }) inventory.action_start() inventory.action_validate() man_order_form = Form(self.env['mrp.production'].with_user( self.allow_user)) man_order_form.product_id = self.product_refrigerator man_order_form.bom_id = self.bom_refri man_order_form.product_qty = 2.0 man_order = man_order_form.save() self.assertEqual(man_order.state, 'draft', "Production order should be in draft state.") man_order.action_confirm() 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.product_id, self.product_refrigerator) first_move = man_order.move_raw_ids.filtered( lambda move: move.product_id == self.product_component1) self.assertEqual(first_move.product_qty, 6.0) first_move = man_order.move_raw_ids.filtered( lambda move: move.product_id == self.product_component2) self.assertEqual(first_move.product_qty, 2.0) # produce product produce_form = Form(self.env['mrp.product.produce'].with_user( self.allow_user).with_context({ 'active_id': man_order.id, 'active_ids': [man_order.id], })) produce_form.qty_producing = 2.0 produce_wizard = produce_form.save() produce_wizard.do_produce() man_order.button_mark_done() landed_cost = Form(self.env['stock.landed.cost'].with_user( self.allow_user)).save() landed_cost.target_model = 'manufacturing' self.assertTrue( man_order.id in landed_cost.allowed_mrp_production_ids.ids) landed_cost.mrp_production_ids = [(6, 0, [man_order.id])] landed_cost.cost_lines = [(0, 0, { 'product_id': self.landed_cost.id, 'price_unit': 5.0, 'split_method': 'equal' })] landed_cost.button_validate() self.assertEqual(landed_cost.state, 'done') self.assertTrue(landed_cost.account_move_id) # Link to one layer of product_refrigerator self.assertEqual(len(landed_cost.stock_valuation_layer_ids), 1) self.assertEqual(landed_cost.stock_valuation_layer_ids.product_id, self.product_refrigerator) self.assertEqual(landed_cost.stock_valuation_layer_ids.value, 5.0)
def test_unbuild_with_duplicate_move(self): """ This test creates a MO from 3 different lot on a consumed product (p2). The unbuild order should revert the correct quantity for each specific lot. """ mo, bom, p_final, p1, p2 = self.generate_mo(tracking_final='none', tracking_base_2='lot', tracking_base_1='none') self.assertEqual(len(mo), 1, 'MO should have been created') lot_1 = self.env['stock.production.lot'].create({ 'name': 'lot_1', 'product_id': p2.id, 'company_id': self.env.company.id, }) lot_2 = self.env['stock.production.lot'].create({ 'name': 'lot_2', 'product_id': p2.id, 'company_id': self.env.company.id, }) lot_3 = self.env['stock.production.lot'].create({ 'name': 'lot_3', 'product_id': p2.id, 'company_id': self.env.company.id, }) self.env['stock.quant']._update_available_quantity( p1, self.stock_location, 100) self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 1, lot_id=lot_1) self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 3, lot_id=lot_2) self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 2, lot_id=lot_3) mo.action_assign() mo_form = Form(mo) mo_form.qty_producing = 5.0 mo = mo_form.save() details_operation_form = Form( mo.move_raw_ids.filtered(lambda ml: ml.product_id == p2), view=self.env.ref('stock.view_stock_move_operations')) with details_operation_form.move_line_ids.edit(0) as ml: ml.qty_done = ml.product_uom_qty with details_operation_form.move_line_ids.edit(1) as ml: ml.qty_done = ml.product_uom_qty with details_operation_form.move_line_ids.edit(2) as ml: ml.qty_done = ml.product_uom_qty details_operation_form.save() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 5, 'You should have the 5 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 80, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_1), 0, 'You should have consumed all the 1 product for lot 1 in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_2), 0, 'You should have consumed all the 3 product for lot 2 in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_3), 1, 'You should have consumed only 1 product for lot3 in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.mo_id = mo x.product_qty = 5 x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location), 0, 'You should have no more final product in stock after unbuild') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 100, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_1), 1, 'You should have get your product with lot 1 in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_2), 3, 'You should have the 3 basic product for lot 2 in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location, lot_id=lot_3), 2, 'You should have get one product back for lot 3')
def test_tracking_on_byproducts(self): product_final = self.env['product.product'].create({ 'name': 'Finished Product', 'type': 'product', 'tracking': 'serial', }) product_1 = self.env['product.product'].create({ 'name': 'Raw 1', 'type': 'product', 'tracking': 'serial', }) product_2 = self.env['product.product'].create({ 'name': 'Raw 2', 'type': 'product', 'tracking': 'serial', }) byproduct_1 = self.env['product.product'].create({ 'name': 'Byproduct 1', 'type': 'product', 'tracking': 'serial', }) byproduct_2 = self.env['product.product'].create({ 'name': 'Byproduct 2', 'type': 'product', 'tracking': 'serial', }) bom_1 = self.env['mrp.bom'].create({ 'product_id': product_final.id, 'product_tmpl_id': product_final.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': product_1.id, 'product_qty': 1}), (0, 0, {'product_id': product_2.id, 'product_qty': 1}) ], 'byproduct_ids': [ (0, 0, {'product_id': byproduct_1.id, 'product_qty': 1, 'product_uom_id': byproduct_1.uom_id.id}), (0, 0, {'product_id': byproduct_2.id, 'product_qty': 1, 'product_uom_id': byproduct_2.uom_id.id}) ]}) mo_form = Form(self.env['mrp.production']) mo_form.product_id = product_final mo_form.bom_id = bom_1 mo_form.product_qty = 2 mo = mo_form.save() mo.action_confirm() produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.finished_lot_id = self.env['stock.production.lot'].create({ 'product_id': product_final.id, 'name': 'Final_lot_1' }) with produce_form.raw_workorder_line_ids.edit(0) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': product_1.id, 'name': 'Raw_1_lot_1' }) with produce_form.raw_workorder_line_ids.edit(1) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': product_2.id, 'name': 'Raw_2_lot_1' }) with produce_form.finished_workorder_line_ids.edit(0) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': byproduct_1.id, 'name': 'Byproduct_1_lot_1' }) with produce_form.finished_workorder_line_ids.edit(1) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': byproduct_2.id, 'name': 'Byproduct_2_lot_1' }) produce_wizard = produce_form.save() produce_wizard.continue_production() produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.finished_lot_id = self.env['stock.production.lot'].create({ 'product_id': product_final.id, 'name': 'Final_lot_2' }) with produce_form.raw_workorder_line_ids.edit(0) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': product_1.id, 'name': 'Raw_1_lot_2' }) with produce_form.raw_workorder_line_ids.edit(1) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': product_2.id, 'name': 'Raw_2_lot_2' }) with produce_form.finished_workorder_line_ids.edit(0) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': byproduct_1.id, 'name': 'Byproduct_1_lot_2' }) with produce_form.finished_workorder_line_ids.edit(1) as line: line.lot_id = self.env['stock.production.lot'].create({ 'product_id': byproduct_2.id, 'name': 'Byproduct_2_lot_2' }) produce_wizard = produce_form.save() produce_wizard.do_produce() mo.button_mark_done() self.assertEqual(len(mo.move_raw_ids.mapped('move_line_ids')), 4) self.assertEqual(len(mo.move_finished_ids.mapped('move_line_ids')), 6) raw_move_lines = mo.move_raw_ids.mapped('move_line_ids') raw_line_raw_1_lot_1 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_1_lot_1') self.assertEqual(set(raw_line_raw_1_lot_1.lot_produced_ids.mapped('name')), set(['Final_lot_1', 'Byproduct_1_lot_1', 'Byproduct_2_lot_1'])) raw_line_raw_1_lot_2 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_1_lot_2') self.assertEqual(set(raw_line_raw_1_lot_2.lot_produced_ids.mapped('name')), set(['Final_lot_2', 'Byproduct_1_lot_2', 'Byproduct_2_lot_2'])) raw_line_raw_2_lot_1 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_2_lot_1') self.assertEqual(set(raw_line_raw_2_lot_1.lot_produced_ids.mapped('name')), set(['Final_lot_1', 'Byproduct_1_lot_1', 'Byproduct_2_lot_1'])) raw_line_raw_2_lot_2 = raw_move_lines.filtered(lambda ml: ml.lot_id.name == 'Raw_2_lot_2') self.assertEqual(set(raw_line_raw_2_lot_2.lot_produced_ids.mapped('name')), set(['Final_lot_2', 'Byproduct_1_lot_2', 'Byproduct_2_lot_2'])) finished_move_lines = mo.move_finished_ids.mapped('move_line_ids') finished_move_line_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Final_lot_1') self.assertEqual(finished_move_line_lot_1.consume_line_ids, raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) finished_move_line_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Final_lot_2') self.assertEqual(finished_move_line_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2) byproduct_move_line_1_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_1_lot_1') self.assertEqual(byproduct_move_line_1_lot_1.consume_line_ids, raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) byproduct_move_line_1_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_1_lot_2') self.assertEqual(byproduct_move_line_1_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2) byproduct_move_line_2_lot_1 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_2_lot_1') self.assertEqual(byproduct_move_line_2_lot_1.consume_line_ids, raw_line_raw_1_lot_1 | raw_line_raw_2_lot_1) byproduct_move_line_2_lot_2 = finished_move_lines.filtered(lambda ml: ml.lot_id.name == 'Byproduct_2_lot_2') self.assertEqual(byproduct_move_line_2_lot_2.consume_line_ids, raw_line_raw_1_lot_2 | raw_line_raw_2_lot_2)
def test_tracking_types_on_mo(self): finished_no_track = self._create_product('none') finished_lot = self._create_product('lot') finished_serial = self._create_product('serial') consumed_no_track = self._create_product('none') consumed_lot = self._create_product('lot') consumed_serial = self._create_product('serial') stock_id = self.env.ref('stock.stock_location_stock').id inventory_adjustment = self.env['stock.inventory'].create({ 'name': 'Initial Inventory', 'location_ids': [(4, stock_id)], }) inventory_adjustment.action_start() inventory_adjustment.write({ 'line_ids': [ (0,0, {'product_id': consumed_no_track.id, 'product_qty': 3, 'location_id': stock_id}), (0,0, {'product_id': consumed_lot.id, 'product_qty': 3, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'L1', 'product_id': consumed_lot.id}).id, 'location_id': stock_id}), (0,0, {'product_id': consumed_serial.id, 'product_qty': 1, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'S1', 'product_id': consumed_serial.id}).id, 'location_id': stock_id}), (0,0, {'product_id': consumed_serial.id, 'product_qty': 1, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'S2', 'product_id': consumed_serial.id}).id, 'location_id': stock_id}), (0,0, {'product_id': consumed_serial.id, 'product_qty': 1, 'prod_lot_id': self.env['stock.production.lot'].create({'name': 'S3', 'product_id': consumed_serial.id}).id, 'location_id': stock_id}), ] }) inventory_adjustment.action_validate() for finished_product in [finished_no_track, finished_lot, finished_serial]: bom = self.env['mrp.bom'].create({ 'product_id': finished_product.id, 'product_tmpl_id': finished_product.product_tmpl_id.id, 'product_uom_id': self.env.ref('uom.product_uom_unit').id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, {'product_id': consumed_no_track.id, 'product_qty': 1}), (0, 0, {'product_id': consumed_lot.id, 'product_qty': 1}), (0, 0, {'product_id': consumed_serial.id, 'product_qty': 1}), ], }) mo_form = Form(self.env['mrp.production']) mo_form.product_id = finished_product mo_form.bom_id = bom mo_form.product_uom_id = self.env.ref('uom.product_uom_unit') mo_form.product_qty = 1 mo = mo_form.save() mo.action_confirm() mo.action_assign() # Start MO production produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) if finished_product.tracking != 'serial': produce_form.qty_producing = 1 if finished_product.tracking != 'none': produce_form.finished_lot_id = self.env['stock.production.lot'].create({'name': 'Serial or Lot finished', 'product_id': finished_product.id}) produce_wizard = produce_form.save() produce_wizard.do_produce() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check results of traceability context = ({ 'active_id': mo.id, 'model': 'mrp.production', }) lines = self.env['stock.traceability.report'].with_context(context).get_lines() self.assertEqual(len(lines), 1, "Should always return 1 line : the final product") final_product = lines[0] self.assertEqual(final_product['unfoldable'], True, "Final product should always be unfoldable") # Find parts of the final products lines = self.env['stock.traceability.report'].get_lines(final_product['id'], **{ 'level': final_product['level'], 'model_id': final_product['model_id'], 'model_name': final_product['model'], }) self.assertEqual(len(lines), 3, "There should be 3 lines. 1 for untracked, 1 for lot, and 1 for serial") for line in lines: tracking = line['columns'][1].split(' ')[1] self.assertEqual( line['columns'][-1], "1.000 Units", 'Part with tracking type "%s", should have quantity = 1' % (tracking) ) unfoldable = False if tracking == 'none' else True self.assertEqual( line['unfoldable'], unfoldable, 'Parts with tracking type "%s", should have be unfoldable : %s' % (tracking, unfoldable) )
def test_unbuild_with_final_lot(self): """ This test creates a MO and then creates 3 unbuild orders for the final product. Only the final product is tracked by lot. It checks the stock state after each order and ensure it is correct. """ mo, bom, p_final, p1, p2 = self.generate_mo(tracking_final='lot') self.assertEqual(len(mo), 1, 'MO should have been created') lot = self.env['stock.production.lot'].create({ 'name': 'lot1', 'product_id': p_final.id, 'company_id': self.env.company.id, }) self.env['stock.quant']._update_available_quantity( p1, self.stock_location, 100) self.env['stock.quant']._update_available_quantity( p2, self.stock_location, 5) mo.action_assign() mo_form = Form(mo) mo_form.qty_producing = 5.0 mo_form.lot_producing_id = lot mo = mo_form.save() mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") # Check quantity in stock before unbuild. self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot), 5, 'You should have the 5 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 80, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 0, 'You should have consumed all the 5 product in stock') # --------------------------------------------------- # unbuild # --------------------------------------------------- # This should fail since we do not choose a lot to unbuild for final product. with self.assertRaises(AssertionError): x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 3 unbuild_order = x.save() x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 3 x.lot_id = lot x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot), 2, 'You should have consumed 3 final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 92, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 3, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 2 x.lot_id = lot x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot), 0, 'You should have 0 finalproduct in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 100, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 5, 'You should have consumed all the 5 product in stock') x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 5 x.lot_id = lot x.save().action_unbuild() self.assertEqual( self.env['stock.quant']._get_available_quantity( p_final, self.stock_location, lot_id=lot, allow_negative=True), -5, 'You should have negative quantity for final product in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p1, self.stock_location), 120, 'You should have 80 products in stock') self.assertEqual( self.env['stock.quant']._get_available_quantity( p2, self.stock_location), 10, 'You should have consumed all the 5 product in stock')
def test_smp_performance(self): total_quantity = 1000 quantity = 1 raw_materials_count = 10 trackings = [ 'none', # 'lot', # 'serial' ] _logger.info('setting up environment') raw_materials = [] for i in range(raw_materials_count): raw_materials.append(self.env['product.product'].create({ 'name': '@raw_material#' + str(i + 1), 'type': 'product', 'tracking': trackings[i % len(trackings)] })) finished = self.env['product.product'].create({ 'name': '@finished', 'type': 'product', 'tracking': 'serial', }) bom = self.env['mrp.bom'].create({ 'product_id': finished.id, 'product_tmpl_id': finished.product_tmpl_id.id, 'product_uom_id': finished.uom_id.id, 'product_qty': 1.0, 'type': 'normal', 'consumption': 'flexible', 'bom_line_ids': [(0, 0, { 'product_id': p[0]['id'], 'product_qty': 1 }) for p in raw_materials] }) form = Form(self.env['mrp.production']) form.product_id = finished form.bom_id = bom form.product_qty = total_quantity mo = form.save() mo.action_confirm() for i in range(raw_materials_count): if raw_materials[i].tracking == 'none': self.env['stock.quant'].with_context( inventory_mode=True).create({ 'product_id': raw_materials[i].id, 'inventory_quantity': total_quantity, 'location_id': mo.location_src_id.id, })._apply_inventory() elif raw_materials[i].tracking == 'lot': qty = total_quantity while qty > 0: lot = self.env['stock.lot'].create({ 'product_id': raw_materials[i].id, 'company_id': self.env.company.id, }) self.env['stock.quant'].with_context( inventory_mode=True).create({ 'product_id': raw_materials[i].id, 'inventory_quantity': 10, 'location_id': mo.location_src_id.id, 'lot_id': lot.id, })._apply_inventory() qty -= 10 else: for _ in range(total_quantity): lot = self.env['stock.lot'].create({ 'product_id': raw_materials[i].id, 'company_id': self.env.company.id, }) self.env['stock.quant'].with_context( inventory_mode=True).create({ 'product_id': raw_materials[i].id, 'inventory_quantity': 1, 'location_id': mo.location_src_id.id, 'lot_id': lot.id, })._apply_inventory() mo.action_assign() action = mo.action_serial_mass_produce_wizard() wizard = Form( self.env['stock.assign.serial'].with_context(**action['context'])) wizard.next_serial_number = "sn#1" wizard.next_serial_count = quantity action = wizard.save().generate_serial_numbers_production() wizard = Form(self.env['stock.assign.serial'].browse(action['res_id'])) wizard = wizard.save() _logger.info('generating serial numbers') start = time.perf_counter() if quantity == total_quantity: wizard.apply() else: wizard.create_backorder() end = time.perf_counter() _logger.info('time to produce %s/%s: %s', quantity, total_quantity, end - start)
def test_00_mrp_byproduct(self): """ Test by product with production order.""" # Create BOM for product B # ------------------------ bom_product_b = self.MrpBom.create({ 'product_tmpl_id': self.product_b.product_tmpl_id.id, 'product_qty': 1.0, 'type': 'normal', 'product_uom_id': self.uom_unit_id, 'bom_line_ids': [(0, 0, {'product_id': self.product_c_id, 'product_uom_id': self.uom_unit_id, 'product_qty': 2})] }) # Create 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})], 'byproduct_ids': [(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_form = Form(self.env['mrp.production']) mnf_product_a_form.product_id = self.product_a mnf_product_a_form.bom_id = bom_product_a mnf_product_a_form.product_qty = 2.0 mnf_product_a = mnf_product_a_form.save() mnf_product_a.action_confirm() # I compute the data of production order context = {"active_model": "mrp.production", "active_ids": [mnf_product_a.id], "active_id": mnf_product_a.id} # I confirm the production order. self.assertEqual(mnf_product_a.state, 'confirmed', 'Production order should be in state confirmed') # Now I check the stock moves for the byproduct I created in the bill of material. # This move is created automatically when I confirmed the production order. moves = mnf_product_a.move_raw_ids | mnf_product_a.move_finished_ids self.assertTrue(moves, 'No moves are created !') # I consume and produce the production of products. # I create record for selecting mode and quantity of products to produce. produce_form = Form(self.env['mrp.product.produce'].with_context(context)) produce_form.qty_producing = 2.00 product_consume = produce_form.save() # I finish the production order. self.assertEqual(len(mnf_product_a.move_raw_ids), 1, "Wrong consume move on production order.") product_consume.do_produce() consume_move_c = mnf_product_a.move_raw_ids by_product_move = mnf_product_a.move_finished_ids.filtered(lambda x: x.product_id.id == self.product_b.id) # Check sub production produced quantity... self.assertEqual(consume_move_c.product_uom_qty, 4, "Wrong consumed quantity of product c.") self.assertEqual(by_product_move.product_uom_qty, 2, "Wrong produced quantity of sub product.") mnf_product_a.post_inventory() # I see that stock moves of External Hard Disk including Headset USB are done now. self.assertFalse(any(move.state != 'done' for move in moves), 'Moves are not done!')
def test_basic(self): """ Basic order test: no routing (thus no workorders), no lot """ self.product_1.type = 'product' self.product_2.type = 'product' 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_validate() test_date_planned = Dt.now() - timedelta(days=1) test_quantity = 2.0 self.bom_1.routing_id = False man_order_form = Form(self.env['mrp.production'].sudo( self.user_mrp_user)) man_order_form.product_id = self.product_4 man_order_form.bom_id = self.bom_1 man_order_form.product_uom_id = self.product_4.uom_id man_order_form.product_qty = test_quantity man_order_form.date_planned_start = test_date_planned man_order_form.location_src_id = self.location_1 man_order_form.location_dest_id = self.warehouse_1.wh_output_stock_loc_id man_order = man_order_form.save() self.assertEqual(man_order.state, 'draft', "Production order should be in draft state.") man_order.action_confirm() 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, 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, 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_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': man_order.id, 'active_ids': [man_order.id], })) produce_form.product_qty = 1.0 produce_wizard = produce_form.save() 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_auto_assign(self): """ When auto reordering rule exists, check for when: 1. There is not enough of a manufactured product to assign (reserve for) a picking => auto-create 1st MO 2. There is not enough of a manufactured component to assign the created MO => auto-create 2nd MO 3. Add an extra manufactured component (not in stock) to 1st MO => auto-create 3rd MO 4. When 2nd MO is completed => auto-assign to 1st MO 5. When 1st MO is completed => auto-assign to picking 6. Additionally check that a MO that has component in stock auto-reserves when MO is confirmed (since default setting = 'at_confirm')""" self.warehouse = self.env.ref('stock.warehouse0') route_manufacture = self.warehouse.manufacture_pull_id.route_id product_1 = self.env['product.product'].create({ 'name': 'Cake', 'type': 'product', 'route_ids': [(6, 0, [route_manufacture.id])] }) product_2 = self.env['product.product'].create({ 'name': 'Cake Mix', 'type': 'product', 'route_ids': [(6, 0, [route_manufacture.id])] }) product_3 = self.env['product.product'].create({ 'name': 'Flour', 'type': 'consu', }) bom1 = self.env['mrp.bom'].create({ 'product_id': product_1.id, 'product_tmpl_id': product_1.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1, 'consumption': 'flexible', 'type': 'normal', 'bom_line_ids': [ (0, 0, { 'product_id': product_2.id, 'product_qty': 1 }), ] }) self.env['mrp.bom'].create({ 'product_id': product_2.id, 'product_tmpl_id': product_2.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1, 'type': 'normal', 'bom_line_ids': [ (0, 0, { 'product_id': product_3.id, 'product_qty': 1 }), ] }) # extra manufactured component added to 1st MO after it is already confirmed product_4 = self.env['product.product'].create({ 'name': 'Flavor Enchancer', 'type': 'product', 'route_ids': [(6, 0, [route_manufacture.id])] }) product_5 = self.env['product.product'].create({ 'name': 'MSG', 'type': 'consu', }) self.env['mrp.bom'].create({ 'product_id': product_4.id, 'product_tmpl_id': product_4.product_tmpl_id.id, 'product_uom_id': self.uom_unit.id, 'product_qty': 1, 'type': 'normal', 'bom_line_ids': [ (0, 0, { 'product_id': product_5.id, 'product_qty': 1 }), ] }) # setup auto orderpoints (reordering rules) self.env['stock.warehouse.orderpoint'].create({ 'name': 'Cake RR', 'location_id': self.warehouse.lot_stock_id.id, 'product_id': product_1.id, 'product_min_qty': 0, 'product_max_qty': 5, }) self.env['stock.warehouse.orderpoint'].create({ 'name': 'Cake Mix RR', 'location_id': self.warehouse.lot_stock_id.id, 'product_id': product_2.id, 'product_min_qty': 0, 'product_max_qty': 5, }) self.env['stock.warehouse.orderpoint'].create({ 'name': 'Flavor Enchancer RR', 'location_id': self.warehouse.lot_stock_id.id, 'product_id': product_4.id, 'product_min_qty': 0, 'product_max_qty': 5, }) # create picking output to trigger creating MO for reordering product_1 pick_output = self.env['stock.picking'].create({ 'name': 'Cake Delivery Order', 'picking_type_id': self.ref('stock.picking_type_out'), 'location_id': self.warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), 'move_lines': [(0, 0, { 'name': '/', 'product_id': product_1.id, 'product_uom': product_1.uom_id.id, 'product_uom_qty': 10.00, 'procure_method': 'make_to_stock', 'location_id': self.warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), })], }) pick_output.action_confirm( ) # should trigger orderpoint to create and confirm 1st MO pick_output.action_assign() mo = self.env['mrp.production'].search([('product_id', '=', product_1.id), ('state', '=', 'confirmed')]) self.assertEqual(len(mo), 1, "Manufacture order was not automatically created") mo.action_assign() mo.is_locked = False self.assertEqual(mo.move_raw_ids.reserved_availability, 0, "No components should be reserved yet") self.assertEqual( mo.product_qty, 15, "Quantity to produce should be picking demand + reordering rule max qty" ) # 2nd MO for product_2 should have been created and confirmed when 1st MO for product_1 was confirmed mo2 = self.env['mrp.production'].search([('product_id', '=', product_2.id), ('state', '=', 'confirmed')]) self.assertEqual(len(mo2), 1, 'Second manufacture order was not created') self.assertEqual( mo2.product_qty, 20, "Quantity to produce should be MO's 'to consume' qty + reordering rule max qty" ) mo2_form = Form(mo2) mo2_form.qty_producing = 20 mo2 = mo2_form.save() mo2.button_mark_done() self.assertEqual(mo.move_raw_ids.reserved_availability, 15, "Components should have been auto-reserved") # add new component to 1st MO mo_form = Form(mo) with mo_form.move_raw_ids.new() as line: line.product_id = product_4 line.product_uom_qty = 1 mo_form.save( ) # should trigger orderpoint to create and confirm 3rd MO mo3 = self.env['mrp.production'].search([('product_id', '=', product_4.id), ('state', '=', 'confirmed')]) self.assertEqual( len(mo3), 1, 'Third manufacture order for added component was not created') self.assertEqual( mo3.product_qty, 6, "Quantity to produce should be 1 + reordering rule max qty") mo_form = Form(mo) mo.move_raw_ids.quantity_done = 15 mo_form.qty_producing = 15 mo = mo_form.save() mo.button_mark_done() self.assertEqual( pick_output.move_ids_without_package.reserved_availability, 10, "Completed products should have been auto-reserved in picking") # make sure next MO auto-reserves components now that they are in stock since # default reservation_method = 'at_confirm' mo_form = Form(self.env['mrp.production']) mo_form.product_id = product_1 mo_form.bom_id = bom1 mo_form.product_qty = 5 mo_form.product_uom_id = product_1.uom_id mo_assign_at_confirm = mo_form.save() mo_assign_at_confirm.action_confirm() self.assertEqual( mo_assign_at_confirm.move_raw_ids.reserved_availability, 5, "Components should have been auto-reserved")