diff --git a/sf_base/models/tool_base_new.py b/sf_base/models/tool_base_new.py index 7977ed80..98eea36c 100644 --- a/sf_base/models/tool_base_new.py +++ b/sf_base/models/tool_base_new.py @@ -331,7 +331,7 @@ class ToolInventory(models.Model): work_material = fields.Selection([('钢', '钢'), ('铝', '铝')], string='加工材料') life_span = fields.Float('寿命(min)') - tool_groups_id = fields.Many2one('sf.tool.groups', string='刀具组') + tool_groups_id = fields.Many2one('sf.tool.groups', string='刀具组', required=True) active = fields.Boolean('已归档', default=True) diff --git a/sf_bf_connect/models/jd_eclp.py b/sf_bf_connect/models/jd_eclp.py index e381242a..c2b8deb7 100644 --- a/sf_bf_connect/models/jd_eclp.py +++ b/sf_bf_connect/models/jd_eclp.py @@ -14,7 +14,7 @@ _logger = logging.getLogger(__name__) class JdEclp(models.Model): _inherit = 'stock.picking' - senderNickName = fields.Char(string='寄件工厂简称', required=True, default='MW') + senderNickName = fields.Char(string='寄件工厂简称', required=True, default='XT') # receiverName = fields.Char(string='收件人姓名') # receiverMobile = fields.Char(string='收件人电话') diff --git a/sf_machine_connect/views/compensation.xml b/sf_machine_connect/views/compensation.xml index f79c539e..07173c17 100644 --- a/sf_machine_connect/views/compensation.xml +++ b/sf_machine_connect/views/compensation.xml @@ -5,39 +5,46 @@ mrp.workorder - - - - + + + + + + - - - - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sf_machine_connect/views/default_delivery.xml b/sf_machine_connect/views/default_delivery.xml index b0890981..0238c736 100644 --- a/sf_machine_connect/views/default_delivery.xml +++ b/sf_machine_connect/views/default_delivery.xml @@ -9,7 +9,6 @@ - diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 32124b28..15a77160 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -292,7 +292,7 @@ class MrpProduction(models.Model): # 则根据设备找到工作中心;否则采用前面描述的工作中心分配机制; # 其他规则限制: 默认只分配给工作中心状态为非故障的工作中心; - def _create_workorder3(self): + def _create_workorder3(self, item): # 根据product_id对self进行分组 grouped_product_ids = {k: list(g) for k, g in groupby(self, key=lambda x: x.product_id.id)} # 初始化一个字典来存储每个product_id对应的生产订单名称列表 @@ -328,19 +328,6 @@ class MrpProduction(models.Model): 'state': 'pending', }] if production.product_id.categ_id.type == '成品': - if production.product_id.id in product_id_to_production_names: - # # 同一个产品多个制造订单对应一个编程单和模型库 - # # 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递 - if not production.programming_no: - production_programming = self.search( - [('product_id.id', '=', production.product_id.id), ('origin', '=', production.origin)], - limit=1, order='id asc') - if not production_programming.programming_no: - production.fetchCNC(', '.join(product_id_to_production_names[production.product_id.id])) - else: - production.write({'programming_no': production_programming.programming_no, - 'programming_state': '编程中'}) - # # 根据加工面板的面数及对应的工序模板生成工单 i = 0 processing_panel_len = len(production.product_id.model_processing_panel.split(',')) @@ -353,10 +340,10 @@ class MrpProduction(models.Model): for route in product_routing_workcenter: if route.is_repeat is True: workorders_values.append( - self.env['mrp.workorder'].json_workorder_str(k, production, route)) - if i == processing_panel_len and route.routing_type == '解除装夹': - workorders_values.append( - self.env['mrp.workorder'].json_workorder_str(k, production, route)) + self.env['mrp.workorder'].json_workorder_str(k, production, route, item)) + # if i == processing_panel_len and route.routing_type == '解除装夹': + # workorders_values.append( + # self.env['mrp.workorder'].json_workorder_str(k, production, route)) # 表面工艺工序 # 获取表面工艺id if production.product_id.model_process_parameters_ids: @@ -404,6 +391,52 @@ class MrpProduction(models.Model): workorders_values.append( self.env['mrp.workorder'].json_workorder_str('', production, route)) production.workorder_ids = workorders_values + # for production_item in productions: + process_parameter_workorder = self.env['mrp.workorder'].search( + [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production.id), + ('is_subcontract', '=', True)]) + if process_parameter_workorder: + is_pick = False + consecutive_workorders = [] + m = 0 + sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id) + for i in range(len(sorted_workorders) - 1): + if m == 0: + is_pick = False + if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \ + sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \ + sorted_workorders[i].id == sorted_workorders[i + 1].id - 1: + if sorted_workorders[i] not in consecutive_workorders: + consecutive_workorders.append(sorted_workorders[i]) + consecutive_workorders.append(sorted_workorders[i + 1]) + m += 1 + continue + else: + if m == len(consecutive_workorders) - 1 and m != 0: + self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, + production_item) + if sorted_workorders[i] in consecutive_workorders: + is_pick = True + consecutive_workorders = [] + m = 0 + # 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单 + if is_pick is False: + self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], + production_item) + if m == len(consecutive_workorders) - 1 and m != 0: + self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, + production_item) + if sorted_workorders[i] in consecutive_workorders: + is_pick = True + consecutive_workorders = [] + m = 0 + if m == len(consecutive_workorders) - 1 and m != 0: + self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item) + if is_pick is False and m == 0: + if len(sorted_workorders) == 1: + self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item) + else: + self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item) for workorder in production.workorder_ids: workorder.duration_expected = workorder._get_duration_expected() @@ -498,46 +531,63 @@ class MrpProduction(models.Model): def _reset_work_order_sequence(self): for rec in self: sequence_list = {} + # 产品模型类型 model_type_id = rec.product_id.product_model_type_id + # 产品加工面板 + model_processing_panel = rec.product_id.model_processing_panel if model_type_id: - tmpl_num = 1 - # 成品工序 - 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_list.update({tmpl_id.route_workcenter_id.name: tmpl_num}) - tmpl_num += 1 - # 表面工艺工序 - # 模型类型的表面工艺工序模版 - 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 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('该产品没有选择【模版类型】!') 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) # if work.name == '获取CNC加工程序': @@ -546,8 +596,8 @@ class MrpProduction(models.Model): # work.button_finish() # 创建工单并进行排序 - def _create_workorder(self): - self._create_workorder3() + def _create_workorder(self, item): + self._create_workorder3(item) self._reset_work_order_sequence() return True diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index ba7bc2c5..8cc7b1f9 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -1,5 +1,5 @@ import re - +import json import logging import base64 import urllib.parse @@ -207,6 +207,7 @@ class ResMrpWorkOrder(models.Model): [("programming", "编程"), ("clamping", "返工"), ("cutter", "刀具"), ("operate computer", "操机"), ("technology", "工艺"), ("customer redrawing", "客户改图"), ("other", "其他"), ], string="原因", tracking=True) detailed_reason = fields.Text('详细原因') + is_send_program_again = fields.Boolean(string='是否重新下发NC程序', default=False) @api.onchange('rfid_code') def _onchange(self): @@ -473,7 +474,7 @@ class ResMrpWorkOrder(models.Model): raise UserError(_("该工单暂未完成,无法进行工件配送")) # 拼接工单对象属性值 - def json_workorder_str(self, k, production, route): + def json_workorder_str(self, k, production, route, item): # 计算预计时长duration_expected if route.routing_type == '切割': duration_expected = self.env['mrp.routing.workcenter'].sudo().search( @@ -506,7 +507,7 @@ class ResMrpWorkOrder(models.Model): 'processing_panel': k, 'quality_point_ids': route.route_workcenter_id.quality_point_ids, 'routing_type': route.routing_type, - 'work_state': '待发起', + # 'work_state': '待发起', 'workcenter_id': self.env['mrp.routing.workcenter'].get_workcenter(route.workcenter_ids.ids, route.routing_type, production.product_id), @@ -515,6 +516,10 @@ class ResMrpWorkOrder(models.Model): 'date_planned_finished': datetime.now() + timedelta(days=1), 'duration_expected': duration_expected, 'duration': 0, + 'cnc_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cnc.processing']._json_cnc_processing( + k, item), + 'cmm_ids': False if route.routing_type != 'CNC加工' else self.env['sf.cmm.program']._json_cmm_program(k, + item), 'workpiece_delivery_ids': False if not route.routing_type == '装夹预调' else self._json_workpiece_delivery_list( production) }] @@ -1073,6 +1078,33 @@ class ResMrpWorkOrder(models.Model): workorder.detection_report = base64.b64encode(open(report_file_path, 'rb').read()) return True + # 重新下发nc程序 + def button_send_program_again(self): + try: + res = {'programming_no': self.production_id.programming_no} + configsettings = self.env['res.config.settings'].get_values() + config_header = Common.get_headers(self, configsettings['token'], configsettings['sf_secret_key']) + url = '/api/intelligent_programming/reset_state_again' + 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: + productions = self.env['mrp.production'].search( + [('programming_no', '=', self.production_id.programming_no), ('programming_state', '=', '已编程')]) + if productions: + workorder = productions.workorder_ids.filtered( + lambda ap: ap.routing_type in ['装夹预调', 'CNC加工'] and ap.state not in ['done', 'cancel', + 'progress']) + if workorder: + workorder.write({'is_send_program_again': True}) + productions.write({'work_state': '编程中', 'programming_state': '编程中'}) + else: + raise UserError(result['message']) + except Exception as e: + logging.info('button_send_program_again error:%s' % e) + raise UserError("重新下发nc程序失败,请联系管理员") + class CNCprocessing(models.Model): _name = 'sf.cnc.processing' @@ -1098,6 +1130,7 @@ class CNCprocessing(models.Model): production_id = fields.Many2one('mrp.production', string="制造订单") button_state = fields.Boolean(string='是否已经下发') program_path = fields.Char('程序文件路径') + program_create_date = fields.Datetime('程序创建日期') # mrs下发编程单创建CNC加工 def cnc_processing_create(self, cnc_workorder, ret, program_path, program_path_tmp): @@ -1130,24 +1163,27 @@ class CNCprocessing(models.Model): cnc_workorder.write({'programming_state': '已编程', 'work_state': '已编程'}) return cnc_processing - def _json_cnc_processing(self, obj): - cnc_processing_str = (0, 0, { - 'sequence_number': obj['sequence_number'], - 'program_name': obj['program_name'], - 'cutting_tool_name': obj['cutting_tool_name'], - 'cutting_tool_no': obj['cutting_tool_no'], - 'processing_type': obj['processing_type'], - 'margin_x_y': obj['margin_x_y'], - 'margin_z': obj['margin_z'], - 'depth_of_processing_z': obj['depth_of_processing_z'], - 'cutting_tool_extension_length': obj['cutting_tool_extension_length'], - 'cutting_tool_handle_type': obj['cutting_tool_handle_type'], - 'estimated_processing_time': obj['estimated_processing_time'], - 'program_path': obj['program_path'], - 'cnc_id': obj['cnc_id'].id, - 'remark': obj['remark'] - }) - return cnc_processing_str + 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'] + })) + return cnc_processing # 根据程序名和加工面匹配到ftp里对应的Nc程序名,可优化为根据cnc_processing.program_path进行匹配 def get_cnc_processing_file(self, serverdir, cnc_processing, program_path): @@ -1177,14 +1213,14 @@ class CNCprocessing(models.Model): }) return attachment - # 将FTP的nc文件下载到临时目录 + # 将FTP的多面的程序单文件下载到临时目录 def download_file_tmp(self, production_no, processing_panel): remotepath = os.path.join('/NC', production_no, 'return', processing_panel) serverdir = os.path.join('/tmp', production_no, 'return', processing_panel) ftp_resconfig = self.env['res.config.settings'].get_values() ftp = FtpController(str(ftp_resconfig['ftp_host']), int(ftp_resconfig['ftp_port']), ftp_resconfig['ftp_user'], ftp_resconfig['ftp_password']) - download_state = ftp.download_file_tree(remotepath, serverdir) + download_state = ftp.download_program_file(remotepath, serverdir) logging.info('download_state:%s' % download_state) return download_state @@ -1589,80 +1625,22 @@ class CMMprogram(models.Model): _name = 'sf.cmm.program' _description = "CMM程序" - cmm_id = fields.Many2one('ir.attachment') sequence_number = fields.Integer('序号') program_name = fields.Char('程序名') - cutting_tool_name = fields.Char('刀具名称') - cutting_tool_no = fields.Char('刀号') - processing_type = fields.Char('加工类型') - margin_x_y = fields.Char('余量_X/Y') - margin_z = fields.Char('余量_Z') - depth_of_processing_z = fields.Char('加工深度(Z)') - cutting_tool_extension_length = fields.Char('刀具伸出长度') - cutting_tool_handle_type = fields.Char('刀柄型号') - estimated_processing_time = fields.Char('预计加工时间') remark = fields.Text('备注') workorder_id = fields.Many2one('mrp.workorder', string="工单") production_id = fields.Many2one('mrp.production', string="制造订单") program_path = fields.Char('程序文件路径') + program_create_date = fields.Datetime('程序创建日期') - def cmm_program_create(self, ret, program_path, program_path_tmp): - cmm_program = None - for obj in ret['programming_list']: - workorder = self.env['mrp.workorder'].search( - [('production_id.name', '=', ret['production_order_no'].split(',')[0]), - ('processing_panel', '=', obj['processing_panel']), - ('routing_type', '=', 'CNC加工')]) - if obj['program_name'] in program_path: - logging.info('obj:%s' % obj['program_name']) - cmm_program = self.sudo().create({ - 'workorder_id': workorder.id, - 'sequence_number': obj['sequence_number'], - 'program_name': obj['program_name'], - 'cutting_tool_name': obj['cutting_tool_name'], - 'cutting_tool_no': obj['cutting_tool_no'], - 'processing_type': obj['processing_type'], - 'margin_x_y': obj['margin_x_y'], - 'margin_z': obj['margin_z'], - 'depth_of_processing_z': obj['depth_of_processing_z'], - 'cutting_tool_extension_length': obj['cutting_tool_extension_length'], - 'cutting_tool_handle_type': obj['cutting_tool_handle_type'], - 'estimated_processing_time': obj['estimated_processing_time'], - 'remark': obj['remark'], - 'program_path': program_path.replace('/tmp', '') - }) - cmm_program.get_cmm_program_file(program_path_tmp, cmm_program, program_path) + 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'), + })) return cmm_program - - # 根据程序名和加工面匹配到ftp里对应的cmm程序名 - def get_cmm_program_file(self, serverdir, cmm_program, program_path): - logging.info('cmm-serverdir:%s' % serverdir) - for root, dirs, files in os.walk(serverdir): - for f in files: - if f in program_path: - cmm_program_file_path = os.path.join(serverdir, root, f) - self.write_file_cmm(cmm_program_file_path, cmm_program) - - # 创建附件(nc文件) - def attachment_create(self, name, data): - attachment = self.env['ir.attachment'].create({ - 'datas': base64.b64encode(data), - 'type': 'binary', - 'public': True, - 'description': '程序文件', - 'name': name - }) - return attachment - - # 将cmm文件存到attach的datas里 - def write_file_cmm(self, cmm_file_path, cnc): - cmm_file_name = cmm_file_path.split('/') - logging.info('cmm_file_name:%s' % cmm_file_name[-1]) - if os.path.exists(cmm_file_path): - with open(cmm_file_path, 'rb') as file: - data_bytes = file.read() - attachment = self.attachment_create(cnc.program_name + cmm_file_name[-1], data_bytes) - cnc.write({'cmm_id': attachment.id}) - file.close() - else: - return False diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 49f00140..be81e110 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import base64 import qrcode +from itertools import groupby from collections import defaultdict, namedtuple import logging import io @@ -205,61 +206,37 @@ class StockRule(models.Model): # 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() + ''' + 创建工单 + ''' + # productions._create_workorder() + # + grouped_product_ids = {k: list(g) for k, g in groupby(productions, key=lambda x: x.product_id.id)} + # 初始化一个字典来存储每个product_id对应的生产订单名称列表 + product_id_to_production_names = {} + # 对于每个product_id,获取其所有生产订单的名称 + for product_id, productions in grouped_product_ids.items(): + # 为同一个product_id创建一个生产订单名称列表 + product_id_to_production_names[product_id] = [production.name for production in productions] for production_item in productions: - process_parameter_workorder = self.env['mrp.workorder'].search( - [('surface_technics_parameters_id', '!=', False), ('production_id', '=', production_item.id), - ('is_subcontract', '=', True)]) - if process_parameter_workorder: - is_pick = False - consecutive_workorders = [] - m = 0 - sorted_workorders = sorted(process_parameter_workorder, key=lambda w: w.id) - for i in range(len(sorted_workorders) - 1): - if m == 0: - is_pick = False - if sorted_workorders[i].supplier_id.id == sorted_workorders[i + 1].supplier_id.id and \ - sorted_workorders[i].is_subcontract == sorted_workorders[i + 1].is_subcontract and \ - sorted_workorders[i].id == sorted_workorders[i + 1].id - 1: - if sorted_workorders[i] not in consecutive_workorders: - consecutive_workorders.append(sorted_workorders[i]) - consecutive_workorders.append(sorted_workorders[i + 1]) - m += 1 - continue + if production_item.product_id.id in product_id_to_production_names: + # # 同一个产品多个制造订单对应一个编程单和模型库 + # # 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递 + if not production_item.programming_no: + production_programming = self.env['mrp.production'].search( + [('product_id.id', '=', production_item.product_id.id), + ('origin', '=', production_item.origin)], + limit=1, order='id asc') + if not production_programming.programming_no: + production_item.fetchCNC( + ', '.join(product_id_to_production_names[production_item.product_id.id])) else: - if m == len(consecutive_workorders) - 1 and m != 0: - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, - production_item) - if sorted_workorders[i] in consecutive_workorders: - is_pick = True - consecutive_workorders = [] - m = 0 - # 当前面的连续工序生成对应的外协出入库单再生成当前工序的外协出入库单 - if is_pick is False: - self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], - production_item) - if m == len(consecutive_workorders) - 1 and m != 0: - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, - production_item) - if sorted_workorders[i] in consecutive_workorders: - is_pick = True - consecutive_workorders = [] - m = 0 - if m == len(consecutive_workorders) - 1 and m != 0: - self.env['stock.picking'].create_outcontract_picking(consecutive_workorders, production_item) - if is_pick is False and m == 0: - if len(sorted_workorders) == 1: - self.env['stock.picking'].create_outcontract_picking(sorted_workorders, production_item) - else: - self.env['stock.picking'].create_outcontract_picking(sorted_workorders[i], production_item) + production_item.write({'programming_no': production_programming.programming_no, + 'programming_state': '编程中'}) for production in productions: ''' diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index 3b75f102..ec2d5a76 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -1,8 +1,9 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_sf_cnc_processing_group_sf_mrp_user,sf_cnc_processing,model_sf_cnc_processing,sf_base.group_sf_mrp_user,1,0,0,0 access_sf_cnc_processing_manager,sf_cnc_processing,model_sf_cnc_processing,sf_base.group_sf_mrp_manager,1,1,1,0 -access_sf_cmm_program_group_sf_mrp_user_group_sf_mrp_user,sf_cmm_program_group_sf_mrp_user,model_sf_cmm_program,sf_base.group_sf_mrp_user,1,0,0,0 +access_sf_cmm_program_group_sf_mrp_user,sf_cmm_program_group_sf_mrp_user,model_sf_cmm_program,sf_base.group_sf_mrp_user,1,0,0,0 access_sf_cmm_program_group_sf_mrp_manager,sf_cmm_program_group_sf_mrp_manager,model_sf_cmm_program,sf_base.group_sf_mrp_manager,1,0,0,0 +access_sf_cmm_program_admin,sf_cmm_program_admin,model_sf_cmm_program,base.group_system,1,1,1,1 access_sf_model_type_group_sf_mrp_user,sf_model_type,model_sf_model_type,sf_base.group_sf_mrp_user,1,0,0,0 access_sf_model_type_admin,sf_model_type_admin,model_sf_model_type,base.group_system,1,1,1,0 access_sf_model_type_manager,sf_model_type,model_sf_model_type,sf_base.group_sf_mrp_manager,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 55dbbe33..e1d0d28c 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -62,16 +62,17 @@ - - - progress,pending_cam,pending_processing,pending_era_cam,completed,done + + + + progress,pending_cam,pending_processing,pending_era_cam,completed,done - + - + @@ -281,7 +282,7 @@ + groups="sf_base.group_sf_mrp_user" confirm="是否确认开始?"/> + groups="sf_base.group_sf_mrp_user" + confirm="是否确认完成?"/> - - - - + + + + + groups="sf_base.group_sf_mrp_user" confirm="是否确认完成?"/> - + @@ -21,6 +21,12 @@ + + + + + + @@ -31,7 +37,7 @@ - + {'invisible': ['|', '|', '|','|','|', ('production_state','in', ('draft', @@ -118,6 +124,7 @@ + @@ -152,7 +159,12 @@ + attrs="{'invisible': ['|','|',('routing_type','!=','装夹预调'),('is_delivery','=',True),('state','!=','done')]}"/> + @@ -204,17 +216,17 @@ + attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/> - - - + attrs='{"invisible": [("routing_type","in",("获取CNC加工程序","切割","装夹预调"))]}'/> + + + + + + @@ -282,20 +294,26 @@ + + + + + + + + + + - - - - @@ -478,13 +496,13 @@ - - - - - - - + + + + + + + @@ -497,11 +515,11 @@ - - + + - @@ -511,8 +529,10 @@ + + - + @@ -520,20 +540,12 @@ - + - - - - - - - - - - + + @@ -563,9 +575,9 @@ - - - + + + @@ -580,10 +592,24 @@ + + + + custom.workorder.search + mrp.workorder + + + + + + + + 工单 ir.actions.act_window diff --git a/sf_mrs_connect/controllers/controllers.py b/sf_mrs_connect/controllers/controllers.py index 78a0c05a..5102efdb 100644 --- a/sf_mrs_connect/controllers/controllers.py +++ b/sf_mrs_connect/controllers/controllers.py @@ -24,82 +24,71 @@ class Sf_Mrs_Connect(http.Controller): ret = json.loads(datas) ret = json.loads(ret['result']) logging.info('下发编程单:%s' % ret) - is_delete_file = False - # 查询状态为进行中且类型为获取CNC加工程序的工单 - cnc_production = request.env['mrp.production'].with_user( - request.env.ref("base.user_admin")).search([('name', '=', ret['production_order_no'].split(',')[0])]) - cnc_program = request.env['mrp.production'].with_user( + productions = request.env['mrp.production'].with_user( request.env.ref("base.user_admin")).search( - [('programming_no', '=', cnc_production.programming_no), ('id', '!=', cnc_production.id)]) - if cnc_production.workorder_ids.filtered(lambda a: a.routing_type == 'CNC加工').cnc_ids: - is_delete_file = True - cnc_production.workorder_ids.filtered( - lambda a1: a1.routing_type == 'CNC加工').cnc_ids.sudo().unlink() - request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(cnc_production) - if cnc_program.workorder_ids.filtered(lambda c: c.routing_type == 'CNC加工').cnc_ids: - cnc_program.workorder_ids.filtered( - lambda c1: c1.routing_type == 'CNC加工').cnc_ids.sudo().unlink() - request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan(cnc_program) - # cnc_program = request.env['mrp.production'].with_user( - # request.env.ref("base.user_admin")).search([('programming_no', '=', cnc_production.programming_no)]) - logging.info('制造订单号:%s' % cnc_production.name) - if cnc_production: - # if ret['glb_file']: - # cnc_production.glb_file = base64.b64encode(ret['glb_file']) + [('programming_no', '=', ret['programming_no'])]) + if productions: # 拉取所有加工面的程序文件 - if is_delete_file is True: - program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', 'R') + for r in ret['processing_panel'].split(','): + program_path_tmp_r = os.path.join('/tmp', ret['folder_name'], 'return', r) files_r = os.listdir(program_path_tmp_r) if files_r: for file_name in files_r: file_path = os.path.join(program_path_tmp_r, file_name) os.remove(file_path) - for r in ret['processing_panel']: - download_state = request.env['sf.cnc.processing'].with_user( - request.env.ref("base.user_admin")).download_file_tmp( - ret['folder_name'], r) - if download_state == 0: - res['status'] = -2 - res['message'] = '制造订单号为%s的CNC程序文件从FTP拉取失败' % (cnc_production.name) - return json.JSONEncoder().encode(res) - logging.info('创建cnc工单') - program_path_tmp = os.path.join('/tmp', ret['folder_name'], 'return', r) - # program_path_tmp = "C://Users//43484//Desktop//机企猫工作文档//其他//model_analysis" - files = os.listdir(program_path_tmp) - cnc_processing_arr = [] - for f in files: - program_path = os.path.join(program_path_tmp, f) - logging.info('cnc程序路径 :%s' % program_path) - if f.endswith(".doc"): - # 插入cmm程序数据 - cmm_program = request.env['sf.cmm.program'].with_user( - request.env.ref("base.user_admin")).cmm_program_create(ret, program_path, program_path_tmp) - cnc_processing = request.env['sf.cnc.processing'].with_user( - request.env.ref("base.user_admin")).cnc_processing_create(cnc_production, ret, program_path, - program_path_tmp) - logging.info('cnc_processing111:%s' % cnc_processing) - if cnc_processing: - cnc_processing_arr.append(cnc_processing._json_cnc_processing(cnc_processing)) - if (cnc_program and cnc_processing_arr) or (not cnc_program and cnc_processing_arr): - cnc_production.workorder_ids.filtered(lambda g: g.routing_type == '装夹预调').write( - {'processing_drawing': cnc_production.workorder_ids.filtered( - lambda g1: g1.routing_type == 'CNC加工').cnc_worksheet}) - if cnc_program and cnc_processing_arr: - cnc_program.write({'programming_state': '已编程', 'work_state': '已编程'}) - cnc_program.workorder_ids.filtered(lambda d: d.routing_type == '装夹预调').write( - {'processing_drawing': cnc_production.workorder_ids.filtered( - lambda d1: d1.routing_type == 'CNC加工').cnc_worksheet}) - cnc_program.workorder_ids.filtered(lambda b: b.routing_type == 'CNC加工').write( - {'cnc_ids': cnc_processing_arr, 'cnc_worksheet': cnc_production.workorder_ids.filtered( - lambda b1: b1.routing_type == 'CNC加工').cnc_worksheet}) - cnc_program |= cnc_production - if not cnc_program and cnc_processing_arr: - cnc_program = cnc_production - cnc_program_ids = [item.id for item in cnc_program] - workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( - [('production_id', 'in', cnc_program_ids)]) - if workpiece_delivery: - workpiece_delivery.write({'is_cnc_program_down': True}) + download_state = request.env['sf.cnc.processing'].with_user( + request.env.ref("base.user_admin")).download_file_tmp( + ret['folder_name'], r) + if download_state == 0: + res['status'] = -2 + res['message'] = '制造订单号为%s的CNC程序文件从FTP拉取失败' % (cnc_production.name) + return json.JSONEncoder().encode(res) + for production in productions: + if not production.workorder_ids: + production.product_id.model_processing_panel = ret['processing_panel'] + production._create_workorder(ret) + else: + for panel in ret['processing_panel'].split(','): + # 查询状态为进行中且工序类型为CNC加工的工单 + cnc_workorder = production.workorder_ids.filtered( + lambda ac: ac.routing_type == 'CNC加工' and ac.state not in ['progress', 'done', + 'cancel'] and ac.processing_panel == panel) + if cnc_workorder: + if cnc_workorder.cnc_ids: + cnc_workorder.cmm_ids.sudo().unlink() + cnc_workorder.cnc_ids.sudo().unlink() + request.env['sf.cam.work.order.program.knife.plan'].sudo().unlink_cam_plan( + production) + # program_path_tmp_panel = os.path.join('C://Users//43484//Desktop//fsdownload//test', + # panel) + program_path_tmp_panel = os.path.join('/tmp', ret['folder_name'], 'return', 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] + logging.info('file_extension:%s' % file_extension) + 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) + cnc_workorder.write( + {'is_send_program_again': False, + 'cnc_ids': cnc_workorder.cnc_ids.sudo()._json_cnc_processing(panel, ret), + 'cmm_ids': cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel, ret), + 'cnc_worksheet': base64.b64encode(open(panel_file_path, 'rb').read())}) + pre_workorder = production.workorder_ids.filtered( + lambda ap: ap.routing_type == '装夹预调' and ap.state not in ['done', + 'cancel'] and ap.processing_panel == panel) + if pre_workorder: + pre_workorder.write( + {'is_send_program_again': False, + 'processing_drawing': base64.b64encode(open(panel_file_path, 'rb').read())}) + productions.write({'programming_state': '已编程', 'work_state': '已编程'}) + cnc_program_ids = [item.id for item in productions] + workpiece_delivery = request.env['sf.workpiece.delivery'].sudo().search( + [('production_id', 'in', cnc_program_ids)]) + if workpiece_delivery: + workpiece_delivery.write({'is_cnc_program_down': True}) return json.JSONEncoder().encode(res) else: res = {'status': 0, 'message': '该制造订单暂未开始'} diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py index fd1e231a..db8f4bf9 100644 --- a/sf_plan/models/custom_plan.py +++ b/sf_plan/models/custom_plan.py @@ -19,7 +19,7 @@ class sf_production_plan(models.Model): ('done', '已排程'), ('processing', '加工中'), ('finished', '已完成') - ], string='工单状态', tracking=True) + ], string='状态', tracking=True) state_order = fields.Integer(compute='_compute_state_order', store=True) @@ -36,7 +36,7 @@ class sf_production_plan(models.Model): _order = 'state_order asc, write_date desc' - name = fields.Char(string='工单编号') + name = fields.Char(string='制造订单') active = fields.Boolean(string='已归档', default=True) # selected = fields.Boolean(default=False) # order_number = fields.Char(string='订单号') @@ -52,7 +52,7 @@ class sf_production_plan(models.Model): schedule_setting = fields.Selection([ ('reverse', '倒排'), ('positive', '顺排')], string='排程设置', default='reverse') product_id = fields.Many2one('product.product', '关联产品') - origin = fields.Char(string='订单号') + origin = fields.Char(string='销售订单') # # 加工时长 # process_time = fields.Float(string='加工时长', digits=(16, 2)) # 实际加工时长、实际开始时间、实际结束时间 @@ -200,22 +200,23 @@ class sf_production_plan(models.Model): raise ValidationError("未选择生产线") else: workorder_id_list = record.production_id.workorder_ids.ids - if record.production_id.workorder_ids: - for item in record.production_id.workorder_ids: - if item.name == 'CNC加工': - item.date_planned_finished = datetime.now() + timedelta(days=100) - # item.date_planned_start = record.date_planned_start - item.date_planned_start = self.date_planned_start if self.date_planned_start else datetime.now() - record.sudo().production_id.plan_start_processing_time = item.date_planned_start - item.date_planned_finished = item.date_planned_start + timedelta( - minutes=record.env['mrp.routing.workcenter'].sudo().search( - [('name', '=', 'CNC加工')]).time_cycle) - item.duration_expected = record.env['mrp.routing.workcenter'].sudo().search( - [('name', '=', 'CNC加工')]).time_cycle - record.calculate_plan_time_before(item, workorder_id_list) - record.calculate_plan_time_after(item, workorder_id_list) - record.date_planned_start, record.date_planned_finished = \ - item.date_planned_start, item.date_planned_finished + if record.production_id: + if record.production_id.workorder_ids: + for item in record.production_id.workorder_ids: + if item.name == 'CNC加工': + item.date_planned_finished = datetime.now() + timedelta(days=100) + # item.date_planned_start = record.date_planned_start + item.date_planned_start = self.date_planned_start if self.date_planned_start else datetime.now() + record.sudo().production_id.plan_start_processing_time = item.date_planned_start + item.date_planned_finished = item.date_planned_start + timedelta( + minutes=record.env['mrp.routing.workcenter'].sudo().search( + [('name', '=', 'CNC加工')]).time_cycle) + item.duration_expected = record.env['mrp.routing.workcenter'].sudo().search( + [('name', '=', 'CNC加工')]).time_cycle + record.calculate_plan_time_before(item, workorder_id_list) + record.calculate_plan_time_after(item, workorder_id_list) + record.date_planned_start, record.date_planned_finished = \ + item.date_planned_start, item.date_planned_finished record.state = 'done' # record.production_id.schedule_state = '已排' record.sudo().production_id.schedule_state = '已排' @@ -231,12 +232,12 @@ class sf_production_plan(models.Model): # record.production_id.date_planned_start = record.date_planned_start # record.production_id.date_planned_finished = record.date_planned_finished record.sudo().production_id.production_line_id = record.production_line_id.id - record.sudo().production_id.workorder_ids.filtered( - lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.write( - {'production_line_id': record.production_line_id.id, - 'plan_start_processing_time': record.date_planned_start}) - else: - raise ValidationError("未找到工单") + if record.production_id.workorder_ids: + record.sudo().production_id.workorder_ids.filtered( + lambda b: b.routing_type == "装夹预调").workpiece_delivery_ids.write( + {'production_line_id': record.production_line_id.id, + 'plan_start_processing_time': record.date_planned_start}) + # record.date_planned_finished = record.date_planned_start + timedelta(days=3) # record.state = 'done' return { diff --git a/sf_plan/views/view.xml b/sf_plan/views/view.xml index 3b53efc2..02e595f7 100644 --- a/sf_plan/views/view.xml +++ b/sf_plan/views/view.xml @@ -17,18 +17,18 @@ decoration-danger="state == 'finished'"/> - + - - - - - + + + + + + + @@ -68,7 +68,7 @@ - + @@ -152,16 +152,27 @@ sf.production.plan + - - + + - - - - + + + + + + + + + + + + + + @@ -188,30 +199,41 @@ + + + - 开始时间: - + 销售订单号: + - 结束时间: - - - - 名称: + 制造订单号: + + 订单交期: + + + + 产品名称: + + 数量: - 状态: - + 计划开始时间: + + + + 计划结束时间: + @@ -246,6 +268,7 @@ ir.actions.act_window sf.production.plan tree,gantt,form + {'search_default_group_by_state': 1, 'search_default_draft': 1, 'display_complete': True} 功能刀具出入库记录 stock.move.line - + @@ -531,7 +532,7 @@ ref="sf_tool_management.sf_inbound_and_outbound_records_of_functional_tools_view_tree"/> - [('functional_tool_name', '!=', False)] + [('rfid', '!=', ''),('functional_tool_name', '!=', '')]