Merge branch 'master' of https://e.coding.net/ctcdevteam/ehs/ehs_server
This commit is contained in:
commit
a4ba33550e
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 3.2.12 on 2025-04-30 05:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('cm', '0003_alter_lablemat_state'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='LabelTemplate',
|
||||
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='删除标记')),
|
||||
('name', models.TextField(verbose_name='名称')),
|
||||
('commands', models.JSONField(blank=True, default=list, verbose_name='指令模板')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
|
@ -14,3 +14,8 @@ class LableMat(BaseModel):
|
|||
notok_sign = models.CharField('不合格标记', max_length=10, null=True, blank=True)
|
||||
defect = models.ForeignKey("qm.defect", verbose_name='缺陷', on_delete=models.SET_NULL, null=True, blank=True)
|
||||
material_origin = models.ForeignKey(Material, verbose_name='原始物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='lm_mo')
|
||||
|
||||
|
||||
class LabelTemplate(BaseModel):
|
||||
name = models.TextField("名称")
|
||||
commands = models.JSONField("指令模板", default=list, blank=True)
|
|
@ -1,7 +1,8 @@
|
|||
from rest_framework import serializers
|
||||
from .models import LableMat
|
||||
from .models import LableMat, LabelTemplate
|
||||
from apps.qm.models import NotOkOption
|
||||
from apps.wpm.models import WmStateOption
|
||||
from apps.utils.serializers import CustomModelSerializer
|
||||
|
||||
|
||||
class TidSerializer(serializers.Serializer):
|
||||
|
@ -24,3 +25,9 @@ class LabelMatSerializer(serializers.ModelSerializer):
|
|||
|
||||
def get_state_name(self, obj):
|
||||
return getattr(WmStateOption, str(obj.state), WmStateOption.OK).label if obj.state else None
|
||||
|
||||
|
||||
class LabelTemplateSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = LabelTemplate
|
||||
fields = '__all__'
|
|
@ -1,12 +1,13 @@
|
|||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from apps.cm.views import LableMatViewSet
|
||||
from apps.cm.views import LableMatViewSet, LabelTemplateViewSet
|
||||
|
||||
API_BASE_URL = 'api/cm/'
|
||||
HTML_BASE_URL = 'dhtml/cm/'
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register('labelmat', LableMatViewSet, basename='labelmat')
|
||||
router.register('labeltemplate', LabelTemplateViewSet, basename='labeltemplate')
|
||||
urlpatterns = [
|
||||
path(API_BASE_URL, include(router.urls)),
|
||||
]
|
|
@ -1,11 +1,11 @@
|
|||
from apps.cm.models import LableMat
|
||||
from apps.cm.models import LableMat, LabelTemplate
|
||||
from rest_framework.decorators import action
|
||||
from apps.cm.serializers import TidSerializer, LabelMatSerializer
|
||||
from apps.cm.serializers import TidSerializer, LabelMatSerializer, LabelTemplateSerializer
|
||||
from apps.inm.models import MaterialBatch, MIOItem
|
||||
from apps.wpm.models import WMaterial
|
||||
from rest_framework.exceptions import ParseError, NotFound
|
||||
from rest_framework.response import Response
|
||||
from apps.utils.viewsets import CustomGenericViewSet, RetrieveModelMixin, CustomListModelMixin
|
||||
from apps.utils.viewsets import CustomGenericViewSet, RetrieveModelMixin, CustomListModelMixin, CustomModelViewSet
|
||||
# Create your views here.
|
||||
|
||||
SPLIT_FIELD = "#"
|
||||
|
@ -68,3 +68,13 @@ class LableMatViewSet(CustomListModelMixin, RetrieveModelMixin, CustomGenericVie
|
|||
rdata = LabelMatSerializer(obj).data
|
||||
rdata["code_label"] = f"mat{SPLIT_FIELD}{obj.id}"
|
||||
return Response(rdata)
|
||||
|
||||
|
||||
class LabelTemplateViewSet(CustomModelViewSet):
|
||||
"""
|
||||
list: 标签模板
|
||||
|
||||
标签模板
|
||||
"""
|
||||
queryset = LabelTemplate.objects.all()
|
||||
serializer_class = LabelTemplateSerializer
|
|
@ -18,6 +18,7 @@ def do_out(item: MIOItem):
|
|||
"""
|
||||
if item.mb and item.mb.defect is not None:
|
||||
raise ParseError("生产领料不支持不合格品")
|
||||
|
||||
from apps.inm.models import MaterialBatch
|
||||
mio:MIO = item.mio
|
||||
belong_dept = mio.belong_dept
|
||||
|
@ -26,6 +27,16 @@ def do_out(item: MIOItem):
|
|||
material:Material = item.material
|
||||
if material.into_wm is False: # 用于混料的原料不与车间库存交互, 这个是配置项目
|
||||
return
|
||||
|
||||
# 获取defect
|
||||
defect:Defect = None
|
||||
if item.wm and item.mb:
|
||||
raise ParseError("车间和仓库库存不能同时存在")
|
||||
if item.wm:
|
||||
defect = item.wm.defect
|
||||
elif item.mb:
|
||||
defect = item.mb.defect
|
||||
|
||||
action_list = []
|
||||
mias = MIOItemA.objects.filter(mioitem=item)
|
||||
is_zhj = False # 是否组合件领料
|
||||
|
@ -35,16 +46,18 @@ def do_out(item: MIOItem):
|
|||
for i in range(len(mias_list)):
|
||||
material, batch, rate = mias_list[i]
|
||||
new_count = rate * item.count # 假设 item.count 存在
|
||||
action_list.append([material, batch, new_count])
|
||||
action_list.append([material, batch, new_count, None])
|
||||
else:
|
||||
action_list = [[item.material, item.batch, item.count]]
|
||||
action_list = [[item.material, item.batch, item.count, defect]]
|
||||
|
||||
if is_zhj:
|
||||
try:
|
||||
mb = MaterialBatch.objects.get(
|
||||
material=item.material,
|
||||
warehouse=item.warehouse,
|
||||
batch=item.batch
|
||||
batch=item.batch,
|
||||
state=WMaterial.WM_OK,
|
||||
defect=None
|
||||
)
|
||||
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
||||
raise ParseError(f"组合件批次错误!{e}")
|
||||
|
@ -58,10 +71,14 @@ def do_out(item: MIOItem):
|
|||
raise ParseError("组合件暂不支持追踪单件")
|
||||
|
||||
xbatches = []
|
||||
if is_zhj:
|
||||
xbatches = [item.batch]
|
||||
for al in action_list:
|
||||
xmaterial:Material = al[0]
|
||||
xbatch:str = al[1]
|
||||
xcount:str = al[2]
|
||||
defect:Defect = al[3]
|
||||
|
||||
xbatches.append(xbatch)
|
||||
if xcount <= 0:
|
||||
raise ParseError("存在非正数!")
|
||||
|
@ -72,8 +89,8 @@ def do_out(item: MIOItem):
|
|||
material=xmaterial,
|
||||
warehouse=item.warehouse,
|
||||
batch=xbatch,
|
||||
state=10,
|
||||
defect=None
|
||||
state=WMaterial.WM_OK,
|
||||
defect=defect
|
||||
)
|
||||
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
||||
raise ParseError(f"批次错误!{e}")
|
||||
|
@ -85,9 +102,10 @@ def do_out(item: MIOItem):
|
|||
|
||||
|
||||
# 领到车间库存(或工段)
|
||||
wm, new_create = WMaterial.objects.get_or_create(batch=xbatch, material=xmaterial,
|
||||
wm, new_create = WMaterial.objects.get_or_create(
|
||||
batch=xbatch, material=xmaterial,
|
||||
belong_dept=belong_dept, mgroup=mgroup,
|
||||
state=WMaterial.WM_OK)
|
||||
state=WMaterial.WM_OK, defect=defect)
|
||||
if new_create:
|
||||
wm.create_by = do_user
|
||||
wm.batch_ofrom = mb.batch if mb else None
|
||||
|
@ -116,8 +134,9 @@ def do_in(item: MIOItem):
|
|||
生产入库后更新车间物料
|
||||
"""
|
||||
mio = item.mio
|
||||
if item.wm and item.wm.defect is not None:
|
||||
raise ParseError("不合格物料无法入库")
|
||||
wmin:WMaterial = item.wm
|
||||
if wmin and wmin.state != WMaterial.WM_OK:
|
||||
raise ParseError("非合格物料无法入库")
|
||||
belong_dept = mio.belong_dept
|
||||
mgroup = mio.mgroup
|
||||
do_user = mio.do_user
|
||||
|
@ -127,30 +146,44 @@ def do_in(item: MIOItem):
|
|||
action_list = []
|
||||
mias = MIOItemA.objects.filter(mioitem=item)
|
||||
is_zhj = False # 是否组合件入仓库
|
||||
|
||||
# 获取defect
|
||||
defect:Defect = None
|
||||
if item.wm and item.mb:
|
||||
raise ParseError("车间和仓库库存不能同时存在")
|
||||
if item.wm:
|
||||
defect = item.wm.defect
|
||||
elif item.mb:
|
||||
defect = item.mb.defect
|
||||
|
||||
if mias.exists():
|
||||
is_zhj = True
|
||||
mias_list = mias.values_list('material', 'batch', 'rate')
|
||||
for i in mias_list:
|
||||
material, batch, rate = i
|
||||
new_count = rate * item.count # 假设 item.count 存在
|
||||
action_list.append([material, batch, new_count])
|
||||
action_list.append([material, batch, new_count, None])
|
||||
else:
|
||||
action_list = [[item.material, item.batch, item.count]]
|
||||
action_list = [[item.material, item.batch, item.count, defect]]
|
||||
|
||||
production_dept = None
|
||||
|
||||
xbatchs = []
|
||||
if is_zhj:
|
||||
xbatchs = [item.batch]
|
||||
for al in action_list:
|
||||
xmaterial, xbatch, xcount = al
|
||||
xmaterial, xbatch, xcount, defect = al
|
||||
if xcount <= 0:
|
||||
raise ParseError("存在非正数!")
|
||||
|
||||
xbatchs.append(xbatch)
|
||||
# 扣减车间库存
|
||||
|
||||
wm_qs = WMaterial.objects.filter(
|
||||
batch=xbatch,
|
||||
material=xmaterial,
|
||||
belong_dept=belong_dept,
|
||||
mgroup=mgroup,
|
||||
defect=defect,
|
||||
state=WMaterial.WM_OK)
|
||||
count_x = wm_qs.count()
|
||||
if count_x == 1:
|
||||
|
@ -161,6 +194,8 @@ def do_in(item: MIOItem):
|
|||
else:
|
||||
raise ParseError(
|
||||
f'{str(xmaterial)}-{xbatch}-存在多个相同批次!')
|
||||
|
||||
# 扣减车间库存
|
||||
new_count = wm.count - xcount
|
||||
if new_count >= 0:
|
||||
wm.count = new_count
|
||||
|
@ -180,8 +215,8 @@ def do_in(item: MIOItem):
|
|||
material=xmaterial,
|
||||
warehouse=item.warehouse,
|
||||
batch=xbatch,
|
||||
state=10,
|
||||
defect=None,
|
||||
state=WMaterial.WM_OK,
|
||||
defect=defect,
|
||||
defaults={
|
||||
"count": 0,
|
||||
"batch_ofrom": wm.batch_ofrom,
|
||||
|
@ -208,6 +243,8 @@ def do_in(item: MIOItem):
|
|||
material=item.material,
|
||||
warehouse=item.warehouse,
|
||||
batch=item.batch,
|
||||
defect=None,
|
||||
state=WMaterial.WM_OK,
|
||||
defaults={"count": 0, "production_dept": production_dept}
|
||||
)
|
||||
if not is_created:
|
||||
|
@ -351,7 +388,9 @@ class InmService:
|
|||
defect = defects_map[defect_id]
|
||||
m_list.append((material, warehouse, i.batch, xcount, defect, i))
|
||||
|
||||
xbatchs = []
|
||||
for material, warehouse, batch, change_count, defect, mioitem in m_list:
|
||||
xbatchs.append(batch)
|
||||
if change_count <= 0:
|
||||
raise ParseError("存在非正数!")
|
||||
state = WMaterial.WM_OK
|
||||
|
@ -402,6 +441,10 @@ class InmService:
|
|||
else:
|
||||
raise ParseError("不支持的操作")
|
||||
|
||||
# 批次统计分析
|
||||
xbatchs = list(set(xbatchs))
|
||||
for xbatch in xbatchs:
|
||||
MyThread(target=get_alldata_with_batch_and_store, args=(xbatch,)).start()
|
||||
|
||||
def daoru_mb(path: str):
|
||||
"""
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.12 on 2025-04-28 06:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('mtm', '0056_mgroup_batch_append_code'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='process',
|
||||
name='number_to_batch',
|
||||
field=models.BooleanField(default=False, verbose_name='个号转批号'),
|
||||
),
|
||||
]
|
|
@ -16,6 +16,7 @@ class Process(CommonBModel):
|
|||
PRO_NORMAL = 10
|
||||
PRO_DIV = 20
|
||||
PRO_MERGE = 30
|
||||
|
||||
name = models.CharField('工序名称', max_length=100)
|
||||
type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (PRO_TEST, '检验工序')))
|
||||
mtype = models.PositiveSmallIntegerField("工序生产类型", default=PRO_NORMAL, choices=((PRO_NORMAL, '常规'), (PRO_DIV, '切分'), (PRO_MERGE, '合并')))
|
||||
|
@ -30,6 +31,7 @@ class Process(CommonBModel):
|
|||
mlog_need_ticket = models.BooleanField('日志提交是否需要审批', default=False)
|
||||
mstate_json = models.JSONField('中间状态', default=list, blank=True)
|
||||
parent = models.ForeignKey('self', verbose_name='父工序', on_delete=models.CASCADE, null=True, blank=True)
|
||||
number_to_batch = models.BooleanField('个号转批号', default=False)
|
||||
|
||||
class Meta:
|
||||
verbose_name = '工序'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from apps.sam.models import Order, OrderItem
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from django.db.models import F
|
||||
from django.db.models import F, Sum
|
||||
from apps.inm.models import MIO, MIOItem
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.12 on 2025-04-28 05:50
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wpm', '0113_mlog_team'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='mlogb',
|
||||
name='number_from',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='来源个编号'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='wmaterial',
|
||||
name='number_from',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='来源于个号'),
|
||||
),
|
||||
]
|
|
@ -118,6 +118,7 @@ class WMaterial(CommonBDModel):
|
|||
count_xtest = models.DecimalField('已检数量', null=True, blank=True, max_digits=11, decimal_places=1)
|
||||
batch_ofrom = models.TextField('原料批次号', null=True, blank=True)
|
||||
material_ofrom = models.ForeignKey(Material, verbose_name='原料物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mofrom')
|
||||
number_from = models.TextField("来源于个号", null=True, blank=True)
|
||||
|
||||
@property
|
||||
def count_working(self):
|
||||
|
@ -382,6 +383,7 @@ class Mlogb(BaseModel):
|
|||
test_user = models.ForeignKey(
|
||||
User, verbose_name='抽检人', on_delete=models.CASCADE, null=True, blank=True, related_name='mlogb_test_user')
|
||||
need_inout = models.BooleanField('是否需要出入库', default=True)
|
||||
number_from = models.TextField('来源个编号', null=True, blank=True)
|
||||
|
||||
|
||||
def get_tracking(self):
|
||||
|
@ -466,6 +468,8 @@ class Mlogbw(BaseModel):
|
|||
@classmethod
|
||||
def cal_count_notok(cls, mlogb: Mlog):
|
||||
from apps.qm.models import Defect
|
||||
# 锁定mlogb以防止并发修改
|
||||
mlogb = Mlogb.objects.select_for_update().get(pk=mlogb.pk)
|
||||
count = Mlogbw.objects.filter(mlogb=mlogb).count()
|
||||
if mlogb.material_in:
|
||||
mlogb.count_use = count
|
||||
|
@ -631,6 +635,8 @@ class BatchSt(BaseModel):
|
|||
return node, False
|
||||
else:
|
||||
version = 1
|
||||
if mio is None and handover is None and mlog is None:
|
||||
raise ParseError("mio or handover or mlog must be provided")
|
||||
# 带有来源的批次获取,需检查批次号是否可用
|
||||
if cls.objects.filter(batch=batch).exists():
|
||||
if reuse_node:
|
||||
|
@ -639,11 +645,8 @@ class BatchSt(BaseModel):
|
|||
raise ParseError(f"{batch}-该批次号因物料不同不可引用")
|
||||
return node, False
|
||||
else:
|
||||
raise ParseError(f"{batch}-该批次号不可使用")
|
||||
# latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"]
|
||||
# version = latest_version + 1
|
||||
if mio is None and handover is None and mlog is None:
|
||||
raise ParseError("mio or handover or mlog must be provided")
|
||||
latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"]
|
||||
version = latest_version + 1
|
||||
ins = cls.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog, material_start=material_start, version=version)
|
||||
return ins, True
|
||||
|
||||
|
|
|
@ -724,6 +724,7 @@ class MlogbInSerializer(CustomModelSerializer):
|
|||
attrs['batch'] = wm_in.batch
|
||||
attrs["batch_ofrom"] = wm_in.batch_ofrom
|
||||
attrs["material_ofrom"] = wm_in.material_ofrom
|
||||
attrs["number_from"] = wm_in.number_from
|
||||
if route and route.batch_bind and mtask is not None:
|
||||
if not WMaterial.mat_in_qs(mtask).filter(id=wm_in.id).exists():
|
||||
raise ParseError('该车间库存非本任务使用')
|
||||
|
@ -799,8 +800,11 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
|
|||
in_or_out, tracking = mlogb.get_tracking()
|
||||
if tracking != Material.MA_TRACKING_SINGLE:
|
||||
raise ParseError('非单件追踪不可使用')
|
||||
if in_or_out == 'in' and not wpr:
|
||||
if in_or_out == 'in':
|
||||
if not wpr:
|
||||
raise ParseError('请选择相应产品')
|
||||
if mlogb.wm_in and wpr.wm != mlogb.wm_in:
|
||||
raise ParseError(f'{wpr.number}-该产品非本批次')
|
||||
return attrs
|
||||
|
||||
def save_ftest(self, mlogbw, ftest_data):
|
||||
|
|
|
@ -340,6 +340,10 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
|
|||
elif isinstance(mlog_or_b, Mlogb):
|
||||
wm.batch_ofrom = mlog_or_b.batch_ofrom
|
||||
wm.material_ofrom = mlog_or_b.material_ofrom
|
||||
if isinstance(mlog_or_b, Mlogb):
|
||||
if mlog_or_b.number_from and wm.number_from is not None and wm.number_from != mlog_or_b.number_from:
|
||||
raise ParseError(f'{wm.batch}-该批号现有库存来源于个号{wm.number_from}')
|
||||
wm.number_from = mlog_or_b.number_from
|
||||
wm.save()
|
||||
if mo_ma.tracking == Material.MA_TRACKING_SINGLE:
|
||||
if notok_sign_or_defect:
|
||||
|
@ -358,7 +362,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
|
|||
wpr_from = item.mlogbw_from.wpr
|
||||
wpr = Wpr.change_or_new(number=item.number,
|
||||
wm=wm, ftest=item.ftest,
|
||||
wpr_from=wpr_from, batch_from=item.mlogb.batch)
|
||||
wpr_from=wpr_from)
|
||||
item.wpr = wpr
|
||||
item.save()
|
||||
|
||||
|
@ -481,6 +485,9 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
|
|||
if wm.count < 0:
|
||||
raise ParseError('车间库存不足, 产物无法回退')
|
||||
elif wm.count >= 0:
|
||||
if isinstance(mlog_or_b, Mlogb):
|
||||
if mlog_or_b.number_from == wm.number_from:
|
||||
wm.number_from = None
|
||||
wm.update_by = user
|
||||
wm.save()
|
||||
if mo_ma.tracking == Material.MA_TRACKING_SINGLE:
|
||||
|
@ -793,7 +800,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
|||
defaults={
|
||||
"batch_ofrom": wm_from.batch_ofrom,
|
||||
"material_ofrom": wm_from.material_ofrom,
|
||||
"create_by": user
|
||||
"create_by": user,
|
||||
"number_from": wm_from.number_from
|
||||
}
|
||||
)
|
||||
elif handover.type == Handover.H_REPAIR:
|
||||
|
@ -816,7 +824,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
|||
defaults={
|
||||
"batch_ofrom": wm_from.batch_ofrom,
|
||||
"material_ofrom": wm_from.material_ofrom,
|
||||
"create_by": user
|
||||
"create_by": user,
|
||||
"number_from": wm_from.number_from
|
||||
}
|
||||
)
|
||||
else:
|
||||
|
@ -848,7 +857,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
|||
defaults={
|
||||
"batch_ofrom": wm_from.batch_ofrom,
|
||||
"material_ofrom": wm_from.material_ofrom,
|
||||
"create_by": user
|
||||
"create_by": user,
|
||||
"number_from": wm_from.number_from
|
||||
}
|
||||
)
|
||||
else:
|
||||
|
@ -867,7 +877,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
|||
defaults={
|
||||
"batch_ofrom": wm_from.batch_ofrom,
|
||||
"material_ofrom": wm_from.material_ofrom,
|
||||
"create_by": user
|
||||
"create_by": user,
|
||||
"number_from": wm_from.number_from
|
||||
}
|
||||
)
|
||||
else:
|
||||
|
@ -887,7 +898,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
|||
defaults={
|
||||
"batch_ofrom": wm_from.batch_ofrom,
|
||||
"material_ofrom": wm_from.material_ofrom,
|
||||
"create_by": user
|
||||
"create_by": user,
|
||||
"number_from": wm_from.number_from
|
||||
}
|
||||
)
|
||||
else:
|
||||
|
|
|
@ -12,7 +12,7 @@ import decimal
|
|||
import logging
|
||||
myLogger = logging.getLogger('log')
|
||||
|
||||
def get_alldata_with_batch_and_store(batch: str):
|
||||
def get_alldata_with_batch_and_store(batch: str, need_update_time=True):
|
||||
"""
|
||||
获取某个批次的整体生产数据并保存
|
||||
"""
|
||||
|
@ -29,6 +29,7 @@ def get_alldata_with_batch_and_store(batch: str):
|
|||
bobj, _ = BatchSt.objects.get_or_create(batch=batch, defaults={
|
||||
"last_time": last_time
|
||||
})
|
||||
if need_update_time:
|
||||
bobj.last_time = last_time
|
||||
bobj.data = json.loads(json.dumps(data, cls=MyJSONEncoder))
|
||||
bobj.save()
|
||||
|
@ -278,23 +279,6 @@ def get_alldata_with_batch(batch: str):
|
|||
myLogger.error(f"六车间_{mgroup_name}_合格率decimal.InvalidOperation-{data}")
|
||||
data[f'六车间_{mgroup_name}_合格率'] = 0
|
||||
|
||||
# 六车间入库/检验数据
|
||||
# mioitem6_qs2 = MIOItem.objects.filter(mio__belong_dept=dept6, mio__type="do_in",
|
||||
# batch=batch,
|
||||
# mio__submit_time__isnull=False)
|
||||
# if mioitem6_qs2.exists():
|
||||
# data["六车间生产入库_日期"] = []
|
||||
# for item in mioitem6_qs:
|
||||
# data["六车间生产入库_日期"].append(item.mio.inout_date)
|
||||
# for field in mioitem_count_fields:
|
||||
# if getattr(item, field) > 0:
|
||||
# if f'六车间生产入库_{field}' not in data:
|
||||
# data[f'六车间生产入库_{field}'] = getattr(item, field)
|
||||
# else:
|
||||
# data[f'六车间生产入库_{field}'] += getattr(item, field)
|
||||
# data["六车间生产入库_日期"] = list(set(data["六车间生产入库_日期"]))
|
||||
# data["六车间生产入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_日期"]])
|
||||
|
||||
ftestwork_count_fields = FtestWork.count_fields()
|
||||
# 六车间中检数据
|
||||
ftestwork_qs = FtestWork.objects.filter(batch=batch, type="process")
|
||||
|
@ -325,6 +309,34 @@ def get_alldata_with_batch(batch: str):
|
|||
data['六车间中检_检验人'] = list(set(data['六车间中检_检验人']))
|
||||
data['六车间中检_检验人'] = ";".join([item.name for item in data['六车间中检_检验人']])
|
||||
|
||||
# 六车间入库/检验数据
|
||||
mioitem6_qs2 = MIOItem.objects.filter(mio__belong_dept=dept6, mio__type="do_in",
|
||||
batch=batch,
|
||||
mio__submit_time__isnull=False)
|
||||
if mioitem6_qs2.exists():
|
||||
data["六车间生产入库_日期"] = []
|
||||
data["六车间生产入库_检验日期"] = []
|
||||
data["六车间生产入库_检验人"] = []
|
||||
for item in mioitem6_qs2:
|
||||
data["六车间生产入库_日期"].append(item.mio.inout_date)
|
||||
if item.test_date:
|
||||
data["六车间生产入库_检验日期"].append(item.test_date)
|
||||
for field in mioitem_count_fields:
|
||||
if getattr(item, field) is not None and (getattr(item, field) > 0 or field in ["count", "count_notok"]):
|
||||
if f'六车间生产入库_{field}' not in data:
|
||||
data[f'六车间生产入库_{field}'] = getattr(item, field)
|
||||
else:
|
||||
data[f'六车间生产入库_{field}'] += getattr(item, field)
|
||||
data["六车间生产入库_日期"] = list(set(data["六车间生产入库_日期"]))
|
||||
data["六车间生产入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_日期"]])
|
||||
data["六车间生产入库_检验日期"] = list(set(data["六车间生产入库_检验日期"]))
|
||||
data["六车间生产入库_检验日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_检验日期"]])
|
||||
try:
|
||||
data['六车间生产入库_合格率'] = round((data['六车间生产入库_count'] - data['六车间生产入库_count_notok']) * 100/ data['六车间生产入库_count'], 1)
|
||||
except decimal.InvalidOperation:
|
||||
myLogger.error("六车间生产入库_合格率decimal.InvalidOperation-{data}")
|
||||
data['六车间生产入库_合格率'] = 0
|
||||
|
||||
# 成品检验数据
|
||||
ftestwork_qs = FtestWork.objects.filter(batch=batch, type="prod")
|
||||
if ftestwork_qs.exists():
|
||||
|
@ -354,9 +366,29 @@ def get_alldata_with_batch(batch: str):
|
|||
data['成品检验_检验人'] = list(set(data['成品检验_检验人']))
|
||||
data['成品检验_检验人'] = ";".join([item.name for item in data['成品检验_检验人']])
|
||||
data['成品检验_合格率'] = round(data['成品检验_count_ok'] * 100/ data['成品检验_count'], 1)
|
||||
if getattr(data, "六车间领料_count", 0) > 0:
|
||||
data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1)
|
||||
if data["棒料成型_count_real"]:
|
||||
if getattr(data, "棒料成型_count_real", 0) > 0:
|
||||
data["七车间_批次应出合格率"] = round(data["成品检验_count_ok"] * 100/ data["棒料成型_count_real"], 1)
|
||||
|
||||
# 销售发货数据
|
||||
mioitem_qs = MIOItem.objects.filter(batch=batch, mio__type="sale_out", mio__submit_time__isnull=False)
|
||||
if mioitem_qs.exists():
|
||||
data["销售发货_日期"] = []
|
||||
data['销售发货_仓库执行人'] = []
|
||||
data['销售发货_count'] = 0
|
||||
for item in mioitem_qs:
|
||||
last_time = item.mio.update_time if item.mio.update_time > last_time else last_time
|
||||
if item.mio.inout_date:
|
||||
data["销售发货_日期"].append(item.mio.inout_date)
|
||||
if item.mio.do_user:
|
||||
data['销售发货_仓库执行人'].append(item.mio.do_user)
|
||||
data['销售发货_count']+= item.count
|
||||
if getattr(data, "棒料成型_count_real", 0) > 0:
|
||||
data["七车间_批次发货合格率"] = round(data["销售发货_count"] * 100/ data["棒料成型_count_real"], 1)
|
||||
if getattr(data, "六车间领料_count", 0) > 0:
|
||||
data["六车间_批次发货合格率"] = round(data["销售发货_count"] * 100/ data["六车间领料_count"], 1)
|
||||
data['销售发货_仓库执行人'] = ";".join([item.name for item in data['销售发货_仓库执行人']])
|
||||
|
||||
return last_time, data
|
||||
|
|
@ -563,6 +563,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
|||
mlogbin: Mlogb = serializer.save()
|
||||
mlog:Mlog = mlogbin.mlog
|
||||
route:Route = mlog.route
|
||||
process: Process = route.process if route else None
|
||||
mtype = route.process.mtype if route else None
|
||||
is_fix = mlog.is_fix
|
||||
qct = mlog.qct
|
||||
|
@ -614,9 +615,17 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
|||
elif mtype == Process.PRO_DIV: # 切分 支持批到批,个到个, 个到批
|
||||
div_number = route.div_number
|
||||
if material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_BATCH:
|
||||
lenx = Mlogbw.objects.filter(mlogb=mlogbin).count()
|
||||
# 用个号做批号是用于后续在复用个号可以追踪到原先的个
|
||||
for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"):
|
||||
if process and process.number_to_batch:
|
||||
m_dict["batch"] = mlogbwin.number
|
||||
Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number}))
|
||||
mlogbout, _ = Mlogb.objects.get_or_create(
|
||||
mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number}))
|
||||
if lenx == 1:
|
||||
mlogbout.mlogb_from = mlogbin
|
||||
mlogbout.number_from = mlogbwin.number
|
||||
mlogbout.save()
|
||||
elif material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_SINGLE:
|
||||
d_count_real = mlogbin.count_use * div_number
|
||||
d_count_ok = d_count_real
|
||||
|
@ -635,13 +644,22 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
|||
mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": d_count_real, "count_ok": d_count_ok}))
|
||||
mlogbout.count_json_from = mlogbin.count_json_from
|
||||
mlogbout.save(update_fields=["count_json_from"])
|
||||
elif mtype == Process.PRO_MERGE: # 支持批到批
|
||||
elif mtype == Process.PRO_MERGE: # 支持批到批,批到个
|
||||
xcount = math.floor( mlogbin.count_use / route.div_number)
|
||||
d_count_real = xcount
|
||||
d_count_ok = xcount
|
||||
mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=update_dict(m_dict, {"count_real": d_count_real, "count_ok": d_count_ok}))
|
||||
mlogbout.count_json_from = mlogbin.count_json_from
|
||||
mlogbout.save(update_fields=["count_json_from"])
|
||||
if material_out.tracking == Material.MA_TRACKING_SINGLE:
|
||||
number = mlogbin.batch
|
||||
if mlogbin.number_from:
|
||||
number = mlogbin.number_from
|
||||
if d_count_real == 1:
|
||||
Mlogbw.objects.get_or_create(number=number, mlogb=mlogbout)
|
||||
else:
|
||||
for i in range(d_count_real):
|
||||
Mlogbw.objects.get_or_create(number=f'{number}-{i+1}', mlogb=mlogbout)
|
||||
elif is_fix:# 支持批到批,个到个
|
||||
d_count_real = mlogbin.count_use
|
||||
d_count_ok = mlogbin.count_use
|
||||
|
@ -711,6 +729,11 @@ class MlogbwViewSet(CustomModelViewSet):
|
|||
@transaction.atomic
|
||||
def perform_create(self, serializer):
|
||||
ins:Mlogbw = serializer.save()
|
||||
if isinstance(ins, list):
|
||||
insx = ins
|
||||
else:
|
||||
insx = [ins]
|
||||
for ins in insx:
|
||||
route:Route = ins.mlogb.mlog.route
|
||||
mlogb:Mlogb = ins.mlogb
|
||||
Mlogbw.cal_count_notok(mlogb)
|
||||
|
@ -740,6 +763,15 @@ class MlogbwViewSet(CustomModelViewSet):
|
|||
for i in range(div_number):
|
||||
Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f"{ins.number}-{i+1}", defaults={"mlogbw_from": ins})
|
||||
Mlogbw.cal_count_notok(mlogbout)
|
||||
elif material_out.tracking == Material.MA_TRACKING_BATCH:
|
||||
number_from = mlogbout.number_from
|
||||
if number_from is None:
|
||||
mlogbout.number_from = ins.number
|
||||
mlogbout.save()
|
||||
elif number_from == ins.number:
|
||||
pass
|
||||
else:
|
||||
raise ParseError("该个号不可产生该批")
|
||||
elif mlogb_qs.exists() and material_out.tracking == Material.MA_TRACKING_SINGLE:
|
||||
for mlogb in mlogb_qs:
|
||||
if route.process.mtype == Process.PRO_NORMAL:
|
||||
|
|
|
@ -26,7 +26,7 @@ class Wpr(BaseModel):
|
|||
wpr_from = models.ForeignKey("self", verbose_name="来源于", on_delete=models.CASCADE, null=True, blank=True)
|
||||
|
||||
@classmethod
|
||||
def change_or_new(cls, wpr=None, number=None, mb=None, wm=None, old_mb=None, old_wm=None, ftest=None, wpr_from=None, batch_from=None):
|
||||
def change_or_new(cls, wpr=None, number=None, mb=None, wm=None, old_mb=None, old_wm=None, ftest=None, wpr_from=None):
|
||||
if wpr is None and number is None:
|
||||
raise ParseError("id和number不能同时为空")
|
||||
if mb and wm:
|
||||
|
@ -38,30 +38,44 @@ class Wpr(BaseModel):
|
|||
wpr.delete()
|
||||
return
|
||||
elif number:
|
||||
try:
|
||||
ins_x = cls.objects.get(number=number)
|
||||
ins_x = cls.objects.filter(number=number).order_by("-version").first()
|
||||
if ins_x:
|
||||
if ins_x.wm is None and ins_x.mb is None:
|
||||
if ins_x.version > 1: # 说明被复用了
|
||||
if wpr_from is None:
|
||||
wpr_from = ins_x
|
||||
else:
|
||||
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||
else:
|
||||
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||
except cls.DoesNotExist:
|
||||
# 创建新的wpr
|
||||
ins = cls(number=number)
|
||||
ins.version = -1
|
||||
ins.oinfo = {}
|
||||
if batch_from: # 尝试从批号追踪来源
|
||||
try:
|
||||
ins_from = cls.objects.get(number=number)
|
||||
if ins_from.wm is None and ins_from.mb is None:
|
||||
if ins_from.version > 1: # 说明被复用了
|
||||
wpr_from = ins_from
|
||||
else:
|
||||
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||
except cls.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||
else:
|
||||
ins = cls(number=number)
|
||||
ins.version = -1
|
||||
ins.oinfo = {}
|
||||
# if batch_from: # 尝试从批号追踪来源
|
||||
# ins_from = cls.objects.filter(number=batch_from).order_by("-version").first()
|
||||
# if ins_from:
|
||||
# if ins_from.wm is None and ins_from.mb is None:
|
||||
# if ins_from.version > 1: # 说明被复用了
|
||||
# wpr_from = ins_from
|
||||
# else:
|
||||
# raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||
# elif wpr_from is None:
|
||||
# raise ParseError(f"该物料编号{number}-尝试从批号追踪来源失败")
|
||||
# elif number_from: # 尝试从编号追踪来源
|
||||
# ins_from = cls.objects.filter(number=number_from).order_by("-version").first()
|
||||
# if ins_from:
|
||||
# if ins_from.wm is None and ins_from.mb is None:
|
||||
# if ins_from.version > 1: # 说明被复用了
|
||||
# wpr_from = ins_from
|
||||
# else:
|
||||
# raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||
# elif wpr_from is None:
|
||||
# raise ParseError(f"该物料编号{number}-尝试历史编号追踪来源失败")
|
||||
|
||||
if old_mb and ins.mb != old_mb:
|
||||
raise ParseError(f"请检查-{ins.number}-所属仓库批次")
|
||||
|
|
39
changelog.md
39
changelog.md
|
@ -1,3 +1,42 @@
|
|||
## 2.6.2025043014
|
||||
- feat: 新增功能
|
||||
- 生产入库和领料支持b类合格品 [caoqianming]
|
||||
- 添加标签模板接口 [caoqianming]
|
||||
- 完善光子批次统计数据 [caoqianming]
|
||||
- 组合件和入库检验追加统计分析 [caoqianming]
|
||||
- get_alldata_with_batch添加内容 [caoqianming]
|
||||
- fix: 问题修复
|
||||
- mio_saleout缺少import [caoqianming]
|
||||
## 2.6.2025042816
|
||||
- feat: 新增功能
|
||||
- 支持个号转批号及配置 [caoqianming]
|
||||
- 批次追踪链条还是可以复用批次 [caoqianming]
|
||||
- mlog结合工序支持批到个 [caoqianming]
|
||||
- mlogbw支持批量创建 [caoqianming]
|
||||
- mlogbwcreate校验增加wm所属 [caoqianming]
|
||||
- ichat 修改LLM 的接口 [zty]
|
||||
- wmaterial筛选条件优化来料已完成 [caoqianming]
|
||||
- 获取该批次的dag数据需要传入version [caoqianming]
|
||||
- 单填写mlog支持返工 [caoqianming]
|
||||
- ichat 修改大模型接口 [zty]
|
||||
- mlogbupdate支持变更批号 [caoqianming]
|
||||
- 6车间合格率统计decimal invalid [caoqianming]
|
||||
- 修改大模型文件 [zty]
|
||||
- model add note [zty]
|
||||
- ichat添加表 [caoqianming]
|
||||
- 缺陷项分类字段数据库约束放开 [caoqianming]
|
||||
- 通过django settings延迟获取BASE_PROJECT_CODE [caoqianming]
|
||||
- base 将配置文件放到单独的config文件夹中防止误操作 [caoqianming]
|
||||
- fix: 问题修复
|
||||
- wpr复用number的bug [caoqianming]
|
||||
- cal_count_notok锁定mlogb以防止并发修改 [caoqianming]
|
||||
- 获取batchst时默认使用version=1 [caoqianming]
|
||||
- mloginit在返工时不接收route [caoqianming]
|
||||
- 生产入库时存入生产车间 [caoqianming]
|
||||
- 返工校验输入物料选择错误 [caoqianming]
|
||||
- 完善负数校验 [caoqianming]
|
||||
- mlogbupdate时attrs遍历时修改的bug [caoqianming]
|
||||
- batchst的version字段bug [caoqianming]
|
||||
## 2.6.2025042311
|
||||
- feat: 新增功能
|
||||
- 切分工序切分数量支持1 [caoqianming]
|
||||
|
|
|
@ -35,7 +35,7 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
|||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
SYS_NAME = '星途工厂综合管理系统'
|
||||
SYS_VERSION = '2.6.2025042311'
|
||||
SYS_VERSION = '2.6.2025043014'
|
||||
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||
|
||||
# Application definition
|
||||
|
|
|
@ -44,7 +44,7 @@ urlpatterns = [
|
|||
|
||||
# api
|
||||
path('', include('apps.auth1.urls')),
|
||||
path('', include('apps.ichat.urls')),
|
||||
# path('', include('apps.ichat.urls')),
|
||||
path('', include('apps.system.urls')),
|
||||
path('', include('apps.monitor.urls')),
|
||||
path('', include('apps.wf.urls')),
|
||||
|
|
Loading…
Reference in New Issue