def test_shipping_cost_numbers(self): # Free delivery should not be taken into account when checking for minimum required threshold p_minimum_threshold_free_delivery = self.env[ 'sale.coupon.program'].create({ 'name': 'free shipping if > 872 tax exl', 'promo_code_usage': 'code_needed', 'promo_code': 'free_shipping', 'reward_type': 'free_shipping', 'program_type': 'promotion_program', 'rule_minimum_amount': 872, }) self.p2 = self.env['sale.coupon.program'].create({ 'name': 'Buy 4 large cabinet, get one for free', 'promo_code_usage': 'no_code_needed', 'reward_type': 'product', 'program_type': 'promotion_program', 'reward_product_id': self.iPadMini.id, 'rule_min_quantity': 3, 'rule_products_domain': '[["name","ilike","large cabinet"]]', }) order = self.empty_order self.iPadMini.taxes_id = self.tax_10pc_incl sol1 = self.env['sale.order.line'].create({ 'product_id': self.iPadMini.id, 'name': 'Large Cabinet', 'product_uom_qty': 3.0, 'order_id': order.id, }) # I add delivery cost in Sales order delivery_wizard = Form( self.env['choose.delivery.carrier'].with_context({ 'default_order_id': order.id, 'default_carrier_id': self.carrier.id })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 2) self.assertEqual(order.reward_amount, 0) # Shipping is 20 + 15%tax self.assertEqual( sum([ line.price_total for line in order._get_no_effect_on_threshold_lines() ]), 23) self.assertEqual(order.amount_untaxed, 872.73 + 20) self.env['sale.coupon.apply.code'].sudo().apply_coupon( order, 'free_shipping') order.recompute_coupon_lines() self.assertEqual( len(order.order_line.ids), 3, "We should get the delivery line and the free delivery since we are below 872.73$ with the 10% discount" ) self.assertEqual(order.reward_amount, -20) self.assertEqual( sum([ line.price_total for line in order._get_no_effect_on_threshold_lines() ]), 0) self.assertEqual(order.amount_untaxed, 872.73) sol1.product_uom_qty = 4 order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 4, "We should get a free Large Cabinet") self.assertEqual(order.reward_amount, -20 - 290.91) self.assertEqual( sum([ line.price_total for line in order._get_no_effect_on_threshold_lines() ]), 0) self.assertEqual(order.amount_untaxed, 872.73) p_specific_product = self.env['sale.coupon.program'].create({ 'name': '20% reduction on large cabinet in cart', 'promo_code_usage': 'no_code_needed', 'reward_type': 'discount', 'program_type': 'promotion_program', 'discount_type': 'percentage', 'discount_percentage': 20.0, 'discount_apply_on': 'cheapest_product', }) p_specific_product.discount_apply_on = 'cheapest_product' order.recompute_coupon_lines() # 872.73 - (20% of 1 iPad) = 872.73 - 58.18 = 814.55 self.assertAlmostEqual( order.amount_untaxed, 814.55, 2, "One large cabinet should be discounted by 20%")
def test_22_bom_report_recursive_bom(self): """ Test report with recursive BoM and different quantities. BoM 1: product = Finished (units) quantity = 100 units - Semi-Finished 5 kg BoM 2: product = Semi-Finished (kg) quantity = 11 kg - Assembly 2 dozens BoM 3: product = Assembly (dozens) quantity = 5 dozens - Raw Material 4 litres (product.product 5$/litre) Check the Price for 80 units of Finished -> 2.92$: """ # Create a products templates uom_unit = self.env.ref('uom.product_uom_unit') uom_kg = self.env.ref('uom.product_uom_kgm') uom_dozen = self.env.ref('uom.product_uom_dozen') uom_litre = self.env.ref('uom.product_uom_litre') finished = self.env['product.product'].create({ 'name': 'Finished', 'type': 'product', 'uom_id': uom_unit.id, 'uom_po_id': uom_unit.id, }) semi_finished = self.env['product.product'].create({ 'name': 'Semi-Finished', 'type': 'product', 'uom_id': uom_kg.id, 'uom_po_id': uom_kg.id, }) assembly = self.env['product.product'].create({ 'name': 'Assembly', 'type': 'product', 'uom_id': uom_dozen.id, 'uom_po_id': uom_dozen.id, }) raw_material = self.env['product.product'].create({ 'name': 'Raw Material', 'type': 'product', 'uom_id': uom_litre.id, 'uom_po_id': uom_litre.id, 'standard_price': 5, }) #Create bom bom_finished = Form(self.env['mrp.bom']) bom_finished.product_tmpl_id = finished.product_tmpl_id bom_finished.product_qty = 100 with bom_finished.bom_line_ids.new() as line: line.product_id = semi_finished line.product_uom_id = uom_kg line.product_qty = 5 bom_finished = bom_finished.save() bom_semi_finished = Form(self.env['mrp.bom']) bom_semi_finished.product_tmpl_id = semi_finished.product_tmpl_id bom_semi_finished.product_qty = 11 with bom_semi_finished.bom_line_ids.new() as line: line.product_id = assembly line.product_uom_id = uom_dozen line.product_qty = 2 bom_semi_finished = bom_semi_finished.save() bom_assembly = Form(self.env['mrp.bom']) bom_assembly.product_tmpl_id = assembly.product_tmpl_id bom_assembly.product_qty = 5 with bom_assembly.bom_line_ids.new() as line: line.product_id = raw_material line.product_uom_id = uom_litre line.product_qty = 4 bom_assembly = bom_assembly.save() report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_finished.id, searchQty=80) self.assertAlmostEqual(report_values['lines']['total'], 2.92)
def test_01_delivery_stock_move(self): # Test if the stored fields of stock moves are computed with invoice before delivery flow self.sale_prepaid = self.SaleOrder.create({ 'partner_id': self.partner_18.id, 'partner_invoice_id': self.partner_18.id, 'partner_shipping_id': self.partner_18.id, 'pricelist_id': self.pricelist_id.id, 'order_line': [(0, 0, { 'name': 'Cable Management Box', 'product_id': self.product_cable_management_box.id, 'product_uom_qty': 2, 'product_uom': self.product_uom_unit.id, 'price_unit': 750.00, })], }) # I add delivery cost in Sales order delivery_wizard = Form( self.env['choose.delivery.carrier'].with_context({ 'default_order_id': self.sale_prepaid.id, 'default_carrier_id': self.normal_delivery.id, })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() # I confirm the SO. self.sale_prepaid.action_confirm() self.sale_prepaid._create_invoices() # I check that the invoice was created self.assertEqual(len(self.sale_prepaid.invoice_ids), 1, "Invoice not created.") # I confirm the invoice self.invoice = self.sale_prepaid.invoice_ids self.invoice.action_post() # I pay the invoice. self.journal = self.AccountJournal.search( [('type', '=', 'cash'), ('company_id', '=', self.sale_prepaid.company_id.id)], limit=1) register_payments = self.env['account.payment.register'].with_context( active_model='account.move', active_ids=self.invoice.ids).create({ 'journal_id': self.journal.id, }) register_payments._create_payments() # Check the SO after paying the invoice self.assertNotEqual(self.sale_prepaid.invoice_count, 0, 'order not invoiced') self.assertTrue(self.sale_prepaid.invoice_status == 'invoiced', 'order is not invoiced') self.assertEqual(len(self.sale_prepaid.picking_ids), 1, 'pickings not generated') # Check the stock moves moves = self.sale_prepaid.picking_ids.move_ids self.assertEqual(moves[0].product_qty, 2, 'wrong product_qty') self.assertEqual(moves[0].weight, 2.0, 'wrong move weight') # Ship moves.move_line_ids.write({'qty_done': 2}) self.picking = self.sale_prepaid.picking_ids._action_done() self.assertEqual(moves[0].move_line_ids.sale_price, 1725.0, 'wrong shipping value')
def test_qty_invoiced(self): """Verify uom rounding is correctly considered during qty_invoiced compute""" sale_order = self.env['sale.order'].with_context( tracking_disable=True).create({ 'partner_id': self.partner_a.id, 'partner_invoice_id': self.partner_a.id, 'partner_shipping_id': self.partner_a.id, 'pricelist_id': self.company_data['default_pricelist'].id, }) SaleOrderLine = self.env['sale.order.line'].with_context( tracking_disable=True) sol_prod_deliver = SaleOrderLine.create({ 'product_id': self.company_data['product_order_no'].id, 'product_uom_qty': 5, 'order_id': sale_order.id, 'tax_id': False, }) # Confirm the SO sale_order.action_confirm() sol_prod_deliver.write({'qty_delivered': 5.0}) # Context self.context = { 'active_model': 'sale.order', 'active_ids': [sale_order.id], 'active_id': sale_order.id, 'default_journal_id': self.company_data['default_journal_sale'].id, } # Let's do an invoice with invoiceable lines invoicing_wizard = self.env['sale.advance.payment.inv'].with_context( self.context).create({'advance_payment_method': 'delivered'}) invoicing_wizard.create_invoices() self.assertEqual(sol_prod_deliver.qty_invoiced, 5.0) # We would have to change the digits of the field to # test a greater decimal precision. quantity = 5.13 move_form = Form(sale_order.invoice_ids) with move_form.invoice_line_ids.edit(0) as line_form: line_form.quantity = quantity move_form.save() # Default uom rounding to 0.01 qty_invoiced_field = sol_prod_deliver._fields.get('qty_invoiced') sol_prod_deliver.env.add_to_compute(qty_invoiced_field, sol_prod_deliver) self.assertEqual(sol_prod_deliver.qty_invoiced, quantity) # Rounding to 0.1, should be rounded with UP (ceil) rounding_method # Not floor or half up rounding. sol_prod_deliver.product_uom.rounding *= 10 sol_prod_deliver.product_uom.flush(['rounding']) expected_qty = 5.2 qty_invoiced_field = sol_prod_deliver._fields.get('qty_invoiced') sol_prod_deliver.env.add_to_compute(qty_invoiced_field, sol_prod_deliver) self.assertEqual(sol_prod_deliver.qty_invoiced, expected_qty)
def test_20_bom_report(self): """ Simulate a crumble receipt with mrp and open the bom structure report and check that data insde are correct. """ uom_kg = self.env.ref('uom.product_uom_kgm') uom_litre = self.env.ref('uom.product_uom_litre') crumble = self.env['product.product'].create({ 'name': 'Crumble', 'type': 'product', 'uom_id': uom_kg.id, 'uom_po_id': uom_kg.id, }) butter = self.env['product.product'].create({ 'name': 'Butter', 'type': 'product', 'uom_id': uom_kg.id, 'uom_po_id': uom_kg.id, 'standard_price': 7.01 }) biscuit = self.env['product.product'].create({ 'name': 'Biscuit', 'type': 'product', 'uom_id': uom_kg.id, 'uom_po_id': uom_kg.id, 'standard_price': 1.5 }) bom_form_crumble = Form(self.env['mrp.bom']) bom_form_crumble.product_tmpl_id = crumble.product_tmpl_id bom_form_crumble.product_qty = 11 bom_form_crumble.product_uom_id = uom_kg bom_crumble = bom_form_crumble.save() workcenter = self.env['mrp.workcenter'].create({ 'costs_hour': 10, 'name': 'Deserts Table' }) operation_1 = self.env['mrp.routing.workcenter'].create({ 'workcenter_id': workcenter.id, 'name': 'Prepare biscuits', 'time_cycle_manual': 5, 'bom_id': bom_crumble.id }) operation_2 = self.env['mrp.routing.workcenter'].create({ 'workcenter_id': workcenter.id, 'name': 'Prepare butter', 'time_cycle_manual': 3, 'bom_id': bom_crumble.id }) operation_3 = self.env['mrp.routing.workcenter'].create({ 'workcenter_id': workcenter.id, 'name': 'Mix manually', 'time_cycle_manual': 5, 'bom_id': bom_crumble.id }) with Form(bom_crumble) as bom: with bom.bom_line_ids.new() as line: line.product_id = butter line.product_uom_id = uom_kg line.product_qty = 5 with bom.bom_line_ids.new() as line: line.product_id = biscuit line.product_uom_id = uom_kg line.product_qty = 6 bom.operation_ids.add(operation_1) bom.operation_ids.add(operation_2) bom.operation_ids.add(operation_3) # TEST BOM STRUCTURE VALUE WITH BOM QUANTITY report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_crumble.id, searchQty=11, searchVariant=False) # 5 min 'Prepare biscuits' + 3 min 'Prepare butter' + 5 min 'Mix manually' = 13 minutes self.assertEqual(report_values['lines']['operations_time'], 13.0, 'Operation time should be the same for 1 unit or for the batch') # Operation cost is the sum of operation line. operation_cost = float_round(5 / 60 * 10, precision_digits=2) * 2 + float_round(3 / 60 * 10, precision_digits=2) self.assertEqual(float_compare(report_values['lines']['operations_cost'], operation_cost, precision_digits=2), 0, '13 minute for 10$/hours -> 2.16') for component_line in report_values['lines']['components']: # standard price * bom line quantity * current quantity / bom finished product quantity if component_line['prod_id'] == butter.id: # 5 kg of butter at 7.01$ for 11kg of crumble -> 35.05$ self.assertEqual(float_compare(component_line['total'], (7.01 * 5), precision_digits=2), 0) if component_line['prod_id'] == biscuit.id: # 6 kg of biscuits at 1.50$ for 11kg of crumble -> 9$ self.assertEqual(float_compare(component_line['total'], (1.5 * 6), precision_digits=2), 0) # total price = 35.05 + 9 + operation_cost(0.83 + 0.83 + 0.5 = 2.16) = 46,21 self.assertEqual(float_compare(report_values['lines']['total'], 46.21, precision_digits=2), 0, 'Product Bom Price is not correct') self.assertEqual(float_compare(report_values['lines']['total'] / 11.0, 4.20, precision_digits=2), 0, 'Product Unit Bom Price is not correct') # TEST BOM STRUCTURE VALUE BY UNIT report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_crumble.id, searchQty=1, searchVariant=False) # 5 min 'Prepare biscuits' + 3 min 'Prepare butter' + 5 min 'Mix manually' = 13 minutes self.assertEqual(report_values['lines']['operations_time'], 13.0, 'Operation time should be the same for 1 unit or for the batch') # Operation cost is the sum of operation line. operation_cost = float_round(5 / 60 * 10, precision_digits=2) * 2 + float_round(3 / 60 * 10, precision_digits=2) self.assertEqual(float_compare(report_values['lines']['operations_cost'], operation_cost, precision_digits=2), 0, '13 minute for 10$/hours -> 2.16') for component_line in report_values['lines']['components']: # standard price * bom line quantity * current quantity / bom finished product quantity if component_line['prod_id'] == butter.id: # 5 kg of butter at 7.01$ for 11kg of crumble -> / 11 for price per unit (3.19) self.assertEqual(float_compare(component_line['total'], (7.01 * 5) * (1 / 11), precision_digits=2), 0) if component_line['prod_id'] == biscuit.id: # 6 kg of biscuits at 1.50$ for 11kg of crumble -> / 11 for price per unit (0.82) self.assertEqual(float_compare(component_line['total'], (1.5 * 6) * (1 / 11), precision_digits=2), 0) # total price = 3.19 + 0.82 + operation_cost(0.83 + 0.83 + 0.5 = 2.16) = 6,17 self.assertEqual(float_compare(report_values['lines']['total'], 6.17, precision_digits=2), 0, 'Product Unit Bom Price is not correct') # TEST OPERATION COST WHEN PRODUCED QTY > BOM QUANTITY report_values_12 = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_crumble.id, searchQty=12, searchVariant=False) report_values_22 = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_crumble.id, searchQty=22, searchVariant=False) operation_cost = float_round(10 / 60 * 10, precision_digits=2) * 2 + float_round(6 / 60 * 10, precision_digits=2) # Both needs 2 operation cycle self.assertEqual(report_values_12['lines']['operations_cost'], report_values_22['lines']['operations_cost']) self.assertEqual(report_values_22['lines']['operations_cost'], operation_cost) report_values_23 = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_crumble.id, searchQty=23, searchVariant=False) operation_cost = float_round(15 / 60 * 10, precision_digits=2) * 2 + float_round(9 / 60 * 10, precision_digits=2) self.assertEqual(report_values_23['lines']['operations_cost'], operation_cost) # Create a more complex BoM with a sub product cheese_cake = self.env['product.product'].create({ 'name': 'Cheese Cake 300g', 'type': 'product', }) cream = self.env['product.product'].create({ 'name': 'cream', 'type': 'product', 'uom_id': uom_litre.id, 'uom_po_id': uom_litre.id, 'standard_price': 5.17, }) bom_form_cheese_cake = Form(self.env['mrp.bom']) bom_form_cheese_cake.product_tmpl_id = cheese_cake.product_tmpl_id bom_form_cheese_cake.product_qty = 60 bom_form_cheese_cake.product_uom_id = self.uom_unit bom_cheese_cake = bom_form_cheese_cake.save() workcenter_2 = self.env['mrp.workcenter'].create({ 'name': 'cake mounting', 'costs_hour': 20, 'time_start': 10, 'time_stop': 15 }) operation_4 = self.env['mrp.routing.workcenter'].create({ 'workcenter_id': workcenter.id, 'name': 'Mix cheese and crumble', 'time_cycle_manual': 10, 'bom_id': bom_cheese_cake.id }) operation_5 = self.env['mrp.routing.workcenter'].create({ 'workcenter_id': workcenter_2.id, 'name': 'Cake mounting', 'time_cycle_manual': 5, 'bom_id': bom_cheese_cake.id }) with Form(bom_cheese_cake) as bom: with bom.bom_line_ids.new() as line: line.product_id = cream line.product_uom_id = uom_litre line.product_qty = 3 with bom.bom_line_ids.new() as line: line.product_id = crumble line.product_uom_id = uom_kg line.product_qty = 5.4 bom.operation_ids.add(operation_4) bom.operation_ids.add(operation_5) # TEST CHEESE BOM STRUCTURE VALUE WITH BOM QUANTITY report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_cheese_cake.id, searchQty=60, searchVariant=False) self.assertEqual(report_values['lines']['operations_time'], 40.0, 'Operation time should be the same for 1 unit or for the batch') # Operation cost is the sum of operation line. operation_cost = float_round(10 / 60 * 10, precision_digits=2) + float_round(30 / 60 * 20, precision_digits=2) self.assertEqual(float_compare(report_values['lines']['operations_cost'], operation_cost, precision_digits=2), 0) for component_line in report_values['lines']['components']: # standard price * bom line quantity * current quantity / bom finished product quantity if component_line['prod_id'] == cream.id: # 3 liter of cream at 5.17$ for 60 unit of cheese cake -> 15.51$ self.assertEqual(float_compare(component_line['total'], (3 * 5.17), precision_digits=2), 0) if component_line['prod_id'] == crumble.id: # 5.4 kg of crumble at the cost of a batch. crumble_cost = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_crumble.id, searchQty=5.4, searchVariant=False)['lines']['total'] self.assertEqual(float_compare(component_line['total'], crumble_cost, precision_digits=2), 0) # total price = 15.51 + crumble_cost + operation_cost(10 + 1.67 = 11.67) = 27.18 + crumble_cost self.assertEqual(float_compare(report_values['lines']['total'], 27.18 + crumble_cost, precision_digits=2), 0, 'Product Bom Price is not correct')
def test_toggle_active_warehouse_1(self): """ Basic test that create a warehouse with classic configuration. Archive it and check that locations, picking types, routes, rules are correclty active or archive. """ wh = Form(self.env['stock.warehouse']) wh.name = "The attic of Willy" wh.code = "WIL" warehouse = wh.save() custom_location = Form(self.env['stock.location']) custom_location.name = "A Trunk" custom_location.location_id = warehouse.lot_stock_id custom_location = custom_location.save() # Archive warehouse warehouse.toggle_active() # Global rule self.assertFalse(warehouse.mto_pull_id.active) # Route self.assertFalse(warehouse.reception_route_id.active) self.assertFalse(warehouse.delivery_route_id.active) # Location self.assertFalse(warehouse.lot_stock_id.active) self.assertFalse(warehouse.wh_input_stock_loc_id.active) self.assertFalse(warehouse.wh_qc_stock_loc_id.active) self.assertFalse(warehouse.wh_output_stock_loc_id.active) self.assertFalse(warehouse.wh_pack_stock_loc_id.active) self.assertFalse(custom_location.active) # Picking Type self.assertFalse(warehouse.in_type_id.active) self.assertFalse(warehouse.out_type_id.active) self.assertFalse(warehouse.int_type_id.active) self.assertFalse(warehouse.pick_type_id.active) self.assertFalse(warehouse.pack_type_id.active) # Active warehouse warehouse.toggle_active() # Global rule self.assertTrue(warehouse.mto_pull_id.active) # Route self.assertTrue(warehouse.reception_route_id.active) self.assertTrue(warehouse.delivery_route_id.active) # Location self.assertTrue(warehouse.lot_stock_id.active) self.assertFalse(warehouse.wh_input_stock_loc_id.active) self.assertFalse(warehouse.wh_qc_stock_loc_id.active) self.assertFalse(warehouse.wh_output_stock_loc_id.active) self.assertFalse(warehouse.wh_pack_stock_loc_id.active) self.assertTrue(custom_location.active) # Picking Type self.assertTrue(warehouse.in_type_id.active) self.assertTrue(warehouse.out_type_id.active) self.assertTrue(warehouse.int_type_id.active) self.assertFalse(warehouse.pick_type_id.active) self.assertFalse(warehouse.pack_type_id.active)
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' 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_pack_in_receipt_two_step_multi_putaway(self): """ Checks all works right in the following specific corner case: * For a two-step receipt, receives two products using two putaways targeting different locations. * Puts these products in a package then valid the receipt. * Cancels the automatically generated internal transfer then create a new one. * In this internal transfer, adds the package then valid it. """ grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_multi_step_rule = self.env.ref('stock.group_adv_location') grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(3, grp_multi_loc.id)]}) self.env.user.write({'groups_id': [(3, grp_multi_step_rule.id)]}) self.env.user.write({'groups_id': [(3, grp_pack.id)]}) self.warehouse.reception_steps = 'two_steps' # Settings of receipt. self.warehouse.in_type_id.show_operations = True self.warehouse.in_type_id.show_entire_packs = True # Settings of internal transfer. self.warehouse.int_type_id.show_operations = True self.warehouse.int_type_id.show_entire_packs = True # Creates two new locations for putaway. location_form = Form(self.env['stock.location']) location_form.name = 'Shelf A' location_form.location_id = self.stock_location loc_shelf_A = location_form.save() location_form = Form(self.env['stock.location']) location_form.name = 'Shelf B' location_form.location_id = self.stock_location loc_shelf_B = location_form.save() # Creates a new putaway rule for productA and productB. putaway = self.env['product.putaway'].create({ 'name': 'Stock Putaway', 'product_location_ids': [(0, 0, { 'product_id': self.productA.id, 'fixed_location_id': loc_shelf_A.id, }), (0, 0, { 'product_id': self.productB.id, 'fixed_location_id': loc_shelf_B.id, })] }) location_form = Form(self.stock_location) location_form.putaway_strategy_id = putaway self.stock_location = location_form.save() # Create a new receipt with the two products. receipt_form = Form(self.env['stock.picking']) receipt_form.picking_type_id = self.warehouse.in_type_id # Add 2 lines with receipt_form.move_ids_without_package.new() as move_line: move_line.product_id = self.productA move_line.product_uom_qty = 1 with receipt_form.move_ids_without_package.new() as move_line: move_line.product_id = self.productB move_line.product_uom_qty = 1 receipt = receipt_form.save() receipt.action_confirm() # Adds quantities then packs them and valids the receipt. receipt_form = Form(receipt) with receipt_form.move_line_ids_without_package.edit(0) as move_line: move_line.qty_done = 1 with receipt_form.move_line_ids_without_package.edit(1) as move_line: move_line.qty_done = 1 receipt = receipt_form.save() receipt.put_in_pack() receipt.button_validate() receipt_package = receipt.package_level_ids_details[0] self.assertEqual(receipt_package.location_dest_id.id, receipt.location_dest_id.id) self.assertEqual(receipt_package.move_line_ids[0].location_dest_id.id, receipt.location_dest_id.id) self.assertEqual(receipt_package.move_line_ids[1].location_dest_id.id, receipt.location_dest_id.id) # Checks an internal transfer was created following the validation of the receipt. internal_transfer = self.env['stock.picking'].search( [('picking_type_id', '=', self.warehouse.int_type_id.id)], order='id desc', limit=1) self.assertEqual(internal_transfer.origin, receipt.name) self.assertEqual(len(internal_transfer.package_level_ids_details), 1) internal_package = internal_transfer.package_level_ids_details[0] self.assertEqual(internal_package.location_dest_id.id, internal_transfer.location_dest_id.id) self.assertNotEqual( internal_package.location_dest_id.id, putaway.product_location_ids[0].fixed_location_id.id, "The package destination location must be the one from the picking." ) self.assertNotEqual( internal_package.move_line_ids[0].location_dest_id.id, putaway.product_location_ids[0].fixed_location_id.id, "The move line destination location must be the one from the picking." ) self.assertNotEqual( internal_package.move_line_ids[1].location_dest_id.id, putaway.product_location_ids[0].fixed_location_id.id, "The move line destination location must be the one from the picking." ) # Cancels the internal transfer and creates a new one. internal_transfer.action_cancel() internal_form = Form(self.env['stock.picking']) internal_form.picking_type_id = self.warehouse.int_type_id internal_form.location_id = self.warehouse.wh_input_stock_loc_id with internal_form.package_level_ids_details.new() as pack_line: pack_line.package_id = receipt_package.package_id internal_transfer = internal_form.save() # Checks the package fields have been correctly set. internal_package = internal_transfer.package_level_ids_details[0] self.assertEqual(internal_package.location_dest_id.id, internal_transfer.location_dest_id.id) internal_transfer.action_assign() self.assertEqual(internal_package.location_dest_id.id, internal_transfer.location_dest_id.id) self.assertNotEqual( internal_package.location_dest_id.id, putaway.product_location_ids[0].fixed_location_id.id, "The package destination location must be the one from the picking." ) self.assertNotEqual( internal_package.move_line_ids[0].location_dest_id.id, putaway.product_location_ids[0].fixed_location_id.id, "The move line destination location must be the one from the picking." ) self.assertNotEqual( internal_package.move_line_ids[1].location_dest_id.id, putaway.product_location_ids[0].fixed_location_id.id, "The move line destination location must be the one from the picking." ) internal_transfer.button_validate()
def test_action_assign_package_level(self): """calling _action_assign on move does not erase lines' "result_package_id" At the end of the method ``StockMove._action_assign()``, the method ``StockPicking._check_entire_pack()`` is called. This method compares the move lines with the quants of their source package, and if the entire package is moved at once in the same transfer, a ``stock.package_level`` is created. On creation of a ``stock.package_level``, the result package of the move lines is directly updated with the entire package. This is good on the first assign of the move, but when we call assign for the second time on a move, for instance because it was made partially available and we want to assign the remaining, it can override the result package we selected before. An override of ``StockPicking._check_move_lines_map_quant_package()`` ensures that we ignore: * picked lines (qty_done > 0) * lines with a different result package already """ package = self.env["stock.quant.package"].create({"name": "Src Pack"}) dest_package1 = self.env["stock.quant.package"].create( {"name": "Dest Pack1"}) # Create new picking: 120 productA picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.warehouse.pick_type_id with picking_form.move_ids_without_package.new() as move_line: move_line.product_id = self.productA move_line.product_uom_qty = 120 picking = picking_form.save() # mark as TO-DO picking.action_confirm() # Update quantity on hand: 100 units in package self.env['stock.quant']._update_available_quantity(self.productA, self.stock_location, 100, package_id=package) # Check Availability picking.action_assign() self.assertEqual(picking.state, "assigned") self.assertEqual(picking.package_level_ids.package_id, package) move = picking.move_lines line = move.move_line_ids # change the result package and set a qty_done line.qty_done = 100 line.result_package_id = dest_package1 # Update quantity on hand: 20 units in new_package new_package = self.env["stock.quant.package"].create( {"name": "New Pack"}) self.env['stock.quant']._update_available_quantity( self.productA, self.stock_location, 20, package_id=new_package) # Check Availability picking.action_assign() # Check that result package is not changed on first line new_line = move.move_line_ids - line self.assertRecordValues( line + new_line, [ { "qty_done": 100, "result_package_id": dest_package1.id }, { "qty_done": 0, "result_package_id": new_package.id }, ], )
def test_standard_delivered_invoice_post_partial_delivery(self): """Standard price set to 10. Get 2 units in stock. Sale order 2@12. Deliver 1, invoice 1, change the standard price to 14, deliver one, change the standard price to 16, invoice 1. The amounts used in Stock OUT and COGS should be 10 then 14.""" self.product.categ_id.property_cost_method = 'standard' self.product.invoice_policy = 'delivery' self.product.standard_price = 10 # Put two items in stock. sale_order = self._so_and_confirm_two_units() # Create and confirm a sale order for 2@12 sale_order = self._so_and_confirm_two_units() # Deliver one. sale_order.picking_ids.move_lines.quantity_done = 1 wiz = sale_order.picking_ids.button_validate() wiz = Form(self.env[wiz['res_model']].with_context(wiz['context'])).save() wiz.process() # Invoice 1 invoice = sale_order._create_invoices() invoice_form = Form(invoice) with invoice_form.invoice_line_ids.edit(0) as invoice_line: invoice_line.quantity = 1 invoice_form.save() invoice.action_post() # Check the resulting accounting entries amls = invoice.line_ids self.assertEqual(len(amls), 4) stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out']) self.assertEqual(stock_out_aml.debit, 0) self.assertEqual(stock_out_aml.credit, 10) cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense']) self.assertEqual(cogs_aml.debit, 10) self.assertEqual(cogs_aml.credit, 0) receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable']) self.assertEqual(receivable_aml.debit, 12) self.assertEqual(receivable_aml.credit, 0) income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue']) self.assertEqual(income_aml.debit, 0) self.assertEqual(income_aml.credit, 12) # change the standard price to 14 self.product.standard_price = 14.0 # deliver the backorder sale_order.picking_ids[0].move_lines.quantity_done = 1 sale_order.picking_ids[0].button_validate() # change the standard price to 16 self.product.standard_price = 16.0 # invoice 1 invoice2 = sale_order._create_invoices() invoice2.action_post() amls = invoice2.line_ids self.assertEqual(len(amls), 4) stock_out_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_stock_out']) self.assertEqual(stock_out_aml.debit, 0) self.assertEqual(stock_out_aml.credit, 14) cogs_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_expense']) self.assertEqual(cogs_aml.debit, 14) self.assertEqual(cogs_aml.credit, 0) receivable_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_receivable']) self.assertEqual(receivable_aml.debit, 12) self.assertEqual(receivable_aml.credit, 0) income_aml = amls.filtered(lambda aml: aml.account_id == self.company_data['default_account_revenue']) self.assertEqual(income_aml.debit, 0) self.assertEqual(income_aml.credit, 12)
def test_reconcile_payment(self): tax = self.env['account.tax'].create({ 'name': 'tax abc', 'type_tax_use': 'purchase', 'amount_type': 'percent', 'amount': 15, 'price_include': False, 'include_base_amount': False, 'tax_exigibility': 'on_payment' }) current_assets_type = self.env.ref( 'account.data_account_type_current_assets') company = self.env.company.id tax.cash_basis_transition_account_id = self.env[ 'account.account'].create({ 'name': "test", 'code': 999991, 'reconcile': True, 'user_type_id': current_assets_type.id, 'company_id': company, }).id sheet = self.env['hr.expense.sheet'].create({ 'company_id': company, 'employee_id': self.expense_employee.id, 'name': 'test sheet', 'expense_line_ids': [ (0, 0, { 'name': 'expense_1', 'date': '2016-01-01', 'product_id': self.product_a.id, 'unit_amount': 10.0, 'employee_id': self.expense_employee.id, 'tax_ids': tax }), (0, 0, { 'name': 'expense_2', 'date': '2016-01-01', 'product_id': self.product_a.id, 'unit_amount': 1.0, 'employee_id': self.expense_employee.id, 'tax_ids': tax }), ], }) #actions sheet.action_submit_sheet() sheet.approve_expense_sheets() sheet.action_sheet_move_create() action_data = sheet.action_register_payment() wizard = Form(self.env['account.payment.register'].with_context( action_data['context'])).save() wizard.action_create_payments() self.assertEqual( sheet.state, 'done', 'all account.move.line linked to expenses must be reconciled after payment' )
def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict( cls.env.context, tracking_disable=True, no_reset_password=True)) # Create new user allowed to change invoice due date group = cls.env.ref( "account_invoice_date_due.allow_to_change_due_date") acc_group = cls.env.ref("account.group_account_manager") # Loose dependency on stock to avoid perm issues. # We don't really care about such permissions in this context! # Eg: # odoo.exceptions.AccessError: # You are not allowed to access 'Stock Valuation Layer' (stock.valuation.layer) records. stock_group = (cls.env.ref("stock.group_stock_manager", False) or cls.env["res.groups"]) cls.user_w_access = cls.env["res.users"].create({ "name": "Test User w/ access", "login": "******", "email": "*****@*****.**", "groups_id": [(6, 0, (group + acc_group + stock_group).ids)], }) # Create new user not allowed to change invoice due date cls.user_wo_access = cls.env["res.users"].create({ "name": "Test User wo/ access", "login": "******", "groups_id": [(6, 0, (acc_group + stock_group).ids)], }) account100 = cls.env["account.account"].create({ "code": "100", "name": "Account 100", "user_type_id": cls.env.ref("account.data_account_type_receivable").id, "reconcile": True, }) account300 = cls.env["account.account"].create({ "code": "300", "name": "Account 300", "user_type_id": cls.env.ref("account.data_account_type_other_income").id, }) move_form = Form(cls.env["account.move"]) move_form.date = fields.Date.today() with move_form.invoice_line_ids.new() as line_form: line_form.name = "move test" line_form.debit = 0.0 line_form.credit = 1000.0 line_form.account_id = account300 with move_form.invoice_line_ids.new() as line_form: line_form.name = "move test" line_form.debit = 1000.0 line_form.credit = 0.0 line_form.account_id = account100 cls.move = move_form.save()
def test_00_delivery_cost(self): # In order to test Carrier Cost # Create sales order with Normal Delivery Charges self.sale_normal_delivery_charges = self.SaleOrder.create({ 'partner_id': self.partner_18.id, 'partner_invoice_id': self.partner_18.id, 'partner_shipping_id': self.partner_18.id, 'pricelist_id': self.pricelist.id, 'order_line': [(0, 0, { 'name': 'PC Assamble + 2GB RAM', 'product_id': self.product_4.id, 'product_uom_qty': 1, 'product_uom': self.product_uom_unit.id, 'price_unit': 750.00, })], }) # I add delivery cost in Sales order self.a_sale = self.AccountAccount.create({ 'code': 'X2020', 'name': 'Product Sales - (test)', 'user_type_id': self.account_data.id, 'tag_ids': [(6, 0, { self.account_tag_operating.id })] }) self.product_consultant = self.Product.create({ 'sale_ok': True, 'list_price': 75.0, 'standard_price': 30.0, 'uom_id': self.product_uom_hour.id, 'uom_po_id': self.product_uom_hour.id, 'name': 'Service', 'categ_id': self.product_category.id, 'type': 'service' }) # I add delivery cost in Sales order delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({ 'default_order_id': self.sale_normal_delivery_charges.id, 'default_carrier_id': self.normal_delivery.id })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() # I check sales order after added delivery cost line = self.SaleOrderLine.search([('order_id', '=', self.sale_normal_delivery_charges.id), ('product_id', '=', self.normal_delivery.product_id.id)]) self.assertEqual(len(line), 1, "Delivery cost is not Added") self.assertEqual(float_compare(line.price_subtotal, 10.0, precision_digits=2), 0, "Delivery cost is not correspond.") # I confirm the sales order self.sale_normal_delivery_charges.action_confirm() # Create one more sales order with Free Delivery Charges self.delivery_sale_order_cost = self.SaleOrder.create({ 'partner_id': self.partner_4.id, 'partner_invoice_id': self.partner_address_13.id, 'partner_shipping_id': self.partner_address_13.id, 'pricelist_id': self.pricelist.id, 'order_line': [(0, 0, { 'name': 'Service on demand', 'product_id': self.product_consultant.id, 'product_uom_qty': 24, 'product_uom': self.product_uom_hour.id, 'price_unit': 75.00, }), (0, 0, { 'name': 'On Site Assistance', 'product_id': self.product_2.id, 'product_uom_qty': 30, 'product_uom': self.product_uom_hour.id, 'price_unit': 38.25, })], }) # I add free delivery cost in Sales order delivery_wizard = Form(self.env['choose.delivery.carrier'].with_context({ 'default_order_id': self.delivery_sale_order_cost.id, 'default_carrier_id': self.free_delivery.id })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() # I check sales order after adding delivery cost line = self.SaleOrderLine.search([('order_id', '=', self.delivery_sale_order_cost.id), ('product_id', '=', self.free_delivery.product_id.id)]) self.assertEqual(len(line), 1, "Delivery cost is not Added") self.assertEqual(float_compare(line.price_subtotal, 0, precision_digits=2), 0, "Delivery cost is not correspond.") # I set default delivery policy self.default_delivery_policy = self.SaleConfigSetting.create({}) self.default_delivery_policy.execute()
def test_free_shipping_reward(self): # Test case 1: The minimum amount is not reached, the reward should # not be created self.immediate_promotion_program.active = False self.env['sale.coupon.program'].create({ 'name': 'Free Shipping if at least 100 euros', 'promo_code_usage': 'no_code_needed', 'reward_type': 'free_shipping', 'rule_minimum_amount': 100.0, 'rule_minimum_amount_tax_inclusion': 'tax_included', 'active': True, }) order = self.env['sale.order'].create({ 'partner_id': self.steve.id, }) # Price of order will be 5*1.15 = 5.75 (tax included) order.write({ 'order_line': [(0, False, { 'product_id': self.product_B.id, 'name': 'Product B', 'product_uom': self.uom_unit.id, 'product_uom_qty': 1.0, })] }) order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 1) # I add delivery cost in Sales order delivery_wizard = Form( self.env['choose.delivery.carrier'].with_context({ 'default_order_id': order.id, 'default_carrier_id': self.env['delivery.carrier'].search([])[1] })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 2) # Test Case 1b: amount is not reached but is on a threshold # The amount of deliverable product + the one of the delivery exceeds the minimum amount # yet the program shouldn't be applied # Order price will be 5.75 + 81.74*1.15 = 99.75 order.write({ 'order_line': [(0, False, { 'product_id': self.product_B.id, 'name': 'Product 1B', 'product_uom': self.uom_unit.id, 'product_uom_qty': 1.0, 'price_unit': 81.74, })] }) order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 3) # Test case 2: the amount is sufficient, the shipping should # be reimbursed order.write({ 'order_line': [(0, False, { 'product_id': self.product_A.id, 'name': 'Product 1', 'product_uom': self.uom_unit.id, 'product_uom_qty': 1.0, 'price_unit': 0.30, })] }) order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 5) # Test case 3: the amount is not sufficient now, the reward should be removed order.write({ 'order_line': [(2, order.order_line.filtered( lambda line: line.product_id.id == self.product_A.id).id, False)] }) order.recompute_coupon_lines() self.assertEqual(len(order.order_line.ids), 3)
def test_vendor_bill_flow_continental_1(self): """In continental accounting, receive 10@10 and invoice. Then invoice 1@50 as a landed costs and create a linked landed costs record. """ self.env.company.anglo_saxon_accounting = False # Create an RFQ for self.product1, 10@10 rfq = Form(self.env['purchase.order']) rfq.partner_id = self.vendor1 with rfq.order_line.new() as po_line: po_line.product_id = self.product1 po_line.product_qty = 10 po_line.price_unit = 10 po_line.taxes_id.clear() rfq = rfq.save() rfq.button_confirm() # Process the receipt receipt = rfq.picking_ids wiz = receipt.button_validate() wiz = Form(self.env['stock.immediate.transfer'].with_context( wiz['context'])).save().process() self.assertEqual(rfq.order_line.qty_received, 10) input_aml = self._get_stock_input_move_lines()[-1] self.assertEqual(input_aml.debit, 0) self.assertEqual(input_aml.credit, 100) valuation_aml = self._get_stock_valuation_move_lines()[-1] self.assertEqual(valuation_aml.debit, 100) self.assertEqual(valuation_aml.credit, 0) # Create a vebdor bill for the RFQ action = rfq.action_create_invoice() vb = self.env['account.move'].browse(action['res_id']) vb.action_post() expense_aml = self._get_expense_move_lines()[-1] self.assertEqual(expense_aml.debit, 100) self.assertEqual(expense_aml.credit, 0) payable_aml = self._get_payable_move_lines()[-1] self.assertEqual(payable_aml.debit, 0) self.assertEqual(payable_aml.credit, 100) # Create a vendor bill for a landed cost product, post it and validate a landed cost # linked to this vendor bill. LC; 1@50 lcvb = Form(self.env['account.move'].with_context( default_move_type='in_invoice')) lcvb.partner_id = self.vendor2 with lcvb.invoice_line_ids.new() as inv_line: inv_line.product_id = self.productlc1 inv_line.price_unit = 50 inv_line.is_landed_costs_line = True with lcvb.invoice_line_ids.edit(0) as inv_line: inv_line.tax_ids.clear() lcvb = lcvb.save() lcvb.action_post() expense_aml = self._get_expense_move_lines()[-1] self.assertEqual(expense_aml.debit, 50) self.assertEqual(expense_aml.credit, 0) payable_aml = self._get_payable_move_lines()[-1] self.assertEqual(payable_aml.debit, 0) self.assertEqual(payable_aml.credit, 50) action = lcvb.button_create_landed_costs() lc = Form(self.env[action['res_model']].browse(action['res_id'])) lc.picking_ids.add(receipt) lc = lc.save() lc.button_validate() self.assertEqual(lc.cost_lines.price_unit, 50) self.assertEqual(lc.cost_lines.product_id, self.productlc1) expense_aml = self._get_expense_move_lines()[-1] self.assertEqual(expense_aml.debit, 0) self.assertEqual(expense_aml.credit, 50) valuation_aml = self._get_stock_valuation_move_lines()[-1] self.assertEqual(valuation_aml.debit, 50) self.assertEqual(valuation_aml.credit, 0) self.assertEqual(self.product1.quantity_svl, 10) self.assertEqual(self.product1.value_svl, 150)
def test_mrp_subcontracting_dropshipping_1(self): """ Mark the subcontracted product with the route dropship and add the subcontractor as seller. The component has the routes 'MTO', 'Replenish on order' and 'Buy'. Also another partner is set as vendor on the comp. Create a SO and check that: - Delivery between subcontractor and customer for subcontracted product. - Delivery for the component to the subcontractor for the specified wh. - Po created for the component. """ mto_route = self.env['stock.location.route'].search([ ('name', '=', 'Replenish on Order (MTO)') ]) resupply_route = self.env['stock.location.route'].search([ ('name', '=', 'Resupply Subcontractor on Order') ]) buy_route = self.env['stock.location.route'].search([('name', '=', 'Buy')]) dropship_route = self.env['stock.location.route'].search([ ('name', '=', 'Dropship') ]) self.comp2.write({ 'route_ids': [(4, buy_route.id), (4, mto_route.id), (4, resupply_route.id)] }) self.finished.write({'route_ids': [(4, dropship_route.id)]}) warehouse = self.env['stock.warehouse'].create({ 'name': 'Warehouse For subcontract', 'code': 'WFS' }) self.env['product.supplierinfo'].create({ 'product_tmpl_id': self.finished.product_tmpl_id.id, 'name': self.subcontractor_partner1.id }) partner = self.env['res.partner'].create({'name': 'Toto'}) self.env['product.supplierinfo'].create({ 'product_tmpl_id': self.comp2.product_tmpl_id.id, 'name': partner.id }) # Create a receipt picking from the subcontractor so_form = Form(self.env['sale.order']) so_form.partner_id = partner so_form.warehouse_id = warehouse with so_form.order_line.new() as line: line.product_id = self.finished line.product_uom_qty = 1 so = so_form.save() so.action_confirm() # Pickings should directly be created po = self.env['purchase.order'].search([('origin', 'ilike', so.name)]) self.assertTrue(po) po.button_approve() picking_finished = po.picking_ids self.assertEqual(len(picking_finished), 1.0) self.assertEqual(picking_finished.location_dest_id, partner.property_stock_customer) self.assertEqual(picking_finished.location_id, self.subcontractor_partner1.property_stock_supplier) self.assertEqual(picking_finished.state, 'assigned') picking_delivery = self.env['stock.move'].search([ ('product_id', '=', self.comp2.id), ('location_id', '=', warehouse.lot_stock_id.id), ('location_dest_id', '=', self.subcontractor_partner1.property_stock_subcontractor.id), ]).picking_id self.assertTrue(picking_delivery) self.assertEqual(picking_delivery.state, 'waiting') po = self.env['purchase.order.line'].search([ ('product_id', '=', self.comp2.id), ('partner_id', '=', partner.id), ]).order_id self.assertTrue(po)
def test_00_shipping_info(self): # Create sale order with Normal Delivery Charges self.percent_tax = self.tax_model.create({ "name": "Percent tax", "amount_type": "percent", "amount": 10, "sequence": 3, }) self.normal_delivery.product_id.taxes_id = self.percent_tax self.sale = self.SaleOrder.create({ "partner_id": self.partner_18.id, "partner_invoice_id": self.partner_18.id, "partner_shipping_id": self.partner_18.id, "pricelist_id": self.pricelist.id, "order_line": [( 0, 0, { "name": "PC Assamble + 2GB RAM", "product_id": self.product_4.id, "product_uom_qty": 1, "product_uom": self.product_uom_unit.id, "price_unit": 750.00, "tax_id": [(4, self.percent_tax.id, 0)], }, )], "carrier_id": self.normal_delivery.id, }) # set delivery cost in Sales order delivery_wizard = Form( self.env["choose.delivery.carrier"].with_context({ "default_order_id": self.sale.id, "default_carrier_id": self.normal_delivery.id, })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() # check sale order computed field after added delivery cost line = self.SaleOrderLine.search([ ("order_id", "=", self.sale.id), ("product_id", "=", self.sale.carrier_id.product_id.id), ]) self.assertEqual(len(line), 1, "Delivery cost is not Added") self.assertEqual( float_compare(line.price_subtotal, 10, precision_digits=2), 0, "Sale line delivery price subtotal is not correct", ) self.assertEqual( float_compare(line.price_total, 11, precision_digits=2), 0, "Sale line delivery price total is not correct", ) self.assertEqual( float_compare(line.price_tax, 1, precision_digits=2), 0, "Sale line delivery price tax is not correct", ) self.assertEqual( float_compare(self.sale.shipping_amount_tax, 1, precision_digits=2), 0, "Shipping amount tax is not correct", ) self.assertEqual( float_compare(self.sale.shipping_amount_untaxed, 10, precision_digits=2), 0, "Shipping amount untaxed is not correct", ) self.assertEqual( float_compare(self.sale.shipping_amount_total, 11, precision_digits=2), 0, "Shipping amount total is not correct", ) self.assertEqual( float_compare(self.sale.item_amount_tax, 75, precision_digits=2), 0, "Item amount tax is not correct", ) self.assertEqual( float_compare(self.sale.item_amount_untaxed, 750, precision_digits=2), 0, "Item amount untaxed is not correct", ) self.assertEqual( float_compare(self.sale.item_amount_total, 825.0, precision_digits=2), 0, "Item amount total is not correct", )
def test_mrp_subcontracting_purchase_2(self): """Let's consider a subcontracted BOM with 1 component. Tick "Resupply Subcontractor on Order" on the component and set a supplier on it. Purchase 1 BOM to the subcontractor. Confirm the purchase and change the purchased quantity to 2. Check that 2 components are delivered to the subcontractor """ # Tick "resupply subconractor on order on component" self.bom.bom_line_ids = [(5, 0, 0)] self.bom.bom_line_ids = [(0, 0, { 'product_id': self.comp1.id, 'product_qty': 1 })] resupply_sub_on_order_route = self.env['stock.location.route'].search([ ('name', '=', 'Resupply Subcontractor on Order') ]) (self.comp1).write( {'route_ids': [(4, resupply_sub_on_order_route.id, None)]}) # Create a supplier and set it to component vendor = self.env['res.partner'].create({ 'name': 'AAA', 'email': '*****@*****.**' }) supplier_info1 = self.env['product.supplierinfo'].create({ 'name': vendor.id, 'price': 50, }) self.comp1.write({ 'seller_ids': [(0, 0, { 'name': vendor.id, 'product_code': 'COMP1' })] }) # Purchase 1 BOM to the subcontractor po = Form(self.env['purchase.order']) po.partner_id = self.subcontractor_partner1 with po.order_line.new() as po_line: po_line.product_id = self.finished po_line.product_qty = 1 po_line.price_unit = 100 po = po.save() # Confirm the purchase po.button_confirm() # Check one delivery order with the component has been created for the subcontractor mo = self.env['mrp.production'].search([('bom_id', '=', self.bom.id)]) self.assertEqual(mo.state, 'confirmed') # Check that 1 delivery with 1 component for the subcontractor has been created picking_delivery = mo.picking_ids wh = picking_delivery.picking_type_id.warehouse_id origin = picking_delivery.origin self.assertEqual(len(picking_delivery), 1) self.assertEqual(len(picking_delivery.move_ids_without_package), 1) self.assertEqual(picking_delivery.picking_type_id, wh.out_type_id) self.assertEqual(picking_delivery.partner_id, self.subcontractor_partner1) # Change the purchased quantity to 2 po.order_line.write({'product_qty': 2}) # Check that two deliveries with 1 component for the subcontractor have been created picking_deliveries = self.env['stock.picking'].search([('origin', '=', origin)]) self.assertEqual(len(picking_deliveries), 2) self.assertEqual(picking_deliveries[0].picking_type_id, wh.out_type_id) self.assertEqual(picking_deliveries[0].partner_id, self.subcontractor_partner1) self.assertTrue(picking_deliveries[0].state != 'cancel') move1 = picking_deliveries[0].move_ids_without_package self.assertEqual(picking_deliveries[1].picking_type_id, wh.out_type_id) self.assertEqual(picking_deliveries[1].partner_id, self.subcontractor_partner1) self.assertTrue(picking_deliveries[1].state != 'cancel') move2 = picking_deliveries[1].move_ids_without_package self.assertEqual(move1.product_id, self.comp1) self.assertEqual(move1.product_uom_qty, 1) self.assertEqual(move2.product_id, self.comp1) self.assertEqual(move2.product_uom_qty, 1)
def test_toggle_active_warehouse_2(self): wh = Form(self.env['stock.warehouse']) wh.name = "The attic of Willy" wh.code = "WIL" wh.reception_steps = "two_steps" wh.delivery_steps = "pick_pack_ship" warehouse = wh.save() warehouse.resupply_wh_ids = [(6, 0, [self.warehouse_1.id])] custom_location = Form(self.env['stock.location']) custom_location.name = "A Trunk" custom_location.location_id = warehouse.lot_stock_id custom_location = custom_location.save() # Add a warehouse on the route. warehouse.reception_route_id.write( {'warehouse_ids': [(4, self.warehouse_1.id)]}) route = Form(self.env['stock.location.route']) route.name = "Stair" route = route.save() route.warehouse_ids = [(6, 0, [warehouse.id, self.warehouse_1.id])] # Pre archive a location and a route warehouse.delivery_route_id.toggle_active() warehouse.wh_pack_stock_loc_id.toggle_active() # Archive warehouse warehouse.toggle_active() # Global rule self.assertFalse(warehouse.mto_pull_id.active) # Route self.assertTrue(warehouse.reception_route_id.active) self.assertFalse(warehouse.delivery_route_id.active) self.assertTrue(route.active) # Location self.assertFalse(warehouse.lot_stock_id.active) self.assertFalse(warehouse.wh_input_stock_loc_id.active) self.assertFalse(warehouse.wh_qc_stock_loc_id.active) self.assertFalse(warehouse.wh_output_stock_loc_id.active) self.assertFalse(warehouse.wh_pack_stock_loc_id.active) self.assertFalse(custom_location.active) # Picking Type self.assertFalse(warehouse.in_type_id.active) self.assertFalse(warehouse.out_type_id.active) self.assertFalse(warehouse.int_type_id.active) self.assertFalse(warehouse.pick_type_id.active) self.assertFalse(warehouse.pack_type_id.active) # Active warehouse warehouse.toggle_active() # Global rule self.assertTrue(warehouse.mto_pull_id.active) # Route self.assertTrue(warehouse.reception_route_id.active) self.assertTrue(warehouse.delivery_route_id.active) # Location self.assertTrue(warehouse.lot_stock_id.active) self.assertTrue(warehouse.wh_input_stock_loc_id.active) self.assertFalse(warehouse.wh_qc_stock_loc_id.active) self.assertTrue(warehouse.wh_output_stock_loc_id.active) self.assertTrue(warehouse.wh_pack_stock_loc_id.active) self.assertTrue(custom_location.active) # Picking Type self.assertTrue(warehouse.in_type_id.active) self.assertTrue(warehouse.out_type_id.active) self.assertTrue(warehouse.int_type_id.active) self.assertTrue(warehouse.pick_type_id.active) self.assertTrue(warehouse.pack_type_id.active)
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() produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.qty_producing = 5.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(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.product_uom_id = self.uom_unit 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.product_uom_id = self.uom_unit 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.product_uom_id = self.uom_unit 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_invoice(self): """ Test create and invoice from the SO, and check qty invoice/to invoice, and the related amounts """ # lines are in draft for line in self.sale_order.order_line: self.assertTrue( float_is_zero(line.untaxed_amount_to_invoice, precision_digits=2), "The amount to invoice should be zero, as the line is in draf state" ) self.assertTrue( float_is_zero(line.untaxed_amount_invoiced, precision_digits=2), "The invoiced amount should be zero, as the line is in draft state" ) # Confirm the SO self.sale_order.action_confirm() # Check ordered quantity, quantity to invoice and invoiced quantity of SO lines for line in self.sale_order.order_line: if line.product_id.invoice_policy == 'delivery': self.assertEqual( line.qty_to_invoice, 0.0, 'Quantity to invoice should be same as ordered quantity') self.assertEqual( line.qty_invoiced, 0.0, 'Invoiced quantity should be zero as no any invoice created for SO' ) self.assertEqual( line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity" ) self.assertEqual( line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity" ) else: self.assertEqual( line.qty_to_invoice, line.product_uom_qty, 'Quantity to invoice should be same as ordered quantity') self.assertEqual( line.qty_invoiced, 0.0, 'Invoiced quantity should be zero as no any invoice created for SO' ) self.assertEqual( line.untaxed_amount_to_invoice, line.product_uom_qty * line.price_unit, "The amount to invoice should the total of the line, as the line is confirmed" ) self.assertEqual( line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line is confirmed" ) # Let's do an invoice with invoiceable lines payment = self.env['sale.advance.payment.inv'].with_context( self.context).create({'advance_payment_method': 'delivered'}) payment.create_invoices() invoice = self.sale_order.invoice_ids[0] # Update quantity of an invoice lines move_form = Form(invoice) with move_form.invoice_line_ids.edit(0) as line_form: line_form.quantity = 3.0 with move_form.invoice_line_ids.edit(1) as line_form: line_form.quantity = 2.0 invoice = move_form.save() # amount to invoice / invoiced should not have changed (amounts take only confirmed invoice into account) for line in self.sale_order.order_line: if line.product_id.invoice_policy == 'delivery': self.assertEqual(line.qty_to_invoice, 0.0, "Quantity to invoice should be zero") self.assertEqual( line.qty_invoiced, 0.0, "Invoiced quantity should be zero as delivered lines are not delivered yet" ) self.assertEqual( line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity (no confirmed invoice)" ) self.assertEqual( line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as no invoice are validated for now" ) else: if line == self.sol_prod_order: self.assertEqual( self.sol_prod_order.qty_to_invoice, 2.0, "Changing the quantity on draft invoice update the qty to invoice on SO lines" ) self.assertEqual( self.sol_prod_order.qty_invoiced, 3.0, "Changing the quantity on draft invoice update the invoiced qty on SO lines" ) else: self.assertEqual( self.sol_serv_order.qty_to_invoice, 1.0, "Changing the quantity on draft invoice update the qty to invoice on SO lines" ) self.assertEqual( self.sol_serv_order.qty_invoiced, 2.0, "Changing the quantity on draft invoice update the invoiced qty on SO lines" ) self.assertEqual( line.untaxed_amount_to_invoice, line.product_uom_qty * line.price_unit, "The amount to invoice should the total of the line, as the line is confirmed (no confirmed invoice)" ) self.assertEqual( line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as no invoice are validated for now" ) invoice.action_post() # Check quantity to invoice on SO lines for line in self.sale_order.order_line: if line.product_id.invoice_policy == 'delivery': self.assertEqual( line.qty_to_invoice, 0.0, "Quantity to invoice should be same as ordered quantity") self.assertEqual( line.qty_invoiced, 0.0, "Invoiced quantity should be zero as no any invoice created for SO" ) self.assertEqual( line.untaxed_amount_to_invoice, 0.0, "The amount to invoice should be zero, as the line based on delivered quantity" ) self.assertEqual( line.untaxed_amount_invoiced, 0.0, "The invoiced amount should be zero, as the line based on delivered quantity" ) else: if line == self.sol_prod_order: self.assertEqual( line.qty_to_invoice, 2.0, "The ordered sale line are totally invoiced (qty to invoice is zero)" ) self.assertEqual( line.qty_invoiced, 3.0, "The ordered (prod) sale line are totally invoiced (qty invoiced come from the invoice lines)" ) else: self.assertEqual( line.qty_to_invoice, 1.0, "The ordered sale line are totally invoiced (qty to invoice is zero)" ) self.assertEqual( line.qty_invoiced, 2.0, "The ordered (serv) sale line are totally invoiced (qty invoiced = the invoice lines)" ) self.assertEqual( line.untaxed_amount_to_invoice, line.price_unit * line.qty_to_invoice, "Amount to invoice is now set as qty to invoice * unit price since no price change on invoice, for ordered products" ) self.assertEqual( line.untaxed_amount_invoiced, line.price_unit * line.qty_invoiced, "Amount invoiced is now set as qty invoiced * unit price since no price change on invoice, for ordered products" )
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.') produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.qty_producing = 5.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(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 x.product_uom_id = self.uom_unit 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.product_uom_id = self.uom_unit 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.product_uom_id = self.uom_unit 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_12_multi_level_variants2(self): """Test skip bom line with same attribute values in bom lines.""" Product = self.env['product.product'] ProductAttribute = self.env['product.attribute'] ProductAttributeValue = self.env['product.attribute.value'] # Product Attribute att_color = ProductAttribute.create({'name': 'Color', 'sequence': 1}) att_size = ProductAttribute.create({'name': 'size', 'sequence': 2}) # Product Attribute color Value att_color_red = ProductAttributeValue.create({'name': 'red', 'attribute_id': att_color.id, 'sequence': 1}) att_color_blue = ProductAttributeValue.create({'name': 'blue', 'attribute_id': att_color.id, 'sequence': 2}) # Product Attribute size Value att_size_big = ProductAttributeValue.create({'name': 'big', 'attribute_id': att_size.id, 'sequence': 1}) att_size_medium = ProductAttributeValue.create({'name': 'medium', 'attribute_id': att_size.id, 'sequence': 2}) # Create Template Product product_template = self.env['product.template'].create({ 'name': 'Sofa', 'attribute_line_ids': [ (0, 0, { 'attribute_id': att_color.id, 'value_ids': [(6, 0, [att_color_red.id, att_color_blue.id])] }), (0, 0, { 'attribute_id': att_size.id, 'value_ids': [(6, 0, [att_size_big.id, att_size_medium.id])] }) ] }) sofa_red = product_template.attribute_line_ids[0].product_template_value_ids[0] sofa_blue = product_template.attribute_line_ids[0].product_template_value_ids[1] sofa_big = product_template.attribute_line_ids[1].product_template_value_ids[0] sofa_medium = product_template.attribute_line_ids[1].product_template_value_ids[1] # Create components Of BOM product_A = Product.create({ 'name': 'Wood'}) product_B = Product.create({ 'name': 'Clothes'}) # Create BOM self.env['mrp.bom'].create({ 'product_tmpl_id': product_template.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [ (0, 0, { 'product_id': product_A.id, 'product_qty': 1, 'bom_product_template_attribute_value_ids': [(4, sofa_red.id), (4, sofa_blue.id), (4, sofa_big.id)], }), (0, 0, { 'product_id': product_B.id, 'product_qty': 1, 'bom_product_template_attribute_value_ids': [(4, sofa_red.id), (4, sofa_blue.id)] }) ] }) dict_consumed_products = { sofa_red + sofa_big: product_A + product_B, sofa_red + sofa_medium: product_B, sofa_blue + sofa_big: product_A + product_B, sofa_blue + sofa_medium: product_B, } # Create production order for all variants. for combination, consumed_products in dict_consumed_products.items(): product = product_template.product_variant_ids.filtered(lambda p: p.product_template_attribute_value_ids == combination) mrp_order_form = Form(self.env['mrp.production']) mrp_order_form.product_id = product mrp_order = mrp_order_form.save() # Check consumed materials in production order. self.assertEqual(mrp_order.move_raw_ids.product_id, consumed_products)
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 x = Form(self.env['mrp.unbuild']) x.product_id = finshed_product x.bom_id = bom x.product_uom_id = self.uom_unit x.mo_id = mo x.product_qty = 1 x.location_id = self.stock_location x.location_dest_id = unbuild_location x.save().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_21_bom_report_variant(self): """ Test a sub BoM process with multiple variants. BOM 1: product template = car quantity = 5 units - red paint 50l -> red car (product.product) - blue paint 50l -> blue car - red dashboard with gps -> red car with GPS - red dashboard w/h gps -> red w/h GPS - blue dashboard with gps -> blue car with GPS - blue dashboard w/h gps -> blue w/h GPS BOM 2: product_tmpl = dashboard quantity = 2 - red paint 1l -> red dashboard (product.product) - blue paint 1l -> blue dashboard - gps -> dashboard with gps Check the Price for a Blue Car with GPS -> 910$: 10l of blue paint -> 200$ 1 blue dashboard GPS -> 710$: - 0.5l of blue paint -> 10$ - GPS -> 700$ Check the price for a red car -> 10.5l of red paint -> 210$ """ # Create a product template car with attributes gps(yes, no), color(red, blue) self.car = self.env['product.template'].create({ 'name': 'Car', }) self.gps_attribute = self.env['product.attribute'].create({'name': 'GPS', 'sequence': 1}) self.gps_yes = self.env['product.attribute.value'].create({ 'name': 'Yes', 'attribute_id': self.gps_attribute.id, 'sequence': 1, }) self.gps_no = self.env['product.attribute.value'].create({ 'name': 'No', 'attribute_id': self.gps_attribute.id, 'sequence': 2, }) self.car_gps_attribute_line = self.env['product.template.attribute.line'].create({ 'product_tmpl_id': self.car.id, 'attribute_id': self.gps_attribute.id, 'value_ids': [(6, 0, [self.gps_yes.id, self.gps_no.id])], }) self.car_gps_yes = self.car_gps_attribute_line.product_template_value_ids[0] self.car_gps_no = self.car_gps_attribute_line.product_template_value_ids[1] self.color_attribute = self.env['product.attribute'].create({'name': 'Color', 'sequence': 1}) self.color_red = self.env['product.attribute.value'].create({ 'name': 'Red', 'attribute_id': self.color_attribute.id, 'sequence': 1, }) self.color_blue = self.env['product.attribute.value'].create({ 'name': 'Blue', 'attribute_id': self.color_attribute.id, 'sequence': 2, }) self.car_color_attribute_line = self.env['product.template.attribute.line'].create({ 'product_tmpl_id': self.car.id, 'attribute_id': self.color_attribute.id, 'value_ids': [(6, 0, [self.color_red.id, self.color_blue.id])], }) self.car_color_red = self.car_color_attribute_line.product_template_value_ids[0] self.car_color_blue = self.car_color_attribute_line.product_template_value_ids[1] # Blue and red paint uom_litre = self.env.ref('uom.product_uom_litre') self.paint = self.env['product.template'].create({ 'name': 'Paint', 'uom_id': uom_litre.id, 'uom_po_id': uom_litre.id }) self.paint_color_attribute_line = self.env['product.template.attribute.line'].create({ 'product_tmpl_id': self.paint.id, 'attribute_id': self.color_attribute.id, 'value_ids': [(6, 0, [self.color_red.id, self.color_blue.id])], }) self.paint_color_red = self.paint_color_attribute_line.product_template_value_ids[0] self.paint_color_blue = self.paint_color_attribute_line.product_template_value_ids[1] self.paint.product_variant_ids.write({'standard_price': 20}) self.dashboard = self.env['product.template'].create({ 'name': 'Dashboard', 'standard_price': 1000, }) self.dashboard_gps_attribute_line = self.env['product.template.attribute.line'].create({ 'product_tmpl_id': self.dashboard.id, 'attribute_id': self.gps_attribute.id, 'value_ids': [(6, 0, [self.gps_yes.id, self.gps_no.id])], }) self.dashboard_gps_yes = self.dashboard_gps_attribute_line.product_template_value_ids[0] self.dashboard_gps_no = self.dashboard_gps_attribute_line.product_template_value_ids[1] self.dashboard_color_attribute_line = self.env['product.template.attribute.line'].create({ 'product_tmpl_id': self.dashboard.id, 'attribute_id': self.color_attribute.id, 'value_ids': [(6, 0, [self.color_red.id, self.color_blue.id])], }) self.dashboard_color_red = self.dashboard_color_attribute_line.product_template_value_ids[0] self.dashboard_color_blue = self.dashboard_color_attribute_line.product_template_value_ids[1] self.gps = self.env['product.product'].create({ 'name': 'GPS', 'standard_price': 700, }) bom_form_car = Form(self.env['mrp.bom']) bom_form_car.product_tmpl_id = self.car bom_form_car.product_qty = 5 with bom_form_car.bom_line_ids.new() as line: line.product_id = self.paint._get_variant_for_combination(self.paint_color_red) line.product_uom_id = uom_litre line.product_qty = 50 line.bom_product_template_attribute_value_ids.add(self.car_color_red) with bom_form_car.bom_line_ids.new() as line: line.product_id = self.paint._get_variant_for_combination(self.paint_color_blue) line.product_uom_id = uom_litre line.product_qty = 50 line.bom_product_template_attribute_value_ids.add(self.car_color_blue) with bom_form_car.bom_line_ids.new() as line: line.product_id = self.dashboard._get_variant_for_combination(self.dashboard_gps_yes + self.dashboard_color_red) line.product_qty = 5 line.bom_product_template_attribute_value_ids.add(self.car_gps_yes) line.bom_product_template_attribute_value_ids.add(self.car_color_red) with bom_form_car.bom_line_ids.new() as line: line.product_id = self.dashboard._get_variant_for_combination(self.dashboard_gps_yes + self.dashboard_color_blue) line.product_qty = 5 line.bom_product_template_attribute_value_ids.add(self.car_gps_yes) line.bom_product_template_attribute_value_ids.add(self.car_color_blue) with bom_form_car.bom_line_ids.new() as line: line.product_id = self.dashboard._get_variant_for_combination(self.dashboard_gps_no + self.dashboard_color_red) line.product_qty = 5 line.bom_product_template_attribute_value_ids.add(self.car_gps_no) line.bom_product_template_attribute_value_ids.add(self.car_color_red) with bom_form_car.bom_line_ids.new() as line: line.product_id = self.dashboard._get_variant_for_combination(self.dashboard_gps_no + self.dashboard_color_blue) line.product_qty = 5 line.bom_product_template_attribute_value_ids.add(self.car_gps_no) line.bom_product_template_attribute_value_ids.add(self.car_color_blue) bom_car = bom_form_car.save() bom_dashboard = Form(self.env['mrp.bom']) bom_dashboard.product_tmpl_id = self.dashboard bom_dashboard.product_qty = 2 with bom_dashboard.bom_line_ids.new() as line: line.product_id = self.paint._get_variant_for_combination(self.paint_color_red) line.product_uom_id = uom_litre line.product_qty = 1 line.bom_product_template_attribute_value_ids.add(self.dashboard_color_red) with bom_dashboard.bom_line_ids.new() as line: line.product_id = self.paint._get_variant_for_combination(self.paint_color_blue) line.product_uom_id = uom_litre line.product_qty = 1 line.bom_product_template_attribute_value_ids.add(self.dashboard_color_blue) with bom_dashboard.bom_line_ids.new() as line: line.product_id = self.gps line.product_qty = 2 line.bom_product_template_attribute_value_ids.add(self.dashboard_gps_yes) bom_dashboard = bom_dashboard.save() blue_car_with_gps = self.car._get_variant_for_combination(self.car_color_blue + self.car_gps_yes) report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_car.id, searchQty=1, searchVariant=blue_car_with_gps.id) # Two lines. blue dashboard with gps and blue paint. self.assertEqual(len(report_values['lines']['components']), 2) # 10l of blue paint blue_paint = self.paint._get_variant_for_combination(self.paint_color_blue) self.assertEqual(blue_paint.id, report_values['lines']['components'][0]['prod_id']) self.assertEqual(report_values['lines']['components'][0]['prod_qty'], 10) # 1 blue dashboard with GPS blue_dashboard_gps = self.dashboard._get_variant_for_combination(self.dashboard_color_blue + self.dashboard_gps_yes) self.assertEqual(blue_dashboard_gps.id, report_values['lines']['components'][1]['prod_id']) self.assertEqual(report_values['lines']['components'][1]['prod_qty'], 1) component = report_values['lines']['components'][1] report_values_dashboad = self.env['report.mrp.report_bom_structure']._get_bom( component['child_bom'], component['prod_id'], component['prod_qty'], component['line_id'], component['level'] + 1) self.assertEqual(len(report_values_dashboad['components']), 2) self.assertEqual(blue_paint.id, report_values_dashboad['components'][0]['prod_id']) self.assertEqual(self.gps.id, report_values_dashboad['components'][1]['prod_id']) # 0.5l of paint at price of 20$/litre -> 10$ self.assertEqual(report_values_dashboad['components'][0]['total'], 10) # GPS 700$ self.assertEqual(report_values_dashboad['components'][1]['total'], 700) # Dashboard blue with GPS should have a BoM cost of 710$ self.assertEqual(report_values['lines']['components'][1]['total'], 710) # 10l of paint at price of 20$/litre -> 200$ self.assertEqual(report_values['lines']['components'][0]['total'], 200) # Total cost of blue car with GPS: 10 + 700 + 200 = 910 self.assertEqual(report_values['lines']['total'], 910) red_car_without_gps = self.car._get_variant_for_combination(self.car_color_red + self.car_gps_no) report_values = self.env['report.mrp.report_bom_structure']._get_report_data(bom_id=bom_car.id, searchQty=1, searchVariant=red_car_without_gps.id) # Same math than before but without GPS self.assertEqual(report_values['lines']['total'], 210)
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() produce_form = Form(self.env['mrp.product.produce'].with_context({ 'active_id': mo.id, 'active_ids': [mo.id], })) produce_form.qty_producing = 5.0 produce_form.finished_lot_id = lot 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(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 x.product_uom_id = self.uom_unit unbuild_order = x.save() x = Form(self.env['mrp.unbuild']) x.product_id = p_final x.bom_id = bom x.product_qty = 3 x.product_uom_id = self.uom_unit 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.product_uom_id = self.uom_unit 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.product_uom_id = self.uom_unit 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_03_invoiced_status(self): super_product = self.env['product.product'].create({ 'name': 'Super Product', 'invoice_policy': 'delivery', }) great_product = self.env['product.product'].create({ 'name': 'Great Product', 'invoice_policy': 'delivery', }) so = self.env['sale.order'].create({ 'name': 'Sale order', 'partner_id': self.partner_a.id, 'partner_invoice_id': self.partner_a.id, 'order_line': [ (0, 0, { 'name': super_product.name, 'product_id': super_product.id, 'product_uom_qty': 1, 'price_unit': 1, }), (0, 0, { 'name': great_product.name, 'product_id': great_product.id, 'product_uom_qty': 1, 'price_unit': 1, }), ] }) # Confirm the SO so.action_confirm() # Deliver one product and create a backorder self.assertEqual( sum([line.quantity_done for line in so.picking_ids.move_ids]), 0) so.picking_ids.move_ids[0].quantity_done = 1 backorder_wizard_dict = so.picking_ids.button_validate() backorder_wizard = Form( self.env[backorder_wizard_dict['res_model']].with_context( backorder_wizard_dict['context'])).save() backorder_wizard.process() self.assertEqual( sum([line.quantity_done for line in so.picking_ids.move_ids]), 1) # Invoice the delivered product invoice = so._create_invoices() invoice.action_post() self.assertEqual(so.invoice_status, 'no') # Add delivery fee delivery_wizard = Form( self.env['choose.delivery.carrier'].with_context({ 'default_order_id': so.id, 'default_carrier_id': self.normal_delivery.id })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() self.assertEqual(so.invoice_status, 'no', 'The status should still be "Nothing To Invoice"')
def test_multi_level_variants2(self): """Test skip bom line with same attribute values in bom lines.""" Product = self.env['product.product'] ProductAttribute = self.env['product.attribute'] ProductAttributeValue = self.env['product.attribute.value'] MrpProduction = self.env['mrp.production'] # Product Attribute att_color = ProductAttribute.create({'name': 'Color'}) att_size = ProductAttribute.create({'name': 'size'}) # Product Attribute color Value att_color_red = ProductAttributeValue.create({'name': 'red', 'attribute_id': att_color.id}) att_color_blue = ProductAttributeValue.create({'name': 'blue', 'attribute_id': att_color.id}) # Product Attribute size Value att_size_big = ProductAttributeValue.create({'name': 'big', 'attribute_id': att_size.id}) att_size_medium = ProductAttributeValue.create({'name': 'medium', 'attribute_id': att_size.id}) # Create Template Product product_template = self.env['product.template'].create({ 'name': 'Sofa', 'attribute_line_ids': [(0, 0, { 'attribute_id': att_color.id, 'value_ids': [(6, 0, [att_color_red.id, att_color_blue.id])] }), (0, 0, { 'attribute_id': att_size.id, 'value_ids': [(6, 0, [att_size_big.id, att_size_medium.id])] })] }) # Create components Of BOM product_A = Product.create({ 'name': 'Wood'}) product_B = Product.create({ 'name': 'Clothes'}) # Create BOM self.env['mrp.bom'].create({ 'product_tmpl_id': product_template.id, 'product_qty': 1.0, 'type': 'normal', 'bom_line_ids': [(0, 0, { 'product_id': product_A.id, 'product_qty': 1, 'attribute_value_ids': [(4, att_color_red.id), (4, att_color_blue.id), (4, att_size_big.id)], }), (0, 0, { 'product_id': product_B.id, 'product_qty': 1, 'attribute_value_ids': [(4, att_color_red.id), (4, att_color_blue.id)] })] }) combination = {(att_color_red, att_size_big): [product_A.id, product_B.id] , (att_color_red, att_size_medium): [product_B.id] , (att_color_blue, att_size_big):[product_A.id, product_B.id], (att_color_blue, att_size_medium): [product_B.id]} # Create production order for all variants. for comb in combination.keys(): consu_product_ids = combination[comb] product = product_template.product_variant_ids.filtered(lambda x: all(value in comb for value in x.attribute_value_ids)) mrp_order_form = Form(self.env['mrp.production']) mrp_order_form.product_id = product mrp_order = mrp_order_form.save() # Check consumed materials in production order. self.assertEqual(mrp_order.move_raw_ids.mapped('product_id').ids, consu_product_ids)
def test_at_cost(self): """ Test vendor bill at cost for product based on ordered and delivered quantities. """ # create SO line and confirm SO (with only one line) sale_order_line1 = self.env['sale.order.line'].create({ 'name': self.company_data['product_order_cost'].name, 'product_id': self.company_data['product_order_cost'].id, 'product_uom_qty': 2, 'qty_delivered': 1, 'product_uom': self.company_data['product_order_cost'].uom_id.id, 'price_unit': self.company_data['product_order_cost'].list_price, 'order_id': self.sale_order.id, }) sale_order_line1.product_id_change() sale_order_line2 = self.env['sale.order.line'].create({ 'name': self.company_data['product_delivery_cost'].name, 'product_id': self.company_data['product_delivery_cost'].id, 'product_uom_qty': 4, 'qty_delivered': 1, 'product_uom': self.company_data['product_delivery_cost'].uom_id.id, 'price_unit': self.company_data['product_delivery_cost'].list_price, 'order_id': self.sale_order.id, }) sale_order_line2.product_id_change() self.sale_order.onchange_partner_id() self.sale_order._compute_tax_id() self.sale_order.action_confirm() # create invoice lines and validate it move_form = Form(self.AccountMove) move_form.partner_id = self.partner_a with move_form.line_ids.new() as line_form: line_form.product_id = self.company_data['product_order_cost'] line_form.quantity = 3.0 line_form.analytic_account_id = self.analytic_account with move_form.line_ids.new() as line_form: line_form.product_id = self.company_data['product_delivery_cost'] line_form.quantity = 3.0 line_form.analytic_account_id = self.analytic_account invoice_a = move_form.save() invoice_a.action_post() sale_order_line3 = self.sale_order.order_line.filtered( lambda sol: sol != sale_order_line1 and sol.product_id == self. company_data['product_order_cost']) sale_order_line4 = self.sale_order.order_line.filtered( lambda sol: sol != sale_order_line2 and sol.product_id == self. company_data['product_delivery_cost']) self.assertTrue( sale_order_line3, "A new sale line should have been created with ordered product") self.assertTrue( sale_order_line4, "A new sale line should have been created with delivered product") self.assertEqual( len(self.sale_order.order_line), 4, "There should be 4 lines on the SO (2 vendor bill lines created)") self.assertEqual( len(self.sale_order.order_line.filtered( lambda sol: sol.is_expense)), 2, "There should be 4 lines on the SO (2 vendor bill lines created)") self.assertEqual( (sale_order_line3.price_unit, sale_order_line3.qty_delivered, sale_order_line3.product_uom_qty, sale_order_line3.qty_invoiced), (self.company_data['product_order_cost'].standard_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice') self.assertEqual( (sale_order_line4.price_unit, sale_order_line4.qty_delivered, sale_order_line4.product_uom_qty, sale_order_line4.qty_invoiced), (self.company_data['product_delivery_cost'].standard_price, 3, 0, 0), 'Sale line is wrong after confirming vendor invoice') self.assertEqual( sale_order_line3.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount" ) self.assertEqual( sale_order_line4.qty_delivered_method, 'analytic', "Delivered quantity of 'expense' SO line should be computed by analytic amount" ) # create second invoice lines and validate it move_form = Form(self.AccountMove) move_form.partner_id = self.partner_a with move_form.line_ids.new() as line_form: line_form.product_id = self.company_data['product_order_cost'] line_form.quantity = 2.0 line_form.analytic_account_id = self.analytic_account with move_form.line_ids.new() as line_form: line_form.product_id = self.company_data['product_delivery_cost'] line_form.quantity = 2.0 line_form.analytic_account_id = self.analytic_account invoice_b = move_form.save() invoice_b.action_post() sale_order_line5 = self.sale_order.order_line.filtered( lambda sol: sol != sale_order_line1 and sol != sale_order_line3 and sol.product_id == self.company_data['product_order_cost']) sale_order_line6 = self.sale_order.order_line.filtered( lambda sol: sol != sale_order_line2 and sol != sale_order_line4 and sol.product_id == self.company_data['product_delivery_cost']) self.assertTrue( sale_order_line5, "A new sale line should have been created with ordered product") self.assertTrue( sale_order_line6, "A new sale line should have been created with delivered product") self.assertEqual( len(self.sale_order.order_line), 6, "There should be still 4 lines on the SO, no new created") self.assertEqual( len(self.sale_order.order_line.filtered( lambda sol: sol.is_expense)), 4, "There should be still 2 expenses lines on the SO") self.assertEqual( (sale_order_line5.price_unit, sale_order_line5.qty_delivered, sale_order_line5.product_uom_qty, sale_order_line5.qty_invoiced), (self.company_data['product_order_cost'].standard_price, 2, 0, 0), 'Sale line 5 is wrong after confirming 2e vendor invoice') self.assertEqual( (sale_order_line6.price_unit, sale_order_line6.qty_delivered, sale_order_line6.product_uom_qty, sale_order_line6.qty_invoiced), (self.company_data['product_delivery_cost'].standard_price, 2, 0, 0), 'Sale line 6 is wrong after confirming 2e vendor invoice')
def test_shipping_cost(self): # Free delivery should not be taken into account when checking for minimum required threshold p_minimum_threshold_free_delivery = self.env[ 'sale.coupon.program'].create({ 'name': 'free shipping if > 872 tax exl', 'promo_code_usage': 'no_code_needed', 'reward_type': 'free_shipping', 'program_type': 'promotion_program', 'rule_minimum_amount': 872, }) p_minimum_threshold_discount = self.env['sale.coupon.program'].create({ 'name': '10% reduction if > 872 tax exl', 'promo_code_usage': 'no_code_needed', 'reward_type': 'discount', 'program_type': 'promotion_program', 'discount_type': 'percentage', 'discount_percentage': 10.0, 'rule_minimum_amount': 872, }) order = self.empty_order self.iPadMini.taxes_id = self.tax_10pc_incl sol1 = self.env['sale.order.line'].create({ 'product_id': self.iPadMini.id, 'name': 'Large Cabinet', 'product_uom_qty': 3.0, 'order_id': order.id, }) order.recompute_coupon_lines() self.assertEqual( len(order.order_line.ids), 2, "We should get the 10% discount line since we bought 872.73$") order.carrier_id = self.env['delivery.carrier'].search([])[1] # I add delivery cost in Sales order delivery_wizard = Form( self.env['choose.delivery.carrier'].with_context({ 'default_order_id': order.id, 'default_carrier_id': self.env['delivery.carrier'].search([])[1] })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() order.recompute_coupon_lines() self.assertEqual( len(order.order_line.ids), 3, "We should get the delivery line but not the free delivery since we are below 872.73$ with the 10% discount" ) p_minimum_threshold_free_delivery.sequence = 10 (order.order_line - sol1).unlink() # I add delivery cost in Sales order delivery_wizard = Form( self.env['choose.delivery.carrier'].with_context({ 'default_order_id': order.id, 'default_carrier_id': self.env['delivery.carrier'].search([])[1] })) choose_delivery_carrier = delivery_wizard.save() choose_delivery_carrier.button_confirm() order.recompute_coupon_lines() self.assertEqual( len(order.order_line.ids), 4, "We should get both promotion line since the free delivery will be applied first and won't change the SO total" )