diff --git a/sf_manufacturing/__manifest__.py b/sf_manufacturing/__manifest__.py index 54538a7d..985b87a4 100644 --- a/sf_manufacturing/__manifest__.py +++ b/sf_manufacturing/__manifest__.py @@ -14,9 +14,11 @@ 'data': [ 'data/stock_data.xml', 'data/empty_racks_data.xml', + 'data/panel_data.xml', 'security/group_security.xml', 'security/ir.model.access.csv', 'wizard/workpiece_delivery_views.xml', + 'wizard/rework_wizard_views.xml', 'views/mrp_views_menus.xml', 'views/stock_lot_views.xml', 'views/mrp_production_addional_change.xml', diff --git a/sf_manufacturing/controllers/controllers.py b/sf_manufacturing/controllers/controllers.py index a67be07d..6624e709 100644 --- a/sf_manufacturing/controllers/controllers.py +++ b/sf_manufacturing/controllers/controllers.py @@ -220,21 +220,22 @@ class Manufacturing_Connect(http.Controller): return json.JSONEncoder().encode(res) # workorder.write({'date_finished': datetime.now()}) if ret['IsComplete'] is True: - workorder.button_finish() - # workorder.process_state = '待解除装夹' - # workorder.sudo().production_id.process_state = '待解除装夹' + workorder.write({'date_finished': datetime.now()}) - # 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态 - # if workorder.date_finished: - # request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write( - # {'actual_end_time': workorder.date_finished, - # 'state': 'finished'}) - # production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)]) - # if production_obj: - # production_obj.sudo().work_order_state = '已完成' - # production_obj.write({'state': 'done'}) - # request.env['sale.order'].sudo().search( - # [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'}) + # workorder.process_state = '待解除装夹' + # workorder.sudo().production_id.process_state = '待解除装夹' + + # 根据工单的实际结束时间修改排程单的结束时间、状态,同时修改销售订单的状态 + # if workorder.date_finished: + # request.env['sf.production.plan'].sudo().search([('production_id', '=', production_id)]).write( + # {'actual_end_time': workorder.date_finished, + # 'state': 'finished'}) + # production_obj = request.env['mrp.production'].sudo().search([('name', '=', production_id)]) + # if production_obj: + # production_obj.sudo().work_order_state = '已完成' + # production_obj.write({'state': 'done'}) + # request.env['sale.order'].sudo().search( + # [('name', '=', production_obj.origin)]).write({'schedule_status': 'to deliver'}) except Exception as e: res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} @@ -540,6 +541,7 @@ class Manufacturing_Connect(http.Controller): ('processing_panel', '=', order.processing_panel)]) if panel_workorder: panel_workorder.write({'production_line_state': '已下产线'}) + workorder.write({'state': 'to be detected'}) workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( [ ('rfid_code', '=', rfid_code), ('type', '=', '下产线'), diff --git a/sf_manufacturing/data/panel_data.xml b/sf_manufacturing/data/panel_data.xml new file mode 100644 index 00000000..0bdf4aa3 --- /dev/null +++ b/sf_manufacturing/data/panel_data.xml @@ -0,0 +1,24 @@ + + + + + ZM + + + + FM + + + YC + + + ZC + + + QC + + + HC + + + \ No newline at end of file diff --git a/sf_manufacturing/data/stock_data.xml b/sf_manufacturing/data/stock_data.xml index 07d59708..9220d827 100644 --- a/sf_manufacturing/data/stock_data.xml +++ b/sf_manufacturing/data/stock_data.xml @@ -1,6 +1,16 @@ + + 返工且编程中的制造订单定时获取Cloud编程单状态 + + code + model._cron_get_programming_state() + 3 + minutes + -1 + + 工序编码规则 mrp.routing.workcenter diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 09f55db7..6554a18f 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import base64 import logging +import json +import os import re import requests from itertools import groupby @@ -24,6 +26,7 @@ class MrpProduction(models.Model): work_order_state = fields.Selection([('未排', '未排'), ('已排', '已排'), ('已完成', '已完成')], string='工单状态', default='未排') + detection_result_ids = fields.One2many('sf.detection.result', 'production_id', '检测报告') # state = fields.Selection(selection_add=[ # ('pending_scheduling', '待排程'), # ('pending_processing', '待加工'), @@ -34,9 +37,10 @@ class MrpProduction(models.Model): ('confirmed', '待排程'), ('pending_cam', '待加工'), ('progress', '加工中'), + ('rework', '返工'), ('to_close', 'To Close'), ('done', 'Done'), - ('cancel', 'Cancelled')], string='State', + ('cancel', '报废')], string='State', compute='_compute_state', copy=False, index=True, readonly=True, store=True, tracking=True, help=" * Draft: The MO is not confirmed yet.\n" @@ -49,12 +53,15 @@ class MrpProduction(models.Model): check_status = fields.Boolean(string='启用状态', default=False, readonly=True) active = fields.Boolean(string='已归档', default=True) programming_no = fields.Char('编程单号') + reprogramming_num = fields.Integer('重新编程次数', default=0) work_state = fields.Char('业务状态') programming_state = fields.Selection( - [('编程中', '编程中'), ('已编程', '已编程')], string='编程状态', tracking=True) + [('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发')], string='编程状态', + tracking=True) glb_file = fields.Binary("glb模型文件") production_line_id = fields.Many2one('sf.production.line', string='生产线', tracking=True) plan_start_processing_time = fields.Datetime('计划开始加工时间') + is_rework = fields.Boolean(string='是否返工', default=False) # production_line_state = fields.Selection( # [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')], # string='上/下产线', default='待上产线', tracking=True) @@ -125,6 +132,15 @@ class MrpProduction(models.Model): # if production.state == 'pending_cam': # if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')): # production.state = 'done' + if any( + ( + wo.test_results == '返工' and wo.state == 'done' and production.programming_state == '已编程') or ( + wo.state == 'rework' and production.programming_state == '编程中') or ( + wo.is_rework is True and wo.state == 'done' and production.programming_state in ['编程中', + '已编程']) + for wo in + production.workorder_ids): + production.state = 'rework' def action_check(self): """ @@ -155,27 +171,63 @@ class MrpProduction(models.Model): for production in self: production.maintenance_count = len(production.request_ids) - # 制造订单报废:编程单更新 - def updateCNC(self): + # 获取cloud编程单的状态 + def _cron_get_programming_state(self): try: - res = {'production_no': self.name, 'programming_no': self.programming_no, - 'order_no': self.origin} + if not self: + reproduction = self.env['mrp.production'].search( + [('state', '=', 'rework'), ('programming_state', '=', '编程中')]) + else: + reproduction = self + if reproduction: + programming_no = [item.programming_no for item in reproduction] + programming_no_str = ','.join(programming_no) + res = {'programming_no': programming_no_str} + logging.info('res=%s:' % res) + configsettings = self.env['res.config.settings'].get_values() + config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) + url = '/api/intelligent_programming/get_state' + config_url = configsettings['sf_url'] + url + ret = requests.post(config_url, json=res, data=None, headers=config_header) + ret = ret.json() + result = json.loads(ret['result']) + logging.info('cron_get_programming_state-ret:%s' % result) + if result['status'] == 1: + for item in result['programming_list']: + if not self: + production = self.env['mrp.production'].search( + [('state', '=', 'rework'), ('programming_no', '=', item['programming_no'])]) + if production: + production.write({'programming_state': '已编程未下发' if item[ + 'programming_state'] == '已编程' else '编程中'}) + else: + return item['programming_state'] + + else: + raise UserError(ret['message']) + except Exception as e: + logging.info('cron_get_programming_state error:%s' % e) + + # 编程单更新 + def update_programming_state(self): + try: + res = {'programming_no': self.programming_no, 'manufacturing_type': 'rework'} logging.info('res=%s:' % res) configsettings = self.env['res.config.settings'].get_values() config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) - url = '/api/intelligent_programming/update_intelligent_programmings' + url = '/api/intelligent_programming/reset_state_again' config_url = configsettings['sf_url'] + url - res['token'] = configsettings['token'] - ret = requests.post(config_url, json={}, data=res, headers=config_header) + ret = requests.post(config_url, json=res, data=None, headers=config_header) ret = ret.json() - logging.info('updateCNC-ret:%s' % ret) - if ret['status'] == 1: - self.write({'work_state': '已编程'}) + result = json.loads(ret['result']) + logging.info('update_programming_state-ret:%s' % result) + if result['status'] == 1: + self.write({'is_rework': True}) else: raise UserError(ret['message']) except Exception as e: - logging.info('updateCNC error:%s' % e) - raise UserError("更新程单失败,请联系管理员") + logging.info('update_programming_state error:%s' % e) + raise UserError("更新编程单状态失败,请联系管理员") # cnc程序获取 def fetchCNC(self, production_names): @@ -201,7 +253,7 @@ class MrpProduction(models.Model): 'material_type_code': self.env['sf.materials.model'].search( [('id', '=', cnc.product_id.materials_type_id.id)]).materials_no, 'machining_processing_panel': cnc.product_id.model_processing_panel, - 'machining_precision': cnc.product_id.model_machining_precision, + 'machining_precision': '', 'embryo_long': cnc.product_id.bom_ids.bom_line_ids.product_id.length, 'embryo_height': cnc.product_id.bom_ids.bom_line_ids.product_id.height, 'embryo_width': cnc.product_id.bom_ids.bom_line_ids.product_id.width, @@ -524,70 +576,90 @@ class MrpProduction(models.Model): def _reset_work_order_sequence(self): for rec in self: - sequence_list = {} + workorder_ids = rec.workorder_ids.filtered(lambda item: item.state in ('返工', 'rework')) # 产品模型类型 model_type_id = rec.product_id.product_model_type_id # 产品加工面板 model_processing_panel = rec.product_id.model_processing_panel - if model_type_id: - if model_processing_panel: - tmpl_num = 1 - panel_list = model_processing_panel.split(',') - for panel in panel_list: - panel_sequence_list = {} - # 成品工序 - product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids - if product_routing_tmpl_ids: - for tmpl_id in product_routing_tmpl_ids: - panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) - tmpl_num += 1 - sequence_list.update({panel: panel_sequence_list}) - # 表面工艺工序 - # 模型类型的表面工艺工序模版 - surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids - # 产品选择的表面工艺 - model_process_parameters_ids = rec.product_id.model_process_parameters_ids - process_dict = {} - if model_process_parameters_ids: - for process_parameters_id in model_process_parameters_ids: - process_id = process_parameters_id.process_id - for surface_tmpl_id in surface_tmpl_ids: - if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: - surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name - process_dict.update({int(process_id.category_id.code): '%s-%s' % ( - surface_tmpl_name, process_parameters_id.name)}) - process_list = sorted(process_dict.keys()) - for process_num in process_list: - sequence_list.update({process_dict.get(process_num): tmpl_num}) - tmpl_num += 1 - # 坯料工序 - tmpl_num = 1 - embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids - if embryo_routing_tmpl_ids: - for tmpl_id in embryo_routing_tmpl_ids: - sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + if not workorder_ids: + sequence_list = {} + if model_type_id: + if model_processing_panel: + tmpl_num = 1 + panel_list = model_processing_panel.split(',') + for panel in panel_list: + panel_sequence_list = {} + # 成品工序 + product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids + if product_routing_tmpl_ids: + for tmpl_id in product_routing_tmpl_ids: + panel_sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + tmpl_num += 1 + sequence_list.update({panel: panel_sequence_list}) + # 表面工艺工序 + # 模型类型的表面工艺工序模版 + surface_tmpl_ids = model_type_id.surface_technics_routing_tmpl_ids + # 产品选择的表面工艺 + model_process_parameters_ids = rec.product_id.model_process_parameters_ids + process_dict = {} + if model_process_parameters_ids: + for process_parameters_id in model_process_parameters_ids: + process_id = process_parameters_id.process_id + for surface_tmpl_id in surface_tmpl_ids: + if process_id == surface_tmpl_id.route_workcenter_id.surface_technics_id: + surface_tmpl_name = surface_tmpl_id.route_workcenter_id.name + process_dict.update({int(process_id.category_id.code): '%s-%s' % ( + surface_tmpl_name, process_parameters_id.name)}) + process_list = sorted(process_dict.keys()) + for process_num in process_list: + sequence_list.update({process_dict.get(process_num): tmpl_num}) tmpl_num += 1 + # 坯料工序 + tmpl_num = 1 + embryo_routing_tmpl_ids = model_type_id.embryo_routing_tmpl_ids + if embryo_routing_tmpl_ids: + for tmpl_id in embryo_routing_tmpl_ids: + sequence_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) + tmpl_num += 1 + else: + raise ValidationError('该产品【加工面板】为空!') else: - raise ValidationError('该产品【加工面板】为空!') + raise ValidationError('该产品没有选择【模版类型】!') - else: - raise ValidationError('该产品没有选择【模版类型】!') - - for work in rec.workorder_ids: - if sequence_list.get(work.name): - work.sequence = sequence_list[work.name] - elif sequence_list.get(work.processing_panel): - processing_panel = sequence_list.get(work.processing_panel) - if processing_panel.get(work.name): - work.sequence = processing_panel[work.name] + for work in rec.workorder_ids: + if sequence_list.get(work.name): + work.sequence = sequence_list[work.name] + elif sequence_list.get(work.processing_panel): + processing_panel = sequence_list.get(work.processing_panel) + if processing_panel.get(work.name): + work.sequence = processing_panel[work.name] + else: + raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) else: raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) - else: - raise ValidationError('工序【%s】在产品选择的模版类型中不存在!' % work.name) - # if work.name == '获取CNC加工程序': - # work.button_start() - # #work.fetchCNC() - # work.button_finish() + # 当单个面触发返工时,将新生成的工单插入到返工工单下方,并且后面的所以工单工序重排 + elif rec.workorder_ids.filtered(lambda item: item.sequence == 0): + # 获取新增的返工工单 + work_ids = rec.workorder_ids.filtered(lambda item: item.sequence == 0) + # 获取当前返工面最后一个工单工序 + sequence_max = sorted( + rec.workorder_ids.filtered(lambda item: item.processing_panel == work_ids[0].processing_panel), + key=lambda item: item.sequence, reverse=True)[0].sequence + # 对当前返工工单之后的工单工序进行重排 + work_order_ids = rec.workorder_ids.filtered(lambda item: item.sequence > sequence_max) + for work_id in work_order_ids: + work_id.sequence = work_id.sequence + 3 + # 生成新增的返工工单的工序 + # 成品工序 + panel_sequence_list = {} + product_routing_tmpl_ids = model_type_id.product_routing_tmpl_ids + if product_routing_tmpl_ids: + for tmpl_id in product_routing_tmpl_ids: + sequence_max += 1 + panel_sequence_list.update({tmpl_id.route_workcenter_id.name: sequence_max}) + for work_id in work_ids: + if panel_sequence_list.get(work_id.name): + work_id.sequence = panel_sequence_list[work_id.name] # 创建工单并进行排序 def _create_workorder(self, item): @@ -688,3 +760,180 @@ class MrpProduction(models.Model): 'view_mode': 'tree,form', }) return action + + # 返工 + def button_rework(self): + cloud_programming_state = None + if self.programming_state != '已编程' and self.reprogramming_num >= 1: + cloud_programming_state = self._cron_get_programming_state() + logging.info('cloud_programming_state:%s' % cloud_programming_state) + logging.info('programming_state:%s' % self.programming_state) + return { + 'name': _('返工'), + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'sf.rework.wizard', + 'target': 'new', + 'context': { + 'default_production_id': self.id, + 'default_product_id': self.product_id.id, + 'default_programming_state': self.programming_state if cloud_programming_state is not None else cloud_programming_state, + 'default_is_reprogramming': False if (cloud_programming_state in ['编程中', + '待编程'] and self.programming_state in [ + '编程中']) + or (cloud_programming_state in [ + '已编程'] and self.programming_state in ['已编程未下发']) else True + } + } + + # 报废 + def button_scrap_new(self): + return True + + # 更新程序 + def do_update_program(self): + program_production = self + if len(program_production) >= 1: + same_product_id = None + is_not_same_product = 0 + for item in program_production: + if same_product_id is None: + same_product_id = item.product_id + if item.product_id != same_product_id: + is_not_same_product += 1 + if item.state != "rework" and item.programming_state != "已编程未下发": + raise UserError("请选择状态为返工且已编程未下发的制造订单") + if is_not_same_product >= 1: + raise UserError("您选择的记录中含有其他产品的制造订单,请选择同一产品的制造订单") + grouped_program_ids = {k: list(g) for k, g in groupby(program_production, key=lambda x: x.programming_no)} + program_to_production_names = {} + for programming_no, program_production in grouped_program_ids.items(): + program_to_production_names[programming_no] = [production.name for production in program_production] + for production in self: + if production.programming_no in program_to_production_names: + rework_workorder = production.workorder_ids.filtered(lambda m: m.state == 'rework') + if rework_workorder: + new_pancel_workorder = production.workorder_ids.filtered( + lambda m1: m1.state != 'rework' and m1.processing_panel == rework_workorder[0].processing_panel) + if not new_pancel_workorder.cnc_ids: + production.get_new_program(rework_workorder[0].processing_panel) + production.write({'state': 'progress', 'programming_state': '已下发'}) + + # 从cloud获取重新编程过的最新程序 + def get_new_program(self, processing_panel): + try: + res = {'programming_no': self.programming_no, 'processing_panel': processing_panel} + configsettings = self.env['res.config.settings'].get_values() + config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) + url = '/api/intelligent_programming/get_new_program' + config_url = configsettings['sf_url'] + url + r = requests.post(config_url, json=res, data=None, headers=config_header) + r = r.json() + result = json.loads(r['result']) + if result['status'] == 1: + # program_path_tmp_panel = os.path.join('/tmp', result['folder_name'], 'return', processing_panel) + # if os.path.exists(program_path_tmp_panel): + # files_r = os.listdir(program_path_tmp_panel) + # if files_r: + # for file_name in files_r: + # file_path = os.path.join(program_path_tmp_panel, file_name) + # os.remove(file_path) + # download_state = self.env['sf.cnc.processing'].download_file_tmp(result['folder_name'], + # processing_panel) + # if download_state is False: + # raise UserError('编程单号为%s的CNC程序文件从FTP拉取失败' % (self.programming_no)) + productions = self.env['mrp.production'].search( + [('programming_no', '=', self.programming_no), ('state', 'not in', ['cancel,done'])]) + if productions: + panel_workorder = productions.workorder_ids.filtered( + lambda + ac: ac.processing_panel == processing_panel and ac.routing_type == 'CNC加工' and ac.state != 'rework') + if panel_workorder: + if panel_workorder.cnc_ids: + panel_workorder.cmm_ids.sudo().unlink() + panel_workorder.cnc_ids.sudo().unlink() + self.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( + productions) + program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', + processing_panel) + logging.info('program_path_tmp_panel:%s' % program_path_tmp_panel) + files_panel = os.listdir(program_path_tmp_panel) + if files_panel: + for file in files_panel: + file_extension = os.path.splitext(file)[1] + if file_extension.lower() == '.pdf': + panel_file_path = os.path.join(program_path_tmp_panel, file) + logging.info('panel_file_path:%s' % panel_file_path) + panel_workorder.write( + {'cnc_ids': panel_workorder.cnc_ids.sudo()._json_cnc_processing(processing_panel, result), + 'cmm_ids': panel_workorder.cmm_ids.sudo()._json_cmm_program(processing_panel, result), + 'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())}) + pre_workorder = productions.workorder_ids.filtered(lambda + ap: ap.routing_type == '装夹预调' and ap.processing_panel == processing_panel and ap.state != 'rework') + if pre_workorder: + pre_workorder.write( + {'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())}) + else: + raise UserError(result['message']) + except Exception as e: + logging.info('get_new_program error:%s' % e) + raise UserError("从云平台获取最新程序失败,请联系管理员") + + +class sf_detection_result(models.Model): + _name = 'sf.detection.result' + _description = "检测结果" + + production_id = fields.Many2one('mrp.production') + processing_panel = fields.Char('加工面') + routing_type = fields.Selection([ + ('装夹预调', '装夹预调'), + ('CNC加工', 'CNC加工')], string="工序类型") + + rework_reason = fields.Selection( + [("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), + ("operate computer", "操机"), + ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True) + detailed_reason = fields.Text('详细原因') + test_results = fields.Selection([("合格", "合格"), ("返工", "返工"), ("报废", "报废")], + string="检测结果", tracking=True) + test_report = fields.Binary('检测报告', readonly=True) + handle_result = fields.Selection([("待处理", "待处理"), ("已处理", "已处理")], default='', string="处理结果", + tracking=True) + + # 查看检测报告 + def button_look_test_report(self): + return { + 'res_model': 'sf.detection.result', + 'type': 'ir.actions.act_window', + 'res_id': self.id, + 'views': [(self.env.ref('sf_manufacturing.sf_test_report_form').id, 'form')], + # 'view_mode': 'form', + # 'context': { + # 'default_id': self.id + # }, + 'target': 'new' + } + + +class sf_processing_panel(models.Model): + _name = 'sf.processing.panel' + _description = "加工面" + + name = fields.Char('加工面') + active = fields.Boolean('有效', default=True) + + # @api.model + # def name_search(self, name, args=None, operator='ilike', limit=100, name_get_uid=None): + # if self.env.user.has_group('sf_base.group_sf_order_user'): + # if self._context.get('product_id'): + # product = self.env['product.product'].search([('id', '=', self._context.get('product_id'))]) + # if product: + # panel = self.env['sf.processing.panel'].search([('name', 'ilike', 'ZM')]) + # if panel: + # ids = [t.id for t in panel] + # domain = [('id', 'in', ids)] + # else: + # domain = [('id', '=', False)] + # return self._search(domain, limit=limit, access_rights_uid=name_get_uid) + # return super()._name_search(name, args, operator, limit, name_get_uid) diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index 16ae67f3..6da74cbc 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -47,6 +47,7 @@ class ResMrpWorkOrder(models.Model): ('切割', '切割'), ('表面工艺', '表面工艺') ], string="工序类型") results = fields.Char('结果') + state = fields.Selection(selection_add=[('to be detected', "待检测"), ('rework', '返工')], tracking=True) manual_quotation = fields.Boolean('人工编程', default=False, readonly=True) @@ -197,13 +198,14 @@ class ResMrpWorkOrder(models.Model): production_line_state = fields.Selection( [('待上产线', '待上产线'), ('已上产线', '已上产线'), ('已下产线', '已下产线')], string='上/下产线', default='待上产线', tracking=True) - detection_report = fields.Binary('检测报告', readonly=True) + detection_report = fields.Binary('检测报告', readonly=False) is_remanufacture = fields.Boolean(string='重新生成制造订单', default=False) is_fetchcnc = fields.Boolean(string='重新获取NC程序', default=False) reason = fields.Selection( - [("programming", "编程"), ("clamping", "返工"), ("cutter", "刀具"), ("operate computer", "操机"), - ("technology", "工艺"), ("customer redrawing", "客户改图"), ("other", "其他"), ], string="原因", tracking=True) + [("programming", "编程"), ("cutter", "刀具"), ("clamping", "装夹"), ("operate computer", "操机"), + ("technology", "工艺"), ("customer redrawing", "客户改图")], string="原因", tracking=True) detailed_reason = fields.Text('详细原因') + is_rework = fields.Boolean(string='是否返工', default=False) # is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False) @@ -431,6 +433,7 @@ class ResMrpWorkOrder(models.Model): work.compensation_value_y = eval(self.material_center_point)[1] # work.process_state = '待加工' # self.sudo().production_id.process_state = '待加工' + # self.sudo().production_id.process_state = '待加工' self.date_finished = datetime.now() workorder.button_finish() @@ -461,6 +464,19 @@ class ResMrpWorkOrder(models.Model): else: raise UserError(_("该工单暂未完成,无法进行工件配送")) + def button_rework_pre(self): + return { + 'name': _('返工'), + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'sf.rework.wizard', + 'target': 'new', + 'context': { + 'default_workorder_id': self.id, + 'default_production_id': self.production_id.id, + 'default_routing_type': self.routing_type + }} + # 拼接工单对象属性值 def json_workorder_str(self, k, production, route, item): # 计算预计时长duration_expected @@ -804,28 +820,67 @@ class ResMrpWorkOrder(models.Model): }] return workorders_values_str - # @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state') - # def _compute_state(self): - # super(ResMrpWorkOrder, self)._compute_state() - # for item in self: - # print(item.name) - # print(item.state) - # print(item.is_remanufacture) - # scrap_workorder = self.env['mrp.workorder'].search( - # [('production_id', '=', item.production_id.id), ('routing_type', '=', 'CNC加工'), - # ('state', '=', 'done'), ('test_results', 'in', ['返工', '报废'])]) - # print(scrap_workorder) - # # if item.routing_type == 'CNC加工' and item.state in ['done'] and item.test_results in ['返工', '报废']: - # if item.routing_type == '解除装夹': - # if scrap_workorder and item.state not in ['cancel']: - # item.state = 'cancel' - # elif item.routing_type == '表面工艺': - # if scrap_workorder: - # stock_move = self.env['stock.move'].search( - # [('origin', '=', item.production_id.name)]) - # stock_move.write({'state': 'cancel'}) - # item.picking_ids.write({'state': 'cancel'}) - # item.state = 'cancel' + @api.depends('production_availability', 'blocked_by_workorder_ids', 'blocked_by_workorder_ids.state') + def _compute_state(self): + super()._compute_state() + for workorder in self: + re_work = self.env['mrp.workorder'].search([('production_id', '=', workorder.production_id.id), + ('processing_panel', '=', workorder.processing_panel), + ('is_rework', '=', True), ('state', 'in', ['done', 'rework'])]) + logging.info('re_work:%s' % re_work.state) + if workorder.state not in ['cancel', 'progress', 'rework']: + logging.info('工序:%s' % workorder.routing_type) + logging.info('状态:%s' % workorder.state) + logging.info('is_rework:%s' % workorder.is_rework) + logging.info('面:%s' % workorder.processing_panel) + logging.info('编程状态:%s' % workorder.production_id.programming_state) + logging.info('制造状态:%s' % workorder.production_id.state) + # if workorder.state =='done' and workorder.is_rework is True: + # workorder.state = 'rework' + # else: + # if re_work: + # workorder.state = 'rework' + if workorder.production_id.state == 'rework': + if (workorder.routing_type == 'CNC加工' and workorder.state == 'done') or ( + workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '编程中' and re_work): + logging.info('面111:%s' % workorder.processing_panel) + workorder.state = 'waiting' + elif workorder.production_id.state == 'progress': + logging.info('面222:%s' % workorder.processing_panel) + if workorder.routing_type == '装夹预调' and workorder.production_id.programming_state == '已下发' and re_work: + workorder.state = 'ready' + # else: + # if workorder.state not in ['cancel', 'rework']: + # workorder.state = 'rework' + # elif workorder.routing_type == 'CNC加工' and workorder.state not in ['done', 'cancel', 'progress', + # 'rework']: + # per_work = self.env['mrp.workorder'].search( + # [('routing_type', '=', '装夹预调'), ('production_id', '=', workorder.production_id.id), + # ('processing_panel', '=', workorder.processing_panel), ('is_rework', '=', True)]) + # if per_work: + # workorder.state = 'waiting' + # if workorder.routing_type == 'CNC加工' and workorder.state == 'progress': + # workorder.state = 'to be detected' + # for workorder in self: + # if workorder.is_rework is True and workorder.state == 'done': + # cnc_work = self.env['mrp.workorder'].search([('routing_type','=','CNC加工'),('production_id','=',workorder.production_id.id)]) + # if cnc_work: + # cnc_work.state = 'waiting' + # if workorder.state == 'pending': + # if all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): + # workorder.state = 'ready' if workorder.production_id.reservation_state == 'assigned' else 'waiting' + # continue + # if workorder.state not in ('waiting', 'ready'): + # continue + # if not all([wo.state in ('done', 'cancel') for wo in workorder.blocked_by_workorder_ids]): + # workorder.state = 'pending' + # continue + # if workorder.production_id.reservation_state not in ('waiting', 'confirmed', 'assigned'): + # continue + # if workorder.production_id.reservation_state == 'assigned' and workorder.state == 'waiting': + # workorder.state = 'ready' + # elif workorder.production_id.reservation_state != 'assigned' and workorder.state == 'ready': + # workorder.state = 'waiting' # 重写工单开始按钮方法 def button_start(self): @@ -942,6 +997,15 @@ class ResMrpWorkOrder(models.Model): record.process_state = '待解除装夹' # record.write({'process_state': '待加工'}) record.production_id.process_state = '待解除装夹' + if record.test_results in ['返工']: + record.production_id.write({'detection_result_ids': [(0, 0, { + 'rework_reason': record.reason, + 'detailed_reason': record.detailed_reason, + 'processing_panel': record.processing_panel, + 'routing_type': record.routing_type, + 'handle_result': '待处理' if record.test_results == '返工' or record.is_rework is True else '', + 'test_results': record.test_results, + 'test_report': record.detection_report})]}) if record.routing_type == '解除装夹': ''' 记录结束时间 @@ -990,10 +1054,7 @@ class ResMrpWorkOrder(models.Model): record.write({ 'date_planned_finished': tem_date_planned_finished # 保持原值 }) - # if record.routing_type == 'CNC加工': - # record.write({ - # 'date_finished': tem_date_finished # 保持原值 - # }) + # if record.routing_type == 'CNC加工' and record.test_results in ['返工', '报废']: # record.production_id.action_cancel() # record.production_id.workorder_ids.write({'rfid_code': False, 'rfid_code_old': record.rfid_code}) @@ -1163,24 +1224,25 @@ class CNCprocessing(models.Model): def _json_cnc_processing(self, panel, ret): cnc_processing = [] - for item in ret['programming_list']: - if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1: - cnc_processing.append((0, 0, { - 'sequence_number': item['sequence_number'], - 'program_name': item['program_name'], - 'cutting_tool_name': item['cutting_tool_name'], - 'cutting_tool_no': item['cutting_tool_no'], - 'processing_type': item['processing_type'], - 'margin_x_y': item['margin_x_y'], - 'margin_z': item['margin_z'], - 'depth_of_processing_z': item['depth_of_processing_z'], - 'cutting_tool_extension_length': item['cutting_tool_extension_length'], - 'cutting_tool_handle_type': item['cutting_tool_handle_type'], - 'estimated_processing_time': item['estimated_processing_time'], - 'program_path': item['ftp_path'], - 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'), - 'remark': item['remark'] - })) + if ret is not False: + for item in ret['programming_list']: + if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') == -1: + cnc_processing.append((0, 0, { + 'sequence_number': item['sequence_number'], + 'program_name': item['program_name'], + 'cutting_tool_name': item['cutting_tool_name'], + 'cutting_tool_no': item['cutting_tool_no'], + 'processing_type': item['processing_type'], + 'margin_x_y': item['margin_x_y'], + 'margin_z': item['margin_z'], + 'depth_of_processing_z': item['depth_of_processing_z'], + 'cutting_tool_extension_length': item['cutting_tool_extension_length'], + 'cutting_tool_handle_type': item['cutting_tool_handle_type'], + 'estimated_processing_time': item['estimated_processing_time'], + 'program_path': item['ftp_path'], + 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'), + 'remark': item['remark'] + })) return cnc_processing # 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配 @@ -1636,12 +1698,13 @@ class CMMprogram(models.Model): def _json_cmm_program(self, panel, ret): cmm_program = [] - for item in ret['programming_list']: - if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1: - cmm_program.append((0, 0, { - 'sequence_number': 1, - 'program_name': item['program_name'], - 'program_path': item['ftp_path'], - 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'), - })) + if ret is not False: + for item in ret['programming_list']: + if item['processing_panel'] == panel and item['ftp_path'].find('.dmi') != -1: + cmm_program.append((0, 0, { + 'sequence_number': 1, + 'program_name': item['program_name'], + 'program_path': item['ftp_path'], + 'program_create_date': datetime.strptime(item['program_create_date'], '%Y-%m-%d %H:%M:%S'), + })) return cmm_program diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index ec2d5a76..5cdb72b7 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -141,3 +141,8 @@ access_sf_model_type_group_sf_stock_manager,sf_model_type_group_sf_mrp_manager,m access_mrp_bom_byproduct_group_sf_stock_user,mrp_bom_byproduct_group_sf_stock_user,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_user,1,0,0,0 access_mrp_bom_byproduct_group_sf_stock_manager,mrp_bom_byproduct_group_sf_mrp_manager,mrp.model_mrp_bom_byproduct,sf_base.group_sf_stock_manager,1,0,0,0 +access_sf_rework_wizard_group_sf_order_user,sf_rework_wizard_group_sf_order_user,model_sf_rework_wizard,sf_base.group_sf_order_user,1,1,1,0 +access_sf_detection_result_group_sf_order_user,sf_detection_result_group_sf_order_user,model_sf_detection_result,sf_base.group_sf_order_user,1,1,1,0 +access_sf_processing_panel_group_sf_order_user,sf_processing_panel_group_sf_order_user,model_sf_processing_panel,sf_base.group_sf_order_user,1,1,1,0 + + diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index b5e348b9..9ee96732 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -1,11 +1,16 @@ + custom.mrp.production.tree mrp.production + +