Merge branch 'master' of https://e.coding.net/ctcdevteam/ehs/ehs_server
This commit is contained in:
commit
7aae49b378
|
@ -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:
|
||||||
|
|
|
@ -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='变更数据'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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]:
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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='日志提交是否需要审批'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='中间状态'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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 = '工序'
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -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)
|
|
@ -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']
|
|
@ -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
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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']
|
|
@ -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"]
|
||||||
}
|
}
|
|
@ -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='抽检合格数量'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='是否更新车间库存'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='班次'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
|
@ -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='外协供应商'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='加工前不良'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='关联工单'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='备注'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='关联生产日志'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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='填写方式'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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
|
|
@ -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()
|
||||||
|
|
|
@ -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)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue