This commit is contained in:
zty 2024-09-13 13:41:29 +08:00
commit 7aae49b378
42 changed files with 1216 additions and 265 deletions

View File

@ -12,6 +12,8 @@ from apps.bi.services import check_sql_safe, format_json_with_placeholders
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from rest_framework.generics import get_object_or_404 from rest_framework.generics import get_object_or_404
from apps.utils.mixins import ListModelMixin from apps.utils.mixins import ListModelMixin
import logging
myLogger = logging.getLogger('log')
# Create your views here. # Create your views here.
@ -101,6 +103,7 @@ class DatasetViewSet(CustomModelViewSet):
results[name], results2[name] = format_sqldata( results[name], results2[name] = format_sqldata(
res[0], res[1]) res[0], res[1])
except Exception as e: except Exception as e:
myLogger.error(f'bi查询异常{str(e)}-{dt.code}--{sql_f}')
if raise_exception: if raise_exception:
raise ParseError(f'查询异常:{str(e)}') raise ParseError(f'查询异常:{str(e)}')
else: else:

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-08-28 08:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('enm', '0043_xscript'),
]
operations = [
migrations.AddField(
model_name='xscript',
name='change_data',
field=models.JSONField(blank=True, default=dict, null=True, verbose_name='变更数据'),
),
]

View File

@ -8,6 +8,7 @@ class Xscript(BaseModel):
name = models.CharField("脚本名称", max_length=50) name = models.CharField("脚本名称", max_length=50)
code = models.TextField("脚本内容", null=True, blank=True) code = models.TextField("脚本内容", null=True, blank=True)
base_data = models.JSONField("基础数据", default=dict, null=True, blank=True) base_data = models.JSONField("基础数据", default=dict, null=True, blank=True)
change_data = models.JSONField("变更数据", default=dict, null=True, blank=True)
myschedule = models.ForeignKey('system.myschedule', verbose_name='周期', on_delete=models.SET_NULL, null=True, blank=True) myschedule = models.ForeignKey('system.myschedule', verbose_name='周期', on_delete=models.SET_NULL, null=True, blank=True)
periodictask = models.ForeignKey(PeriodicTask, verbose_name='关联定时任务', on_delete=models.CASCADE, related_name='xscript_periodictask', null=True, blank=True) periodictask = models.ForeignKey(PeriodicTask, verbose_name='关联定时任务', on_delete=models.CASCADE, related_name='xscript_periodictask', null=True, blank=True)

View File

@ -210,7 +210,7 @@ class XscriptSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Xscript model = Xscript
fields = "__all__" fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ['periodictask'] read_only_fields = EXCLUDE_FIELDS_BASE + ['periodictask', 'change_data']
def validate(self, attrs): def validate(self, attrs):
code = attrs['code'] code = attrs['code']

View File

@ -21,12 +21,9 @@ myLogger = logging.getLogger("log")
def db_insert_mplogx_batch(rows): def db_insert_mplogx_batch(rows):
for row in rows: for row in rows:
_, tag_val, tag_code, tag_update = row _, tag_val, tag_code, tag_update = row
# if '散装' in tag_code or '袋装' in tag_code: if tag_update.tzinfo is None:
# myLogger.info(f"db_ins_mplogx tag_val: {tag_val} tag_code: {tag_code}-------{str(type(tag_val))}") tag_update = timezone.make_aware(tag_update)
# mpoint = Mpoint.objects.get(code=tag_code) insert_mplogx_item(tag_code, tag_val, tag_update, {})
# tag_val = float(tag_val)
# myLogger.info(f"db_ins_mpoint_id: {mpoint.id}-------db_ins_float_val: {tag_val}")
insert_mplogx_item(tag_code, tag_val, timezone.make_aware(tag_update), {})
def translate_eval_formula(exp_str: str, year: int, month: int, day: int, hour: int): def translate_eval_formula(exp_str: str, year: int, month: int, day: int, hour: int):
""" """

View File

