From 10e995ec7f76dc935da66f40e9240a915ad35302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Mon, 16 Jun 2025 16:25:01 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E4=BA=A7=E9=87=8F=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index cefa4ba8..b94d70bf 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -377,7 +377,11 @@ class Sf_Dashboard_Connect(http.Controller): line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')]) line_list = list(map(lambda x: x.name, line_list_obj)) # print('line_list: %s' % line_list) - res['LineList'] = line_list + res['LineList'] = ['业绩总览'] + res['LineList'] += line_list + res['LineList'].append('人工线下加工中心') + # 增加“业绩总览”与“人工线下加工中心” + except Exception as e: res = {'Succeed': False, 'ErrorCode': 202, 'Error': e} @@ -401,37 +405,57 @@ class Sf_Dashboard_Connect(http.Controller): try: plan_obj = request.env['sf.production.plan'].sudo() - production_obj = request.env['mrp.production'].sudo() + # production_obj = request.env['mrp.production'].sudo() work_order_obj = request.env['mrp.workorder'].sudo() line_list = ast.literal_eval(kw['line_list']) + + line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')]) + cnc_line_list = list(map(lambda x: x.name, line_list_obj)) # print('line_list: %s' % line_list) for line in line_list: + if line == '业绩总览': + work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] + plan_domain = [] + quality_check_domain = [('production_line_id.name', 'in', cnc_line_list + ['人工线下加工中心'])] + # production_domain = [] + elif line == '人工线下加工中心': + work_order_domain = [('routing_type', '=', '人工线下加工')] + plan_domain = [('production_type', '=', '人工线下加工')] + quality_check_domain = [('production_line_id.name', '=', line)] + # production_domain = [] + else: + work_order_domain = [ + ('production_line_id.name', '=', line), + ('routing_type', '=', 'CNC加工') + ] + plan_domain = [('production_line_id.name', '=', line)] + quality_check_domain = [('operation_id.name', '=', 'CNC加工')] + # production_domain = [] # # 工单计划量 # plan_data_total_counts = production_obj.search_count( # [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']), # ('active', '=', True)]) # 工单计划量切换为CNC工单 - plan_data_total_counts = work_order_obj.search_count( - [('production_line_id.name', '=', line), ('id', '!=', 8061), - ('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')]) + plan_data_total_counts = work_order_obj.search_count(work_order_domain + [ + ('id', '!=', 8061), + ('state', 'in', ['ready', 'progress', 'done']) + ]) # # 工单完成量 # plan_data_finish_counts = plan_obj.search_count( # [('production_line_id.name', '=', line), ('state', 'in', ['finished'])]) # 工单完成量切换为CNC工单 - plan_data_finish_counts = work_order_obj.search_count( - [('production_line_id.name', '=', line), - ('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')]) + plan_data_finish_counts = work_order_obj.search_count(work_order_domain + [ + ('state', 'in', ['done']) + ]) # 超期完成量 # 搜索所有已经完成的工单 - plan_data_overtime = work_order_obj.search([ - ('production_line_id.name', '=', line), - ('state', 'in', ['done']), - ('routing_type', '=', 'CNC加工') + plan_data_overtime = work_order_obj.search(work_order_domain + [ + ('state', 'in', ['done']) ]) # 使用 filtered 进行字段比较 @@ -443,24 +467,21 @@ class Sf_Dashboard_Connect(http.Controller): plan_data_overtime_counts = len(plan_data_overtime_counts) # 查找符合条件的生产计划记录 - plan_data = plan_obj.search([ - ('production_line_id.name', '=', line), - ]) + plan_data = plan_obj.search(plan_domain) # 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录 # faulty_plans = plan_data.filtered(lambda p: any( # result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids # )) - faulty_plans = request.env['quality.check'].sudo().search([ - ('operation_id.name', '=', 'CNC加工'), + faulty_plans = request.env['quality.check'].sudo().search(quality_check_domain + [ ('quality_state', 'in', ['fail']) ]) # 查找制造订单取消与归档的数量 - cancel_order_count = production_obj.search_count( - [('production_line_id.name', '=', line), ('state', 'in', ['cancel']), - ('active', '=', False)]) + # cancel_order_count = production_obj.search_count( + # [('production_line_id.name', '=', line), ('state', 'in', ['cancel']), + # ('active', '=', False)]) # 计算符合条件的记录数量 # plan_data_fault_counts = len(faulty_plans) + cancel_order_count @@ -468,8 +489,9 @@ class Sf_Dashboard_Connect(http.Controller): # 工单返工数量 - plan_data_rework_counts = plan_obj.search_count( - [('production_line_id.name', '=', line), ('production_id.state', 'in', ['rework'])]) + plan_data_rework_counts = plan_obj.search_count(plan_domain + [ + ('production_id.state', 'in', ['rework']) + ]) # 工单完成率 finishe_rate = round( @@ -479,8 +501,9 @@ class Sf_Dashboard_Connect(http.Controller): plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts # 完成记录 - plan_data_finish_orders = plan_obj.search( - [('production_line_id.name', '=', line), ('state', 'in', ['finished'])]) + plan_data_finish_orders = plan_obj.search(plan_domain + [ + ('state', 'in', ['finished']) + ]) # # 检测量 # detection_nums = 0 From 61034c34244f8fed1cb9425e813b8e6dba0f7550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Mon, 16 Jun 2025 17:33:55 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 62 +++++++++++-------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index b94d70bf..4f8fdce3 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -438,20 +438,24 @@ class Sf_Dashboard_Connect(http.Controller): # ('active', '=', True)]) # 工单计划量切换为CNC工单 - plan_data_total_counts = work_order_obj.search_count(work_order_domain + [ + plan_data_total = work_order_obj.search(work_order_domain + [ ('id', '!=', 8061), ('state', 'in', ['ready', 'progress', 'done']) ]) + plan_data_total_counts = sum(plan_data_total.mapped('qty_produced')) + # # 工单完成量 # plan_data_finish_counts = plan_obj.search_count( # [('production_line_id.name', '=', line), ('state', 'in', ['finished'])]) # 工单完成量切换为CNC工单 - plan_data_finish_counts = work_order_obj.search_count(work_order_domain + [ + plan_data_finish = work_order_obj.search(work_order_domain + [ ('state', 'in', ['done']) ]) + plan_data_finish_counts = sum(plan_data_finish.mapped('qty_produced')) + # 超期完成量 # 搜索所有已经完成的工单 plan_data_overtime = work_order_obj.search(work_order_domain + [ @@ -464,7 +468,8 @@ class Sf_Dashboard_Connect(http.Controller): ) # 获取数量 - plan_data_overtime_counts = len(plan_data_overtime_counts) + # plan_data_overtime_counts = len(plan_data_overtime_counts) + plan_data_overtime_counts = sum(plan_data_overtime_counts.mapped('qty_produced')) # 查找符合条件的生产计划记录 plan_data = plan_obj.search(plan_domain) @@ -485,7 +490,8 @@ class Sf_Dashboard_Connect(http.Controller): # 计算符合条件的记录数量 # plan_data_fault_counts = len(faulty_plans) + cancel_order_count - plan_data_fault_counts = len(faulty_plans) + # plan_data_fault_counts = len(faulty_plans) + plan_data_fault_counts = sum(faulty_plans.workorder_id.mapped('qty_produced')) # 工单返工数量 @@ -631,11 +637,22 @@ class Sf_Dashboard_Connect(http.Controller): date_list.append(current_date) current_date += timedelta(days=1) return date_list + for line in line_list: date_field_name = 'date_finished' # 替换为你模型中的实际字段名 order_counts = [] + if line == '业绩总览': + work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] + elif line == '人工线下加工中心': + work_order_domain = [('routing_type', '=', '人工线下加工')] + else: + work_order_domain = [ + ('production_line_id.name', '=', line), + ('routing_type', '=', 'CNC加工') + ] + if time_unit == 'hour': time_intervals = get_time_intervals(begin_time, end_time, time_unit) print('============================= %s' % time_intervals) @@ -652,9 +669,7 @@ class Sf_Dashboard_Connect(http.Controller): # (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间 # ]) - orders = request.env['mrp.workorder'].sudo().search([ - ('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来 - ('production_line_id.name', '=', line), + orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ ('state', 'in', ['done']), (date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')), (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) @@ -674,25 +689,22 @@ class Sf_Dashboard_Connect(http.Controller): for date in date_list: next_day = date + timedelta(days=1) - orders = request.env['mrp.workorder'].sudo().search( - [('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']), - ('routing_type', '=', 'CNC加工'), - (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), - (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) - ]) + orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ + ('state', 'in', ['done']), + (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), + (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) + ]) - rework_orders = request.env['mrp.workorder'].sudo().search( - [('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']), - ('routing_type', '=', 'CNC加工'), - (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), - (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) - ]) - not_passed_orders = request.env['mrp.workorder'].sudo().search( - [('production_id.production_line_id.name', '=', line), ('state', 'in', ['scrap', 'cancel']), - ('routing_type', '=', 'CNC加工'), - (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), - (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) - ]) + rework_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ + ('state', 'in', ['rework']), + (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), + (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) + ]) + not_passed_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ + ('state', 'in', ['scrap', 'cancel']), + (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), + (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) + ]) order_counts.append({ 'date': date.strftime('%Y-%m-%d'), 'order_count': len(orders), From d21e0c7fd922eeaebf7a58697979fd5c51939349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Thu, 19 Jun 2025 10:54:33 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=A7=E5=B1=8F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=BA=BF=E4=B8=8B=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 132 +++++++++++------- 1 file changed, 79 insertions(+), 53 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 4f8fdce3..1ba59c6b 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -417,21 +417,15 @@ class Sf_Dashboard_Connect(http.Controller): if line == '业绩总览': work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] plan_domain = [] - quality_check_domain = [('production_line_id.name', 'in', cnc_line_list + ['人工线下加工中心'])] - # production_domain = [] elif line == '人工线下加工中心': work_order_domain = [('routing_type', '=', '人工线下加工')] plan_domain = [('production_type', '=', '人工线下加工')] - quality_check_domain = [('production_line_id.name', '=', line)] - # production_domain = [] else: work_order_domain = [ ('production_line_id.name', '=', line), ('routing_type', '=', 'CNC加工') ] plan_domain = [('production_line_id.name', '=', line)] - quality_check_domain = [('operation_id.name', '=', 'CNC加工')] - # production_domain = [] # # 工单计划量 # plan_data_total_counts = production_obj.search_count( # [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']), @@ -472,15 +466,15 @@ class Sf_Dashboard_Connect(http.Controller): plan_data_overtime_counts = sum(plan_data_overtime_counts.mapped('qty_produced')) # 查找符合条件的生产计划记录 - plan_data = plan_obj.search(plan_domain) + # plan_data = plan_obj.search(plan_domain) # 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录 # faulty_plans = plan_data.filtered(lambda p: any( # result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids # )) - faulty_plans = request.env['quality.check'].sudo().search(quality_check_domain + [ - ('quality_state', 'in', ['fail']) + faulty_plans = work_order_obj.search(work_order_domain + [ + ('state', 'in', ['scrap', 'rework']) ]) # 查找制造订单取消与归档的数量 @@ -491,14 +485,16 @@ class Sf_Dashboard_Connect(http.Controller): # 计算符合条件的记录数量 # plan_data_fault_counts = len(faulty_plans) + cancel_order_count # plan_data_fault_counts = len(faulty_plans) - plan_data_fault_counts = sum(faulty_plans.workorder_id.mapped('qty_produced')) + plan_data_fault_counts = sum(faulty_plans.mapped('qty_produced')) # 工单返工数量 - plan_data_rework_counts = plan_obj.search_count(plan_domain + [ - ('production_id.state', 'in', ['rework']) + plan_data_rework = work_order_obj.search(plan_domain + [ + ('state', 'in', ['rework']) ]) + plan_data_rework_counts = sum(plan_data_rework.mapped('qty_produced')) + # 工单完成率 finishe_rate = round( (plan_data_finish_counts / plan_data_total_counts if plan_data_total_counts > 0 else 0), 3) @@ -563,25 +559,25 @@ class Sf_Dashboard_Connect(http.Controller): delay_rate = round((delay_num / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3) on_time_rate = 1 - delay_rate - if plan_data: - data = { - 'plan_data_total_counts': plan_data_total_counts, - 'plan_data_finish_counts': plan_data_finish_counts, - 'plan_data_plan_counts': plan_data_total_counts, - 'plan_data_fault_counts': plan_data_fault_counts, - 'nopass_orders_counts': detection_data - len(pass_nums), - 'finishe_rate': finishe_rate, - 'plan_data_progress_deviation': plan_data_progress_deviation, - 'plan_data_rework_counts': plan_data_rework_counts, - 'on_time_rate': on_time_rate, - # 'detection_data': detection_data, - 'detection_data': plan_data_finish_counts, - 'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts, - 'plan_data_overtime_counts': plan_data_overtime_counts, - 'overtime_rate': plan_data_overtime_counts / plan_data_finish_counts - if plan_data_finish_counts > 0 else 0, - } - res['data'][line] = data + # if plan_data: + data = { + 'plan_data_total_counts': plan_data_total_counts, + 'plan_data_finish_counts': plan_data_finish_counts, + 'plan_data_plan_counts': plan_data_total_counts, + 'plan_data_fault_counts': plan_data_fault_counts, + 'nopass_orders_counts': detection_data - len(pass_nums), + 'finishe_rate': finishe_rate, + 'plan_data_progress_deviation': plan_data_progress_deviation, + 'plan_data_rework_counts': plan_data_rework_counts, + 'on_time_rate': on_time_rate, + # 'detection_data': detection_data, + 'detection_data': plan_data_finish_counts, + 'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts, + 'plan_data_overtime_counts': plan_data_overtime_counts, + 'overtime_rate': plan_data_overtime_counts / plan_data_finish_counts + if plan_data_finish_counts > 0 else 0, + } + res['data'][line] = data return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode() @@ -677,7 +673,8 @@ class Sf_Dashboard_Connect(http.Controller): # 使用小时和分钟作为键,确保每个小时的数据有独立的键 key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键 - time_count_dict[key] = len(orders) + # time_count_dict[key] = len(orders) + time_count_dict[key] = sum(orders.mapped('qty_produced')) # order_counts.append() res['data'][line] = { 'finish_order_nums': time_count_dict, @@ -707,9 +704,9 @@ class Sf_Dashboard_Connect(http.Controller): ]) order_counts.append({ 'date': date.strftime('%Y-%m-%d'), - 'order_count': len(orders), - 'rework_orders': len(rework_orders), - 'not_passed_orders': len(not_passed_orders) + 'order_count': sum(orders.mapped('qty_produced')), + 'rework_orders': sum(rework_orders.mapped('qty_produced')), + 'not_passed_orders': sum(not_passed_orders.mapped('qty_produced')) }) # 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div # 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理 @@ -723,7 +720,7 @@ class Sf_Dashboard_Connect(http.Controller): @http.route('/api/RealTimeProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") def RealTimeProduct(self, **kw): """ - 获取实时产量 + 获取实时产量(作废) :param kw: :return: """ @@ -746,6 +743,21 @@ class Sf_Dashboard_Connect(http.Controller): # 当班计划量 for line in line_list: + + if line == '业绩总览': + work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] + plan_domain = [] + elif line == '人工线下加工中心': + work_order_domain = [('routing_type', '=', '人工线下加工')] + plan_domain = [('production_type', '=', '人工线下加工')] + else: + work_order_domain = [ + ('production_line_id.name', '=', line), + ('routing_type', '=', 'CNC加工') + ] + plan_domain = [('production_line_id.name', '=', line)] + + plan_order_nums = plan_obj.search_count( [('production_line_id.name', '=', line), ('state', 'not in', ['draft']), ('date_planned_start', '>=', begin_time), @@ -791,6 +803,7 @@ class Sf_Dashboard_Connect(http.Controller): # res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []} res = {'status': 1, 'message': '成功', 'data': {}} plan_obj = request.env['sf.production.plan'].sudo() + work_order_obj = request.env['mrp.workorder'].sudo() line_list = ast.literal_eval(kw['line_list']) begin_time_str = kw['begin_time'].strip('"') end_time_str = kw['end_time'].strip('"') @@ -800,28 +813,39 @@ class Sf_Dashboard_Connect(http.Controller): not_done_data = [] done_data = [] final_data = {} + not_done_index = 1 + done_index = 1 for line in line_list: + + if line == '业绩总览': + work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] + elif line == '人工线下加工中心': + work_order_domain = [('routing_type', '=', '人工线下加工')] + else: + work_order_domain = [ + ('production_line_id.name', '=', line), + ('routing_type', '=', 'CNC加工') + ] # 未完成订单 # not_done_orders = plan_obj.search( # [('production_line_id.name', '=', line), ('state', 'not in', ['finished']), # ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True) # ]) - not_done_orders = request.env['mrp.workorder'].sudo().search( - [('production_line_id.name', '=', line), ('state', 'in', ['ready', 'progress']), - ('routing_type', '=', 'CNC加工') - ]) + not_done_orders = work_order_obj.search(work_order_domain + + [('state', 'in', ['ready', 'progress'])], order='id asc' + ) # 完成订单 # 获取当前时间,并计算24小时前的时间 current_time = datetime.now() time_24_hours_ago = current_time - timedelta(hours=24) - finish_orders = plan_obj.search([ - ('production_line_id.name', '=', line), ('state', 'in', ['finished']), - ('production_id.state', 'not in', ['cancel']), ('active', '=', True), - ('actual_end_time', '>=', time_24_hours_ago) - ]) + finish_orders = work_order_obj.search(work_order_domain + [ + ('state', 'in', ['finished']), + ('production_id.state', 'not in', ['cancel']), + ('date_finished', '>=', time_24_hours_ago) + ], order='id asc') # print(finish_orders) # 获取所有未完成订单的ID列表 @@ -830,14 +854,14 @@ class Sf_Dashboard_Connect(http.Controller): finish_order_ids = [order.id for order in finish_orders] # 对ID进行排序 - sorted_order_ids = sorted(order_ids) + # sorted_order_ids = sorted(order_ids) - finish_sorted_order_ids = sorted(finish_order_ids) + # finish_sorted_order_ids = sorted(finish_order_ids) # 创建ID与序号的对应关系 - id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(sorted_order_ids)} + # id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(sorted_order_ids)} - finish_id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(finish_sorted_order_ids)} + # finish_id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(finish_sorted_order_ids)} # # 输出结果或进一步处理 # for order_id, sequence in id_to_sequence.items(): @@ -868,16 +892,17 @@ class Sf_Dashboard_Connect(http.Controller): } line_dict = { - 'sequence': id_to_sequence[order.id], + 'sequence': not_done_index, 'workorder_name': order.production_id.name, 'blank_name': blank_name, 'material': material, 'dimensions': dimensions, - 'order_qty': 1, + 'order_qty': order.qty_production, 'state': state_dict[order.state], } not_done_data.append(line_dict) + not_done_index += 1 for finish_order in finish_orders: if not finish_order.actual_end_time: @@ -896,17 +921,18 @@ class Sf_Dashboard_Connect(http.Controller): material = material_match.group(1) if material_match else 'No match found' line_dict = { - 'sequence': finish_id_to_sequence[finish_order.id], + 'sequence': done_index, 'workorder_name': finish_order.name, 'blank_name': blank_name, 'material': material, 'dimensions': dimensions, - 'order_qty': finish_order.product_qty, + 'order_qty': order.qty_produced, 'finish_time': finish_order.actual_end_time.strftime( '%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' ' } done_data.append(line_dict) + done_index += 1 # 开始包一层 res['data'][line] = {'not_done_data': not_done_data, 'done_data': done_data} From 55cc4906ef8b3059d37cc2597d9cf4f7ad768600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=83=A1=E5=B0=A7?= Date: Thu, 19 Jun 2025 15:02:55 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=A4=A7=E5=B1=8F?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=97=A5=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E9=87=8F=E7=BB=9F=E8=AE=A1,=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E4=BA=A7=E7=BA=BF=E4=BA=A7=E9=87=8F=E7=9B=B8=E5=85=B3,?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E5=B7=A5=E5=8D=95=E6=98=8E=E7=BB=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sf_machine_connect/controllers/controllers.py | 160 ++++++++++++------ 1 file changed, 109 insertions(+), 51 deletions(-) diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index 1ba59c6b..4aaf8ff6 100644 --- a/sf_machine_connect/controllers/controllers.py +++ b/sf_machine_connect/controllers/controllers.py @@ -635,25 +635,32 @@ class Sf_Dashboard_Connect(http.Controller): return date_list - for line in line_list: - date_field_name = 'date_finished' # 替换为你模型中的实际字段名 - order_counts = [] + if time_unit == 'hour': + + for line in line_list: + date_field_name = 'date_finished' # 替换为你模型中的实际字段名 + order_counts = [] - if line == '业绩总览': - work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] - elif line == '人工线下加工中心': - work_order_domain = [('routing_type', '=', '人工线下加工')] - else: - work_order_domain = [ - ('production_line_id.name', '=', line), - ('routing_type', '=', 'CNC加工') - ] - - if time_unit == 'hour': + if line == '业绩总览': + work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] + elif line == '人工线下加工中心': + work_order_domain = [('routing_type', '=', '人工线下加工')] + else: + work_order_domain = [ + ('production_line_id.name', '=', line), + ('routing_type', '=', 'CNC加工') + ] time_intervals = get_time_intervals(begin_time, end_time, time_unit) print('============================= %s' % time_intervals) time_count_dict = {} + plan_count_dict = {} + + orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ + ('state', 'in', ['done']), + (date_field_name, '>=', begin_time.strftime('%Y-%m-%d %H:%M:%S')), + (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) + ]) for time_interval in time_intervals: start_time, end_time = time_interval @@ -665,55 +672,106 @@ class Sf_Dashboard_Connect(http.Controller): # (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间 # ]) - orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ - ('state', 'in', ['done']), - (date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')), - (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) - ]) + interval_orders = orders.filtered( + lambda o: o[date_field_name] >= start_time + and o[date_field_name] <= end_time + ) # 使用小时和分钟作为键,确保每个小时的数据有独立的键 key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键 # time_count_dict[key] = len(orders) - time_count_dict[key] = sum(orders.mapped('qty_produced')) + time_count_dict[key] = sum(interval_orders.mapped('qty_produced')) + + # 计划量,目前只能从mail.message中筛选出 + plan_order_messages = request.env['mail.message'].sudo().search([ + ('model', '=', 'mrp.workorder'), + ('create_date', '>=', begin_time.strftime('%Y-%m-%d %H:%M:%S')), + ('create_date', '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')), + ('tracking_value_ids.field_desc', '=', '状态'), + ('tracking_value_ids.new_value_char', '=', '就绪') + ]) + + for time_interval in time_intervals: + start_time, end_time = time_interval + + # orders = plan_obj.search([ + # ('production_line_id.name', '=', line), + # ('state', 'in', ['done']), + # (date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')), + # (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间 + # ]) + + interval_plan_orders = plan_order_messages.filtered( + lambda o: o.create_date >= start_time + and o.create_date <= end_time + ) + + interval_orders = request.env['mrp.workorder'].sudo().browse(interval_plan_orders.mapped('res_id')) + if line == '业绩总览': + interval_orders = interval_orders.filtered(lambda o: o.routing_type in ['人工线下加工', 'CNC加工']) + elif line == '人工线下加工中心': + interval_orders = interval_orders.filtered(lambda o: o.routing_type == '人工线下加工') + else: + interval_orders = interval_orders.filtered(lambda o: o.routing_type == 'CNC加工' and o.production_line_id.name == line) + + # 使用小时和分钟作为键,确保每个小时的数据有独立的键 + key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键 + # time_count_dict[key] = len(orders) + plan_count_dict[key] = sum(interval_orders.mapped('qty_produced')) + # order_counts.append() res['data'][line] = { 'finish_order_nums': time_count_dict, - 'plan_order_nums': 28 + 'plan_order_nums': plan_count_dict } - return json.dumps(res) + else: - date_list = get_date_list(begin_time, end_time) + for line in line_list: + date_field_name = 'date_finished' # 替换为你模型中的实际字段名 + order_counts = [] - for date in date_list: - next_day = date + timedelta(days=1) - orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ - ('state', 'in', ['done']), - (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), - (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) - ]) + if line == '业绩总览': + work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])] + elif line == '人工线下加工中心': + work_order_domain = [('routing_type', '=', '人工线下加工')] + else: + work_order_domain = [ + ('production_line_id.name', '=', line), + ('routing_type', '=', 'CNC加工') + ] - rework_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ - ('state', 'in', ['rework']), - (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), - (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) - ]) - not_passed_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ - ('state', 'in', ['scrap', 'cancel']), - (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), - (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) - ]) - order_counts.append({ - 'date': date.strftime('%Y-%m-%d'), - 'order_count': sum(orders.mapped('qty_produced')), - 'rework_orders': sum(rework_orders.mapped('qty_produced')), - 'not_passed_orders': sum(not_passed_orders.mapped('qty_produced')) - }) - # 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div - # 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理 + date_list = get_date_list(begin_time, end_time) - # date_list_dict = {line: order_counts} + for date in date_list: + next_day = date + timedelta(days=1) + orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ + ('state', 'in', ['done']), + (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), + (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) + ]) - res['data'][line] = order_counts + rework_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ + ('state', 'in', ['rework']), + (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), + (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) + ]) + not_passed_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [ + ('state', 'in', ['scrap', 'cancel']), + (date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')), + (date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00')) + ]) + order_counts.append({ + 'date': date.strftime('%Y-%m-%d'), + 'order_count': sum(orders.mapped('qty_produced')), + 'rework_orders': sum(rework_orders.mapped('qty_produced')), + 'not_passed_orders': sum(not_passed_orders.mapped('qty_produced')) + }) + # 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div + # 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理 + + # date_list_dict = {line: order_counts} + + res['data'][line] = order_counts return json.dumps(res) # 实时产量 @@ -799,7 +857,7 @@ class Sf_Dashboard_Connect(http.Controller): :param kw: :return: """ - + request.env['stock.warehouse'].browse(request.env.company.id).pbm_loc_id # res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []} res = {'status': 1, 'message': '成功', 'data': {}} plan_obj = request.env['sf.production.plan'].sudo()