Compare commits

...

1 Commits

Author SHA1 Message Date
liaodanlong
186ac391ea 并发创建制造订单的工单 2025-01-21 15:53:09 +08:00
2 changed files with 228 additions and 157 deletions

View File

@@ -1,20 +1,27 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import asyncio
import base64 import base64
import cProfile
import concurrent
import datetime import datetime
import io
import logging import logging
import json import json
import os import os
import pstats
import re import re
import threading
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import requests import requests
from itertools import groupby from itertools import groupby
from collections import defaultdict, namedtuple 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.exceptions import UserError, ValidationError
from odoo.addons.sf_base.commons.common import Common from odoo.addons.sf_base.commons.common import Common
from odoo.tools import float_compare, float_round, float_is_zero, format_datetime from odoo.tools import float_compare, float_round, float_is_zero, format_datetime
class MrpProduction(models.Model): class MrpProduction(models.Model):
_inherit = 'mrp.production' _inherit = 'mrp.production'
_description = "制造订单" _description = "制造订单"
@@ -759,24 +766,21 @@ class MrpProduction(models.Model):
# if self.product_id.tracking == 'serial': # if self.product_id.tracking == 'serial':
# self._set_qty_producing() # self._set_qty_producing()
# 重载根据工序生成工单的程序如果产品BOM中没有工序时 # 重载根据工序生成工单的程序如果产品BOM中没有工序时
# 根据产品对应的模板类型中工序,去生成工单; # 根据产品对应的模板类型中工序,去生成工单;
# CNC加工工序的选取规则 # CNC加工工序的选取规则
# 如果自动报价有带过来预分配的机床, # 如果自动报价有带过来预分配的机床,
# 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制; # 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制;
# 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心; # 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心;
def process_production(self):
def _create_workorder3(self, item):
for production in self:
if not production.bom_id or not production.product_id:
continue
workorders_values = [] workorders_values = []
product_qty = production.product_uom_id._compute_quantity(production.product_qty, # production = self.env['mrp.production'].browse(production_id)
production.bom_id.product_uom_id) product_qty = self.product_uom_id._compute_quantity(self.product_qty,
exploded_boms, dummy = production.bom_id.explode(production.product_id, self.bom_id.product_uom_id)
product_qty / production.bom_id.product_qty, exploded_boms, dummy = self.bom_id.explode(self.product_id,
picking_type=production.bom_id.picking_type_id) product_qty / self.bom_id.product_qty,
picking_type=self.bom_id.picking_type_id)
for bom, bom_data in exploded_boms: 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 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[ if not (bom.operation_ids and (not bom_data['parent_line'] or bom_data[
@@ -787,36 +791,80 @@ class MrpProduction(models.Model):
continue continue
workorders_values += [{ workorders_values += [{
'name': operation.name, 'name': operation.name,
'production_id': production.id, 'production_id': self.id,
'workcenter_id': operation.workcenter_id.id, 'workcenter_id': operation.workcenter_id.id,
'product_uom_id': production.product_uom_id.id, 'product_uom_id': self.product_uom_id.id,
'operation_id': operation.id, 'operation_id': operation.id,
'state': 'pending', 'state': 'pending',
}] }]
if production.product_id.categ_id.type in ['成品', '坯料']: if self.product_id.categ_id.type in ['成品', '坯料']:
# # 根据工序设计生成工单 # # 根据工序设计生成工单
technology_design_ids = sorted(production.technology_design_ids, key=lambda x: x.sequence) technology_design_ids = sorted(self.technology_design_ids, key=lambda x: x.sequence)
for route in technology_design_ids: for route in technology_design_ids:
workorder_has = self.env['mrp.workorder'].search( workorder_has = self.env['mrp.workorder'].search(
[('technology_design_id', '=', route.id), ('production_id', '=', production.id)]) [('technology_design_id', '=', route.id), ('production_id', '=', self.id)])
if not workorder_has: if not workorder_has:
if route.route_id.routing_type not in ['表面工艺']: if route.route_id.routing_type not in ['表面工艺']:
workorders_values.append( workorders_values.append(
self.env['mrp.workorder'].json_workorder_str(production, route)) self.env['mrp.workorder'].json_workorder_str(self, route))
else: else:
product_production_process = self.env['product.template'].search( product_production_process = self.env['product.template'].search(
[('server_product_process_parameters_id', '=', route.process_parameters_id.id)]) [('server_product_process_parameters_id', '=', route.process_parameters_id.id)])
workorders_values.append( workorders_values.append(
self.env[ self.env[
'mrp.workorder']._json_workorder_surface_process_str( 'mrp.workorder']._json_workorder_surface_process_str(
production, route, product_production_process.seller_ids[0].partner_id.id)) self, route, product_production_process.seller_ids[0].partner_id.id))
production.workorder_ids = workorders_values 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: for workorder in production.workorder_ids:
workorder.duration_expected = workorder._get_duration_expected() 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):
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
# 提交每个生产任务到线程池
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): def get_subcontract_pick_purchase(self,productions):
production_all = self.sorted(lambda x: x.id) production_all = productions.sorted(lambda x: x.id)
product_id_to_production_names = {} product_id_to_production_names = {}
grouped_product_ids = {k: list(g) for k, g in grouped_product_ids = {k: list(g) for k, g in
groupby(production_all, key=lambda x: x.product_id.id)} groupby(production_all, key=lambda x: x.product_id.id)}
@@ -825,9 +873,10 @@ class MrpProduction(models.Model):
sorted_workorders = None sorted_workorders = None
for production in production_all: for production in production_all:
proc_workorders = [] proc_workorders = []
process_parameter_workorder = self.env['mrp.workorder'].search( process_parameter_workorder=production.workorder_ids.filtered(lambda w: w.surface_technics_parameters_id and w.is_subcontract and w.state!='cancel')
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), # process_parameter_workorder = self.env['mrp.workorder'].search(
('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc') # [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id),
# ('is_subcontract', '=', True), ('state', '!=', 'cancel')], order='sequence asc')
if process_parameter_workorder: if process_parameter_workorder:
# 将这些特殊表面工艺工单的采购单与调拨单置为失效 # 将这些特殊表面工艺工单的采购单与调拨单置为失效
for workorder in process_parameter_workorder: for workorder in process_parameter_workorder:
@@ -942,11 +991,7 @@ class MrpProduction(models.Model):
if purchase_order_line: if purchase_order_line:
line.unlink() line.unlink()
def _reset_work_order_sequence(self): def _process_reset_work_order_sequence(self,rec):
"""
工单工序排序方法(新)
"""
for rec in self:
workorder_ids = rec.workorder_ids workorder_ids = rec.workorder_ids
technology_design_ids = rec.technology_design_ids technology_design_ids = rec.technology_design_ids
if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')): if workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')):
@@ -983,7 +1028,12 @@ class MrpProduction(models.Model):
key=lambda w: w.sequence).sequence key=lambda w: w.sequence).sequence
for cw in cancel_work_ids: for cw in cancel_work_ids:
cw.sequence = sequence + 1 cw.sequence = sequence + 1
def _reset_work_order_sequence(self,productions):
"""
工单工序排序方法(新)
"""
for rec in productions:
self._process_reset_work_order_sequence(rec)
def _reset_work_order_sequence_1(self): def _reset_work_order_sequence_1(self):
""" """
工单工序排序方法(旧) 工单工序排序方法(旧)
@@ -1079,9 +1129,9 @@ class MrpProduction(models.Model):
# 创建工单并进行排序 # 创建工单并进行排序
def _create_workorder(self, item): def _create_workorder(self, item):
self._create_workorder3(item) productions = self._create_workorder3(item)
self._reset_work_order_sequence() self._reset_work_order_sequence(productions)
return True return productions
def production_process(self, pro_plan): def production_process(self, pro_plan):
type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False} type_map = {'装夹预调': False, 'CNC加工': False, '解除装夹': False}
@@ -1756,3 +1806,5 @@ class sf_processing_panel(models.Model):
name = fields.Char('加工面') name = fields.Char('加工面')
active = fields.Boolean('有效', default=True) active = fields.Boolean('有效', default=True)

