From 002653957e85df6474cb5078567f659d4cbf0ac1 Mon Sep 17 00:00:00 2001 From: "jinling.yang" Date: Thu, 27 Oct 2022 17:27:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E9=80=A0=E5=88=B6=E9=80=A0=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E9=87=8C=E7=9A=84=E7=94=9F=E6=88=90=E5=B7=A5=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_base/__manifest__.py | 6 +- sf_base/models/__init__.py | 6 +- sf_base/views/mrs_common_view.xml | 30 +++-- sf_manufacturing_orders/models/__init__.py | 3 +- .../models/mrp_workorder.py | 124 ++++++++++++++++++ .../models/sf_production.py | 49 ++++++- 6 files changed, 194 insertions(+), 24 deletions(-) create mode 100644 sf_manufacturing_orders/models/mrp_workorder.py diff --git a/sf_base/__manifest__.py b/sf_base/__manifest__.py index 800d07c0..5c5c2725 100644 --- a/sf_base/__manifest__.py +++ b/sf_base/__manifest__.py @@ -10,18 +10,18 @@ """, 'category': 'YZ', 'website': 'https://www.sf.jikimo.com', - 'depends': ['account', 'base', 'mrp'], + 'depends': ['account', 'base', 'mrp', 'sale'], 'data': [ 'security/group_security.xml', 'security/ir.model.access.csv', 'data/product_data.xml', 'data/process_data.xml', - 'views/product_template_view.xml', - 'views/sale_order_view.xml', 'views/mrs_base_view.xml', 'views/mrs_common_view.xml', 'views/menu_view.xml', 'views/mrp_routing_workcenter_view.xml', + 'views/sale_order_view.xml', + 'views/product_template_view.xml', ], 'demo': [ diff --git a/sf_base/models/__init__.py b/sf_base/models/__init__.py index 1b906dd5..0f189de6 100644 --- a/sf_base/models/__init__.py +++ b/sf_base/models/__init__.py @@ -1,5 +1,5 @@ -from. import sf_base -from. import sf_common -from . import sale_order +from . import sf_base +from . import sf_common from . import product_template +from . import sale_order from . import process diff --git a/sf_base/views/mrs_common_view.xml b/sf_base/views/mrs_common_view.xml index 8c3e92d2..56178e82 100644 --- a/sf_base/views/mrs_common_view.xml +++ b/sf_base/views/mrs_common_view.xml @@ -277,6 +277,18 @@ #------------------托盘------------------ + + 托盘 + ir.actions.act_window + sf.tray + tree,form + +

+ 创建托盘吧 +

+
+
+ sf.tray.search sf.tray @@ -285,9 +297,9 @@ - - - + + + @@ -321,17 +333,7 @@ - - 托盘 - ir.actions.act_window - sf.tray - tree,form - -

- 创建托盘吧 -

-
-
+ \ No newline at end of file diff --git a/sf_manufacturing_orders/models/__init__.py b/sf_manufacturing_orders/models/__init__.py index 3fb32983..e1f5282a 100644 --- a/sf_manufacturing_orders/models/__init__.py +++ b/sf_manufacturing_orders/models/__init__.py @@ -1 +1,2 @@ -from. import sf_production +from . import sf_production +from . import mrp_workorder diff --git a/sf_manufacturing_orders/models/mrp_workorder.py b/sf_manufacturing_orders/models/mrp_workorder.py new file mode 100644 index 00000000..6469e23e --- /dev/null +++ b/sf_manufacturing_orders/models/mrp_workorder.py @@ -0,0 +1,124 @@ +from collections import defaultdict +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models, SUPERUSER_ID, _ +from odoo.osv import expression +from odoo.addons.stock.models.stock_rule import ProcurementException +from odoo.tools import float_compare, OrderedSet + + +class StockRule(models.Model): + _inherit = 'stock.rule' + + +def _run_manufacture(self, procurements): + productions_values_by_company = defaultdict(list) + errors = [] + for procurement, rule in procurements: + if float_compare(procurement.product_qty, 0, precision_rounding=procurement.product_uom.rounding) <= 0: + # If procurement contains negative quantity, don't create a MO that would be for a negative value. + continue + bom = rule._get_matching_bom(procurement.product_id, procurement.company_id, procurement.values) + + productions_values_by_company[procurement.company_id.id].append(rule._prepare_mo_vals(*procurement, bom)) + + if errors: + raise ProcurementException(errors) + + for company_id, productions_values in productions_values_by_company.items(): + # create the MO as SUPERUSER because the current user may not have the rights to do it (mto product launched by a sale for example) + productions = self.env['mrp.production'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create( + productions_values) + self.env['stock.move'].sudo().create(productions._get_moves_raw_values()) + self.env['stock.move'].sudo().create(productions._get_moves_finished_values()) + # 查出产品的加工面板并对根据面板的数量循环生成工序 + + productions._create_workorder() + productions.filtered(lambda p: (not p.orderpoint_id and p.move_raw_ids) or \ + ( + p.move_dest_ids.procure_method != 'make_to_order' and not p.move_raw_ids and not p.workorder_ids)).action_confirm() + + for production in productions: + origin_production = production.move_dest_ids and production.move_dest_ids[ + 0].raw_material_production_id or False + orderpoint = production.orderpoint_id + if orderpoint and orderpoint.create_uid.id == SUPERUSER_ID and orderpoint.trigger == 'manual': + production.message_post( + body=_('This production order has been created from Replenishment Report.'), + message_type='comment', + subtype_xmlid='mail.mt_note') + elif orderpoint: + production.message_post_with_view( + 'mail.message_origin_link', + values={'self': production, 'origin': orderpoint}, + subtype_id=self.env.ref('mail.mt_note').id) + elif origin_production: + production.message_post_with_view( + 'mail.message_origin_link', + values={'self': production, 'origin': origin_production}, + subtype_id=self.env.ref('mail.mt_note').id) + return True + + +class ResMrpWorkOrder(models.Model): + _inherit = 'mrp.workorder' + + workcenter_id = fields.Many2one('mrp.workcenter', required=False) + processing_panel = fields.Char('加工面') + + @api.model_create_multi + def create(self, values): + res = super().create(values) + if self.env.context.get('skip_confirm'): + return res + to_confirm = res.filtered(lambda wo: wo.production_id.state in ("confirmed", "progress", "to_close")) + to_confirm = to_confirm.production_id.workorder_ids + to_confirm._action_confirm() + return res + + def _action_confirm(self): + workorders_by_production = defaultdict(lambda: self.env['mrp.workorder']) + for workorder in self: + workorders_by_production[workorder.production_id] |= workorder + + for production, workorders in workorders_by_production.items(): + workorders_by_bom = defaultdict(lambda: self.env['mrp.workorder']) + bom = self.env['mrp.bom'] + moves = production.move_raw_ids | production.move_finished_ids + + for workorder in workorders: + bom = workorder.operation_id.bom_id or workorder.production_id.bom_id + previous_workorder = workorders_by_bom[bom][-1:] + previous_workorder.next_work_order_id = workorder.id + workorders_by_bom[bom] |= workorder + + moves.filtered(lambda m: m.operation_id == workorder.operation_id).write({ + 'workorder_id': workorder.id + }) + + exploded_boms, dummy = production.bom_id.explode(production.product_id, 1, + picking_type=production.bom_id.picking_type_id) + exploded_boms = {b[0]: b[1] for b in exploded_boms} + for move in moves: + if move.workorder_id: + continue + bom = move.bom_line_id.bom_id + while bom and bom not in workorders_by_bom: + bom_data = exploded_boms.get(bom, {}) + bom = bom_data.get('parent_line') and bom_data['parent_line'].bom_id or False + if bom in workorders_by_bom: + move.write({ + 'workorder_id': workorders_by_bom[bom][-1:].id + }) + else: + move.write({ + 'workorder_id': workorders_by_bom[production.bom_id][-1:].id + }) + + for workorders in workorders_by_bom.values(): + if not workorders: + continue + if workorders[0].state == 'pending': + workorders[0].state = 'ready' if workorders[0].production_availability == 'assigned' else 'waiting' + for workorder in workorders: + workorder._start_nextworkorder() diff --git a/sf_manufacturing_orders/models/sf_production.py b/sf_manufacturing_orders/models/sf_production.py index 6995005a..b02c895d 100644 --- a/sf_manufacturing_orders/models/sf_production.py +++ b/sf_manufacturing_orders/models/sf_production.py @@ -29,10 +29,54 @@ class MrpProduction(models.Model): _inherit = 'mrp.production' _description = "制造订单" + # 重载根据工序生成工单的程序:如果产品BOM中没有工序时, + # 根据产品对应的模板类型中工序,去生成工单; + # 工单对应的工作中心,根据工序中的工作中心去匹配, + # 如果只配置了一个工作中心,则默认采用该工作中心; + # 如果有多个工作中心, + # 则根据该工作中心的工单个数进行分配(优先分配给工单个数最少的); + # CNC加工工序的选取规则: + # 如果自动报价有带过来预分配的机床, + # 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制; + # 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心; + + def _create_workorder(self): + for production in self: + print(production.product_id.model_type_id) + if not production.bom_id or not production.product_id: + continue + workorders_values = [] + + product_qty = production.product_uom_id._compute_quantity(production.product_qty, + production.bom_id.product_uom_id) + exploded_boms, dummy = production.bom_id.explode(production.product_id, + product_qty / production.bom_id.product_qty, + picking_type=production.bom_id.picking_type_id) + + for bom, bom_data in exploded_boms: + # If the operations of the parent BoM and phantom BoM are the same, don't recreate work orders. + if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[ + 'parent_line'].bom_id.operation_ids != bom.operation_ids)): + continue + for operation in bom.operation_ids: + if operation._skip_operation_line(bom_data['product']): + continue + workorders_values += [{ + 'name': operation.name, + 'production_id': production.id, + 'workcenter_id': operation.workcenter_id.id, + 'product_uom_id': production.product_uom_id.id, + 'operation_id': operation.id, + 'state': 'pending', + }] + production.workorder_ids = [(5, 0)] + [(0, 0, value) for value in workorders_values] + for workorder in production.workorder_ids: + workorder.duration_expected = workorder._get_duration_expected() + + class StockRule(models.Model): _inherit = 'stock.rule' - @api.model def _run_pull(self, procurements): moves_values_by_company = defaultdict(list) @@ -142,6 +186,7 @@ class StockRule(models.Model): moves = self.env['stock.move'].with_user(SUPERUSER_ID).sudo().with_company(company_id).create(moves_values) # Since action_confirm launch following procurement_group we should activate it. moves._action_confirm() + return True # @api.model @@ -256,5 +301,3 @@ class StockRule(models.Model): # mo_lists.append(vals) # # return mo_lists - -