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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ class Xscript(BaseModel):
name = models.CharField("脚本名称", max_length=50)
code = models.TextField("脚本内容", 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)
periodictask = models.ForeignKey(PeriodicTask, verbose_name='关联定时任务', on_delete=models.CASCADE, related_name='xscript_periodictask', null=True, blank=True)

View File

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

View File

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

View File

@ -27,6 +27,7 @@ from apps.wpm.tasks import get_total_sec_now, cal_exp_duration_sec
from apps.utils.sql import DbConnection
from apps.enm.services import db_insert_mplogx_batch
from apps.enm.xscript import main
from django.core.exceptions import ObjectDoesNotExist
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_hour_p= dt - datetime.timedelta(hours=1) # 上个整点
dt_hour_n= dt + datetime.timedelta(hours=1) # 下个整点
if mpoint.material and mpoint.val_type in ['float', 'int']: # 如果计量的是物料 # 累计量 有的会清零,需要额外处理(还未做)
if (mpoint.material or mpoint.type == Mpoint.MT_COMPUTE) and mpoint.val_type in ['float', 'int']: # 如果计量的是物料 # 累计量 有的会清零,需要额外处理(还未做)
params = {"mpoint": mpoint, "type": "hour"}
params["year"], params["month"], params["day"], params["hour"] = year, month, day, hour
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"]
if val is None:
val = 0
myLogger.info(str(val), "-------val")
else:
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")
@ -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)
# 再统计计算测点
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 = []
for item in mpoints_compute:
# 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":
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) # 加锁
material_bulk_clinker = Material.objects.get(code="bulk_clinker") # 散装熟料
material_bulk_cement = Material.objects.get(code="bulk_cement") # 散装水泥
material_bag_cement = Material.objects.get(code="bag_cement") # 袋装水泥
try:
material_bulk_clinker = Material.objects.get(code="bulk_clinker") # 散装熟料
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":
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":
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:
enstat2.bulk_cement_val = 0
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":
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:
enstat2.bag_cement_val = 0
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":
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:
enstat2.bulk_clinker_val = 0

View File

@ -92,6 +92,17 @@ class XscriptViewSet(CustomModelViewSet):
periodictask.save()
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
def perform_destroy(self, instance):
periodictask = instance.periodictask

View File

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

View File

@ -10,6 +10,7 @@ from apps.utils.serializers import CustomModelSerializer
from .models import MIO, MaterialBatch, MIOItem, WareHouse, MIOItemA, MaterialBatchA
from django.db import transaction
from server.settings import get_sysconfig
class WareHourseSerializer(CustomModelSerializer):
@ -104,7 +105,9 @@ class MIOItemCreateSerializer(CustomModelSerializer):
if mio.state != MIO.MIO_CREATE:
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])
if mis.exists() and (not mis.exclude(test_date=None).exists()):
raise ValidationError('该批次的物料未经检验')
@ -165,6 +168,7 @@ class MIODoSerializer(CustomModelSerializer):
model = MIO
fields = ['id', 'number', 'note', 'do_user',
'belong_dept', 'type', 'inout_date', 'mgroup']
extra_kwargs = {'inout_date': {'required': True}, 'do_user': {'required': True}}
def validate(self, attrs):
if 'mgroup' in attrs and attrs['mgroup']:
@ -190,6 +194,7 @@ class MIOSaleSerializer(CustomModelSerializer):
class Meta:
model = MIO
fields = ['id', 'number', 'note', 'order', 'inout_date', 'customer']
extra_kwargs = {'inout_date': {'required': True}}
def create(self, validated_data):
validated_data['type'] = MIO.MIO_TYPE_SALE_OUT
@ -216,6 +221,7 @@ class MIOPurSerializer(CustomModelSerializer):
class Meta:
model = MIO
fields = ['id', 'number', 'note', 'pu_order', 'inout_date', 'supplier']
extra_kwargs = {'inout_date': {'required': True}}
def create(self, validated_data):
validated_data['type'] = MIO.MIO_TYPE_PUR_IN
@ -241,6 +247,7 @@ class MIOOtherSerializer(CustomModelSerializer):
model = MIO
fields = ['id', 'number', 'note', 'supplier',
'customer', 'type', 'inout_date']
extra_kwargs = {'inout_date': {'required': True}}
def create(self, validated_data):
if validated_data['type'] not in [MIO.MIO_TYPE_OTHER_OUT, MIO.MIO_TYPE_OTHER_IN]:

