From 026697f363bbd5892b4eb830f3627bba1bf3a052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Fri, 11 Apr 2025 16:12:58 +0800 Subject: [PATCH 01/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9sf=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E4=B8=8D=E5=9C=A8=E9=83=A8=E7=BD=B2=E6=97=B6?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/jikimo_workorder_exception.py | 4 ++-- sf_mrs_connect/data/ir_cron_data.xml | 23 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py b/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py index 0ab0d6ae..f4a49c1f 100644 --- a/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py +++ b/jikimo_workorder_exception_notify/models/jikimo_workorder_exception.py @@ -52,10 +52,10 @@ class JikimoWorkorderException(models.Model): def _get_message(self, message_queue_ids): contents, _ = super(JikimoWorkorderException, self)._get_message(message_queue_ids) - url = self.env['ir.config_parameter'].get_param('web.base.url') + base_url = self.env['ir.config_parameter'].get_param('web.base.url') action_id = self.env.ref('mrp.mrp_production_action').id for index, content in enumerate(contents): exception_id = self.env['jikimo.workorder.exception'].browse(message_queue_ids[index].res_id) - url = url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id) + url = base_url + '/web#id=%s&view_type=form&action=%s' % (exception_id.workorder_id.production_id.id, action_id) contents[index] = content.replace('{{url}}', url) return contents, message_queue_ids diff --git a/sf_mrs_connect/data/ir_cron_data.xml b/sf_mrs_connect/data/ir_cron_data.xml index 7cc18c8e..a2d43891 100644 --- a/sf_mrs_connect/data/ir_cron_data.xml +++ b/sf_mrs_connect/data/ir_cron_data.xml @@ -1,16 +1,16 @@ - - - 制造-配置:每日定时同步cloud的静态资源库 - - code - model.sf_all_sync() - 1 - days - -1 - - + + + 制造-配置:每日定时同步cloud的静态资源库 + + code + model.sf_all_sync() + 1 + days + -1 + + @@ -220,4 +220,5 @@ + \ No newline at end of file From a8961086389c4b433bac551d9896cf6d4811bca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Fri, 11 Apr 2025 16:49:18 +0800 Subject: [PATCH 02/20] =?UTF-8?q?=E4=BA=A7=E5=93=81=E4=B8=BB=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E3=80=90=E6=9D=A1=E7=A0=81=E3=80=91=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E6=A8=A1=E5=9E=8BID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/__manifest__.py | 1 + sf_manufacturing/views/product_template_views.xml | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 sf_manufacturing/views/product_template_views.xml diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index ba0c6751..a19f5542 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -44,6 +44,7 @@ 'views/sale_order_views.xml', 'views/mrp_workorder_batch_replan.xml', 'views/purchase_order_view.xml', + 'views/product_template_views.xml', ], 'assets': { diff --git a/sf_manufacturing/views/product_template_views.xml b/sf_manufacturing/views/product_template_views.xml new file mode 100644 index 00000000..a99a332a --- /dev/null +++ b/sf_manufacturing/views/product_template_views.xml @@ -0,0 +1,15 @@ + + + + + product.template.product.form.inherit.sf_manufacture + product.template + + + + + + + + + \ No newline at end of file From 61a3cf606e6d1d9db41dfb5236ae1c0b3b92517b Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Mon, 14 Apr 2025 15:23:47 +0800 Subject: [PATCH 03/20] =?UTF-8?q?=E8=A1=A8=E9=9D=A2=E5=B7=A5=E8=89=BA?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=86=85=E7=BD=AE=E4=B8=8E=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=90=8C=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 @@ + + + + + + + + + + +