From ca67a1479f66f461aedf9ba226c9fb3cba8165b7 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 25 Dec 2025 14:25:40 +0800 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20resignation=20ticket=E5=AD=98?= =?UTF-8?q?=E5=85=A5employee=5Fname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/hrm/views.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/hrm/views.py b/apps/hrm/views.py index 1716cd1b..f8bb5cfc 100755 --- a/apps/hrm/views.py +++ b/apps/hrm/views.py @@ -401,6 +401,9 @@ class ResignationViewSet(TicketMixin, EuModelViewSet): search_fields = ["employee__name"] workflow_key = "wf_resignation" + def gen_other_ticket_data(self, instance): + return {"employee_name": instance.employee.name} + @staticmethod def update_handle_date(ticket: Ticket, transition, new_ticket_data: dict): handle_date = new_ticket_data.get("handle_date", None) From aa07c041fbf9fea794a0de20f252bad255e2cbdb Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 25 Dec 2025 14:27:28 +0800 Subject: [PATCH 02/16] =?UTF-8?q?feat:=20=E5=9B=BA=E5=AE=9A=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E5=85=A5=E5=BA=93=E6=B5=81=E7=A8=8Bapply?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/asm/serializers.py | 3 +++ apps/asm/views.py | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/asm/serializers.py b/apps/asm/serializers.py index f6fa70b1..66080407 100644 --- a/apps/asm/serializers.py +++ b/apps/asm/serializers.py @@ -12,6 +12,9 @@ class AssetCateSerializer(CustomModelSerializer): read_only_fields = EXCLUDE_FIELDS class AssetSerializer(CustomModelSerializer): + keep_dept_name = serializers.CharField(source="keep_dept.name", read_only=True) + keeper_name = serializers.CharField(source="keeper.name", read_only=True) + cate_name = serializers.CharField(source="cate.name", read_only=True) class Meta: model = Asset fields = '__all__' diff --git a/apps/asm/views.py b/apps/asm/views.py index 75ff3454..785140c1 100644 --- a/apps/asm/views.py +++ b/apps/asm/views.py @@ -60,5 +60,12 @@ class AssetLogViewSet(TicketMixin, CustomModelViewSet): def gen_other_ticket_data(self, instance:AssetLog): return {"keep_dept_name": instance.keep_dept.name} - + + @staticmethod + def apply(ticket: Ticket, transition, new_ticket_data: dict): + assetlog:AssetLog = ticket.assetlog_ticket + items = assetlog.items + sr = AssetSerializer(data=items, many=True) + sr.is_valid(raise_exception=True) + sr.save(create_by=ticket.create_by) From 09a7c64b3c36b1901cc4e4bae3ffab907e5dce35 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 25 Dec 2025 14:29:22 +0800 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20mlogbw=20patch=E6=9D=83=E9=99=90?= 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 41f88666..fe17f4e7 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -1074,7 +1074,7 @@ class BatchStViewSet(CustomListModelMixin, ComplexQueryMixin, CustomGenericViewS class MlogbwViewSet(CustomModelViewSet): - perms_map = {"get": "*", "post": "mlog.update", "put": "mlog.update", "delete": "mlog.update"} + perms_map = {"get": "*", "post": "mlog.update", "put": "mlog.update", "delete": "mlog.update", "patch": "mlog.update"} queryset = Mlogbw.objects.all() serializer_class = MlogbwCreateUpdateSerializer list_serializer_class = MlogbwListSerializer From 3f183583c8457bdc7b1631c406d043956759ab54 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Thu, 25 Dec 2025 15:00:24 +0800 Subject: [PATCH 04/16] release: 3.0.2025122514 --- changelog.md | 145 +++++++++++++++++++++++++++++++++++++++++++++ server/settings.py | 2 +- 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 536b772e..00e7c0f9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,148 @@ +## 3.0.2025122514 +- feat: 新增功能 + - mlogbw patch权限 [caoqianming] + - 固定资产入库流程apply [caoqianming] + - resignation ticket存入employee_name [caoqianming] + - 新增人员交接单及其修改 人员交接时候反存校验 [TianyangZhang] + - asm assetlogcreate [caoqianming] + - base 提交时可变动工单title [caoqianming] + - 车间库存支持传入count_all [caoqianming] + - wmaterial search时去除material__number [caoqianming] + - base ticketmixin 修改 ”perform_update“ bug [TianyangZhang] + - wmaterial添加mlog_date_start筛选条件 [caoqianming] + - 新增hrm --人员交接表 [TianyangZhang] + - asm初步接口 [caoqianming] + - base userfilter获取归属于该部门及以下部门的人 [caoqianming] + - 玻纤添加mioitemw检验导入表 [caoqianming] + - 修改mioitemw test的导入逻辑 [caoqianming] + - 光子六车间批次生产合格率修改 [caoqianming] + - 取消handover_submit无用校验 [caoqianming] + - 合批时校验批次是否已存在 [caoqianming] + - routepack删除时做一下校验 [caoqianming] + - 维修记录初步完成 [caoqianming] + - base ticketmixin传入other_data [caoqianming] + - base 模板字段改为textfield [caoqianming] + - 删除asm同步文件 [caoqianming] + - 修改hrm/urls --EMPneed [TianyangZhang] + - 新增模块固定资产 asm [TianyangZhang] + - 添加员工需求表 [caoqianming] + - route接口增加按product获取总图 [caoqianming] + - base wf增加ticket_count接口添加分类 [caoqianming] + - validate_dag检查检查未到达的物料 [caoqianming] + - 自动生成物料的逻辑优化 [caoqianming] + - cal_x_task_count默认使用target_quantity [caoqianming] + - base wf 捕获expr is None的错误 [caoqianming] + - validate_dag增加传入参数 [caoqianming] + - 改版交接支持拆合批 [caoqianming] + - translate_eval_formula 打印报错信息 [caoqianming] + - base 优化safe_get_or_create [caoqianming] + - base 增加statedetailserializer可返回节点操作人员 [caoqianming] + - 获取batchlog dag数据支持临近节点返回 [caoqianming] + - base ticket create支持transition非必传 [caoqianming] + - 供应商审核通过后即可添加供应商 [caoqianming] + - 校验SupplierAudit供应商名称已存在 [caoqianming] + - material list filter low_inm优化 [caoqianming] + - material list 如需获取库存数据需指定传参 [caoqianming] + - 添加部分索引 [caoqianming] + - base ticket create支持transition非必传 [caoqianming] + - 供应商审核通过后即可添加供应商 [caoqianming] + - 校验SupplierAudit供应商名称已存在 [caoqianming] + - material list filter low_inm优化 [caoqianming] + - material list 如需获取库存数据需指定传参 [caoqianming] + - 添加部分索引 [caoqianming] + - srm -model-Papersecret 增加 organization 申请部门字段 [TianyangZhang] + - 优化mlogbw list接口速度 [caoqianming] + - srm-patent修改字段类型 [TianyangZhang] + - resignation添加ticketrelate_name [caoqianming] + - base get_object加锁时注意is_deleted过滤采用base_manager [caoqianming] + - base wf 调用方法支持静态方法 [caoqianming] + - mlogbw关于wpr的校验修改以支持手动新增 [caoqianming] + - resignatioin提交时调用的方法 [caoqianming] + - ResignationSerializer create bug [caoqianming] + - base ticketmixin添加ticket_auto_submit_on_create [caoqianming] + - resignation添加ticketMixin [caoqianming] + - base wfmixin 修改时校验 [caoqianming] + - base ticketDetail添加create_by_name [caoqianming] + - 短信发送功能未开启 [caoqianming] + - 批次号格式错误的校验 [caoqianming] + - mioitemcreate时接收count_send [caoqianming] + - mioitemw增加筛选条件 [caoqianming] + - wpr_bxerp优化mlogbw的获取 [caoqianming] + - do_out报错更明确 [caoqianming] + - 优化ana batchwork [caoqianming] + - 改版交接支持new_wm [caoqianming] + - ofm-models borrowRecord 增加借阅数量 [TianyangZhang] + - ofm-models fix bug [TianyangZhang] + - feat : ofm -vehicle fix bug [TianyangZhang] + - ofm 修改车辆model 字段 [TianyangZhang] + - ofm-service fix bug [TianyangZhang] + - feat:ofm-service 修改 bug [TianyangZhang] + - ofm -修改 view 字段 [TianyangZhang] + - ofm 修改 ofm 字段 并重新生成迁移文件 [TianyangZhang] + - base workflow添加分类字段 [caoqianming] + - ofm-views 修改 车辆字段 [TianyangZhang] + - ofm-vehicle 修改车辆审批申请 改完按时间段进行选择 [TianyangZhang] + - mioitem添加count_send字段 [caoqianming] + - 校验改版时选择的改版物料 [caoqianming] + - 导入物料明细支持直接从名称等匹配 [caoqianming] + - 增强新批次号校验 [caoqianming] + - 提供修改编号的接口 [caoqianming] + - supplieraudit采用新工作流挂载方式 [caoqianming] + - base 添加ticketmixin可集成到viewset下以支持工作流 [caoqianming] + - base 可跳过短信发送 [caoqianming] + - 反向操作时忽略明细校验 [caoqianming] + - 拦截new_batch的错误 [caoqianming] + - fix 修改 srm 的 views [TianyangZhang] + - base wfservice创建出工单时处理人为提交人 [caoqianming] + - feat:srm fix serializer [TianyangZhang] + - base handle_ticket 完善transition校验 [caoqianming] + - DatasetRecord添加filter [caoqianming] + - handover添加selectfield [caoqianming] + - 交接记录返回material_changed_fname [caoqianming] + - 改版交接必须为合批操作 [caoqianming] + - base 创建数据时检验不包含id2 [caoqianming] + - fix srm serializer [TianyangZhang] + - srm-service 去掉 platform belong_dept [TianyangZhang] + - add new model platform '平台审批' [TianyangZhang] + - base 创建数据时检验不包含id [caoqianming] + - 添加供应商审核 [caoqianming] + - base 开始编写ticketMixin可自动挂载 [caoqianming] + - ResignationSerializer返回employee_id_number [caoqianming] + - base 添加EuModelViewSet [caoqianming] + - 离职申请支持删除 [caoqianming] + - Resignationserializer返回employee_name [caoqianming] + - base handle_ticket 默认参数 [caoqianming] + - 优化bind_resignation [caoqianming] + - base 优化wf create [caoqianming] + - ResignationViewSet添加RetrieveModelMixin [caoqianming] + - bind_resignation bug [caoqianming] + - 返回TicketSimpleSerializer [caoqianming] +- fix: 问题修复 + - repair swaggger显示问题 [caoqianming] + - 修改 ofm - vehicle 中的出发里程 [TianyangZhang] + - handoverserializer 关于new_wm的处理 [caoqianming] + - handovermerge时new_state未定义 [caoqianming] + - 修改印章申请binging_seal [TianyangZhang] + - update_mb_item时考虑检验的存在 [caoqianming] + - hrm-用人需求修改标题模板 [TianyangZhang] + - 先调整asm以启动服务 [caoqianming] + - do_in bug [caoqianming] + - base ticketmixin先创建再handle [caoqianming] + - fix :修改提交审批报错 RelatedManager' object has no attribute 'belong_dept [TianyangZhang] + - fix : srm 修改 paperrecord 字段 cor_author 字段类型 [TianyangZhang] + - fix:srm 修改论文审批 反存 论文台账 主要修改model 字段类型 [TianyangZhang] + - srm 修改专利台账 [TianyangZhang] + - base wfmixin gen_ticket_data保存t_id转为str [caoqianming] + - fix : 修改ofm -views -vehicle && services [TianyangZhang] + - format_json_with_placeholders 处理decimal [caoqianming] + - base wf工作流分类接口detail错误 [caoqianming] + - do_in 可直接取用wm [caoqianming] + - material get_queryset忘记return qs [caoqianming] + - material增加count__gt等查询条件 [caoqianming] + - mioitem create时校验是否混批的bug [caoqianming] + - base handle_ticket处理ticket_title [caoqianming] + - bind_resignation bug [caoqianming] + - clean_data关于material的处理 [caoqianming] ## 3.0.2025120109 - feat: 新增功能 - 供应商审核通过后即可添加供应商 [caoqianming] diff --git a/server/settings.py b/server/settings.py index 4b37f2eb..b6b8b02e 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 = '3.0.2025120109' +SYS_VERSION = '3.0.2025122514' X_FRAME_OPTIONS = 'SAMEORIGIN' # Application definition From a8ae8ee32a80cc3818f8612b7d052613169f5018 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 26 Dec 2025 14:52:54 +0800 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20base=20dept=20filter=E6=94=AF?= =?UTF-8?q?=E6=8C=81parent=20isnull=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/system/filters.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/system/filters.py b/apps/system/filters.py index d3f10c99..481b6398 100755 --- a/apps/system/filters.py +++ b/apps/system/filters.py @@ -45,5 +45,6 @@ class DeptFilterSet(filters.FilterSet): model = Dept fields = { 'type': ['exact', 'in'], - 'name': ['exact', 'in', 'contains'] + 'name': ['exact', 'in', 'contains'], + "parent": ['exact', 'isnull'], } From c56f908b42530e888498a560ee744abbc647b878 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Fri, 26 Dec 2025 17:00:00 +0800 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20mlogbin=20qct=20=E5=8F=AF?= =?UTF-8?q?=E4=BE=9D=E6=8D=AEfix=E9=80=89=E6=8B=A9?= 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 fe17f4e7..1c18407d 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -836,7 +836,7 @@ class MlogbInViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, BulkDestroyMode "batch": mlogbin.batch, "batch_ofrom": wm_in.batch_ofrom, "material_ofrom": wm_in.material_ofrom, - "qct": Qct.get(material_out, "process", "out"), + "qct": Qct.get(material_out, "fix" if mlog.is_fix else "process", "out"), } if mtype == Process.PRO_DIV and material_in.tracking == Material.MA_TRACKING_SINGLE: pass From ec13b8b166d382ca6a894f84d8e8d722c16d8afb Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 29 Dec 2025 14:41:06 +0800 Subject: [PATCH 07/16] =?UTF-8?q?fix:=20=E6=AD=A3=E5=B8=B8=E4=BA=A4?= =?UTF-8?q?=E6=8E=A5=E6=94=AF=E6=8C=81new=5Fwm=E4=B8=94=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=B8=8D=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/wpm/services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index b9dea08d..d560efc8 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -785,7 +785,7 @@ def handover_submit(handover:Handover, user: User, now: Union[datetime.datetime, if handover.type == Handover.H_NORMAL: if mtype == Handover.H_MERGE and handover.new_wm: wm_to = handover.new_wm - if wm_to.state != WMaterial.WM_OK or wm_to.material != wm_from.material or wm_to.defect != wm_from.defect: + if wm_to.state != wm_from.state or wm_to.material != wm_from.material or wm_to.defect != wm_from.defect: raise ParseError("正常合并到的车间库存状态或物料异常") else: wm_to, _ = WMaterial.objects.get_or_create( From 29f4e2f76aafa79a82447dbcf60a19db94ed91a9 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 29 Dec 2025 15:09:45 +0800 Subject: [PATCH 08/16] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96mlog=5Fsubmit?= =?UTF-8?q?=20=E8=BF=94=E5=B7=A5=E5=90=8E=E4=BA=A7=E5=93=81=E6=94=BE?= =?UTF-8?q?=E5=9C=A8=E6=9C=AC=E5=B7=A5=E6=AE=B5=E4=B8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index d560efc8..86d18917 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -172,13 +172,15 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): mgroup = mlog.mgroup process = mgroup.process - into_wm_mgroup = process.into_wm_mgroup + stored_mgroup = process.into_wm_mgroup need_store_notok = process.store_notok belong_dept = mgroup.belong_dept material_out: Material = mlog.material_out material_in: Material = mlog.material_in supplier = mlog.supplier # 外协 is_fix = mlog.is_fix + if is_fix: # 如果是返工,直接放到工段下 + stored_mgroup = True m_ins_list = [] m_ins_bl_list = [] @@ -275,8 +277,6 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): mlogb_out_qs = Mlogb.objects.filter(mlog=mlog, material_out__isnull=False) - stored_mgroup = into_wm_mgroup - stored_notok = need_store_notok if mlogb_out_qs.exists(): mlogb_out_qs = mlogb_out_qs.filter(need_inout=True) m_outs_list = [(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok_full if mo.count_ok_full is not None else mo.count_ok, mlog.count_real_eweight, None, mo) for mo in mlogb_out_qs.all()] @@ -339,7 +339,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): lookup['defect'] = notok_sign_or_defect elif notok_sign_or_defect is not None: lookup['notok_sign'] = notok_sign_or_defect - if into_wm_mgroup: + if stored_mgroup: lookup['mgroup'] = mgroup else: lookup['belong_dept'] = belong_dept @@ -529,7 +529,6 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): # 再生成消耗 m_ins_list = [] m_ins_bl_list = [] - into_wm_mgroup = process.into_wm_mgroup m_ins = Mlogb.objects.filter(mlog=mlog, material_in__isnull=False) if m_ins.exists(): m_ins = m_ins.filter(need_inout=True) @@ -553,7 +552,7 @@ def mlog_revert(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): else: # 针对光子的情况,实际上必须需要wm_in lookup = {'batch': mi_batch, 'material': mi_ma, 'mgroup': None, 'state': WMaterial.WM_OK} - if into_wm_mgroup: + if stored_mgroup: # 退回到本工段 lookup['mgroup'] = mgroup else: From c3c7675ac51dc8b4d6585005849da084016db352 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Mon, 29 Dec 2025 15:54:14 +0800 Subject: [PATCH 09/16] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96mlog=5Fsubmit?= =?UTF-8?q?=20=E8=BF=94=E5=B7=A5=E5=90=8E=E4=BA=A7=E5=93=81=E6=94=BE?= =?UTF-8?q?=E5=9C=A8=E6=9C=AC=E5=B7=A5=E6=AE=B5=E4=B8=8B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 86d18917..37db83d0 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -173,7 +173,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): mgroup = mlog.mgroup process = mgroup.process stored_mgroup = process.into_wm_mgroup - need_store_notok = process.store_notok + stored_notok = process.store_notok belong_dept = mgroup.belong_dept material_out: Material = mlog.material_out material_in: Material = mlog.material_in @@ -254,7 +254,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): Wpr.change_or_new(wpr=item.wpr, old_wm=wm, ftest=item.ftest) # 针对加工前不良的暂时额外处理 - if need_store_notok: + if stored_notok: for item in m_ins_bl_list: material, batch, count, defect, mi_ = item if count <= 0: @@ -280,7 +280,7 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if mlogb_out_qs.exists(): mlogb_out_qs = mlogb_out_qs.filter(need_inout=True) m_outs_list = [(mo.material_out, mo.batch if mo.batch else mlog.batch, mo.count_ok_full if mo.count_ok_full is not None else mo.count_ok, mlog.count_real_eweight, None, mo) for mo in mlogb_out_qs.all()] - if need_store_notok: + if stored_notok: for item in mlogb_out_qs: mbd_qs = MlogbDefect.get_defect_qs_from_mlogb(item) if item.qct is not None or mbd_qs.exists(): @@ -309,7 +309,6 @@ def mlog_submit(mlog: Mlog, user: User, now: Union[datetime.datetime, None]): if 'count_n_' in f.name and getattr(item, f.name) > 0: notok_sign = f.name.replace('count_n_', '') m_outs_list.append( (item.material_out, item.batch if item.batch else mlog.batch, getattr(item, f.name), mlog.count_real_eweight, notok_sign, item)) - stored_notok = True # 这里有一个漏洞,在产出物为兄弟件的情况下,不合格品的数量是记录在mlog上的, # 而不是mlogb上,以上的额外处理就没有效果了, 不过光子不记录不合格品 else: From c37e71d60fbb3541c105a8cebc92dd9dc1290406 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 30 Dec 2025 10:07:41 +0800 Subject: [PATCH 10/16] =?UTF-8?q?feat:=20wpr=E6=B7=BB=E5=8A=A0material=5Fn?= =?UTF-8?q?ame=E6=9F=A5=E8=AF=A2=E6=9D=A1=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpmw/filters.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/wpmw/filters.py b/apps/wpmw/filters.py index fae8b731..36d89328 100644 --- a/apps/wpmw/filters.py +++ b/apps/wpmw/filters.py @@ -17,6 +17,7 @@ class WprFilter(filters.FilterSet): "mb": ["exact", "isnull"], "wm": ["exact", "isnull"], "material__process": ["exact"], + "material__name": ["exact", "contains"], "state": ["exact"], "defects": ["exact"], "number": ["exact"] From e385a558e9a00fa691a90fa4e8a9c9e25694ffab Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 30 Dec 2025 14:28:20 +0800 Subject: [PATCH 11/16] =?UTF-8?q?feat:=20=E8=BD=A6=E9=97=B4=E5=BA=93?= =?UTF-8?q?=E5=AD=98=E6=A3=80=E9=AA=8C=E6=94=AF=E6=8C=81=E6=92=A4=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/services.py | 37 +++++++++++++++++++++++++++++++++++++ apps/qm/views.py | 16 +++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/apps/qm/services.py b/apps/qm/services.py index a6bb0577..ac7a45dd 100644 --- a/apps/qm/services.py +++ b/apps/qm/services.py @@ -157,6 +157,43 @@ def ftestwork_submit(ins:FtestWork, user: User): # 触发批次统计分析 ana_batch_thread(xbatchs=[ins.batch]) + +def ftestwork_revert(ins: FtestWork): + wm:WMaterial = ins.wm + if wm and ins.need_update_wm: + fwd_qs = FtestworkDefect.objects.filter(ftestwork=ins) + for item in fwd_qs: + item:FtestworkDefect = item + if item.count > 0: + wm.count = wm.count + item.count + wm.save() + wmstate = WMaterial.WM_OK + if item.defect.okcate == Defect.DEFECT_NOTOK: + wmstate = WMaterial.WM_NOTOK + try: + wmx = WMaterial.objects.get( + material=wm.material, + batch=wm.batch, + mgroup=wm.mgroup, + belong_dept=wm.belong_dept, + state=wmstate, + notok_sign=None, + defect=item.defect, + ) + except Exception as e: + raise ParseError(f'找不到{item.defect.name}的车间库存: {str(e)}') + wmx.count = wmx.count - item.count + if wmx.count < 0: + raise ParseError("数量不足,撤销失败") + wmx.save() + else: + raise ParseError("该检验工作不支持撤销") + ins.submit_user = None + ins.submit_time = None + ins.save() + # 触发批次统计分析 + ana_batch_thread(xbatchs=[ins.batch]) + def bind_ftestwork(ticket: Ticket, transition, new_ticket_data: dict): ins = FtestWork.objects.get(id=new_ticket_data['t_id']) if ins.submit_time is not None: diff --git a/apps/qm/views.py b/apps/qm/views.py index 927fe158..e0f29ea2 100644 --- a/apps/qm/views.py +++ b/apps/qm/views.py @@ -17,7 +17,7 @@ from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.wpm.models import SfLog from apps.qm.filters import QuaStatFilter, TestItemFilter, FtestWorkFilter, QctFilter, FtestFilter from django.db import transaction -from apps.qm.services import ftestwork_submit +from apps.qm.services import ftestwork_submit, ftestwork_revert from apps.wpm.services_2 import ana_batch_thread from apps.wf.models import State # Create your views here. @@ -327,4 +327,18 @@ class FtestWorkViewSet(CustomModelViewSet): ftestwork_submit(ins, request.user) else: raise ParseError('该检验工作已提交') + return Response() + + @action(methods=['post'], detail=True, perms_map={'post': 'ftestwork.submit'}, serializer_class=Serializer) + @transaction.atomic + def revert(self, request, *args, **kwargs): + """撤销检验工作 + + 撤销检验工作 + """ + ins:FtestWork = self.get_object() + if ins.submit_time: + ftestwork_revert(ins) + else: + raise ParseError('该检验工作未提交') return Response() \ No newline at end of file From 6ac7c020bd5824fc638b53e0747e9534b32d11ab Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 30 Dec 2025 14:32:29 +0800 Subject: [PATCH 12/16] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E6=92=A4?= =?UTF-8?q?=E5=9B=9E=E5=92=8C=E6=92=A4=E9=94=80=E7=9A=84=E8=A1=A8=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/services.py | 4 ++-- apps/qm/views.py | 2 ++ apps/wpm/services.py | 4 ++-- apps/wpm/views.py | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/qm/services.py b/apps/qm/services.py index ac7a45dd..8de5259e 100644 --- a/apps/qm/services.py +++ b/apps/qm/services.py @@ -184,10 +184,10 @@ def ftestwork_revert(ins: FtestWork): raise ParseError(f'找不到{item.defect.name}的车间库存: {str(e)}') wmx.count = wmx.count - item.count if wmx.count < 0: - raise ParseError("数量不足,撤销失败") + raise ParseError("数量不足,撤回失败") wmx.save() else: - raise ParseError("该检验工作不支持撤销") + raise ParseError("该检验工作不支持撤回") ins.submit_user = None ins.submit_time = None ins.save() diff --git a/apps/qm/views.py b/apps/qm/views.py index e0f29ea2..65dc165b 100644 --- a/apps/qm/views.py +++ b/apps/qm/views.py @@ -338,6 +338,8 @@ class FtestWorkViewSet(CustomModelViewSet): """ ins:FtestWork = self.get_object() if ins.submit_time: + if self.request.user != ins.submit_user: + raise ParseError('只能由提交人撤回') ftestwork_revert(ins) else: raise ParseError('该检验工作未提交') diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 37db83d0..3f296312 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -950,7 +950,7 @@ def handover_revert(handover:Handover, handler:User=None): ticket:Ticket = handover.ticket if ticket: # 首先把ticket改回开始状态 - WfService.retreat(ticket=ticket, suggestion="撤销交接单", handler=handler, next_handler=handover.create_by) + WfService.retreat(ticket=ticket, suggestion="撤回交接单", handler=handler, next_handler=handover.create_by) mids = [] # handover_type = handover.type # handover_mtype = handover.mtype @@ -971,7 +971,7 @@ def handover_revert(handover:Handover, handler:User=None): wm = item.wm wm_to = item.wm_to if wm is None or wm_to is None: - raise ParseError('该交接单不支持撤销2!') + raise ParseError('该交接单不支持撤回2!') if wm == wm_to: # 此时是自己交给自己,不需要做任何操作 pass diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 1c18407d..236bd779 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -446,9 +446,9 @@ class MlogViewSet(CustomModelViewSet): raise ParseError("该日志存在审批!") user = request.user if ins.submit_time is None: - raise ParseError("日志未提交不可撤销") + raise ParseError("日志未提交不可撤回") if user != ins.submit_user: - raise ParseError("非提交人不可撤销!") + raise ParseError("非提交人不可撤回!") now = timezone.now() mlog_revert(ins, user, now) return Response(MlogSerializer(instance=ins).data) From 81770e89fa93a39e868fa82bf539a957aac3dee9 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Tue, 30 Dec 2025 14:34:34 +0800 Subject: [PATCH 13/16] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E6=92=A4?= =?UTF-8?q?=E5=9B=9E=E5=92=8C=E6=92=A4=E9=94=80=E7=9A=84=E8=A1=A8=E8=BF=B0?= =?UTF-8?q?2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/qm/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/qm/views.py b/apps/qm/views.py index 65dc165b..e60d3db2 100644 --- a/apps/qm/views.py +++ b/apps/qm/views.py @@ -332,9 +332,9 @@ class FtestWorkViewSet(CustomModelViewSet): @action(methods=['post'], detail=True, perms_map={'post': 'ftestwork.submit'}, serializer_class=Serializer) @transaction.atomic def revert(self, request, *args, **kwargs): - """撤销检验工作 + """撤回检验工作 - 撤销检验工作 + 撤回检验工作 """ ins:FtestWork = self.get_object() if ins.submit_time: From 52ebac68a0e7228effd3d253e32bbea74e667dee Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sun, 4 Jan 2026 10:10:26 +0800 Subject: [PATCH 14/16] =?UTF-8?q?feat:=20=E5=87=BA=E5=85=A5=E5=BA=93?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E8=BF=94=E5=9B=9E=E5=AD=90=E8=A1=A8=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/inm/serializers.py | 9 ++++++++ apps/inm/views.py | 47 +++++++++++++++++++++++++++++------------ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/apps/inm/serializers.py b/apps/inm/serializers.py index a1a6a6e6..3f8fdce5 100644 --- a/apps/inm/serializers.py +++ b/apps/inm/serializers.py @@ -247,6 +247,15 @@ class MIOItemAListSerializer(CustomModelSerializer): read_only_fields = EXCLUDE_FIELDS_BASE +class MIOItemListSimpleSerializer(CustomModelSerializer): + warehouse_name = serializers.CharField(source='warehouse.name', read_only=True) + material_name = serializers.StringRelatedField( + source='material', read_only=True) + + class Meta: + model = MIOItem + fields = ["id", "mio", "material", "warehouse", "material_name", "warehouse_name", "batch", "count", "test_date", "count_notok"] + class MIOItemSerializer(CustomModelSerializer): warehouse_name = serializers.CharField(source='warehouse.name', read_only=True) material_ = MaterialSerializer(source='material', read_only=True) diff --git a/apps/inm/views.py b/apps/inm/views.py index e1a3c988..a5d35267 100644 --- a/apps/inm/views.py +++ b/apps/inm/views.py @@ -14,7 +14,7 @@ from apps.inm.serializers import ( MaterialBatchSerializer, WareHourseSerializer, MIOListSerializer, MIOItemSerializer, MioItemAnaSerializer, MIODoSerializer, MIOSaleSerializer, MIOPurSerializer, MIOOtherSerializer, MIOItemCreateSerializer, MaterialBatchDetailSerializer, MIODetailSerializer, MIOItemTestSerializer, MIOItemPurInTestSerializer, - MIOItemwSerializer, MioItemDetailSerializer, PackSerializer, PackMioSerializer) + MIOItemwSerializer, MioItemDetailSerializer, PackSerializer, PackMioSerializer, MIOItemListSimpleSerializer) from apps.inm.serializers2 import MIOItemwCreateUpdateSerializer from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from apps.inm.services import InmService @@ -163,20 +163,39 @@ class MIOViewSet(CustomModelViewSet): return mio def add_info_for_list(self, data): - # 获取检验状态 - mio_dict = {} + # 1. 收集所有mio的ID + mio_ids = [item['id'] for item in data] + + # 2. 预初始化mio字典和items列表 + mio_dict = {item['id']: { + **item, + 'is_tested': False, # 默认值设为False + 'mioitems': [] + } for item in data} + + # 3. 批量查询MIOItem数据 + if mio_ids: # 避免空查询 + mioitems = MIOItemListSimpleSerializer( + instance=MIOItem.objects.filter( + mio__id__in=mio_ids + ).select_related("warehouse", "material"), + many=True + ).data + + # 4. 单次循环处理所有item + for item in mioitems: + mio_id = item['mio'] + if mio_id in mio_dict: + mio_dict[mio_id]['mioitems'].append(item) + # 更新is_tested状态(只要有一个item有test_date就为True) + if item.get('test_date'): + mio_dict[mio_id]['is_tested'] = True + + # 5. 直接返回原始data列表,避免额外转换 for item in data: - item['is_tested'] = None - mio_dict[item['id']] = item - mioitems = list(MIOItem.objects.filter(mio__id__in=mio_dict.keys()).values_list("mio__id", "test_date")) - for item in mioitems: - mioId, test_date = item - is_tested = False - if test_date: - is_tested = True - mio_dict[mioId]['is_tested'] = is_tested - datax = [mio_dict[key] for key in mio_dict.keys()] - return datax + item.update(mio_dict[item['id']]) + + return data def get_serializer_class(self): if self.action in ['create', 'update', 'partial_update']: From 9ed78f8d3242c5c15264dbb4da60e2feacd4e9d0 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sun, 4 Jan 2026 11:10:54 +0800 Subject: [PATCH 15/16] =?UTF-8?q?feat:=20mlogbbpatch=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E6=89=B9=E6=AC=A1=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/serializers.py | 5 +++++ apps/wpm/views.py | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/wpm/serializers.py b/apps/wpm/serializers.py index 968ae690..f306bccc 100644 --- a/apps/wpm/serializers.py +++ b/apps/wpm/serializers.py @@ -1049,6 +1049,11 @@ class MlogbwStartTestSerializer(serializers.Serializer): test_equip=test_equip ) +class MlogbOutPatchUpdateSerializer(CustomModelSerializer): + class Meta: + model = Mlogb + fields = ["batch"] + class MlogbOutUpdateSerializer(CustomModelSerializer): mlogbdefect = MlogbDefectSerializer(many=True, required=False) count_json = CountJsonSerializer(required=False, many=True) diff --git a/apps/wpm/views.py b/apps/wpm/views.py index 236bd779..1a0fc458 100644 --- a/apps/wpm/views.py +++ b/apps/wpm/views.py @@ -50,7 +50,8 @@ from .serializers import ( MlogQuickSerializer, MlogbwStartTestSerializer, HandoverListSerializer, - BatchChangeSerializer + BatchChangeSerializer, + MlogbOutPatchUpdateSerializer ) from .services import mlog_submit, handover_submit, mlog_revert, get_batch_dag, handover_revert from apps.wpm.services import mlog_submit_validate, generate_new_batch @@ -1020,18 +1021,22 @@ class MlogbInViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, BulkDestroyMode class MlogbOutViewSet(BulkUpdateModelMixin, CustomGenericViewSet): - perms_map = {"put": "mlog.update"} + perms_map = {"put": "mlog.update", "patch": "mlog.update"} queryset = Mlogb.objects.filter(material_out__isnull=False) serializer_class = MlogbOutUpdateSerializer + partial_update_serializer_class = MlogbOutPatchUpdateSerializer def perform_update(self, serializer): - ins: Mlogb = serializer.instance - mlog = MlogViewSet.lock_and_check_can_update(ins.mlog) - material_out = serializer.validated_data.get("material_out") - if material_out and material_out.tracking == Material.MA_TRACKING_SINGLE: - raise ParseError("单件产品不支持直接修改") - ins: Mlogb = serializer.save() - mlog.cal_mlog_count_from_mlogb() + if self.request.method == "PATCH": + serializer.save() + else: + ins: Mlogb = serializer.instance + mlog = MlogViewSet.lock_and_check_can_update(ins.mlog) + material_out = serializer.validated_data.get("material_out") + if material_out and material_out.tracking == Material.MA_TRACKING_SINGLE: + raise ParseError("单件产品不支持直接修改") + ins: Mlogb = serializer.save() + mlog.cal_mlog_count_from_mlogb() class FmlogViewSet(CustomModelViewSet): From 952cdb1bc7ba7f5adf99742326c00e33497257f6 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sun, 4 Jan 2026 13:55:09 +0800 Subject: [PATCH 16/16] =?UTF-8?q?feat:=20get=5Fbatch=5Fdag=E8=BF=98?= =?UTF-8?q?=E6=98=AF=E5=8F=AA=E8=BF=94=E5=9B=9E=E7=9B=B4=E6=8E=A5=E5=89=8D?= =?UTF-8?q?=E5=90=8E=E7=BA=A7=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wpm/services.py | 53 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/apps/wpm/services.py b/apps/wpm/services.py index 3f296312..7b265863 100644 --- a/apps/wpm/services.py +++ b/apps/wpm/services.py @@ -1062,6 +1062,7 @@ def get_batch_dag(batch_number: str, method="full"): } if method == "full": + raise ParseError("不支持获取全局关系链条") # 完整DAG模式 - 收集所有相关批次和边(原逻辑) nodes_set = {batch_ins.id} edges = [] @@ -1118,33 +1119,33 @@ def get_batch_dag(batch_number: str, method="full"): }) # 查询作为source的其他关系 - leftLogs = BatchLog.objects.filter(source_id__in=left_source_ids).exclude(id__in=exist_log_ids) - for log in leftLogs: - source = log.source.id - target = log.target.id - nodes_set.add(log.target.id) - edges.append({ - 'id': log.id, - 'source': source, - 'target': target, - "handover": log.handover.id if log.handover else None, - "mlog": log.mlog.id if log.mlog else None, - 'label': r_dict.get(log.relation_type, ""), - }) + # leftLogs = BatchLog.objects.filter(source_id__in=left_source_ids).exclude(id__in=exist_log_ids) + # for log in leftLogs: + # source = log.source.id + # target = log.target.id + # nodes_set.add(log.target.id) + # edges.append({ + # 'id': log.id, + # 'source': source, + # 'target': target, + # "handover": log.handover.id if log.handover else None, + # "mlog": log.mlog.id if log.mlog else None, + # 'label': r_dict.get(log.relation_type, ""), + # }) - rightLogs = BatchLog.objects.filter(target_id__in=right_target_ids).exclude(id__in=exist_log_ids) - for log in rightLogs: - source = log.source.id - target = log.target.id - nodes_set.add(log.source.id) - edges.append({ - 'id': log.id, - 'source': source, - 'target': target, - "handover": log.handover.id if log.handover else None, - "mlog": log.mlog.id if log.mlog else None, - 'label': r_dict.get(log.relation_type, ""), - }) + # rightLogs = BatchLog.objects.filter(target_id__in=right_target_ids).exclude(id__in=exist_log_ids) + # for log in rightLogs: + # source = log.source.id + # target = log.target.id + # nodes_set.add(log.source.id) + # edges.append({ + # 'id': log.id, + # 'source': source, + # 'target': target, + # "handover": log.handover.id if log.handover else None, + # "mlog": log.mlog.id if log.mlog else None, + # 'label': r_dict.get(log.relation_type, ""), + # }) else: raise ParseError("不支持的查询方法,请使用'full'或'direct'")