@@ -7,17 +7,39 @@ 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 '
_description = ' sf_production_demand_plan '
def get_location_id ( self ) :
cu stomer _location_id = self . env [ ' ir.model.data ' ] . _xmlid_to_res_id ( ' stock.stock_location_customers ' )
return cu stomer _location_ id
stock _location = self . env [ ' stock.location ' ] . sudo ( ) . search ( [ ( ' name ' , ' = ' , ' 客户 ' ) ] , limit = 1 )
return stock _location. id
priority = fields . Selection ( related = ' demand_plan_id.priority ' , string = ' 优先级 ' , store = True )
priority = fields . Selection ( related = ' demand_plan_id.priority ' , string = ' 优先级 ' )
status = fields . Selection ( [
( ' 10 ' , ' 草稿 ' ) ,
( ' 20 ' , ' 待确认 ' ) ,
@@ -34,7 +56,7 @@ class SfProductionDemandPlan(models.Model):
company_id = fields . Many2one (
related = ' sale_order_id.company_id ' ,
store = True , index = True , precompute = True )
customer_name = fields . Char ( ' 客户 ' , related = ' sale_order_id.customer_name ' , store = True )
customer_name = fields . Char ( ' 客户 ' , related = ' sale_order_id.customer_name ' )
order_remark = fields . Text ( related = ' sale_order_id.remark ' ,
string = " 订单备注 " , store = True )
glb_url = fields . Char ( related = ' sale_order_line_id.glb_url ' , string = ' glb文件地址 ' )
@@ -83,7 +105,7 @@ class SfProductionDemandPlan(models.Model):
related = ' product_id.blank_precision ' )
unit_number = fields . Float ( ' 单件用量 ' , digits = ( 16 , 3 ) , related = ' product_id.unit_number ' )
embryo_long = fields . Char ( ' 坯料尺寸(mm) ' , related = ' demand_plan_id.embryo_long ' )
materials_id = fields . Char ( ' 材料 ' , related = ' demand_plan_id.materials_id ' , store = True )
materials_id = fields . Char ( ' 材料 ' , related = ' demand_plan_id.materials_id ' )
model_machining_precision = fields . Selection ( related = ' product_id.model_machining_precision ' , string = ' 精度 ' )
model_process_parameters_ids = fields . Many2many ( related = ' demand_plan_id.model_process_parameters_ids ' ,
string = ' 表面工艺 ' , )
@@ -127,12 +149,6 @@ class SfProductionDemandPlan(models.Model):
string = ' 字段自制类型只读 '
)
is_processing = fields . Boolean (
string = ' 正在处理中 ' ,
default = False ,
help = ' 用于防止重复点击按钮 '
)
# hide_action_open_mrp_production = fields.Boolean(
# string='显示待工艺确认按钮',
# compute='_compute_hid_button',
@@ -613,6 +629,11 @@ class SfProductionDemandPlan(models.Model):
filtered_plan = self . filtered ( lambda mo : mo . status == ' 30 ' )
if not filtered_plan :
raise UserError ( _ ( " 没有需要下达的计划! " ) )
check_overdelivery_allowed = False
if not self . demand_plan_id . overdelivery_allowed :
customer_location_id = self . env [ ' ir.model.data ' ] . _xmlid_to_res_id ( ' stock.stock_location_customers ' )
if self . location_id . id == customer_location_id :
check_overdelivery_allowed = True
# 按产品分组并计算总数
product_data = { }
for plan in filtered_plan :
@@ -660,15 +681,9 @@ class SfProductionDemandPlan(models.Model):
' default_demand_plan_line_id ' : self . ids ,
' default_release_message ' : warning_message ,
} }
else :
for demand_plan_line_id in filtered_plan :
demand_plan_line_id . action_confirm ( )
def button_release_plan ( self ) :
self . ensure_one ( )
if self . is_processing :
return
self . is_processing = True
check_overdelivery_allowed = False
if not self . demand_plan_id . overdelivery_allowed :
customer_location_id = self . env [ ' ir.model.data ' ] . _xmlid_to_res_id ( ' stock.stock_location_customers ' )
@@ -695,38 +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 mrp_bom_create ( self ) :
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 ( )
elif self . supply_method == ' purchase ' :
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 ( )
# 复制成品模板上的属性
self . product_id . product_tmpl_id . copy_template ( product_template_id )
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 ' ) )
@@ -739,13 +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 ( )
elif self . supply_method == ' purchase ' :
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 ( )
# 使用模板复制内容到当前产品
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 ,
@@ -758,110 +803,84 @@ 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_m ach ining_id ,
item ,
order_id , ' self_machining ' , product_seria , product )
# 创建坯料的bom
self_machining_bom = self . env [ ' mrp.bom ' ] . with_user (
self . env . ref ( " base.user_admin " ) ) . bom_create (
self_machining_embryo , ' norm al' , False )
# 创建坯料里bom的组件
self_machining_bom_line = 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
el if 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_i d ,
' 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 isinstance ( embryo_product , models . Model ) : # 确保返回的是记录而非错误码
embryo_product . write ( { ' tr ack ing ' : tracking_method } )
if embryo_product == - 3 :
raise UserError ( ' 该订单模型的材料型号暂未设置获取方式和供应商,请先配置再进行分配 ' )
# 设置胚料 BOM 类型
if embryo_key in ( ' automation ' , ' manu al' , ' material_customer_provided ' ) :
embryo_bom_type = ' normal '
elif embryo_key == ' outsourcing ' :
embryo_bom_type = ' subcontract '
elif embryo_key == ' purchase ' :
embryo_bom_type = ' 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 UserError ( ' 该订单模型的材料型号暂未有原材料,请先配置再进行分配 ' )
# 创建成品 BOM( 包含胚料)
product_bom = self . env [ ' mrp.bom ' ] . with_user ( self . env . ref ( " base.user_admin " ) ) . bom_create (
product , bom_type , ' product ' , code , tracking = tracking_metho d)
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 } )
@@ -869,27 +888,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 ,
@@ -897,11 +931,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 ,
@@ -917,7 +958,6 @@ class SfProductionDemandPlan(models.Model):
' sequence ' : self . sale_order_line_id . sequence ,
' demand_plan_line_id ' : self . id
}
return values
def button_plan_detail ( self ) :
pass