This commit is contained in:
zty 2025-04-30 14:33:05 +08:00
commit a4ba33550e
19 changed files with 388 additions and 115 deletions

View File

@ -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,
},
),
]

View File

@ -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)

View File

@ -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__'

View File

@ -1,12 +1,13 @@
from django.urls import path, include from django.urls import path, include
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from apps.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)),
] ]

View File

@ -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

View File

@ -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):
""" """

View File

@ -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='个号转批号'),
),
]

View File

@ -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 = '工序'

View File

@ -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

View File

@ -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='来源于个号'),
),
]

View File

@ -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

View File

@ -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):

View File

@ -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:

View File

@ -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

View File

@ -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):

View File

@ -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}-所属仓库批次")

View File

@ -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]

View File

@ -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

View File

@ -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')),