@ -27,6 +27,7 @@ from apps.wpm.tasks import get_total_sec_now, cal_exp_duration_sec
from apps.utils.sql import DbConnection from apps.utils.sql import DbConnection
from apps.enm.services import db_insert_mplogx_batch from apps.enm.services import db_insert_mplogx_batch
from apps.enm.xscript import main from apps.enm.xscript import main
from django.core.exceptions import ObjectDoesNotExist
myLogger = logging.getLogger("log") myLogger = logging.getLogger("log")
@ -130,8 +131,7 @@ def cal_mpointstat_hour(mpointId: str, year: int, month: int, day: int, hour: in
dt = datetime.datetime(year=year, month=month, day=day, hour=hour, minute=0, second=0, tzinfo=mytz) # 整点时间 dt = datetime.datetime(year=year, month=month, day=day, hour=hour, minute=0, second=0, tzinfo=mytz) # 整点时间
dt_hour_p= dt - datetime.timedelta(hours=1) # 上个整点 dt_hour_p= dt - datetime.timedelta(hours=1) # 上个整点
dt_hour_n= dt + datetime.timedelta(hours=1) # 下个整点 dt_hour_n= dt + datetime.timedelta(hours=1) # 下个整点
if (mpoint.material or mpoint.type == Mpoint.MT_COMPUTE) and mpoint.val_type in ['float', 'int']: # 如果计量的是物料 # 累计量 有的会清零,需要额外处理(还未做)
if mpoint.material and mpoint.val_type in ['float', 'int']: # 如果计量的是物料 # 累计量 有的会清零,需要额外处理(还未做)
params = {"mpoint": mpoint, "type": "hour"} params = {"mpoint": mpoint, "type": "hour"}
params["year"], params["month"], params["day"], params["hour"] = year, month, day, hour params["year"], params["month"], params["day"], params["hour"] = year, month, day, hour
val = 0 val = 0
@ -141,7 +141,6 @@ def cal_mpointstat_hour(mpointId: str, year: int, month: int, day: int, hour: in
val = MpLogx.objects.filter(mpoint=mpoint, timex__gte=dt, timex__lt=dt_hour_n).aggregate(sum=Sum(f'val_{mpoint.val_type}'))["sum"] val = MpLogx.objects.filter(mpoint=mpoint, timex__gte=dt, timex__lt=dt_hour_n).aggregate(sum=Sum(f'val_{mpoint.val_type}'))["sum"]
if val is None: if val is None:
val = 0 val = 0
myLogger.info(str(val), "-------val")
else: else:
mrs0 = MpLogx.objects.filter(mpoint=mpoint, timex__gte=dt_hour_p, timex__lte=dt).order_by("timex") mrs0 = MpLogx.objects.filter(mpoint=mpoint, timex__gte=dt_hour_p, timex__lte=dt).order_by("timex")
mrs = MpLogx.objects.filter(mpoint=mpoint, timex__gte=dt, timex__lte=dt_hour_n).order_by("timex") mrs = MpLogx.objects.filter(mpoint=mpoint, timex__gte=dt, timex__lte=dt_hour_n).order_by("timex")
@ -281,7 +280,7 @@ def cal_mpointstats(is_now=1, year=None, month=None, day=None, hour=None, m_code
cal_mpointstat_hour(item.id, year, month, day, hour) cal_mpointstat_hour(item.id, year, month, day, hour)
# 再统计计算测点 # 再统计计算测点
mpoints_compute = Mpoint.objects.filter(type=Mpoint.MT_COMPUTE, enabled=True, material__isnull=False).exclude(formula="").order_by('report_sortstr', 'create_time') mpoints_compute = Mpoint.objects.filter(type=Mpoint.MT_COMPUTE, enabled=True).exclude(formula="").order_by('report_sortstr', 'create_time')
# mpoints_other_group = [] # mpoints_other_group = []
for item in mpoints_compute: for item in mpoints_compute:
# mpoints_other_group.append(cal_mpointstat_hour.s(item.id, year, month, day, hour)) # mpoints_other_group.append(cal_mpointstat_hour.s(item.id, year, month, day, hour))
@ -688,34 +687,42 @@ def cal_enstat2(type: str, year_s: int, month_s: int, day_s: int, cascade=True):
elif type == "day_s": elif type == "day_s":
enstat2, _ = EnStat2.objects.get_or_create(type="day_s", year_s=year_s, month_s=month_s, day_s=day_s, defaults={"year_s": year_s, "month_s": month_s, "day_s": day_s, "type": "day_s"}) enstat2, _ = EnStat2.objects.get_or_create(type="day_s", year_s=year_s, month_s=month_s, day_s=day_s, defaults={"year_s": year_s, "month_s": month_s, "day_s": day_s, "type": "day_s"})
# enstat2 = EnStat2.objects.select_for_update().get(id=enstat2.id) # 加锁 # enstat2 = EnStat2.objects.select_for_update().get(id=enstat2.id) # 加锁
material_bulk_clinker = Material.objects.get(code="bulk_clinker") # 散装熟料 try:
material_bulk_cement = Material.objects.get(code="bulk_cement") # 散装水泥 material_bulk_clinker = Material.objects.get(code="bulk_clinker") # 散装熟料
material_bag_cement = Material.objects.get(code="bag_cement") # 袋装水泥 enstat2.bulk_clinker_price = get_price_unit(material_bulk_clinker, year_s, month_s)
except ObjectDoesNotExist:
enstat2.bulk_clinker_price = 0
try:
material_bulk_cement = Material.objects.get(code="bulk_cement") # 散装水泥
enstat2.bulk_cement_price = get_price_unit(material_bulk_cement, year_s, month_s)
except ObjectDoesNotExist:
enstat2.bulk_cement_price = 0
try:
material_bag_cement = Material.objects.get(code="bag_cement") # 袋装水泥
enstat2.bag_cement_price = get_price_unit(material_bag_cement, year_s, month_s)
except ObjectDoesNotExist:
enstat2.bag_cement_price = 0
enstat2.bulk_cement_price = get_price_unit(material_bulk_cement, year_s, month_s)
enstat2.bulk_clinker_price = get_price_unit(material_bulk_clinker, year_s, month_s)
enstat2.bag_cement_price = get_price_unit(material_bag_cement, year_s, month_s)
if type == "month_s": if type == "month_s":
enstat2.bulk_cement_val = MpointStat.objects.filter(type="month_s", mpoint__material=material_bulk_cement, year_s=year_s, month_s=month_s).aggregate(sum=Sum("val"))["sum"] enstat2.bulk_cement_val = MpointStat.objects.filter(type="month_s", mpoint__material__code="bulk_cement", year_s=year_s, month_s=month_s).aggregate(sum=Sum("val"))["sum"]
elif type == "day_s": elif type == "day_s":
enstat2.bulk_cement_val = MpointStat.objects.filter(type="day_s", mpoint__material=material_bulk_cement, year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum("val"))["sum"] enstat2.bulk_cement_val = MpointStat.objects.filter(type="day_s", mpoint__material__code="bulk_cement", year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum("val"))["sum"]
if enstat2.bulk_cement_val is None: if enstat2.bulk_cement_val is None:
enstat2.bulk_cement_val = 0 enstat2.bulk_cement_val = 0
if type == "month_s": if type == "month_s":
enstat2.bag_cement_val = MpointStat.objects.filter(type="month_s", mpoint__material=material_bag_cement, year_s=year_s, month_s=month_s).aggregate(sum=Sum("val"))["sum"] enstat2.bag_cement_val = MpointStat.objects.filter(type="month_s", mpoint__material__code='bag_cement', year_s=year_s, month_s=month_s).aggregate(sum=Sum("val"))["sum"]
elif type == "day_s": elif type == "day_s":
enstat2.bag_cement_val = MpointStat.objects.filter(type="day_s", mpoint__material=material_bag_cement, year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum("val"))["sum"] enstat2.bag_cement_val = MpointStat.objects.filter(type="day_s", mpoint__material__code='bag_cement', year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum("val"))["sum"]
if enstat2.bag_cement_val is None: if enstat2.bag_cement_val is None:
enstat2.bag_cement_val = 0 enstat2.bag_cement_val = 0
if type == "month_s": if type == "month_s":
enstat2.bulk_clinker_val = MpointStat.objects.filter(type="month_s", mpoint__material=material_bulk_clinker, year_s=year_s, month_s=month_s).aggregate(sum=Sum("val"))["sum"] enstat2.bulk_clinker_val = MpointStat.objects.filter(type="month_s", mpoint__material__code='bulk_clinker', year_s=year_s, month_s=month_s).aggregate(sum=Sum("val"))["sum"]
elif type == "day_s": elif type == "day_s":
enstat2.bulk_clinker_val = MpointStat.objects.filter(type="day_s", mpoint__material=material_bulk_clinker, year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum("val"))["sum"] enstat2.bulk_clinker_val = MpointStat.objects.filter(type="day_s", mpoint__material__code='bulk_clinker', year_s=year_s, month_s=month_s, day_s=day_s).aggregate(sum=Sum("val"))["sum"]
if enstat2.bulk_clinker_val is None: if enstat2.bulk_clinker_val is None:
enstat2.bulk_clinker_val = 0 enstat2.bulk_clinker_val = 0

View File

@ -92,6 +92,17 @@ class XscriptViewSet(CustomModelViewSet):
periodictask.save() periodictask.save()
return Response() return Response()
@action(methods=['put'], detail=True, perms_map={'put': 'xscript.update'})
def change_data(self, request, pk=None):
"""修改变动数据
修改变动数据
"""
obj: Xscript = self.get_object()
obj.change_data = request.data.get('change_data', {})
obj.save(update_fields=['change_data'])
return Response()
@transaction.atomic @transaction.atomic
def perform_destroy(self, instance): def perform_destroy(self, instance):
periodictask = instance.periodictask periodictask = instance.periodictask

View File

@ -3,6 +3,8 @@ from apps.utils.sql import DbConnection
from datetime import datetime, timedelta from datetime import datetime, timedelta
from apps.enm.services import db_insert_mplogx_batch from apps.enm.services import db_insert_mplogx_batch
import requests import requests
import logging
myLogger = logging.getLogger("log")
def main(xscript, mcodes_list): def main(xscript, mcodes_list):
exec(xscript.code) exec(xscript.code)

View File

@ -10,6 +10,7 @@ from apps.utils.serializers import CustomModelSerializer
from .models import MIO, MaterialBatch, MIOItem, WareHouse, MIOItemA, MaterialBatchA from .models import MIO, MaterialBatch, MIOItem, WareHouse, MIOItemA, MaterialBatchA
from django.db import transaction from django.db import transaction
from server.settings import get_sysconfig
class WareHourseSerializer(CustomModelSerializer): class WareHourseSerializer(CustomModelSerializer):
@ -104,7 +105,9 @@ class MIOItemCreateSerializer(CustomModelSerializer):
if mio.state != MIO.MIO_CREATE: if mio.state != MIO.MIO_CREATE:
raise ValidationError('出入库记录非创建中不可新增') raise ValidationError('出入库记录非创建中不可新增')
# 生产领料要校验是否进行检验 # 生产领料要校验是否进行检验
if mio.type == MIO.MIO_TYPE_DO_OUT: # 某些客户此处无需校验
check_test_when_do_out = get_sysconfig('mes.check_test_when_do_out', True)
if check_test_when_do_out and mio.type == MIO.MIO_TYPE_DO_OUT:
mis = MIOItem.objects.filter(batch=batch, material=material, mio__type__in=[MIO.MIO_TYPE_PUR_IN, MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_OTHER_IN]) mis = MIOItem.objects.filter(batch=batch, material=material, mio__type__in=[MIO.MIO_TYPE_PUR_IN, MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_OTHER_IN])
if mis.exists() and (not mis.exclude(test_date=None).exists()): if mis.exists() and (not mis.exclude(test_date=None).exists()):
raise ValidationError('该批次的物料未经检验') raise ValidationError('该批次的物料未经检验')
@ -165,6 +168,7 @@ class MIODoSerializer(CustomModelSerializer):
model = MIO model = MIO
fields = ['id', 'number', 'note', 'do_user', fields = ['id', 'number', 'note', 'do_user',
'belong_dept', 'type', 'inout_date', 'mgroup'] 'belong_dept', 'type', 'inout_date', 'mgroup']
extra_kwargs = {'inout_date': {'required': True}, 'do_user': {'required': True}}
def validate(self, attrs): def validate(self, attrs):
if 'mgroup' in attrs and attrs['mgroup']: if 'mgroup' in attrs and attrs['mgroup']:
@ -190,6 +194,7 @@ class MIOSaleSerializer(CustomModelSerializer):
class Meta: class Meta:
model = MIO model = MIO
fields = ['id', 'number', 'note', 'order', 'inout_date', 'customer'] fields = ['id', 'number', 'note', 'order', 'inout_date', 'customer']
extra_kwargs = {'inout_date': {'required': True}}
def create(self, validated_data): def create(self, validated_data):
validated_data['type'] = MIO.MIO_TYPE_SALE_OUT validated_data['type'] = MIO.MIO_TYPE_SALE_OUT
@ -216,6 +221,7 @@ class MIOPurSerializer(CustomModelSerializer):
class Meta: class Meta:
model = MIO model = MIO
fields = ['id', 'number', 'note', 'pu_order', 'inout_date', 'supplier'] fields = ['id', 'number', 'note', 'pu_order', 'inout_date', 'supplier']
extra_kwargs = {'inout_date': {'required': True}}
def create(self, validated_data): def create(self, validated_data):
validated_data['type'] = MIO.MIO_TYPE_PUR_IN validated_data['type'] = MIO.MIO_TYPE_PUR_IN
@ -241,6 +247,7 @@ class MIOOtherSerializer(CustomModelSerializer):
model = MIO model = MIO
fields = ['id', 'number', 'note', 'supplier', fields = ['id', 'number', 'note', 'supplier',
'customer', 'type', 'inout_date'] 'customer', 'type', 'inout_date']
extra_kwargs = {'inout_date': {'required': True}}
def create(self, validated_data): def create(self, validated_data):
if validated_data['type'] not in [MIO.MIO_TYPE_OTHER_OUT, MIO.MIO_TYPE_OTHER_IN]: if validated_data['type'] not in [MIO.MIO_TYPE_OTHER_OUT, MIO.MIO_TYPE_OTHER_IN]:

View File

@ -5,7 +5,7 @@ from apps.mtm.models import Material, Shift, Mgroup, Process
@admin.register(Process) @admin.register(Process)
class ProcessAdmin(admin.ModelAdmin): class ProcessAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'cate', 'sort', 'into_wm_mgroup', 'store_notok', 'batch_append_equip') list_display = ('id', 'name', 'cate', 'sort', 'into_wm_mgroup', 'store_notok', 'batch_append_equip', 'mlog_need_ticket')
@admin.register(Material) @admin.register(Material)

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-09-02 09:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0039_alter_material_unit_price'),
]
operations = [
migrations.AddField(
model_name='process',
name='mlog_need_ticket',
field=models.BooleanField(default=False, verbose_name='日志提交是否需要审批'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-09-03 02:57
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mtm', '0040_process_mlog_need_ticket'),
]
operations = [
migrations.AddField(
model_name='process',
name='mstate_json',
field=models.JSONField(blank=True, default=list, verbose_name='中间状态'),
),
]

View File

@ -18,6 +18,8 @@ class Process(CommonBModel):
into_wm_mgroup = models.BooleanField('交接到工段', default=False) into_wm_mgroup = models.BooleanField('交接到工段', default=False)
store_notok = models.BooleanField('不合格品是否入库', default=False) store_notok = models.BooleanField('不合格品是否入库', default=False)
batch_append_equip = models.BooleanField('批号追加设备', default=False) batch_append_equip = models.BooleanField('批号追加设备', default=False)
mlog_need_ticket = models.BooleanField('日志提交是否需要审批', default=False)
mstate_json = models.JSONField('中间状态', default=list, blank=True)
class Meta: class Meta:
verbose_name = '工序' verbose_name = '工序'

View File

@ -153,10 +153,14 @@ def bind_routepack(ticket: Ticket, transition, new_ticket_data: dict):
raise ParseError('重复创建工单') raise ParseError('重复创建工单')
if not Route.objects.filter(routepack=routepack).exists(): if not Route.objects.filter(routepack=routepack).exists():
raise ParseError('缺少步骤') raise ParseError('缺少步骤')
first_route = Route.objects.filter(routepack=routepack).order_by('sort', 'process__sort', 'create_time').first() 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: if first_route.batch_bind:
first_route.batch_bind = False first_route.batch_bind = False
first_route.save(update_fields=['batch_bind']) first_route.save(update_fields=['batch_bind'])
if last_route.material_out != routepack.material:
raise ParseError('最后一步产出与工艺包不一致')
ticket_data = ticket.ticket_data ticket_data = ticket.ticket_data
ticket_data.update({ ticket_data.update({
't_model': 'routepack', 't_model': 'routepack',

View File

@ -0,0 +1,32 @@
# Generated by Django 3.2.12 on 2024-09-03 08:38
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('pm', '0019_auto_20240703_1618'),
]
operations = [
migrations.CreateModel(
name='Mtaskb',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('count', models.PositiveIntegerField(default=0, verbose_name='任务数')),
('handle_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='mtaskb_handle_user', to=settings.AUTH_USER_MODEL, verbose_name='操作人')),
('mtask', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='b_mtask', to='pm.mtask', verbose_name='关联任务')),
],
options={
'abstract': False,
},
),
]

View File

@ -1,5 +1,5 @@
from django.db import models from django.db import models
from apps.utils.models import CommonADModel, CommonBDModel from apps.utils.models import CommonADModel, CommonBDModel, BaseModel
from apps.mtm.models import Material, Mgroup, RoutePack, Route from apps.mtm.models import Material, Mgroup, RoutePack, Route
# Create your models here. # Create your models here.
@ -111,3 +111,14 @@ class Mtask(CommonADModel):
def mlogs(self): def mlogs(self):
from apps.wpm.models import Mlog from apps.wpm.models import Mlog
return Mlog.objects.filter(mtask=self) return Mlog.objects.filter(mtask=self)
@property
def mtaskb(self):
return Mtaskb.objects.filter(mtask=self)
class Mtaskb(BaseModel):
mtask = models.ForeignKey(Mtask, verbose_name='关联任务', on_delete=models.CASCADE, related_name='b_mtask')
handle_user = models.ForeignKey(
'system.user', verbose_name='操作人', on_delete=models.CASCADE, related_name='mtaskb_handle_user')
count = models.PositiveIntegerField('任务数', default=0)

View File

@ -3,11 +3,12 @@ from rest_framework.exceptions import ValidationError, ParseError
import math import math
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer
from apps.pm.models import Mtask, Utask, SCHEDULE_TYPE from apps.pm.models import Mtask, Utask, SCHEDULE_TYPE, Mtaskb
from apps.sam.models import OrderItem from apps.sam.models import OrderItem
from apps.utils.serializers import CustomModelSerializer from apps.utils.serializers import CustomModelSerializer
from apps.system.models import Dept from apps.system.models import Dept
from apps.wpm.models import Mlog from apps.wpm.models import Mlog
from apps.utils.constants import EXCLUDE_FIELDS_BASE
class UtaskSerializer(CustomModelSerializer): class UtaskSerializer(CustomModelSerializer):
@ -54,6 +55,25 @@ class MlogSimpleSerializer(CustomModelSerializer):
fields = ['id', 'shift_name', 'count_use', fields = ['id', 'shift_name', 'count_use',
'count_ok', 'count_real', 'submit_time'] 'count_ok', 'count_real', 'submit_time']
class MtaskbAddSerializer(CustomModelSerializer):
handle_user_name = serializers.StringRelatedField(
source='handle_user.name', read_only=True)
class Meta:
model = Mtaskb
fields = ['id', 'handle_user', 'handle_user_name', 'count']
class MtaskbSerializer(CustomModelSerializer):
handle_user_name = serializers.StringRelatedField(
source='handle_user.name', read_only=True)
class Meta:
model = Mtaskb
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS_BASE
class MtaskbUpdateSerializer(CustomModelSerializer):
class Meta:
model = Mtaskb
fields = ['id', 'count']
class MtaskSerializer(CustomModelSerializer): class MtaskSerializer(CustomModelSerializer):
material_out_ = MaterialSimpleSerializer( material_out_ = MaterialSimpleSerializer(
@ -66,6 +86,7 @@ class MtaskSerializer(CustomModelSerializer):
source='submit_user.name', read_only=True) source='submit_user.name', read_only=True)
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True) mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
mlogs = MlogSimpleSerializer(label='日志信息', many=True, required=False) mlogs = MlogSimpleSerializer(label='日志信息', many=True, required=False)
mtaskb = MtaskbSerializer(label='子任务信息', many=True, required=False, read_only=True)
class Meta: class Meta:
model = Mtask model = Mtask
@ -109,4 +130,4 @@ class MtaskDaySerializer(serializers.Serializer):
class MtaskAddInfoSerializer(CustomModelSerializer): class MtaskAddInfoSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mtask model = Mtask
fields = ['peifen_kg'] fields = ['peifen_kg']

View File

@ -311,6 +311,8 @@ class PmService:
now = timezone.now() now = timezone.now()
if mtask.state == Mtask.MTASK_ASSGINED: if mtask.state == Mtask.MTASK_ASSGINED:
mlogs = Mlog.objects.filter(mtask=mtask)|Mlog.objects.filter(b_mlog__mtask=mtask) mlogs = Mlog.objects.filter(mtask=mtask)|Mlog.objects.filter(b_mlog__mtask=mtask)
if not mlogs.exists():
raise ParseError('该任务没有日志')
if mlogs.filter(submit_time__isnull=True).exists(): if mlogs.filter(submit_time__isnull=True).exists():
raise ParseError('存在未提交的日志') raise ParseError('存在未提交的日志')
mtask.state = Mtask.MTASK_SUBMIT mtask.state = Mtask.MTASK_SUBMIT

View File

@ -1,12 +1,13 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.pm.views import (MtaskViewSet, UtaskViewSet) from apps.pm.views import (MtaskViewSet, UtaskViewSet, MtaskbViewSet)
API_BASE_URL = 'api/pm/' API_BASE_URL = 'api/pm/'
HTML_BASE_URL = 'pm/' HTML_BASE_URL = 'pm/'
router = DefaultRouter() router = DefaultRouter()
router.register('mtask', MtaskViewSet, basename='mtask') router.register('mtask', MtaskViewSet, basename='mtask')
router.register('mtaskb', MtaskbViewSet, basename='mtaskb')
router.register('utask', UtaskViewSet, basename='utask') router.register('utask', UtaskViewSet, basename='utask')
urlpatterns = [ urlpatterns = [
path(API_BASE_URL, include(router.urls)), path(API_BASE_URL, include(router.urls)),

View File

@ -8,9 +8,11 @@ from apps.utils.serializers import PkSerializer
from apps.utils.viewsets import CustomModelViewSet from apps.utils.viewsets import CustomModelViewSet
from .filters import MtaskFilter, UtaskFilter from .filters import MtaskFilter, UtaskFilter
from .models import Mtask, Utask from .models import Mtask, Utask, Mtaskb
from .serializers import (MtaskSerializer, SchedueSerializer, UtaskSerializer, from .serializers import (MtaskSerializer, SchedueSerializer, UtaskSerializer,
MtaskDaySerializer, MtaskAddInfoSerializer, SchedueMtasksSerializer) MtaskDaySerializer, MtaskAddInfoSerializer,
SchedueMtasksSerializer, MtaskbSerializer,
MtaskbUpdateSerializer, MtaskbAddSerializer)
from .services import PmService from .services import PmService
from django.utils import timezone from django.utils import timezone
@ -129,7 +131,7 @@ class MtaskViewSet(CustomModelViewSet):
serializer_class = MtaskSerializer serializer_class = MtaskSerializer
filterset_class = MtaskFilter filterset_class = MtaskFilter
select_related_fields = ['material_in', 'material_out', 'mgroup'] select_related_fields = ['material_in', 'material_out', 'mgroup']
prefetch_related_fields = ['mlog_mtask'] prefetch_related_fields = ['mlog_mtask', 'b_mtask']
ordering_fields = ['start_date', 'mgroup__process__sort', 'create_time'] ordering_fields = ['start_date', 'mgroup__process__sort', 'create_time']
ordering = ['-start_date', 'mgroup__process__sort', '-create_time'] ordering = ['-start_date', 'mgroup__process__sort', '-create_time']
@ -204,3 +206,38 @@ class MtaskViewSet(CustomModelViewSet):
sr.is_valid(raise_exception=True) sr.is_valid(raise_exception=True)
sr.save() sr.save()
return Response() return Response()
@action(methods=['post'], detail=True, perms_map={'post': 'mtaskb.create'}, serializer_class=MtaskbAddSerializer)
@transaction.atomic
def add_mtaskb(self, request, *args, **kwargs):
mtask = self.get_object()
if mtask.state != Mtask.MTASK_ASSGINED:
raise ParseError('该任务不可分配')
sr = MtaskbAddSerializer(data=request.data, many=True)
sr.is_valid(raise_exception=True)
vdata = sr.validated_data
total = 0
userIds = []
for v in vdata:
total += v['count']
userIds.append(v['handle_user'].id)
if total != mtask.count:
raise ParseError('任务数量与分配数量不匹配')
for v in vdata:
mtaskb, _ = Mtaskb.objects.get_or_create(mtask=mtask, handle_user=v['handle_user'])
mtaskb.count = v['count']
mtaskb.save()
dobjs = Mtaskb.objects.filter(mtask=mtask).exclude(handle_user__in=userIds)
from apps.wpm.models import Mlog
if Mlog.objects.filter(mtaskb__in=dobjs).exists():
raise ParseError('已存在工作日志,无法修改分配')
else:
dobjs.delete()
return Response()
class MtaskbViewSet(CustomModelViewSet):
queryset = Mtaskb.objects.all()
serializer_class = MtaskbSerializer
update_serializer_class = MtaskbUpdateSerializer
filterset_fields = {"mtask": ["exact"], "handle_user": ["exact"], "mtask__mgroup": ["exact"]}
ordering = ['id']

View File

@ -30,7 +30,10 @@ class FtestWorkFilter(filters.FilterSet):
fields = { fields = {
"material__process__name": ["exact", "contains"], "material__process__name": ["exact", "contains"],
"material": ["exact"], "material": ["exact"],
"wm": ["exact"],
"mb": ["exact"],
"batch": ["exact"], "batch": ["exact"],
"type": ["exact"], "type": ["exact"],
"type2": ["exact"], "type2": ["exact"],
"shift": ["exact"]
} }

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-08-30 05:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0021_ftestwork_mb'),
]
operations = [
migrations.AddField(
model_name='ftestwork',
name='count_sampling_ok',
field=models.IntegerField(default=0, verbose_name='抽检合格数量'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2024-09-02 07:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('qm', '0022_ftestwork_count_sampling_ok'),
]
operations = [
migrations.AddField(
model_name='ftestwork',
name='need_update_wm',
field=models.BooleanField(default=True, verbose_name='是否更新车间库存'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-09-11 08:28
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mtm', '0041_process_mstate_json'),
('qm', '0023_ftestwork_need_update_wm'),
]
operations = [
migrations.AddField(
model_name='ftestwork',
name='shift',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mtm.shift', verbose_name='班次'),
),
]

View File

@ -1,7 +1,7 @@
from django.db import models from django.db import models
from apps.system.models import CommonAModel, CommonADModel, User from apps.system.models import CommonAModel, CommonADModel, User
from apps.utils.models import CommonBDModel, BaseModel from apps.utils.models import CommonBDModel, BaseModel
from apps.mtm.models import Material, Mgroup, Team from apps.mtm.models import Material, Mgroup, Team, Shift
from apps.wpm.models import SfLog, WMaterial from apps.wpm.models import SfLog, WMaterial
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -56,6 +56,8 @@ class NotOkOption(models.TextChoices):
z = "z", _("") z = "z", _("")
zhg = "zhg", _("准合格") zhg = "zhg", _("准合格")
yz = "yz", _("圆准") yz = "yz", _("圆准")
jgqbl = "jgqbl", _("加工前不良")
qt = "qt", _("其它") qt = "qt", _("其它")
@ -109,6 +111,7 @@ class FtestWork(CommonBDModel):
""" """
type = models.CharField('检验类型', max_length=20, choices=FTEST_TYPE_CHOICES, default='prod') type = models.CharField('检验类型', max_length=20, choices=FTEST_TYPE_CHOICES, default='prod')
type2 = models.PositiveSmallIntegerField('检验类型2', choices=((10, '抽检'), (20, '全检')), default=10) type2 = models.PositiveSmallIntegerField('检验类型2', choices=((10, '抽检'), (20, '全检')), default=10)
shift = models.ForeignKey(Shift, verbose_name='班次', on_delete=models.SET_NULL, null=True, blank=True)
wm = models.ForeignKey(WMaterial, verbose_name='关联车间库存', on_delete=models.SET_NULL, null=True, blank=True) wm = models.ForeignKey(WMaterial, verbose_name='关联车间库存', on_delete=models.SET_NULL, null=True, blank=True)
mb = models.ForeignKey('inm.materialbatch', verbose_name='关联仓库', on_delete=models.SET_NULL, null=True, blank=True) mb = models.ForeignKey('inm.materialbatch', verbose_name='关联仓库', on_delete=models.SET_NULL, null=True, blank=True)
test_date = models.DateField('检验日期') test_date = models.DateField('检验日期')
@ -117,8 +120,10 @@ class FtestWork(CommonBDModel):
batch = models.CharField('生产批次', max_length=50) batch = models.CharField('生产批次', max_length=50)
count = models.IntegerField('检验数量') count = models.IntegerField('检验数量')
count_sampling = models.IntegerField('抽检数量', default=0) count_sampling = models.IntegerField('抽检数量', default=0)
count_sampling_ok = models.IntegerField('抽检合格数量', default=0)
count_ok = models.IntegerField('合格数量', default=0) count_ok = models.IntegerField('合格数量', default=0)
count_notok = models.IntegerField('不合格数量', default=0) count_notok = models.IntegerField('不合格数量', default=0)
need_update_wm = models.BooleanField('是否更新车间库存', default=True)
count_notok_json = models.JSONField('不合格项数量统计', default=dict, null=False, blank=True) count_notok_json = models.JSONField('不合格项数量统计', default=dict, null=False, blank=True)
test_user = models.ForeignKey( test_user = models.ForeignKey(
User, verbose_name='操作人', on_delete=models.CASCADE, related_name='ftestwork_test_user', null=True, blank=True) User, verbose_name='操作人', on_delete=models.CASCADE, related_name='ftestwork_test_user', null=True, blank=True)

View File

@ -66,7 +66,7 @@ class QuaStatUpdateSerializer(CustomModelSerializer):
class FtestWorkCreateUpdateSerializer(CustomModelSerializer): class FtestWorkCreateUpdateSerializer(CustomModelSerializer):
class Meta: class Meta:
model = FtestWork model = FtestWork
fields = ['id', 'wm', 'mb', 'type', 'type2', 'test_date', 'count', 'count_sampling', 'count_ok', 'count_notok', 'count_notok_json', 'test_user'] fields = ['id', 'shift', 'wm', 'mb', 'type', 'type2', 'test_date', 'count', 'count_sampling', 'count_sampling_ok', 'count_ok', 'count_notok', 'count_notok_json', 'test_user', 'need_update_wm']
extra_kwargs = {'test_user': {'required': True}, 'type': {'required': True}} extra_kwargs = {'test_user': {'required': True}, 'type': {'required': True}}
def validate(self, attrs): def validate(self, attrs):
@ -99,6 +99,8 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer):
attrs['batch'] = attrs['mb'].batch attrs['batch'] = attrs['mb'].batch
else: else:
raise ValidationError('请选择车间/仓库库存') raise ValidationError('请选择车间/仓库库存')
if attrs['test_user']:
attrs['belong_dept'] = attrs['test_user'].belong_dept
return attrs return attrs
@ -107,6 +109,7 @@ class FtestWorkSerializer(CustomModelSerializer):
source='material', read_only=True) source='material', read_only=True)
material_cate = serializers.CharField(source='material.cate', read_only=True) material_cate = serializers.CharField(source='material.cate', read_only=True)
mb_ = MaterialBatchDetailSerializer(source='mb', read_only=True) mb_ = MaterialBatchDetailSerializer(source='mb', read_only=True)
test_user_name = serializers.CharField(source='test_user.name', read_only=True)
class Meta: class Meta:
model = FtestWork model = FtestWork

View File

@ -7,67 +7,68 @@ from django.utils import timezone
def ftestwork_submit(ins:FtestWork, user: User): def ftestwork_submit(ins:FtestWork, user: User):
wm:WMaterial = ins.wm wm:WMaterial = ins.wm
if wm.state == WMaterial.WM_TEST: if ins.need_update_wm:
# 更新对应的车间库存 if wm.state == WMaterial.WM_TEST:
wm.count = wm.count - ins.count # 更新对应的车间库存
if wm.count >= 0: wm.count = wm.count - ins.count
# 已检测的数量 if wm.count >= 0:
wm.count_xtest = wm.count_xtest + ins.count # 已检测的数量
wm.save() wm.count_xtest = wm.count_xtest + ins.count
else:
raise ParseError("超过待检数量")
# 生成合格的
count_ok = ins.count_ok
if count_ok > 0:
wm, new_create = WMaterial.objects.get_or_create(
material=wm.material,
batch=wm.batch,
mgroup=wm.mgroup,
belong_dept=wm.belong_dept,
state=WMaterial.WM_OK,
defaults={
'count': count_ok,
'material': wm.material,
'batch': wm.batch,
'mgroup': wm.mgroup,
'belong_dept': wm.belong_dept,
}
)
if not new_create:
wm.count = wm.count + count_ok
wm.save() wm.save()
else: else:
wm.count = wm.count - ins.count_notok raise ParseError("超过待检数量")
if wm.count >= 0: # 生成合格的
wm.save() count_ok = ins.count_ok
if count_ok > 0:
wm, new_create = WMaterial.objects.get_or_create(
material=wm.material,
batch=wm.batch,
mgroup=wm.mgroup,
belong_dept=wm.belong_dept,
state=WMaterial.WM_OK,
defaults={
'count': count_ok,
'material': wm.material,
'batch': wm.batch,
'mgroup': wm.mgroup,
'belong_dept': wm.belong_dept,
}
)
if not new_create:
wm.count = wm.count + count_ok
wm.save()
else: else:
raise ParseError("不合格数不可大于批次数量") wm.count = wm.count - ins.count_notok
if wm.count >= 0:
# 生成不合格的 wm.save()
count_notok_json = ins.count_notok_json else:
for k, v in count_notok_json.items(): raise ParseError("不合格数不可大于批次数量")
if v > 0:
notok_sign = k.replace('count_n_', '') # 生成不合格的
wm_n, new_create = WMaterial.objects.get_or_create( count_notok_json = ins.count_notok_json
material=wm.material, for k, v in count_notok_json.items():
batch=wm.batch, if v > 0:
mgroup=wm.mgroup, notok_sign = k.replace('count_n_', '')
belong_dept=wm.belong_dept, wm_n, new_create = WMaterial.objects.get_or_create(
notok_sign=notok_sign, material=wm.material,
state=WMaterial.WM_NOTOK, batch=wm.batch,
defaults={ mgroup=wm.mgroup,
'count': v, belong_dept=wm.belong_dept,
'material': wm.material, notok_sign=notok_sign,
'batch': wm.batch, state=WMaterial.WM_NOTOK,
'mgroup': wm.mgroup, defaults={
'belong_dept': wm.belong_dept, 'count': v,
'notok_sign': notok_sign, 'material': wm.material,
'state': WMaterial.WM_NOTOK, 'batch': wm.batch,
} 'mgroup': wm.mgroup,
) 'belong_dept': wm.belong_dept,
if not new_create: 'notok_sign': notok_sign,
wm_n.count = wm_n.count + v 'state': WMaterial.WM_NOTOK,
wm_n.save() }
)
if not new_create:
wm_n.count = wm_n.count + v
wm_n.save()
ins.submit_user = user ins.submit_user = user
ins.submit_time = timezone.now() ins.submit_time = timezone.now()
ins.save() ins.save()

111
apps/wpm/datax.py Normal file
View File

@ -0,0 +1,111 @@
from apps.utils.viewsets import GenericViewSet
from rest_framework.decorators import action
from apps.em.models import Equipment
from apps.wpm.models import Mlog, Handover
from apps.mtm.models import Mgroup
from django.utils import timezone
from django.db.models import Sum
from datetime import datetime, timedelta
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from apps.inm.models import MIOItem, MIO
def tran_time_to_mstate(mstate_json, reminder_interval_list, work_start_time: datetime, now: datetime):
if len(reminder_interval_list) != len(mstate_json):
return '未运行'
xval = 0
for ind, val in enumerate(reminder_interval_list):
xval += val
if work_start_time + timedelta(minutes=xval) > now:
return mstate_json[ind]['name']
return '未运行'
class AnaViewSet(GenericViewSet):
perms_map = {}
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=Serializer)
def equip_last_mlog(self, request):
"""设备最后生产日志
设备最后生产日志
"""
data = request.data
now = timezone.now()
mgroup: Mgroup = Mgroup.objects.get(name=data['mgroup_name'])
# 子状态
mstate_json = mgroup.process.mstate_json
# 生产设备
equip_qs = Equipment.objects.filter(mgroup=mgroup, type = 10)
equip_qs_v = equip_qs.values('id', 'name', 'number', 'state').order_by('number')
equip_qs_l = list(equip_qs_v)
# 设备最后生产日志
mlog_qs_0 = Mlog.objects.filter(equipment__in=equip_qs, mgroup=mgroup)
mlog_qs = mlog_qs_0.exclude(work_end_time__lt=now)
mlog_qs = mlog_qs.annotate(t_count_use=Sum('b_mlog__count_use'))
mlog_qs = mlog_qs.order_by('work_start_time')
mlog_qs_v = mlog_qs.values('id', 'equipment__id', 't_count_use', 'reminder_interval_list', 'work_start_time', 'work_end_time')
mlog_qs_l = list(mlog_qs_v)
mlog_dict = {item['equipment__id']: item for item in mlog_qs_l}
# 统计数据
保温 = 0
冷却 = 0
未运行 = 0
故障 = 0
now = timezone.now()
for item in equip_qs_l:
item['mstate'] = '未运行'
if item['id'] in mlog_dict:
mlog_dict_v = mlog_dict[item['id']]
item['t_count_use'] = mlog_dict_v['t_count_use']
item['reminder_interval_list'] = mlog_dict_v['reminder_interval_list']
item['work_start_time'] = timezone.localtime(mlog_dict_v['work_start_time']).strftime('%Y-%m-%d %H:%M:%S') if mlog_dict_v['work_start_time'] else None
item['work_end_time'] = timezone.localtime(mlog_dict_v['work_end_time']).strftime('%Y-%m-%d %H:%M:%S') if mlog_dict_v['work_end_time'] else None
item['mstate'] = tran_time_to_mstate(mstate_json, mlog_dict_v['reminder_interval_list'], mlog_dict_v['work_start_time'], now)
if item['state'] in [Equipment.EQUIP_STATE_SCRAP, Equipment.EQUIP_STATE_FIX]:
item['mstate'] = '故障'
if item['mstate'] == '保温':
保温 += 1
elif item['mstate'] == '冷却':
冷却 += 1
elif item['mstate'] == '未运行':
未运行 += 1
elif item['mstate'] == '故障':
故障 += 1
ret = {"保温": 保温, "冷却": 冷却, "未运行": 未运行, "故障": 故障}
ret['mstate_json'] = mstate_json
ret['now'] = timezone.localtime(now).strftime('%Y-%m-%d %H:%M:%S')
ret["rows"] = equip_qs_l
return Response(ret)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=Serializer)
def put_prod(self, request):
"""
投产分析
"""
now = timezone.now()
now_2 = now.replace(hour=2, minute=0, second=0, microsecond=0)
mgroup: Mgroup = Mgroup.objects.get(name='退火')
# 子状态
mstate_json = mgroup.process.mstate_json
ret = {}
ret['今日退火投产预测'] = 0
ret['明日退火投产预测'] = 0
ret['rows'] = {}
# 生产记录/今日退火投产预测
# 昨日2点
now_2_yesterday = now_2 - timedelta(days=1)
mlog_qs_0 = Mlog.objects.filter(mgroup=mgroup)
mlog_qs = mlog_qs_0.exclude(work_end_time__lt=now_2_yesterday).annotate(t_count_use=Sum('b_mlog__count_use')).order_by('work_start_time')
mlog_qs_v = mlog_qs.values('id', 'equipment__id', 't_count_use', 'reminder_interval_list', 'work_start_time', 'work_end_time')
mlog_qs_l = list(mlog_qs_v)
for item in mlog_qs_l:
if tran_time_to_mstate(mstate_json, item['reminder_interval_list'], item['work_start_time'], now) == '冷却':
if item['work_end_time'] is None or item['work_end_time'] > now_2:
ret['明日退火投产预测'] += item['t_count_use']
ret['今日退火投产预测'] += item['t_count_use']
return Response(ret)

View File

@ -50,7 +50,8 @@ class WMaterialFilter(filters.FilterSet):
"mgroup__name": ["exact", "in"], "mgroup__name": ["exact", "in"],
"count": ["gte", "lte", "exact"], "count": ["gte", "lte", "exact"],
"notok_sign": ["exact", "in", "isnull"], "notok_sign": ["exact", "in", "isnull"],
"count_xtest": ["gte", "isnull"] "count_xtest": ["gte", "isnull"],
"supplier": ["exact"],
} }
class MlogFilter(filters.FilterSet): class MlogFilter(filters.FilterSet):
@ -65,15 +66,20 @@ class MlogFilter(filters.FilterSet):
"mtask__mgroup__belong_dept__name": ["exact", "contains", "in"], "mtask__mgroup__belong_dept__name": ["exact", "contains", "in"],
"mgroup__belong_dept__name": ["exact", "in", "contains"], "mgroup__belong_dept__name": ["exact", "in", "contains"],
"mgroup__name": ["exact", "in", "contains"], "mgroup__name": ["exact", "in", "contains"],
"submit_time": ["isnull"] "submit_time": ["isnull"],
"fmlog": ["exact"]
} }
class HandoverFilter(filters.FilterSet): class HandoverFilter(filters.FilterSet):
mgroup = filters.CharFilter(label='MgroupId', method='filter_mgroup') mgroup = filters.CharFilter(label='MgroupId', method='filter_mgroup')
dept = filters.CharFilter(label='DeptId', method='filter_dept')
def filter_mgroup(self, queryset, name, value): def filter_mgroup(self, queryset, name, value):
return queryset.filter(send_mgroup__id=value)|queryset.filter(recive_mgroup__id=value) return queryset.filter(send_mgroup__id=value)|queryset.filter(recive_mgroup__id=value)
def filter_dept(self, queryset, name, value):
return queryset.filter(send_dept__id=value)|queryset.filter(recive_dept__id=value)
class Meta: class Meta:
model = Handover model = Handover

