391 lines
15 KiB
Python
391 lines
15 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import time
|
|
|
|
from odoo.tests import Form, TransactionCase
|
|
from odoo.tools import mute_logger
|
|
from odoo import Command
|
|
|
|
|
|
class TestSaleMrpProcurement(TransactionCase):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.env.ref('base.group_user').write({'implied_ids': [(4, cls.env.ref('product.group_product_variant').id)]})
|
|
|
|
def test_sale_mrp(self):
|
|
# Required for `uom_id` to be visible in the view
|
|
self.env.user.groups_id += self.env.ref('uom.group_uom')
|
|
self.env.ref('stock.route_warehouse0_mto').active = True
|
|
warehouse0 = self.env.ref('stock.warehouse0')
|
|
# In order to test the sale_mrp module in OpenERP, I start by creating a new product 'Slider Mobile'
|
|
# I define product category Mobile Products Sellable.
|
|
|
|
with mute_logger('odoo.tests.common.onchange'):
|
|
# Suppress warning on "Changing your cost method" when creating a
|
|
# product category
|
|
pc = Form(self.env['product.category'])
|
|
pc.name = 'Mobile Products Sellable'
|
|
product_category_allproductssellable0 = pc.save()
|
|
|
|
uom_unit = self.env.ref('uom.product_uom_unit')
|
|
|
|
self.assertIn("seller_ids", self.env['product.template'].fields_get())
|
|
|
|
# I define product for Slider Mobile.
|
|
product = Form(self.env['product.template'])
|
|
|
|
product.categ_id = product_category_allproductssellable0
|
|
product.list_price = 200.0
|
|
product.name = 'Slider Mobile'
|
|
product.is_storable = True
|
|
product.uom_id = uom_unit
|
|
product.uom_po_id = uom_unit
|
|
product.route_ids.clear()
|
|
product.route_ids.add(warehouse0.manufacture_pull_id.route_id)
|
|
product.route_ids.add(warehouse0.mto_pull_id.route_id)
|
|
product_template_slidermobile0 = product.save()
|
|
|
|
product_template_slidermobile0.standard_price = 189
|
|
|
|
product_component = Form(self.env['product.product'])
|
|
product_component.name = 'Battery'
|
|
product_product_bettery = product_component.save()
|
|
|
|
with Form(self.env['mrp.bom']) as bom:
|
|
bom.product_tmpl_id = product_template_slidermobile0
|
|
with bom.bom_line_ids.new() as line:
|
|
line.product_id = product_product_bettery
|
|
line.product_qty = 4
|
|
|
|
# I create a sale order for product Slider mobile
|
|
so_form = Form(self.env['sale.order'])
|
|
so_form.partner_id = self.env['res.partner'].create({'name': 'Another Test Partner'})
|
|
with so_form.order_line.new() as line:
|
|
line.product_id = product_template_slidermobile0.product_variant_ids
|
|
line.price_unit = 200
|
|
line.product_uom_qty = 500.0
|
|
line.customer_lead = 7.0
|
|
sale_order_so0 = so_form.save()
|
|
|
|
# I confirm the sale order
|
|
sale_order_so0.action_confirm()
|
|
|
|
# I verify that a manufacturing order has been generated, and that its name and reference are correct
|
|
mo = self.env['mrp.production'].search([('origin', 'like', sale_order_so0.name)], limit=1)
|
|
self.assertTrue(mo, 'Manufacturing order has not been generated')
|
|
|
|
# Check the mo is displayed on the so
|
|
self.assertEqual(mo.id, sale_order_so0.action_view_mrp_production()['res_id'])
|
|
|
|
def test_sale_mrp_pickings(self):
|
|
""" Test sale of multiple mrp products in MTO
|
|
to avoid generating multiple deliveries
|
|
to the customer location
|
|
"""
|
|
# Required for `uom_id` to be visible in the view
|
|
self.env.user.groups_id += self.env.ref('uom.group_uom')
|
|
# Required for `manufacture_step` to be visible in the view
|
|
self.env.user.groups_id += self.env.ref('stock.group_adv_location')
|
|
self.env.ref('stock.route_warehouse0_mto').active = True
|
|
# Create warehouse
|
|
self.customer_location = self.env['ir.model.data']._xmlid_to_res_id('stock.stock_location_customers')
|
|
self.warehouse = self.env['stock.warehouse'].create({
|
|
'name': 'Test Warehouse',
|
|
'code': 'TWH'
|
|
})
|
|
|
|
self.uom_unit = self.env.ref('uom.product_uom_unit')
|
|
|
|
# Create raw product for manufactured product
|
|
product_form = Form(self.env['product.product'])
|
|
product_form.name = 'Raw Stick'
|
|
product_form.is_storable = True
|
|
product_form.uom_id = self.uom_unit
|
|
product_form.uom_po_id = self.uom_unit
|
|
self.raw_product = product_form.save()
|
|
|
|
# Create manufactured product
|
|
product_form = Form(self.env['product.product'])
|
|
product_form.name = 'Stick'
|
|
product_form.uom_id = self.uom_unit
|
|
product_form.uom_po_id = self.uom_unit
|
|
product_form.is_storable = True
|
|
product_form.route_ids.clear()
|
|
product_form.route_ids.add(self.warehouse.manufacture_pull_id.route_id)
|
|
product_form.route_ids.add(self.warehouse.mto_pull_id.route_id)
|
|
self.finished_product = product_form.save()
|
|
|
|
# Create manifactured product which uses another manifactured
|
|
product_form = Form(self.env['product.product'])
|
|
product_form.name = 'Arrow'
|
|
product_form.is_storable = True
|
|
product_form.route_ids.clear()
|
|
product_form.route_ids.add(self.warehouse.manufacture_pull_id.route_id)
|
|
product_form.route_ids.add(self.warehouse.mto_pull_id.route_id)
|
|
self.complex_product = product_form.save()
|
|
|
|
## Create raw product for manufactured product
|
|
product_form = Form(self.env['product.product'])
|
|
product_form.name = 'Raw Iron'
|
|
product_form.is_storable = True
|
|
product_form.uom_id = self.uom_unit
|
|
product_form.uom_po_id = self.uom_unit
|
|
self.raw_product_2 = product_form.save()
|
|
|
|
# Create bom for manufactured product
|
|
bom_product_form = Form(self.env['mrp.bom'])
|
|
bom_product_form.product_id = self.finished_product
|
|
bom_product_form.product_tmpl_id = self.finished_product.product_tmpl_id
|
|
bom_product_form.product_qty = 1.0
|
|
bom_product_form.type = 'normal'
|
|
with bom_product_form.bom_line_ids.new() as bom_line:
|
|
bom_line.product_id = self.raw_product
|
|
bom_line.product_qty = 2.0
|
|
|
|
self.bom = bom_product_form.save()
|
|
|
|
## Create bom for manufactured product
|
|
bom_product_form = Form(self.env['mrp.bom'])
|
|
bom_product_form.product_id = self.complex_product
|
|
bom_product_form.product_tmpl_id = self.complex_product.product_tmpl_id
|
|
with bom_product_form.bom_line_ids.new() as line:
|
|
line.product_id = self.finished_product
|
|
line.product_qty = 1.0
|
|
with bom_product_form.bom_line_ids.new() as line:
|
|
line.product_id = self.raw_product_2
|
|
line.product_qty = 1.0
|
|
|
|
self.complex_bom = bom_product_form.save()
|
|
|
|
with Form(self.warehouse) as warehouse:
|
|
warehouse.manufacture_steps = 'pbm_sam'
|
|
|
|
so_form = Form(self.env['sale.order'])
|
|
so_form.partner_id = self.env['res.partner'].create({'name': 'Another Test Partner'})
|
|
with so_form.order_line.new() as line:
|
|
line.product_id = self.complex_product
|
|
line.price_unit = 1
|
|
line.product_uom_qty = 1
|
|
with so_form.order_line.new() as line:
|
|
line.product_id = self.finished_product
|
|
line.price_unit = 1
|
|
line.product_uom_qty = 1
|
|
sale_order_so0 = so_form.save()
|
|
|
|
sale_order_so0.action_confirm()
|
|
|
|
# Verify buttons are working as expected
|
|
self.assertEqual(sale_order_so0.mrp_production_count, 3, "2 Mos for the 2 sale order line + 1 child Mo for the complex product")
|
|
|
|
pickings = sale_order_so0.picking_ids
|
|
|
|
# One delivery...
|
|
self.assertEqual(len(pickings), 1)
|
|
|
|
# ...with two products
|
|
self.assertEqual(len(pickings[0].move_ids), 2)
|
|
|
|
def test_post_prod_location_child_of_stock_location(self):
|
|
"""
|
|
3-steps manufacturing, the post-prod location is a child of the stock
|
|
location. Have a manufactured product with the manufacture route and a
|
|
RR min=max=0. Confirm a SO with that product -> It should generate a MO
|
|
"""
|
|
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
|
|
manufacture_route = warehouse.manufacture_pull_id.route_id
|
|
|
|
warehouse.manufacture_steps = 'pbm_sam'
|
|
warehouse.sam_loc_id.location_id = warehouse.lot_stock_id
|
|
|
|
product, component = self.env['product.product'].create([{
|
|
'name': 'Finished',
|
|
'is_storable': True,
|
|
'route_ids': [(6, 0, manufacture_route.ids)],
|
|
}, {
|
|
'name': 'Component',
|
|
'type': 'consu',
|
|
}])
|
|
|
|
self.env['mrp.bom'].create({
|
|
'product_id': product.id,
|
|
'product_tmpl_id': product.product_tmpl_id.id,
|
|
'product_uom_id': product.uom_id.id,
|
|
'product_qty': 1.0,
|
|
'type': 'normal',
|
|
'bom_line_ids': [
|
|
(0, 0, {'product_id': component.id, 'product_qty': 1}),
|
|
],
|
|
})
|
|
|
|
self.env['stock.warehouse.orderpoint'].create({
|
|
'name': product.name,
|
|
'location_id': warehouse.lot_stock_id.id,
|
|
'product_id': product.id,
|
|
'product_min_qty': 0,
|
|
'product_max_qty': 0,
|
|
'trigger': 'auto',
|
|
})
|
|
|
|
so = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'Super Partner'}).id,
|
|
'order_line': [
|
|
(0, 0, {
|
|
'name': product.name,
|
|
'product_id': product.id,
|
|
'product_uom_qty': 1.0,
|
|
'product_uom': product.uom_id.id,
|
|
'price_unit': 1,
|
|
})],
|
|
})
|
|
so.action_confirm()
|
|
self.assertEqual(so.state, 'sale')
|
|
|
|
mo = self.env['mrp.production'].search([('product_id', '=', product.id)], order='id desc', limit=1)
|
|
self.assertIn(so.name, mo.origin)
|
|
|
|
def test_so_reordering_rule(self):
|
|
kit_1, component_1 = self.env['product.product'].create([{
|
|
'name': n,
|
|
'is_storable': True,
|
|
} for n in ['Kit 1', 'Compo 1']])
|
|
|
|
self.env['mrp.bom'].create([{
|
|
'product_tmpl_id': kit_1.product_tmpl_id.id,
|
|
'product_qty': 1,
|
|
'type': 'phantom',
|
|
'bom_line_ids': [
|
|
(0, 0, {'product_id': component_1.id, 'product_qty': 1}),
|
|
],
|
|
}])
|
|
customer = self.env['res.partner'].create({
|
|
'name': 'customer',
|
|
})
|
|
so = self.env['sale.order'].create({
|
|
'partner_id': customer.id,
|
|
'order_line': [
|
|
(0, 0, {
|
|
'product_id': kit_1.id,
|
|
'product_uom_qty': 1.0,
|
|
})],
|
|
})
|
|
so.action_confirm()
|
|
|
|
self.env['stock.warehouse.orderpoint']._get_orderpoint_action()
|
|
orderpoint_product = self.env['stock.warehouse.orderpoint'].search(
|
|
[('product_id', '=', kit_1.id)])
|
|
self.assertFalse(orderpoint_product)
|
|
|
|
def test_so_reordering_rule_02(self):
|
|
"""
|
|
Have a manufactured product in kg unit of measure with the manufacturing route
|
|
and a reordering rule (RR) set to min=max=0, and a BoM in grams.
|
|
Confirm a SO with that product in 510 grams -> It should generate a MO with 510g.
|
|
Create a second SO with 510g -> It should update the MO to 1020g.
|
|
"""
|
|
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
|
|
manufacture_route = warehouse.manufacture_pull_id.route_id
|
|
|
|
uom_kg = self.env.ref('uom.product_uom_kgm')
|
|
uom_gram = self.env.ref('uom.product_uom_gram')
|
|
|
|
product, component = self.env['product.product'].create([{
|
|
'name': 'Finished',
|
|
'is_storable': True,
|
|
'uom_id': uom_kg.id,
|
|
'uom_po_id': uom_kg.id,
|
|
'route_ids': [(6, 0, manufacture_route.ids)],
|
|
}, {
|
|
'name': 'Component',
|
|
'type': 'consu',
|
|
}])
|
|
|
|
self.env['mrp.bom'].create({
|
|
'product_id': product.id,
|
|
'product_tmpl_id': product.product_tmpl_id.id,
|
|
'product_uom_id': uom_gram.id,
|
|
'product_qty': 1.0,
|
|
'type': 'normal',
|
|
'bom_line_ids': [
|
|
(0, 0, {'product_id': component.id, 'product_qty': 1}),
|
|
],
|
|
})
|
|
|
|
self.env['stock.warehouse.orderpoint'].create({
|
|
'name': product.name,
|
|
'location_id': warehouse.lot_stock_id.id,
|
|
'product_id': product.id,
|
|
'product_min_qty': 0,
|
|
'product_max_qty': 0,
|
|
'trigger': 'auto',
|
|
'qty_multiple': 0.01,
|
|
})
|
|
|
|
so = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'Super Partner'}).id,
|
|
'order_line': [
|
|
(0, 0, {
|
|
'name': product.name,
|
|
'product_id': product.id,
|
|
'product_uom_qty': 510,
|
|
'product_uom': uom_gram.id,
|
|
'price_unit': 1,
|
|
})],
|
|
})
|
|
so.action_confirm()
|
|
self.assertEqual(so.state, 'sale')
|
|
|
|
mo = self.env['mrp.production'].search([('product_id', '=', product.id)], order='id desc', limit=1)
|
|
self.assertIn(so.name, mo.origin)
|
|
self.assertEqual(mo.product_uom_id, uom_gram)
|
|
self.assertEqual(mo.product_qty, 510)
|
|
|
|
so_2 = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'Super Partner'}).id,
|
|
'order_line': [
|
|
(0, 0, {
|
|
'name': product.name,
|
|
'product_id': product.id,
|
|
'product_uom_qty': 510,
|
|
'product_uom': uom_gram.id,
|
|
'price_unit': 1,
|
|
})],
|
|
})
|
|
so_2.action_confirm()
|
|
self.assertEqual(so_2.state, 'sale')
|
|
self.assertEqual(mo.product_uom_id, uom_gram)
|
|
self.assertEqual(mo.product_qty, 1020)
|
|
|
|
def test_sale_mrp_avoid_multiple_pickings(self):
|
|
"""
|
|
Test sale of multiple products. Avoid multiple pickings being
|
|
generated when we are not in 3 steps manufacturing.
|
|
"""
|
|
|
|
warehouse = self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
|
|
warehouse.sam_loc_id = warehouse.lot_stock_id
|
|
|
|
so = self.env['sale.order'].create({
|
|
'partner_id': self.env['res.partner'].create({'name': 'My Partner'}).id,
|
|
'order_line': [
|
|
Command.create({
|
|
'name': 'sol_p1',
|
|
'product_id': self.env['product.product'].create({'name': 'p1'}).id,
|
|
'product_uom_qty': 1,
|
|
'product_uom': self.env.ref('uom.product_uom_unit').id,
|
|
}),
|
|
Command.create({
|
|
'name': 'sol_p2',
|
|
'product_id': self.env['product.product'].create({'name': 'p2'}).id,
|
|
'product_uom_qty': 1,
|
|
'product_uom': self.env.ref('uom.product_uom_unit').id,
|
|
}),
|
|
],
|
|
})
|
|
|
|
so.action_confirm()
|
|
self.assertEqual(len(so.picking_ids), 1)
|
|
self.assertEqual(so.picking_ids.picking_type_id, warehouse.out_type_id)
|