From 8697288d69991dd5242d07695c464fa706fcba12 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sun, 27 Apr 2025 12:17:13 +0800 Subject: [PATCH 01/26] =?UTF-8?q?feat:=20mlogbwcreate=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0wm=E6=89=80=E5=B1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 8edd22c3..f85cce27 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -799,8 +799,11 @@ class MlogbwCreateUpdateSerializer(CustomModelSerializer): in_or_out, tracking = mlogb.get_tracking() if tracking != Material.MA_TRACKING_SINGLE: raise ParseError('非单件追踪不可使用') - if in_or_out == 'in' and not wpr: - raise ParseError('请选择相应产品') + if in_or_out == 'in': + if not wpr: + raise ParseError('请选择相应产品') + if mlogb.wm_in and wpr.wm != mlogb.wm_in: + raise ParseError(f'{wpr.number}-该产品非本批次') return attrs def save_ftest(self, mlogbw, ftest_data): From 8a83f8b7d80f5a7fdd796311076fc7bba45e6498 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sun, 27 Apr 2025 13:41:45 +0800 Subject: [PATCH 02/26] =?UTF-8?q?fix:=20cal=5Fcount=5Fnotok=E9=94=81?= =?UTF-8?q?=E5=AE=9Amlogb=E4=BB=A5=E9=98=B2=E6=AD=A2=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/wpm/models.py b/apps/wpm/models.py index cf57b70a..31337a0f 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -466,6 +466,8 @@ class Mlogbw(BaseModel): @classmethod def cal_count_notok(cls, mlogb: Mlog): from apps.qm.models import Defect + # 锁定mlogb以防止并发修改 + mlogb = Mlogb.objects.select_for_update().get(pk=mlogb.pk) count = Mlogbw.objects.filter(mlogb=mlogb).count() if mlogb.material_in: mlogb.count_use = count From cd0045c56165f07ed29d264af9bca4f60388bed4 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sun, 27 Apr 2025 14:12:37 +0800 Subject: [PATCH 03/26] =?UTF-8?q?feat:=20mlogbw=E6=94=AF=E6=8C=81=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/views.py | 79 +++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 653bab38..a8b81b38 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -711,43 +711,48 @@ class MlogbwViewSet(CustomModelViewSet): @transaction.atomic def perform_create(self, serializer): ins:Mlogbw = serializer.save() - route:Route = ins.mlogb.mlog.route - mlogb:Mlogb = ins.mlogb - Mlogbw.cal_count_notok(mlogb) - # 如果是输入且输出追踪到个,需同步创建 - material_in:Material = mlogb.material_in - if material_in is not None: - mlogb_qs = Mlogb.objects.filter(mlogb_from=mlogb) - material_out:Material = mlogb.mlog.material_out - mtype = route.process.mtype if route.process else None - if mtype == Process.PRO_DIV: - mlogbin = ins.mlogb - wm_in = mlogbin.wm_in - mlog = mlogbin.mlog - div_number = route.div_number - m_dict = { - "mtask": mlogbin.mtask, - "mlog": mlog, - "batch": ins.number, - "material_out": material_out, - "batch_ofrom": wm_in.batch_ofrom, - "material_ofrom": wm_in.material_ofrom, - "count_real": div_number, - "count_ok": div_number, "qct": mlog.qct - } - mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=ins, defaults=m_dict) - if material_out.tracking == Material.MA_TRACKING_SINGLE: - for i in range(div_number): - Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f"{ins.number}-{i+1}", defaults={"mlogbw_from": ins}) - Mlogbw.cal_count_notok(mlogbout) - elif 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) + if isinstance(ins, list): + insx = ins + else: + insx = [ins] + for ins in insx: + route:Route = ins.mlogb.mlog.route + mlogb:Mlogb = ins.mlogb + Mlogbw.cal_count_notok(mlogb) + # 如果是输入且输出追踪到个,需同步创建 + material_in:Material = mlogb.material_in + if material_in is not None: + mlogb_qs = Mlogb.objects.filter(mlogb_from=mlogb) + material_out:Material = mlogb.mlog.material_out + mtype = route.process.mtype if route.process else None + if mtype == Process.PRO_DIV: + mlogbin = ins.mlogb + wm_in = mlogbin.wm_in + mlog = mlogbin.mlog + div_number = route.div_number + m_dict = { + "mtask": mlogbin.mtask, + "mlog": mlog, + "batch": ins.number, + "material_out": material_out, + "batch_ofrom": wm_in.batch_ofrom, + "material_ofrom": wm_in.material_ofrom, + "count_real": div_number, + "count_ok": div_number, "qct": mlog.qct + } + mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=ins, defaults=m_dict) + if material_out.tracking == Material.MA_TRACKING_SINGLE: + for i in range(div_number): + Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f"{ins.number}-{i+1}", defaults={"mlogbw_from": ins}) + Mlogbw.cal_count_notok(mlogbout) + elif 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 def perform_update(self, serializer): From 5d94f9615ce293c9bdd541448c3906c87597a225 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 08:17:15 +0800 Subject: [PATCH 04/26] =?UTF-8?q?fix:=20wpr=E5=A4=8D=E7=94=A8number?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpmw/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/wpmw/models.py b/apps/wpmw/models.py index 2c0df756..0928dd6c 100644 --- a/apps/wpmw/models.py +++ b/apps/wpmw/models.py @@ -39,11 +39,11 @@ class Wpr(BaseModel): return elif number: try: - ins_x = cls.objects.get(number=number) - if ins_x.wm is None and ins_x.mb is None: - if ins_x.version > 1: # 说明被复用了 + ins = cls.objects.get(number=number) + if ins.wm is None and ins.mb is None: + if ins.version > 1: # 说明被复用了 if wpr_from is None: - wpr_from = ins_x + wpr_from = ins else: raise ParseError(f"该物料编号{number}-已存在不可使用") else: From a0346f00bf00fb748159ad5349fc4722e7a74591 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 09:17:01 +0800 Subject: [PATCH 05/26] =?UTF-8?q?fix:=20wpr=E5=A4=8D=E7=94=A8number?= =?UTF-8?q?=E7=9A=84bug2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpmw/models.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/wpmw/models.py b/apps/wpmw/models.py index 2c0df756..7ca50929 100644 --- a/apps/wpmw/models.py +++ b/apps/wpmw/models.py @@ -38,30 +38,34 @@ class Wpr(BaseModel): wpr.delete() return elif number: - try: - ins_x = cls.objects.get(number=number) + ins_x = cls.objects.filter(number=number).order_by("-version").first() + if ins_x: if ins_x.wm is None and ins_x.mb is None: if ins_x.version > 1: # 说明被复用了 if wpr_from is None: wpr_from = ins_x + # 创建新的wpr + ins = cls(number=number) + ins.version = -1 + ins.oinfo = {} else: raise ParseError(f"该物料编号{number}-已存在不可使用") else: raise ParseError(f"该物料编号{number}-已存在不可使用") - except cls.DoesNotExist: + else: ins = cls(number=number) ins.version = -1 ins.oinfo = {} if batch_from: # 尝试从批号追踪来源 - try: - ins_from = cls.objects.get(number=number) + ins_from = cls.objects.filter(number=batch_from).order_by("-version").first() + if ins_from: if ins_from.wm is None and ins_from.mb is None: if ins_from.version > 1: # 说明被复用了 wpr_from = ins_from else: raise ParseError(f"该物料编号{number}-已存在不可使用") - except cls.DoesNotExist: - pass + elif wpr_from is None: + raise ParseError(f"该物料编号{number}-尝试从批号追踪来源失败") if old_mb and ins.mb != old_mb: raise ParseError(f"请检查-{ins.number}-所属仓库批次") From 9d634940d31d21c8af1144882881c72aef4c2020 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 10:24:28 +0800 Subject: [PATCH 06/26] =?UTF-8?q?feat:=20mlog=E7=BB=93=E5=90=88=E5=B7=A5?= =?UTF-8?q?=E5=BA=8F=E6=94=AF=E6=8C=81=E6=89=B9=E5=88=B0=E4=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/views.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index a8b81b38..3c17d181 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -635,13 +635,20 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": d_count_real, "count_ok": d_count_ok})) mlogbout.count_json_from = mlogbin.count_json_from mlogbout.save(update_fields=["count_json_from"]) - elif mtype == Process.PRO_MERGE: # 支持批到批 + elif mtype == Process.PRO_MERGE: # 支持批到批,批到个 xcount = math.floor( mlogbin.count_use / route.div_number) d_count_real = xcount d_count_ok = xcount mlogbout, _ = Mlogb.objects.get_or_create(mlogb_from=mlogbin, defaults=update_dict(m_dict, {"count_real": d_count_real, "count_ok": d_count_ok})) mlogbout.count_json_from = mlogbin.count_json_from mlogbout.save(update_fields=["count_json_from"]) + if material_out.tracking == Material.MA_TRACKING_SINGLE: + number = mlogbin.batch + if d_count_real == 1: + Mlogbw.objects.get_or_create(number=number, mlogb=mlogbout) + else: + for i in range(d_count_real): + Mlogbw.objects.get_or_create(number=f'{number}-{i+1}', mlogb=mlogbout) elif is_fix:# 支持批到批,个到个 d_count_real = mlogbin.count_use d_count_ok = mlogbin.count_use From 76adab157168b08f3d67894eea83593b8d49360d Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 11:08:47 +0800 Subject: [PATCH 07/26] =?UTF-8?q?feat:=20=E6=89=B9=E6=AC=A1=E8=BF=BD?= =?UTF-8?q?=E8=B8=AA=E9=93=BE=E6=9D=A1=E8=BF=98=E6=98=AF=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E5=A4=8D=E7=94=A8=E6=89=B9=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/models.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 31337a0f..6491731b 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -633,6 +633,8 @@ class BatchSt(BaseModel): return node, False else: version = 1 + if mio is None and handover is None and mlog is None: + raise ParseError("mio or handover or mlog must be provided") # 带有来源的批次获取,需检查批次号是否可用 if cls.objects.filter(batch=batch).exists(): if reuse_node: @@ -641,11 +643,8 @@ class BatchSt(BaseModel): raise ParseError(f"{batch}-该批次号因物料不同不可引用") return node, False else: - raise ParseError(f"{batch}-该批次号不可使用") - # latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"] - # version = latest_version + 1 - if mio is None and handover is None and mlog is None: - raise ParseError("mio or handover or mlog must be provided") + latest_version = BatchSt.objects.filter(batch=batch).aggregate(Max("version"))["version__max"] + version = latest_version + 1 ins = cls.objects.create(batch=batch, mio=mio, handover=handover, mlog=mlog, material_start=material_start, version=version) return ins, True From 76a48d46eec49b6ed86a93f1a63f4f8700d75510 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 11:28:45 +0800 Subject: [PATCH 08/26] =?UTF-8?q?feat:=20=E4=B8=AA=E5=88=B0=E6=89=B9?= =?UTF-8?q?=E6=97=B6=E6=94=AF=E6=8C=81=E8=8B=A5=E6=98=AF=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E8=BF=98=E6=98=AF=E6=B2=BF=E7=94=A8=E5=8E=9F=E6=9C=89=E6=89=B9?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/views.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 3c17d181..ab2b8546 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -614,9 +614,15 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust elif mtype == Process.PRO_DIV: # 切分 支持批到批,个到个, 个到批 div_number = route.div_number if material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_BATCH: - for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"): - m_dict["batch"] = mlogbwin.number - Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number})) + if Mlogbw.objects.filter(mlogb=mlogbin).count() == 1: + mlogbwin = Mlogbw.objects.filter(mlogb=mlogbin).first() + mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": div_number, "count_ok": div_number})) + mlogbout.count_json_from = mlogbin.count_json_from + mlogbout.save(update_fields=["count_json_from"]) + else: + for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"): + m_dict["batch"] = mlogbwin.number + Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number})) elif material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_SINGLE: d_count_real = mlogbin.count_use * div_number d_count_ok = d_count_real From 849e63da3eb2811a5198efc8bc1506c3ba24a4f6 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 13:48:22 +0800 Subject: [PATCH 09/26] =?UTF-8?q?feat:=20=E5=B0=9D=E8=AF=95=E4=BB=8E?= =?UTF-8?q?=E4=B8=AA=E5=8F=B7=E8=BF=BD=E8=B8=AA=E5=88=B0=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=9A=84=E4=B8=AA=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/models.py | 2 ++ apps/wpm/services.py | 11 ++++++++++- apps/wpm/views.py | 2 ++ apps/wpmw/models.py | 12 +++++++++++- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/apps/wpm/models.py b/apps/wpm/models.py index 6491731b..f6e306dd 100644 --- a/apps/wpm/models.py +++ b/apps/wpm/models.py @@ -118,6 +118,7 @@ class WMaterial(CommonBDModel): count_xtest = models.DecimalField('已检数量', null=True, blank=True, max_digits=11, decimal_places=1) batch_ofrom = models.TextField('原料批次号', null=True, blank=True) material_ofrom = models.ForeignKey(Material, verbose_name='原料物料', on_delete=models.SET_NULL, null=True, blank=True, related_name='wm_mofrom') + number_from = models.TextField("来源于个号", null=True, blank=True) @property def count_working(self): @@ -382,6 +383,7 @@ class Mlogb(BaseModel): test_user = models.ForeignKey( User, verbose_name='抽检人', on_delete=models.CASCADE, null=True, blank=True, related_name='mlogb_test_user') need_inout = models.BooleanField('是否需要出入库', default=True) + number_from = models.TextField('来源个编号', null=True, blank=True) def get_tracking(self): diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 825fb63d..a5126879 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -340,6 +340,10 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): elif isinstance(mlog_or_b, Mlogb): wm.batch_ofrom = mlog_or_b.batch_ofrom wm.material_ofrom = mlog_or_b.material_ofrom + if isinstance(mlog_or_b, Mlogb): + if mlog_or_b.number_from and wm.number_from is not None: + raise ParseError(f'{wm.batch}-该批号现有库存来源于个号{wm.number_from}') + wm.number_from = mlog_or_b.number_from wm.save() if mo_ma.tracking == Material.MA_TRACKING_SINGLE: if notok_sign_or_defect: @@ -356,9 +360,11 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): wpr_from = None if item.mlogbw_from: wpr_from = item.mlogbw_from.wpr + number_from = item.mlogb.number_from + batch_from = item.mlogb.batch if number_from else None wpr = Wpr.change_or_new(number=item.number, wm=wm, ftest=item.ftest, - wpr_from=wpr_from, batch_from=item.mlogb.batch) + wpr_from=wpr_from, batch_from=batch_from, number_from=number_from) item.wpr = wpr item.save() @@ -481,6 +487,9 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if wm.count < 0: raise ParseError('车间库存不足, 产物无法回退') elif wm.count >= 0: + if isinstance(mlog_or_b, Mlogb): + if mlog_or_b.number_from == wm.number_from: + wm.number_from = None wm.update_by = user wm.save() if mo_ma.tracking == Material.MA_TRACKING_SINGLE: diff --git a/apps/wpm/views.py b/apps/wpm/views.py index ab2b8546..fcc012f7 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -618,8 +618,10 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust mlogbwin = Mlogbw.objects.filter(mlogb=mlogbin).first() mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": div_number, "count_ok": div_number})) mlogbout.count_json_from = mlogbin.count_json_from + mlogbout.number_from = mlogbwin.number mlogbout.save(update_fields=["count_json_from"]) else: + # 用个号做批号是用于后续在复用个号可以追踪到原先的个 for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"): m_dict["batch"] = mlogbwin.number Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number})) diff --git a/apps/wpmw/models.py b/apps/wpmw/models.py index 7ca50929..3f7745c0 100644 --- a/apps/wpmw/models.py +++ b/apps/wpmw/models.py @@ -26,7 +26,7 @@ class Wpr(BaseModel): wpr_from = models.ForeignKey("self", verbose_name="来源于", on_delete=models.CASCADE, null=True, blank=True) @classmethod - def change_or_new(cls, wpr=None, number=None, mb=None, wm=None, old_mb=None, old_wm=None, ftest=None, wpr_from=None, batch_from=None): + def change_or_new(cls, wpr=None, number=None, mb=None, wm=None, old_mb=None, old_wm=None, ftest=None, wpr_from=None, batch_from=None, number_from=None): if wpr is None and number is None: raise ParseError("id和number不能同时为空") if mb and wm: @@ -66,6 +66,16 @@ class Wpr(BaseModel): raise ParseError(f"该物料编号{number}-已存在不可使用") elif wpr_from is None: raise ParseError(f"该物料编号{number}-尝试从批号追踪来源失败") + elif number_from: # 尝试从编号追踪来源 + ins_from = cls.objects.filter(number=number_from).order_by("-version").first() + if ins_from: + if ins_from.wm is None and ins_from.mb is None: + if ins_from.version > 1: # 说明被复用了 + wpr_from = ins_from + else: + raise ParseError(f"该物料编号{number}-已存在不可使用") + elif wpr_from is None: + raise ParseError(f"该物料编号{number}-尝试历史编号追踪来源失败") if old_mb and ins.mb != old_mb: raise ParseError(f"请检查-{ins.number}-所属仓库批次") From 25a111bc6bdfb3959ac51af3daccee0cb58590e0 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 13:51:00 +0800 Subject: [PATCH 10/26] =?UTF-8?q?feat:=20=E5=90=8C=E6=AD=A5=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wpm/migrations/0114_auto_20250428_1350.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 apps/wpm/migrations/0114_auto_20250428_1350.py diff --git a/apps/wpm/migrations/0114_auto_20250428_1350.py b/apps/wpm/migrations/0114_auto_20250428_1350.py new file mode 100644 index 00000000..34924007 --- /dev/null +++ b/apps/wpm/migrations/0114_auto_20250428_1350.py @@ -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='来源于个号'), + ), + ] From c5a7a19f745be76a9f64b64cec551ed94b1327ba Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 14:12:00 +0800 Subject: [PATCH 11/26] =?UTF-8?q?feat:=20=E5=B0=9D=E8=AF=95=E4=BB=8E?= =?UTF-8?q?=E4=B8=AA=E5=8F=B7=E8=BF=BD=E8=B8=AA=E5=88=B0=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=9A=84=E4=B8=AA=E5=8F=B72?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services.py | 15 ++++++++++----- apps/wpm/views.py | 7 ++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index a5126879..c4274799 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -802,7 +802,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, defaults={ "batch_ofrom": wm_from.batch_ofrom, "material_ofrom": wm_from.material_ofrom, - "create_by": user + "create_by": user, + "number_from": wm_from.number_from } ) elif handover.type == Handover.H_REPAIR: @@ -825,7 +826,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, defaults={ "batch_ofrom": wm_from.batch_ofrom, "material_ofrom": wm_from.material_ofrom, - "create_by": user + "create_by": user, + "number_from": wm_from.number_from } ) else: @@ -857,7 +859,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, defaults={ "batch_ofrom": wm_from.batch_ofrom, "material_ofrom": wm_from.material_ofrom, - "create_by": user + "create_by": user, + "number_from": wm_from.number_from } ) else: @@ -876,7 +879,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, defaults={ "batch_ofrom": wm_from.batch_ofrom, "material_ofrom": wm_from.material_ofrom, - "create_by": user + "create_by": user, + "number_from": wm_from.number_from } ) else: @@ -896,7 +900,8 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, defaults={ "batch_ofrom": wm_from.batch_ofrom, "material_ofrom": wm_from.material_ofrom, - "create_by": user + "create_by": user, + "number_from": wm_from.number_from } ) else: diff --git a/apps/wpm/views.py b/apps/wpm/views.py index fcc012f7..2277e10e 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -617,14 +617,15 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust if Mlogbw.objects.filter(mlogb=mlogbin).count() == 1: mlogbwin = Mlogbw.objects.filter(mlogb=mlogbin).first() mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": div_number, "count_ok": div_number})) - mlogbout.count_json_from = mlogbin.count_json_from mlogbout.number_from = mlogbwin.number - mlogbout.save(update_fields=["count_json_from"]) + mlogbout.save(update_fields=["number_from"]) else: # 用个号做批号是用于后续在复用个号可以追踪到原先的个 for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"): m_dict["batch"] = mlogbwin.number - Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number})) + mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, defaults=update_dict(m_dict, {"count_real": div_number, "count_ok": div_number})) + mlogbout.number_from = mlogbwin.number + mlogbout.save(update_fields=["number_from"]) elif material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_SINGLE: d_count_real = mlogbin.count_use * div_number d_count_ok = d_count_real From 0ff9aa1e5a23aa9bdfed1a998e39750fc362fd38 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 14:43:22 +0800 Subject: [PATCH 12/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=AA?= =?UTF-8?q?=E5=8F=B7=E8=BD=AC=E6=89=B9=E5=8F=B7=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../0057_process_number_to_batch.py | 18 +++++++++++++++++ apps/mtm/models.py | 2 ++ apps/wpm/views.py | 20 +++++++++---------- 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 apps/mtm/migrations/0057_process_number_to_batch.py diff --git a/apps/mtm/migrations/0057_process_number_to_batch.py b/apps/mtm/migrations/0057_process_number_to_batch.py new file mode 100644 index 00000000..fa8017f0 --- /dev/null +++ b/apps/mtm/migrations/0057_process_number_to_batch.py @@ -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='个号转批号'), + ), + ] diff --git a/apps/mtm/models.py b/apps/mtm/models.py index c432a023..c1dde7f9 100644 --- a/apps/mtm/models.py +++ b/apps/mtm/models.py @@ -16,6 +16,7 @@ class Process(CommonBModel): PRO_NORMAL = 10 PRO_DIV = 20 PRO_MERGE = 30 + name = models.CharField('工序名称', max_length=100) type = models.PositiveSmallIntegerField("工序类型", default=PRO_PROD, choices=((PRO_PROD, '生产工序'), (PRO_TEST, '检验工序'))) mtype = models.PositiveSmallIntegerField("工序生产类型", default=PRO_NORMAL, choices=((PRO_NORMAL, '常规'), (PRO_DIV, '切分'), (PRO_MERGE, '合并'))) @@ -30,6 +31,7 @@ class Process(CommonBModel): mlog_need_ticket = models.BooleanField('日志提交是否需要审批', default=False) mstate_json = models.JSONField('中间状态', default=list, blank=True) parent = models.ForeignKey('self', verbose_name='父工序', on_delete=models.CASCADE, null=True, blank=True) + number_to_batch = models.BooleanField('个号转批号', default=False) class Meta: verbose_name = '工序' diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 2277e10e..bfb8f8ff 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -563,6 +563,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust mlogbin: Mlogb = serializer.save() mlog:Mlog = mlogbin.mlog route:Route = mlog.route + process: Process = route.process if route else None mtype = route.process.mtype if route else None is_fix = mlog.is_fix qct = mlog.qct @@ -614,18 +615,17 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust elif mtype == Process.PRO_DIV: # 切分 支持批到批,个到个, 个到批 div_number = route.div_number if material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_BATCH: - if Mlogbw.objects.filter(mlogb=mlogbin).count() == 1: - mlogbwin = Mlogbw.objects.filter(mlogb=mlogbin).first() - mlogbout, _ = Mlogb.objects.get_or_create(mlogbw_from=mlogbwin, mlogb_from=mlogbin, defaults=update_dict(m_dict,{"count_real": div_number, "count_ok": div_number})) + lenx = Mlogbw.objects.filter(mlogb=mlogbin).count() + # 用个号做批号是用于后续在复用个号可以追踪到原先的个 + for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("number"): + if process and process.number_to_batch: + m_dict["batch"] = mlogbwin.number + 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(update_fields=["number_from"]) - else: - # 用个号做批号是用于后续在复用个号可以追踪到原先的个 - for mlogbwin in Mlogbw.objects.filter(mlogb=mlogbin).order_by("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})) - mlogbout.number_from = mlogbwin.number - mlogbout.save(update_fields=["number_from"]) elif material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_SINGLE: d_count_real = mlogbin.count_use * div_number d_count_ok = d_count_real From 7338d09f5d26e97cc6551a299a3e399ad7afddfc Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 15:25:41 +0800 Subject: [PATCH 13/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=AA?= =?UTF-8?q?=E5=8F=B7=E8=BD=AC=E6=89=B9=E5=8F=B7=E7=9A=84=E9=85=8D=E7=BD=AE?= =?UTF-8?q?2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index bfb8f8ff..d318a8da 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -625,7 +625,7 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust if lenx == 1: mlogbout.mlogb_from = mlogbin mlogbout.number_from = mlogbwin.number - mlogbout.save(update_fields=["number_from"]) + mlogbout.save() elif material_in.tracking == Material.MA_TRACKING_SINGLE and material_out.tracking == Material.MA_TRACKING_SINGLE: d_count_real = mlogbin.count_use * div_number d_count_ok = d_count_real From 7131697802b9335bd200046a991a634c0b13f462 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 15:32:04 +0800 Subject: [PATCH 14/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=AA?= =?UTF-8?q?=E5=8F=B7=E8=BD=AC=E6=89=B9=E5=8F=B73?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/views.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index d318a8da..de69f873 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -761,6 +761,15 @@ class MlogbwViewSet(CustomModelViewSet): for i in range(div_number): Mlogbw.objects.get_or_create(mlogb=mlogbout, number=f"{ins.number}-{i+1}", defaults={"mlogbw_from": ins}) Mlogbw.cal_count_notok(mlogbout) + elif material_out.tracking == Material.MA_TRACKING_BATCH: + number_from = mlogbout.number_from + if number_from is None: + mlogbout.number_from = ins.number + mlogbout.save() + elif number_from == ins.number: + pass + else: + raise ParseError("该个号不可产生该批") elif mlogb_qs.exists() and material_out.tracking == Material.MA_TRACKING_SINGLE: for mlogb in mlogb_qs: if route.process.mtype == Process.PRO_NORMAL: From 6b8fa4e58c0358d8afd43e377fd3e4e5d814a83e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 15:45:05 +0800 Subject: [PATCH 15/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=AA?= =?UTF-8?q?=E5=8F=B7=E8=BD=AC=E6=89=B9=E5=8F=B74?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services.py | 4 +--- apps/wpm/views.py | 2 ++ apps/wpmw/models.py | 42 +++++++++++++++++++++--------------------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index c4274799..8add9df3 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -360,11 +360,9 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): wpr_from = None if item.mlogbw_from: wpr_from = item.mlogbw_from.wpr - number_from = item.mlogb.number_from - batch_from = item.mlogb.batch if number_from else None wpr = Wpr.change_or_new(number=item.number, wm=wm, ftest=item.ftest, - wpr_from=wpr_from, batch_from=batch_from, number_from=number_from) + wpr_from=wpr_from) item.wpr = wpr item.save() diff --git a/apps/wpm/views.py b/apps/wpm/views.py index de69f873..67c49ed1 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -653,6 +653,8 @@ class MlogbInViewSet(CreateModelMixin, UpdateModelMixin, DestroyModelMixin, Cust 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: diff --git a/apps/wpmw/models.py b/apps/wpmw/models.py index 3f7745c0..f1121708 100644 --- a/apps/wpmw/models.py +++ b/apps/wpmw/models.py @@ -26,7 +26,7 @@ class Wpr(BaseModel): wpr_from = models.ForeignKey("self", verbose_name="来源于", on_delete=models.CASCADE, null=True, blank=True) @classmethod - def change_or_new(cls, wpr=None, number=None, mb=None, wm=None, old_mb=None, old_wm=None, ftest=None, wpr_from=None, batch_from=None, number_from=None): + def change_or_new(cls, wpr=None, number=None, mb=None, wm=None, old_mb=None, old_wm=None, ftest=None, wpr_from=None): if wpr is None and number is None: raise ParseError("id和number不能同时为空") if mb and wm: @@ -56,26 +56,26 @@ class Wpr(BaseModel): ins = cls(number=number) ins.version = -1 ins.oinfo = {} - if batch_from: # 尝试从批号追踪来源 - ins_from = cls.objects.filter(number=batch_from).order_by("-version").first() - if ins_from: - if ins_from.wm is None and ins_from.mb is None: - if ins_from.version > 1: # 说明被复用了 - wpr_from = ins_from - else: - raise ParseError(f"该物料编号{number}-已存在不可使用") - elif wpr_from is None: - raise ParseError(f"该物料编号{number}-尝试从批号追踪来源失败") - elif number_from: # 尝试从编号追踪来源 - ins_from = cls.objects.filter(number=number_from).order_by("-version").first() - if ins_from: - if ins_from.wm is None and ins_from.mb is None: - if ins_from.version > 1: # 说明被复用了 - wpr_from = ins_from - else: - raise ParseError(f"该物料编号{number}-已存在不可使用") - elif wpr_from is None: - raise ParseError(f"该物料编号{number}-尝试历史编号追踪来源失败") + # if batch_from: # 尝试从批号追踪来源 + # ins_from = cls.objects.filter(number=batch_from).order_by("-version").first() + # if ins_from: + # if ins_from.wm is None and ins_from.mb is None: + # if ins_from.version > 1: # 说明被复用了 + # wpr_from = ins_from + # else: + # raise ParseError(f"该物料编号{number}-已存在不可使用") + # elif wpr_from is None: + # raise ParseError(f"该物料编号{number}-尝试从批号追踪来源失败") + # elif number_from: # 尝试从编号追踪来源 + # ins_from = cls.objects.filter(number=number_from).order_by("-version").first() + # if ins_from: + # if ins_from.wm is None and ins_from.mb is None: + # if ins_from.version > 1: # 说明被复用了 + # wpr_from = ins_from + # else: + # raise ParseError(f"该物料编号{number}-已存在不可使用") + # elif wpr_from is None: + # raise ParseError(f"该物料编号{number}-尝试历史编号追踪来源失败") if old_mb and ins.mb != old_mb: raise ParseError(f"请检查-{ins.number}-所属仓库批次") From ced3e7a16c5c69676d520393865ed345ec156455 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 15:50:36 +0800 Subject: [PATCH 16/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=AA?= =?UTF-8?q?=E5=8F=B7=E8=BD=AC=E6=89=B9=E5=8F=B75?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 8add9df3..48df5540 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -341,7 +341,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): wm.batch_ofrom = mlog_or_b.batch_ofrom wm.material_ofrom = mlog_or_b.material_ofrom if isinstance(mlog_or_b, Mlogb): - if mlog_or_b.number_from and wm.number_from is not None: + 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() From e59c83aa3fca02c31a076a926b01ca84f748c19e Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 15:59:29 +0800 Subject: [PATCH 17/26] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E4=B8=AA?= =?UTF-8?q?=E5=8F=B7=E8=BD=AC=E6=89=B9=E5=8F=B76?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index f85cce27..fe1a62c1 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -724,6 +724,7 @@ class MlogbInSerializer(CustomModelSerializer): attrs['batch'] = wm_in.batch attrs["batch_ofrom"] = wm_in.batch_ofrom attrs["material_ofrom"] = wm_in.material_ofrom + attrs["number_from"] = wm_in.number_from if route and route.batch_bind and mtask is not None: if not WMaterial.mat_in_qs(mtask).filter(id=wm_in.id).exists(): raise ParseError('该车间库存非本任务使用') From 325fad0a6e8a6842fdb9fc4837d2ae612bc3f5f4 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 28 Apr 2025 16:22:40 +0800 Subject: [PATCH 18/26] release: 2.6.2025042816 --- changelog.md | 30 ++++++++++++++++++++++++++++++ server/settings.py | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 9b22aba2..191b2f20 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,33 @@ +## 2.6.2025042816 +- feat: 新增功能 + - 支持个号转批号及配置 [caoqianming] + - 批次追踪链条还是可以复用批次 [caoqianming] + - mlog结合工序支持批到个 [caoqianming] + - mlogbw支持批量创建 [caoqianming] + - mlogbwcreate校验增加wm所属 [caoqianming] + - ichat 修改LLM 的接口 [zty] + - wmaterial筛选条件优化来料已完成 [caoqianming] + - 获取该批次的dag数据需要传入version [caoqianming] + - 单填写mlog支持返工 [caoqianming] + - ichat 修改大模型接口 [zty] + - mlogbupdate支持变更批号 [caoqianming] + - 6车间合格率统计decimal invalid [caoqianming] + - 修改大模型文件 [zty] + - model add note [zty] + - ichat添加表 [caoqianming] + - 缺陷项分类字段数据库约束放开 [caoqianming] + - 通过django settings延迟获取BASE_PROJECT_CODE [caoqianming] + - base 将配置文件放到单独的config文件夹中防止误操作 [caoqianming] +- fix: 问题修复 + - wpr复用number的bug [caoqianming] + - cal_count_notok锁定mlogb以防止并发修改 [caoqianming] + - 获取batchst时默认使用version=1 [caoqianming] + - mloginit在返工时不接收route [caoqianming] + - 生产入库时存入生产车间 [caoqianming] + - 返工校验输入物料选择错误 [caoqianming] + - 完善负数校验 [caoqianming] + - mlogbupdate时attrs遍历时修改的bug [caoqianming] + - batchst的version字段bug [caoqianming] ## 2.6.2025042311 - feat: 新增功能 - 切分工序切分数量支持1 [caoqianming] diff --git a/server/settings.py b/server/settings.py index cdfee897..0a53ee2d 100755 --- a/server/settings.py +++ b/server/settings.py @@ -35,7 +35,7 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) ALLOWED_HOSTS = ['*'] SYS_NAME = '星途工厂综合管理系统' -SYS_VERSION = '2.6.2025042311' +SYS_VERSION = '2.6.2025042816' X_FRAME_OPTIONS = 'SAMEORIGIN' # Application definition From 7c752ec39b2b66756713e1c57859811d5a80cdd0 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 29 Apr 2025 14:42:29 +0800 Subject: [PATCH 19/26] =?UTF-8?q?feat:=20get=5Falldata=5Fwith=5Fbatch?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services_2.py | 71 ++++++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/apps/wpm/services_2.py b/apps/wpm/services_2.py index 66bfea14..1865ccf8 100644 --- a/apps/wpm/services_2.py +++ b/apps/wpm/services_2.py @@ -12,7 +12,7 @@ import decimal import logging myLogger = logging.getLogger('log') -def get_alldata_with_batch_and_store(batch: str): +def get_alldata_with_batch_and_store(batch: str, need_update_time=True): """ 获取某个批次的整体生产数据并保存 """ @@ -29,7 +29,8 @@ def get_alldata_with_batch_and_store(batch: str): bobj, _ = BatchSt.objects.get_or_create(batch=batch, defaults={ "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.save() @@ -278,23 +279,6 @@ def get_alldata_with_batch(batch: str): myLogger.error(f"六车间_{mgroup_name}_合格率decimal.InvalidOperation-{data}") data[f'六车间_{mgroup_name}_合格率'] = 0 - # 六车间入库/检验数据 - # mioitem6_qs2 = MIOItem.objects.filter(mio__belong_dept=dept6, mio__type="do_in", - # batch=batch, - # mio__submit_time__isnull=False) - # if mioitem6_qs2.exists(): - # data["六车间生产入库_日期"] = [] - # for item in mioitem6_qs: - # data["六车间生产入库_日期"].append(item.mio.inout_date) - # for field in mioitem_count_fields: - # if getattr(item, field) > 0: - # if f'六车间生产入库_{field}' not in data: - # data[f'六车间生产入库_{field}'] = getattr(item, field) - # else: - # data[f'六车间生产入库_{field}'] += getattr(item, field) - # data["六车间生产入库_日期"] = list(set(data["六车间生产入库_日期"])) - # data["六车间生产入库_日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_日期"]]) - ftestwork_count_fields = FtestWork.count_fields() # 六车间中检数据 ftestwork_qs = FtestWork.objects.filter(batch=batch, type="process") @@ -325,6 +309,35 @@ def get_alldata_with_batch(batch: str): data['六车间中检_检验人'] = list(set(data['六车间中检_检验人'])) data['六车间中检_检验人'] = ";".join([item.name for item in data['六车间中检_检验人']]) + # 六车间入库/检验数据 + mioitem6_qs2 = MIOItem.objects.filter(mio__belong_dept=dept6, mio__type="do_in", + batch=batch, + mio__submit_time__isnull=False) + if mioitem6_qs2.exists(): + data["六车间生产入库_日期"] = [] + data["六车间生产入库_检验日期"] = [] + data["六车间生产入库_检验人"] = [] + for item in mioitem6_qs: + 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) > 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["六车间生产入库_日期"]]) + data["六车间生产入库_检验日期"] = list(set(data["六车间生产入库_检验日期"])) + data["六车间生产入库_检验日期"] = ";".join([item.strftime("%Y-%m-%d") for item in data["六车间生产入库_检验日期"]]) + try: + data['六车间生产入库_合格率'] = round((data['六车间生产入库_count']-data['六车间生产入库_count_notok ']) * 100/ data['六车间生产入库_count'], 1) + except decimal.InvalidOperation: + myLogger.error("六车间生产入库_合格率decimal.InvalidOperation-{data}") + data['六车间生产入库_合格率'] = 0 + # 成品检验数据 ftestwork_qs = FtestWork.objects.filter(batch=batch, type="prod") if ftestwork_qs.exists(): @@ -354,9 +367,27 @@ def get_alldata_with_batch(batch: str): data['成品检验_检验人'] = list(set(data['成品检验_检验人'])) data['成品检验_检验人'] = ";".join([item.name for item in data['成品检验_检验人']]) data['成品检验_合格率'] = round(data['成品检验_count_ok'] * 100/ data['成品检验_count'], 1) - data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1) + if data["六车间领料_count"]: + data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1) if data["棒料成型_count_real"]: 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) + data['销售发货_执行人'].append(item.mio.user) + data['销售发货_count']+= item.count + if data["棒料成型_count_real"]: + data["七车间_批次发货合格率"] = round(data["销售发货_count"] * 100/ data["棒料成型_count_real"], 1) + if data["六车间领料_count"]: + data["六车间_批次发货合格率"] = round(data["销售发货_count"] * 100/ data["六车间领料_count"], 1) + return last_time, data \ No newline at end of file From ac610a53ce4231186bdd18225f821408f5c24ebb Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 29 Apr 2025 14:43:09 +0800 Subject: [PATCH 20/26] =?UTF-8?q?feat:=20=E7=BB=84=E5=90=88=E4=BB=B6?= =?UTF-8?q?=E5=92=8C=E5=85=A5=E5=BA=93=E6=A3=80=E9=AA=8C=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E5=88=86=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/inm/services.py b/apps/inm/services.py index f7617c04..8a006572 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -58,6 +58,8 @@ def do_out(item: MIOItem): raise ParseError("组合件暂不支持追踪单件") xbatches = [] + if is_zhj: + xbatches = [item.batch] for al in action_list: xmaterial:Material = al[0] xbatch:str = al[1] @@ -140,6 +142,8 @@ def do_in(item: MIOItem): production_dept = None xbatchs = [] + if is_zhj: + xbatchs = [item.batch] for al in action_list: xmaterial, xbatch, xcount = al if xcount <= 0: @@ -351,7 +355,9 @@ class InmService: defect = defects_map[defect_id] m_list.append((material, warehouse, i.batch, xcount, defect, i)) + xbatchs = [] for material, warehouse, batch, change_count, defect, mioitem in m_list: + xbatchs.append(batch) if change_count <= 0: raise ParseError("存在非正数!") state = WMaterial.WM_OK @@ -402,6 +408,10 @@ class InmService: else: raise ParseError("不支持的操作") + # 批次统计分析 + xbatchs = list(set(xbatchs)) + for xbatch in xbatchs: + MyThread(target=get_alldata_with_batch_and_store, args=(xbatch,)).start() def daoru_mb(path: str): """ From f35d2d7d9e528a921b65f16fa573f1e98b2f35bf Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 29 Apr 2025 15:14:02 +0800 Subject: [PATCH 21/26] fix: getattr(item, field) is not None --- apps/wpm/services_2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/services_2.py b/apps/wpm/services_2.py index 1865ccf8..f86a8223 100644 --- a/apps/wpm/services_2.py +++ b/apps/wpm/services_2.py @@ -323,7 +323,7 @@ def get_alldata_with_batch(batch: str): data["六车间生产入库_检验日期"].append(item.test_date) for field in mioitem_count_fields: - if getattr(item, field) > 0: + if getattr(item, field) is not None and getattr(item, field) > 0: if f'六车间生产入库_{field}' not in data: data[f'六车间生产入库_{field}'] = getattr(item, field) else: From dd6dcad74a93d2df52bf86e12febfa92a7e50704 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 30 Apr 2025 08:51:00 +0800 Subject: [PATCH 22/26] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E5=85=89?= =?UTF-8?q?=E5=AD=90=E6=89=B9=E6=AC=A1=E7=BB=9F=E8=AE=A1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services_2.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/apps/wpm/services_2.py b/apps/wpm/services_2.py index f86a8223..1f0787ff 100644 --- a/apps/wpm/services_2.py +++ b/apps/wpm/services_2.py @@ -317,13 +317,12 @@ def get_alldata_with_batch(batch: str): data["六车间生产入库_日期"] = [] data["六车间生产入库_检验日期"] = [] data["六车间生产入库_检验人"] = [] - for item in mioitem6_qs: + 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: + 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: @@ -333,7 +332,7 @@ def get_alldata_with_batch(batch: str): 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) + data['六车间生产入库_合格率'] = round((data['六车间生产入库_count'] - data['六车间生产入库_count_notok']) * 100/ data['六车间生产入库_count'], 1) except decimal.InvalidOperation: myLogger.error("六车间生产入库_合格率decimal.InvalidOperation-{data}") data['六车间生产入库_合格率'] = 0 @@ -367,27 +366,29 @@ def get_alldata_with_batch(batch: str): data['成品检验_检验人'] = list(set(data['成品检验_检验人'])) data['成品检验_检验人'] = ";".join([item.name for item in data['成品检验_检验人']]) data['成品检验_合格率'] = round(data['成品检验_count_ok'] * 100/ data['成品检验_count'], 1) - if data["六车间领料_count"]: + if getattr(data, "六车间领料_count", 0) > 0: data["六车间_批次生产合格率"] = round(data["成品检验_count_ok"] * 100/ data["六车间领料_count"], 1) - if data["棒料成型_count_real"]: + if getattr(data, "棒料成型_count_real", 0) > 0: data["七车间_批次应出合格率"] = round(data["成品检验_count_ok"] * 100/ data["棒料成型_count_real"], 1) # 销售发货数据 mioitem_qs = MIOItem.objects.filter(batch=batch, mio__type="sale_out", mio__submit_time__isnull=False) if mioitem_qs.exists(): data["销售发货_日期"] = [] - data['销售发货_执行人'] = [] + data['销售发货_仓库执行人'] = [] 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) - data['销售发货_执行人'].append(item.mio.user) + if item.mio.do_user: + data['销售发货_仓库执行人'].append(item.mio.do_user) data['销售发货_count']+= item.count - if data["棒料成型_count_real"]: - data["七车间_批次发货合格率"] = round(data["销售发货_count"] * 100/ data["棒料成型_count_real"], 1) - if data["六车间领料_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 \ No newline at end of file From a2cba2128f10bd630b051af3e3ed81c172255c6d Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 30 Apr 2025 13:43:17 +0800 Subject: [PATCH 23/26] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=A8=A1=E6=9D=BF=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cm/migrations/0004_labeltemplate.py | 28 ++++++++++++++++++++++++ apps/cm/models.py | 7 +++++- apps/cm/serializers.py | 11 ++++++++-- apps/cm/urls.py | 3 ++- apps/cm/views.py | 16 +++++++++++--- 5 files changed, 58 insertions(+), 7 deletions(-) create mode 100644 apps/cm/migrations/0004_labeltemplate.py diff --git a/apps/cm/migrations/0004_labeltemplate.py b/apps/cm/migrations/0004_labeltemplate.py new file mode 100644 index 00000000..862fb412 --- /dev/null +++ b/apps/cm/migrations/0004_labeltemplate.py @@ -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, + }, + ), + ] diff --git a/apps/cm/models.py b/apps/cm/models.py index e080d261..3329dcfe 100644 --- a/apps/cm/models.py +++ b/apps/cm/models.py @@ -13,4 +13,9 @@ class LableMat(BaseModel): 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) 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') \ No newline at end of file + 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) \ No newline at end of file diff --git a/apps/cm/serializers.py b/apps/cm/serializers.py index ba6976a4..075c7f0f 100644 --- a/apps/cm/serializers.py +++ b/apps/cm/serializers.py @@ -1,7 +1,8 @@ from rest_framework import serializers -from .models import LableMat +from .models import LableMat, LabelTemplate from apps.qm.models import NotOkOption from apps.wpm.models import WmStateOption +from apps.utils.serializers import CustomModelSerializer class TidSerializer(serializers.Serializer): @@ -23,4 +24,10 @@ class LabelMatSerializer(serializers.ModelSerializer): return getattr(NotOkOption, obj.notok_sign, NotOkOption.qt).label if obj.notok_sign else None def get_state_name(self, obj): - return getattr(WmStateOption, str(obj.state), WmStateOption.OK).label if obj.state else None \ No newline at end of file + return getattr(WmStateOption, str(obj.state), WmStateOption.OK).label if obj.state else None + + +class LabelTemplateSerializer(CustomModelSerializer): + class Meta: + model = LabelTemplate + fields = '__all__' \ No newline at end of file diff --git a/apps/cm/urls.py b/apps/cm/urls.py index cf30518d..087b32d4 100644 --- a/apps/cm/urls.py +++ b/apps/cm/urls.py @@ -1,12 +1,13 @@ from django.urls import path, include from rest_framework.routers import DefaultRouter -from apps.cm.views import LableMatViewSet +from apps.cm.views import LableMatViewSet, LabelTemplateViewSet API_BASE_URL = 'api/cm/' HTML_BASE_URL = 'dhtml/cm/' router = DefaultRouter() router.register('labelmat', LableMatViewSet, basename='labelmat') +router.register('labeltemplate', LabelTemplateViewSet, basename='labeltemplate') urlpatterns = [ path(API_BASE_URL, include(router.urls)), ] \ No newline at end of file diff --git a/apps/cm/views.py b/apps/cm/views.py index ccc4f90f..2ab8c35d 100644 --- a/apps/cm/views.py +++ b/apps/cm/views.py @@ -1,11 +1,11 @@ -from apps.cm.models import LableMat +from apps.cm.models import LableMat, LabelTemplate from rest_framework.decorators import action -from apps.cm.serializers import TidSerializer, LabelMatSerializer +from apps.cm.serializers import TidSerializer, LabelMatSerializer, LabelTemplateSerializer from apps.inm.models import MaterialBatch, MIOItem from apps.wpm.models import WMaterial from rest_framework.exceptions import ParseError, NotFound from rest_framework.response import Response -from apps.utils.viewsets import CustomGenericViewSet, RetrieveModelMixin, CustomListModelMixin +from apps.utils.viewsets import CustomGenericViewSet, RetrieveModelMixin, CustomListModelMixin, CustomModelViewSet # Create your views here. SPLIT_FIELD = "#" @@ -68,3 +68,13 @@ class LableMatViewSet(CustomListModelMixin, RetrieveModelMixin, CustomGenericVie rdata = LabelMatSerializer(obj).data rdata["code_label"] = f"mat{SPLIT_FIELD}{obj.id}" return Response(rdata) + + +class LabelTemplateViewSet(CustomModelViewSet): + """ + list: 标签模板 + + 标签模板 + """ + queryset = LabelTemplate.objects.all() + serializer_class = LabelTemplateSerializer \ No newline at end of file From da69e654bcdcaf25bff4a827613833c577bdb254 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 30 Apr 2025 14:19:49 +0800 Subject: [PATCH 24/26] =?UTF-8?q?feat:=20=E7=94=9F=E4=BA=A7=E5=85=A5?= =?UTF-8?q?=E5=BA=93=E5=92=8C=E9=A2=86=E6=96=99=E6=94=AF=E6=8C=81b?= =?UTF-8?q?=E7=B1=BB=E5=90=88=E6=A0=BC=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/services.py | 67 +++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/apps/inm/services.py b/apps/inm/services.py index 8a006572..081adc9b 100644 --- a/apps/inm/services.py +++ b/apps/inm/services.py @@ -18,6 +18,7 @@ def do_out(item: MIOItem): """ if item.mb and item.mb.defect is not None: raise ParseError("生产领料不支持不合格品") + from apps.inm.models import MaterialBatch mio:MIO = item.mio belong_dept = mio.belong_dept @@ -26,6 +27,16 @@ def do_out(item: MIOItem): material:Material = item.material if material.into_wm is False: # 用于混料的原料不与车间库存交互, 这个是配置项目 return + + # 获取defect + defect:Defect = None + if item.wm and item.mb: + raise ParseError("车间和仓库库存不能同时存在") + if item.wm: + defect = item.wm.defect + elif item.mb: + defect = item.mb.defect + action_list = [] mias = MIOItemA.objects.filter(mioitem=item) is_zhj = False # 是否组合件领料 @@ -35,16 +46,18 @@ def do_out(item: MIOItem): for i in range(len(mias_list)): material, batch, rate = mias_list[i] new_count = rate * item.count # 假设 item.count 存在 - action_list.append([material, batch, new_count]) + action_list.append([material, batch, new_count, None]) else: - action_list = [[item.material, item.batch, item.count]] + action_list = [[item.material, item.batch, item.count, defect]] if is_zhj: try: mb = MaterialBatch.objects.get( material=item.material, warehouse=item.warehouse, - batch=item.batch + batch=item.batch, + state=WMaterial.WM_OK, + defect=None ) except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e: raise ParseError(f"组合件批次错误!{e}") @@ -64,6 +77,8 @@ def do_out(item: MIOItem): xmaterial:Material = al[0] xbatch:str = al[1] xcount:str = al[2] + defect:Defect = al[3] + xbatches.append(xbatch) if xcount <= 0: raise ParseError("存在非正数!") @@ -74,8 +89,8 @@ def do_out(item: MIOItem): material=xmaterial, warehouse=item.warehouse, batch=xbatch, - state=10, - defect=None + state=WMaterial.WM_OK, + defect=defect ) except (MaterialBatch.DoesNotExist, MaterialBatch.MultipleObjectsReturned) as e: raise ParseError(f"批次错误!{e}") @@ -87,9 +102,10 @@ def do_out(item: MIOItem): # 领到车间库存(或工段) - wm, new_create = WMaterial.objects.get_or_create(batch=xbatch, material=xmaterial, - belong_dept=belong_dept, mgroup=mgroup, - state=WMaterial.WM_OK) + wm, new_create = WMaterial.objects.get_or_create( + batch=xbatch, material=xmaterial, + belong_dept=belong_dept, mgroup=mgroup, + state=WMaterial.WM_OK, defect=defect) if new_create: wm.create_by = do_user wm.batch_ofrom = mb.batch if mb else None @@ -118,8 +134,9 @@ def do_in(item: MIOItem): 生产入库后更新车间物料 """ mio = item.mio - if item.wm and item.wm.defect is not None: - raise ParseError("不合格物料无法入库") + wmin:WMaterial = item.wm + if wmin and wmin.state != WMaterial.WM_OK: + raise ParseError("非合格物料无法入库") belong_dept = mio.belong_dept mgroup = mio.mgroup do_user = mio.do_user @@ -129,15 +146,25 @@ def do_in(item: MIOItem): action_list = [] mias = MIOItemA.objects.filter(mioitem=item) is_zhj = False # 是否组合件入仓库 + + # 获取defect + defect:Defect = None + if item.wm and item.mb: + raise ParseError("车间和仓库库存不能同时存在") + if item.wm: + defect = item.wm.defect + elif item.mb: + defect = item.mb.defect + if mias.exists(): is_zhj = True mias_list = mias.values_list('material', 'batch', 'rate') for i in mias_list: material, batch, rate = i new_count = rate * item.count # 假设 item.count 存在 - action_list.append([material, batch, new_count]) + action_list.append([material, batch, new_count, None]) else: - action_list = [[item.material, item.batch, item.count]] + action_list = [[item.material, item.batch, item.count, defect]] production_dept = None @@ -145,16 +172,18 @@ def do_in(item: MIOItem): if is_zhj: xbatchs = [item.batch] for al in action_list: - xmaterial, xbatch, xcount = al + xmaterial, xbatch, xcount, defect = al if xcount <= 0: raise ParseError("存在非正数!") + xbatchs.append(xbatch) - # 扣减车间库存 + wm_qs = WMaterial.objects.filter( batch=xbatch, material=xmaterial, belong_dept=belong_dept, - mgroup=mgroup, + mgroup=mgroup, + defect=defect, state=WMaterial.WM_OK) count_x = wm_qs.count() if count_x == 1: @@ -165,6 +194,8 @@ def do_in(item: MIOItem): else: raise ParseError( f'{str(xmaterial)}-{xbatch}-存在多个相同批次!') + + # 扣减车间库存 new_count = wm.count - xcount if new_count >= 0: wm.count = new_count @@ -184,8 +215,8 @@ def do_in(item: MIOItem): material=xmaterial, warehouse=item.warehouse, batch=xbatch, - state=10, - defect=None, + state=WMaterial.WM_OK, + defect=defect, defaults={ "count": 0, "batch_ofrom": wm.batch_ofrom, @@ -212,6 +243,8 @@ def do_in(item: MIOItem): material=item.material, warehouse=item.warehouse, batch=item.batch, + defect=None, + state=WMaterial.WM_OK, defaults={"count": 0, "production_dept": production_dept} ) if not is_created: From 725dd4d5549a913da8d4f94201f9da18923fbacf Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 30 Apr 2025 14:20:15 +0800 Subject: [PATCH 25/26] =?UTF-8?q?fix:=20mio=5Fsaleout=E7=BC=BA=E5=B0=91imp?= =?UTF-8?q?ort?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sam/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sam/services.py b/apps/sam/services.py index 65ce2b01..b577991e 100644 --- a/apps/sam/services.py +++ b/apps/sam/services.py @@ -1,6 +1,6 @@ from apps.sam.models import Order, OrderItem from rest_framework.exceptions import ValidationError -from django.db.models import F +from django.db.models import F, Sum from apps.inm.models import MIO, MIOItem From 8e3544343391a8384c903f862fbb08159d9b378f Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 30 Apr 2025 14:27:57 +0800 Subject: [PATCH 26/26] release: 2.6.2025043014 --- changelog.md | 9 +++++++++ server/settings.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 191b2f20..0daaedaf 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,12 @@ +## 2.6.2025043014 +- feat: 新增功能 + - 生产入库和领料支持b类合格品 [caoqianming] + - 添加标签模板接口 [caoqianming] + - 完善光子批次统计数据 [caoqianming] + - 组合件和入库检验追加统计分析 [caoqianming] + - get_alldata_with_batch添加内容 [caoqianming] +- fix: 问题修复 + - mio_saleout缺少import [caoqianming] ## 2.6.2025042816 - feat: 新增功能 - 支持个号转批号及配置 [caoqianming] diff --git a/server/settings.py b/server/settings.py index 0a53ee2d..dceec682 100755 --- a/server/settings.py +++ b/server/settings.py @@ -35,7 +35,7 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) ALLOWED_HOSTS = ['*'] SYS_NAME = '星途工厂综合管理系统' -SYS_VERSION = '2.6.2025042816' +SYS_VERSION = '2.6.2025043014' X_FRAME_OPTIONS = 'SAMEORIGIN' # Application definition