from apps.mtm.models import Goal, Mgroup, RoutePack, Route from django.core.cache import cache from django.db.models import Q from apps.mtm.models import Material, Process, Shift from rest_framework.exceptions import ParseError from apps.utils.tools import ranstr from datetime import datetime, timedelta from apps.wf.models import Ticket from django.db.models import Sum from typing import List from apps.utils.snowflake import idWorker from django.utils import timezone def get_mgroup_goals(mgroupId, year, reload=False): """ 获取工段某年的全部目标值, 以字典形式返回, 带缓存 """ goals = Goal.objects.filter(mgroup__id=mgroupId, year=year) key = f'mgroup_{mgroupId}_goals_{year}' if reload is False: mgroup_goals = cache.get(key, None) if mgroup_goals is not None: return mgroup_goals mgroup_goals = {} if not goals.exists(): # 尝试寻找去年的 goals_last_year = Goal.objects.filter(mgroup__id=mgroupId, year=year-1) if goals_last_year.exists(): for goal in goals_last_year: # 复用去年的数据创建 goal.id = idWorker.get_id() goal.create_time = timezone.now() goal.update_time = goal.create_time goal.year = year goal.save() goals = Goal.objects.filter(mgroup__id=mgroupId, year=year) for goal in goals: mgroup_goals[f'{goal.goal_cate.code}_year'] = goal.goal_val for i in range(12): mgroup_goals[f'{goal.goal_cate.code}_{i+1}'] = getattr( goal, f'goal_val_{i+1}') cache.set(key, mgroup_goals) return mgroup_goals def daoru_material(path: str): """ 导入物料信息 """ type_dict = {'主要原料': 30, '半成品': 20, '成品': 10, '辅助材料': 40, '加工工具': 50, '辅助工装': 60, '办公用品': 70} from apps.utils.snowflake import idWorker from openpyxl import load_workbook wb = load_workbook(path, read_only=True) sheet = wb.active process_l = Process.objects.all() process_d = {p.name: p for p in process_l} i = 3 if sheet['a2'].value != '物料编号': raise ParseError('列错误导入失败') while sheet[f'b{i}'].value is not None or sheet[f'd{i}'].value is not None: type_str = sheet[f'b{i}'].value.replace(' ', '') try: type = type_dict[type_str] cate = sheet[f'c{i}'].value.replace(' ', '') if sheet[f'c{i}'].value else "" number = str(sheet[f'a{i}'].value).replace(' ', '') if sheet[f'a{i}'].value else None if sheet[f'd{i}'].value: name = str(sheet[f'd{i}'].value).replace(' ', '') else: raise ParseError(f'{i}行物料信息错误: 物料名称必填') specification = str(sheet[f'e{i}'].value).replace( '×', '*').replace(' ', '') if sheet[f'e{i}'].value else None model = str(sheet[f'f{i}'].value).replace(' ', '') if sheet[f'f{i}'].value else None unit = sheet[f'g{i}'].value.replace(' ', '') count_safe = float(sheet[f'i{i}'].value) if sheet[f'i{i}'].value else None unit_price = float(sheet[f'j{i}'].value) if sheet[f'j{i}'].value else None bin_number_main = sheet[f'k{i}'].value.replace(' ', '') if sheet[f'k{i}'].value else None except Exception as e: raise ParseError(f'{i}行物料信息错误: {e}') if type in [20, 30]: try: process = process_d[sheet[f'h{i}'].value.replace(' ', '')] except Exception as e: raise ParseError(f'{i}行物料信息错误: {e}') try: filters = {'type': type, 'name': name, 'specification': specification, 'model': model, 'unit__iexact': unit} if type in [20, 30]: filters['process'] = process default = {'type': type, 'name': name, 'specification': specification, 'model': model, 'unit': unit, 'number': number if number else f'm{type}_{ranstr(6)}', 'id': idWorker.get_id(), 'count_safe': count_safe, 'unit_price': unit_price, 'cate': cate, 'bin_number_main': bin_number_main} material, is_created = Material.objects.get_or_create( **filters, defaults=default) if not is_created: material.count_safe = count_safe material.save() except Exception as e: raise ParseError(f'{i}行物料有误, 导入失败--{e}') i = i + 1 print(number, type, name, specification, model, unit, '导入成功') def mgroup_run_change(mgroup: Mgroup, new_run: bool, last_timex: datetime, note: str = ''): """ 调用工段运行变动 """ from apps.wpm.services import get_sflog from apps.wpm.tasks import cal_exp_duration_sec from apps.wpm.models import StLog, SfLogExp mgroup.is_running = new_run mgroup.save(update_fields=["is_running"]) last_stlog = StLog.objects.filter(mgroup=mgroup, is_shutdown=True).order_by("-start_time").first() # 找到最后一次停机记录 need_cal_exp = True if last_stlog: if last_timex >= last_stlog.start_time: # 认为是有效信号 if last_stlog.end_time is None and new_run: # 从停到开 last_stlog.end_time = last_timex last_stlog.duration_sec = (last_stlog.end_time - last_stlog.start_time).total_seconds() last_stlog.save() elif last_stlog.end_time and new_run is False and last_timex > last_stlog.end_time: # 从开到停 cur_sflog = get_sflog(mgroup, last_timex) last_stlog, is_created = StLog.objects.get_or_create( mgroup=mgroup, is_shutdown=True, start_time=last_timex, defaults={ 'title': '停机', 'end_time': None, 'sflog': cur_sflog, 'reason':note, } ) if is_created: SfLogExp.objects.get_or_create(stlog=last_stlog, sflog=cur_sflog) need_cal_exp = False elif new_run is False: cur_sflog = get_sflog(mgroup, last_timex) last_stlog = StLog.objects.create(title="停机", is_shutdown=True, mgroup=mgroup, end_time=None, start_time=last_timex, sflog=cur_sflog, reason = note) mgroup.is_running = False mgroup.save() SfLogExp.objects.get_or_create(stlog=last_stlog, sflog=cur_sflog) need_cal_exp = False if last_stlog and need_cal_exp: cal_exp_duration_sec(last_stlog.id) # 触发时间分配 def bind_routepack(ticket: Ticket, transition, new_ticket_data: dict): routepack = RoutePack.objects.get(id=new_ticket_data['t_id']) routepack.get_gjson(need_update=True) if routepack.ticket and routepack.ticket.id!=ticket.id: raise ParseError('重复创建工单') if not Route.objects.filter(routepack=routepack).exists(): raise ParseError('缺少步骤') r_qs = Route.objects.filter(routepack=routepack).order_by('sort', 'process__sort', 'create_time') first_route = r_qs.first() if first_route.batch_bind: first_route.batch_bind = False first_route.save(update_fields=['batch_bind']) # last_route = r_qs.last() # if last_route.material_out != routepack.material: # raise ParseError('最后一步产出与工艺包不一致') ticket_data = ticket.ticket_data ticket_data.update({ 't_model': 'routepack', 't_id': routepack.id, }) ticket.ticket_data = ticket_data ticket.create_by = routepack.create_by ticket.save() if routepack.ticket is None: routepack.ticket = ticket routepack.state = RoutePack.RP_S_AUDIT routepack.save() def routepack_audit_end(ticket: Ticket): routepack = RoutePack.objects.get(id=ticket.ticket_data['t_id']) routepack.state = RoutePack.RP_S_CONFIRM routepack.save() def routepack_ticket_change(ticket: Ticket): routepack = RoutePack.objects.get(id=ticket.ticket_data['t_id']) if ticket.act_state in [Ticket.TICKET_ACT_STATE_DRAFT, Ticket.TICKET_ACT_STATE_BACK, Ticket.TICKET_ACT_STATE_RETREAT]: routepack.state = RoutePack.RP_S_CREATE routepack.save() def get_mgroup_days(mgroup: Mgroup, now: datetime): """ 获取给定时间所属的班年月日 """ if now is None: now = datetime.now() shift_rule_name = mgroup.shift_rule shift = Shift.objects.filter(rule=shift_rule_name).order_by('sort').first() if shift is None: raise ParseError('工段班次规则不存在') current_time = now.time().replace(second=0, microsecond=0) if current_time >= shift.start_time_o: now_b = now - timedelta(days=1) else: now = now - timedelta(days=1) now_b = now - timedelta(days=1) return {"year_s": now.year, "month_s": now.month, "day_s": now.day, "year_s_b": now_b.year, "month_s_b": now_b.month, "day_s_b": now_b.day}