View File

@ -0,0 +1,20 @@
# Generated by Django 3.2.12 on 2024-08-30 01:17
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('pum', '0008_auto_20240731_1829'),
('wpm', '0062_auto_20240828_1052'),
]
operations = [
migrations.AddField(
model_name='wmaterial',
name='supplier',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pum.supplier', verbose_name='外协供应商'),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 3.2.12 on 2024-09-02 06:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0063_wmaterial_supplier'),
]
operations = [
migrations.AddField(
model_name='mlog',
name='count_pn_jgqbl',
field=models.PositiveIntegerField(default=0, verbose_name='加工前不良'),
),
migrations.AddField(
model_name='mlogb',
name='count_pn_jgqbl',
field=models.PositiveIntegerField(default=0, verbose_name='加工前不良'),
),
]

View File

@ -0,0 +1,25 @@
# Generated by Django 3.2.12 on 2024-09-03 01:04
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('wf', '0002_alter_state_filter_dept'),
('wpm', '0064_auto_20240902_1411'),
]
operations = [
migrations.AddField(
model_name='mlog',
name='oinfo_json',
field=models.JSONField(blank=True, default=dict, verbose_name='其他信息'),
),
migrations.AddField(
model_name='mlog',
name='ticket',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mlog_ticket', to='wf.ticket', verbose_name='关联工单'),
),
]

