@@ -7,7 +7,29 @@ from odoo.tools import float_compare
from datetime import datetime , timedelta
from odoo . exceptions import UserError
import re
#本地环境问题 不加报错
class ProductCategory ( models . Model ) :
_inherit = ' product.category '
negative_inventory_allowed = fields . Boolean ( string = " 允许负库存 " , default = False )
class StockPicking ( models . Model ) :
_inherit = ' stock.picking '
whether_show_quality_check = fields . Boolean ( string = " 是否显示质检 " )
class ProductTemplate ( models . Model ) :
_inherit = ' product.template '
blank_type = fields . Selection ( [
( ' 圆料 ' , ' 圆料 ' ) ,
( ' 方料 ' , ' 方料 ' ) ,
] , string = ' 坯料分类 ' )
blank_precision = fields . Selection ( [
( ' 精坯 ' , ' 精坯 ' ) ,
( ' 粗坯 ' , ' 粗坯 ' ) ,
] , string = ' 坯料类型 ' )
class SfProductionDemandPlan ( models . Model ) :
_name = ' sf.production.demand.plan '
@@ -688,19 +710,49 @@ class SfProductionDemandPlan(models.Model):
self . action_confirm ( )
def action_confirm ( self ) :
self = self . with_context (
demand_plan_line_id = self . id
)
"""
确认需求计划行,创建 BOM、触发库存规则, 并更新状态。
"""
# 将当前需求计划行 ID 写入上下文,便于后续方法使用
self = self . with_context ( demand_plan_line_id = self . id )
# 创建物料清单( BOM) , 根据供货方式进行不同的处理
self . mrp_bom_create ( )
# 启动库存规则(创建采购、生产等)
self . _action_launch_stock_rule ( )
# 根据供货方式设置状态字段
if self . supply_method in ( ' automation ' , ' manual ' ) :
self . write ( { ' status ' : ' 50 ' } )
self . write ( { ' status ' : ' 50 ' } ) # 自动/手工 供货:待排产
self . update_sale_order_state ( )
else :
self . write ( { ' status ' : ' 60 ' } )
self . write ( { ' status ' : ' 60 ' } ) # 外购/外协/客户自供:无需排产
self . update_sale_order_state ( )
def _get_embryo_template_by_supply_method ( self ) :
"""
根据供货方式返回对应的胚料模板 product.template 记录。
"""
supply_map = {
' automation ' : self . env . ref ( ' sf_dlm.product_embryo_sf_self_machining ' ) . sudo ( ) ,
' outsourcing ' : self . env . ref ( ' sf_dlm.product_embryo_sf_outsource ' ) . sudo ( ) ,
' purchase ' : self . env . ref ( ' sf_dlm.product_embryo_sf_purchase ' ) . sudo ( ) ,
' manual ' : self . env . ref ( ' jikimo_sale_multiple_supply_methods.product_template_manual_processing ' ) . sudo ( ) ,
' material_customer_provided ' : self . env . ref ( ' jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided ' ) . sudo ( ) ,
}
template = supply_map . get ( self . supply_method )
if not template :
raise UserError ( f " 未配置供货方式 { self . supply_method } 对应的胚料模板 " )
return template
def mrp_bom_create ( self ) :
"""
创建 BOM( 包含胚料与成品 BOM) , 用于后续生产或采购流程。
"""
# 如果同一计划中已有对应的 BOM 可复用,则直接使用
if self . supply_method in ( ' automation ' , ' manual ' ) :
line_ids = self . demand_plan_id . line_ids . filtered (
lambda p : p . supply_method in ( ' automation ' , ' manual ' ) and p . status in ( ' 50 ' , ' 60 ' ) )
@@ -713,32 +765,32 @@ class SfProductionDemandPlan(models.Model):
if line_ids :
self . bom_id = line_ids [ 0 ] . bom_id . id
return
# 根据供货方式选择模板和 BOM 类型
bom_type = ' '
# 根据供货方式修改成品模板
if self . supply_method == ' automation ' :
bom_type = ' normal '
product_template_id = self . env . ref ( ' sf_dlm.product_template_sf ' ) . sudo ( ) . product_tmpl_id
elif self . supply_method == ' outsourcing ' :
bom_type = ' subcontract '
product_template_id = self . env . ref (
' jikimo_sale_multiple_supply_methods.product_template_outsourcing ' ) . sudo ( )
product_template_id = self . env . ref ( ' jikimo_sale_multiple_supply_methods.product_template_outsourcing ' ) . sudo ( )
elif self . supply_method == ' purchase ' :
product_template_id = self . env . ref (
' jikimo_sale_multiple_supply_methods.product_template_purchase ' ) . sudo ( )
product_template_id = self . env . ref ( ' jikimo_sale_multiple_supply_methods.product_template_purchase ' ) . sudo ( )
elif self . supply_method == ' manual ' :
bom_type = ' normal '
product_template_id = self . env . ref (
' jikimo_sale_multiple_supply_methods.product_template_manual_processing ' ) . sudo ( )
product_template_id = self . env . ref ( ' jikimo_sale_multiple_supply_methods.product_template_manual_processing ' ) . sudo ( )
# 复制成品模板上的属性
# 使用模板复制内容到当前产品
self . product_id . product_tmpl_id . copy_template ( product_template_id )
# 构造 BOM 编码(包含时间戳)
future_time = datetime . now ( ) + timedelta ( hours = 8 )
# 生成BOM单据编码
code = f " { self . product_id . default_code } - { bom_type } - { future_time . strftime ( ' % Y % m %d % H % M % S ' ) } "
order_id = self . sale_order_id
product = self . product_id
# 拼接方法需要的item结构, 成品的模型数据信息就是坯料的数据信息
# 构造胚料产品的参数
item = {
' texture_code ' : product . materials_id . materials_no ,
' texture_type_code ' : product . materials_type_id . materials_no ,
@@ -751,110 +803,81 @@ class SfProductionDemandPlan(models.Model):
' embryo_redundancy_id ' : self . sale_order_line_id . embryo_redundancy_id ,
' model_id ' : self . model_id
}
# 从产品名中提取编号(如 S12345-3)
product_name = ' '
match = re . search ( r ' (S \ d {5} - \ d+) ' , product . name )
product_seria = 0
# 如果匹配成功,提取结果
if match :
product_name = match . group ( 0 )
# 获取成品名结尾-n的n
product_seria = int ( product_name . split ( ' - ' ) [ - 1 ] )
# 成品 供货方式为采购则不生成bom
# 如果 供货方式不是采购,则需要先创建胚料产品
if self . supply_method != ' purchase ' :
# 当成品上带有客供料选项时,生成坯料时选择“客供料”路线
# 判断是否为客户自供
if self . sale_order_line_id . embryo_redundancy_id :
# 将成品模板的内容复制到成品上
customer_provided_embryo = self . env . ref (
' jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided ' ) . sudo ( )
# 创建坯料, 客供料的批量不需要创建bom
material_customer_provided_embryo = self . env [ ' product.template ' ] . sudo ( ) . no_bom_product_create (
customer_provided_embryo . with_context ( active_test = False ) . product_variant_id ,
item ,
order_id , ' material_customer_provided ' , product_seria , product )
# 成品配置bom
product_bom_material_customer_provided = self . env [ ' mrp.bom ' ] . with_user (
self . env . ref ( " base.user_admin " ) ) . bom _create(
product , bom_type , ' product ' , code )
product_bom_material_customer_provided . with_user (
self . env . ref ( " base.user_admin " ) ) . bom_create_line_has (
material_customer_provided_embryo )
self . bom_id = product_bom_material_customer_provided . id
elif self . product_id . materials_type_id . gain_way == ' 自加工 ' :
self_machining_id = self . env . ref ( ' sf_dlm.product_embryo_sf_self_machining ' ) . sudo ( )
# 创建坯料
self_machining_embryo = self . env [ ' product.template ' ] . sudo ( ) . no_bom_product_create (
self_machining_id ,
item ,
order_id , ' self_machining ' , product_seria , product )
# 创建坯料的bom
self_machining_bom = self . env [ ' mrp.bom ' ] . with_user (
s elf . env . ref ( " base.user_admin " ) ) . bom_create (
self_machining_embryo , ' normal ' , False )
# 创建坯料里bom的组件
self_machining _bom_lin e = self_machining_bom . with_user (
self . env . ref ( " base.user_admin " ) ) . bom_create_line (
self_machining_embryo )
if not self_machining_bom_line :
raise UserError ( ' 该订单模型的材料型号暂未有原材料,请先配置再进行分配 ' )
# 产品配置bom
product_bom_self_machining = self . env [ ' mrp.bom ' ] . with_user (
self . env . ref ( " base.user_admin " ) ) . bom_create (
product , bom_type , ' product ' , code )
product_bom_self_machining . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create_line_has (
self_machining_embryo )
self . bom_id = product_bom_self_machining . id
elif self . product_id . materials_type_id . gain_way == ' 外协 ' :
outsource_id = self . env . ref ( ' sf_dlm.product_embryo_sf_outsource ' ) . sudo ( )
# 创建坯料
outsource_embryo = self . env [ ' product.template ' ] . sudo ( ) . no_ bom_product_create ( outsource_id ,
item ,
order_id ,
' subcontract ' ,
product_seria ,
product )
if outsource_embryo == - 3 :
raise UserError ( ' 该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配 ' )
# 创建坯料的bom
outsource_bom = self . env [ ' mrp.bom ' ] . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create (
outsource_embryo ,
' subcontract ' , True )
# 创建坯料的bom的组件
outsource_bom_line = outsource_bom . with_user (
self . env . ref ( " base.user_admin " ) ) . bom_create_line ( outsource_embryo )
if not outsource_bom_line :
raise UserError ( ' 该订单模型的材料型号暂未有原材料,请先配置再进行分配 ' )
# 产品配置bom
product_bom_outsource = self . env [ ' mrp.bom ' ] . with_user (
self . env . ref ( " base.user_admin " ) ) . bom_create ( product , bom_type , ' product ' , code )
product_bom_outsource . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create_line_has (
outsource_embryo )
self . bom_id = product_bom_outsource . id
elif self . product_id . materials_type_id . gain_way == ' 采购 ' :
purchase_id = self . env . ref ( ' sf_dlm.product_embryo_sf_purchase ' ) . sudo ( )
purchase_embryo = self . env [ ' product.template ' ] . sudo ( ) . no_bom_product_create ( purchase_id ,
item ,
order_id ,
' purchase ' ,
product_seria ,
product )
if purchase_embryo and purchase_embryo == - 3 :
raise UserError ( ' 该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配 ' )
else :
# 产品配置bom
product_bom_purchase = self . env [ ' mrp.bom ' ] . with_user (
self . env . ref ( " base.user_admin " ) ) . bom_create ( product , bom_type , ' product ' , code )
product_bom_purchase . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create_line_has (
purchase_embryo )
self . bom_id = product_bom_purchase . id
embryo_template = self . env . ref ( ' jikimo_sale_multiple_supply_methods.product_template_embryo_customer_provided ' ) . sudo ( )
embryo_key = ' material_customer_provided '
else :
embryo_template = self . _get_embryo_template_by_supply_method ( )
embryo_key = self . supply_method
# 获取批次追踪方式
tracking_method = embryo_template . tracking
# 创建胚料产品(无 BOM 产品)
embryo_product = self . env [ ' product.template ' ] . sudo ( ) . no_bom_product _create(
embryo_template . with_context ( active_test = False ) . product_variant_id ,
item ,
order_id ,
embryo_key ,
product_seria ,
product
)
if embryo_product == - 3 :
raise U serError ( ' 该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配 ' )
# 设置胚料 BOM 类型
if embryo_key in ( ' automation ' , ' manual ' , ' material_customer_provided ' ) :
embryo_bom_type = ' normal '
eli f embryo_key == ' outsourcing ' :
embryo_bom_type = ' subcontract '
elif embryo_key == ' purchase ' :
embryo _bom_typ e = ' purchase '
else :
embryo_bom_type = ' normal '
# 创建胚料 BOM 及 BOM 行
embryo_bom = self . env [ ' mrp.bom ' ] . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create (
embryo_product , embryo_bom_type , True , tracking = tracking_method )
embryo_bom_line = embryo_bom . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create_line ( embryo_product )
if not embryo_bom_line :
raise U serError ( ' 该订单模型的材料型号暂未有原材料,请先配置再进行分配 ' )
# 创建成品 BOM( 包含胚料)
product_bom = self . env [ ' mrp.bom ' ] . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create (
product , bom_type , ' product ' , code , tracking = tracking_method )
product_bom . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create_line_has ( embryo_product )
# 赋值 BOM ID
self . bom_id = product_bom . id
def _action_launch_stock_rule ( self ) :
"""
触发库存规则(如采购、生产),并确认相关拣货单。
"""
procurements = [ ]
group_id = self . sale_order_id . procurement_group_id
if not group_id :
# 没有分组则创建
group_id = self . env [ ' procurement.group ' ] . create ( self . _prepare_procurement_group_vals ( ) )
self . sale_order_id . procurement_group_id = group_id
else :
# 若已有分组但字段有变动则更新
updated_vals = { }
if group_id . partner_id != self . sale_order_id . partner_shipping_id :
updated_vals . update ( { ' partner_id ' : self . sale_order_id . partner_shipping_id . id } )
@@ -862,27 +885,42 @@ class SfProductionDemandPlan(models.Model):
updated_vals . update ( { ' move_type ' : self . sale_order_id . picking_policy } )
if updated_vals :
group_id . write ( updated_vals )
# 构造 procurement 所需的字段
values = self . _prepare_procurement_values ( group_id = group_id )
# 单位换算
line_uom = self . sale_order_line_id . product_uom
quant_uom = self . product_id . uom_id
plan_uom_qty , procurement_uom = line_uom . _adjust_uom_quantities ( self . plan_uom_qty , quant_uom )
# 创建 procurement 请求
procurements . append ( self . env [ ' procurement.group ' ] . Procurement (
self . product_id , plan_uom_qty , procurement_uom ,
self . sale_order_id . partner_shipping_id . property_stock_customer ,
self . product_id . display_name , self . sale_order_id . name , self . sale_order_id . company_id , values ) )
# 执行调度
if procurements :
procurement_group = self . env [ ' procurement.group ' ]
if self . env . context . get ( ' import_file ' ) :
procurement_group = procurement_group . with_context ( import_file = False )
procurement_group . run ( procurements )
# 确认相关的拣货单
orders = self . mapped ( ' sale_order_id ' )
for order in orders :
pickings_to_confirm = order . picking_ids . filtered ( lambda p : p . state not in [ ' cancel ' , ' done ' ] )
if pickings_to_confirm :
pickings_to_confirm . action_confirm ( )
return True
def _prepare_procurement_group_vals ( self ) :
"""
构造创建 procurement group 所需的字段。
"""
return {
' name ' : self . sale_order_id . name ,
' move_type ' : self . sale_order_id . picking_policy ,
@@ -890,11 +928,18 @@ class SfProductionDemandPlan(models.Model):
' partner_id ' : self . sale_order_id . partner_shipping_id . id ,
}
def _prepare_procurement_values ( self , group_id = False ) :
"""
构造单个 procurement 请求所需的字段字典。
"""
self . ensure_one ( )
# 交货日期与计划日期
date_deadline = self . sale_order_id . commitment_date or (
self . sale_order_id . date_order + timedelta ( days = self . sale_order_line_id . customer_lead or 0.0 ) )
self . sale_order_id . date_order + timedelta ( days = self . sale_order_line_id . customer_lead or 0.0 ) )
date_planned = date_deadline - timedelta ( days = self . sale_order_id . company_id . security_lead )
values = {
' group_id ' : group_id ,
' sale_line_id ' : self . sale_order_line_id . id ,
@@ -910,4 +955,6 @@ class SfProductionDemandPlan(models.Model):
' sequence ' : self . sale_order_line_id . sequence ,
' demand_plan_line_id ' : self . id
}
return values