diff --git a/sf_dlm/data/product_data.xml b/sf_dlm/data/product_data.xml
index b8cab582..06bed6ea 100644
--- a/sf_dlm/data/product_data.xml
+++ b/sf_dlm/data/product_data.xml
@@ -14,10 +14,12 @@
原材料
原材料
-
表面工艺
表面工艺
+
+ fifo
+ manual_periodic
@@ -40,10 +42,10 @@
-
-
-
-
+
+
+
+
功能刀具
diff --git a/sf_dlm_management/__init__.py b/sf_dlm_management/__init__.py
index e69de29b..9a7e03ed 100644
--- a/sf_dlm_management/__init__.py
+++ b/sf_dlm_management/__init__.py
@@ -0,0 +1 @@
+from . import models
\ No newline at end of file
diff --git a/sf_dlm_management/__manifest__.py b/sf_dlm_management/__manifest__.py
index 9a94082a..96344803 100644
--- a/sf_dlm_management/__manifest__.py
+++ b/sf_dlm_management/__manifest__.py
@@ -9,8 +9,9 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
- 'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing','jikimo_attachment_viewer'],
+ 'depends': ['sf_sale', 'sf_dlm', 'sf_manufacturing', 'jikimo_attachment_viewer'],
'data': [
+ 'data/sequence.xml',
'data/stock_data.xml',
'views/product_template_management_view.xml',
],
diff --git a/sf_dlm_management/data/sequence.xml b/sf_dlm_management/data/sequence.xml
new file mode 100644
index 00000000..39379169
--- /dev/null
+++ b/sf_dlm_management/data/sequence.xml
@@ -0,0 +1,10 @@
+
+
+
+ 工艺可选参数编码序列
+ sf.production.process.parameter
+ WKSP
+ 9
+
+
+
\ No newline at end of file
diff --git a/sf_dlm_management/models/__init__.py b/sf_dlm_management/models/__init__.py
index 8c38257e..650ac243 100644
--- a/sf_dlm_management/models/__init__.py
+++ b/sf_dlm_management/models/__init__.py
@@ -1,2 +1,3 @@
# from . import product_template
# from . import product_supplierinfo
+from . import sf_production_common
\ No newline at end of file
diff --git a/sf_dlm_management/models/sf_production_common.py b/sf_dlm_management/models/sf_production_common.py
new file mode 100644
index 00000000..918b6b6d
--- /dev/null
+++ b/sf_dlm_management/models/sf_production_common.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+import logging
+from odoo import fields, models, api
+from odoo.exceptions import UserError
+from odoo.tools import str2bool
+
+
+class SfProductionProcessParameter(models.Model):
+ _inherit = 'sf.production.process.parameter'
+ @api.model
+ def create(self, vals):
+ if vals.get('code', '/') == '/' or vals.get('code', '/') is False:
+ vals['code'] = self.env['ir.sequence'].next_by_code('sf.production.process.parameter') or '/'
+ if not vals.get('process_id') and vals.get('routing_id'):
+ vals['gain_way'] = '外协'
+ routing_id = self.env['mrp.routing.workcenter'].browse(vals.get('routing_id'))
+ if routing_id.surface_technics_id:
+ vals['process_id'] = routing_id.surface_technics_id.id
+ obj = super(SfProductionProcessParameter, self).create(vals)
+ return obj
+ def create_service_product(self):
+ service_categ = self.env.ref(
+ 'sf_dlm.product_category_surface_technics_sf').sudo()
+
+ product_name = f"{self.process_id.name}{self.name}"
+ product_id = self.env['product.template'].search(
+ [("name", '=', product_name)])
+ if product_id:
+ product_id.server_product_process_parameters_id = self.id
+ else:
+ self.env['product.template'].create({
+ 'detailed_type': 'service',
+ 'name': product_name,
+ 'invoice_policy': 'delivery',
+ 'categ_id': service_categ.id,
+ 'description': f"基于{self.name}创建的服务产品",
+ 'sale_ok': True, # 可销售
+ 'purchase_ok': True, # 可采购
+ 'server_product_process_parameters_id': self.id,
+ })
+
+ def create_work_center(self):
+ production_process_parameter = self
+ if not production_process_parameter.process_id:
+ return
+ if not production_process_parameter.routing_id:
+ workcenter_id = self.env['mrp.routing.workcenter'].search(
+ [("surface_technics_id", '=', production_process_parameter.process_id.id)])
+ if not workcenter_id:
+ outsourcing_work_center = self.env['mrp.workcenter'].search(
+ [("name", '=', '外协工作中心')])
+ routing_id = self.env['mrp.routing.workcenter'].create({
+ 'workcenter_ids': [(6, 0, outsourcing_work_center.ids)],
+ 'routing_tag': 'special',
+ 'routing_type': '表面工艺',
+ 'is_outsource': True,
+ 'surface_technics_id': production_process_parameter.process_id.id,
+ 'name': production_process_parameter.process_id.name,
+ })
+ production_process_parameter.routing_id = routing_id.id
+ else:
+ production_process_parameter.routing_id = workcenter_id.id
+
+ def init(self):
+ super(SfProductionProcessParameter, self).init()
+ # 在模块初始化时触发计算字段的更新
+ records = self.search([])
+ if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_process',
+ default='False')):
+ return
+ for record in records:
+ if not record.outsourced_service_products:
+ record.create_service_product()
+ record.create_work_center()
+ self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_process', True)
diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py
index ba0c6751..b0d77c53 100644
--- a/sf_manufacturing/__manifest__.py
+++ b/sf_manufacturing/__manifest__.py
@@ -10,7 +10,8 @@
""",
'category': 'sf',
'website': 'https://www.sf.jikimo.com',
- 'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse','jikimo_attachment_viewer', 'jikimo_sale_multiple_supply_methods'],
+ 'depends': ['sf_base', 'sf_maintenance', 'web_widget_model_viewer', 'sf_warehouse', 'jikimo_attachment_viewer',
+ 'jikimo_sale_multiple_supply_methods','product'],
'data': [
'data/cron_data.xml',
'data/stock_data.xml',
@@ -18,6 +19,7 @@
'data/panel_data.xml',
'data/sf_work_individuation_page.xml',
'data/agv_scheduling_data.xml',
+ 'data/product_data.xml',
'security/group_security.xml',
'security/ir.model.access.csv',
'wizard/workpiece_delivery_views.xml',
diff --git a/sf_manufacturing/data/product_data.xml b/sf_manufacturing/data/product_data.xml
new file mode 100644
index 00000000..8331c373
--- /dev/null
+++ b/sf_manufacturing/data/product_data.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ 服务
+
+ fifo
+ manual_periodic
+
+
+ 工序外协
+
+ fifo
+ manual_periodic
+
+
+ 其他
+
+ fifo
+ manual_periodic
+
+
+
\ No newline at end of file
diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py
index 52c5f463..adddbd7d 100644
--- a/sf_manufacturing/models/mrp_production.py
+++ b/sf_manufacturing/models/mrp_production.py
@@ -917,9 +917,11 @@ class MrpProduction(models.Model):
sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.sequence)
if not sorted_workorders:
return
+ purchase_request_line = []
for workorders in reversed(sorted_workorders):
self.env['stock.picking'].create_outcontract_picking(workorders, production, sorted_workorders)
self.env['purchase.order'].get_purchase_order(workorders, production, product_id_to_production_names)
+ purchase_request_line = purchase_request_line + self.env['purchase.order'].get_purchase_request(workorders, production)
# 工单排序
def _reset_work_order_sequence1(self, k):
diff --git a/sf_manufacturing/models/mrp_routing_workcenter.py b/sf_manufacturing/models/mrp_routing_workcenter.py
index 666be375..a8b84231 100644
--- a/sf_manufacturing/models/mrp_routing_workcenter.py
+++ b/sf_manufacturing/models/mrp_routing_workcenter.py
@@ -1,6 +1,7 @@
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
+from odoo.tools import str2bool
class ResMrpRoutingWorkcenter(models.Model):
@@ -24,10 +25,29 @@ class ResMrpRoutingWorkcenter(models.Model):
workcenter_ids = fields.Many2many('mrp.workcenter', 'rel_workcenter_route', required=True)
bom_id = fields.Many2one('mrp.bom', required=False)
surface_technics_id = fields.Many2one('sf.production.process', string="表面工艺")
+ optional_process_parameters = fields.One2many('sf.production.process.parameter','routing_id',string='可选工艺参数')
reserved_duration = fields.Float('预留时长', default=30, tracking=True)
is_outsource = fields.Boolean('外协', default=False)
individuation_page_ids = fields.Many2many('sf.work.individuation.page', string='个性化记录')
+ @api.onchange('surface_technics_id')
+ def optional_process_parameters_date(self):
+ for record in self:
+ if not record.surface_technics_id:
+ continue
+ parameter_ids = self.env['sf.production.process.parameter'].search([
+ ('process_id', '=', record.surface_technics_id.id),
+ ])
+ record.optional_process_parameters = parameter_ids.ids
+
+ def init(self):
+ super(ResMrpRoutingWorkcenter, self).init()
+ # 在模块初始化时触发计算字段的更新
+ records = self.search([])
+ if str2bool(self.env['ir.config_parameter'].get_param('sf.production.process.parameter.is_init_workcenter',default='False')):
+ return
+ records.optional_process_parameters_date()
+ self.env['ir.config_parameter'].set_param('sf.production.process.parameter.is_init_workcenter', True)
def get_no(self):
international_standards = self.search(
[('code', '!=', ''), ('active', 'in', [True, False])],
diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py
index 27fdb37c..9434651c 100644
--- a/sf_manufacturing/models/mrp_workcenter.py
+++ b/sf_manufacturing/models/mrp_workcenter.py
@@ -21,7 +21,16 @@ class ResWorkcenter(models.Model):
related='equipment_id.production_line_id', store=True)
is_process_outsourcing = fields.Boolean('工艺外协')
users_ids = fields.Many2many("res.users", 'users_workcenter', tracking=True)
-
+ @api.constrains('name')
+ def _check_unique_name_code(self):
+ for record in self:
+ # 检查是否已经存在相同的 name 和 code 组合
+ existing = self.search([
+ ('name', '=', record.name),
+ ('id', '!=', record.id) # 排除当前记录
+ ])
+ if existing:
+ raise ValueError('记录已存在')
def write(self, vals):
if 'users_ids' in vals:
old_users = self.users_ids
diff --git a/sf_manufacturing/models/product_template.py b/sf_manufacturing/models/product_template.py
index 3554967c..1af6226a 100644
--- a/sf_manufacturing/models/product_template.py
+++ b/sf_manufacturing/models/product_template.py
@@ -51,7 +51,7 @@ class ResProductMo(models.Model):
# domain="[('materials_id', '=', materials_id)]")
# cutting_tool_model_id.material_model_id
server_product_process_parameters_id = fields.Many2one('sf.production.process.parameter',
- string='表面工艺参数(服务产品)')
+ string='工艺参数(服务产品)')
model_process_parameters_ids = fields.Many2many('sf.production.process.parameter', 'process_parameter_rel',
string='表面工艺参数')
diff --git a/sf_manufacturing/models/sf_production_common.py b/sf_manufacturing/models/sf_production_common.py
index 113858c1..4cebbd27 100644
--- a/sf_manufacturing/models/sf_production_common.py
+++ b/sf_manufacturing/models/sf_production_common.py
@@ -2,11 +2,44 @@
import logging
from odoo import fields, models, api
from odoo.exceptions import UserError
+from odoo.tools import str2bool
class SfProductionProcessParameter(models.Model):
_inherit = 'sf.production.process.parameter'
+ outsourced_service_products = fields.One2many(
+ 'product.template', # 另一个模型的名称
+ 'server_product_process_parameters_id', # 对应的 Many2one 字段名称
+ string='外协服务产品'
+ )
+ is_product_button = fields.Boolean(compute='_compute_is_product_button',default=False)
+ is_delete_button = fields.Boolean(compute='_compute_is_delete_button', default=False)
+ routing_id = fields.Many2one('mrp.routing.workcenter', string="工序")
+ @api.depends('outsourced_service_products')
+ def _compute_is_product_button(self):
+ for record in self:
+ if record.outsourced_service_products:
+ record.is_product_button = True
+ else:
+ record.is_product_button = False
+
+ def has_wksp_prefix(self,code):
+ """
+ 判断字符串是否以WKSP开头(不区分大小写)
+ :param text: 要检查的字符串
+ :return: True/False
+ """
+ return code.upper().startswith('WKSP')
+ @api.depends('outsourced_service_products','code')
+ def _compute_is_delete_button(self):
+ for record in self:
+ if record.outsourced_service_products and self.has_wksp_prefix(record.code):
+ record.is_delete_button = False
+ elif record.outsourced_service_products:
+ record.is_delete_button = True
+ else:
+ record.is_delete_button = True
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None):
if self._context.get('route_id'):
@@ -21,3 +54,34 @@ class SfProductionProcessParameter(models.Model):
domain = [('process_id', '=', routing.surface_technics_id.id), ('id', 'not in', parameter)]
return self._search(domain, limit=limit, access_rights_uid=name_get_uid)
return super()._name_search(name, args, operator, limit, name_get_uid)
+
+ def action_create_service_product(self):
+ # 获取产品分类(服务)
+ service_categ = self.env.ref('sf_manufacturing.product_category_outsource_other_process').sudo()
+ default_values = {
+ 'detailed_type': 'service',
+ 'name': f"{self.process_id.name}{self.name}",
+ 'invoice_policy': 'delivery',
+ 'categ_id': service_categ.id,
+ 'description': f"基于{self.name}创建的服务产品",
+ 'sale_ok': True, # 可销售
+ 'purchase_ok': True, # 可采购
+ 'server_product_process_parameters_id': self.id,
+ }
+ # 创建服务产品
+ # 返回打开新创建产品的表单视图
+ return {
+ 'name': '创建服务产品',
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'product.product',
+ 'view_mode': 'form',
+ 'view_id': self.env.ref('product.product_normal_form_view').id,
+ 'target': 'new', # 关键参数,使窗口以弹窗形式打开
+ 'context': {
+ 'default_' + k: v for k, v in default_values.items()
+ },
+ }
+
+ def action_hide_service_products(self):
+ self.outsourced_service_products.active = False
+ self.active = False
diff --git a/sf_manufacturing/views/mrp_routing_workcenter_view.xml b/sf_manufacturing/views/mrp_routing_workcenter_view.xml
index 67bc0c97..402c9ccb 100644
--- a/sf_manufacturing/views/mrp_routing_workcenter_view.xml
+++ b/sf_manufacturing/views/mrp_routing_workcenter_view.xml
@@ -22,6 +22,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sf_mrs_connect/models/sync_common.py b/sf_mrs_connect/models/sync_common.py
index 652ec3eb..b2b65e29 100644
--- a/sf_mrs_connect/models/sync_common.py
+++ b/sf_mrs_connect/models/sync_common.py
@@ -1135,8 +1135,10 @@ class sfProductionProcessParameter(models.Model):
[("code", '=', item['code']), ('active', 'in', [True, False])])
process = self.env['sf.production.process'].search(
[('code', '=', item['process_id_code'])], limit=1)
+ production_process_parameter = self.search(
+ [("code", '=', item['code']), ('active', 'in', [True, False])])
if not production_process_parameter:
- self.create({
+ production_process_parameter = self.create({
"name": item['name'],
"process_description": item['process_description'],
"processing_day": item['processing_day'],
@@ -1148,6 +1150,7 @@ class sfProductionProcessParameter(models.Model):
[('materials_no', 'in', item['materials_model_ids_codes'])]),
'processing_mm': item['processing_mm']
})
+ production_process_parameter.create_service_product()
else:
production_process_parameter.name = item['name']
production_process_parameter.process_description = item['process_description']
@@ -1158,6 +1161,9 @@ class sfProductionProcessParameter(models.Model):
[('materials_no', 'in', item['materials_model_ids_codes'])])
production_process_parameter.active = item['active']
production_process_parameter.processing_mm = item['processing_mm']
+ if not production_process_parameter.outsourced_service_products:
+ production_process_parameter.create_service_product()
+ production_process_parameter.create_work_center()
else:
raise ValidationError("表面工艺可选参数认证未通过")
diff --git a/sf_sale/models/sale_order.py b/sf_sale/models/sale_order.py
index 1c8480dd..5a37cc12 100644
--- a/sf_sale/models/sale_order.py
+++ b/sf_sale/models/sale_order.py
@@ -376,6 +376,21 @@ class RePurchaseOrder(models.Model):
if not line.taxes_id:
raise UserError('请对【产品】中的【税】进行选择')
+ def get_purchase_request(self, consecutive_process_parameters, production):
+ result = []
+ for pp in consecutive_process_parameters:
+ result.append({
+ "product_id": pp.product_id.id,
+ "name": production.name,
+ "date_required": fields.Datetime.now(),
+ "product_uom_id":pp.product_id.uom_po_id.id,
+ "product_qty": 1,
+ "request_id": False,
+ "move_dest_ids": False,
+ "orderpoint_id": False,
+ })
+ return result
+
def get_purchase_order(self, consecutive_process_parameters, production, product_id_to_production_names):
for pp in consecutive_process_parameters:
server_product_process = []