View File

@ -0,0 +1,28 @@
# Generated by Django 3.2.12 on 2024-09-03 06:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('wpm', '0065_auto_20240903_0904'),
]
operations = [
migrations.AddField(
model_name='mlog',
name='test_file',
field=models.TextField(blank=True, null=True, verbose_name='检验文件'),
),
migrations.AddField(
model_name='mlogb',
name='count_notok_json',
field=models.JSONField(blank=True, default=list, verbose_name='不合格情况'),
),
migrations.AddField(
model_name='mlogb',
name='note',
field=models.TextField(blank=True, default='', verbose_name='备注'),
),
]

View File

@ -0,0 +1,42 @@
# Generated by Django 3.2.12 on 2024-09-04 01:03
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mtm', '0041_process_mstate_json'),
('pm', '0020_mtaskb'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('wpm', '0066_auto_20240903_1401'),
]
operations = [
migrations.CreateModel(
name='Fmlog',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('note', models.TextField(blank=True, default='', verbose_name='备注')),
('create_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fmlog_create_by', to=settings.AUTH_USER_MODEL, verbose_name='创建人')),
('mgroup', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fmlog_mgroup', to='mtm.mgroup', verbose_name='工段')),
('mtask', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fmlog_mtask', to='pm.mtask', verbose_name='任务')),
('route', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mtm.route', verbose_name='生产路线')),
('update_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='fmlog_update_by', to=settings.AUTH_USER_MODEL, verbose_name='最后编辑人')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='mlog',
name='fmlog',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mlog_fmlog', to='wpm.fmlog', verbose_name='关联生产日志'),
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 3.2.12 on 2024-09-04 07:49
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('pm', '0020_mtaskb'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('wpm', '0067_auto_20240904_0903'),
]
operations = [
migrations.AddField(
model_name='mlog',
name='mtaskb',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mlog_mtaskb', to='pm.mtaskb', verbose_name='关联个人任务'),
),
migrations.AddField(
model_name='mlog',
name='test_time',
field=models.DateTimeField(blank=True, null=True, verbose_name='检验时间'),
),
migrations.AddField(
model_name='mlog',
name='test_user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='mlog_test_user', to=settings.AUTH_USER_MODEL, verbose_name='检验人'),
),
migrations.AlterField(
model_name='mlog',
name='fill_way',
field=models.PositiveSmallIntegerField(default=10, help_text='10:仅二级;20:二三级;30:一二级', verbose_name='填写方式'),
),
]

View File

@ -0,0 +1,40 @@
# Generated by Django 3.2.12 on 2024-09-05 02:57
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('wpm', '0068_auto_20240904_1549'),
]
operations = [
migrations.AlterField(
model_name='handover',
name='batch',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='批次号'),
),
migrations.AlterField(
model_name='handover',
name='type',
field=models.PositiveSmallIntegerField(choices=[(10, '正常交接'), (20, '返修交接'), (30, '检验交接'), (40, '报废交接')], default=10, verbose_name='交接类型'),
),
migrations.CreateModel(
name='Handoverb',
fields=[
('id', models.CharField(editable=False, help_text='主键ID', max_length=20, primary_key=True, serialize=False, verbose_name='主键ID')),
('create_time', models.DateTimeField(default=django.utils.timezone.now, help_text='创建时间', verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, help_text='修改时间', verbose_name='修改时间')),
('is_deleted', models.BooleanField(default=False, help_text='删除标记', verbose_name='删除标记')),
('count', models.PositiveIntegerField(default=0, verbose_name='送料数')),
('handover', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='wpm.handover', verbose_name='关联交接记录')),
('wm', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='handoverb_wm', to='wpm.wmaterial', verbose_name='关联车间库存')),
],
options={
'abstract': False,
},
),
]

View File

@ -1,13 +1,14 @@
from django.db import models from django.db import models
from apps.utils.models import CommonADModel, CommonBDModel, BaseModel from apps.utils.models import CommonADModel, CommonBDModel, BaseModel
from apps.mtm.models import Mgroup, Team, Shift, Material, Route from apps.mtm.models import Mgroup, Team, Shift, Material, Route
from apps.pm.models import Mtask from apps.pm.models import Mtask, Mtaskb
from apps.system.models import User from apps.system.models import User
from django.utils.timezone import localtime from django.utils.timezone import localtime
from apps.em.models import Equipment from apps.em.models import Equipment
from apps.system.models import Dept from apps.system.models import Dept
from datetime import timedelta from datetime import timedelta
from apps.pum.models import Supplier from apps.pum.models import Supplier
from django.db.models import Sum
# Create your models here. # Create your models here.
@ -97,6 +98,7 @@ class WMaterial(CommonBDModel):
state = models.PositiveSmallIntegerField('状态', default=10, choices=((10, '合格'), (20, '不合格'), (30, '返修'), (40, '检验'), (50, '报废'))) state = models.PositiveSmallIntegerField('状态', default=10, choices=((10, '合格'), (20, '不合格'), (30, '返修'), (40, '检验'), (50, '报废')))
material = models.ForeignKey( material = models.ForeignKey(
Material, verbose_name='物料', on_delete=models.CASCADE, related_name='wm_m') Material, verbose_name='物料', on_delete=models.CASCADE, related_name='wm_m')
supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, null=True, blank=True)
mgroup = models.ForeignKey(Mgroup, verbose_name='所在工段', on_delete=models.CASCADE, null=True, blank=True) mgroup = models.ForeignKey(Mgroup, verbose_name='所在工段', on_delete=models.CASCADE, null=True, blank=True)
batch = models.CharField('批次号', max_length=50) batch = models.CharField('批次号', max_length=50)
count = models.PositiveIntegerField('当前数量', default=0) count = models.PositiveIntegerField('当前数量', default=0)
@ -105,17 +107,29 @@ class WMaterial(CommonBDModel):
material_origin = models.ForeignKey(Material, verbose_name='原始物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mo') material_origin = models.ForeignKey(Material, verbose_name='原始物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mo')
count_xtest = models.PositiveIntegerField('已检数量', null=True, blank=True) count_xtest = models.PositiveIntegerField('已检数量', null=True, blank=True)
@property
def count_working(self):
return int(Mlogb.objects.filter(wm_in=self, mlog__work_end_time__isnull=True).aggregate(count=Sum('count_use'))['count'] or 0)
class Fmlog(CommonADModel):
route = models.ForeignKey(Route, verbose_name='生产路线', on_delete=models.SET_NULL, null=True, blank=True)
mtask = models.ForeignKey(Mtask, verbose_name='任务', on_delete=models.CASCADE, related_name='fmlog_mtask')
mgroup = models.ForeignKey(Mgroup, verbose_name='工段', on_delete=models.CASCADE, related_name='fmlog_mgroup')
note = models.TextField('备注', default='', blank=True)
class Mlog(CommonADModel): class Mlog(CommonADModel):
""" """
生产日志 生产日志
""" """
# 变成父级的字段 # 变成父级的字段
MLOG_ONETIME = 10 MLOG_2 = 10
MLOG_STEP = 20 MLOG_23 = 20
MLOG_12 = 30
MTYPE_SELF = 10 MTYPE_SELF = 10
MTYPE_OUT = 20 MTYPE_OUT = 20
fill_way = models.PositiveSmallIntegerField("填写方式", default=10, help_text='10:一次填写;20:分步填写') fmlog = models.ForeignKey(Fmlog, verbose_name='关联生产日志', on_delete=models.SET_NULL, null=True, blank=True, related_name='mlog_fmlog')
mtaskb = models.ForeignKey(Mtaskb, verbose_name='关联个人任务', on_delete=models.CASCADE, related_name='mlog_mtaskb', null=True, blank=True)
fill_way = models.PositiveSmallIntegerField("填写方式", default=10, help_text='10:仅二级;20:二三级;30:一二级')
mtype = models.PositiveSmallIntegerField('生产类型', default=10, help_text='10:自生产;20:外协生产', choices=((10, '自生产'), (20, '外协生产'))) mtype = models.PositiveSmallIntegerField('生产类型', default=10, help_text='10:自生产;20:外协生产', choices=((10, '自生产'), (20, '外协生产')))
supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, null=True, blank=True) supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, null=True, blank=True)
work_start_time = models.DateTimeField('生产开始时间', null=True, blank=True) work_start_time = models.DateTimeField('生产开始时间', null=True, blank=True)
@ -154,6 +168,7 @@ class Mlog(CommonADModel):
count_notok = models.PositiveIntegerField('不合格数', default=0) count_notok = models.PositiveIntegerField('不合格数', default=0)
count_break_t = models.PositiveIntegerField('检验碎料数', default=0) count_break_t = models.PositiveIntegerField('检验碎料数', default=0)
count_pn_jgqbl = models.PositiveIntegerField('加工前不良', default=0)
count_n_zw = models.PositiveIntegerField('炸纹', default=0) count_n_zw = models.PositiveIntegerField('炸纹', default=0)
count_n_tw = models.PositiveIntegerField('条纹', default=0) count_n_tw = models.PositiveIntegerField('条纹', default=0)
count_n_qp = models.PositiveIntegerField('气泡', default=0) count_n_qp = models.PositiveIntegerField('气泡', default=0)
@ -191,8 +206,6 @@ class Mlog(CommonADModel):
count_n_yd = models.PositiveIntegerField('圆度', default=0) count_n_yd = models.PositiveIntegerField('圆度', default=0)
count_n_txd = models.PositiveIntegerField('同心度', default=0) count_n_txd = models.PositiveIntegerField('同心度', default=0)
count_n_hd = models.PositiveIntegerField('厚度', default=0) count_n_hd = models.PositiveIntegerField('厚度', default=0)
count_n_qt = models.PositiveIntegerField('其他', default=0) count_n_qt = models.PositiveIntegerField('其他', default=0)
handle_date = models.DateField('操作日期', null=True, blank=True) handle_date = models.DateField('操作日期', null=True, blank=True)
@ -212,6 +225,14 @@ class Mlog(CommonADModel):
submit_user = models.ForeignKey( submit_user = models.ForeignKey(
User, verbose_name='提交人', on_delete=models.CASCADE, null=True, blank=True, related_name='mlog_submit_user') User, verbose_name='提交人', on_delete=models.CASCADE, null=True, blank=True, related_name='mlog_submit_user')
oinfo_json = models.JSONField('其他信息', default=dict, blank=True)
ticket = models.ForeignKey('wf.ticket', verbose_name='关联工单',
on_delete=models.SET_NULL, related_name='mlog_ticket', null=True, blank=True, db_constraint=False)
test_file = models.TextField('检验文件', null=True, blank=True)
test_user = models.ForeignKey(
User, verbose_name='检验人', on_delete=models.CASCADE, null=True, blank=True, related_name='mlog_test_user')
test_time = models.DateTimeField('检验时间', null=True, blank=True)
@property @property
def mlogb(self): def mlogb(self):
return Mlogb.objects.filter(mlog=self).exclude(material_out=None) return Mlogb.objects.filter(mlog=self).exclude(material_out=None)
@ -229,6 +250,7 @@ class Mlog(CommonADModel):
class Mlogb(BaseModel): class Mlogb(BaseModel):
mlog = models.ForeignKey(Mlog, verbose_name='关联日志', mlog = models.ForeignKey(Mlog, verbose_name='关联日志',
on_delete=models.CASCADE, related_name='b_mlog') on_delete=models.CASCADE, related_name='b_mlog')
note = models.TextField('备注', default='', blank=True)
batch = models.CharField('批次号', max_length=50, null=True, blank=True) batch = models.CharField('批次号', max_length=50, null=True, blank=True)
mtask = models.ForeignKey(Mtask, verbose_name='关联任务', mtask = models.ForeignKey(Mtask, verbose_name='关联任务',
on_delete=models.CASCADE, related_name='mlogb_mtask', null=True, blank=True) on_delete=models.CASCADE, related_name='mlogb_mtask', null=True, blank=True)
@ -245,6 +267,7 @@ class Mlogb(BaseModel):
count_ok = models.PositiveIntegerField('合格数量', default=0) count_ok = models.PositiveIntegerField('合格数量', default=0)
count_notok = models.PositiveIntegerField('不合格数', default=0) count_notok = models.PositiveIntegerField('不合格数', default=0)
count_pn_jgqbl = models.PositiveIntegerField('加工前不良', default=0)
# 添加不合格字段后需要更改cal_mlog_count_from_mlogb # 添加不合格字段后需要更改cal_mlog_count_from_mlogb
count_n_hs = models.PositiveIntegerField('划伤', default=0) count_n_hs = models.PositiveIntegerField('划伤', default=0)
count_n_qp = models.PositiveIntegerField('气泡', default=0) count_n_qp = models.PositiveIntegerField('气泡', default=0)
@ -260,6 +283,7 @@ class Mlogb(BaseModel):
count_n_txd = models.PositiveIntegerField('同心度', default=0) count_n_txd = models.PositiveIntegerField('同心度', default=0)
count_n_hd = models.PositiveIntegerField('厚度', default=0) count_n_hd = models.PositiveIntegerField('厚度', default=0)
count_n_qt = models.PositiveIntegerField('其他', default=0) count_n_qt = models.PositiveIntegerField('其他', default=0)
count_notok_json = models.JSONField('不合格情况', default=list, blank=True)
class Handover(CommonADModel): class Handover(CommonADModel):
""" """
@ -268,7 +292,8 @@ class Handover(CommonADModel):
H_NORMAL = 10 H_NORMAL = 10
H_REPAIR = 20 H_REPAIR = 20
H_TEST = 30 H_TEST = 30
type = models.PositiveSmallIntegerField('交接类型', choices=[(H_NORMAL, '正常交接'), (H_REPAIR, '返修交接'), (H_TEST, '检验交接')], default=H_NORMAL) H_SCRAP = 40
type = models.PositiveSmallIntegerField('交接类型', choices=[(H_NORMAL, '正常交接'), (H_REPAIR, '返修交接'), (H_TEST, '检验交接'), (H_SCRAP, '报废交接')], default=H_NORMAL)
send_date = models.DateField('送料日期') send_date = models.DateField('送料日期')
send_user = models.ForeignKey( send_user = models.ForeignKey(
User, verbose_name='交送人', on_delete=models.CASCADE, related_name='handover_send_user') User, verbose_name='交送人', on_delete=models.CASCADE, related_name='handover_send_user')
@ -276,7 +301,7 @@ class Handover(CommonADModel):
Mgroup, verbose_name='送料工段', on_delete=models.CASCADE, null=True, blank=True) Mgroup, verbose_name='送料工段', on_delete=models.CASCADE, null=True, blank=True)
send_dept = models.ForeignKey( send_dept = models.ForeignKey(
Dept, verbose_name='送料部门', on_delete=models.CASCADE, related_name='handover_send_dept') Dept, verbose_name='送料部门', on_delete=models.CASCADE, related_name='handover_send_dept')
batch = models.CharField('批次号', max_length=50) batch = models.CharField('批次号', max_length=50, null=True, blank=True)
material = models.ForeignKey( material = models.ForeignKey(
Material, verbose_name='物料', on_delete=models.CASCADE, related_name='h_ma') Material, verbose_name='物料', on_delete=models.CASCADE, related_name='h_ma')
material_changed = models.ForeignKey(Material, verbose_name='变更后物料', on_delete=models.CASCADE, null=True, blank=True, related_name='h_ma_c') material_changed = models.ForeignKey(Material, verbose_name='变更后物料', on_delete=models.CASCADE, null=True, blank=True, related_name='h_ma_c')
@ -298,6 +323,15 @@ class Handover(CommonADModel):
submit_user = models.ForeignKey( submit_user = models.ForeignKey(
User, verbose_name='提交人', on_delete=models.CASCADE, null=True, blank=True, related_name='handover_submit_user') User, verbose_name='提交人', on_delete=models.CASCADE, null=True, blank=True, related_name='handover_submit_user')
@property
def handoverb(self):
return Handoverb.objects.filter(handover=self)
class Handoverb(BaseModel):
handover = models.ForeignKey(Handover, verbose_name='关联交接记录', on_delete=models.CASCADE)
wm = models.ForeignKey(WMaterial, verbose_name='关联车间库存', on_delete=models.SET_NULL,
null=True, blank=True, related_name='handoverb_wm')
count = models.PositiveIntegerField('送料数', default=0)
class AttLog(CommonADModel): class AttLog(CommonADModel):
""" """

