# -*- coding: utf-8 -*- # Part of YiZuo. See LICENSE file for full copyright and licensing details. import logging from odoo.exceptions import UserError, ValidationError from datetime import datetime from odoo import models, api, fields, _ from odoo.tools import groupby class ReworkWizard(models.TransientModel): _name = 'sf.rework.wizard' _description = '返工向导' workorder_id = fields.Many2one('mrp.workorder', string='工单') product_id = fields.Many2one('product.product') production_id = fields.Many2one('mrp.production', string='制造订单号') workorder_ids = fields.Many2many('mrp.workorder', 'rework_wizard_to_work_order', string='所有工单', domain="[('production_id', '=', production_id),('state','=','done')]") hidden_workorder_ids = fields.Char('') rework_reason = fields.Selection( [("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"), ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因") detailed_reason = fields.Text('详细原因') routing_type = fields.Selection([ ('装夹预调', '装夹预调'), ('CNC加工', 'CNC加工'), ('解除装夹', '解除装夹'), ('切割', '切割'), ('表面工艺', '表面工艺'), ('线切割', '线切割'), ('人工线下加工', '人工线下加工')], string="工序类型") # 根据工单的加工面来显示 processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面") is_reprogramming = fields.Boolean(string='申请重新编程', default=False) is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False) is_clamp_measure = fields.Boolean(string='保留装夹测量数据', default=True) is_clamping = fields.Boolean(string='制造订单是否存在装夹预调工单') reprogramming_num = fields.Integer('重新编程次数', default=0) programming_state = fields.Selection( [('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'), ('已下发', '已下发')], string='编程状态') tool_state = fields.Selection(string='功能刀具状态', related='production_id.tool_state') @api.onchange('hidden_workorder_ids') def _onchange_hidden_workorder_ids(self): for item in self: if item.hidden_workorder_ids not in ['', None, False]: hidden_workorder_list = item.hidden_workorder_ids.split(',') # 获取加工面对应需要返工的工单 rw_ids = item.workorder_ids.filtered( lambda w: str(w.ids[0]) in hidden_workorder_list and w.processing_panel not in ['', None, False]) grouped_rw_ids = {key: list(group) for key, group in groupby(rw_ids, key=lambda w: w.processing_panel)} for panel, panel_rw_ids in grouped_rw_ids.items(): work_ids = item.workorder_ids.filtered(lambda w: w.state == 'done' and w.processing_panel == panel) if len(work_ids) == 3 and len(panel_rw_ids) != 3: for work_id in work_ids: if work_id not in panel_rw_ids: hidden_workorder_list.append(str(work_id.ids[0])) elif len(work_ids) == 2 and len(panel_rw_ids) < 2 and panel_rw_ids[0].name == 'CNC加工': if rw_ids.filtered(lambda w: (w.sequence < panel_rw_ids[0].sequence and w.processing_panel != panel_rw_ids[0].processing_panel)): hidden_workorder_list.append(str(work_ids.filtered( lambda w: (w.processing_panel == panel_rw_ids[0].processing_panel and w.name == '装夹预调')).ids[0])) hidden_workorder_list.sort() item.hidden_workorder_ids = ','.join(hidden_workorder_list) def efficacy_rework_wo(self, wk_ids): """限制判断 """ # 判断检测结果待处理所对应的工单是否勾选 result_ids = self.production_id.detection_result_ids.filtered(lambda dr: dr.handle_result == '待处理') work_id_list = [] if result_ids: work_id_list = [self.workorder_ids.filtered( lambda wk: (wk.name == result_id.routing_type and wk.processing_panel == result_id.processing_panel and wk.state == 'done')).id for result_id in result_ids] if len(wk_ids.filtered(lambda wk: wk.id in work_id_list)) != len(work_id_list): raise ValidationError('存在【检测结果】为【待处理】所对应的工单未进行勾选!!!') # 获取已完成的标准工单 grouped_rw_ids = {key: list(group) for key, group in groupby(wk_ids, key=lambda w: w.processing_panel)} for panel, panel_rw_ids in grouped_rw_ids.items(): # 1、当制造订单内ZM面的工单都已完成时,返工勾选工序时只能勾选上ZM面的所有工序进行返工 work_ids = self.workorder_ids.filtered(lambda w: w.state == 'done' and w.processing_panel == panel) if len(work_ids) == 3 and len(panel_rw_ids) != 3: raise ValidationError( '因为[%s]面的工单已全部完成,如果要对[%s]面的工单进行返工,请勾选这个面的所有工单。' % (panel, panel)) # 2、当FM工单在CNC工单进行选择返工,并将已全部完成的ZM面工序全部勾选上时,FM工单上所有的已完成的工单(装夹预调工单)也必须进行勾选 if not wk_ids.filtered(lambda wk: wk.name == '装夹预调' and wk.processing_panel == panel): if wk_ids.filtered(lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel): sequence_max = wk_ids.filtered( lambda wk: wk.name == 'CNC加工' and wk.processing_panel == panel).sequence for wk_id in wk_ids.filtered(lambda wk: wk.sequence < sequence_max): if len(wk_ids.filtered(lambda wk: wk.processing_panel == wk_id.processing_panel)) == 3: raise ValidationError( '由于在[%s]面之前存在整个面进行了勾选的情况,所以在勾选了[%s]面的【CNC加工】工单的时,请勾选[%s]面的装夹预调工单!' % ( panel, panel, panel)) def confirm(self): if self.routing_type in ['装夹预调', 'CNC加工']: self.is_clamp_measure = False self.workorder_id.is_rework = True self.production_id.write({'detection_result_ids': [(0, 0, { 'rework_reason': self.rework_reason, 'detailed_reason': self.detailed_reason, 'processing_panel': self.workorder_id.processing_panel, 'routing_type': self.workorder_id.routing_type, 'handle_result': '待处理' if self.workorder_id.test_results == '返工' or self.workorder_id.is_rework is True else '', 'test_results': '返工' if not self.routing_type == 'CNC加工' else self.workorder_id.test_results, 'test_report': self.workorder_id.detection_report})]}) self.workorder_id.button_finish() else: if self.hidden_workorder_ids: hidden_workorder_list = self.hidden_workorder_ids.split(',') rework_workorder_ids = self.workorder_ids.filtered(lambda w: str(w.id) in hidden_workorder_list) # 调用效验方法 self.efficacy_rework_wo(rework_workorder_ids) else: raise ValidationError('请选择返工工单!!!') if rework_workorder_ids: # 限制 # 1、单独返工CNC工单则不解绑托盘RFID,如单独返工装夹预调工单,则自动解绑托盘RFID; # 2、返工CNC工单和装夹预调工单则自动解绑RFID clamp_workorder_ids = rework_workorder_ids.filtered(lambda rp: rp.routing_type == '装夹预调') if clamp_workorder_ids: for clamp_workorder_id in clamp_workorder_ids: self.production_id.workorder_ids.filtered( lambda wk: wk.processing_panel == clamp_workorder_id.processing_panel).write( {'rfid_code': None}) # 返工工单状态设置为【返工】 rework_workorder_ids.write({'state': 'rework'}) # 查询返工工单对应的工艺设计记录,并调用方法拼接数据,用于创建新的工单 workorders_values = [] for work in rework_workorder_ids: route = self.production_id.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) or (item.route_id == work.routing_work_center_id and not work.processing_panel and not work.surface_technics_parameters_id)) if route: work_list = self.env['mrp.workorder'].json_workorder_str(self.production_id, route[0]) work_list[2].update({'tag_type': '重新加工', 'sequence': 0}) workorders_values.append(work_list) # 创建新工单,并进行返工配置的相关操作 if workorders_values: # 创建新工单、工序排序、完成检测结果单据 self.production_id.write({'workorder_ids': workorders_values, 'is_rework': True}) new_work_ids = self.production_id.workorder_ids.filtered(lambda kw: kw.sequence == 0) new_pre_workorder_ids = self.production_id.workorder_ids.filtered( lambda kw: kw.routing_type == '装夹预调' and kw.sequence == 0) self.production_id._reset_work_order_sequence() # ====新工单绑定rfid=== for new_work_id in new_work_ids: if new_work_id.routing_type in ['CNC加工', '解除装夹']: new_work_id.write({'rfid_code': self.production_id.workorder_ids.filtered( lambda wk: wk.sequence == new_work_id.sequence - 1).rfid_code}) self.production_id.detection_result_ids.filtered( lambda ap1: ap1.handle_result == '待处理').write({'handle_result': '已处理'}) panels = [] # 返工的加工面 # ===================保留装夹测量数据===================== if self.is_clamp_measure and clamp_workorder_ids and new_pre_workorder_ids: for new_pre_workorder in new_pre_workorder_ids: if new_pre_workorder.processing_panel and new_pre_workorder.processing_panel not in panels: panels.append(new_pre_workorder.processing_panel) for rework_clamp_workorder in clamp_workorder_ids: if new_pre_workorder.sequence == rework_clamp_workorder.sequence + 1: new_pre_workorder.write( {'X1_axis': rework_clamp_workorder.X1_axis, 'Y1_axis': rework_clamp_workorder.Y1_axis , 'Z1_axis': rework_clamp_workorder.Z1_axis, 'X2_axis': rework_clamp_workorder.X2_axis , 'Y2_axis': rework_clamp_workorder.Y2_axis, 'Z2_axis': rework_clamp_workorder.Z2_axis , 'X3_axis': rework_clamp_workorder.X3_axis, 'Y3_axis': rework_clamp_workorder.Y3_axis , 'Z3_axis': rework_clamp_workorder.Z3_axis, 'X4_axis': rework_clamp_workorder.X4_axis , 'Y4_axis': rework_clamp_workorder.Y4_axis, 'Z4_axis': rework_clamp_workorder.Z4_axis , 'X5_axis': rework_clamp_workorder.X5_axis, 'Y5_axis': rework_clamp_workorder.Y5_axis , 'Z5_axis': rework_clamp_workorder.Z5_axis, 'X6_axis': rework_clamp_workorder.X6_axis , 'Y6_axis': rework_clamp_workorder.Y6_axis, 'Z6_axis': rework_clamp_workorder.Z6_axis , 'X7_axis': rework_clamp_workorder.X7_axis, 'Y7_axis': rework_clamp_workorder.Y7_axis , 'Z7_axis': rework_clamp_workorder.Z7_axis, 'X8_axis': rework_clamp_workorder.X8_axis , 'Y8_axis': rework_clamp_workorder.Y8_axis, 'Z8_axis': rework_clamp_workorder.Z8_axis , 'X9_axis': rework_clamp_workorder.X9_axis, 'Y9_axis': rework_clamp_workorder.Y9_axis , 'Z9_axis': rework_clamp_workorder.Z9_axis, 'X10_axis': rework_clamp_workorder.X10_axis , 'Y10_axis': rework_clamp_workorder.Y10_axis, 'Z10_axis': rework_clamp_workorder.Z10_axis , 'X_deviation_angle': rework_clamp_workorder.X_deviation_angle, 'material_center_point': rework_clamp_workorder.material_center_point }) break # =====================不申请重新编程==================== if self.is_reprogramming is False: if self.programming_state in ['已编程', '已下发']: if self.reprogramming_num >= 1 and self.programming_state == '已编程': for panel_name in panels: self.production_id.get_new_program(panel_name) if self.reprogramming_num >= 0 and self.programming_state == '已下发': # ============= 处理CNC加工加工工单的 CNC程序和cmm程序 信息============= for cnc_work in new_work_ids.filtered(lambda wk: wk.name == 'CNC加工'): ret = {'programming_list': []} old_cnc_rework = max(self.production_id.workorder_ids.filtered( lambda crw: crw.processing_panel == cnc_work.processing_panel and crw.state == 'rework' and crw.routing_type == 'CNC加工'), key=lambda w: w.create_date ) # 获取当前工单的CNC程序和cmm程序 if old_cnc_rework.cnc_ids: for item_line in old_cnc_rework.cnc_ids: vals = { 'sequence_number': item_line.sequence_number, 'program_name': item_line.program_name, 'cutting_tool_name': item_line.cutting_tool_name, 'cutting_tool_no': item_line.cutting_tool_no, 'processing_type': item_line.processing_type, 'margin_x_y': item_line.margin_x_y, 'margin_z': item_line.margin_z, 'depth_of_processing_z': item_line.depth_of_processing_z, 'cutting_tool_extension_length': item_line.cutting_tool_extension_length, 'estimated_processing_time': item_line.estimated_processing_time, 'cutting_tool_handle_type': item_line.cutting_tool_handle_type, 'program_path': item_line.program_path, 'ftp_path': item_line.program_path, 'processing_panel': cnc_work.processing_panel, 'program_create_date': datetime.strftime(item_line.program_create_date, '%Y-%m-%d %H:%M:%S'), 'remark': item_line.remark } ret['programming_list'].append(vals) for cmm_line in old_cnc_rework.cmm_ids: vals = { 'sequence_number': cmm_line.sequence_number, 'program_name': cmm_line.program_name, 'program_path': cmm_line.program_path, 'ftp_path': cmm_line.program_path, 'processing_panel': cnc_work.processing_panel, 'program_create_date': datetime.strftime( cmm_line.program_create_date, '%Y-%m-%d %H:%M:%S') } ret['programming_list'].append(vals) # 获取新的 new_cnc_workorder = self.production_id.workorder_ids.filtered( lambda ap1: ap1.processing_panel == cnc_work.processing_panel and ap1.state not in ( 'rework', 'done') and ap1.routing_type == 'CNC加工' ) if not new_cnc_workorder.cnc_ids: new_cnc_workorder.write({ 'cnc_ids': new_cnc_workorder.cnc_ids.sudo()._json_cnc_processing( cnc_work.processing_panel, ret), 'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program( cnc_work.processing_panel, ret), 'cnc_worksheet': old_cnc_rework.cnc_worksheet}) # ========== 处理装夹预调 【装夹图纸】 数据 ================ for new_pre_work in new_pre_workorder_ids: pre_rework = max(self.production_id.workorder_ids.filtered( lambda pr: (pr.processing_panel == new_pre_work.processing_panel and pr.state in ['rework'] and pr.routing_type == '装夹预调')), key=lambda w1: w1.create_date) new_pre_work.write( {'processing_drawing': pre_rework.processing_drawing}) self.production_id.write({'state': 'progress', 'is_rework': False}) elif self.programming_state in ['待编程', '编程中']: self.production_id.write( {'programming_state': '编程中', 'work_state': '编程中', 'is_rework': True}) # ================================================= if self.production_id.state == 'progress': if self.programming_state: self.production_id.write({'programming_state': '已编程', 'work_state': '已编程'}) if self.reprogramming_num >= 1 and self.programming_state == '已编程': productions_not_delivered = self.env['mrp.production'].search( [('programming_no', '=', self.production_id.programming_no), ('programming_state', '=', '已编程未下发')]) if productions_not_delivered: productions_not_delivered.write( {'state': 'progress', 'programming_state': '已编程', 'work_state': '已编程', 'is_rework': False}) # ==================申请重新编程======================= if self.is_reprogramming is True: self.production_id.update_programming_state(trigger_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S')) self.production_id.write( {'programming_state': '编程中', 'work_state': '编程中', 'state': 'progress'}) # ================= 返工完成,制造订单状态置为加工中 ============== self.production_id.write({'state': 'progress', 'is_rework': False}) @api.onchange('production_id') def onchange_processing_panel_id(self): for item in self: domain = [('id', '=', False)] production_id = item.production_id if production_id: if self.env.user.has_group('sf_base.group_sf_order_user'): panel_ids = [] panel_arr = production_id.product_id.model_processing_panel if panel_arr is False: break for p in production_id.detection_result_ids.filtered( lambda ap1: ap1.handle_result == '待处理'): if p.processing_panel is not False and p.processing_panel not in panel_arr: panel_arr += ','.join(p.processing_panel) for item in panel_arr.split(','): panel = self.env['sf.processing.panel'].search( [('name', 'ilike', item)]) if panel: panel_ids.append(panel.id) domain = {'processing_panel_id': [('id', 'in', panel_ids)]} return {'domain': domain}