From 2f26aee90af0f9113ea3b03f080cfc678890122f Mon Sep 17 00:00:00 2001 From: guanhuan Date: Mon, 23 Jun 2025 17:21:50 +0800 Subject: [PATCH 01/22] =?UTF-8?q?=E7=89=A9=E6=96=99=E9=9C=80=E6=B1=82?= =?UTF-8?q?=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_demand_plan/models/__init__.py | 1 + sf_demand_plan/models/sf_demand_plan.py | 123 ++++++++++++++++++ .../models/sf_production_demand_plan.py | 70 +--------- 3 files changed, 131 insertions(+), 63 deletions(-) create mode 100644 sf_demand_plan/models/sf_demand_plan.py diff --git a/sf_demand_plan/models/__init__.py b/sf_demand_plan/models/__init__.py index a0554c11..acf4ad1e 100644 --- a/sf_demand_plan/models/__init__.py +++ b/sf_demand_plan/models/__init__.py @@ -2,3 +2,4 @@ from . import sf_production_demand_plan from . import sale_order +from . import sf_demand_plan diff --git a/sf_demand_plan/models/sf_demand_plan.py b/sf_demand_plan/models/sf_demand_plan.py new file mode 100644 index 00000000..abf8cd41 --- /dev/null +++ b/sf_demand_plan/models/sf_demand_plan.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- + +from odoo import models, fields, api, _ + + +class SfDemandPlan(models.Model): + _name = 'sf.demand.plan' + _description = 'sf_demand_plan' + + def _get_machining_precision(self): + machinings = self.env['sf.machining.accuracy'].sudo().search([]) + list = [(m.sync_id, m.name) for m in machinings] + return list + + line_ids = fields.One2many('sf.production.demand.plan', 'demand_plan_id', string="需求计划", copy=True) + + sale_order_id = fields.Many2one(comodel_name="sale.order", + string="销售订单", readonly=True) + sale_order_line_id = fields.Many2one(comodel_name="sale.order.line", + string="销售订单明细", readonly=True) + + product_id = fields.Many2one( + comodel_name='product.product', + related='sale_order_line_id.product_id', + string='产品', store=True, index=True) + + part_name = fields.Char('零件名称', related='product_id.part_name') + part_number = fields.Char('零件图号', compute='_compute_part_number', store=True) + materials_id = fields.Char('材料', compute='_compute_materials_id', store=True) + + blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类', + related='product_id.blank_type') + embryo_long = fields.Char('坯料尺寸(mm)', compute='_compute_embryo_long', store=True) + is_incoming_material = fields.Boolean('客供料', related='sale_order_line_id.is_incoming_material', store=True) + # product_uom_qty = fields.Float( + # string="待计划", + # related='sale_order_line_id.product_uom_qty', store=True) + model_id = fields.Char('模型ID', related='product_id.model_id') + customer_name = fields.Char('客户', related='sale_order_id.customer_name') + product_uom_qty = fields.Float( + string="需求数量", + related='sale_order_line_id.product_uom_qty', store=True) + deadline_of_delivery = fields.Date('客户交期', related='sale_order_line_id.delivery_end_date', store=True) + contract_date = fields.Date('合同日期', related='sale_order_id.contract_date') + contract_code = fields.Char('合同号', related='sale_order_id.contract_code', store=True) + + model_process_parameters_ids = fields.Many2many('sf.production.process.parameter', + 'plan_process_parameter_rel', + string='表面工艺', + compute='_compute_model_process_parameters_ids' + , store=True + ) + model_machining_precision = fields.Selection(selection=_get_machining_precision, string='精度', + related='product_id.model_machining_precision') + + inventory_quantity_auto_apply = fields.Float( + string="成品库存", + compute='_compute_inventory_quantity_auto_apply', store=True + ) + + priority = fields.Selection([ + ('1', '紧急'), + ('2', '高'), + ('3', '中'), + ('4', '低'), + ], string='优先级', default='3') + + @api.depends('product_id.part_number', 'product_id.model_name') + def _compute_part_number(self): + for line in self: + if line.product_id: + if line.product_id.part_number: + line.part_number = line.product_id.part_number + else: + if line.product_id.model_name: + line.part_number = line.product_id.model_name.rsplit('.', 1)[0] + else: + line.part_number = None + + @api.depends('product_id.materials_id') + def _compute_materials_id(self): + for line in self: + if line.product_id: + line.materials_id = f"{line.product_id.materials_id.name}/{line.product_id.materials_type_id.name}" + else: + line.materials_id = None + + @api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height') + def _compute_embryo_long(self): + for line in self: + if line.product_id: + line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}" + else: + line.embryo_long = None + + @api.depends('product_id.model_process_parameters_ids') + def _compute_model_process_parameters_ids(self): + for line in self: + if line.product_id and line.product_id.model_process_parameters_ids: + line.model_process_parameters_ids = [(6, 0, line.product_id.model_process_parameters_ids.ids)] + else: + line.model_process_parameters_ids = [(5, 0, 0)] + + def _compute_inventory_quantity_auto_apply(self): + location_id = self.env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id + product_ids = self.mapped('product_id').ids + if product_ids: + quant_data = self.env['stock.quant'].read_group( + domain=[ + ('product_id', 'in', product_ids), + ('location_id', '=', location_id) + ], + fields=['product_id', 'inventory_quantity_auto_apply'], + groupby=['product_id'] + ) + quantity_map = {item['product_id'][0]: item['inventory_quantity_auto_apply'] for item in quant_data} + else: + quantity_map = {} + for line in self: + if line.product_id: + line.inventory_quantity_auto_apply = quantity_map.get(line.product_id.id, 0.0) + else: + line.inventory_quantity_auto_apply = 0.0 diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index d07e4936..afc40275 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -31,6 +31,8 @@ class SfProductionDemandPlan(models.Model): ('60', '已下达'), ('100', '取消'), ], string='状态', compute='_compute_status', store=True) + demand_plan_id = fields.Many2one(comodel_name="sf.demand.plan", + string="物料需求", readonly=True) sale_order_id = fields.Many2one(comodel_name="sale.order", string="销售订单", readonly=True) sale_order_line_id = fields.Many2one(comodel_name="sale.order.line", @@ -49,7 +51,7 @@ class SfProductionDemandPlan(models.Model): string='产品', store=True, index=True) model_id = fields.Char('模型ID', related='product_id.model_id') part_name = fields.Char('零件名称', related='product_id.part_name') - part_number = fields.Char('零件图号', compute='_compute_part_number', store=True) + part_number = fields.Char('零件图号', related='demand_plan_id.part_number') is_incoming_material = fields.Boolean('客供料', related='sale_order_line_id.is_incoming_material', store=True) supply_method = fields.Selection([ ('automation', "自动化产线加工"), @@ -63,7 +65,7 @@ class SfProductionDemandPlan(models.Model): deadline_of_delivery = fields.Date('客户交期', related='sale_order_line_id.delivery_end_date', store=True) inventory_quantity_auto_apply = fields.Float( string="成品库存", - compute='_compute_inventory_quantity_auto_apply' + related='demand_plan_id.inventory_quantity_auto_apply' ) qty_delivered = fields.Float( "交货数量", related='sale_order_line_id.qty_delivered') @@ -72,14 +74,14 @@ class SfProductionDemandPlan(models.Model): model_long = fields.Char('尺寸(mm)', compute='_compute_model_long') blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类', related='product_id.blank_type') - embryo_long = fields.Char('坯料尺寸(mm)', compute='_compute_embryo_long') - materials_id = fields.Char('材料', compute='_compute_materials_id', store=True) + embryo_long = fields.Char('坯料尺寸(mm)', related='demand_plan_id.embryo_long') + materials_id = fields.Char('材料', related='demand_plan_id.materials_id') model_machining_precision = fields.Selection(selection=_get_machining_precision, string='精度', related='product_id.model_machining_precision') model_process_parameters_ids = fields.Many2many('sf.production.process.parameter', 'plan_process_parameter_rel', string='表面工艺', - compute='_compute_model_process_parameters_ids' + related='demand_plan_id.model_process_parameters_ids' , store=True ) product_remark = fields.Char("产品备注", related='product_id.model_remark') @@ -176,18 +178,6 @@ class SfProductionDemandPlan(models.Model): else: line.sale_order_line_number = None - @api.depends('product_id.part_number', 'product_id.model_name') - def _compute_part_number(self): - for line in self: - if line.product_id: - if line.product_id.part_number: - line.part_number = line.product_id.part_number - else: - if line.product_id.model_name: - line.part_number = line.product_id.model_name.rsplit('.', 1)[0] - else: - line.part_number = None - @api.depends('product_id.length', 'product_id.width', 'product_id.height') def _compute_model_long(self): for line in self: @@ -196,51 +186,6 @@ class SfProductionDemandPlan(models.Model): else: line.model_long = None - @api.depends('product_id.model_long', 'product_id.model_width', 'product_id.model_height') - def _compute_embryo_long(self): - for line in self: - if line.product_id: - line.embryo_long = f"{round(line.product_id.model_long, 3)}*{round(line.product_id.model_width, 3)}*{round(line.product_id.model_height, 3)}" - else: - line.embryo_long = None - - @api.depends('product_id.materials_id') - def _compute_materials_id(self): - for line in self: - if line.product_id: - line.materials_id = f"{line.product_id.materials_id.name}/{line.product_id.materials_type_id.name}" - else: - line.materials_id = None - - @api.depends('product_id.model_process_parameters_ids') - def _compute_model_process_parameters_ids(self): - for line in self: - if line.product_id and line.product_id.model_process_parameters_ids: - line.model_process_parameters_ids = [(6, 0, line.product_id.model_process_parameters_ids.ids)] - else: - line.model_process_parameters_ids = [(5, 0, 0)] - - def _compute_inventory_quantity_auto_apply(self): - location_id = self.env['stock.location'].search([('name', '=', '成品存货区')], limit=1).id - product_ids = self.mapped('product_id').ids - if product_ids: - quant_data = self.env['stock.quant'].read_group( - domain=[ - ('product_id', 'in', product_ids), - ('location_id', '=', location_id) - ], - fields=['product_id', 'inventory_quantity_auto_apply'], - groupby=['product_id'] - ) - quantity_map = {item['product_id'][0]: item['inventory_quantity_auto_apply'] for item in quant_data} - else: - quantity_map = {} - for line in self: - if line.product_id: - line.inventory_quantity_auto_apply = quantity_map.get(line.product_id.id, 0.0) - else: - line.inventory_quantity_auto_apply = 0.0 - @api.depends('sale_order_id.mrp_production_ids.workorder_ids.date_start') def _compute_actual_start_date(self): for record in self: @@ -329,7 +274,6 @@ class SfProductionDemandPlan(models.Model): for pro_plan in pro_plan_list: pro_plan.do_production_schedule() - def button_action_print(self): return { 'res_model': 'sf.demand.plan.print.wizard', From 02b4f763269a8e2bf9691aea0cb37b22a64f6c3a Mon Sep 17 00:00:00 2001 From: guanhuan Date: Tue, 24 Jun 2025 11:17:41 +0800 Subject: [PATCH 02/22] =?UTF-8?q?=E7=89=A9=E6=96=99=E9=9C=80=E6=B1=82?= =?UTF-8?q?=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_demand_plan/__manifest__.py | 1 + sf_demand_plan/models/__init__.py | 2 +- sf_demand_plan/models/sale_order.py | 3 ++ sf_demand_plan/models/sf_demand_plan.py | 15 ++++++- .../models/sf_production_demand_plan.py | 10 +++++ sf_demand_plan/security/ir.model.access.csv | 4 ++ sf_demand_plan/views/demand_plan.xml | 2 + sf_demand_plan/views/demand_plan_info.xml | 40 +++++++++++++++++++ 8 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 sf_demand_plan/views/demand_plan_info.xml diff --git a/sf_demand_plan/__manifest__.py b/sf_demand_plan/__manifest__.py index e65fdca7..dc772ab2 100644 --- a/sf_demand_plan/__manifest__.py +++ b/sf_demand_plan/__manifest__.py @@ -13,6 +13,7 @@ 'depends': ['sf_plan','jikimo_printing'], 'data': [ 'security/ir.model.access.csv', + 'views/demand_plan_info.xml.xml', 'views/demand_plan.xml', 'wizard/sf_demand_plan_print_wizard_view.xml', ], diff --git a/sf_demand_plan/models/__init__.py b/sf_demand_plan/models/__init__.py index acf4ad1e..544214d3 100644 --- a/sf_demand_plan/models/__init__.py +++ b/sf_demand_plan/models/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- +from . import sf_demand_plan from . import sf_production_demand_plan from . import sale_order -from . import sf_demand_plan diff --git a/sf_demand_plan/models/sale_order.py b/sf_demand_plan/models/sale_order.py index 27e5c603..620bc3b3 100644 --- a/sf_demand_plan/models/sale_order.py +++ b/sf_demand_plan/models/sale_order.py @@ -16,7 +16,10 @@ class ReSaleOrder(models.Model): 'sale_order_id': ret.order_id.id, 'sale_order_line_id': ret.id, } + demand_plan_info = self.env['sf.demand.plan'].sudo().create(vals) + vals.update({'demand_plan_id': demand_plan_info.id}) demand_plan = self.env['sf.production.demand.plan'].sudo().create(vals) + demand_plan_info.write({'line_ids': demand_plan.id}) if demand_plan.product_id.machining_drawings_name: filename_url = demand_plan.product_id.machining_drawings_name.rsplit('.', 1)[0] wizard_vals = { diff --git a/sf_demand_plan/models/sf_demand_plan.py b/sf_demand_plan/models/sf_demand_plan.py index abf8cd41..14c6a9de 100644 --- a/sf_demand_plan/models/sf_demand_plan.py +++ b/sf_demand_plan/models/sf_demand_plan.py @@ -12,7 +12,15 @@ class SfDemandPlan(models.Model): list = [(m.sync_id, m.name) for m in machinings] return list - line_ids = fields.One2many('sf.production.demand.plan', 'demand_plan_id', string="需求计划", copy=True) + state = fields.Selection([ + ('10', '草稿'), + ('20', '待工艺设计'), + ('30', '部分下达'), + ('40', '已下达'), + ], string='状态') + + line_ids = fields.One2many(comodel_name='sf.production.demand.plan', + inverse_name='demand_plan_id', string="需求计划", copy=True) sale_order_id = fields.Many2one(comodel_name="sale.order", string="销售订单", readonly=True) @@ -35,6 +43,9 @@ class SfDemandPlan(models.Model): # product_uom_qty = fields.Float( # string="待计划", # related='sale_order_line_id.product_uom_qty', store=True) + # product_uom_qty = fields.Float( + # string="已计划", + # related='sale_order_line_id.product_uom_qty', store=True) model_id = fields.Char('模型ID', related='product_id.model_id') customer_name = fields.Char('客户', related='sale_order_id.customer_name') product_uom_qty = fields.Float( @@ -65,6 +76,8 @@ class SfDemandPlan(models.Model): ('4', '低'), ], string='优先级', default='3') + remark = fields.Char('备注') + @api.depends('product_id.part_number', 'product_id.model_name') def _compute_part_number(self): for line in self: diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index afc40275..6c745f3b 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -270,6 +270,16 @@ class SfProductionDemandPlan(models.Model): pro_plan_list.date_planned_start = date_planned_start self._do_production_schedule(pro_plan_list) + def edit_button(self): + return { + 'res_model': 'sf.demand.plan', + 'type': 'ir.actions.act_window', + 'name': _("需求计划"), + 'domain': [('line_ids', 'in', self.demand_plan_id.ids)], + 'views': [[self.env.ref('sf_demand_plan.view_sf_demand_plan_list').id, 'list']], + 'target': 'new', + } + def _do_production_schedule(self, pro_plan_list): for pro_plan in pro_plan_list: pro_plan.do_production_schedule() diff --git a/sf_demand_plan/security/ir.model.access.csv b/sf_demand_plan/security/ir.model.access.csv index 56e8e247..106f6fc9 100644 --- a/sf_demand_plan/security/ir.model.access.csv +++ b/sf_demand_plan/security/ir.model.access.csv @@ -4,3 +4,7 @@ access_sf_production_demand_plan_for_dispatch,sf.production.demand.plan for disp access_sf_demand_plan_print_wizard,sf.demand.plan.print.wizard,model_sf_demand_plan_print_wizard,base.group_user,1,0,0,0 access_sf_demand_plan_print_wizard_for_dispatch,sf.demand.plan.print.wizard for dispatch,model_sf_demand_plan_print_wizard,sf_base.group_plan_dispatch,1,1,0,0 + + +access_sf_demand_plan,sf.demand.plan,model_sf_demand_plan,base.group_user,1,0,0,0 +access_sf_demand_plan_for_dispatch,sf.demand.plan for dispatch,model_sf_demand_plan,sf_base.group_plan_dispatch,1,1,0,0 diff --git a/sf_demand_plan/views/demand_plan.xml b/sf_demand_plan/views/demand_plan.xml index 24a2707e..b4c643ce 100644 --- a/sf_demand_plan/views/demand_plan.xml +++ b/sf_demand_plan/views/demand_plan.xml @@ -69,6 +69,8 @@ + + + + From deda36a0f9a5489305a1b77b7f2c6536542133a2 Mon Sep 17 00:00:00 2001 From: guanhuan Date: Wed, 25 Jun 2025 15:01:02 +0800 Subject: [PATCH 06/22] =?UTF-8?q?=E7=89=A9=E6=96=99=E9=9C=80=E6=B1=82?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_demand_plan/models/sf_production_demand_plan.py | 5 ++--- sf_demand_plan/views/demand_plan.xml | 4 ++-- sf_demand_plan/views/demand_plan_info.xml | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index 118494fc..3abdd0f3 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -30,7 +30,7 @@ class SfProductionDemandPlan(models.Model): ('50', '待下达生产'), ('60', '已下达'), ('100', '取消'), - ], string='状态', compute='_compute_status', store=True, readonly=False) + ], string='状态', default='30', compute='_compute_status', store=True) demand_plan_id = fields.Many2one(comodel_name="sf.demand.plan", string="物料需求", readonly=True) sale_order_id = fields.Many2one(comodel_name="sale.order", @@ -85,8 +85,7 @@ class SfProductionDemandPlan(models.Model): order_state = fields.Selection( string='订单状态', related='sale_order_line_id.state') - route_id = fields.Many2one('stock.route', string='路线', related='sale_order_line_id.route_id', store=True) - route_ids = fields.Many2many('stock.route', 'stock_route_demand_plan', 'demand_plan_id', 'route_id', '路线', + route_ids = fields.Many2many('stock.route', 'stock_route_demand_plan', 'demand_plan_id', 'route_id', '库存路线', domain=[('demand_plan_selectable', '=', True)], compute='_compute_route_ids', store=True) contract_date = fields.Date('合同日期', related='sale_order_id.contract_date') diff --git a/sf_demand_plan/views/demand_plan.xml b/sf_demand_plan/views/demand_plan.xml index 4c63fb8e..5f623267 100644 --- a/sf_demand_plan/views/demand_plan.xml +++ b/sf_demand_plan/views/demand_plan.xml @@ -21,7 +21,7 @@ - + @@ -38,7 +38,7 @@ - diff --git a/sf_demand_plan/views/demand_plan_info.xml b/sf_demand_plan/views/demand_plan_info.xml index 0e7c7794..16f75b35 100644 --- a/sf_demand_plan/views/demand_plan_info.xml +++ b/sf_demand_plan/views/demand_plan_info.xml @@ -41,8 +41,8 @@ - - + + From 413cf43ea062b5c924782204c2b94e7421086a35 Mon Sep 17 00:00:00 2001 From: guanhuan Date: Wed, 25 Jun 2025 15:33:24 +0800 Subject: [PATCH 07/22] =?UTF-8?q?=E7=89=A9=E6=96=99=E9=9C=80=E6=B1=82?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_demand_plan/models/sale_order.py | 17 ++--- .../models/sf_production_demand_plan.py | 68 +++++++++---------- sf_demand_plan/models/stock_route.py | 2 +- 3 files changed, 39 insertions(+), 48 deletions(-) diff --git a/sf_demand_plan/models/sale_order.py b/sf_demand_plan/models/sale_order.py index f0bd5570..0d0bbf1c 100644 --- a/sf_demand_plan/models/sale_order.py +++ b/sf_demand_plan/models/sale_order.py @@ -50,19 +50,10 @@ class ReSaleOrder(models.Model): def action_view_demand_plan(self): self.ensure_one() demand_plan_ids = self.env['sf.production.demand.plan'].sudo().search([('sale_order_id', '=', self.id)]).ids - action = { + return { 'res_model': 'sf.production.demand.plan', 'type': 'ir.actions.act_window', + 'name': _("需求计划"), + 'domain': [('id', 'in', demand_plan_ids)], + 'view_mode': 'tree', } - if len(demand_plan_ids) == 1: - action.update({ - 'view_mode': 'form', - 'res_id': demand_plan_ids[0], - }) - else: - action.update({ - 'name': _("需求计划"), - 'domain': [('id', 'in', demand_plan_ids)], - 'view_mode': 'tree', - }) - return action diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index 3abdd0f3..97d0fcfb 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -30,7 +30,7 @@ class SfProductionDemandPlan(models.Model): ('50', '待下达生产'), ('60', '已下达'), ('100', '取消'), - ], string='状态', default='30', compute='_compute_status', store=True) + ], string='状态', default='30', readonly=True) demand_plan_id = fields.Many2one(comodel_name="sf.demand.plan", string="物料需求", readonly=True) sale_order_id = fields.Many2one(comodel_name="sale.order", @@ -154,41 +154,41 @@ class SfProductionDemandPlan(models.Model): [('demand_plan_selectable', '=', True), ('stock_route_group_ids', '=', group_id.id)]) if route_ids: pdp.route_ids = route_ids.ids - break + continue pdp.route_ids = None - @api.depends('sale_order_id.state', 'sale_order_id.mrp_production_ids.schedule_state', 'sale_order_id.order_line', - 'sale_order_id.mrp_production_ids.state') - def _compute_status(self): - for record in self: - if record.sale_order_id: - sale_order_state = record.sale_order_id.state - if sale_order_state in ('draft', 'sent', 'supply method'): - record.status = '20' # 待确认 - if record.supply_method in ('purchase', 'outsourcing') and sale_order_state in ( - 'sale', 'processing', 'physical_distribution', 'delivered', - 'done') and sale_order_state != 'cancel': - record.status = '60' # 已下达 - if record.supply_method in ('automation', 'manual'): - if sale_order_state in ( - 'sale', 'processing', 'physical_distribution', 'delivered', - 'done') and sale_order_state != 'cancel': - record.status = '30' # 需求确认 - # 检查所有制造订单的排程单状态,有一个为待排程状态,就为待下达生产 - pending_productions = record.sale_order_id.mrp_production_ids.filtered( - lambda p: p.state == 'confirmed' and p.product_id.id == record.product_id.id - ) - if pending_productions: - record.status = '50' # 待下达生产 - # 检查所有制造订单的排程单状态 - if record.sale_order_id.mrp_production_ids: - product_productions = record.sale_order_id.mrp_production_ids.filtered( - lambda p: p.product_id.id == record.product_id.id - ) - if product_productions and all(order.schedule_state != '未排' for order in product_productions): - record.status = '60' # 已下达 - if sale_order_state == 'cancel' or not record.sale_order_line_id: - record.status = '100' # 取消 + # @api.depends('sale_order_id.state', 'sale_order_id.mrp_production_ids.schedule_state', 'sale_order_id.order_line', + # 'sale_order_id.mrp_production_ids.state') + # def _compute_status(self): + # for record in self: + # if record.sale_order_id: + # sale_order_state = record.sale_order_id.state + # if sale_order_state in ('draft', 'sent', 'supply method'): + # record.status = '20' # 待确认 + # if record.supply_method in ('purchase', 'outsourcing') and sale_order_state in ( + # 'sale', 'processing', 'physical_distribution', 'delivered', + # 'done') and sale_order_state != 'cancel': + # record.status = '60' # 已下达 + # if record.supply_method in ('automation', 'manual'): + # if sale_order_state in ( + # 'sale', 'processing', 'physical_distribution', 'delivered', + # 'done') and sale_order_state != 'cancel': + # record.status = '30' # 需求确认 + # # 检查所有制造订单的排程单状态,有一个为待排程状态,就为待下达生产 + # pending_productions = record.sale_order_id.mrp_production_ids.filtered( + # lambda p: p.state == 'confirmed' and p.product_id.id == record.product_id.id + # ) + # if pending_productions: + # record.status = '50' # 待下达生产 + # # 检查所有制造订单的排程单状态 + # if record.sale_order_id.mrp_production_ids: + # product_productions = record.sale_order_id.mrp_production_ids.filtered( + # lambda p: p.product_id.id == record.product_id.id + # ) + # if product_productions and all(order.schedule_state != '未排' for order in product_productions): + # record.status = '60' # 已下达 + # if sale_order_state == 'cancel' or not record.sale_order_line_id: + # record.status = '100' # 取消 @api.depends('sale_order_line_id.product_id.name') def _compute_sale_order_line_number(self): diff --git a/sf_demand_plan/models/stock_route.py b/sf_demand_plan/models/stock_route.py index 20e4b3c9..7719d7b1 100644 --- a/sf_demand_plan/models/stock_route.py +++ b/sf_demand_plan/models/stock_route.py @@ -19,7 +19,7 @@ class SfStockRoute(models.Model): [('supply_method', 'in', stock_route_group)]) if demand_plan_ids: sr.demand_plan_ids = demand_plan_ids.ids - break + continue sr.demand_plan_ids = None # def name_get(self): From c14e2c19bfa185da372aa37fc0031498c760b9e6 Mon Sep 17 00:00:00 2001 From: guanhuan Date: Thu, 26 Jun 2025 11:13:56 +0800 Subject: [PATCH 08/22] =?UTF-8?q?=E7=89=A9=E6=96=99=E9=9C=80=E6=B1=82?= =?UTF-8?q?=E8=AE=A1=E5=88=92=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_demand_plan/__manifest__.py | 1 + sf_demand_plan/models/__init__.py | 1 + sf_demand_plan/models/mrp_bom.py | 20 +++ .../models/sf_production_demand_plan.py | 159 +++++++++++++++++- sf_demand_plan/security/ir.model.access.csv | 3 + sf_demand_plan/views/demand_plan_info.xml | 6 +- sf_demand_plan/wizard/__init__.py | 1 + .../wizard/sf_release_plan_wizard.py | 22 +++ .../wizard/sf_release_plan_wizard_views.xml | 22 +++ 9 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 sf_demand_plan/models/mrp_bom.py create mode 100644 sf_demand_plan/wizard/sf_release_plan_wizard.py create mode 100644 sf_demand_plan/wizard/sf_release_plan_wizard_views.xml diff --git a/sf_demand_plan/__manifest__.py b/sf_demand_plan/__manifest__.py index 8a913e0b..4327514a 100644 --- a/sf_demand_plan/__manifest__.py +++ b/sf_demand_plan/__manifest__.py @@ -19,6 +19,7 @@ 'views/stock_route.xml', 'views/sale_order_views.xml', 'wizard/sf_demand_plan_print_wizard_view.xml', + 'wizard/sf_release_plan_wizard_views.xml', ], 'demo': [ ], diff --git a/sf_demand_plan/models/__init__.py b/sf_demand_plan/models/__init__.py index 2952477c..b6bb7609 100644 --- a/sf_demand_plan/models/__init__.py +++ b/sf_demand_plan/models/__init__.py @@ -4,3 +4,4 @@ from . import sf_demand_plan from . import sf_production_demand_plan from . import sale_order from . import stock_route +from . import mrp_bom diff --git a/sf_demand_plan/models/mrp_bom.py b/sf_demand_plan/models/mrp_bom.py new file mode 100644 index 00000000..8973cb87 --- /dev/null +++ b/sf_demand_plan/models/mrp_bom.py @@ -0,0 +1,20 @@ +from odoo import models, fields + + +class MrpBom(models.Model): + _inherit = 'mrp.bom' + + # 业务平台分配工厂后在智能工厂先创建销售订单再创建该产品后再次进行创建bom + def bom_create(self, product, bom_type, product_type, code=None): + bom_id = self.env['mrp.bom'].create({ + 'product_tmpl_id': product.product_tmpl_id.id, + 'type': bom_type, + # 'subcontractor_id': '' or subcontract.partner_id.id, + 'product_qty': 1, + 'product_uom_id': 1, + 'code': code + }) + if bom_type == 'subcontract' and product_type is not False: + subcontract = self.get_supplier(product.materials_type_id) + bom_id.subcontractor_id = subcontract.partner_id.id + return bom_id diff --git a/sf_demand_plan/models/sf_production_demand_plan.py b/sf_demand_plan/models/sf_production_demand_plan.py index 97d0fcfb..97e8b99a 100644 --- a/sf_demand_plan/models/sf_production_demand_plan.py +++ b/sf_demand_plan/models/sf_production_demand_plan.py @@ -6,6 +6,7 @@ from odoo.exceptions import ValidationError from odoo.tools import float_compare from datetime import datetime, timedelta from odoo.exceptions import UserError +import re class SfProductionDemandPlan(models.Model): @@ -30,7 +31,7 @@ class SfProductionDemandPlan(models.Model): ('50', '待下达生产'), ('60', '已下达'), ('100', '取消'), - ], string='状态', default='30', readonly=True) + ], string='状态', default='30') demand_plan_id = fields.Many2one(comodel_name="sf.demand.plan", string="物料需求", readonly=True) sale_order_id = fields.Many2one(comodel_name="sale.order", @@ -144,6 +145,7 @@ class SfProductionDemandPlan(models.Model): blank_arrival_date = fields.Date('采购计划到货(坯料)') finished_product_arrival_date = fields.Date('采购计划到货(成品)') + bom_id = fields.Many2one('mrp.bom', readonly=True, string="BOM") @api.depends('supply_method') def _compute_route_ids(self): @@ -532,3 +534,158 @@ class SfProductionDemandPlan(models.Model): if len(self.demand_plan_id.line_ids) == 1: raise ValidationError(f"最后一条计划,不能删除!") self.unlink() + + def button_release_plan(self): + if not self.supply_method: + raise ValidationError(f"供货方式不能为空!") + if self.plan_uom_qty > self.product_uom_qty: + return { + 'name': _('需求计划'), + 'type': 'ir.actions.act_window', + 'views': [(self.env.ref( + 'sf_demand_plan.sf_release_plan_wizard_form').id, + 'form')], + 'res_model': 'sf.release.plan.wizard', + 'target': 'new', + 'context': { + 'default_demand_plan_line_id': self.id, + 'default_release_message': f"您正在下达计划量 {self.plan_uom_qty},需求数量为 {self.product_uom_qty},已超过需求数量,是否继续?", + }} + self.mrp_bom_create() + + def mrp_bom_create(self): + self.ensure_one() + 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单据编码 + code = f"{self.product_id.default_code}-{bom_type}-{datetime.now().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, + 'model_long': product.length, + 'model_width': product.width, + 'model_height': product.height, + 'blank_volume': product.model_volume, + 'blank_area': product.model_area, + 'price': product.list_price, + 'embryo_redundancy_id': self.sale_order_line_id.embryo_redundancy_id, + 'model_id': self.model_id + } + 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_machining_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, 'normal', 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 + elif 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_id, + '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 diff --git a/sf_demand_plan/security/ir.model.access.csv b/sf_demand_plan/security/ir.model.access.csv index 5a35ba20..9bd2e29b 100644 --- a/sf_demand_plan/security/ir.model.access.csv +++ b/sf_demand_plan/security/ir.model.access.csv @@ -11,3 +11,6 @@ access_sf_demand_plan_for_dispatch,sf.demand.plan for dispatch,model_sf_demand_p access_stock_route_group,stock.route.group,model_stock_route_group,base.group_user,1,0,0,0 access_stock_route_group_dispatch,stock.route.group.dispatch,model_stock_route_group,sf_base.group_plan_dispatch,1,1,0,0 + +access_sf_release_plan_wizard,sf.release.plan.wizard,model_sf_release_plan_wizard,base.group_user,1,0,0,0 +access_sf_release_plan_wizard_for_dispatch,sf.release.plan.wizard for dispatch,model_sf_release_plan_wizard,sf_base.group_plan_dispatch,1,1,1,1 \ No newline at end of file diff --git a/sf_demand_plan/views/demand_plan_info.xml b/sf_demand_plan/views/demand_plan_info.xml index 16f75b35..7057efc1 100644 --- a/sf_demand_plan/views/demand_plan_info.xml +++ b/sf_demand_plan/views/demand_plan_info.xml @@ -44,7 +44,7 @@ - + @@ -54,6 +54,10 @@ +