From 61a3cf606e6d1d9db41dfb5236ae1c0b3b92517b Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Mon, 14 Apr 2025 15:23:47 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A8=E9=9D=A2=E5=B7=A5=E8=89=BA=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=86=85=E7=BD=AE=E4=B8=8E=E6=95=B0=E6=8D=AE=E5=90=8C?= =?UTF-8?q?=E6=AD=A5=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_dlm/data/product_data.xml | 12 +-- sf_dlm_management/__init__.py | 1 + sf_dlm_management/__manifest__.py | 3 +- sf_dlm_management/data/sequence.xml | 10 +++ sf_dlm_management/models/__init__.py | 1 + .../models/sf_production_common.py | 75 +++++++++++++++++++ sf_manufacturing/__manifest__.py | 4 +- sf_manufacturing/data/product_data.xml | 23 ++++++ sf_manufacturing/models/mrp_production.py | 2 + .../models/mrp_routing_workcenter.py | 20 +++++ sf_manufacturing/models/mrp_workcenter.py | 11 ++- sf_manufacturing/models/product_template.py | 2 +- .../models/sf_production_common.py | 64 ++++++++++++++++ .../views/mrp_routing_workcenter_view.xml | 20 +++++ sf_mrs_connect/models/sync_common.py | 8 +- sf_sale/models/sale_order.py | 15 ++++ 16 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 sf_dlm_management/data/sequence.xml create mode 100644 sf_dlm_management/models/sf_production_common.py create mode 100644 sf_manufacturing/data/product_data.xml 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 @@ + + + + + + + + + + +