From 95c25ac7b8e6d162188a5a4aaab7f143e54415b5 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Tue, 29 Apr 2025 13:39:36 +0800 Subject: [PATCH 01/13] =?UTF-8?q?=E5=B7=A5=E8=89=BA=E5=A4=96=E5=8D=8F?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/mrp_production.py | 6 +- .../models/mrp_routing_workcenter.py | 32 ++-- .../models/sf_production_common.py | 170 +++++++++--------- sf_manufacturing/models/mrp_production.py | 84 ++++----- .../models/mrp_routing_workcenter.py | 22 +-- sf_manufacturing/models/mrp_workcenter.py | 20 +-- sf_manufacturing/models/mrp_workorder.py | 85 +++++---- sf_manufacturing/models/purchase_order.py | 112 ++++++++---- .../models/purchase_request_line.py | 60 +++---- .../models/sf_production_common.py | 168 ++++++++--------- .../views/mrp_production_addional_change.xml | 2 +- .../views/mrp_routing_workcenter_view.xml | 40 ++--- sf_manufacturing/views/mrp_workorder_view.xml | 22 +-- .../production_technology_re_adjust_wizard.py | 16 +- sf_mrs_connect/models/sync_common.py | 8 +- sf_sale/models/sale_order.py | 50 +++--- 16 files changed, 472 insertions(+), 425 deletions(-) diff --git a/jikimo_purchase_request/models/mrp_production.py b/jikimo_purchase_request/models/mrp_production.py index dd9d4aa9..ff352ff6 100644 --- a/jikimo_purchase_request/models/mrp_production.py +++ b/jikimo_purchase_request/models/mrp_production.py @@ -9,8 +9,7 @@ class MrpProduction(models.Model): @api.depends('state') def _compute_pr_mp_count(self): for item in self: - pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name)]) - # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')]) + pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')]) if pr_ids: item.pr_mp_count = len(pr_ids) else: @@ -21,8 +20,7 @@ class MrpProduction(models.Model): 采购请求 """ self.ensure_one() - # pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)]) - pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name)]) + pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', self.name),('is_subcontract', '!=', True)]) action = { 'res_model': 'purchase.request', 'type': 'ir.actions.act_window', diff --git a/sf_dlm_management/models/mrp_routing_workcenter.py b/sf_dlm_management/models/mrp_routing_workcenter.py index dd9e59e3..4a04eb87 100644 --- a/sf_dlm_management/models/mrp_routing_workcenter.py +++ b/sf_dlm_management/models/mrp_routing_workcenter.py @@ -1,16 +1,16 @@ -# import logging -# from odoo import fields, models, api -# from odoo.exceptions import UserError -# from odoo.tools import str2bool -# -# -# class ResMrpRoutingWorkcenter(models.Model): -# _inherit = 'mrp.routing.workcenter' -# 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) \ No newline at end of file +import logging +from odoo import fields, models, api +from odoo.exceptions import UserError +from odoo.tools import str2bool + + +class ResMrpRoutingWorkcenter(models.Model): + _inherit = 'mrp.routing.workcenter' + 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) \ 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 index 42cb9ff2..637aeb26 100644 --- a/sf_dlm_management/models/sf_production_common.py +++ b/sf_dlm_management/models/sf_production_common.py @@ -1,85 +1,85 @@ -# # -*- 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'] = '101'+self.routing_id.code +self.env['ir.sequence'].next_by_code('sf.production.process.parameter') -# if 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 and not vals.get('process_id'): -# vals['process_id'] = routing_id.surface_technics_id.id -# if vals.get('code', '/') == '/' or vals.get('code', '/') is False: -# vals['code'] = '101' + routing_id.code + self.env['ir.sequence'].next_by_code( -# 'sf.production.process.parameter') -# 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: -# res_partner = self.env['res.partner'].search([('name','=','湖南傲派自动化设备有限公司')]) -# 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, -# 'seller_ids': [(0, 0, { -# # 'delay': 1, -# 'partner_id': res_partner.id, -# 'price': 1, })], -# }) -# -# 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) +# -*- 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'] = '101'+self.routing_id.code +self.env['ir.sequence'].next_by_code('sf.production.process.parameter') + if 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 and not vals.get('process_id'): + vals['process_id'] = routing_id.surface_technics_id.id + if vals.get('code', '/') == '/' or vals.get('code', '/') is False: + vals['code'] = '101' + routing_id.code + self.env['ir.sequence'].next_by_code( + 'sf.production.process.parameter') + 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: + res_partner = self.env['res.partner'].search([('name','=','湖南傲派自动化设备有限公司')]) + 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, + 'seller_ids': [(0, 0, { + # 'delay': 1, + 'partner_id': res_partner.id, + 'price': 1, })], + }) + + 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/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 25a016e3..df73fb72 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -900,40 +900,40 @@ class MrpProduction(models.Model): for workorder in production.workorder_ids: workorder.duration_expected = workorder._get_duration_expected() - # def _create_subcontract_purchase_request(self, purchase_request_line): - # sorted_list = sorted(purchase_request_line, key=itemgetter('name')) - # grouped_purchase_request_line = { - # k: list(g) - # for k, g in groupby(sorted_list, key=itemgetter('name')) - # } - # for name, request_line in grouped_purchase_request_line.items(): - # request_line_sorted_list = sorted(request_line, key=itemgetter('product_id')) - # grouped_purchase_request_line_sorted_list = { - # k: list(g) - # for k, g in groupby(request_line_sorted_list, key=itemgetter('product_id')) - # } - # purchase_request_model = self.env["purchase.request"] - # origin = ", ".join({item['production_name'] for item in request_line_sorted_list if item.get('production_name')}) - # pr = purchase_request_model.create({ - # "origin": origin, - # "company_id": self.company_id.id, - # "picking_type_id": self.env.ref('stock.picking_type_in').id, - # "group_id": request_line[0].get('group_id'), - # "requested_by": self.env.context.get("uid", self.env.uid), - # "assigned_to": False, - # "bom_id": self[0].bom_id.id, - # "is_subcontract":True, - # }) - # self[0].bom_id.bom_line_ids.product_id.route_ids = [(4,self.env.ref( - # 'sf_stock.stock_route_process_outsourcing').id)] - # for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items(): - # cur_request_line = request_line_list[0] - # cur_request_line['product_qty'] = len(request_line_list) - # cur_request_line['request_id'] = pr.id - # cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')}) - # cur_request_line.pop('group_id', None) - # cur_request_line.pop('production_name', None) - # self.env["purchase.request.line"].create(cur_request_line) + def _create_subcontract_purchase_request(self, purchase_request_line): + sorted_list = sorted(purchase_request_line, key=itemgetter('name')) + grouped_purchase_request_line = { + k: list(g) + for k, g in groupby(sorted_list, key=itemgetter('name')) + } + for name, request_line in grouped_purchase_request_line.items(): + request_line_sorted_list = sorted(request_line, key=itemgetter('product_id')) + grouped_purchase_request_line_sorted_list = { + k: list(g) + for k, g in groupby(request_line_sorted_list, key=itemgetter('product_id')) + } + purchase_request_model = self.env["purchase.request"] + origin = ", ".join({item['production_name'] for item in request_line_sorted_list if item.get('production_name')}) + pr = purchase_request_model.create({ + "origin": origin, + "company_id": self.company_id.id, + "picking_type_id": self.env.ref('stock.picking_type_in').id, + "group_id": request_line[0].get('group_id'), + "requested_by": self.env.context.get("uid", self.env.uid), + "assigned_to": False, + "bom_id": self[0].bom_id.id, + "is_subcontract":True, + }) + self[0].bom_id.bom_line_ids.product_id.route_ids = [(4,self.env.ref( + 'sf_stock.stock_route_process_outsourcing').id)] + for product_id, request_line_list in grouped_purchase_request_line_sorted_list.items(): + cur_request_line = request_line_list[0] + cur_request_line['product_qty'] = len(request_line_list) + cur_request_line['request_id'] = pr.id + cur_request_line['origin'] = ", ".join({item['production_name'] for item in request_line_list if item.get('production_name')}) + cur_request_line.pop('group_id', None) + cur_request_line.pop('production_name', None) + self.env["purchase.request.line"].create(cur_request_line) # 外协出入库单处理 def get_subcontract_pick_purchase(self): @@ -961,14 +961,14 @@ class MrpProduction(models.Model): if not sorted_workorders: return 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) - # all_workorders += workorders - # self._create_subcontract_purchase_request(purchase_request_line) - # for workorder in all_workorders: - # workorder._compute_pr_mp_count() + # 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) + all_workorders += workorders + self._create_subcontract_purchase_request(purchase_request_line) + for workorder in all_workorders: + workorder._compute_pr_mp_count() # 工单排序 def _reset_work_order_sequence1(self, k): for rec in self: diff --git a/sf_manufacturing/models/mrp_routing_workcenter.py b/sf_manufacturing/models/mrp_routing_workcenter.py index a1387595..6a2fb6f6 100644 --- a/sf_manufacturing/models/mrp_routing_workcenter.py +++ b/sf_manufacturing/models/mrp_routing_workcenter.py @@ -1,7 +1,7 @@ import logging from odoo import fields, models, api from odoo.exceptions import UserError -# from odoo.tools import str2bool +from odoo.tools import str2bool class ResMrpRoutingWorkcenter(models.Model): @@ -25,20 +25,20 @@ 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='可选工艺参数') + 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 + @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 # @api.model # def _auto_init(self): diff --git a/sf_manufacturing/models/mrp_workcenter.py b/sf_manufacturing/models/mrp_workcenter.py index 39c500dc..e18472d7 100644 --- a/sf_manufacturing/models/mrp_workcenter.py +++ b/sf_manufacturing/models/mrp_workcenter.py @@ -21,16 +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('记录已存在') + @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/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 070ff404..3a9744d9 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -70,21 +70,21 @@ class ResMrpWorkOrder(models.Model): delivery_warning = fields.Selection([('normal', '正常'), ('warning', '告警'), ('overdue', '逾期')], string='时效', tracking=True) back_button_display = fields.Boolean(default=False, compute='_compute_back_button_display', store=True) - # pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True) - # - # @api.depends('state') - # def _compute_pr_mp_count(self): - # for item in self: - # if not item.is_subcontract: - # item.pr_mp_count = 0 - # continue - # pr_ids = self.env['purchase.request'].sudo().search( - # [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'), - # ('state', '!=', 'rejected')]) - # if pr_ids: - # item.pr_mp_count = len(pr_ids) - # else: - # item.pr_mp_count = 0 + pr_mp_count = fields.Integer('采购申请单数量', compute='_compute_pr_mp_count', store=True) + + @api.depends('state') + def _compute_pr_mp_count(self): + for item in self: + if not item.is_subcontract: + item.pr_mp_count = 0 + continue + pr_ids = self.env['purchase.request'].sudo().search( + [('origin', 'like', item.production_id.name), ('is_subcontract', '=', 'True'), + ('state', '!=', 'rejected')]) + if pr_ids: + item.pr_mp_count = len(pr_ids) + else: + item.pr_mp_count = 0 @api.depends('state') def _compute_back_button_display(self): for record in self: @@ -443,12 +443,11 @@ class ResMrpWorkOrder(models.Model): def _compute_surface_technics_purchase_ids(self): for order in self: if order.routing_type == '表面工艺' and order.state not in ['cancel']: + # domain = [('group_id', '=', self.production_id.procurement_group_id.id), + # ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')] domain = [('purchase_type', '=', 'consignment'), ('origin', 'like', '%' + self.production_id.name + '%'), ('state', '!=', 'cancel')] - # domain = [('purchase_type', '=', 'consignment'), - # ('origin', 'like', '%' + self.production_id.name + '%'), - # ('state', '!=', 'cancel')] purchase = self.env['purchase.order'].search(domain) order.surface_technics_purchase_count = 0 if not purchase: @@ -461,30 +460,30 @@ class ResMrpWorkOrder(models.Model): else: order.surface_technics_purchase_count = 0 - # def action_view_pr_mrp_workorder(self): - # """ - # 采购请求 - # """ - # self.ensure_one() - # pr_ids = self.env['purchase.request'].sudo().search( - # [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'), - # ('state', '!=', 'rejected')]) - # action = { - # 'res_model': 'purchase.request', - # 'type': 'ir.actions.act_window', - # } - # if len(pr_ids) == 1: - # action.update({ - # 'view_mode': 'form', - # 'res_id': pr_ids[0].id, - # }) - # else: - # action.update({ - # 'name': _("从 %s生成采购请求单", self.name), - # 'domain': [('id', 'in', pr_ids)], - # 'view_mode': 'tree,form', - # }) - # return action + def action_view_pr_mrp_workorder(self): + """ + 采购请求 + """ + self.ensure_one() + pr_ids = self.env['purchase.request'].sudo().search( + [('origin', 'like', self.production_id.name), ('is_subcontract', '=', 'True'), + ('state', '!=', 'rejected')]) + action = { + 'res_model': 'purchase.request', + 'type': 'ir.actions.act_window', + } + if len(pr_ids) == 1: + action.update({ + 'view_mode': 'form', + 'res_id': pr_ids[0].id, + }) + else: + action.update({ + 'name': _("从 %s生成采购请求单", self.name), + 'domain': [('id', 'in', pr_ids)], + 'view_mode': 'tree,form', + }) + return action def action_view_surface_technics_purchase(self): self.ensure_one() # if self.routing_type == '表面工艺': @@ -513,7 +512,7 @@ class ResMrpWorkOrder(models.Model): return result def _get_surface_technics_purchase_ids(self): - domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'), ('state', '!=', 'cancel')] + domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment'), ('is_subcontract', '=', 'True'), ('state', '!=', 'cancel')] # domain = [('origin', 'like', '%' + self.production_id.name + '%'), ('purchase_type', '=', 'consignment')] # domain = [('group_id', '=', self.production_id.procurement_group_id.id), ('purchase_type', '=', 'consignment')] purchase_orders = self.env['purchase.order'].search(domain, order='id desc') diff --git a/sf_manufacturing/models/purchase_order.py b/sf_manufacturing/models/purchase_order.py index 9b682d83..9e11e7c0 100644 --- a/sf_manufacturing/models/purchase_order.py +++ b/sf_manufacturing/models/purchase_order.py @@ -59,6 +59,86 @@ class PurchaseOrder(models.Model): production_id = self.env['mrp.production'].search([('origin', 'in', origins)]) purchase.production_count = len(production_id) + def process_replenish(self,production,total_qty): + record = self + bom_line_id = production.bom_id.bom_line_ids + replenish = self.env['stock.warehouse.orderpoint'].search([ + ('product_id', '=', bom_line_id.product_id.id), + ( + 'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id), + # ('state', 'in', ['draft', 'confirmed']) + ], limit=1) + if not replenish: + replenish_model = self.env['stock.warehouse.orderpoint'] + replenish = replenish_model.create({ + 'product_id': bom_line_id.product_id.id, + 'location_id': self.env.ref( + 'sf_stock.stock_location_outsourcing_material_receiving_area').id, + 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id, + 'group_id': record.group_id.id, + 'qty_to_order': total_qty, + 'origin': record.name, + }) + else: + replenish.write({ + 'product_id': bom_line_id.product_id.id, + 'location_id': self.env.ref( + 'sf_stock.stock_location_outsourcing_material_receiving_area').id, + 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id, + 'group_id': record.group_id.id, + 'qty_to_order': total_qty + replenish.qty_to_order, + 'origin': record.name + ',' + replenish.origin, + }) + replenish.action_replenish() + + def outsourcing_service_replenishment(self): + record = self + if record.purchase_type != 'consignment': + return + grouped_lines = {} + for line in record.order_line: + if line.related_product.id not in grouped_lines: + grouped_lines[line.related_product.id] = [] + grouped_lines[line.related_product.id].append(line) + for product_id,lines in grouped_lines.items(): + production = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1) + if not production: + continue + total_qty = sum(line.product_qty for line in lines) + record.process_replenish(production,total_qty) + for product_id,lines in grouped_lines.items(): + productions = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1) + if not productions: + continue + # production.bom_id.bom_line_ids.product_id + location_id = self.env['stock.location'].search([('name', '=', '制造前')]) + quants = self.env['stock.quant'].search([ + ('product_id', '=', productions.bom_id.bom_line_ids.product_id.id), + ('location_id', '=', location_id.id) + ]) + total_qty = sum(quants.mapped('quantity')) # 计算该位置的总库存量 + is_available = total_qty > 0 + if not is_available: + continue + for production_id in productions: + work_ids = production_id.workorder_ids.filtered( + lambda wk: wk.state not in ['done', 'rework', 'cancel']) + if not work_ids: + continue + min_sequence_wk = min(work_ids, key=lambda wk: wk.sequence) + if min_sequence_wk.is_subcontract: + picking_id = production_id.picking_ids.filtered( + lambda wk: wk.location_id.name == '制造前' and wk.location_dest_id.name == '外协加工区') + move_out = picking_id.move_ids + for mo in move_out: + if mo.state != 'done': + mo.write({'state': 'assigned', 'production_id': False}) + if not mo.move_line_ids: + self.env['stock.move.line'].create( + mo.get_move_line(production_id, min_sequence_wk)) + # product = self.env['mrp.production'].search([('product_id', '=', product_id)], limit=1) + # match = re.search(r'(S\d{5}-\d)',product.name) + # pass def button_confirm(self): for record in self: for line in record.order_line: @@ -66,37 +146,7 @@ class PurchaseOrder(models.Model): raise UserError('请对【产品】中的【数量】进行输入') if line.price_unit <= 0: raise UserError('请对【产品】中的【单价】进行输入') - # if record.purchase_type == 'consignment': - # bom_line_id = record.order_line[0].purchase_request_lines.request_id.bom_id.bom_line_ids - # replenish = self.env['stock.warehouse.orderpoint'].search([ - # ('product_id', '=', bom_line_id.product_id.id), - # ( - # 'location_id', '=', self.env.ref('sf_stock.stock_location_outsourcing_material_receiving_area').id), - # # ('state', 'in', ['draft', 'confirmed']) - # ], limit=1) - # if not replenish: - # replenish_model = self.env['stock.warehouse.orderpoint'] - # replenish = replenish_model.create({ - # 'product_id': bom_line_id.product_id.id, - # 'location_id': self.env.ref( - # 'sf_stock.stock_location_outsourcing_material_receiving_area').id, - # 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id, - # 'group_id': record.group_id.id, - # 'qty_to_order': 1, - # 'origin': record.name, - # }) - # else: - # replenish.write({ - # 'product_id': bom_line_id.product_id.id, - # 'location_id': self.env.ref( - # 'sf_stock.stock_location_outsourcing_material_receiving_area').id, - # 'route_id': self.env.ref('sf_stock.stock_route_process_outsourcing').id, - # 'group_id': record.group_id.id, - # 'qty_to_order': 1 + replenish.qty_to_order, - # 'origin': record.name + ',' + replenish.origin, - # }) - # replenish.action_replenish() - + record.outsourcing_service_replenishment() return super(PurchaseOrder, self).button_confirm() diff --git a/sf_manufacturing/models/purchase_request_line.py b/sf_manufacturing/models/purchase_request_line.py index eca52d3b..b08bb3e7 100644 --- a/sf_manufacturing/models/purchase_request_line.py +++ b/sf_manufacturing/models/purchase_request_line.py @@ -1,30 +1,30 @@ -# # -*- coding: utf-8 -*- -# import base64 -# import datetime -# import logging -# import json -# import os -# import re -# import traceback -# from operator import itemgetter -# -# import requests -# from itertools import groupby -# from collections import defaultdict, namedtuple -# -# from odoo import api, fields, models, SUPERUSER_ID, _ -# from odoo.exceptions import UserError, ValidationError -# from odoo.tools import float_compare, float_round, float_is_zero, format_datetime -# -# -# class PurchaseRequestLine(models.Model): -# _inherit = 'purchase.request' -# is_subcontract = fields.Boolean(string='是否外协',default=False) -# class PurchaseRequestLine(models.Model): -# _inherit = 'purchase.request.line' -# is_subcontract = fields.Boolean(string='是否外协') -# -# -# class PurchaseRequest(models.Model): -# _inherit = 'purchase.request' -# bom_id = fields.Many2one('mrp.bom') +# -*- coding: utf-8 -*- +import base64 +import datetime +import logging +import json +import os +import re +import traceback +from operator import itemgetter + +import requests +from itertools import groupby +from collections import defaultdict, namedtuple + +from odoo import api, fields, models, SUPERUSER_ID, _ +from odoo.exceptions import UserError, ValidationError +from odoo.tools import float_compare, float_round, float_is_zero, format_datetime + + +class PurchaseRequestLine(models.Model): + _inherit = 'purchase.request' + is_subcontract = fields.Boolean(string='是否外协',default=False) +class PurchaseRequestLine(models.Model): + _inherit = 'purchase.request.line' + is_subcontract = fields.Boolean(string='是否外协') + + +class PurchaseRequest(models.Model): + _inherit = 'purchase.request' + bom_id = fields.Many2one('mrp.bom') diff --git a/sf_manufacturing/models/sf_production_common.py b/sf_manufacturing/models/sf_production_common.py index 2cdfd750..1c066bbe 100644 --- a/sf_manufacturing/models/sf_production_common.py +++ b/sf_manufacturing/models/sf_production_common.py @@ -7,74 +7,74 @@ from odoo.exceptions import UserError, ValidationError class SfProductionProcessParameter(models.Model): _inherit = 'sf.production.process.parameter' - # service_products = fields.Many2one( - # 'product.template', - # string='外协服务产品',compute='_compute_service_products',inverse='_inverse_service_products', - # store=True - # ) - # 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_service_products(self): - # for record in self: - # # 假设取第一条作为主明细 - # record.service_products = record.outsourced_service_products.id if record.outsourced_service_products else False - # - # def _inverse_service_products(self): - # for record in self: - # if record.service_products: - # # 确保关联关系正确 - # record.outsourced_service_products = record.service_products.ids if record.service_products else False - # else: - # record.outsourced_service_products = False - # def name_get(self): - # result = [] - # for record in self: - # name = f"{record.process_id.name} - {record.name}" # 自定义显示格式 - # result.append((record.id, name)) - # return result - # @api.constrains('outsourced_service_products') - # def _validate_partner_limit(self): - # for record in self: - # if len(record.outsourced_service_products) > 1: - # raise ValidationError("工艺参数不能与多个产品关联") - # - # @api.onchange('outsourced_service_products') - # def _onchange_validate_partner_limit(self): - # for record in self: - # if len(record.outsourced_service_products) > 1: - # raise ValidationError("工艺参数不能与多个产品关联") - # @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): - # """ - # 判断字符串是否以WKSP开头(不区分大小写) - # :param text: 要检查的字符串 - # :return: True/False - # """ - # return self.code.upper().startswith('101'+self.routing_id.code) - # @api.depends('outsourced_service_products','code') - # def _compute_is_delete_button(self): - # for record in self: - # if record.outsourced_service_products and record.has_wksp_prefix(): - # record.is_delete_button = False - # elif record.outsourced_service_products: - # record.is_delete_button = True - # else: - # record.is_delete_button = True + service_products = fields.Many2one( + 'product.template', + string='外协服务产品',compute='_compute_service_products',inverse='_inverse_service_products', + store=True + ) + 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_service_products(self): + for record in self: + # 假设取第一条作为主明细 + record.service_products = record.outsourced_service_products.id if record.outsourced_service_products else False + + def _inverse_service_products(self): + for record in self: + if record.service_products: + # 确保关联关系正确 + record.outsourced_service_products = record.service_products.ids if record.service_products else False + else: + record.outsourced_service_products = False + def name_get(self): + result = [] + for record in self: + name = f"{record.process_id.name} - {record.name}" # 自定义显示格式 + result.append((record.id, name)) + return result + @api.constrains('outsourced_service_products') + def _validate_partner_limit(self): + for record in self: + if len(record.outsourced_service_products) > 1: + raise ValidationError("工艺参数不能与多个产品关联") + + @api.onchange('outsourced_service_products') + def _onchange_validate_partner_limit(self): + for record in self: + if len(record.outsourced_service_products) > 1: + raise ValidationError("工艺参数不能与多个产品关联") + @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): + """ + 判断字符串是否以WKSP开头(不区分大小写) + :param text: 要检查的字符串 + :return: True/False + """ + return self.code.upper().startswith('101'+self.routing_id.code) + @api.depends('outsourced_service_products','code') + def _compute_is_delete_button(self): + for record in self: + if record.outsourced_service_products and record.has_wksp_prefix(): + 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'): @@ -90,19 +90,19 @@ class SfProductionProcessParameter(models.Model): 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): - # if self.id: # 如果是已存在的记录 - # self.write({}) # 空写入会触发保存 - # else: # 如果是新记录 - # self = self.create(self._convert_to_write(self.read()[0])) - # return { - # 'type': 'ir.actions.act_window', - # 'name': '向导名称', - # 'res_model': 'product.creation.wizard', - # 'view_mode': 'form', - # 'target': 'new', - # 'context': {'default_process_parameter_id': self.id}, # 传递当前记录ID - # } + def action_create_service_product(self): + if self.id: # 如果是已存在的记录 + self.write({}) # 空写入会触发保存 + else: # 如果是新记录 + self = self.create(self._convert_to_write(self.read()[0])) + return { + 'type': 'ir.actions.act_window', + 'name': '向导名称', + 'res_model': 'product.creation.wizard', + 'view_mode': 'form', + 'target': 'new', + 'context': {'default_process_parameter_id': self.id}, # 传递当前记录ID + } # # return { # 'name': '创建服务产品', @@ -116,6 +116,6 @@ class SfProductionProcessParameter(models.Model): # }, # } - # def action_hide_service_products(self): - # # self.outsourced_service_products.active = False - # self.active = False + def action_hide_service_products(self): + # self.outsourced_service_products.active = False + self.active = False diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index 89c92117..ef4f6f25 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -383,7 +383,7 @@ + options="{'no_create': True}" domain="[('routing_id', '=', 'route_id')]"/> - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + -->