factory/apps/wpm/scripts/batch_gxerp.md

14 KiB
Raw Blame History

batch_gxerp.py 批次统计计算规则

apps/wpm/scripts/batch_gxerp.py 用于按 批次号 (batch) 汇总该批次在各工序组下的生产/检验数据,写入 BatchSt.data (JSON),同时维护 first_time / last_time


一、入参与前置条件

参数 说明
batch 批次号
mgroup_obj 工序组对象(当前实现未直接使用,仅作为入口签名)
  • 必须存在 BatchSt.objects.get(batch=batch, version=1),否则报错并退出。
  • 遍历 Mgroup.objects.all().order_by("sort"),对每个工序组分别统计。
  • 仅统计已提交且已出库的报工单:mlog.submit_time__isnull=Falsematerial_out__isnull=False,并要求 need_inout=True

二、字段命名约定

输出 JSON data 的 key 形式如下(<G> 代表工序组名):

Key 模式 含义
批次号 批次号
<G>_日期 该工序组所有报工日期,去重后 ; 拼接
<G>_小日期 / <G>_大日期 该工序组最早 / 最晚日期(YYYY-MM-DD
<G>_操作人 操作人姓名去重后 ; 拼接
<G>_班次 班次名去重后 ; 拼接
<G>_count_use 领用数(来自上游 mlogb_from
<G>_count_real 实际生产数
<G>_count_ok 合格数
<G>_count_notok 不合格数
<G>_count_ok_full 完全合格数
<G>_count_pn_jgqbl 加工前不良数(来自上游 mlogb_from
<G>_合格率 合格率(%
<G>_完全合格率 完全合格率(%
<G>_缺陷_<缺陷名> 该工序组的缺陷数量(按缺陷名汇总)
<G>_缺陷_<缺陷名>_比例 缺陷数 / count_real × 100单位 %
<G>_加工前_缺陷_<缺陷名> 上游 mlogb_from 上的缺陷数量
<G>_加工前_缺陷_<缺陷名>_比例 加工前缺陷数 / count_use × 100单位 %

三、主流程:按工序组汇总(普通报工,is_fix=False

数据源 mlogb1_qs

Mlogb where mlog.submit_time IS NOT NULL
        and material_out IS NOT NULL
        and mlog.mgroup = <G>
        and mlog.is_fix = False
        and batch = <batch>
        and need_inout = True

1. 累加规则

对每条 Mlogb

字段 累加来源
count_use mlogb_from.count_use(上游报工子项;若无 mlogb_from 则跳过)
count_pn_jgqbl mlogb_from.count_pn_jgqbl
count_real 当前 item.count_real
count_ok 当前 item.count_ok
count_ok_full 当前 item.count_ok_full or 0
count_notok 当前 item.count_notok or 0
操作人 mlog.handle_user(最终去重,按 name 拼接)
日期 mlog.handle_date(最终去重排序)
班次 mlog.shift.name

并记录所有上游 mlogb_from.idmlogb_q_ids,用于"加工前缺陷"统计。

2. 合格率计算

完全合格率 = round(count_ok_full / count_real * 100, 2)
合格率     = round(count_ok      / count_real * 100, 2)

decimal.InvalidOperation 异常时(如分母为 00

3. 缺陷统计

  • 当前工序缺陷:MlogbDefect.filter(mlogb__in=mlogb1_qs, count__gt=0).values('defect__name').annotate(total=Sum('count'))
    • <G>_缺陷_<name> = total
    • <G>_缺陷_<name>_比例 = round(total / count_real * 100, 2)
  • 加工前(上游)缺陷:MlogbDefect.filter(mlogb__id__in=mlogb_q_ids, count__gt=0)...
    • <G>_加工前_缺陷_<name> = total
    • <G>_加工前_缺陷_<name>_比例 = round(total / count_use * 100, 2)

4. 日期字段

data[f"{G}_小日期"] = min(日期列表).strftime("%Y-%m-%d")  # 最早日期
data[f"{G}_大日期"] = max(日期列表).strftime("%Y-%m-%d")  # 最晚日期

最终 <G>_日期 被覆写为去重排序后的字符串(; 拼接)。


四、外观检验返修(is_fix=True

数据源 mlogb2_qs

Mlogb where mlog.submit_time IS NOT NULL
        and material_out IS NOT NULL
        and mlog.mgroup.name = '外观检验'
        and mlog.is_fix = True
        and batch = <batch>
        and need_inout = True

输出字段:

Key 计算
外观检验_返修_日期 报工日期,去重 ; 拼接
外观检验_返修_操作人 操作人姓名,去重 ; 拼接
外观检验_返修_count_real Σ count_real
外观检验_返修_count_ok Σ count_ok
外观检验_返修_count_ok_full Σ count_ok_full or 0
外观检验_返修_缺陷_<name> MlogbDefect 按缺陷名汇总
外观检验_返修_缺陷_<name>_比例 round(total / 外观检验_返修_count_real * 100, 2)

五、外观检验车间库存抽检

数据源 ft_qs

FtestWork where type2 = TYPE2_SOME
          and wm.mgroup.name = '外观检验'
          and batch = <batch>
          and submit_time IS NOT NULL

输出字段:

Key 计算
外观检验_车间库存抽检_日期 test_date 去重 ; 拼接
外观检验_车间库存抽检_操作人 test_user.name 去重 ; 拼接
外观检验_车间库存抽检_count_notok Σ count_notok or 0
外观检验_车间库存抽检_缺陷_<name> FtestworkDefect 按缺陷名汇总(无比例字段)

六、外观检验汇总指标(仅当存在 外观检验_count_ok 时计算)

Key 公式
外观检验_总合格数 外观检验_count_ok + 外观检验_返修_count_ok默认0
外观检验_总合格率 round(外观检验_总合格数 / 外观检验_count_real * 100, 2)
外观检验_完全总合格数 外观检验_count_ok_full + 外观检验_返修_count_ok_full默认0
外观检验_完全总合格率 round(外观检验_完全总合格数 / 外观检验_count_real * 100, 2)
外观检验_直通合格数 外观检验_总合格数 - 外观检验_车间库存抽检_count_notok默认0

直通合格率(依赖尺寸检验)

仅当 尺寸检验_合格率 存在时:

外观检验_直通合格率  = round(外观检验_总合格率 * 尺寸检验_合格率 / 100, 2)
外观检验_直通合格率2 = round(外观检验_直通合格数 / 尺寸检验_count_use * 100, 2)

仅当 尺寸检验_完全合格率 存在时:

外观检验_完全直通合格率 = round(外观检验_完全总合格率 * 尺寸检验_完全合格率 / 100, 2)

异常(InvalidOperation / ZeroDivisionError)回退为 0


七、写回 BatchSt

  1. dataMyJSONEncoder 序列化后回填 batchst.data
  2. get_f_l_date(data):扫描所有以 _日期 结尾的字段,按 ; 拆开后逐个 datetime.strptime("%Y-%m-%d") 解析为 date 对象,再取最小/最大并转成上海时区的 00:00:00 / 23:59:59,得到 first_time / last_time。无法解析的片段会写日志并跳过。
  3. 仅当现有 first_time 为空或新值更早时更新;last_time 反之。
  4. 调用 batchst.save() 持久化。

八、关键依赖与字段含义速查

模型字段 中文释义 来源
Mlogb.count_use 领用数 报工子项(来自 mlogb_from
Mlogb.count_real 实际生产数 报工子项
Mlogb.count_ok 合格数 count_real - count_notok
Mlogb.count_ok_full 完全合格数 count_real - count_notok_full
Mlogb.count_notok 不合格数 缺陷 okcate=30 求和
Mlogb.count_pn_jgqbl 加工前不良 MlogbDefect 中加工前不良求和
Mlogb.need_inout 是否需出入库 True 参与统计
Mlogb.mlogb_from 上游报工子项 用于追溯领用与加工前缺陷

九、已知潜在问题

  1. mgroup_obj 入参未使用:当前实现忽略该参数,对所有 Mgroup 全量遍历。

十、大批直通良率(main 末尾自动触发)

定义

  • 大批 = 拆出检验小批的那个批(如 2605-BJ-0023,退火后合批改名产生;外购半成品则为采购入库批)。由 BatchLog 拆合批关系确定,不依赖批次号命名和工艺路线。
  • 直通良率 = 大批下所有子批次 外观检验_直通合格数 之和 ÷ 大批白料数(进炉前数量)× 100。

归属锁定(BatchSt.zt_batch

批次统计完成后(handle_zt

  1. 当前批 data尺寸检验_count_real / 外观检验_count_real 时解析归属(resolve_zt_big
    • 唯一拆批来源handover split→ 该来源即大批;
    • 合批复检批(如 大批-A-1)→ 所有合批来源归属同一大批时继承;不一致则记日志不归属;
    • 无任何拆合批上游 → 自身即大批(未拆批直接检验、外购直检)。
  2. 归属写入 zt_batch(锁定,已锁定的直接复用);随后重算大批(cal_zt_big)。
  3. 大批自身重算时(有子批 zt_batch 指向它)也会触发重算。
  4. 提交时纯继承services.inherit_zt_batch):拆批交接、报工改号提交时,目标批直接继承来源批已有的 zt_batch(来源料子集式流转,永远安全);合批不继承——不良集中批(如 黑检片-00)等共享池会汇入多个大批的料,继承会造成错误归属,合批复检批(-A-1)的归属仍由统计侧"所有来源同大批"判定。已有归属冲突时不覆盖并记日志。

分子

BatchSt.filter(zt_batch=大批) 各批 外观检验_直通合格数 之和(+大批自身若有)。子批发现从大批侧沿拆批边/同大批合批边展开(cal_zt_big,支持 -A-1 复检批、多级拆分)。检验前合并的子批无检验数据,天然不重复计数。

分母(白料数,_zt_white_count 回溯)

从大批沿 BatchLog 向上回溯(限深 ZT_MAX_DEPTH=12,按路径防环,跳过合并回自身的整理性边):

  • 锚点:批次存在锚点工段(ZT_ANCHOR_MGROUPS=["黑化"])产出报工时停止,白料数 = 该批锚点工段 mlogb_from.count_use(进炉前领用数)。多次黑化的链取离大批最近的一次。
  • 分摊_zt_source_out_ratio):来源批有多条生产主线流出边(报工改号/拆批/汇入大批格式 ZT_BIG_BATCH_RE 的合批)时,按各去向数量占比分摊;汇入不良集中批(如 黑检片-00)的旁路不占白料份额,使炉后不良计入生产主线分母。单一去向全额传递。
  • 边数量:拆批取 Handoverb(batch=目标批),合批取 Handoverb(batch=来源批),报工改号取目标产出行的 mlogb_from.count_use;无明细时兜底整单数量。
  • 外购/遗留兜底:无上游谱系 → 白料数 = 该批原料性入库数量之和(MIOItem,仅 pur_in/other_in,排除生产入库),口径记 入库数量
  • 老数据兜底_zt_white_legacyBatchLog 上线前的批次无边且无入库,沿 BatchSt.handover/mlog 创建来源续上回溯,口径记 谱系兜底
  • 性能护栏:回溯按批次号记忆化(网状/菱形谱系线性复杂度,防环截断过的节点不入缓存);单次回溯节点访问超 ZT_MAX_VISITS=3000 时放弃,口径记 回溯规模超限(负缓存,不重复付代价)。

缓存与隔离

  • 白料数算出后缓存在大批 data(含算不出的负缓存);子批触发的重算直接复用、只重聚合分子。大批自身重算时 data 重建、缓存失效并重新回溯。上游补改报工后需大批重算或全量回刷才会刷新。
  • 总合格数 > 白料数(如白料池循环整理导致回溯不全)→ 直通_良率 置空并在口径标注 谱系异常记错误日志2026-06 抽样 120 个大批116 正常 / 2 异常隔离 / 2 谱系断头不可得)。

输出字段(写入大批 data

Key 含义
直通_子批次 归属本大批的子批号 ; 拼接
直通_总合格数 分子
直通_白料数 分母(不可得为 null
直通_良率 百分比(不可得/异常为 null
直通_口径 白料来源口径:黑化领用 / 入库数量 / 谱系兜底 及异常标注

接口(/wpm/batchst/

  • 响应自带 zt_batch(所属大批号);?with_zt_big=yes 时每行附 zt_big = 所属大批的完整 BatchSt直通_*),自身即大批时为 null直通字段就在本行 data 里)。
  • 过滤:?zt_batch=<大批号> 查某大批全部子批;with_source_near=yes 仍可附 BatchLog 直接来源批。

部署

  1. python manage.py migrate wpm(新增 BatchSt.zt_batch,迁移 0133_batchst_zt_batch)。
  2. 历史回刷:python scripts/correct_zt_gxerp.py [起始日期] [--fresh]——只解析归属并重算各大批 直通_* 字段(每个大批仅算一次),不重建批次其他统计;--fresh 强制清缓存重新回溯白料。全量重建统计仍用 scripts/correct_batchst.py(其 main 末尾也会触发直通计算)。

十一、修订记录

  • 修复 小日期 / 大日期 命名与含义反向,小日期 取最早日期、大日期 取最晚日期;同步修正 batch_bxerp.pybatch_gzerp.py 中所有同类字段。
  • get_f_l_date 改为先 datetime.strptime 解析为 date 对象再比较,移除对字符串字典序的隐式依赖;非法日期片段记录日志后跳过。
  • 所有合格率/直通率计算的兜底 except 同时捕获 decimal.InvalidOperationZeroDivisionError,以兼容 Decimal 与原生数值的除零场景;batch_bxerp.pybatch_gzerp.py 中仅捕获 decimal.InvalidOperation 的合格率分支也同步扩展。
  • 新增大批直通良率统计(见第十节):BatchStzt_batch 归属锁定字段,main 末尾自动解析归属并重算大批的 直通_白料数 / 直通_总合格数 / 直通_良率 / 直通_口径