oca-technical/odoo-bringout-oca-ddmrp-ddmrp/ddmrp/tests/common.py
2025-08-29 15:43:03 +02:00

549 lines
20 KiB
Python

# Copyright 2016-20 ForgeFlow S.L. (http://www.forgeflow.com)
# Copyright 2016 Aleph Objects, Inc. (https://www.alephobjects.com/)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from datetime import datetime
import odoo.tests.common as common
class TestDdmrpCommon(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(
context=dict(
cls.env.context,
tracking_disable=True,
# compatibility with ddmrp_cron_actions_as_job,
# that would delay calls to "cron_actions" in these tests
test_queue_job_no_delay=True,
)
)
# Models
cls.productModel = cls.env["product.product"]
cls.templateModel = cls.env["product.template"]
cls.bomModel = cls.env["mrp.bom"]
cls.bomlineModel = cls.env["mrp.bom.line"]
cls.bufferModel = cls.env["stock.buffer"]
cls.pickingModel = cls.env["stock.picking"]
cls.moveModel = cls.env["stock.move"]
cls.quantModel = cls.env["stock.quant"]
cls.estimateModel = cls.env["stock.demand.estimate"]
cls.aducalcmethodModel = cls.env["product.adu.calculation.method"]
cls.locationModel = cls.env["stock.location"]
cls.make_procurement_wiz = cls.env["make.procurement.buffer"]
cls.user_model = cls.env["res.users"]
cls.partner_model = cls.env["res.partner"]
cls.supinfo_model = cls.env["product.supplierinfo"]
cls.pol_model = cls.env["purchase.order.line"]
cls.wh_model = cls.env["stock.warehouse"]
cls.orderpoint_model = cls.env["stock.warehouse.orderpoint"]
# Refs
cls.main_company = cls.env.ref("base.main_company")
cls.second_company = cls.env.ref("stock.res_company_1")
cls.warehouse = cls.env.ref("stock.warehouse0")
cls.warehouse2 = cls.wh_model.create(
{
"partner_id": cls.env.ref("base.main_partner").id,
"name": "Warehouse 2",
"code": "WH2",
}
)
cls.warehouse_sc = cls.env.ref("stock.stock_warehouse_shop0")
cls.stock_location = cls.env.ref("stock.stock_location_stock")
cls.stock_location_sc = cls.warehouse_sc.lot_stock_id
cls.location_shelf1 = cls.env.ref("stock.stock_location_components")
cls.supplier_location = cls.env.ref("stock.stock_location_suppliers")
cls.customer_location = cls.env.ref("stock.stock_location_customers")
cls.inter_wh = cls.env.ref("stock.stock_location_inter_wh")
cls.inventory_location = cls.env["stock.location"].search(
[("usage", "=", "inventory"), ("company_id", "=", cls.main_company.id)],
limit=1,
)
cls.picking_type_out = cls.env.ref("stock.picking_type_out").copy(
{
"reservation_method": "manual",
"sequence_code": "DDMRP-OUT",
}
)
cls.picking_type_in = cls.env.ref("stock.picking_type_in")
cls.picking_type_internal = cls.env.ref("stock.picking_type_internal").copy(
{
"reservation_method": "manual",
"sequence_code": "DDMRP-INTERNAL",
}
)
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
cls.dozen_unit = cls.env.ref("uom.product_uom_dozen")
cls.buffer_profile_pur = cls.env.ref(
"ddmrp.stock_buffer_profile_replenish_purchased_medium_medium"
)
cls.buffer_profile_mmm = cls.env.ref(
"ddmrp.stock_buffer_profile_replenish_manufactured_medium_medium"
)
cls.buffer_profile_distr = cls.env.ref(
"ddmrp.stock_buffer_profile_replenish_distributed_medium_medium"
)
cls.buffer_profile_override = cls.env.ref(
"ddmrp.stock_buffer_profile_replenish_override_purchased_short_low"
)
cls.adu_fixed = cls.env.ref("ddmrp.adu_calculation_method_fixed")
cls.group_stock_manager = cls.env.ref("stock.group_stock_manager")
cls.group_mrp_user = cls.env.ref("mrp.group_mrp_user")
cls.group_change_procure_qty = cls.env.ref(
"ddmrp.group_change_buffer_procure_qty"
)
cls.group_buffer_manager = cls.env.ref("ddmrp.group_stock_buffer_maintainer")
cls.calendar = cls.env.ref("resource.resource_calendar_std")
cls.warehouse.calendar_id = cls.calendar
# Create users
cls.user = cls._create_user(
"user_1",
[
cls.group_stock_manager,
cls.group_mrp_user,
cls.group_change_procure_qty,
cls.group_buffer_manager,
],
)
# Create Partners:
vendor = cls.partner_model.create({"name": "Test Vendor 1"})
# Create products and BoM's:
manufacture_route = cls.env.ref("mrp.route_warehouse0_manufacture")
cls.productA = cls.productModel.create(
{
"name": "product A",
"standard_price": 1,
"type": "product",
"uom_id": cls.uom_unit.id,
"default_code": "A",
"route_ids": [(6, 0, manufacture_route.ids)],
"produce_delay": 10.0,
}
)
cls.component_a1 = cls.productModel.create(
{
"name": "Component A-1",
"standard_price": 1,
"type": "product",
"uom_id": cls.uom_unit.id,
"default_code": "RM-test01",
}
)
cls.bom_a = cls.bomModel.create(
{
"product_tmpl_id": cls.productA.product_tmpl_id.id,
"product_id": cls.productA.id,
}
)
cls.bomlineModel.create(
{
"product_id": cls.component_a1.id,
"product_qty": 2.0,
"bom_id": cls.bom_a.id,
}
)
# Create locations:
cls.binA = cls.locationModel.create(
{
"usage": "internal",
"name": "Bin A",
"location_id": cls.location_shelf1.id,
"company_id": cls.main_company.id,
}
)
cls.binB = cls.locationModel.create(
{
"usage": "internal",
"name": "Bin B",
"location_id": cls.location_shelf1.id,
"company_id": cls.main_company.id,
}
)
cls.locationModel._parent_store_compute()
cls.quant = cls.quantModel.create(
{
"location_id": cls.binA.id,
"company_id": cls.main_company.id,
"product_id": cls.productA.id,
"inventory_quantity": 200.0,
}
)
cls.quant.action_apply_inventory()
# Product B (purchased):
buy_route = cls.env.ref("purchase_stock.route_warehouse0_buy")
cls.product_purchased = cls.productModel.create(
{
"name": "product Purchased",
"standard_price": 1,
"type": "product",
"uom_id": cls.uom_unit.id,
"default_code": "B",
"route_ids": [(6, 0, buy_route.ids)],
}
)
cls.product_purchased_2 = cls.productModel.create(
{
"name": "product Purchased 2",
"standard_price": 1,
"type": "product",
"uom_id": cls.uom_unit.id,
"default_code": "B",
"route_ids": [(6, 0, buy_route.ids)],
}
)
cls.supinfo_model.create(
{
"product_tmpl_id": cls.product_purchased.product_tmpl_id.id,
"partner_id": vendor.id,
"delay": 20.0,
}
)
# Product C (purchased and with variants):
cls.template_c = cls.templateModel.create(
{
"name": "Product C",
"type": "product",
"uom_id": cls.uom_unit.id,
"default_code": "C",
"route_ids": [(6, 0, buy_route.ids)],
}
)
cls.color_attribute = cls.env["product.attribute"].create(
{"name": "Color", "sequence": 1}
)
cls.color_blue = cls.env["product.attribute.value"].create(
{"name": "Blue", "attribute_id": cls.color_attribute.id, "sequence": 1}
)
cls.color_orange = cls.env["product.attribute.value"].create(
{"name": "Orange", "attribute_id": cls.color_attribute.id, "sequence": 2}
)
cls.color_green = cls.env["product.attribute.value"].create(
{"name": "Green", "attribute_id": cls.color_attribute.id, "sequence": 3}
)
cls.p_c_color_attribute_line = cls.env[
"product.template.attribute.line"
].create(
{
"product_tmpl_id": cls.template_c.id,
"attribute_id": cls.color_attribute.id,
"value_ids": [
(6, 0, [cls.color_blue.id, cls.color_orange.id, cls.color_green.id])
],
}
)
cls.product_c_blue = cls.template_c.product_variant_ids[0]
cls.product_c_orange = cls.template_c.product_variant_ids[1]
cls.product_c_green = cls.template_c.product_variant_ids[2]
cls.p_c_supinfo_blue = cls.supinfo_model.create(
{
"product_tmpl_id": cls.template_c.id,
"product_id": cls.product_c_blue.id,
"partner_id": vendor.id,
"delay": 5.0,
}
)
cls.p_c_supinfo_orange = cls.supinfo_model.create(
{
"product_tmpl_id": cls.template_c.id,
"product_id": cls.product_c_orange.id,
"partner_id": vendor.id,
"delay": 10.0,
}
)
cls.p_c_supinfo_no_variant = cls.supinfo_model.create(
{
"product_tmpl_id": cls.template_c.id,
"partner_id": vendor.id,
"delay": 8.0,
}
)
# Product D (distributed):
cls.product_distributed = cls.productModel.create(
{
"name": "product Distributed",
"standard_price": 1,
"type": "product",
"uom_id": cls.uom_unit.id,
"default_code": "D",
# TODO: "route_ids": [(6, 0, distribution_route.id)],
}
)
# Create buffers:
cls.buffer_a = cls.bufferModel.create(
{
"buffer_profile_id": cls.buffer_profile_mmm.id,
"product_id": cls.productA.id,
"location_id": cls.stock_location.id,
"warehouse_id": cls.warehouse.id,
"qty_multiple": 1.0,
"adu_calculation_method": cls.adu_fixed.id,
"adu_fixed": 4.0,
"lead_days": 10.0,
"order_spike_horizon": 10.0,
}
)
cls.buffer_purchase = cls.bufferModel.create(
{
"buffer_profile_id": cls.buffer_profile_pur.id,
"product_id": cls.product_purchased.id,
"location_id": cls.stock_location.id,
"warehouse_id": cls.warehouse.id,
"qty_multiple": 1.0,
"adu_calculation_method": cls.adu_fixed.id,
"adu_fixed": 5.0,
}
)
cls.buffer_c_blue = cls.bufferModel.create(
{
"buffer_profile_id": cls.buffer_profile_pur.id,
"product_id": cls.product_c_blue.id,
"location_id": cls.stock_location.id,
"warehouse_id": cls.warehouse.id,
"qty_multiple": 1.0,
"adu_calculation_method": cls.adu_fixed.id,
"adu_fixed": 5.0,
}
)
cls.buffer_c_orange = cls.bufferModel.create(
{
"buffer_profile_id": cls.buffer_profile_pur.id,
"product_id": cls.product_c_orange.id,
"location_id": cls.stock_location.id,
"warehouse_id": cls.warehouse.id,
"qty_multiple": 1.0,
"adu_calculation_method": cls.adu_fixed.id,
"adu_fixed": 5.0,
}
)
cls.buffer_distributed = cls.bufferModel.create(
{
"buffer_profile_id": cls.buffer_profile_distr.id,
"product_id": cls.product_distributed.id,
"location_id": cls.stock_location.id,
"warehouse_id": cls.warehouse.id,
"qty_multiple": 1.0,
"adu_calculation_method": cls.adu_fixed.id,
"adu_fixed": 5.0,
"lead_days": 20,
}
)
# dates for a period of 120 days for estimates.
cls.estimate_date_from = cls.calendar.plan_days(1, datetime.today()).date()
days = 119
dt = cls.calendar.plan_days(+1 * days + 1, datetime.today())
cls.estimate_date_to = dt.date()
# Run cron jobs:
cls.bufferModel.cron_ddmrp_adu()
cls.bufferModel.cron_ddmrp()
@classmethod
def _create_user(cls, login, groups):
"""Create a user."""
group_ids = [group.id for group in groups]
user = cls.user_model.with_context(no_reset_password=True).create(
{
"name": "Test User",
"login": login,
"password": "demo",
"email": "test@yourcompany.com",
"groups_id": [(6, 0, group_ids)],
}
)
return user
def create_pickingoutA(self, date_move, qty, uom=False, source_location=None):
if not uom:
uom = self.productA.uom_id
if not source_location:
source_location = self.binA
picking = self.pickingModel.with_user(self.user).create(
{
"picking_type_id": self.picking_type_out.id,
"location_id": source_location.id,
"location_dest_id": self.customer_location.id,
"scheduled_date": date_move,
"move_ids": [
(
0,
0,
{
"name": "Test move",
"product_id": self.productA.id,
"date": date_move,
"product_uom": uom.id,
"product_uom_qty": qty,
"location_id": source_location.id,
"location_dest_id": self.customer_location.id,
},
)
],
}
)
picking.action_confirm()
return picking
def create_pickinginA(self, date_move, qty):
picking = self.pickingModel.with_user(self.user).create(
{
"picking_type_id": self.picking_type_in.id,
"location_id": self.supplier_location.id,
"location_dest_id": self.binA.id,
"scheduled_date": date_move,
"move_ids": [
(
0,
0,
{
"name": "Test move",
"product_id": self.productA.id,
"date": date_move,
"date_deadline": date_move,
"product_uom": self.productA.uom_id.id,
"product_uom_qty": qty,
"location_id": self.supplier_location.id,
"location_dest_id": self.binA.id,
},
)
],
}
)
picking.action_confirm()
return picking
def create_pickinginternalA(self, date_move, qty):
picking = self.pickingModel.with_user(self.user).create(
{
"picking_type_id": self.picking_type_internal.id,
"location_id": self.binA.id,
"location_dest_id": self.binB.id,
"scheduled_date": date_move,
"move_ids": [
(
0,
0,
{
"name": "Test move",
"product_id": self.productA.id,
"date": date_move,
"product_uom": self.productA.uom_id.id,
"product_uom_qty": qty,
"location_id": self.binA.id,
"location_dest_id": self.binB.id,
},
)
],
}
)
picking.action_confirm()
return picking
def create_picking_out(self, product, date_move, qty, source_location=None):
if not source_location:
source_location = self.binA
picking = self.pickingModel.with_user(self.user).create(
{
"picking_type_id": self.picking_type_out.id,
"location_id": source_location.id,
"location_dest_id": self.customer_location.id,
"scheduled_date": date_move,
"move_ids": [
(
0,
0,
{
"name": "Test move",
"product_id": product.id,
"date": date_move,
"product_uom": product.uom_id.id,
"product_uom_qty": qty,
"location_id": source_location.id,
"location_dest_id": self.customer_location.id,
},
)
],
}
)
picking.action_confirm()
return picking
def create_picking_in(self, product, date_move, qty):
picking = self.pickingModel.with_user(self.user).create(
{
"picking_type_id": self.picking_type_in.id,
"location_id": self.supplier_location.id,
"location_dest_id": self.binA.id,
"scheduled_date": date_move,
"move_ids": [
(
0,
0,
{
"name": "Test move",
"product_id": product.id,
"date": date_move,
"product_uom": product.uom_id.id,
"product_uom_qty": qty,
"location_id": self.supplier_location.id,
"location_dest_id": self.binA.id,
},
)
],
}
)
picking.action_confirm()
return picking
def _do_picking(self, picking, date):
"""Do picking with only one move on the given date."""
picking.action_confirm()
picking.move_ids.quantity_done = picking.move_ids.product_uom_qty
picking._action_done()
for move in picking.move_ids:
move.date = date
def create_orderpoint_procurement(self, buffer, make_procurement=True):
"""Make Procurement from Reordering Rule"""
wizard = (
self.make_procurement_wiz.with_user(self.user)
.with_context(
active_model="stock.buffer", active_ids=buffer.ids, active_id=buffer.id
)
.create({})
)
if make_procurement:
wizard.make_procurement()
return wizard
def create_inventorylossA(self, date_move, qty):
move = self.moveModel.with_user(self.user).create(
{
"name": "Test inventory move",
"product_id": self.productA.id,
"date": date_move,
"product_uom": self.productA.uom_id.id,
"product_uom_qty": qty,
"location_id": self.binA.id,
"location_dest_id": self.inventory_location.id,
},
)
return move
def _do_move(self, move, date):
move._action_confirm()
move.move_line_ids.qty_done = move.move_line_ids.reserved_uom_qty
move._action_done()
move.date = date