View File

@ -5,7 +5,7 @@ from apps.mtm.models import Material, Shift, Mgroup, Process
@admin.register(Process)
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)

View File

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

View File

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

View File

@ -18,6 +18,8 @@ class Process(CommonBModel):
into_wm_mgroup = models.BooleanField('交接到工段', default=False)
store_notok = 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:
verbose_name = '工序'

View File

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

View File

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

View File

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

View File

@ -3,11 +3,12 @@ from rest_framework.exceptions import ValidationError, ParseError
import math
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.utils.serializers import CustomModelSerializer
from apps.system.models import Dept
from apps.wpm.models import Mlog
from apps.utils.constants import EXCLUDE_FIELDS_BASE
class UtaskSerializer(CustomModelSerializer):
@ -54,6 +55,25 @@ class MlogSimpleSerializer(CustomModelSerializer):
fields = ['id', 'shift_name', 'count_use',
'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):
material_out_ = MaterialSimpleSerializer(
@ -66,6 +86,7 @@ class MtaskSerializer(CustomModelSerializer):
source='submit_user.name', read_only=True)
mgroup_name = serializers.CharField(source='mgroup.name', read_only=True)
mlogs = MlogSimpleSerializer(label='日志信息', many=True, required=False)
mtaskb = MtaskbSerializer(label='子任务信息', many=True, required=False, read_only=True)
class Meta:
model = Mtask
@ -109,4 +130,4 @@ class MtaskDaySerializer(serializers.Serializer):
class MtaskAddInfoSerializer(CustomModelSerializer):
class Meta:
model = Mtask
fields = ['peifen_kg']
fields = ['peifen_kg']

View File

@ -311,6 +311,8 @@ class PmService:
now = timezone.now()
if mtask.state == Mtask.MTASK_ASSGINED:
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():
raise ParseError('存在未提交的日志')
mtask.state = Mtask.MTASK_SUBMIT

View File

@ -1,12 +1,13 @@
from django.urls import path, include
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/'
HTML_BASE_URL = 'pm/'
router = DefaultRouter()
router.register('mtask', MtaskViewSet, basename='mtask')
router.register('mtaskb', MtaskbViewSet, basename='mtaskb')
router.register('utask', UtaskViewSet, basename='utask')
urlpatterns = [
path(API_BASE_URL, include(router.urls)),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
from django.db import models
from apps.system.models import CommonAModel, CommonADModel, User
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 django.utils.translation import gettext_lazy as _
@ -56,6 +56,8 @@ class NotOkOption(models.TextChoices):
z = "z", _("")
zhg = "zhg", _("准合格")
yz = "yz", _("圆准")
jgqbl = "jgqbl", _("加工前不良")
qt = "qt", _("其它")
@ -109,6 +111,7 @@ class FtestWork(CommonBDModel):
"""
type = models.CharField('检验类型', max_length=20, choices=FTEST_TYPE_CHOICES, default='prod')
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)
mb = models.ForeignKey('inm.materialbatch', verbose_name='关联仓库', on_delete=models.SET_NULL, null=True, blank=True)
test_date = models.DateField('检验日期')
@ -117,8 +120,10 @@ class FtestWork(CommonBDModel):
batch = models.CharField('生产批次', max_length=50)
count = models.IntegerField('检验数量')
count_sampling = models.IntegerField('抽检数量', default=0)
count_sampling_ok = models.IntegerField('抽检合格数量', default=0)
count_ok = 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)
test_user = models.ForeignKey(
User, verbose_name='操作人', on_delete=models.CASCADE, related_name='ftestwork_test_user', null=True, blank=True)

View File

@ -66,7 +66,7 @@ class QuaStatUpdateSerializer(CustomModelSerializer):
class FtestWorkCreateUpdateSerializer(CustomModelSerializer):
class Meta:
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}}
def validate(self, attrs):
@ -99,6 +99,8 @@ class FtestWorkCreateUpdateSerializer(CustomModelSerializer):
attrs['batch'] = attrs['mb'].batch
else:
raise ValidationError('请选择车间/仓库库存')
if attrs['test_user']:
attrs['belong_dept'] = attrs['test_user'].belong_dept
return attrs
@ -107,6 +109,7 @@ class FtestWorkSerializer(CustomModelSerializer):
source='material', read_only=True)
material_cate = serializers.CharField(source='material.cate', read_only=True)
mb_ = MaterialBatchDetailSerializer(source='mb', read_only=True)
test_user_name = serializers.CharField(source='test_user.name', read_only=True)
class Meta:
model = FtestWork

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,13 +1,14 @@
from django.db import models
from apps.utils.models import CommonADModel, CommonBDModel, BaseModel
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 django.utils.timezone import localtime
from apps.em.models import Equipment
from apps.system.models import Dept
from datetime import timedelta
from apps.pum.models import Supplier
from django.db.models import Sum
# Create your models here.
@ -97,6 +98,7 @@ class WMaterial(CommonBDModel):
state = models.PositiveSmallIntegerField('状态', default=10, choices=((10, '合格'), (20, '不合格'), (30, '返修'), (40, '检验'), (50, '报废')))
material = models.ForeignKey(
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)
batch = models.CharField('批次号', max_length=50)
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')
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):
"""
生产日志
"""
# 变成父级的字段
MLOG_ONETIME = 10
MLOG_STEP = 20
MLOG_2 = 10
MLOG_23 = 20
MLOG_12 = 30
MTYPE_SELF = 10
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, '外协生产')))
supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, 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_break_t = models.PositiveIntegerField('检验碎料数', default=0)
count_pn_jgqbl = models.PositiveIntegerField('加工前不良', default=0)
count_n_zw = models.PositiveIntegerField('炸纹', default=0)
count_n_tw = 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_txd = models.PositiveIntegerField('同心度', default=0)
count_n_hd = models.PositiveIntegerField('厚度', default=0)
count_n_qt = models.PositiveIntegerField('其他', default=0)
handle_date = models.DateField('操作日期', null=True, blank=True)
@ -212,6 +225,14 @@ class Mlog(CommonADModel):
submit_user = models.ForeignKey(
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
def mlogb(self):
return Mlogb.objects.filter(mlog=self).exclude(material_out=None)
@ -229,6 +250,7 @@ class Mlog(CommonADModel):
class Mlogb(BaseModel):
mlog = models.ForeignKey(Mlog, verbose_name='关联日志',
on_delete=models.CASCADE, related_name='b_mlog')
note = models.TextField('备注', default='', blank=True)
batch = models.CharField('批次号', max_length=50, null=True, blank=True)
mtask = models.ForeignKey(Mtask, verbose_name='关联任务',
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_notok = models.PositiveIntegerField('不合格数', default=0)
count_pn_jgqbl = models.PositiveIntegerField('加工前不良', default=0)
# 添加不合格字段后需要更改cal_mlog_count_from_mlogb
count_n_hs = 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_hd = models.PositiveIntegerField('厚度', default=0)
count_n_qt = models.PositiveIntegerField('其他', default=0)
count_notok_json = models.JSONField('不合格情况', default=list, blank=True)
class Handover(CommonADModel):
"""
@ -268,7 +292,8 @@ class Handover(CommonADModel):
H_NORMAL = 10
H_REPAIR = 20
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_user = models.ForeignKey(
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)
send_dept = models.ForeignKey(
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, 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')
@ -298,6 +323,15 @@ class Handover(CommonADModel):
submit_user = models.ForeignKey(
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):
"""

View File

@ -4,12 +4,13 @@ from rest_framework import serializers
from rest_framework.exceptions import ValidationError, ParseError
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.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.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.serializers import MaterialSimpleSerializer
from django.db import transaction
@ -17,7 +18,7 @@ from django.utils import timezone
from django.core.cache import cache
from django.utils.timezone import localdate
from apps.qm.models import NotOkOption
from apps.wf.serializers import TicketSimpleSerializer
class OtherLogSerializer(CustomModelSerializer):
class Meta:
@ -172,6 +173,7 @@ class SflogExpSerializer(CustomModelSerializer):
class WMaterialSerializer(CustomModelSerializer):
material_ = MaterialSimpleSerializer(source='material', read_only=True)
supplier_name = serializers.CharField(source='supplier.name', read_only=True)
material_name = serializers.StringRelatedField(
source='material', 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)
material_origin_name = serializers.StringRelatedField(source='material_origin', read_only=True)
notok_sign_name = serializers.SerializerMethodField()
count_working = serializers.CharField(read_only=True, label='在制数量')
def get_notok_sign_name(self, obj):
return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None
@ -211,7 +214,9 @@ class MlogbDetailSerializer(CustomModelSerializer):
fields = '__all__'
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)
routepack_name = serializers.CharField(source='route.routepack.name', read_only=True)
belong_dept = serializers.CharField(
source='mgroup.belong_dept.id', read_only=True)
belong_dept_name = serializers.CharField(
@ -260,6 +265,8 @@ class MlogSerializer(CustomModelSerializer):
source='handle_users', many=True, read_only=True)
equipments_name = serializers.StringRelatedField(
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:
model = Mlog
@ -271,21 +278,23 @@ class MlogSerializer(CustomModelSerializer):
}
def create(self, validated_data):
mtask: Mtask = validated_data.get('mtask', None)
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('缺少工段或产物!')
material_out = validated_data['material_out']
with transaction.atomic():
mlogb = validated_data.pop('mlogb', [])
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
if brotherId_should_list:
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'])
else:
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
def update(self, instance, validated_data):
if instance.fill_way == Mlog.MLOG_23:
raise ParseError('不支持的填写类型')
validated_data.pop('mtask', None)
validated_data.pop('mgroup', None)
if instance.mtask:
@ -305,17 +334,70 @@ class MlogSerializer(CustomModelSerializer):
# validated_data.pop('handle_user', None)
with transaction.atomic():
mlogb = validated_data.pop('mlogb', [])
instance = super().update(instance, validated_data)
if mlogb:
instance: Mlog = super().update(instance, validated_data)
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)
for item in mlogb:
Mlogb.objects.filter(mlog=instance, material_out=item['material_out']).update(
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
def validate(self, attrs):
attrs['fill_way'] = Mlog.MLOG_ONETIME
attrs['fill_way'] = Mlog.MLOG_2
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)
count_notok = 0
for i in attrs:
@ -330,17 +412,45 @@ class MlogSerializer(CustomModelSerializer):
if mtask.start_date == mtask.end_date:
attrs['handle_date'] = mtask.start_date
else:
if attrs['handle_date'] >= mtask.start_date and attrs['handle_date'] <= mtask.end_date:
pass
else:
raise ValidationError('操作日期错误')
if attrs['work_end_time']:
attrs['handle_date'] = attrs['work_end_time'].date()
elif attrs['work_start_time']:
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
class MlogInitSerializer(CustomModelSerializer):
class Meta:
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 = {
'work_start_time': {'required': True},
'route':{'required': True},
@ -357,17 +467,19 @@ class MlogInitSerializer(CustomModelSerializer):
attrs['hour_work'] = route.hour_work
attrs['material_in'] = route.material_in
attrs['material_out'] = route.material_out
attrs['fill_way'] = Mlog.MLOG_STEP
attrs['fill_way'] = Mlog.MLOG_23
if mtype == Mlog.MTYPE_OUT:
supplier = attrs.get('supplier', None)
if not supplier:
raise ValidationError('外协必须选择外协单位')
if attrs.get('work_end_time', None):
attrs['handle_date'] = localdate(attrs['work_end_time'])
return attrs
class MlogChangeSerializer(CustomModelSerializer):
class Meta:
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):
if attrs.get('work_end_time', None):
@ -378,12 +490,14 @@ class MlogChangeSerializer(CustomModelSerializer):
class MlogbInSerializer(CustomModelSerializer):
class Meta:
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}}
def validate(self, attrs):
mlog: Mlog = attrs['mlog']
mtask: Mtask = attrs['mtask']
if mtask.state != Mtask.MTASK_ASSGINED:
raise ValidationError('该任务非下达中不可选择')
wm_in: WMaterial = attrs['wm_in']
if wm_in.state != WMaterial.WM_OK:
raise ValidationError('非合格品不可使用')
@ -416,18 +530,41 @@ class MlogbInSerializer(CustomModelSerializer):
class MlogbInUpdateSerializer(CustomModelSerializer):
class Meta:
model = Mlogb
fields = ['id', 'count_use', 'count_break']
fields = ['id', 'count_use', 'count_break', 'count_pn_jgqbl', 'note']
class MlogbOutUpdateSerializer(CustomModelSerializer):
class Meta:
model = Mlogb
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):
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
for i in attrs:
if 'count_n_' in i:
if not hasattr(Mlogb, i):
raise ValidationError(f'{i}不存在')
count_notok = count_notok + attrs[i]
attrs['count_notok'] = count_notok
if attrs['count_real'] >= attrs['count_ok'] + attrs['count_notok']:
@ -448,6 +585,14 @@ class MlogRelatedSerializer(serializers.Serializer):
class DeptBatchSerializer(serializers.Serializer):
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):
# wm = serializers.PrimaryKeyRelatedField(
@ -466,18 +611,28 @@ class HandoverSerializer(CustomModelSerializer):
material_name = serializers.StringRelatedField(
source='material', read_only=True)
wm_notok_sign = serializers.CharField(source='wm.notok_sign', read_only=True)
handoverb = HandoverbSerializer(many=True)
def validate(self, attrs):
if 'type' not in attrs:
attrs['type'] = Handover.H_NORMAL
wm:WMaterial = attrs['wm']
material = wm.material
wm:WMaterial = attrs.get('wm', None)
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['batch'] = wm.batch
attrs['send_dept'] = wm.belong_dept
if attrs['wm'].mgroup:
if 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('必须指定交接工段')
if 'recive_mgroup' in attrs and attrs['recive_mgroup']:
attrs['recive_dept'] = attrs['recive_mgroup'].belong_dept
@ -485,10 +640,24 @@ class HandoverSerializer(CustomModelSerializer):
raise ValidationError('收料车间和收料工段必须有一个')
if 'send_dept' not in attrs and 'send_mgroup' not in attrs:
raise ValidationError('送料车间和送料工段必须有一个')
if wm.notok_sign is not None and attrs['type'] in [Handover.H_NORMAL, Handover.H_TEST]:
raise ValidationError('物料不合格,不能进行正常/检验交接')
if wm.count_xtest is not None:
raise ValidationError('物料检验中,不能进行交接')
t_count = 0
for ind, item in enumerate(attrs['handoverb']):
wm = item["wm"]
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
class Meta:
@ -497,22 +666,36 @@ class HandoverSerializer(CustomModelSerializer):
read_only_fields = EXCLUDE_FIELDS + ["mlog"]
extra_kwargs = {
"type": {"required": False},
"wm": {"required": True},
"wm": {"required": False},
"send_dept": {"required": False},
"recive_mgroup": {"required": False},
"recive_dept": {"required": False},
"material": {"required": False},
"batch": {"required": False},
"count": {"required": False},
"count_eweight": {"required": False}
}
def create(self, validated_data):
type = validated_data['type']
if type == Handover.H_REPAIR:
recive_mgroup = validated_data.get("recive_mgroup", None)
if recive_mgroup is None:
raise ParseError('返工交接需指定工段')
validated_data['material_changed'] = find_material_can_change(validated_data['material'], recive_mgroup)
return super().create(validated_data)
handoverb = validated_data.pop('handoverb', [])
with transaction.atomic():
ins = super().create(validated_data)
for item in handoverb:
Handoverb.objects.get_or_create(handover=ins, wm=item["wm"], count=item["count"])
return ins
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 Meta:
@ -520,6 +703,7 @@ class HandoverUpdateSerializer(CustomModelSerializer):
fields = ['id', 'send_date', 'send_user', 'count', 'count_eweight', 'recive_user']
class GenHandoverSerializer(serializers.Serializer):
mlogs = serializers.PrimaryKeyRelatedField(
label='mlog的ID列表', queryset=Mlog.objects.all(), many=True)
@ -570,3 +754,34 @@ class AttLogSerializer(CustomModelSerializer):
class Meta:
model = AttLog
fields = '__all__'
class FmlogSerializer(CustomModelSerializer):
routepack_name = serializers.CharField(
source='route.routepack.name', read_only=True)
mtask_number = serializers.CharField(source='mtask.number', read_only=True)
class Meta:
model = Fmlog
fields = '__all__'
read_only_fields = EXCLUDE_FIELDS
def validate(self, attrs):
route: Route = attrs['route']
mtask: Mtask = attrs['mtask']
if mtask.state != Mtask.MTASK_ASSGINED:
raise ParseError('该任务非下达中不可选择')
mgroup: Mgroup = attrs['mgroup']
if route.process != mgroup.process:
raise ParseError('工序不匹配')
if mtask.mgroup != mgroup:
raise ParseError('工段不匹配')
return attrs
class FmlogUpdateSerializer(CustomModelSerializer):
class Meta:
model = Fmlog
fields = ['id', 'note']
class MlogTCreateSerializer(CustomModelSerializer):
pass

View File

@ -4,7 +4,6 @@ from django.core.cache import cache
from django.db.models import Sum
from django.utils import timezone
from typing import Union
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
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.mtm.models import Mgroup, Shift, Material, Route, RoutePack
from .models import SfLog, SfLogExp, WMaterial, Mlog, Mlogb, Handover
from apps.mtm.models import Process
from .models import SfLog, WMaterial, Mlog, Mlogb, Handover, Handoverb
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):
"""
@ -204,10 +219,16 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
belong_dept = mgroup.belong_dept
material_out = mlog.material_out
material_in = mlog.material_in
supplier = mlog.supplier # 外协
if material_in: # 需要进行车间库存管理
m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False)
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:
m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in)]
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.update_by = user
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: # 需要入车间库存
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
@ -271,6 +306,8 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
wm.count = wm.count + mo_count
wm.count_eweight = mo_count_eweight
wm.update_by = user
if supplier is not None:
wm.supplier = supplier
wm.save()
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
m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False)
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:
m_ins_list = [(material_in, mlog.batch, mlog.count_use, mlog.wm_in)]
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.update_by = user
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: # 产物退回
# 有多个产物的情况
# 需要考虑不合格品退回的情况
@ -339,6 +396,8 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
for mo in m_outs_list:
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
lookup = {'batch': mo_batch, 'material': mo_ma, 'mgroup': None, 'notok_sign': notok_sign, 'state': wm_state}
if stored_mgroup:
@ -379,7 +438,7 @@ def cal_mlog_count_from_mlogb(mlog: Mlog):
"""
通过mlogb计算mlog count 合计
"""
if mlog.fill_way == Mlog.MLOG_STEP:
if mlog.fill_way == Mlog.MLOG_23:
a_dict = {
"total_count_use": Sum('count_use'),
"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_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:
a_dict[f'total_{f}'] = Sum(f)
mlogb_summary = Mlogb.objects.filter(mlog=mlog).aggregate(
@ -410,9 +469,9 @@ def cal_mtask_progress_from_mlog(mlog: Mlog):
"""
更新mlog关联的任务进度(可线程中执行)
"""
if mlog.fill_way == Mlog.MLOG_ONETIME and mlog.mtask:
update_mtask(mlog.mtask, fill_way=Mlog.MLOG_ONETIME)
elif mlog.fill_way == Mlog.MLOG_STEP:
if mlog.fill_way in [Mlog.MLOG_2, Mlog.MLOG_12] and mlog.mtask:
update_mtask(mlog.mtask, fill_way=mlog.fill_way)
elif mlog.fill_way == Mlog.MLOG_23:
cal_mlog_count_from_mlogb(mlog)
m_outs_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False)
caled_mtask = []
@ -420,7 +479,7 @@ def cal_mtask_progress_from_mlog(mlog: Mlog):
mtask = item.mtask
if mtask in caled_mtask:
continue
update_mtask(mtask, fill_way=Mlog.MLOG_STEP)
update_mtask(mtask, fill_way=mlog.fill_way)
caled_mtask.append(mtask)
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):
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(
'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
@ -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:
utask.state = Utask.UTASK_SUBMIT
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)
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:
return
now = timezone.now()
handoverb_qs = Handoverb.objects.filter(handover=handover)
need_add = True
material = handover.material
batch = handover.batch
wm_from = handover.wm
if wm_from is None:
raise ParseError('找不到车间库存')
if '混料' in material.name: # hard code
need_add = False
count_x = wm_from.count - handover.count
if count_x < 0:
raise ParseError('车间库存不足!')
if handoverb_qs.exists():
handoverb_list = [(item.wm, item.count) for item in handoverb_qs]
else:
wm_from.count = count_x
wm_from.save()
if need_add:
if handover.type == Handover.H_NORMAL:
if handover.recive_mgroup:
handoverb_list = [(handover.wm, handover.count)]
recive_mgroup = handover.recive_mgroup
recive_dept = handover.recive_dept
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(
batch=batch,
material=material,
mgroup=handover.recive_mgroup,
state=WMaterial.WM_OK,
defaults={"batch": batch, "material": material, "mgroup": handover.recive_mgroup, "belong_dept": handover.recive_dept},
mgroup=recive_mgroup,
belong_dept=recive_dept,
state=WMaterial.WM_OK
)
else:
wm_to, _ = WMaterial.objects.get_or_create(
batch=batch,
material=material,
belong_dept=handover.recive_dept,
mgroup=None,
state=WMaterial.WM_OK,
defaults={"batch": batch, "material": material, "belong_dept": handover.recive_dept}
)
elif handover.type == Handover.H_REPAIR:
if handover.recive_mgroup:
elif handover.type == Handover.H_REPAIR:
if handover.recive_mgroup:
wm_to, _ = WMaterial.objects.get_or_create(
batch=batch,
material=handover.material_changed,
mgroup=recive_mgroup,
belong_dept=recive_dept,
notok_sign=wm_from.notok_sign,
material_origin=material,
state=WMaterial.WM_REPAIR
)
else:
raise ParseError("返工交接必须指定接收工段")
elif handover.type == Handover.H_TEST:
wm_to, _ = WMaterial.objects.get_or_create(
batch=batch,
material=handover.material_changed,
mgroup=handover.recive_mgroup,
notok_sign=handover.wm.notok_sign,
material_origin=handover.material,
state=WMaterial.WM_REPAIR,
material=material,
mgroup=recive_mgroup,
state=WMaterial.WM_TEST,
belong_dept=recive_dept,
defaults={
"batch": batch,
"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
"count_xtest": 0,
},
)
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:
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("不支持交接类型")
raise ParseError("不支持该交接类型")
wm_to.count = wm_to.count + handover.count
wm_to.count_eweight = handover.count_eweight # 这行代码有隐患
wm_to.save()
wm_to.count = wm_to.count + xcount
wm_to.count_eweight = handover.count_eweight # 这行代码有隐患
wm_to.save()
handover.submit_user = user
handover.submit_time = now
handover.save()
def mlog_submit_validate(ins: Mlog):
if ins.submit_time:
raise ParseError('该日志已提交!')
if ins.mtask and ins.mtask.state == Mtask.MTASK_STOP:
raise ParseError('该任务已停止!')
if ins.fill_way == Mlog.MLOG_23:
if not Mlogb.objects.filter(material_out__isnull=False, mlog=ins).exists():
raise ParseError('该日志未指定产出!')
if not Mlogb.objects.filter(material_in__isnull=False, mlog=ins).exists():
raise ParseError('该日志未指定消耗!')
if Mlogb.objects.filter(material_out__isnull=False, count_real=0, mlog=ins).exists():
raise ParseError('产出数量不能为0!')
def bind_mlog(ticket: Ticket, transition, new_ticket_data: dict):
ins = Mlog.objects.get(id=new_ticket_data['t_id'])
mlog_submit_validate(ins) # 校验是否可submit
ticket_data = ticket.ticket_data
ticket_data.update({
't_model': 'mlog',
't_id': ins.id,
})
ticket.ticket_data = ticket_data
ticket.create_by = ins.create_by
ticket.save()
if ins.ticket is None:
ins.ticket = ticket
ins.save()
def mlog_audit_end(ticket: Ticket):
now = timezone.now()
ins = Mlog.objects.get(id=ticket.ticket_data['t_id'])
mlog_submit(ins, ticket.create_by, now)
MyThread(target=cal_mtask_progress_from_mlog,args=(ins,)).start()
MyThread(target=cal_material_count_from_mlog,args=(ins,)).start()