View File

@ -4,12 +4,13 @@ from rest_framework import serializers
from rest_framework.exceptions import ValidationError, ParseError from rest_framework.exceptions import ValidationError, ParseError
from datetime import datetime from datetime import datetime
from .models import SfLog, StLog, SfLogExp, WMaterial, Mlog, Handover, Mlogb, AttLog, OtherLog from .models import (SfLog, StLog, SfLogExp, WMaterial, Mlog,
Handover, Handoverb, Mlogb, AttLog, OtherLog, Fmlog)
from apps.system.models import Dept, User from apps.system.models import Dept, User
from apps.system.serializers import UserSimpleSerializer from apps.system.serializers import UserSimpleSerializer
from apps.pm.models import Mtask from apps.pm.models import Mtask, Mtaskb
from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change, cal_exp_duration_sec from apps.wpm.tasks import cal_enstat_when_pcoal_heat_change, cal_enstat_when_team_change, cal_exp_duration_sec
from apps.wpm.services import get_sflog, find_material_can_change from apps.wpm.services import get_sflog, find_material_can_change, generate_new_batch
from apps.mtm.models import Mgroup, TeamMember, Shift, Material, Route from apps.mtm.models import Mgroup, TeamMember, Shift, Material, Route
from apps.mtm.serializers import MaterialSimpleSerializer from apps.mtm.serializers import MaterialSimpleSerializer
from django.db import transaction from django.db import transaction
@ -17,7 +18,7 @@ from django.utils import timezone
from django.core.cache import cache from django.core.cache import cache
from django.utils.timezone import localdate from django.utils.timezone import localdate
from apps.qm.models import NotOkOption from apps.qm.models import NotOkOption
from apps.wf.serializers import TicketSimpleSerializer
class OtherLogSerializer(CustomModelSerializer): class OtherLogSerializer(CustomModelSerializer):
class Meta: class Meta:
@ -172,6 +173,7 @@ class SflogExpSerializer(CustomModelSerializer):
class WMaterialSerializer(CustomModelSerializer): class WMaterialSerializer(CustomModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True) material_ = MaterialSimpleSerializer(source='material', read_only=True)
supplier_name = serializers.CharField(source='supplier.name', read_only=True)
material_name = serializers.StringRelatedField( material_name = serializers.StringRelatedField(
source='material', read_only=True) source='material', read_only=True)
mgroup_name = serializers.StringRelatedField(source='mgroup.name', read_only=True) mgroup_name = serializers.StringRelatedField(source='mgroup.name', read_only=True)
@ -179,6 +181,7 @@ class WMaterialSerializer(CustomModelSerializer):
source='belong_dept.name', read_only=True) source='belong_dept.name', read_only=True)
material_origin_name = serializers.StringRelatedField(source='material_origin', read_only=True) material_origin_name = serializers.StringRelatedField(source='material_origin', read_only=True)
notok_sign_name = serializers.SerializerMethodField() notok_sign_name = serializers.SerializerMethodField()
count_working = serializers.CharField(read_only=True, label='在制数量')
def get_notok_sign_name(self, obj): def get_notok_sign_name(self, obj):
return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None
@ -211,7 +214,9 @@ class MlogbDetailSerializer(CustomModelSerializer):
fields = '__all__' fields = '__all__'
class MlogSerializer(CustomModelSerializer): class MlogSerializer(CustomModelSerializer):
mstate_json = serializers.JSONField(source='mgroup.process.mstate_json', read_only=True)
supplier_name = serializers.CharField(source='supplier.name', read_only=True) supplier_name = serializers.CharField(source='supplier.name', read_only=True)
routepack_name = serializers.CharField(source='route.routepack.name', read_only=True)
belong_dept = serializers.CharField( belong_dept = serializers.CharField(
source='mgroup.belong_dept.id', read_only=True) source='mgroup.belong_dept.id', read_only=True)
belong_dept_name = serializers.CharField( belong_dept_name = serializers.CharField(
@ -260,6 +265,8 @@ class MlogSerializer(CustomModelSerializer):
source='handle_users', many=True, read_only=True) source='handle_users', many=True, read_only=True)
equipments_name = serializers.StringRelatedField( equipments_name = serializers.StringRelatedField(
source='equipments', read_only=True, many=True) source='equipments', read_only=True, many=True)
ticket_ = TicketSimpleSerializer(source='ticket', read_only=True)
test_user_name = serializers.CharField(source='test_user.name', read_only=True)
class Meta: class Meta:
model = Mlog model = Mlog
@ -271,21 +278,23 @@ class MlogSerializer(CustomModelSerializer):
} }
def create(self, validated_data): def create(self, validated_data):
mtask: Mtask = validated_data.get('mtask', None) material_out = validated_data['material_out']
if mtask:
validated_data['mgroup'] = mtask.mgroup
validated_data['material_in'] = mtask.material_in
material_out = mtask.material_out
validated_data['material_out'] = material_out
validated_data['handle_date'] = mtask.end_date
else:
mgroup = validated_data['mgroup']
material_out = validated_data['material_out']
if not (mgroup and material_out):
raise ValidationError('缺少工段或产物!')
with transaction.atomic(): with transaction.atomic():
mlogb = validated_data.pop('mlogb', []) mlogb = validated_data.pop('mlogb', [])
instance: Mlog = super().create(validated_data) instance: Mlog = super().create(validated_data)
# 自动生成mlogb
batch_in = instance.batch
if instance.wm_in:
batch_in = instance.wm_in.batch
add_dict = {
'mlog': instance, 'batch': batch_in, 'wm_in': instance.wm_in,
'mtask': instance.mtask, 'material_in': instance.material_in,
'count_use': instance.count_use, 'count_break': instance.count_break,
'count_pn_jgqbl': instance.count_pn_jgqbl
}
Mlogb.objects.create(**add_dict)
# mlogb只用于组合件输出物填写
brotherId_should_list = material_out.brothers brotherId_should_list = material_out.brothers
if brotherId_should_list: if brotherId_should_list:
if mlogb: if mlogb:
@ -295,9 +304,29 @@ class MlogSerializer(CustomModelSerializer):
mlog=instance, batch=instance.batch, mtask=instance.mtask, material_out=item['material_out'], count_ok=item['count_ok']) mlog=instance, batch=instance.batch, mtask=instance.mtask, material_out=item['material_out'], count_ok=item['count_ok'])
else: else:
raise ValidationError('缺少产出物信息') raise ValidationError('缺少产出物信息')
else:
# 生成产出物
batch_out = validated_data.get('batch', None)
if batch_out:
pass
else:
batch_out = generate_new_batch(batch_in, instance)
add_dict_2 = {
'mlog': instance, 'batch': batch_out,
'mtask': instance.mtask, 'material_out': instance.material_out,
'count_ok': instance.count_ok, 'count_notok': instance.count_notok,
'count_break_t': instance.count_break_t
}
for f in Mlogb._meta.fields:
if 'count_n_' in f.name:
add_dict_2[f.name] = getattr(instance, f.name)
Mlogb.objects.create(**add_dict_2)
return instance return instance
def update(self, instance, validated_data): def update(self, instance, validated_data):
if instance.fill_way == Mlog.MLOG_23:
raise ParseError('不支持的填写类型')
validated_data.pop('mtask', None) validated_data.pop('mtask', None)
validated_data.pop('mgroup', None) validated_data.pop('mgroup', None)
if instance.mtask: if instance.mtask:
@ -305,17 +334,70 @@ class MlogSerializer(CustomModelSerializer):
# validated_data.pop('handle_user', None) # validated_data.pop('handle_user', None)
with transaction.atomic(): with transaction.atomic():
mlogb = validated_data.pop('mlogb', []) mlogb = validated_data.pop('mlogb', [])
instance = super().update(instance, validated_data) instance: Mlog = super().update(instance, validated_data)
if mlogb: if instance.fill_way == Mlog.MLOG_12:
# 自动生成mlogb
batch_in = instance.batch
if instance.wm_in:
batch_in = instance.wm_in.batch
minx, _ = Mlogb.objects.get_or_create(
mlog=instance,
batch=batch_in,
wm_in=instance.wm_in,
mtask=instance.mtask,
material_in=instance.material_in
)
minx.count_use = instance.count_use
minx.count_break = instance.count_break
minx.count_pn_jgqbl = instance.count_pn_jgqbl
minx.save()
if mlogb and instance.fill_way == Mlog.MLOG_2:
Mlogb.objects.filter(mlog=instance, material_out__isnull=False).update(count_ok=0) Mlogb.objects.filter(mlog=instance, material_out__isnull=False).update(count_ok=0)
for item in mlogb: for item in mlogb:
Mlogb.objects.filter(mlog=instance, material_out=item['material_out']).update( Mlogb.objects.filter(mlog=instance, material_out=item['material_out']).update(
count_ok=item['count_ok']) count_ok=item['count_ok'])
elif instance.fill_way == Mlog.MLOG_12:
# 生成产出物
batch_out = instance.batch
if batch_out:
pass
else:
batch_out = generate_new_batch(batch_in, instance)
mox, _ = Mlogb.objects.get_or_create(mlog=instance, batch=batch_out,
mtask=instance.mtask, material_out=instance.material_out)
mox.count_ok = instance.count_ok
mox.count_notok = instance.count_notok
mox.count_break_t = instance.count_break_t
for f in Mlogb._meta.fields:
if 'count_n_' in f.name:
setattr(mox, f.name, getattr(instance, f.name))
mox.save()
Mlogb.objects.filter(mlog=instance, material_out__isnull=False).exclude(id=mox.id).delete()
return instance return instance
def validate(self, attrs): def validate(self, attrs):
attrs['fill_way'] = Mlog.MLOG_ONETIME attrs['fill_way'] = Mlog.MLOG_2
attrs['mtype'] = Mlog.MTYPE_SELF # 默认为自生产 attrs['mtype'] = Mlog.MTYPE_SELF # 默认为自生产
fmlog = attrs.get('fmlog', None)
mtaskb = attrs.get('mtaskb', None)
if fmlog:
attrs['fill_way'] = Mlog.MLOG_12
wm_in: WMaterial = attrs.get('wm_in', None)
if wm_in:
pass
else:
raise ParseError('未提供消耗的车间物料')
attrs['route'] = fmlog.route
attrs['mgroup'] = fmlog.mgroup
attrs['mtask'] = fmlog.mtask
if attrs['mtask'].mtaskb and mtaskb is None:
raise ParseError('子任务不能为空')
if mtaskb and mtaskb.mtask != fmlog.mtask:
raise ParseError('子任务不一致')
if wm_in.material != attrs['mtask'].material_in:
raise ParseError('消耗物料与任务不一致')
mtask = attrs.get('mtask', None) mtask = attrs.get('mtask', None)
count_notok = 0 count_notok = 0
for i in attrs: for i in attrs:
@ -330,17 +412,45 @@ class MlogSerializer(CustomModelSerializer):
if mtask.start_date == mtask.end_date: if mtask.start_date == mtask.end_date:
attrs['handle_date'] = mtask.start_date attrs['handle_date'] = mtask.start_date
else: else:
if attrs['handle_date'] >= mtask.start_date and attrs['handle_date'] <= mtask.end_date: if attrs['work_end_time']:
pass attrs['handle_date'] = attrs['work_end_time'].date()
else: elif attrs['work_start_time']:
raise ValidationError('操作日期错误') attrs['handle_date'] = attrs['work_start_time'].date()
# if attrs['handle_date'] >= mtask.start_date and attrs['handle_date'] <= mtask.end_date:
# pass
# else:
# if attrs['handle_date'] >= mtask.start_date and attrs['handle_date'] <= mtask.end_date:
# pass
# else:
# raise ValidationError('操作日期错误')
mtaskb: Mtaskb = attrs.get('mtaskb', None)
if mtaskb:
mtask = mtaskb.mtask
attrs['mtask'] = mtask
attrs['handle_user'] = mtaskb.handle_user
else:
mtask: Mtask = attrs.get('mtask', None)
if mtask:
attrs['mgroup'] = mtask.mgroup
attrs['material_in'] = mtask.material_in
material_out = mtask.material_out
attrs['material_out'] = material_out
if mtask.start_date == mtask.end_date:
attrs['handle_date'] = mtask.end_date
else:
mgroup = attrs['mgroup']
material_out = attrs['material_out']
if not (mgroup and material_out):
raise ValidationError('缺少工段或产物!')
return attrs return attrs
class MlogInitSerializer(CustomModelSerializer): class MlogInitSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mlog model = Mlog
fields = ['id', 'work_start_time', 'mgroup', 'reminder_interval_list', 'route', 'equipment', 'handle_user', 'note', 'mtype', 'supplier'] fields = ['id',
'work_start_time', 'work_end_time', 'mgroup', 'reminder_interval_list',
'route', 'equipment', 'handle_user', 'note', 'mtype', 'supplier', 'test_file', 'test_user', 'test_time', 'oinfo_json']
extra_kwargs = { extra_kwargs = {
'work_start_time': {'required': True}, 'work_start_time': {'required': True},
'route':{'required': True}, 'route':{'required': True},
@ -357,17 +467,19 @@ class MlogInitSerializer(CustomModelSerializer):
attrs['hour_work'] = route.hour_work attrs['hour_work'] = route.hour_work
attrs['material_in'] = route.material_in attrs['material_in'] = route.material_in
attrs['material_out'] = route.material_out attrs['material_out'] = route.material_out
attrs['fill_way'] = Mlog.MLOG_STEP attrs['fill_way'] = Mlog.MLOG_23
if mtype == Mlog.MTYPE_OUT: if mtype == Mlog.MTYPE_OUT:
supplier = attrs.get('supplier', None) supplier = attrs.get('supplier', None)
if not supplier: if not supplier:
raise ValidationError('外协必须选择外协单位') raise ValidationError('外协必须选择外协单位')
if attrs.get('work_end_time', None):
attrs['handle_date'] = localdate(attrs['work_end_time'])
return attrs return attrs
class MlogChangeSerializer(CustomModelSerializer): class MlogChangeSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mlog model = Mlog
fields = ['id', 'work_end_time', 'handle_user', 'note'] fields = ['id', 'work_end_time', 'handle_user', 'note', 'oinfo_json', 'test_file', 'test_user', 'test_time']
def validate(self, attrs): def validate(self, attrs):
if attrs.get('work_end_time', None): if attrs.get('work_end_time', None):
@ -378,12 +490,14 @@ class MlogChangeSerializer(CustomModelSerializer):
class MlogbInSerializer(CustomModelSerializer): class MlogbInSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mlogb model = Mlogb
fields = ['id', 'mlog', 'mtask', 'wm_in', 'count_use'] fields = ['id', 'mlog', 'mtask', 'wm_in', 'count_use', 'count_pn_jgqbl', 'count_break', 'note']
extra_kwargs = {'count_use': {'required': True}, 'mtask': {'required': True}, 'wm_in': {'required': True}} extra_kwargs = {'count_use': {'required': True}, 'mtask': {'required': True}, 'wm_in': {'required': True}}
def validate(self, attrs): def validate(self, attrs):
mlog: Mlog = attrs['mlog'] mlog: Mlog = attrs['mlog']
mtask: Mtask = attrs['mtask'] mtask: Mtask = attrs['mtask']
if mtask.state != Mtask.MTASK_ASSGINED:
raise ValidationError('该任务非下达中不可选择')
wm_in: WMaterial = attrs['wm_in'] wm_in: WMaterial = attrs['wm_in']
if wm_in.state != WMaterial.WM_OK: if wm_in.state != WMaterial.WM_OK:
raise ValidationError('非合格品不可使用') raise ValidationError('非合格品不可使用')
@ -416,18 +530,41 @@ class MlogbInSerializer(CustomModelSerializer):
class MlogbInUpdateSerializer(CustomModelSerializer): class MlogbInUpdateSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mlogb model = Mlogb
fields = ['id', 'count_use', 'count_break'] fields = ['id', 'count_use', 'count_break', 'count_pn_jgqbl', 'note']
class MlogbOutUpdateSerializer(CustomModelSerializer): class MlogbOutUpdateSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Mlogb model = Mlogb
fields = "__all__" fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ['mlog', 'mtask', 'wm_in', 'material_in', 'material_out', 'count_use', 'count_break'] read_only_fields = EXCLUDE_FIELDS_BASE + ['mlog', 'mtask', 'wm_in', 'material_in', 'material_out', 'count_use', 'count_break', 'count_pn_jgqbl']
def validate(self, attrs): def validate(self, attrs):
count_notok_json = attrs.get('count_notok_json', [])
# count_notok_json字段处理
if count_notok_json:
# 先置0字段
for i in attrs:
if 'count_n_' in i:
i = 0
count_notok_dict = {}
for item in count_notok_json:
notok = item['notok']
count = item['count']
full_notok = f'count_n_{notok}'
if not hasattr(Mlogb, full_notok):
raise ValidationError(f'{notok}-该不合格项不存在')
if full_notok in count_notok_dict:
count_notok_dict[full_notok] = count_notok_dict[full_notok] + count
else:
count_notok_dict[full_notok] = count
for k, v in count_notok_dict.items():
attrs[k] = v
count_notok = 0 count_notok = 0
for i in attrs: for i in attrs:
if 'count_n_' in i: if 'count_n_' in i:
if not hasattr(Mlogb, i):
raise ValidationError(f'{i}不存在')
count_notok = count_notok + attrs[i] count_notok = count_notok + attrs[i]
attrs['count_notok'] = count_notok attrs['count_notok'] = count_notok
if attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']: if attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']:
@ -448,6 +585,14 @@ class MlogRelatedSerializer(serializers.Serializer):
class DeptBatchSerializer(serializers.Serializer): class DeptBatchSerializer(serializers.Serializer):
belong_dept_name = serializers.CharField(label='车间名称') belong_dept_name = serializers.CharField(label='车间名称')
class HandoverbSerializer(CustomModelSerializer):
batch = serializers.CharField(source='wm.batch', read_only=True)
notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
class Meta:
model = Handoverb
fields = "__all__"
read_only_fields = EXCLUDE_FIELDS_BASE + ['handover']
extra_kwargs = {'wm': {'required': True}}
class HandoverSerializer(CustomModelSerializer): class HandoverSerializer(CustomModelSerializer):
# wm = serializers.PrimaryKeyRelatedField( # wm = serializers.PrimaryKeyRelatedField(
@ -466,18 +611,28 @@ class HandoverSerializer(CustomModelSerializer):
material_name = serializers.StringRelatedField( material_name = serializers.StringRelatedField(
source='material', read_only=True) source='material', read_only=True)
wm_notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True) wm_notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
handoverb = HandoverbSerializer(many=True)
def validate(self, attrs): def validate(self, attrs):
if 'type' not in attrs: if 'type' not in attrs:
attrs['type'] = Handover.H_NORMAL attrs['type'] = Handover.H_NORMAL
wm:WMaterial = attrs['wm'] wm:WMaterial = attrs.get('wm', None)
material = wm.material handoverb = attrs.get('handoverb', [])
if wm:
attrs['handoverb'] = [{"wm": wm, "count": attrs["count"] }]
handoverb = attrs['handoverb']
attrs['batch'] = wm.batch
elif handoverb:
wm: WMaterial = handoverb[0]["wm"]
if wm:
pass
else:
raise ParseError('必须指定车间库存')
attrs['material'] = wm.material attrs['material'] = wm.material
attrs['batch'] = wm.batch
attrs['send_dept'] = wm.belong_dept attrs['send_dept'] = wm.belong_dept
if attrs['wm'].mgroup: if wm.mgroup:
attrs['send_mgroup'] = wm.mgroup attrs['send_mgroup'] = wm.mgroup
if material.process and material.process.into_wm_mgroup and 'recive_mgroup' not in attrs: if attrs['material'].process and attrs['material'].process.into_wm_mgroup and 'recive_mgroup' not in attrs:
raise ValidationError('必须指定交接工段') raise ValidationError('必须指定交接工段')
if 'recive_mgroup' in attrs and attrs['recive_mgroup']: if 'recive_mgroup' in attrs and attrs['recive_mgroup']:
attrs['recive_dept'] = attrs['recive_mgroup'].belong_dept attrs['recive_dept'] = attrs['recive_mgroup'].belong_dept
@ -485,10 +640,24 @@ class HandoverSerializer(CustomModelSerializer):
raise ValidationError('收料车间和收料工段必须有一个') raise ValidationError('收料车间和收料工段必须有一个')
if 'send_dept' not in attrs and 'send_mgroup' not in attrs: if 'send_dept' not in attrs and 'send_mgroup' not in attrs:
raise ValidationError('送料车间和送料工段必须有一个') raise ValidationError('送料车间和送料工段必须有一个')
if wm.notok_sign is not None and attrs['type'] in [Handover.H_NORMAL, Handover.H_TEST]: t_count = 0
raise ValidationError('物料不合格,不能进行正常/检验交接') for ind, item in enumerate(attrs['handoverb']):
if wm.count_xtest is not None: wm = item["wm"]
raise ValidationError('物料检验中,不能进行交接') t_count += item["count"]
if wm.mgroup != attrs['send_mgroup']:
raise ParseError(f'{ind+1}物料与交接工段不一致')
if attrs["material"] != wm.material:
raise ParseError(f'{ind+1}物料与交接物料不一致')
if wm.notok_sign is not None and attrs['type'] in [Handover.H_NORMAL, Handover.H_TEST]:
raise ParseError(f'{ind+1}物料不合格,不能进行正常/检验交接')
if wm.count_xtest is not None:
raise ParseError(f'{ind+1}物料检验中,不能进行交接')
attrs["count"] = t_count
if attrs['type'] == Handover.H_REPAIR:
recive_mgroup = attrs.get("recive_mgroup", None)
if recive_mgroup is None:
raise ParseError('返工交接需指定工段')
attrs['material_changed'] = find_material_can_change(attrs['material'], recive_mgroup)
return attrs return attrs
class Meta: class Meta:
@ -497,22 +666,36 @@ class HandoverSerializer(CustomModelSerializer):
read_only_fields = EXCLUDE_FIELDS + ["mlog"] read_only_fields = EXCLUDE_FIELDS + ["mlog"]
extra_kwargs = { extra_kwargs = {
"type": {"required": False}, "type": {"required": False},
"wm": {"required": True}, "wm": {"required": False},
"send_dept": {"required": False}, "send_dept": {"required": False},
"recive_mgroup": {"required": False}, "recive_mgroup": {"required": False},
"recive_dept": {"required": False}, "recive_dept": {"required": False},
"material": {"required": False}, "material": {"required": False},
"batch": {"required": False}, "batch": {"required": False},
"count": {"required": False},
"count_eweight": {"required": False}
} }
def create(self, validated_data): def create(self, validated_data):
type = validated_data['type'] handoverb = validated_data.pop('handoverb', [])
if type == Handover.H_REPAIR: with transaction.atomic():
recive_mgroup = validated_data.get("recive_mgroup", None) ins = super().create(validated_data)
if recive_mgroup is None: for item in handoverb:
raise ParseError('返工交接需指定工段') Handoverb.objects.get_or_create(handover=ins, wm=item["wm"], count=item["count"])
validated_data['material_changed'] = find_material_can_change(validated_data['material'], recive_mgroup) return ins
return super().create(validated_data)
def update(self, instance, validated_data):
handoverb = validated_data.pop('handoverb', [])
with transaction.atomic():
super().update(instance, validated_data)
wmIds = []
for item in handoverb:
wmIds.append(item["wm"].id)
hb, _ = Handoverb.objects.get_or_create(handover=instance, wm=item["wm"])
hb.count = item["count"]
hb.save()
Handoverb.objects.filter(handover=instance).exclude(wm__in=wmIds).delete()
return instance
class HandoverUpdateSerializer(CustomModelSerializer): class HandoverUpdateSerializer(CustomModelSerializer):
class Meta: class Meta:
@ -520,6 +703,7 @@ class HandoverUpdateSerializer(CustomModelSerializer):
fields = ['id', 'send_date', 'send_user', 'count', 'count_eweight', 'recive_user'] fields = ['id', 'send_date', 'send_user', 'count', 'count_eweight', 'recive_user']
class GenHandoverSerializer(serializers.Serializer): class GenHandoverSerializer(serializers.Serializer):
mlogs = serializers.PrimaryKeyRelatedField( mlogs = serializers.PrimaryKeyRelatedField(
label='mlog的ID列表', queryset=Mlog.objects.all(), many=True) label='mlog的ID列表', queryset=Mlog.objects.all(), many=True)
@ -570,3 +754,34 @@ class AttLogSerializer(CustomModelSerializer):
class Meta: class Meta:
model = AttLog model = AttLog
fields = '__all__' fields = '__all__'
class FmlogSerializer(CustomModelSerializer):
routepack_name = serializers.CharField(
source='route.routepack.name', read_only=True)
mtask_number = serializers.CharField(source='mtask.number', read_only=True)
class Meta:
model = Fmlog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
def validate(self, attrs):
route: Route = attrs['route']
mtask: Mtask = attrs['mtask']
if mtask.state != Mtask.MTASK_ASSGINED:
raise ParseError('该任务非下达中不可选择')
mgroup: Mgroup = attrs['mgroup']
if route.process != mgroup.process:
raise ParseError('工序不匹配')
if mtask.mgroup != mgroup:
raise ParseError('工段不匹配')
return attrs
class FmlogUpdateSerializer(CustomModelSerializer):
class Meta:
model = Fmlog
fields = ['id', 'note']
class MlogTCreateSerializer(CustomModelSerializer):
pass

