diff --git a/sf_machine_connect/controllers/controllers.py b/sf_machine_connect/controllers/controllers.py index cefa4ba8..4aaf8ff6 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,55 @@ 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 = [] + 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_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 = 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( - [('production_line_id.name', '=', line), - ('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')]) + 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([ - ('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 进行字段比较 @@ -440,36 +462,38 @@ 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([ - ('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加工'), - ('quality_state', 'in', ['fail']) + faulty_plans = work_order_obj.search(work_order_domain + [ + ('state', 'in', ['scrap', 'rework']) ]) # 查找制造订单取消与归档的数量 - 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 - plan_data_fault_counts = len(faulty_plans) + # plan_data_fault_counts = len(faulty_plans) + plan_data_fault_counts = sum(faulty_plans.mapped('qty_produced')) # 工单返工数量 - plan_data_rework_counts = plan_obj.search_count( - [('production_line_id.name', '=', line), ('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( @@ -479,8 +503,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 @@ -534,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() @@ -608,16 +633,34 @@ 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 time_unit == 'hour': + + for line in line_list: + date_field_name = 'date_finished' # 替换为你模型中的实际字段名 + order_counts = [] - 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 @@ -629,66 +672,113 @@ 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), - ('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] = len(orders) + 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( - [('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')) - ]) + 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( - [('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')) - ]) - 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) - }) - # 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层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) # 实时产量 @http.route('/api/RealTimeProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*") def RealTimeProduct(self, **kw): """ - 获取实时产量 + 获取实时产量(作废) :param kw: :return: """ @@ -711,6 +801,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), @@ -752,10 +857,11 @@ 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() + 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('"') @@ -765,28 +871,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列表 @@ -795,14 +912,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(): @@ -833,16 +950,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: @@ -861,17 +979,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}