factory/apps/wpm/scripts/batch_gxerp.md

217 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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=False` 且 `material_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.id``mlogb_q_ids`,用于"加工前缺陷"统计。
### 2. 合格率计算
```
完全合格率 = round(count_ok_full / count_real * 100, 2)
合格率 = round(count_ok / count_real * 100, 2)
```
`decimal.InvalidOperation` 异常时(如分母为 0`0`
### 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. 日期字段
```python
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. `data``MyJSONEncoder` 序列化后回填 `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` 全量遍历。
## 十、修订记录
- 修复 `小日期 / 大日期` 命名与含义反向,`小日期` 取最早日期、`大日期` 取最晚日期;同步修正 `batch_bxerp.py`、`batch_gzerp.py` 中所有同类字段。
- `get_f_l_date` 改为先 `datetime.strptime` 解析为 `date` 对象再比较,移除对字符串字典序的隐式依赖;非法日期片段记录日志后跳过。
- 所有合格率/直通率计算的兜底 `except` 同时捕获 `decimal.InvalidOperation``ZeroDivisionError`,以兼容 `Decimal` 与原生数值的除零场景;`batch_bxerp.py` 与 `batch_gzerp.py` 中仅捕获 `decimal.InvalidOperation` 的合格率分支也同步扩展。