diff --git a/apps/inm/services.py b/apps/inm/services.py index 9dfbfbf4..5e2c52b6 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -6,7 +6,7 @@ from apps.mtm.models import Material, Process from apps.utils.tools import ranstr from apps.utils.thread import MyThread from apps.mtm.services_2 import cal_material_count -from apps.wpm.models import WMaterial +from apps.wpm.models import WMaterial, BatchSt, BatchLog from apps.wpm.services_2 import get_alldata_with_batch_and_store from apps.wpmw.models import Wpr from apps.qm.models import Ftest, Defect @@ -236,10 +236,22 @@ class InmService: if instance.type == MIO.MIO_TYPE_PUR_IN: # 需要更新订单 # 这里还需要对入厂检验进行处理 + if is_reverse: + BatchLog.clear(mio=instance) + else: + batches = [item.batch for item in MIOItem.objects.filter(mio=instance)] + for item in batches: + BatchSt.create(batch=item, mio=instance) from apps.pum.services import PumService cls.update_mb(instance, in_or_out) PumService.mio_purin(instance, is_reverse) elif instance.type == MIO.MIO_TYPE_OTHER_IN: + if is_reverse: + BatchLog.clear(mio=instance) + else: + batches = [item.batch for item in MIOItem.objects.filter(mio=instance)] + for item in batches: + BatchSt.create(batch=item, mio=instance) cls.update_mb(instance, in_or_out) elif instance.type == MIO.MIO_TYPE_DO_IN: mioitems = MIOItem.objects.filter(mio=instance) @@ -319,6 +331,7 @@ class InmService: if xcount > 0: defect = defects_map[defect_id] m_list.append((material, warehouse, i.batch, xcount, defect, i)) + for material, warehouse, batch, change_count, defect, mioitem in m_list: if change_count <= 0: continue diff --git a/apps/wpm/migrations/0101_auto_20250321_1414.py b/apps/wpm/migrations/0101_auto_20250321_1414.py new file mode 100644 index 00000000..3d00f554 --- /dev/null +++ b/apps/wpm/migrations/0101_auto_20250321_1414.py @@ -0,0 +1,53 @@ +# Generated by Django 3.2.12 on 2025-03-21 06:14 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('inm', '0029_alter_mioitem_batch'), + ('wpm', '0100_auto_20250317_0955'), + ] + + operations = [ + migrations.AddField( + model_name='batchst', + name='handover', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.handover', verbose_name='由何交接记录创建'), + ), + migrations.AddField( + model_name='batchst', + name='mio', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='inm.mio', verbose_name='由何出入库记录创建'), + ), + migrations.AddField( + model_name='batchst', + name='mlog', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.mlog', verbose_name='由何日志创建'), + ), + migrations.AlterField( + model_name='batchst', + name='batch', + field=models.TextField(db_index=True, unique=True, verbose_name='批次号'), + ), + migrations.CreateModel( + name='BatchLog', + 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='删除标记')), + ('source_b', models.TextField(db_index=True, verbose_name='来源批次')), + ('target_b', models.TextField(db_index=True, verbose_name='目标批次')), + ('relation_type', models.CharField(default='split', help_text='split/merge', max_length=20, verbose_name='关联类型')), + ('handover', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.handover', verbose_name='关联交接记录')), + ('mlog', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='wpm.mlog', verbose_name='关联生产记录')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 2a99738b..4d1aedb1 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -13,15 +13,6 @@ from rest_framework.exceptions import ParseError from django.db.models import Count # Create your models here. - -class BatchSt(BaseModel): - """ - TN: 产品批次统计 - """ - batch = models.TextField("批次号") - last_time = models.DateTimeField("最后操作时间", null=True, blank=True) - data = models.JSONField("数据", default=list, blank=True) - class SfLog(CommonADModel): """TN: 值班记录 """ @@ -581,3 +572,64 @@ class OtherLog(CommonADModel): count_real = models.PositiveIntegerField('实际生产数', default=0) count_ok = models.PositiveIntegerField('合格数', default=0) count_delivered = models.PositiveIntegerField('交付数', default=0) + +class BatchSt(BaseModel): + """ + TN: 产品批次统计 + """ + batch = models.TextField("批次号", unique=True, db_index=True) + last_time = models.DateTimeField("最后操作时间", null=True, blank=True) + data = models.JSONField("数据", default=list, blank=True) + mio = models.ForeignKey("inm.mio", verbose_name="由何出入库记录创建", on_delete=models.CASCADE, null=True, blank=True) + handover = models.ForeignKey(Handover, verbose_name='由何交接记录创建', on_delete=models.CASCADE, null=True, blank=True) + mlog = models.ForeignKey(Mlog, verbose_name='由何日志创建', on_delete=models.CASCADE, null=True, blank=True) + + @classmethod + def create(cls, batch:str, mio=None, handover=None, mlog=None): + """ + 创建新的批次 + """ + try: + BatchSt.objects.get(batch=batch) + raise ParseError(f"{batch} 该批号不可使用") + except BatchSt.DoesNotExist: + if mio is None and handover is None and mlog is None: + raise ParseError("mio or handover or mlog must be provided") + BatchSt.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog) + + + + +class BatchLog(BaseModel): + """ + TN: 拆合批变更记录 + """ + # source = models.ForeignKey(BatchSt, verbose_name='来源批次', on_delete=models.CASCADE, related_name="batch_p") + # target = models.ForeignKey(BatchSt, verbose_name='目标批次', on_delete=models.CASCADE, related_name="batch_c") + source_b = models.TextField("来源批次", db_index=True) + target_b = models.TextField("目标批次", db_index=True) + handover = models.ForeignKey(Handover, verbose_name='关联交接记录', on_delete=models.CASCADE, null=True, blank=True) + mlog = models.ForeignKey(Mlog, verbose_name='关联生产记录', on_delete=models.CASCADE, null=True, blank=True) + relation_type = models.CharField('关联类型', max_length=20, help_text="split/merge", default="split") + + @classmethod + def g_create(cls, source_b:str, target_b:str=None, relation_type="split", handover=None, mlog=None): + """ + 创建新的关系 + """ + if relation_type not in ["split", "merge"]: + raise ParseError("relation_type must be split or merge") + if handover is None and mlog is None: + raise ParseError("handover or mlog must be provided") + cls.objects.get_or_create(source_b=source_b, target_b=target_b, relation_type=relation_type, handover=handover, mlog=mlog) + + @classmethod + def clear(cls, handover=None, mlog=None, mio=None): + if handover: + cls.objects.filter(handover=handover).delete() + BatchSt.objects.filter(handover=handover).delete() + if mlog: + cls.objects.filter(mlog=mlog).delete() + BatchSt.objects.filter(mlog=mlog).delete() + if mio: + BatchSt.objects.filter(mio=mio).delete() \ No newline at end of file diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 91076397..e017a5b4 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -11,7 +11,7 @@ from apps.system.models import User from apps.pm.models import Mtask from apps.mtm.models import Mgroup, Shift, Material, Route, RoutePack, Team, Srule -from .models import SfLog, WMaterial, Mlog, Mlogb, Mlogbw, Handover, Handoverb, Handoverbw, MlogbDefect +from .models import SfLog, WMaterial, Mlog, Mlogb, Mlogbw, Handover, Handoverb, Handoverbw, MlogbDefect, BatchLog, BatchSt from apps.mtm.services_2 import cal_material_count from apps.wf.models import Ticket from apps.utils.thread import MyThread @@ -166,6 +166,17 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): is_fix = mlog.is_fix m_ins_list = [] m_ins_bl_list = [] + + # 建立关系链 + m_outs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) + for item in m_outs: + if item.mlogb_from and item.batch != item.mlogb_from.batch: + BatchSt.create(batch=item.batch, mlog=mlog) + BatchLog.g_create(source_b=item.mlogb_from.batch, target_b=item.batch, mlog=mlog) + if item.mlogbw_from and item.batch != item.mlogbw_from.mlogb.batch: + BatchSt.create(batch=item.batch, mlog=mlog) + BatchLog.g_create(source_b=item.mlogbw_from.mlogb.batch, target_b=item.batch, mlog=mlog) + if material_in or is_fix: # 需要进行车间库存管理 m_ins_list = [] m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False) @@ -542,6 +553,9 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): # 更新物料数量 cal_material_count_from_mlog(mlog) + # 清除关系链 + BatchLog.clear(mlog=mlog) + # 触发批次统计分析 if mlog.batch: MyThread(target=get_alldata_with_batch_and_store, args=(mlog.batch,)).start() @@ -702,8 +716,12 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, # 合并为新批 if mtype == Handover.H_MERGE: batch = new_batch + BatchSt.create(batch=batch, handover=handover) + BatchLog.g_create(source_b=wm_from.batch, target_b=batch, handover=handover, relation_type="merge") elif mtype == Handover.H_DIV: batch = handover_or_b.batch + BatchSt.create(batch=batch, handover=handover) + BatchLog.g_create(source_b=handover.wm.batch, target_b=batch, handover=handover, relation_type="split") else: batch = wm_from.batch batches.append(batch) @@ -840,6 +858,7 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, MyThread(target=get_alldata_with_batch_and_store, args=(batch,)).start() def handover_revert(handover:Handover): + BatchLog.clear(handover=handover) pass def mlog_submit_validate(ins: Mlog):