195 lines
8.2 KiB
Python
195 lines
8.2 KiB
Python
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 = str(sheet[f'a{i}'].value).replace(' ', '') if sheet[f'a{i}'].value else None
|
||
if sheet[f'c{i}'].value:
|
||
name = str(sheet[f'c{i}'].value).replace(' ', '')
|
||
else:
|
||
raise ParseError(f'{i}行物料信息错误: 物料名称必填')
|
||
specification = str(sheet[f'd{i}'].value).replace(
|
||
'×', '*').replace(' ', '') if sheet[f'd{i}'].value else None
|
||
model = str(sheet[f'e{i}'].value).replace(' ', '') if sheet[f'e{i}'].value else None
|
||
unit = sheet[f'f{i}'].value.replace(' ', '')
|
||
count_safe = float(sheet[f'h{i}'].value) if sheet[f'h{i}'].value else None
|
||
unit_price = float(sheet[f'i{i}'].value) if sheet[f'i{i}'].value else None
|
||
except Exception as e:
|
||
raise ParseError(f'{i}行物料信息错误: {e}')
|
||
if type in [20, 30]:
|
||
try:
|
||
process = process_d[sheet[f'g{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}
|
||
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'])
|
||
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()
|
||
last_route = r_qs.last()
|
||
if first_route.batch_bind:
|
||
first_route.batch_bind = False
|
||
first_route.save(update_fields=['batch_bind'])
|
||
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 == Ticket.TICKET_ACT_STATE_DRAFT:
|
||
routepack.state = RoutePack.RP_S_CREATE
|
||
routepack.save() |