diff --git a/sf_manufacturing/models/mrp_production.py b/sf_manufacturing/models/mrp_production.py index 232b4907..65be98c7 100644 --- a/sf_manufacturing/models/mrp_production.py +++ b/sf_manufacturing/models/mrp_production.py @@ -331,6 +331,9 @@ class MrpProduction(models.Model): production.state = 'pending_cam' if production.is_rework is True: production.state = 'rework' + if (production.state == 'rework' and production.tool_state == '0' + and production.schedule_state == '已排' and production.is_rework is False): + production.state = 'pending_cam' # if production.state == 'pending_cam': # if all(wo_state in 'done' for wo_state in production.workorder_ids.mapped('state')): # production.state = 'done' @@ -353,6 +356,7 @@ class MrpProduction(models.Model): if production.tool_state == '2': production.state = 'rework' + # 退回调整 def technology_back_adjust(self): process_parameters = [] diff --git a/sf_manufacturing/models/mrp_workorder.py b/sf_manufacturing/models/mrp_workorder.py index a0bc0bb6..d4d97a2e 100644 --- a/sf_manufacturing/models/mrp_workorder.py +++ b/sf_manufacturing/models/mrp_workorder.py @@ -1097,8 +1097,7 @@ class ResMrpWorkOrder(models.Model): # ================= 如果制造订单刀具状态为[无效刀、缺刀] 或者 制造订单状态为[返工]========================== if (workorder.production_id.tool_state in ['1', '2'] or workorder.production_id.state == 'rework' or workorder.production_id.schedule_state != '已排' - or len( - workorder.production_id.picking_ids.filtered(lambda w: w.state not in ['done', 'cancel'])) != 0 + or workorder.production_id.reservation_state not in ['assigned'] or workorder.production_id.workorder_ids.filtered( lambda wk: wk.sequence == workorder.sequence - 1).test_results in ['报废', '返工']): if workorder.state != 'waiting': diff --git a/sf_manufacturing/models/stock.py b/sf_manufacturing/models/stock.py index 1f540131..41a5837a 100644 --- a/sf_manufacturing/models/stock.py +++ b/sf_manufacturing/models/stock.py @@ -318,7 +318,7 @@ class StockRule(models.Model): if production_item.product_id.id in product_id_to_production_names: # 同一个产品多个制造订单对应一个编程单和模型库 # 只调用一次fetchCNC,并将所有生产订单的名称作为字符串传递 - if not production_item.programming_no and production.production_type == '自动化产线加工': + if not production_item.programming_no and production_item.production_type == '自动化产线加工': if not production_programming.programming_no: production_item.fetchCNC( ', '.join(product_id_to_production_names[production_item.product_id.id])) diff --git a/sf_manufacturing/security/ir.model.access.csv b/sf_manufacturing/security/ir.model.access.csv index 24c38570..cdf62571 100644 --- a/sf_manufacturing/security/ir.model.access.csv +++ b/sf_manufacturing/security/ir.model.access.csv @@ -52,6 +52,7 @@ access_mrp_routing_workcenter_manager_group_sf_mrp_user,mrp.routing.workcenter.m access_mrp_bom_manager_group_sf_mrp_user,mrp.bom.manager,mrp.model_mrp_bom,sf_base.group_sf_mrp_user,1,1,1,0 access_mrp_bom_line_manager_group_sf_mrp_user,mrp.bom.line.manager,mrp.model_mrp_bom_line,sf_base.group_sf_mrp_user,1,1,1,0 access_mrp_bom_line_group_plan_director,mrp_bom_line_group_plan_director,mrp.model_mrp_bom_line,sf_base.group_plan_director,1,1,1,0 +access_mrp_bom_line_group_sf_stock_user,mrp_bom_line_group_sf_stock_user,mrp.model_mrp_bom_line,sf_base.group_sf_stock_user,1,1,1,0 access_mrp_bom_line_group_sale_director,mrp_bom_line_group_sale_director,mrp.model_mrp_bom_line,sf_base.group_sale_director,1,1,1,0 access_mrp_bom_line_group_sale_salemanager,mrp_bom_line_group_sale_salemanager,mrp.model_mrp_bom_line,sf_base.group_sale_salemanager,1,0,1,0 access_mrp_bom_line_group_purchase_director,mrp_bom_line_group_purchase_director,mrp.model_mrp_bom_line,sf_base.group_purchase_director,1,1,1,0 diff --git a/sf_manufacturing/views/model_type_view.xml b/sf_manufacturing/views/model_type_view.xml index f0ffd81a..0f8b3085 100644 --- a/sf_manufacturing/views/model_type_view.xml +++ b/sf_manufacturing/views/model_type_view.xml @@ -31,7 +31,7 @@
- + diff --git a/sf_manufacturing/views/mrp_production_addional_change.xml b/sf_manufacturing/views/mrp_production_addional_change.xml index 8a2e1815..5f9a7ef3 100644 --- a/sf_manufacturing/views/mrp_production_addional_change.xml +++ b/sf_manufacturing/views/mrp_production_addional_change.xml @@ -745,5 +745,12 @@ groups="sf_base.group_plan_dispatch,sf_base.group_sf_mrp_manager" sequence="1"/> + + \ No newline at end of file diff --git a/sf_manufacturing/wizard/rework_wizard.py b/sf_manufacturing/wizard/rework_wizard.py index bb787c11..eeae9ae9 100644 --- a/sf_manufacturing/wizard/rework_wizard.py +++ b/sf_manufacturing/wizard/rework_wizard.py @@ -4,6 +4,7 @@ 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): @@ -37,6 +38,30 @@ class ReworkWizard(models.TransientModel): 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 confirm(self): if self.routing_type in ['装夹预调', 'CNC加工']: self.is_clamp_measure = False @@ -58,16 +83,16 @@ class ReworkWizard(models.TransientModel): # 1、当制造订单内ZM面的工单都已完成时,返工勾选工序时只能勾选上ZM面的所有工序进行返工 # 2、当FM工单在CNC工单进行选择返工,并将已全部完成的ZM面工序全部勾选上时,FM工单上所有的已完成的工单(装夹预调工单)也必须进行勾选 # 获取已完成的标准工单 - done_normative_workorder_ids = self.workorder_ids.filtered( - lambda w: w.state == 'done' and w.processing_panel is not False) - # 获取需要返工的标准工单 - rework_normative_workorder_ids = rework_workorder_ids.filtered( - lambda w: w.processing_panel is not False) - if rework_normative_workorder_ids: - for rw in rework_normative_workorder_ids: - if len(done_normative_workorder_ids.filtered( - lambda w: w.processing_panel == rw.processing_panel)) == 3: - pass + # done_normative_workorder_ids = self.workorder_ids.filtered( + # lambda w: w.state == 'done' and w.processing_panel is not False) + # # 获取需要返工的标准工单 + # rework_normative_workorder_ids = rework_workorder_ids.filtered( + # lambda w: w.processing_panel is not False) + # if rework_normative_workorder_ids: + # for rw in rework_normative_workorder_ids: + # if len(done_normative_workorder_ids.filtered( + # lambda w: w.processing_panel == rw.processing_panel)) == 3: + # pass else: raise ValidationError('请选择返工工单!!!') if rework_workorder_ids: diff --git a/sf_plan/models/custom_plan.py b/sf_plan/models/custom_plan.py index e7785ad4..147caa96 100644 --- a/sf_plan/models/custom_plan.py +++ b/sf_plan/models/custom_plan.py @@ -232,41 +232,38 @@ class sf_production_plan(models.Model): raise ValidationError("未选择生产线") else: # 自动化产线加工 - if record.production_id.production_type == '自动化产线加工': - if record.production_id.workorder_ids: - last_cnc_start = record.date_planned_start if record.date_planned_start else datetime.now() - for item in record.production_id.workorder_ids: - if item.name == 'CNC加工': - # 将同一个面的所有工单筛选出来 - workorder_list = record.production_id.workorder_ids.filtered(lambda x: x.processing_panel == item.processing_panel) - routing_workcenter = record.env['mrp.routing.workcenter'].sudo().search( - [('name', '=', 'CNC加工')], limit=1) - # 设置一个小的开始时间 - item.date_planned_start = datetime.now() - timedelta(days=100) - item.date_planned_finished = last_cnc_start + timedelta( - minutes=routing_workcenter.time_cycle) - item.date_planned_start = last_cnc_start - record.sudo().production_id.plan_start_processing_time = item.date_planned_start - item.duration_expected = routing_workcenter.time_cycle - pre_duration , next_duration = record.calculate_plan_time(item, workorder_list) - record.date_planned_finished = item.date_planned_finished - # 计算下一个cnc工单的开始时间 - last_cnc_start = workorder_list[-1].date_planned_finished + timedelta(minutes=pre_duration) - # 没有工单也能排程 - else: - # 人工线下加工只排第一张工单 - if record.production_id.workorder_ids: - item = record.production_id.workorder_ids[0] - last_wo_start = record.date_planned_start if record.date_planned_start else datetime.now() - routing_workcenter = record.env['mrp.routing.workcenter'].sudo().search( - [('name', '=', item.routing_type)], limit=1) - item.date_planned_start = datetime.now() - timedelta(days=100) - item.date_planned_finished = last_wo_start + timedelta( + if record.production_id.workorder_ids: + # 自动化产线加工 + if record.production_id.production_type == '自动化产线加工': + # 找到第一张CNC加工工单 + first_cnc_workorder = record.production_id.workorder_ids.filtered(lambda x: x.name == 'CNC加工')[0] + date_start = record.date_planned_start if record.date_planned_start else datetime.now() + routing_workcenter = first_cnc_workorder.technology_design_id.route_id + # 设置一个小的开始时间 + first_cnc_workorder.date_planned_start = datetime.now() - timedelta(days=100) + first_cnc_workorder.date_planned_finished = date_start + timedelta( minutes=routing_workcenter.time_cycle) - item.date_planned_start = last_wo_start + first_cnc_workorder.date_planned_start = date_start + record.sudo().production_id.plan_start_processing_time = first_cnc_workorder.date_planned_start + first_cnc_workorder.duration_expected = routing_workcenter.time_cycle + record.calculate_plan_time(first_cnc_workorder, record.production_id.workorder_ids) + # 找到最后一张CNC加工工单 + last_cnc_workorder = record.production_id.workorder_ids.filtered(lambda x: x.name == 'CNC加工')[-1] + record.date_planned_finished = last_cnc_workorder.date_planned_finished + else: + # 人工线下加工只排第一张工单 + item = record.production_id.workorder_ids[0] + wo_start = record.date_planned_start if record.date_planned_start else datetime.now() + routing_workcenter = item.technology_design_id.route_id + item.date_planned_start = datetime.now() - timedelta(days=100) + item.date_planned_finished = wo_start + timedelta( + minutes=routing_workcenter.time_cycle) + item.date_planned_start = wo_start record.sudo().production_id.plan_start_processing_time = item.date_planned_start item.duration_expected = routing_workcenter.time_cycle - record.calculate_plan_time(item, item) + record.calculate_plan_time(item, record.production_id.workorder_ids) + last_cnc_workorder = record.production_id.workorder_ids[-1] + record.date_planned_finished = last_cnc_workorder.date_planned_finished record.state = 'done' # record.production_id.schedule_state = '已排' record.sudo().production_id.schedule_state = '已排' @@ -322,44 +319,40 @@ class sf_production_plan(models.Model): def calculate_plan_time(self, item, workorder_list): """ 根据CNC工单的时间去计算之前的其他工单的开始结束时间 + param: + item: 基准工单(根据该工单的开始结束时间去计算其他工单的开始结束时间) + workorder_list: 需排程的工单列表 """ item_position = 0 for index, workorder in enumerate(workorder_list): if workorder.id == item.id: item_position = index break - routing_workcenters = self.env['mrp.routing.workcenter'].sudo().search([]) - # 记录所有前序工序时长 - previous_workorder_duration = 0 for i in range(item_position, -1, -1): if i < 1: break current_workorder = workorder_list[i] next_workorder = workorder_list[i - 1] - routing_workcenter = routing_workcenters.filtered(lambda x: x.name == next_workorder.name)[0] + routing_workcenter = next_workorder.technology_design_id.route_id # 设置一个小的开始时间 next_workorder.date_planned_start = datetime.now() - timedelta(days=100) next_workorder.date_planned_finished = current_workorder.date_planned_start next_workorder.date_planned_start = next_workorder.date_planned_finished - timedelta( minutes=routing_workcenter.time_cycle) next_workorder.duration_expected = routing_workcenter.time_cycle - previous_workorder_duration += routing_workcenter.time_cycle - # 记录所有后续工序时长 - next_workorder_duration = 0 + for i in range(item_position, len(workorder_list) - 1): if i > len(workorder_list) - 1: break current_workorder = workorder_list[i] next_workorder = workorder_list[i + 1] - routing_workcenter = routing_workcenters.filtered(lambda x: x.name == next_workorder.name)[0] + routing_workcenter = next_workorder.technology_design_id.route_id # 设置一个小的开始时间 next_workorder.date_planned_start = datetime.now() - timedelta(days=100) next_workorder.date_planned_finished = current_workorder.date_planned_finished + timedelta( minutes=routing_workcenter.time_cycle) next_workorder.date_planned_start = current_workorder.date_planned_finished next_workorder.duration_expected = routing_workcenter.time_cycle - next_workorder_duration += routing_workcenter.time_cycle - return previous_workorder_duration, next_workorder_duration def calculate_plan_time_after(self, item, workorder_id_list): """ diff --git a/sf_quality/security/ir.model.access.csv b/sf_quality/security/ir.model.access.csv index 19818982..907c115b 100644 --- a/sf_quality/security/ir.model.access.csv +++ b/sf_quality/security/ir.model.access.csv @@ -11,6 +11,7 @@ access_quality_point_group_sf_mrp_manager,quality_point_group_sf_mrp_manager,qua access_quality_check_group_quality,quality_check_group_quality,quality.model_quality_check,sf_base.group_quality,1,1,1,0 access_quality_check_group_quality_director,quality_check_group_quality_director,quality.model_quality_check,sf_base.group_quality_director,1,1,1,0 access_quality_check_group_plan_dispatch,quality_check_group_plan_dispatch,quality.model_quality_check,sf_base.group_plan_dispatch,1,0,0,0 +access_quality_check_group_sf_stock_user,quality_check_group_sf_stock_user,quality.model_quality_check,sf_base.group_sf_stock_user,1,0,0,0 access_quality_check_group_plan_director,quality_check_group_plan_director,quality.model_quality_check,sf_base.group_plan_director,1,0,0,0 access_quality_check_group_purchase,quality_check_group_purchase,quality.model_quality_check,sf_base.group_purchase,1,0,0,0 access_quality_check_group_purchase_director,quality_check_group_purchase_director,quality.model_quality_check,sf_base.group_purchase_director,1,0,0,0