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 from rest_framework.exceptions import ParseError from apps.utils.tools import ranstr from datetime import datetime from apps.wf.models import Ticket from django.db.models import Sum from typing import List def cal_material_count(materialId_list: List[str]=None): """ 计算物料总数量 """ from apps.inm.models import MaterialBatch from apps.wpm.models import WMaterial if materialId_list is None: materialId_list = [] if materialId_list: objs = Material.objects.filter(id__in=set(materialId_list)) else: objs = Material.objects.all() for material in objs: mb_count = MaterialBatch.objects.filter(material=material).aggregate(total=Sum("count"))["total"] or 0 wm_count = WMaterial.objects.filter(material=material).aggregate(total=Sum("count"))["total"] or 0 if mb_count is None: mb_count = 0 if wm_count is None: wm_count = 0 Material.objects.filter(id=material.id).update( count_wm=wm_count, count_mb=mb_count, count=mb_count + wm_count) def get_mgroup_goals(mgroupId, year, reload=False): """ 获取工段某年的全部目标值, 以字典形式返回, 带缓存 """ goals = Goal.objects.filter( Q(mgroup__id=mgroupId) | Q(mgroup__name=mgroupId), year=year) key = f'mgroup_{mgroupId}_goals' if reload is False: mgroup_goals = cache.get(key, None) if mgroup_goals is not None: return mgroup_goals mgroup_goals = {} 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, 10) 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) sheet = wb['物料'] 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: type_str = sheet[f'b{i}'].value.replace(' ', '') try: type = type_dict[type_str] number = sheet[f'a{i}'].value.replace(' ', '') name = sheet[f'c{i}'].value.replace(' ', '') specification = sheet[f'd{i}'].value.replace( '×', '*').replace(' ', '') model = sheet[f'e{i}'].value.replace(' ', '') unit = sheet[f'f{i}'].value.replace(' ', '') count_safe = sheet[f'h{i}'].value unit_price = sheet[f'i{i}'].value except Exception as e: raise ParseError(f'{i}行物料信息错误: {str(e)}') if type in [20, 30]: try: process = process_d[sheet[f'g{i}'].value.replace(' ', '')] except Exception as e: raise ParseError(f'{i}行物料信息错误: {str(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} 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): """ 调用工段运行变动 """ from apps.wpm.services import get_sflog from apps.wpm.tasks import cal_exp_duration_sec from apps.wpm.models import StLog 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() # 找到最后一次停机记录 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() cal_exp_duration_sec(last_stlog.id) # 触发时间分配 elif last_stlog.end_time and new_run is False and last_timex > last_stlog.end_time: # 从开到停 has_same_stlog =StLog.objects.filter(mgroup=mgroup, is_shutdown=True, start_time=last_timex).exists() if not has_same_stlog: StLog.objects.create(title="停机", is_shutdown=True, mgroup=mgroup, end_time=None, start_time=last_timex, sflog=get_sflog(mgroup, last_timex)) elif new_run is False: StLog.objects.create(title="停机", is_shutdown=True, mgroup=mgroup, end_time=None, start_time=last_timex, sflog=get_sflog(mgroup, last_timex)) mgroup.is_running = False mgroup.save() def bind_routepack(ticket: Ticket, transition, new_ticket_data: dict): routepack = RoutePack.objects.get(id=new_ticket_data['t_id']) if routepack.ticket and routepack.ticket.id!=ticket.id: raise ParseError('重复创建工单') if not Route.objects.filter(routepack=routepack).exists(): raise ParseError('缺少步骤') first_route = Route.objects.filter(routepack=routepack).order_by('sort', 'process__sort', 'create_time').first() if first_route.batch_bind: first_route.batch_bind = False first_route.save(update_fields=['batch_bind']) 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 == Ticket.TICKET_ACT_STATE_DRAFT: routepack.state = RoutePack.RP_S_CREATE routepack.save()