From a2f26c89942bacdb31a3a06fbf4379e06d13eb4f Mon Sep 17 00:00:00 2001 From: souplearn Date: Sat, 25 Jul 2026 01:53:18 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=A2=9E=E5=8A=A0=E6=8A=A5=E5=B7=A5?= =?UTF-8?q?=E8=B7=A8=E6=97=A5=E6=9C=9F=E5=B7=A5=E6=97=B6=E5=BD=92=E5=B1=9E?= =?UTF-8?q?=E6=8B=86=E5=88=86=E8=AE=BE=E8=AE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...026-07-25-report-time-allocation-design.md | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-25-report-time-allocation-design.md diff --git a/docs/superpowers/specs/2026-07-25-report-time-allocation-design.md b/docs/superpowers/specs/2026-07-25-report-time-allocation-design.md new file mode 100644 index 0000000..b747ccc --- /dev/null +++ b/docs/superpowers/specs/2026-07-25-report-time-allocation-design.md @@ -0,0 +1,350 @@ +# 报工跨日期工时归属拆分设计 + +## 背景 + +现有报工记录使用 `report_date` 作为单一报工日期。这个字段被报工记录、管理员审核、经理看板、导出、对账小账本、设备/模具使用统计等模块复用。 + +当员工夜里或凌晨跨日期工作时,单一 `report_date` 无法准确表达工时归属。例如: + +- `00:10` 上班,`11:00` 下班 +- `00:10-06:00` 是前一天夜班尾段 +- `06:00-08:00` 是作息空白区间,但按新规则应算加班 +- `08:00-11:00` 是当天白班 + +如果整条报工强行归前一天或当天,都会造成一部分工时、数量、工资统计不合理。 + +## 目标 + +实现一条原始报工记录可以按工时归属拆分到多个日期。 + +核心原则: + +- 原始报工记录仍然只保留一条,不拆成多条审核记录。 +- 管理员审核、作废、更改、水印、员工记录仍围绕原始报工记录。 +- 经理看板、导出、对账小账本、设备/模具统计、概览等统计类模块按拆分后的归属明细汇总。 +- 跨日期时,成品数量、不良数量、报废数量、换料次数、参考工资等涉及统计的字段按有效工时比例拆分。 +- 作息空白区间统一算加班,但不直接改变原始 `report_date`。 + +## 非目标 + +- 不把一条原始报工拆成多条 `ProductionReport`。 +- 不开放管理员手工编辑 `report_date`。 +- 不批量回改历史记录的原始 `report_date`。 +- 不改变员工扫码上班、换模具、下班提交的主流程。 + +## 术语 + +- 原始报工:员工提交或系统自动提交后形成的一条 `ProductionReport`。 +- 工序明细:一条报工下的产品工序明细。 +- 归属明细:按日期和班种拆出来的统计明细。 +- 有效工时:扣除符合规则的吃饭时间后,用于统计、工资和数量分摊的工时。 +- 空白区间:不属于白班、夜班、原加班区间的作息时间,例如默认 `06:00-08:00`、`17:20-18:00`。 + +## 推荐方案 + +采用“一条原始报工 + 多条归属明细”的方案。 + +### 原始记录 + +`ProductionReport` 继续保存员工真实提交的完整记录: + +- 考勤点 +- 员工 +- 上班时间 +- 下班时间 +- 报工明细 +- 审核状态 +- 作废状态 +- 更改状态 +- 原始 `report_date` + +原始 `report_date` 仍用于报工记录卡片的基础展示,但统计类模块不再只依赖它。 + +### 归属明细 + +新增一张后端表,例如 `production_report_allocations`。 + +建议字段: + +- `id` +- `report_id` +- `report_item_id` +- `attendance_point_name` +- `employee_phone` +- `allocation_date` +- `day_minutes` +- `overtime_minutes` +- `night_minutes` +- `effective_minutes` +- `good_qty` +- `defect_qty` +- `scrap_qty` +- `changeover_count` +- `reference_wage` +- `created_at` +- `updated_at` + +如果后续设备/模具统计需要直接按设备或模具拆分,可以再补: + +- `device_no` +- `mold_name` +- `product_name` +- `process_name` +- `stamping_method` + +第一版建议保留到 `report_item_id` 级别,设备/模具统计可以根据现有工序明细和设备段关联计算,避免一次性改表过重。 + +## 拆分规则 + +### 班种区间 + +按报工所属考勤点的作息配置计算: + +- 白班:`day_start` 到 `day_end` +- 原加班:`overtime_start` 到 `overtime_end` +- 夜班:`night_start` 到 `night_end` +- 午饭、晚饭:继续使用当前吃饭扣除规则 + +夜班跨天时,例如 `20:00-06:00`,按当前系统已有跨天区间逻辑处理。 + +### 空白区间 + +一天中不属于白班、原加班、夜班的时间,统一计入加班。 + +默认作息下: + +- `06:00-08:00` 算加班 +- `17:20-18:00` 算加班 + +空白区间只影响工时分布,不单独改变原始 `report_date`。 + +### 日期归属 + +归属明细按时间片所在的自然日期和夜班归属拆分。 + +默认作息 `20:00-06:00` 下: + +- `20:00-24:00` 夜班归属当天 +- `00:00-06:00` 夜班归属前一天 +- `06:00` 之后的空白加班、白班、加班归属当天 + +例子:`2026-07-25 00:10` 到 `2026-07-25 11:00` + +- `2026-07-24`:夜班 `350` 分钟 +- `2026-07-25`:加班 `120` 分钟,白班 `180` 分钟 + +### 数量和金额分摊 + +每个工序明细先计算总有效分钟,再按日期归属比例拆分。 + +公式: + +```text +归属比例 = 某归属日有效分钟 / 当前工序明细总有效分钟 +归属数量 = 原始数量 * 归属比例 +归属工资 = 原始参考工资 * 归属比例 +``` + +适用字段: + +- 成品数量 +- 不良数量 +- 报废数量 +- 换料次数 +- 参考工资 + +### 小数处理 + +后端内部保留两位小数。 + +导出和页面展示可以使用两位小数,避免比例拆分后出现不合理的整数强行取整。 + +如果未来现场要求成品必须整数,可以另开一个“整数分摊余数归属规则”,本次不引入。 + +## 数据流 + +### 员工提交报工 + +1. 员工扫码、填写报工并提交。 +2. 后端创建一条 `ProductionReport` 和对应 `ProductionReportItem`。 +3. 后端计算原始记录指标。 +4. 后端生成或刷新归属明细。 +5. 返回原始报工卡片。 + +### 系统自动提交 + +1. 自动提交服务创建原始报工。 +2. 原始结束时间为系统自动提交时间。 +3. 后端计算原始记录指标。 +4. 后端生成或刷新归属明细。 + +### 管理员审核或更改 + +管理员调整以下内容时,需要重算归属明细: + +- 上班时间 +- 下班时间 +- 换模具时间 +- 产品工序 +- 设备 +- 成品数量 +- 不良数量 +- 报废数量 +- 换料次数 +- 工序单价 +- 作废/撤销作废 + +保存流程: + +1. 校验管理员权限和编辑窗口。 +2. 保存原始记录修改。 +3. 重算原始记录指标。 +4. 删除旧归属明细。 +5. 生成新归属明细。 +6. 设置更改水印。 + +作废记录不参与统计。作废时可以保留归属明细,但统计查询必须过滤;也可以删除归属明细。第一版建议保留并在统计查询中过滤,方便撤销作废后立即恢复统计。 + +## 模块影响 + +### 员工报工记录 + +仍显示一条原始记录。 + +可在明细里补充展示工时归属,例如: + +```text +工时归属:2026-07-24 夜班 5小时50分;2026-07-25 加班 2小时、白班 3小时 +``` + +第一版只在管理员和经理侧展示归属摘要,员工侧报工记录保留现状。 + +### 管理员审核页 + +仍按原始记录审核。 + +管理员改时间或数量后,页面返回重算后的原始指标和归属摘要。 + +审核页可以展示归属摘要,帮助管理员知道这条记录会被拆到哪些日期。 + +### 经理报工看板 + +看板按归属明细汇总,不再只按 `ProductionReport.report_date` 汇总。 + +筛选日期查的是 `allocation_date`。 + +同人、同考勤点、同项目、同产品、同工序、同冲压方式的聚合,应基于归属明细汇总。 + +### 经理看板导出 + +第一张明细 sheet 按归属明细导出。 + +建议保留两个日期字段: + +- 原始报工日期 +- 统计归属日期 + +这样经理既能追溯原始记录,也能看到工资和工时实际归属。 + +参考工资、工序单价、工种、备注等现有导出字段继续保留。 + +工资汇总 sheet 按归属明细汇总。 + +### 对账小账本 + +已报工成品数量使用归属明细统计。 + +最后一道工序判断仍按产品清单数字工序大小确定。 + +月份归属改为 `allocation_date` 所在月份。 + +### 设备/模具使用统计 + +默认统计审核通过且未作废记录。 + +时间筛选按归属明细日期。 + +普通设备/模具: + +- 时长按归属明细有效分钟汇总 +- 报工次数按原始报工 ID 去重 + +建议第一版: + +- 时长按归属明细汇总 +- 报工次数按原始报工 ID 去重 + +清洗设备: + +- 清洗数量按归属明细数量汇总 +- 报工次数按原始报工 ID 去重 + +### 概览 + +涉及今日、历史待审核、已通过等指标时: + +- 审核状态类数量仍按原始报工记录。 +- 工时、数量、工资类指标按归属明细。 + +## 错误处理 + +- 如果归属明细生成失败,原始报工保存应回滚,避免原始记录和统计数据不一致。 +- 如果旧数据没有归属明细,统计查询应提供兼容兜底:用原始记录实时计算或用迁移脚本补齐。 +- 如果有效分钟为 `0`,数量和工资不做比例拆分,统一归到原始 `report_date`,同时记录日志,避免除零。 +- 如果作息配置缺失,使用当前默认作息。 + +## 历史数据处理 + +历史原始 `report_date` 不批量回改。 + +但为了让统计类模块切换到新口径,建议提供一次性迁移脚本: + +1. 扫描审核通过且未作废的历史报工。 +2. 根据记录当时的上下班时间、工序明细、考勤点作息生成归属明细。 +3. 不修改原始 `report_date`。 +4. 迁移后输出处理数量、跳过数量、异常记录 ID。 + +如果担心历史作息被后续修改导致还原不准,第一版接受使用当前考勤点作息生成历史归属明细。后续如要严格追溯,需要在报工时快照作息配置。 + +## 测试用例 + +核心用例: + +- `2026-07-25 00:10` 到 `2026-07-25 11:00` + - `2026-07-24` 夜班 `350` 分钟 + - `2026-07-25` 加班 `120` 分钟,白班 `180` 分钟 +- `2026-07-25 05:59` 到 `2026-07-25 11:00` + - `2026-07-24` 夜班 `1` 分钟 + - `2026-07-25` 加班 `120` 分钟,白班 `180` 分钟 +- `2026-07-24 20:00` 到 `2026-07-25 06:30` + - `2026-07-24` 夜班 `600` 分钟 + - `2026-07-25` 加班 `30` 分钟 +- `2026-07-25 06:00` 到 `2026-07-25 08:00` + - `2026-07-25` 加班 `120` 分钟 +- `2026-07-25 17:20` 到 `2026-07-25 18:00` + - `2026-07-25` 加班 `40` 分钟 +- 覆盖吃饭扣除和 5 分钟缓冲规则。 +- 覆盖管理员修改上下班时间后归属明细重算。 +- 覆盖管理员修改换模具时间后归属明细重算。 +- 覆盖作废记录从统计中排除。 +- 覆盖撤销作废后统计恢复。 + +## 实施顺序建议 + +1. 后端新增归属计算服务和单元测试。 +2. 新增归属明细表和迁移脚本。 +3. 报工提交、自动提交、管理员审核保存后写入归属明细。 +4. 经理看板和导出切换到归属明细。 +5. 对账小账本、设备/模具统计、概览按风险顺序切换。 +6. 前端增加归属摘要展示。 +7. 跑迁移脚本补齐历史归属明细。 + +## 已固定技术口径 + +当前方案已按以下业务口径固定: + +- 涉及拆分的字段都按工时比例拆。 +- 空白作息区间算加班。 +- 空白作息区间不影响原始 `report_date`。 +- 原始报工记录不拆。 +- 报工次数统计按原始报工 ID 去重,不因一条报工拆到多个日期而重复计数。