View File

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

View File

@ -16,15 +16,17 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin
from .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,
MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer, HandoverUpdateSerializer,
GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer,
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 apps.utils.thread import MyThread
from apps.monitor.services import create_auditlog, delete_auditlog
from apps.wpm.services import mlog_submit_validate, generate_new_batch
# Create your views here.
@ -112,11 +114,17 @@ class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
perms_map = {'get': '*'}
queryset = WMaterial.objects.filter(count__gt=0)
serializer_class = WMaterialSerializer
select_related_fields = ['material', 'belong_dept', 'material__process']
select_related_fields = ['material', 'belong_dept', 'material__process', 'supplier']
search_fields = ['material__name',
'material__number', 'material__specification', 'batch', 'material__model']
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)
@transaction.atomic
def batchs(self, request):
@ -141,7 +149,9 @@ class MlogViewSet(CustomModelViewSet):
queryset = Mlog.objects.all()
serializer_class = MlogSerializer
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',
'material_outs', 'b_mlog', 'equipments']
filterset_class = MlogFilter
@ -204,15 +214,13 @@ class MlogViewSet(CustomModelViewSet):
ins: Mlog = self.get_object()
vdata_old = MlogSerializer(ins).data
now = timezone.now()
if ins.submit_time:
raise ParseError('该日志已提交!')
if ins.mtask and ins.mtask.state == Mtask.MTASK_STOP:
raise ParseError('该任务已停止!')
if ins.fill_way == Mlog.MLOG_STEP:
if not Mlogb.objects.filter(material_out__isnull=False).exists():
raise ParseError('该日志未指定产出!')
if not Mlogb.objects.filter(material_in__isnull=False).exists():
raise ParseError('该日志未指定消耗!')
if ins.ticket:
raise ParseError('该日志存在审批!')
else:
p: Process = ins.mgroup.process
if p.mlog_need_ticket:
raise ParseError('该日志需要审批!')
mlog_submit_validate(ins)
with transaction.atomic():
mlog_submit(ins, self.request.user, now)
vdata_new = MlogSerializer(ins).data
@ -339,10 +347,11 @@ class HandoverViewSet(CustomModelViewSet):
"""
ins: Handover = self.get_object()
user: User = self.request.user
if user == ins.recive_user or user.belong_dept == ins.recive_user.belong_dept:
pass
else:
raise ParseError('非接收人不可提交')
if ins.type != Handover.H_SCRAP:
if user == ins.recive_user or user.belong_dept == ins.recive_user.belong_dept:
pass
else:
raise ParseError('非接收人不可提交')
if ins.submit_time is None:
handover_submit(ins, user, None)
return Response()
@ -468,8 +477,6 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
def perform_create(self, serializer):
ins: Mlogb = serializer.save()
mlog: Mlog = ins.mlog
process: Process = mlog.mgroup.process
supplier = mlog.supplier
# 创建输出
if ins.mtask and ins.material_in:
material_out = mlog.material_out
@ -480,20 +487,26 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
"mlog": ins.mlog,
"material_out": ins.mlog.material_out
}
new_batch = ins.batch
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
m_dict['batch'] = generate_new_batch(ins.batch, mlog)
Mlogb.objects.get_or_create(**m_dict, defaults=m_dict)
class MlogbOutViewSet(UpdateModelMixin, CustomGenericViewSet):
perms_map = {"put": "mlog.update"}
queryset = Mlogb.objects.filter(material_out__isnull=False)
serializer_class = MlogbOutUpdateSerializer
serializer_class = MlogbOutUpdateSerializer
class FmlogViewSet(CustomModelViewSet):
perms_map = {'get': '*', 'post': 'mlog.create', 'put': 'mlog.update', 'delete': 'mlog.delete'}
queryset = Fmlog.objects.all()
serializer_class = FmlogSerializer
update_serializer_class = FmlogUpdateSerializer
filterset_fields = ['mtask', 'mgroup', 'route']
select_related_fields = ['mtask', 'mgroup', 'route', 'route__routepack']
def destroy(self, request, *args, **kwargs):
ins = self.get_object()
if Mlog.objects.filter(fmlog=ins).exists():
raise ParseError('因存在二级日志不可删除')
return super().destroy(request, *args, **kwargs)

View File

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