From 186ac391eaf2f600680dc4514742e8a99a2e9a26 Mon Sep 17 00:00:00 2001 From: liaodanlong Date: Tue, 21 Jan 2025 15:53:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B9=B6=E5=8F=91=E5=88=9B=E5=BB=BA=E5=88=B6?= =?UTF-8?q?=E9=80=A0=E8=AE=A2=E5=8D=95=E7=9A=84=E5=B7=A5=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_manufacturing/models/mrp_production.py | 240 +++++++++++------- .../wizard/production_technology_wizard.py | 145 ++++++----- 2 files changed, 228 insertions(+), 157 deletions(-) diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 1bfa5f07..b13f1095 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -1,20 +1,27 @@ # -*- coding: utf-8 -*- +import asyncio import base64 +import cProfile +import concurrent import datetime +import io import logging import json import os +import pstats import re +import threading +import time +from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor import requests from itertools import groupby from collections import defaultdict, namedtuple -from odoo import api, fields, models, SUPERUSER_ID, _ +from odoo import api, fields, models, SUPERUSER_ID, _, tools from odoo.exceptions import UserError, ValidationError from odoo.addons.sf_base.commons.common import Common from odoo.tools import float_compare, float_round, float_is_zero, format_datetime - class MrpProduction(models.Model): _inherit = 'mrp.production' _description = "制造订单" @@ -759,64 +766,105 @@ class MrpProduction(models.Model): # if self.product_id.tracking == 'serial': # self._set_qty_producing() + # 重载根据工序生成工单的程序:如果产品BOM中没有工序时, # 根据产品对应的模板类型中工序,去生成工单; # CNC加工工序的选取规则: # 如果自动报价有带过来预分配的机床, # 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制; # 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心; + def process_production(self): + workorders_values = [] + # production = self.env['mrp.production'].browse(production_id) + product_qty = self.product_uom_id._compute_quantity(self.product_qty, + self.bom_id.product_uom_id) + exploded_boms, dummy = self.bom_id.explode(self.product_id, + product_qty / self.bom_id.product_qty, + picking_type=self.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': self.id, + 'workcenter_id': operation.workcenter_id.id, + 'product_uom_id': self.product_uom_id.id, + 'operation_id': operation.id, + 'state': 'pending', + }] + if self.product_id.categ_id.type in ['成品', '坯料']: + # # 根据工序设计生成工单 + technology_design_ids = sorted(self.technology_design_ids, key=lambda x: x.sequence) + for route in technology_design_ids: + workorder_has = self.env['mrp.workorder'].search( + [('technology_design_id', '=', route.id), ('production_id', '=', self.id)]) + if not workorder_has: + if route.route_id.routing_type not in ['表面工艺']: + workorders_values.append( + self.env['mrp.workorder'].json_workorder_str(self, route)) + else: + product_production_process = self.env['product.template'].search( + [('server_product_process_parameters_id', '=', route.process_parameters_id.id)]) + workorders_values.append( + self.env[ + 'mrp.workorder']._json_workorder_surface_process_str( + self, route, product_production_process.seller_ids[0].partner_id.id)) + return workorders_values + + def _set_workorder_duration_expected(self): + try: + # 在每个线程中创建独立的 ORM 环境 + with api.Environment.manage(): + new_cr = self.pool.cursor() + self = self.with_env(self.env(cr=new_cr)) + # program_ids = self.sudo().env['loyalty.program'].browse(program_ids.ids) + # 使用独立的环境来处理数据库事务 + # 在独立的环境中对 workorder 进行操作 + production = self.sudo().env['mrp.production'].browse(self.id) + workorders_values = production.process_production() + production.write({'workorder_ids': workorders_values}) + for workorder in production.workorder_ids: + workorder.duration_expected = workorder._get_duration_expected() + # 可以进行其他与工作单相关的操作 + # workorder_env.write({'...'}) + return production + except Exception as e: + logging.error(f"Error processing workorder {workorder.id}: {e}") + # workorders_values = self.process_production(self) + # print('_set_workorder_duration_expected wqio ', self) + # self.write({'workorder_ids': workorders_values}) + # for workorder in self.workorder_ids: + # workorder.duration_expected = workorder._get_duration_expected() def _create_workorder3(self, item): - for production in self: - 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)): + with ThreadPoolExecutor(max_workers=os.cpu_count()) as executor: + futures = [] + for production in self: + if not production.bom_id or not production.product_id: 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', - }] - if production.product_id.categ_id.type in ['成品', '坯料']: - # # 根据工序设计生成工单 - technology_design_ids = sorted(production.technology_design_ids, key=lambda x: x.sequence) - for route in technology_design_ids: - workorder_has = self.env['mrp.workorder'].search( - [('technology_design_id', '=', route.id), ('production_id', '=', production.id)]) - if not workorder_has: - if route.route_id.routing_type not in ['表面工艺']: - workorders_values.append( - self.env['mrp.workorder'].json_workorder_str(production, route)) - else: - product_production_process = self.env['product.template'].search( - [('server_product_process_parameters_id', '=', route.process_parameters_id.id)]) - workorders_values.append( - self.env[ - 'mrp.workorder']._json_workorder_surface_process_str( - production, route, product_production_process.seller_ids[0].partner_id.id)) - production.workorder_ids = workorders_values - for workorder in production.workorder_ids: - workorder.duration_expected = workorder._get_duration_expected() + # 提交每个生产任务到线程池 + futures.append(executor.submit(production._set_workorder_duration_expected)) + + # 等待所有线程完成任务 + results = [] + for future in futures: + try: + result = future.result() + if result: + results.append(result) + + except Exception as e: + logging.error(f"Error processing production: {e}") + return results # 外协出入库单处理 - def get_subcontract_pick_purchase(self): - production_all = self.sorted(lambda x: x.id) + def get_subcontract_pick_purchase(self,productions): + production_all = productions.sorted(lambda x: x.id) product_id_to_production_names = {} grouped_product_ids = {k: list(g) for k, g in groupby(production_all, key=lambda x: x.product_id.id)} @@ -825,9 +873,10 @@ class MrpProduction(models.Model): sorted_workorders = None for production in production_all: proc_workorders = [] - process_parameter_workorder = self.env['mrp.workorder'].search( - [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), - ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc') + process_parameter_workorder=production.workorder_ids.filtered(lambda w: w.surface_technics_parameters_id and w.is_subcontract and w.state!='cancel') + # process_parameter_workorder = self.env['mrp.workorder'].search( + # [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), + # ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc') if process_parameter_workorder: # 将这些特殊表面工艺工单的采购单与调拨单置为失效 for workorder in process_parameter_workorder: @@ -942,48 +991,49 @@ class MrpProduction(models.Model): if purchase_order_line: line.unlink() - def _reset_work_order_sequence(self): + def _process_reset_work_order_sequence(self,rec): + workorder_ids = rec.workorder_ids + technology_design_ids = rec.technology_design_ids + if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')): + # 获取返工后新生成的工单 + work_ids = workorder_ids.filtered(lambda item: item.sequence == 0) + # 对工单进行逐个插入 + for work_id in work_ids: + order_rework_ids = rec.workorder_ids.filtered( + lambda item: (item.sequence > 0 and work_id.name == item.name + and work_id.processing_panel == item.processing_panel)) + order_rework_ids = sorted(order_rework_ids, key=lambda item: item.sequence, reverse=True) + work_id.sequence = order_rework_ids[0].sequence + 1 + # 对该工单之后的工单工序进行加一 + work_order_ids = rec.workorder_ids.filtered( + lambda item: item.sequence >= work_id.sequence and item.id != work_id.id) + for work in work_order_ids: + work.sequence = work.sequence + 1 + + else: + # 将工艺设计生成的工单序号赋值给工单的序号 + for work in workorder_ids: + td_ids = technology_design_ids.filtered( + lambda item: (item.route_id.name in work.name and item.process_parameters_id + and item.process_parameters_id == work.surface_technics_parameters_id) or + (item.route_id.name == work.name and item.panel + and item.panel == work.processing_panel)) + if work.name == '人工线下加工': + td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name)) + if td_ids: + work.sequence = td_ids[0].sequence + cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel')) + if cancel_work_ids: + sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')), + key=lambda w: w.sequence).sequence + for cw in cancel_work_ids: + cw.sequence = sequence + 1 + def _reset_work_order_sequence(self,productions): """ 工单工序排序方法(新) """ - for rec in self: - workorder_ids = rec.workorder_ids - technology_design_ids = rec.technology_design_ids - if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')): - # 获取返工后新生成的工单 - work_ids = workorder_ids.filtered(lambda item: item.sequence == 0) - # 对工单进行逐个插入 - for work_id in work_ids: - order_rework_ids = rec.workorder_ids.filtered( - lambda item: (item.sequence > 0 and work_id.name == item.name - and work_id.processing_panel == item.processing_panel)) - order_rework_ids = sorted(order_rework_ids, key=lambda item: item.sequence, reverse=True) - work_id.sequence = order_rework_ids[0].sequence + 1 - # 对该工单之后的工单工序进行加一 - work_order_ids = rec.workorder_ids.filtered( - lambda item: item.sequence >= work_id.sequence and item.id != work_id.id) - for work in work_order_ids: - work.sequence = work.sequence + 1 - - else: - # 将工艺设计生成的工单序号赋值给工单的序号 - for work in workorder_ids: - td_ids = technology_design_ids.filtered( - lambda item: (item.route_id.name in work.name and item.process_parameters_id - and item.process_parameters_id == work.surface_technics_parameters_id) or - (item.route_id.name == work.name and item.panel - and item.panel == work.processing_panel)) - if work.name == '人工线下加工': - td_ids = technology_design_ids.filtered(lambda item: (item.route_id.name in work.name)) - if td_ids: - work.sequence = td_ids[0].sequence - cancel_work_ids = workorder_ids.filtered(lambda item: item.state in ('已取消', 'cancel')) - if cancel_work_ids: - sequence = max(workorder_ids.filtered(lambda item: item.state not in ('已取消', 'cancel')), - key=lambda w: w.sequence).sequence - for cw in cancel_work_ids: - cw.sequence = sequence + 1 - + for rec in productions: + self._process_reset_work_order_sequence(rec) def _reset_work_order_sequence_1(self): """ 工单工序排序方法(旧) @@ -1079,9 +1129,9 @@ class MrpProduction(models.Model): # 创建工单并进行排序 def _create_workorder(self, item): - self._create_workorder3(item) - self._reset_work_order_sequence() - return True + productions = self._create_workorder3(item) + self._reset_work_order_sequence(productions) + return productions def production_process(self, pro_plan): type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False} @@ -1756,3 +1806,5 @@ class sf_processing_panel(models.Model): name = fields.Char('加工面') active = fields.Boolean('有效', default=True) + + diff --git a/sf_manufacturing/wizard/production_technology_wizard.py b/sf_manufacturing/wizard/production_technology_wizard.py index 0415d1fd..16d2c423 100644 --- a/sf_manufacturing/wizard/production_technology_wizard.py +++ b/sf_manufacturing/wizard/production_technology_wizard.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- # Part of YiZuo. See LICENSE file for full copyright and licensing details. -import logging -from itertools import groupby +import cProfile +import io +import pstats +from concurrent.futures import ThreadPoolExecutor from odoo import models, api, fields, _ @@ -13,6 +15,71 @@ class ProductionTechnologyWizard(models.TransientModel): origin = fields.Char(string='源单据') is_technology_confirm = fields.Boolean(default=True) + def _process_production(self,productions,technology_designs): + for production in productions: + # self._process_production_special_design(production,technology_designs) + with ThreadPoolExecutor(max_workers=4) as executor: + executor.submit(self._process_production_special_design, production,technology_designs) + def _process_production_special_design(self,production,technology_designs): + if production != self.production_id: + self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, + production) + # 特殊表面工艺 + special_design = self.env['sf.technology.design'].sudo().search( + [('routing_tag', '=', 'special'), ('production_id', '=', production.id), + ('is_auto', '=', False), ('active', 'in', [True, False])]) + for special in special_design: + workorders_values = [] + if special.active is False: + # is_cancel = False + # 工单采购单外协出入库单皆需取消 + domain = [('production_id', '=', special.production_id.id)] + if special.process_parameters_id: + domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), + ('state', '!=', 'cancel')] + else: + domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')] + workorder = self.env['mrp.workorder'].search(domain) + # previous_workorder = self.env['mrp.workorder'].search( + # [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'), + # ('production_id', '=', workorder.production_id.id)]) + # if previous_workorder: + # if previous_workorder.supplier_id != workorder.supplier_id: + # is_cancel = True + # if workorder.state != 'cancel' and is_cancel is True: + workorder.write({'state': 'cancel'}) + workorder.picking_ids.write({'state': 'cancel'}) + workorder.picking_ids.move_ids.write({'state': 'cancel'}) + purchase_order = self.env['purchase.order'].search( + [('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')]) + for line in purchase_order.order_line: + if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id: + purchase_order.write({'state': 'cancel'}) + else: + if special.production_id.workorder_ids: + workorder = self.env['mrp.workorder'].search( + [('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), + ('state', '!=', 'cancel')]) + if not workorder: + if special.route_id.routing_type == '表面工艺': + product_production_process = self.env['product.template'].search( + [('server_product_process_parameters_id', '=', special.process_parameters_id.id)]) + workorders_values.append( + self.env[ + 'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special, + product_production_process.seller_ids[ + 0].partner_id.id)) + else: + workorders_values.append( + self.env['mrp.workorder'].json_workorder_str(special.production_id, special)) + special.production_id.write({'workorder_ids': workorders_values}) + else: + if len(workorder.blocked_by_workorder_ids) > 1: + if workorder.sequence == 1: + workorder.blocked_by_workorder_ids = None + else: + if workorder.blocked_by_workorder_ids: + workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0] def confirm(self): if self.is_technology_confirm is True and self.production_id.product_id.categ_id.type in ['成品', '坯料']: domain = [('origin', '=', self.origin), ('state', '=', 'technology_to_confirmed'), @@ -23,72 +90,24 @@ class ProductionTechnologyWizard(models.TransientModel): [('production_id', '=', self.production_id.id), ('active', 'in', [True, False])]) # technology_designs = self.production_id.technology_design_ids productions = self.env['mrp.production'].search(domain) - for production in productions: - if production != self.production_id: - self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, - production) - # 特殊表面工艺 - special_design = self.env['sf.technology.design'].sudo().search( - [('routing_tag', '=', 'special'), ('production_id', '=', production.id), - ('is_auto', '=', False), ('active', 'in', [True, False])]) - for special in special_design: - workorders_values = [] - if special.active is False: - # is_cancel = False - # 工单采购单外协出入库单皆需取消 - domain = [('production_id', '=', special.production_id.id)] - if special.process_parameters_id: - domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')] - else: - domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')] - workorder = self.env['mrp.workorder'].search(domain) - # previous_workorder = self.env['mrp.workorder'].search( - # [('sequence', '=', workorder.sequence - 1), ('routing_type', '=', '表面工艺'), - # ('production_id', '=', workorder.production_id.id)]) - # if previous_workorder: - # if previous_workorder.supplier_id != workorder.supplier_id: - # is_cancel = True - # if workorder.state != 'cancel' and is_cancel is True: - workorder.write({'state': 'cancel'}) - workorder.picking_ids.write({'state': 'cancel'}) - workorder.picking_ids.move_ids.write({'state': 'cancel'}) - purchase_order = self.env['purchase.order'].search( - [('origin', '=', workorder.production_id.name), ('purchase_type', '=', 'consignment')]) - for line in purchase_order.order_line: - if line.product_id.server_product_process_parameters_id == workorder.surface_technics_parameters_id: - purchase_order.write({'state': 'cancel'}) - else: - if special.production_id.workorder_ids: - workorder = self.env['mrp.workorder'].search( - [('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')]) - if not workorder: - if special.route_id.routing_type == '表面工艺': - product_production_process = self.env['product.template'].search( - [('server_product_process_parameters_id', '=', special.process_parameters_id.id)]) - workorders_values.append( - self.env[ - 'mrp.workorder']._json_workorder_surface_process_str(special.production_id, special, - product_production_process.seller_ids[ - 0].partner_id.id)) - else: - workorders_values.append( - self.env['mrp.workorder'].json_workorder_str(special.production_id, special)) - special.production_id.write({'workorder_ids': workorders_values}) - else: - if len(workorder.blocked_by_workorder_ids) > 1: - if workorder.sequence == 1: - workorder.blocked_by_workorder_ids = None - else: - if workorder.blocked_by_workorder_ids: - workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0] - productions._create_workorder(False) + pr = cProfile.Profile() + pr.enable() + self._process_production(productions,technology_designs) + productions = productions._create_workorder(False) if self.production_id.product_id.categ_id.type == '成品': - productions.get_subcontract_pick_purchase() - productions.is_adjust = False + self.production_id.get_subcontract_pick_purchase(productions) for item in productions: + item.is_adjust = False workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted( key=lambda a: a.sequence) if workorder[0].state in ['pending']: if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程': workorder[0].state = 'waiting' + pr.disable() # 停止性能分析 + + # 将结果输出到 StringIO + s = io.StringIO() + ps = pstats.Stats(pr, stream=s) + ps.strip_dirs().sort_stats('cumulative').print_stats() + analysis_output = s.getvalue() return productions