View File

@ -4,7 +4,6 @@ from django.core.cache import cache
from django.db.models import Sum from django.db.models import Sum
from django.utils import timezone from django.utils import timezone
from typing import Union from typing import Union
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
@ -13,9 +12,25 @@ from apps.inm.models import MIO, MIOItem, MIOItemA
from apps.pm.models import Mtask from apps.pm.models import Mtask
from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack
from .models import SfLog, SfLogExp, WMaterial, Mlog, Mlogb, Handover from .models import SfLog, WMaterial, Mlog, Mlogb, Handover, Handoverb
from apps.mtm.models import Process
from apps.mtm.services import cal_material_count from apps.mtm.services import cal_material_count
from apps.wf.models import Ticket
from django.db import transaction
from apps.utils.thread import MyThread
def generate_new_batch(old_batch: str, mlog: Mlog):
new_batch = old_batch
supplier = mlog.supplier
process = mlog.mgroup.process
if mlog.mtype == Mlog.MTYPE_OUT:
supplier_number = supplier.number if supplier else ''
if supplier_number:
new_batch = f'{new_batch}-{supplier_number}'
elif process.batch_append_equip:
number = mlog.equipment.number if mlog.equipment else ''
if number:
new_batch = f'{new_batch}-{number}'
return new_batch
def find_material_can_change(material: Material, mgroup_to: Mgroup): def find_material_can_change(material: Material, mgroup_to: Mgroup):
""" """
@ -204,10 +219,16 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
belong_dept = mgroup.belong_dept belong_dept = mgroup.belong_dept
material_out = mlog.material_out material_out = mlog.material_out
material_in = mlog.material_in material_in = mlog.material_in
supplier = mlog.supplier # 外协
if material_in: # 需要进行车间库存管理 if material_in: # 需要进行车间库存管理
m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False) m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False)
if m_ins.exists(): if m_ins.exists():
m_ins_list = [(mi.material_in, mi.batch, mi.count_use, mi.wm_in) for mi in m_ins.all()] m_ins_list = []
m_ins_bl_list = []
for mi in m_ins.all():
m_ins_list.append((mi.material_in, mi.batch, mi.count_use, mi.wm_in))
if mi.count_pn_jgqbl > 0:
m_ins_bl_list.append((mi.material_in, mi.batch, mi.count_pn_jgqbl))
else: else:
m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in)] m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in)]
for mi in m_ins_list: for mi in m_ins_list:
@ -238,6 +259,20 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
wm.count = wm.count - mi_count wm.count = wm.count - mi_count
wm.update_by = user wm.update_by = user
wm.save() wm.save()
# 针对加工前不良的暂时额外处理
for item in m_ins_bl_list:
material, batch, count_pn_jgqbl = item
if count_pn_jgqbl> 0:
lookup = {'batch': batch, 'material': material, 'mgroup': mgroup, 'notok_sign': 'jgqbl', 'state': WMaterial.WM_NOTOK}
wm, is_create = WMaterial.objects.get_or_create(**lookup, defaults={**lookup, "belong_dept": belong_dept})
wm.count = wm.count + count_pn_jgqbl
if is_create:
wm.create_by = user
else:
wm.update_by = user
wm.save()
if material_out: # 需要入车间库存 if material_out: # 需要入车间库存
into_wm_mgroup = material_out.process.into_wm_mgroup if material_out.process else False into_wm_mgroup = material_out.process.into_wm_mgroup if material_out.process else False
need_store_notok = material_out.process.store_notok if material_out.process else False need_store_notok = material_out.process.store_notok if material_out.process else False
@ -271,6 +306,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
wm.count = wm.count + mo_count wm.count = wm.count + mo_count
wm.count_eweight = mo_count_eweight wm.count_eweight = mo_count_eweight
wm.update_by = user wm.update_by = user
if supplier is not None:
wm.supplier = supplier
wm.save() wm.save()
mlog.submit_time = now mlog.submit_time = now
@ -299,7 +336,12 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
into_wm_mgroup = material_in.process.into_wm_mgroup if material_in.process else False into_wm_mgroup = material_in.process.into_wm_mgroup if material_in.process else False
m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False) m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False)
if m_ins.exists(): if m_ins.exists():
m_ins_list = [(mi.material_in, mi.batch, mi.count_use, mi.wm_in) for mi in m_ins.all()] m_ins_list = []
m_ins_bl_list = []
for mi in m_ins.all():
m_ins_list.append((mi.material_in, mi.batch, mi.count_use, mi.wm_in))
if mi.count_pn_jgqbl > 0:
m_ins_bl_list.append((mi.material_in, mi.batch, mi.count_pn_jgqbl))
else: else:
m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in)] m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in)]
for mi in m_ins_list: for mi in m_ins_list:
@ -321,6 +363,21 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
wm.count = wm.count + mi_count wm.count = wm.count + mi_count
wm.update_by = user wm.update_by = user
wm.save() wm.save()
# 针对加工前不良的暂时额外处理
for item in m_ins_bl_list:
material, batch, count_pn_jgqbl = item
if count_pn_jgqbl> 0:
lookup = {'batch': batch, 'material': material, 'mgroup': mgroup, 'notok_sign': 'jgqbl', 'state': WMaterial.WM_NOTOK}
wm, is_create = WMaterial.objects.get_or_create(**lookup, defaults={**lookup, "belong_dept": belong_dept})
wm.count = wm.count - count_pn_jgqbl
if wm.count < 0:
raise ParseError('加工前不良数量大于库存量')
if is_create:
wm.create_by = user
else:
wm.update_by = user
wm.save()
if material_out: # 产物退回 if material_out: # 产物退回
# 有多个产物的情况 # 有多个产物的情况
# 需要考虑不合格品退回的情况 # 需要考虑不合格品退回的情况
@ -339,6 +396,8 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
for mo in m_outs_list: for mo in m_outs_list:
mo_ma, mo_batch, mo_count, _, notok_sign = mo mo_ma, mo_batch, mo_count, _, notok_sign = mo
if mo_count == 0:
continue
wm_state = WMaterial.WM_OK if notok_sign is None else WMaterial.WM_NOTOK wm_state = WMaterial.WM_OK if notok_sign is None else WMaterial.WM_NOTOK
lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': notok_sign, 'state': wm_state} lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': notok_sign, 'state': wm_state}
if stored_mgroup: if stored_mgroup:
@ -379,7 +438,7 @@ def cal_mlog_count_from_mlogb(mlog: Mlog):
""" """
通过mlogb计算mlog count 合计 通过mlogb计算mlog count 合计
""" """
if mlog.fill_way == Mlog.MLOG_STEP: if mlog.fill_way == Mlog.MLOG_23:
a_dict = { a_dict = {
"total_count_use": Sum('count_use'), "total_count_use": Sum('count_use'),
"total_count_break": Sum('count_break'), "total_count_break": Sum('count_break'),
@ -388,7 +447,7 @@ def cal_mlog_count_from_mlogb(mlog: Mlog):
"total_count_ok": Sum('count_ok'), "total_count_ok": Sum('count_ok'),
"total_count_notok": Sum('count_notok'), "total_count_notok": Sum('count_notok'),
} }
f_names = [f.name for f in Mlogb._meta.fields if 'count_n' in f.name] f_names = [f.name for f in Mlogb._meta.fields if 'count_n_' in f.name]
for f in f_names: for f in f_names:
a_dict[f'total_{f}'] = Sum(f) a_dict[f'total_{f}'] = Sum(f)
mlogb_summary = Mlogb.objects.filter(mlog=mlog).aggregate( mlogb_summary = Mlogb.objects.filter(mlog=mlog).aggregate(
@ -410,9 +469,9 @@ def cal_mtask_progress_from_mlog(mlog: Mlog):
""" """
更新mlog关联的任务进度(可线程中执行) 更新mlog关联的任务进度(可线程中执行)
""" """
if mlog.fill_way == Mlog.MLOG_ONETIME and mlog.mtask: if mlog.fill_way in [Mlog.MLOG_2, Mlog.MLOG_12] and mlog.mtask:
update_mtask(mlog.mtask, fill_way=Mlog.MLOG_ONETIME) update_mtask(mlog.mtask, fill_way=mlog.fill_way)
elif mlog.fill_way == Mlog.MLOG_STEP: elif mlog.fill_way == Mlog.MLOG_23:
cal_mlog_count_from_mlogb(mlog) cal_mlog_count_from_mlogb(mlog)
m_outs_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) m_outs_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False)
caled_mtask = [] caled_mtask = []
@ -420,7 +479,7 @@ def cal_mtask_progress_from_mlog(mlog: Mlog):
mtask = item.mtask mtask = item.mtask
if mtask in caled_mtask: if mtask in caled_mtask:
continue continue
update_mtask(mtask, fill_way=Mlog.MLOG_STEP) update_mtask(mtask, fill_way=mlog.fill_way)
caled_mtask.append(mtask) caled_mtask.append(mtask)
def cal_material_count_from_mlog(mlog: Mlog): def cal_material_count_from_mlog(mlog: Mlog):
@ -443,7 +502,7 @@ def cal_material_count_from_mlog(mlog: Mlog):
def update_mtask(mtask: Mtask, fill_way: int = 10): def update_mtask(mtask: Mtask, fill_way: int = 10):
from apps.pm.models import Utask from apps.pm.models import Utask
if fill_way == Mlog.MLOG_ONETIME: if fill_way == Mlog.MLOG_2:
res = Mlog.objects.filter(mtask=mtask).exclude(submit_time=None).aggregate(sum_count_real=Sum( res = Mlog.objects.filter(mtask=mtask).exclude(submit_time=None).aggregate(sum_count_real=Sum(
'count_real'), sum_count_ok=Sum('count_ok'), sum_count_notok=Sum('count_notok')) 'count_real'), sum_count_ok=Sum('count_ok'), sum_count_notok=Sum('count_notok'))
mtask.count_real = res['sum_count_real'] if res['sum_count_real'] else 0 mtask.count_real = res['sum_count_real'] if res['sum_count_real'] else 0
@ -462,7 +521,7 @@ def update_mtask(mtask: Mtask, fill_way: int = 10):
if Mtask.objects.filter(utask=utask).exclude(state=Mtask.MTASK_SUBMIT).count() == 0: if Mtask.objects.filter(utask=utask).exclude(state=Mtask.MTASK_SUBMIT).count() == 0:
utask.state = Utask.UTASK_SUBMIT utask.state = Utask.UTASK_SUBMIT
utask.save() utask.save()
elif fill_way == Mlog.MLOG_STEP: elif fill_way in [Mlog.MLOG_23, Mlog.MLOG_12]:
# 已经提交的日志 # 已经提交的日志
m_outs_qs_mtask = Mlogb.objects.filter(mtask=mtask, material_out__isnull=False, mlog__submit_time__isnull=False) m_outs_qs_mtask = Mlogb.objects.filter(mtask=mtask, material_out__isnull=False, mlog__submit_time__isnull=False)
res = m_outs_qs_mtask.aggregate( res = m_outs_qs_mtask.aggregate(
@ -502,90 +561,117 @@ def handover_submit(handover: Handover, user: User, now: Union[datetime.datetime
if handover.submit_time is not None: if handover.submit_time is not None:
return return
now = timezone.now() now = timezone.now()
handoverb_qs = Handoverb.objects.filter(handover=handover)
need_add = True need_add = True
material = handover.material material = handover.material
batch = handover.batch
wm_from = handover.wm
if wm_from is None:
raise ParseError('找不到车间库存')
if '混料' in material.name: # hard code if '混料' in material.name: # hard code
need_add = False need_add = False
count_x = wm_from.count - handover.count if handoverb_qs.exists():
if count_x < 0: handoverb_list = [(item.wm, item.count) for item in handoverb_qs]
raise ParseError('车间库存不足!')
else: else:
wm_from.count = count_x handoverb_list = [(handover.wm, handover.count)]
wm_from.save()
if need_add: recive_mgroup = handover.recive_mgroup
if handover.type == Handover.H_NORMAL: recive_dept = handover.recive_dept
if handover.recive_mgroup: for item in handoverb_list:
wm_from, xcount = item
batch = wm_from.batch
if wm_from is None:
raise ParseError('找不到车间库存')
count_x = wm_from.count - xcount
if count_x < 0:
raise ParseError('车间库存不足!')
else:
wm_from.count = count_x
wm_from.save()
if need_add:
# 开始变动
if handover.type == Handover.H_NORMAL:
wm_to, _ = WMaterial.objects.get_or_create( wm_to, _ = WMaterial.objects.get_or_create(
batch=batch, batch=batch,
material=material, material=material,
mgroup=handover.recive_mgroup, mgroup=recive_mgroup,
state=WMaterial.WM_OK, belong_dept=recive_dept,
defaults={"batch": batch, "material": material, "mgroup": handover.recive_mgroup, "belong_dept": handover.recive_dept}, state=WMaterial.WM_OK
) )
else: elif handover.type == Handover.H_REPAIR:
wm_to, _ = WMaterial.objects.get_or_create( if handover.recive_mgroup:
batch=batch, wm_to, _ = WMaterial.objects.get_or_create(
material=material, batch=batch,
belong_dept=handover.recive_dept, material=handover.material_changed,
mgroup=None, mgroup=recive_mgroup,
state=WMaterial.WM_OK, belong_dept=recive_dept,
defaults={"batch": batch, "material": material, "belong_dept": handover.recive_dept} notok_sign=wm_from.notok_sign,
) material_origin=material,
elif handover.type == Handover.H_REPAIR: state=WMaterial.WM_REPAIR
if handover.recive_mgroup: )
else:
raise ParseError("返工交接必须指定接收工段")
elif handover.type == Handover.H_TEST:
wm_to, _ = WMaterial.objects.get_or_create( wm_to, _ = WMaterial.objects.get_or_create(
batch=batch, batch=batch,
material=handover.material_changed, material=material,
mgroup=handover.recive_mgroup, mgroup=recive_mgroup,
notok_sign=handover.wm.notok_sign, state=WMaterial.WM_TEST,
material_origin=handover.material, belong_dept=recive_dept,
state=WMaterial.WM_REPAIR,
defaults={ defaults={
"batch": batch, "count_xtest": 0,
"material": handover.material_changed,
"mgroup": handover.recive_mgroup,
"notok_sign": handover.wm.notok_sign,
"material_origin": handover.material,
"belong_dept": handover.recive_dept,
"state": WMaterial.WM_REPAIR
}, },
) )
elif handover.type == Handover.H_SCRAP:
if recive_mgroup:
wm_to, _ = WMaterial.objects.get_or_create(
batch=batch,
material=material,
mgroup=recive_mgroup,
belong_dept=recive_dept,
notok_sign=wm_from.notok_sign,
state=WMaterial.WM_SCRAP
)
else:
raise ParseError("不支持非工段报废")
else: else:
raise ParseError("返工交接必须指定接收工段") raise ParseError("不支持该交接类型")
elif handover.type == Handover.H_TEST:
if handover.recive_mgroup:
wm_to, _ = WMaterial.objects.get_or_create(
batch=batch,
material=material,
mgroup=handover.recive_mgroup,
state=WMaterial.WM_TEST,
defaults={
"batch": batch,
"material": material,
"mgroup": handover.recive_mgroup,
"belong_dept": handover.recive_dept,
"count_xtest": 0,
"state": WMaterial.WM_TEST},
)
else:
wm_to, _ = WMaterial.objects.get_or_create(
batch=batch,
material=material,
belong_dept=handover.recive_dept,
mgroup=None,
state=WMaterial.WM_TEST,
defaults={"batch": batch, "material": material, "belong_dept": handover.recive_dept, "count_xtest": 0, "state": WMaterial.WM_TEST},
)
else:
raise ParseError("不支持交接类型")
wm_to.count = wm_to.count + handover.count wm_to.count = wm_to.count + xcount
wm_to.count_eweight = handover.count_eweight # 这行代码有隐患 wm_to.count_eweight = handover.count_eweight # 这行代码有隐患
wm_to.save() wm_to.save()
handover.submit_user = user handover.submit_user = user
handover.submit_time = now handover.submit_time = now
handover.save() handover.save()
def mlog_submit_validate(ins: Mlog):
if ins.submit_time:
raise ParseError('该日志已提交!')
if ins.mtask and ins.mtask.state == Mtask.MTASK_STOP:
raise ParseError('该任务已停止!')
if ins.fill_way == Mlog.MLOG_23:
if not Mlogb.objects.filter(material_out__isnull=False, mlog=ins).exists():
raise ParseError('该日志未指定产出!')
if not Mlogb.objects.filter(material_in__isnull=False, mlog=ins).exists():
raise ParseError('该日志未指定消耗!')
if Mlogb.objects.filter(material_out__isnull=False, count_real=0, mlog=ins).exists():
raise ParseError('产出数量不能为0!')
def bind_mlog(ticket: Ticket, transition, new_ticket_data: dict):
ins = Mlog.objects.get(id=new_ticket_data['t_id'])
mlog_submit_validate(ins) # 校验是否可submit
ticket_data = ticket.ticket_data
ticket_data.update({
't_model': 'mlog',
't_id': ins.id,
})
ticket.ticket_data = ticket_data
ticket.create_by = ins.create_by
ticket.save()
if ins.ticket is None:
ins.ticket = ticket
ins.save()
def mlog_audit_end(ticket: Ticket):
now = timezone.now()
ins = Mlog.objects.get(id=ticket.ticket_data['t_id'])
mlog_submit(ins, ticket.create_by, now)
MyThread(target=cal_mtask_progress_from_mlog,args=(ins,)).start()
MyThread(target=cal_material_count_from_mlog,args=(ins,)).start()

View File

@ -3,7 +3,9 @@ from rest_framework.routers import DefaultRouter
from apps.wpm.views import (SfLogViewSet, StLogViewSet, SfLogExpViewSet, from apps.wpm.views import (SfLogViewSet, StLogViewSet, SfLogExpViewSet,
WMaterialViewSet, MlogViewSet, HandoverViewSet, WMaterialViewSet, MlogViewSet, HandoverViewSet,
AttlogViewSet, OtherLogViewSet, MlogbViewSet, MlogbInViewSet, MlogbOutViewSet) AttlogViewSet, OtherLogViewSet, MlogbViewSet, MlogbInViewSet,
MlogbOutViewSet, FmlogViewSet)
from apps.wpm.datax import AnaViewSet
API_BASE_URL = 'api/wpm/' API_BASE_URL = 'api/wpm/'
@ -14,6 +16,7 @@ router.register('sflog', SfLogViewSet, basename='sflog')
router.register('stlog', StLogViewSet, basename='stlog') router.register('stlog', StLogViewSet, basename='stlog')
router.register('sflogexp', SfLogExpViewSet, basename='sflogexp') router.register('sflogexp', SfLogExpViewSet, basename='sflogexp')
router.register('wmaterial', WMaterialViewSet, basename='wmaterial') router.register('wmaterial', WMaterialViewSet, basename='wmaterial')
router.register('fmlog', FmlogViewSet, basename='fmlog')
router.register('mlog', MlogViewSet, basename='mlog') router.register('mlog', MlogViewSet, basename='mlog')
router.register('mlogb', MlogbViewSet) router.register('mlogb', MlogbViewSet)
router.register('mlogb/in', MlogbInViewSet) router.register('mlogb/in', MlogbInViewSet)
@ -21,6 +24,8 @@ router.register('mlogb/out', MlogbOutViewSet)
router.register('handover', HandoverViewSet, basename='handover') router.register('handover', HandoverViewSet, basename='handover')
router.register('attlog', AttlogViewSet, basename='attlog') router.register('attlog', AttlogViewSet, basename='attlog')
router.register('otherlog', OtherLogViewSet, basename='otherlog') router.register('otherlog', OtherLogViewSet, basename='otherlog')
router.register('ana', AnaViewSet, basename='ana')
urlpatterns = [ urlpatterns = [
path(API_BASE_URL, include(router.urls)), path(API_BASE_URL, include(router.urls)),
] ]

View File

@ -16,15 +16,17 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin from apps.utils.mixins import BulkCreateModelMixin
from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter, MlogbFilter from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter, MlogbFilter
from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, AttLog, OtherLog from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, AttLog, OtherLog, Fmlog
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogRevertSerializer, from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogRevertSerializer,
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, HandoverUpdateSerializer, MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, HandoverUpdateSerializer,
GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer, GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer,
AttLogSerializer, OtherLogSerializer, MlogInitSerializer, MlogChangeSerializer, AttLogSerializer, OtherLogSerializer, MlogInitSerializer, MlogChangeSerializer,
MlogbDetailSerializer, MlogbInSerializer, MlogbInUpdateSerializer, MlogbOutUpdateSerializer) MlogbDetailSerializer, MlogbInSerializer, MlogbInUpdateSerializer,
MlogbOutUpdateSerializer, FmlogSerializer, FmlogUpdateSerializer)
from .services import mlog_submit, update_mtask, handover_submit, mlog_revert, cal_material_count_from_mlog, cal_mtask_progress_from_mlog from .services import mlog_submit, update_mtask, handover_submit, mlog_revert, cal_material_count_from_mlog, cal_mtask_progress_from_mlog
from apps.utils.thread import MyThread from apps.utils.thread import MyThread
from apps.monitor.services import create_auditlog, delete_auditlog from apps.monitor.services import create_auditlog, delete_auditlog
from apps.wpm.services import mlog_submit_validate, generate_new_batch
# Create your views here. # Create your views here.
@ -112,11 +114,17 @@ class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
perms_map = {'get': '*'} perms_map = {'get': '*'}
queryset = WMaterial.objects.filter(count__gt=0) queryset = WMaterial.objects.filter(count__gt=0)
serializer_class = WMaterialSerializer serializer_class = WMaterialSerializer
select_related_fields = ['material', 'belong_dept', 'material__process'] select_related_fields = ['material', 'belong_dept', 'material__process', 'supplier']
search_fields = ['material__name', search_fields = ['material__name',
'material__number', 'material__specification', 'batch', 'material__model'] 'material__number', 'material__specification', 'batch', 'material__model']
filterset_class = WMaterialFilter filterset_class = WMaterialFilter
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
if self.request.query_params.get('state_all'):
return queryset
return queryset.exclude(state=WMaterial.WM_SCRAP)
@action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=DeptBatchSerializer) @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=DeptBatchSerializer)
@transaction.atomic @transaction.atomic
def batchs(self, request): def batchs(self, request):
@ -141,7 +149,9 @@ class MlogViewSet(CustomModelViewSet):
queryset = Mlog.objects.all() queryset = Mlog.objects.all()
serializer_class = MlogSerializer serializer_class = MlogSerializer
select_related_fields = ['create_by', 'update_by', 'mtask', select_related_fields = ['create_by', 'update_by', 'mtask',
'handle_user', 'handle_user_2', 'equipment', 'equipment_2', 'material_in', 'material_out', 'supplier'] 'handle_user', 'handle_user_2', 'equipment',
'equipment_2', 'material_in', 'material_out', 'route__routepack',
'supplier', 'ticket', 'mgroup__process', 'test_user']
prefetch_related_fields = ['handle_users', prefetch_related_fields = ['handle_users',
'material_outs', 'b_mlog', 'equipments'] 'material_outs', 'b_mlog', 'equipments']
filterset_class = MlogFilter filterset_class = MlogFilter
@ -204,15 +214,13 @@ class MlogViewSet(CustomModelViewSet):
ins: Mlog = self.get_object() ins: Mlog = self.get_object()
vdata_old = MlogSerializer(ins).data vdata_old = MlogSerializer(ins).data
now = timezone.now() now = timezone.now()
if ins.submit_time: if ins.ticket:
raise ParseError('该日志已提交!') raise ParseError('该日志存在审批!')
if ins.mtask and ins.mtask.state == Mtask.MTASK_STOP: else:
raise ParseError('该任务已停止!') p: Process = ins.mgroup.process
if ins.fill_way == Mlog.MLOG_STEP: if p.mlog_need_ticket:
if not Mlogb.objects.filter(material_out__isnull=False).exists(): raise ParseError('该日志需要审批!')
raise ParseError('该日志未指定产出!') mlog_submit_validate(ins)
if not Mlogb.objects.filter(material_in__isnull=False).exists():
raise ParseError('该日志未指定消耗!')
with transaction.atomic(): with transaction.atomic():
mlog_submit(ins, self.request.user, now) mlog_submit(ins, self.request.user, now)
vdata_new = MlogSerializer(ins).data vdata_new = MlogSerializer(ins).data
@ -339,10 +347,11 @@ class HandoverViewSet(CustomModelViewSet):
""" """
ins: Handover = self.get_object() ins: Handover = self.get_object()
user: User = self.request.user user: User = self.request.user
if user == ins.recive_user or user.belong_dept == ins.recive_user.belong_dept: if ins.type != Handover.H_SCRAP:
pass if user == ins.recive_user or user.belong_dept == ins.recive_user.belong_dept:
else: pass
raise ParseError('非接收人不可提交') else:
raise ParseError('非接收人不可提交')
if ins.submit_time is None: if ins.submit_time is None:
handover_submit(ins, user, None) handover_submit(ins, user, None)
return Response() return Response()
@ -468,8 +477,6 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
def perform_create(self, serializer): def perform_create(self, serializer):
ins: Mlogb = serializer.save() ins: Mlogb = serializer.save()
mlog: Mlog = ins.mlog mlog: Mlog = ins.mlog
process: Process = mlog.mgroup.process
supplier = mlog.supplier
# 创建输出 # 创建输出
if ins.mtask and ins.material_in: if ins.mtask and ins.material_in:
material_out = mlog.material_out material_out = mlog.material_out
@ -480,20 +487,26 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
"mlog": ins.mlog, "mlog": ins.mlog,
"material_out": ins.mlog.material_out "material_out": ins.mlog.material_out
} }
new_batch = ins.batch m_dict['batch'] = generate_new_batch(ins.batch, mlog)
if mlog.mtype == Mlog.MTYPE_OUT:
supplier_number = supplier.number if supplier else ''
if supplier_number:
new_batch = f'{new_batch}-{supplier_number}'
elif process.batch_append_equip:
number = mlog.equipment.number if mlog.equipment else ''
if number:
new_batch = f'{new_batch}-{number}'
m_dict['batch'] = new_batch
Mlogb.objects.get_or_create(**m_dict, defaults=m_dict) Mlogb.objects.get_or_create(**m_dict, defaults=m_dict)
class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet): class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet):
perms_map = {"put": "mlog.update"} perms_map = {"put": "mlog.update"}
queryset = Mlogb.objects.filter(material_out__isnull=False) queryset = Mlogb.objects.filter(material_out__isnull=False)
serializer_class = MlogbOutUpdateSerializer serializer_class = MlogbOutUpdateSerializer
class FmlogViewSet(CustomModelViewSet):
perms_map = {'get': '*', 'post': 'mlog.create', 'put': 'mlog.update', 'delete': 'mlog.delete'}
queryset = Fmlog.objects.all()
serializer_class = FmlogSerializer
update_serializer_class = FmlogUpdateSerializer
filterset_fields = ['mtask', 'mgroup', 'route']
select_related_fields = ['mtask', 'mgroup', 'route', 'route__routepack']
def destroy(self, request, *args, **kwargs):
ins = self.get_object()
if Mlog.objects.filter(fmlog=ins).exists():
raise ParseError('因存在二级日志不可删除')
return super().destroy(request, *args, **kwargs)

View File

@ -341,7 +341,7 @@ LOGGING = {
##### 加载客户可自定义配置并提供操作方法 ##### ##### 加载客户可自定义配置并提供操作方法 #####
SYS_JSON_PATH = os.path.join(BASE_DIR, 'server/conf.json') SYS_JSON_PATH = os.path.join(BASE_DIR, 'server/conf.json')
def get_sysconfig(key='', reload=False): def get_sysconfig(key='', default='raise_error', reload=False):
"""获取系统配置可指定key字符串 """获取系统配置可指定key字符串
""" """
config = cache.get('system_config', None) config = cache.get('system_config', None)
@ -355,7 +355,13 @@ def get_sysconfig(key='', reload=False):
if key: if key:
k_l = key.split('.') k_l = key.split('.')
for k in k_l: for k in k_l:
config = config[k] try:
config = config[k]
except KeyError:
if default == 'raise_error':
raise
else:
return default
return config return config