View File

@@ -1,7 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of YiZuo. See LICENSE file for full copyright and licensing details. # Part of YiZuo. See LICENSE file for full copyright and licensing details.
import logging import cProfile
from itertools import groupby import io
import pstats
from concurrent.futures import ThreadPoolExecutor
from odoo import models, api, fields, _ from odoo import models, api, fields, _
@@ -13,17 +15,12 @@ class ProductionTechnologyWizard(models.TransientModel):
origin = fields.Char(string='源单据') origin = fields.Char(string='源单据')
is_technology_confirm = fields.Boolean(default=True) is_technology_confirm = fields.Boolean(default=True)
def confirm(self): def _process_production(self,productions,technology_designs):
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'),
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.env['sf.technology.design'].sudo().search(
[('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: 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: if production != self.production_id:
self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs, self.env['sf.technology.design'].sudo().unified_procedure_multiple_work_orders(technology_designs,
production) production)
@@ -38,7 +35,8 @@ class ProductionTechnologyWizard(models.TransientModel):
# 工单采购单外协出入库单皆需取消 # 工单采购单外协出入库单皆需取消
domain = [('production_id', '=', special.production_id.id)] domain = [('production_id', '=', special.production_id.id)]
if special.process_parameters_id: if special.process_parameters_id:
domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id), ('state', '!=', 'cancel')] domain += [('surface_technics_parameters_id', '=', special.process_parameters_id.id),
('state', '!=', 'cancel')]
else: else:
domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')] domain += [('technology_design_id', '=', special.id), ('state', '!=', 'cancel')]
workorder = self.env['mrp.workorder'].search(domain) workorder = self.env['mrp.workorder'].search(domain)
@@ -60,7 +58,8 @@ class ProductionTechnologyWizard(models.TransientModel):
else: else:
if special.production_id.workorder_ids: if special.production_id.workorder_ids:
workorder = self.env['mrp.workorder'].search( workorder = self.env['mrp.workorder'].search(
[('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id), ('state', '!=', 'cancel')]) [('technology_design_id', '=', special.id), ('production_id', '=', special.production_id.id),
('state', '!=', 'cancel')])
if not workorder: if not workorder:
if special.route_id.routing_type == '表面工艺': if special.route_id.routing_type == '表面工艺':
product_production_process = self.env['product.template'].search( product_production_process = self.env['product.template'].search(
@@ -81,14 +80,34 @@ class ProductionTechnologyWizard(models.TransientModel):
else: else:
if workorder.blocked_by_workorder_ids: if workorder.blocked_by_workorder_ids:
workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0] workorder.blocked_by_workorder_ids = blocked_by_workorder_ids[0]
productions._create_workorder(False) 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'),
('product_id', '=', self.production_id.product_id.id)]
else:
domain = [('id', '=', self.production_id.id)]
technology_designs = self.env['sf.technology.design'].sudo().search(
[('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)
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 == '成品': if self.production_id.product_id.categ_id.type == '成品':
productions.get_subcontract_pick_purchase() self.production_id.get_subcontract_pick_purchase(productions)
productions.is_adjust = False
for item in productions: for item in productions:
item.is_adjust = False
workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted( workorder = item.workorder_ids.filtered(lambda wo: wo.state not in ('cancel')).sorted(
key=lambda a: a.sequence) key=lambda a: a.sequence)
if workorder[0].state in ['pending']: if workorder[0].state in ['pending']:
if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程': if workorder[0].production_id.product_id.categ_id.type == '成品' and item.programming_state != '已编程':
workorder[0].state = 'waiting' 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 return productions