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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -13,4 +13,9 @@ class LableMat(BaseModel):
|
||||||
supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, null=True, blank=True)
|
supplier = models.ForeignKey(Supplier, verbose_name='外协供应商', on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
notok_sign = models.CharField('不合格标记', max_length=10, null=True, blank=True)
|
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)
|
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')
|
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 rest_framework import serializers
|
||||||
from .models import LableMat
|
from .models import LableMat, LabelTemplate
|
||||||
from apps.qm.models import NotOkOption
|
from apps.qm.models import NotOkOption
|
||||||
from apps.wpm.models import WmStateOption
|
from apps.wpm.models import WmStateOption
|
||||||
|
from apps.utils.serializers import CustomModelSerializer
|
||||||
|
|
||||||
|
|
||||||
class TidSerializer(serializers.Serializer):
|
class TidSerializer(serializers.Serializer):
|
||||||
|
@ -23,4 +24,10 @@ class LabelMatSerializer(serializers.ModelSerializer):
|
||||||
return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None
|
return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None
|
||||||
|
|
||||||
def get_state_name(self, obj):
|
def get_state_name(self, obj):
|
||||||
return getattr(WmStateOption, str(obj.state), WmStateOption.OK).label if obj.state else None
|
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 django.urls import path, include
|
||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from apps.cm.views import LableMatViewSet
|
from apps.cm.views import LableMatViewSet, LabelTemplateViewSet
|
||||||
|
|
||||||
API_BASE_URL = 'api/cm/'
|
API_BASE_URL = 'api/cm/'
|
||||||
HTML_BASE_URL = 'dhtml/cm/'
|
HTML_BASE_URL = 'dhtml/cm/'
|
||||||
|
|
||||||
router = DefaultRouter()
|
router = DefaultRouter()
|
||||||
router.register('labelmat', LableMatViewSet, basename='labelmat')
|
router.register('labelmat', LableMatViewSet, basename='labelmat')
|
||||||
|
router.register('labeltemplate', LabelTemplateViewSet, basename='labeltemplate')
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(API_BASE_URL, include(router.urls)),
|
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 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.inm.models import MaterialBatch, MIOItem
|
||||||
from apps.wpm.models import WMaterial
|
from apps.wpm.models import WMaterial
|
||||||
from rest_framework.exceptions import ParseError, NotFound
|
from rest_framework.exceptions import ParseError, NotFound
|
||||||
from rest_framework.response import Response
|
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.
|
# Create your views here.
|
||||||
|
|
||||||
SPLIT_FIELD = "#"
|
SPLIT_FIELD = "#"
|
||||||
|
@ -68,3 +68,13 @@ class LableMatViewSet(CustomListModelMixin, RetrieveModelMixin, CustomGenericVie
|
||||||
rdata = LabelMatSerializer(obj).data
|
rdata = LabelMatSerializer(obj).data
|
||||||
rdata["code_label"] = f"mat{SPLIT_FIELD}{obj.id}"
|
rdata["code_label"] = f"mat{SPLIT_FIELD}{obj.id}"
|
||||||
return Response(rdata)
|
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:
|
if item.mb and item.mb.defect is not None:
|
||||||
raise ParseError("生产领料不支持不合格品")
|
raise ParseError("生产领料不支持不合格品")
|
||||||
|
|
||||||
from apps.inm.models import MaterialBatch
|
from apps.inm.models import MaterialBatch
|
||||||
mio:MIO = item.mio
|
mio:MIO = item.mio
|
||||||
belong_dept = mio.belong_dept
|
belong_dept = mio.belong_dept
|
||||||
|
@ -26,6 +27,16 @@ def do_out(item: MIOItem):
|
||||||
material:Material = item.material
|
material:Material = item.material
|
||||||
if material.into_wm is False: # 用于混料的原料不与车间库存交互, 这个是配置项目
|
if material.into_wm is False: # 用于混料的原料不与车间库存交互, 这个是配置项目
|
||||||
return
|
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 = []
|
action_list = []
|
||||||
mias = MIOItemA.objects.filter(mioitem=item)
|
mias = MIOItemA.objects.filter(mioitem=item)
|
||||||
is_zhj = False # 是否组合件领料
|
is_zhj = False # 是否组合件领料
|
||||||
|
@ -35,16 +46,18 @@ def do_out(item: MIOItem):
|
||||||
for i in range(len(mias_list)):
|
for i in range(len(mias_list)):
|
||||||
material, batch, rate = mias_list[i]
|
material, batch, rate = mias_list[i]
|
||||||
new_count = rate * item.count # 假设 item.count 存在
|
new_count = rate * item.count # 假设 item.count 存在
|
||||||
action_list.append([material, batch, new_count])
|
action_list.append([material, batch, new_count, None])
|
||||||
else:
|
else:
|
||||||
action_list = [[item.material, item.batch, item.count]]
|
action_list = [[item.material, item.batch, item.count, defect]]
|
||||||
|
|
||||||
if is_zhj:
|
if is_zhj:
|
||||||
try:
|
try:
|
||||||
mb = MaterialBatch.objects.get(
|
mb = MaterialBatch.objects.get(
|
||||||
material=item.material,
|
material=item.material,
|
||||||
warehouse=item.warehouse,
|
warehouse=item.warehouse,
|
||||||
batch=item.batch
|
batch=item.batch,
|
||||||
|
state=WMaterial.WM_OK,
|
||||||
|
defect=None
|
||||||
)
|
)
|
||||||
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
||||||
raise ParseError(f"组合件批次错误!{e}")
|
raise ParseError(f"组合件批次错误!{e}")
|
||||||
|
@ -58,10 +71,14 @@ def do_out(item: MIOItem):
|
||||||
raise ParseError("组合件暂不支持追踪单件")
|
raise ParseError("组合件暂不支持追踪单件")
|
||||||
|
|
||||||
xbatches = []
|
xbatches = []
|
||||||
|
if is_zhj:
|
||||||
|
xbatches = [item.batch]
|
||||||
for al in action_list:
|
for al in action_list:
|
||||||
xmaterial:Material = al[0]
|
xmaterial:Material = al[0]
|
||||||
xbatch:str = al[1]
|
xbatch:str = al[1]
|
||||||
xcount:str = al[2]
|
xcount:str = al[2]
|
||||||
|
defect:Defect = al[3]
|
||||||
|
|
||||||
xbatches.append(xbatch)
|
xbatches.append(xbatch)
|
||||||
if xcount <= 0:
|
if xcount <= 0:
|
||||||
raise ParseError("存在非正数!")
|
raise ParseError("存在非正数!")
|
||||||
|
@ -72,8 +89,8 @@ def do_out(item: MIOItem):
|
||||||
material=xmaterial,
|
material=xmaterial,
|
||||||
warehouse=item.warehouse,
|
warehouse=item.warehouse,
|
||||||
batch=xbatch,
|
batch=xbatch,
|
||||||
state=10,
|
state=WMaterial.WM_OK,
|
||||||
defect=None
|
defect=defect
|
||||||
)
|
)
|
||||||
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e:
|
||||||
raise ParseError(f"批次错误!{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(
|
||||||
belong_dept=belong_dept, mgroup=mgroup,
|
batch=xbatch, material=xmaterial,
|
||||||
state=WMaterial.WM_OK)
|
belong_dept=belong_dept, mgroup=mgroup,
|
||||||
|
state=WMaterial.WM_OK, defect=defect)
|
||||||
if new_create:
|
if new_create:
|
||||||
wm.create_by = do_user
|
wm.create_by = do_user
|
||||||
wm.batch_ofrom = mb.batch if mb else None
|
wm.batch_ofrom = mb.batch if mb else None
|
||||||
|
@ -116,8 +134,9 @@ def do_in(item: MIOItem):
|
||||||
生产入库后更新车间物料
|
生产入库后更新车间物料
|
||||||
"""
|
"""
|
||||||
mio = item.mio
|
mio = item.mio
|
||||||
if item.wm and item.wm.defect is not None:
|
wmin:WMaterial = item.wm
|
||||||
raise ParseError("不合格物料无法入库")
|
if wmin and wmin.state != WMaterial.WM_OK:
|
||||||
|
raise ParseError("非合格物料无法入库")
|
||||||
belong_dept = mio.belong_dept
|
belong_dept = mio.belong_dept
|
||||||
mgroup = mio.mgroup
|
mgroup = mio.mgroup
|
||||||
do_user = mio.do_user
|
do_user = mio.do_user
|
||||||
|
@ -127,30 +146,44 @@ def do_in(item: MIOItem):
|
||||||
action_list = []
|
action_list = []
|
||||||
mias = MIOItemA.objects.filter(mioitem=item)
|
mias = MIOItemA.objects.filter(mioitem=item)
|
||||||
is_zhj = False # 是否组合件入仓库
|
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():
|
if mias.exists():
|
||||||
is_zhj = True
|
is_zhj = True
|
||||||
mias_list = mias.values_list('material', 'batch', 'rate')
|
mias_list = mias.values_list('material', 'batch', 'rate')
|
||||||
for i in mias_list:
|
for i in mias_list:
|
||||||
material, batch, rate = i
|
material, batch, rate = i
|
||||||
new_count = rate * item.count # 假设 item.count 存在
|
new_count = rate * item.count # 假设 item.count 存在
|
||||||
action_list.append([material, batch, new_count])
|
action_list.append([material, batch, new_count, None])
|
||||||
else:
|
else:
|
||||||
action_list = [[item.material, item.batch, item.count]]
|
action_list = [[item.material, item.batch, item.count, defect]]
|
||||||
|
|
||||||
production_dept = None
|
production_dept = None
|
||||||
|
|
||||||
xbatchs = []
|
xbatchs = []
|
||||||
|
if is_zhj:
|
||||||
|
xbatchs = [item.batch]
|
||||||
for al in action_list:
|
for al in action_list:
|
||||||
xmaterial, xbatch, xcount = al
|
xmaterial, xbatch, xcount, defect = al
|
||||||
if xcount <= 0:
|
if xcount <= 0:
|
||||||
raise ParseError("存在非正数!")
|
raise ParseError("存在非正数!")
|
||||||
|
|
||||||
xbatchs.append(xbatch)
|
xbatchs.append(xbatch)
|
||||||
# 扣减车间库存
|
|
||||||
wm_qs = WMaterial.objects.filter(
|
wm_qs = WMaterial.objects.filter(
|
||||||
batch=xbatch,
|
batch=xbatch,
|
||||||
material=xmaterial,
|
material=xmaterial,
|
||||||
belong_dept=belong_dept,
|
belong_dept=belong_dept,
|
||||||
mgroup=mgroup,
|
mgroup=mgroup,
|
||||||
|
defect=defect,
|
||||||
state=WMaterial.WM_OK)
|
state=WMaterial.WM_OK)
|
||||||
count_x = wm_qs.count()
|
count_x = wm_qs.count()
|
||||||
if count_x == 1:
|
if count_x == 1:
|
||||||
|
@ -161,6 +194,8 @@ def do_in(item: MIOItem):
|
||||||
else:
|
else:
|
||||||
raise ParseError(
|
raise ParseError(
|
||||||
f'{str(xmaterial)}-{xbatch}-存在多个相同批次!')
|
f'{str(xmaterial)}-{xbatch}-存在多个相同批次!')
|
||||||
|
|
||||||
|
# 扣减车间库存
|
||||||
new_count = wm.count - xcount
|
new_count = wm.count - xcount
|
||||||
if new_count >= 0:
|
if new_count >= 0:
|
||||||
wm.count = new_count
|
wm.count = new_count
|
||||||
|
@ -180,8 +215,8 @@ def do_in(item: MIOItem):
|
||||||
material=xmaterial,
|
material=xmaterial,
|
||||||
warehouse=item.warehouse,
|
warehouse=item.warehouse,
|
||||||
batch=xbatch,
|
batch=xbatch,
|
||||||
state=10,
|
state=WMaterial.WM_OK,
|
||||||
defect=None,
|
defect=defect,
|
||||||
defaults={
|
defaults={
|
||||||
"count": 0,
|
"count": 0,
|
||||||
"batch_ofrom": wm.batch_ofrom,
|
"batch_ofrom": wm.batch_ofrom,
|
||||||
|
@ -208,6 +243,8 @@ def do_in(item: MIOItem):
|
||||||
material=item.material,
|
material=item.material,
|
||||||
warehouse=item.warehouse,
|
warehouse=item.warehouse,
|
||||||
batch=item.batch,
|
batch=item.batch,
|
||||||
|
defect=None,
|
||||||
|
state=WMaterial.WM_OK,
|
||||||
defaults={"count": 0, "production_dept": production_dept}
|
defaults={"count": 0, "production_dept": production_dept}
|
||||||
)
|
)
|
||||||
if not is_created:
|
if not is_created:
|
||||||
|
@ -351,7 +388,9 @@ class InmService:
|
||||||
defect = defects_map[defect_id]
|
defect = defects_map[defect_id]
|
||||||
m_list.append((material, warehouse, i.batch, xcount, defect, i))
|
m_list.append((material, warehouse, i.batch, xcount, defect, i))
|
||||||
|
|
||||||
|
xbatchs = []
|
||||||
for material, warehouse, batch, change_count, defect, mioitem in m_list:
|
for material, warehouse, batch, change_count, defect, mioitem in m_list:
|
||||||
|
xbatchs.append(batch)
|
||||||
if change_count <= 0:
|
if change_count <= 0:
|
||||||
raise ParseError("存在非正数!")
|
raise ParseError("存在非正数!")
|
||||||
state = WMaterial.WM_OK
|
state = WMaterial.WM_OK
|
||||||
|
@ -402,6 +441,10 @@ class InmService:
|
||||||
else:
|
else:
|
||||||
raise ParseError("不支持的操作")
|
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):
|
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_NORMAL = 10
|
||||||
PRO_DIV = 20
|
PRO_DIV = 20
|
||||||
PRO_MERGE = 30
|
PRO_MERGE = 30
|
||||||
|
|
||||||
name = models.CharField('工序名称', max_length=100)
|
name = models.CharField('工序名称', max_length=100)
|
||||||
type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (PRO_TEST, '检验工序')))
|
type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (PRO_TEST, '检验工序')))
|
||||||
mtype = models.PositiveSmallIntegerField("工序生产类型", default=PRO_NORMAL, choices=((PRO_NORMAL, '常规'), (PRO_DIV, '切分'), (PRO_MERGE, '合并')))
|
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)
|
mlog_need_ticket = models.BooleanField('日志提交是否需要审批', default=False)
|
||||||
mstate_json = models.JSONField('中间状态', default=list, blank=True)
|
mstate_json = models.JSONField('中间状态', default=list, blank=True)
|
||||||
parent = models.ForeignKey('self', verbose_name='父工序', on_delete=models.CASCADE, null=True, 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:
|
class Meta:
|
||||||
verbose_name = '工序'
|
verbose_name = '工序'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from apps.sam.models import Order, OrderItem
|
from apps.sam.models import Order, OrderItem
|
||||||
from rest_framework.exceptions import ValidationError
|
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
|
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)
|
count_xtest = models.DecimalField('已检数量', null=True, blank=True, max_digits=11, decimal_places=1)
|
||||||
batch_ofrom = models.TextField('原料批次号', null=True, blank=True)
|
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')
|
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
|
@property
|
||||||
def count_working(self):
|
def count_working(self):
|
||||||
|
@ -382,6 +383,7 @@ class Mlogb(BaseModel):
|
||||||
test_user = models.ForeignKey(
|
test_user = models.ForeignKey(
|
||||||
User, verbose_name='抽检人', on_delete=models.CASCADE, null=True, blank=True, related_name='mlogb_test_user')
|
User, verbose_name='抽检人', on_delete=models.CASCADE, null=True, blank=True, related_name='mlogb_test_user')
|
||||||
need_inout = models.BooleanField('是否需要出入库', default=True)
|
need_inout = models.BooleanField('是否需要出入库', default=True)
|
||||||
|
number_from = models.TextField('来源个编号', null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
def get_tracking(self):
|
def get_tracking(self):
|
||||||
|
@ -466,6 +468,8 @@ class Mlogbw(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def cal_count_notok(cls, mlogb: Mlog):
|
def cal_count_notok(cls, mlogb: Mlog):
|
||||||
from apps.qm.models import Defect
|
from apps.qm.models import Defect
|
||||||
|
# 锁定mlogb以防止并发修改
|
||||||
|
mlogb = Mlogb.objects.select_for_update().get(pk=mlogb.pk)
|
||||||
count = Mlogbw.objects.filter(mlogb=mlogb).count()
|
count = Mlogbw.objects.filter(mlogb=mlogb).count()
|
||||||
if mlogb.material_in:
|
if mlogb.material_in:
|
||||||
mlogb.count_use = count
|
mlogb.count_use = count
|
||||||
|
@ -631,6 +635,8 @@ class BatchSt(BaseModel):
|
||||||
return node, False
|
return node, False
|
||||||
else:
|
else:
|
||||||
version = 1
|
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 cls.objects.filter(batch=batch).exists():
|
||||||
if reuse_node:
|
if reuse_node:
|
||||||
|
@ -639,11 +645,8 @@ class BatchSt(BaseModel):
|
||||||
raise ParseError(f"{batch}-该批次号因物料不同不可引用")
|
raise ParseError(f"{batch}-该批次号因物料不同不可引用")
|
||||||
return node, False
|
return node, False
|
||||||
else:
|
else:
|
||||||
raise ParseError(f"{batch}-该批次号不可使用")
|
latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"]
|
||||||
# latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"]
|
version = latest_version + 1
|
||||||
# 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")
|
|
||||||
ins = cls.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog, material_start=material_start, version=version)
|
ins = cls.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog, material_start=material_start, version=version)
|
||||||
return ins, True
|
return ins, True
|
||||||
|
|
||||||
|
|
|
@ -724,6 +724,7 @@ class MlogbInSerializer(CustomModelSerializer):
|
||||||
attrs['batch'] = wm_in.batch
|
attrs['batch'] = wm_in.batch
|
||||||
attrs["batch_ofrom"] = wm_in.batch_ofrom
|
attrs["batch_ofrom"] = wm_in.batch_ofrom
|
||||||
attrs["material_ofrom"] = wm_in.material_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 route and route.batch_bind and mtask is not None:
|
||||||
if not WMaterial.mat_in_qs(mtask).filter(id=wm_in.id).exists():
|
if not WMaterial.mat_in_qs(mtask).filter(id=wm_in.id).exists():
|
||||||
raise ParseError('该车间库存非本任务使用')
|
raise ParseError('该车间库存非本任务使用')
|
||||||
|
@ -799,8 +800,11 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer):
|
||||||
in_or_out, tracking = mlogb.get_tracking()
|
in_or_out, tracking = mlogb.get_tracking()
|
||||||
if tracking != Material.MA_TRACKING_SINGLE:
|
if tracking != Material.MA_TRACKING_SINGLE:
|
||||||
raise ParseError('非单件追踪不可使用')
|
raise ParseError('非单件追踪不可使用')
|
||||||
if in_or_out == 'in' and not wpr:
|
if in_or_out == 'in':
|
||||||
raise ParseError('请选择相应产品')
|
if not wpr:
|
||||||
|
raise ParseError('请选择相应产品')
|
||||||
|
if mlogb.wm_in and wpr.wm != mlogb.wm_in:
|
||||||
|
raise ParseError(f'{wpr.number}-该产品非本批次')
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def save_ftest(self, mlogbw, ftest_data):
|
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):
|
elif isinstance(mlog_or_b, Mlogb):
|
||||||
wm.batch_ofrom = mlog_or_b.batch_ofrom
|
wm.batch_ofrom = mlog_or_b.batch_ofrom
|
||||||
wm.material_ofrom = mlog_or_b.material_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()
|
wm.save()
|
||||||
if mo_ma.tracking == Material.MA_TRACKING_SINGLE:
|
if mo_ma.tracking == Material.MA_TRACKING_SINGLE:
|
||||||
if notok_sign_or_defect:
|
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_from = item.mlogbw_from.wpr
|
||||||
wpr = Wpr.change_or_new(number=item.number,
|
wpr = Wpr.change_or_new(number=item.number,
|
||||||
wm=wm, ftest=item.ftest,
|
wm=wm, ftest=item.ftest,
|
||||||
wpr_from=wpr_from, batch_from=item.mlogb.batch)
|
wpr_from=wpr_from)
|
||||||
item.wpr = wpr
|
item.wpr = wpr
|
||||||
item.save()
|
item.save()
|
||||||
|
|
||||||
|
@ -481,6 +485,9 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]):
|
||||||
if wm.count < 0:
|
if wm.count < 0:
|
||||||
raise ParseError('车间库存不足, 产物无法回退')
|
raise ParseError('车间库存不足, 产物无法回退')
|
||||||
elif wm.count >= 0:
|
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.update_by = user
|
||||||
wm.save()
|
wm.save()
|
||||||
if mo_ma.tracking == Material.MA_TRACKING_SINGLE:
|
if mo_ma.tracking == Material.MA_TRACKING_SINGLE:
|
||||||
|
@ -793,7 +800,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
||||||
defaults={
|
defaults={
|
||||||
"batch_ofrom": wm_from.batch_ofrom,
|
"batch_ofrom": wm_from.batch_ofrom,
|
||||||
"material_ofrom": wm_from.material_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:
|
elif handover.type == Handover.H_REPAIR:
|
||||||
|
@ -816,7 +824,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
||||||
defaults={
|
defaults={
|
||||||
"batch_ofrom": wm_from.batch_ofrom,
|
"batch_ofrom": wm_from.batch_ofrom,
|
||||||
"material_ofrom": wm_from.material_ofrom,
|
"material_ofrom": wm_from.material_ofrom,
|
||||||
"create_by": user
|
"create_by": user,
|
||||||
|
"number_from": wm_from.number_from
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -848,7 +857,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
||||||
defaults={
|
defaults={
|
||||||
"batch_ofrom": wm_from.batch_ofrom,
|
"batch_ofrom": wm_from.batch_ofrom,
|
||||||
"material_ofrom": wm_from.material_ofrom,
|
"material_ofrom": wm_from.material_ofrom,
|
||||||
"create_by": user
|
"create_by": user,
|
||||||
|
"number_from": wm_from.number_from
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -867,7 +877,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
||||||
defaults={
|
defaults={
|
||||||
"batch_ofrom": wm_from.batch_ofrom,
|
"batch_ofrom": wm_from.batch_ofrom,
|
||||||
"material_ofrom": wm_from.material_ofrom,
|
"material_ofrom": wm_from.material_ofrom,
|
||||||
"create_by": user
|
"create_by": user,
|
||||||
|
"number_from": wm_from.number_from
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -887,7 +898,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime,
|
||||||
defaults={
|
defaults={
|
||||||
"batch_ofrom": wm_from.batch_ofrom,
|
"batch_ofrom": wm_from.batch_ofrom,
|
||||||
"material_ofrom": wm_from.material_ofrom,
|
"material_ofrom": wm_from.material_ofrom,
|
||||||
"create_by": user
|
"create_by": user,
|
||||||
|
"number_from": wm_from.number_from
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -12,7 +12,7 @@ import decimal
|
||||||
import logging
|
import logging
|
||||||
myLogger = logging.getLogger('log')
|
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,7 +29,8 @@ def get_alldata_with_batch_and_store(batch: str):
|
||||||
bobj, _ = BatchSt.objects.get_or_create(batch=batch, defaults={
|
bobj, _ = BatchSt.objects.get_or_create(batch=batch, defaults={
|
||||||
"last_time": last_time
|
"last_time": last_time
|
||||||
})
|
})
|
||||||
bobj.last_time = last_time
|
if need_update_time:
|
||||||
|
bobj.last_time = last_time
|
||||||
bobj.data = json.loads(json.dumps(data, cls=MyJSONEncoder))
|
bobj.data = json.loads(json.dumps(data, cls=MyJSONEncoder))
|
||||||
bobj.save()
|
bobj.save()
|
||||||
|
|
||||||
|
@ -278,23 +279,6 @@ def get_alldata_with_batch(batch: str):
|
||||||
myLogger.error(f"六车间_{mgroup_name}_合格率decimal.InvalidOperation-{data}")
|
myLogger.error(f"六车间_{mgroup_name}_合格率decimal.InvalidOperation-{data}")
|
||||||
data[f'六车间_{mgroup_name}_合格率'] = 0
|
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_count_fields = FtestWork.count_fields()
|
||||||
# 六车间中检数据
|
# 六车间中检数据
|
||||||
ftestwork_qs = FtestWork.objects.filter(batch=batch, type="process")
|
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['六车间中检_检验人'] = list(set(data['六车间中检_检验人']))
|
||||||
data['六车间中检_检验人'] = ";".join([item.name for item in 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")
|
ftestwork_qs = FtestWork.objects.filter(batch=batch, type="prod")
|
||||||
if ftestwork_qs.exists():
|
if ftestwork_qs.exists():
|
||||||
|
@ -354,9 +366,29 @@ def get_alldata_with_batch(batch: str):
|
||||||
data['成品检验_检验人'] = list(set(data['成品检验_检验人']))
|
data['成品检验_检验人'] = list(set(data['成品检验_检验人']))
|
||||||
data['成品检验_检验人'] = ";".join([item.name for item in data['成品检验_检验人']])
|
data['成品检验_检验人'] = ";".join([item.name for item in data['成品检验_检验人']])
|
||||||
data['成品检验_合格率'] = round(data['成品检验_count_ok'] * 100/ data['成品检验_count'], 1)
|
data['成品检验_合格率'] = round(data['成品检验_count_ok'] * 100/ data['成品检验_count'], 1)
|
||||||
data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1)
|
if getattr(data, "六车间领料_count", 0) > 0:
|
||||||
if data["棒料成型_count_real"]:
|
data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1)
|
||||||
|
if getattr(data, "棒料成型_count_real", 0) > 0:
|
||||||
data["七车间_批次应出合格率"] = round(data["成品检验_count_ok"] * 100/ data["棒料成型_count_real"], 1)
|
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
|
return last_time, data
|
||||||
|
|
|
@ -563,6 +563,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
||||||
mlogbin: Mlogb = serializer.save()
|
mlogbin: Mlogb = serializer.save()
|
||||||
mlog:Mlog = mlogbin.mlog
|
mlog:Mlog = mlogbin.mlog
|
||||||
route:Route = mlog.route
|
route:Route = mlog.route
|
||||||
|
process: Process = route.process if route else None
|
||||||
mtype = route.process.mtype if route else None
|
mtype = route.process.mtype if route else None
|
||||||
is_fix = mlog.is_fix
|
is_fix = mlog.is_fix
|
||||||
qct = mlog.qct
|
qct = mlog.qct
|
||||||
|
@ -614,9 +615,17 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust
|
||||||
elif mtype == Process.PRO_DIV: # 切分 支持批到批,个到个, 个到批
|
elif mtype == Process.PRO_DIV: # 切分 支持批到批,个到个, 个到批
|
||||||
div_number = route.div_number
|
div_number = route.div_number
|
||||||
if material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_BATCH:
|
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"):
|
for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"):
|
||||||
m_dict["batch"] = mlogbwin.number
|
if process and process.number_to_batch:
|
||||||
Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number}))
|
m_dict["batch"] = mlogbwin.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:
|
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_real = mlogbin.count_use * div_number
|
||||||
d_count_ok = d_count_real
|
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, _ = 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.count_json_from = mlogbin.count_json_from
|
||||||
mlogbout.save(update_fields=["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)
|
xcount = math.floor( mlogbin.count_use / route.div_number)
|
||||||
d_count_real = xcount
|
d_count_real = xcount
|
||||||
d_count_ok = 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, _ = 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.count_json_from = mlogbin.count_json_from
|
||||||
mlogbout.save(update_fields=["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:# 支持批到批,个到个
|
elif is_fix:# 支持批到批,个到个
|
||||||
d_count_real = mlogbin.count_use
|
d_count_real = mlogbin.count_use
|
||||||
d_count_ok = mlogbin.count_use
|
d_count_ok = mlogbin.count_use
|
||||||
|
@ -711,43 +729,57 @@ class MlogbwViewSet(CustomModelViewSet):
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
ins:Mlogbw = serializer.save()
|
ins:Mlogbw = serializer.save()
|
||||||
route:Route = ins.mlogb.mlog.route
|
if isinstance(ins, list):
|
||||||
mlogb:Mlogb = ins.mlogb
|
insx = ins
|
||||||
Mlogbw.cal_count_notok(mlogb)
|
else:
|
||||||
# 如果是输入且输出追踪到个,需同步创建
|
insx = [ins]
|
||||||
material_in:Material = mlogb.material_in
|
for ins in insx:
|
||||||
if material_in is not None:
|
route:Route = ins.mlogb.mlog.route
|
||||||
mlogb_qs = Mlogb.objects.filter(mlogb_from=mlogb)
|
mlogb:Mlogb = ins.mlogb
|
||||||
material_out:Material = mlogb.mlog.material_out
|
Mlogbw.cal_count_notok(mlogb)
|
||||||
mtype = route.process.mtype if route.process else None
|
# 如果是输入且输出追踪到个,需同步创建
|
||||||
if mtype == Process.PRO_DIV:
|
material_in:Material = mlogb.material_in
|
||||||
mlogbin = ins.mlogb
|
if material_in is not None:
|
||||||
wm_in = mlogbin.wm_in
|
mlogb_qs = Mlogb.objects.filter(mlogb_from=mlogb)
|
||||||
mlog = mlogbin.mlog
|
material_out:Material = mlogb.mlog.material_out
|
||||||
div_number = route.div_number
|
mtype = route.process.mtype if route.process else None
|
||||||
m_dict = {
|
if mtype == Process.PRO_DIV:
|
||||||
"mtask": mlogbin.mtask,
|
mlogbin = ins.mlogb
|
||||||
"mlog": mlog,
|
wm_in = mlogbin.wm_in
|
||||||
"batch": ins.number,
|
mlog = mlogbin.mlog
|
||||||
"material_out": material_out,
|
div_number = route.div_number
|
||||||
"batch_ofrom": wm_in.batch_ofrom,
|
m_dict = {
|
||||||
"material_ofrom": wm_in.material_ofrom,
|
"mtask": mlogbin.mtask,
|
||||||
"count_real": div_number,
|
"mlog": mlog,
|
||||||
"count_ok": div_number, "qct": mlog.qct
|
"batch": ins.number,
|
||||||
}
|
"material_out": material_out,
|
||||||
mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=ins, defaults=m_dict)
|
"batch_ofrom": wm_in.batch_ofrom,
|
||||||
if material_out.tracking == Material.MA_TRACKING_SINGLE:
|
"material_ofrom": wm_in.material_ofrom,
|
||||||
for i in range(div_number):
|
"count_real": div_number,
|
||||||
Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f"{ins.number}-{i+1}", defaults={"mlogbw_from": ins})
|
"count_ok": div_number, "qct": mlog.qct
|
||||||
Mlogbw.cal_count_notok(mlogbout)
|
}
|
||||||
elif mlogb_qs.exists() and material_out.tracking == Material.MA_TRACKING_SINGLE:
|
mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=ins, defaults=m_dict)
|
||||||
for mlogb in mlogb_qs:
|
if material_out.tracking == Material.MA_TRACKING_SINGLE:
|
||||||
if route.process.mtype == Process.PRO_NORMAL:
|
for i in range(div_number):
|
||||||
Mlogbw.objects.get_or_create(mlogb=mlogb, wpr=ins.wpr, defaults={"number": ins.number, "mlogbw_from": ins})
|
Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f"{ins.number}-{i+1}", defaults={"mlogbw_from": ins})
|
||||||
elif route.process.mtype == Process.PRO_DIV:
|
Mlogbw.cal_count_notok(mlogbout)
|
||||||
for i in range(route.div_number):
|
elif material_out.tracking == Material.MA_TRACKING_BATCH:
|
||||||
Mlogbw.objects.get_or_create(mlogb=mlogb, number=f'{ins.number}-{i+1}', defaults={"mlogbw_from": ins})
|
number_from = mlogbout.number_from
|
||||||
Mlogbw.cal_count_notok(mlogb)
|
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:
|
||||||
|
Mlogbw.objects.get_or_create(mlogb=mlogb, wpr=ins.wpr, defaults={"number": ins.number, "mlogbw_from": ins})
|
||||||
|
elif route.process.mtype == Process.PRO_DIV:
|
||||||
|
for i in range(route.div_number):
|
||||||
|
Mlogbw.objects.get_or_create(mlogb=mlogb, number=f'{ins.number}-{i+1}', defaults={"mlogbw_from": ins})
|
||||||
|
Mlogbw.cal_count_notok(mlogb)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def perform_update(self, serializer):
|
def perform_update(self, serializer):
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Wpr(BaseModel):
|
||||||
wpr_from = models.ForeignKey("self", verbose_name="来源于", on_delete=models.CASCADE, null=True, blank=True)
|
wpr_from = models.ForeignKey("self", verbose_name="来源于", on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
@classmethod
|
@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:
|
if wpr is None and number is None:
|
||||||
raise ParseError("id和number不能同时为空")
|
raise ParseError("id和number不能同时为空")
|
||||||
if mb and wm:
|
if mb and wm:
|
||||||
|
@ -38,30 +38,44 @@ class Wpr(BaseModel):
|
||||||
wpr.delete()
|
wpr.delete()
|
||||||
return
|
return
|
||||||
elif number:
|
elif number:
|
||||||
try:
|
ins_x = cls.objects.filter(number=number).order_by("-version").first()
|
||||||
ins_x = cls.objects.get(number=number)
|
if ins_x:
|
||||||
if ins_x.wm is None and ins_x.mb is None:
|
if ins_x.wm is None and ins_x.mb is None:
|
||||||
if ins_x.version > 1: # 说明被复用了
|
if ins_x.version > 1: # 说明被复用了
|
||||||
if wpr_from is None:
|
if wpr_from is None:
|
||||||
wpr_from = ins_x
|
wpr_from = ins_x
|
||||||
|
# 创建新的wpr
|
||||||
|
ins = cls(number=number)
|
||||||
|
ins.version = -1
|
||||||
|
ins.oinfo = {}
|
||||||
else:
|
else:
|
||||||
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||||
else:
|
else:
|
||||||
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||||
except cls.DoesNotExist:
|
else:
|
||||||
ins = cls(number=number)
|
ins = cls(number=number)
|
||||||
ins.version = -1
|
ins.version = -1
|
||||||
ins.oinfo = {}
|
ins.oinfo = {}
|
||||||
if batch_from: # 尝试从批号追踪来源
|
# if batch_from: # 尝试从批号追踪来源
|
||||||
try:
|
# ins_from = cls.objects.filter(number=batch_from).order_by("-version").first()
|
||||||
ins_from = cls.objects.get(number=number)
|
# if ins_from:
|
||||||
if ins_from.wm is None and ins_from.mb is None:
|
# if ins_from.wm is None and ins_from.mb is None:
|
||||||
if ins_from.version > 1: # 说明被复用了
|
# if ins_from.version > 1: # 说明被复用了
|
||||||
wpr_from = ins_from
|
# wpr_from = ins_from
|
||||||
else:
|
# else:
|
||||||
raise ParseError(f"该物料编号{number}-已存在不可使用")
|
# raise ParseError(f"该物料编号{number}-已存在不可使用")
|
||||||
except cls.DoesNotExist:
|
# elif wpr_from is None:
|
||||||
pass
|
# 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:
|
if old_mb and ins.mb != old_mb:
|
||||||
raise ParseError(f"请检查-{ins.number}-所属仓库批次")
|
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
|
## 2.6.2025042311
|
||||||
- feat: 新增功能
|
- feat: 新增功能
|
||||||
- 切分工序切分数量支持1 [caoqianming]
|
- 切分工序切分数量支持1 [caoqianming]
|
||||||
|
|
|
@ -35,7 +35,7 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
|
|
||||||
SYS_NAME = '星途工厂综合管理系统'
|
SYS_NAME = '星途工厂综合管理系统'
|
||||||
SYS_VERSION = '2.6.2025042311'
|
SYS_VERSION = '2.6.2025043014'
|
||||||
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
X_FRAME_OPTIONS = 'SAMEORIGIN'
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
|
@ -44,7 +44,7 @@ urlpatterns = [
|
||||||
|
|
||||||
# api
|
# api
|
||||||
path('', include('apps.auth1.urls')),
|
path('', include('apps.auth1.urls')),
|
||||||
path('', include('apps.ichat.urls')),
|
# path('', include('apps.ichat.urls')),
|
||||||
path('', include('apps.system.urls')),
|
path('', include('apps.system.urls')),
|
||||||
path('', include('apps.monitor.urls')),
|
path('', include('apps.monitor.urls')),
|
||||||
path('', include('apps.wf.urls')),
|
path('', include('apps.wf.urls')),
|
||||||
|
|
Loading…
Reference in New Issue