vault backup: 2026-05-26 00:18:06

This commit is contained in:
Willie
2026-05-26 00:18:06 +08:00
parent 7987adb131
commit 896265cad6
4 changed files with 592 additions and 3 deletions

View File

@@ -197,6 +197,9 @@
},
"active": "849c5ff8936a2b67",
"lastOpenFiles": [
"prop-acc/scenarios/meter/read-batch-via-excel-import.md",
"prop-acc/scenarios/meter/read-single-meter-manual.md",
"prop-acc/scenarios/meter/decommission-without-replacement.md",
"prop-acc/scenarios/meter/replace-broken-meter.md",
"prop-acc/scenarios/meter/register-single-meter.md",
"prop-acc/scenarios/meter/init-new-community-batch.md",
@@ -222,9 +225,6 @@
"prop-acc/scenarios/prepaid/consume-meter-bill.md",
"prop-acc/scenarios/prepaid/consume-multiple-bills-priority.md",
"prop-acc/scenarios/prepaid/consume-monthly-property-bill.md",
"prop-acc/scenarios/prepaid/deposit-via-miniapp-pending.md",
"prop-acc/scenarios/prepaid/deposit-additional-topup.md",
"prop-acc/scenarios/prepaid/deposit-first-time.md",
"prop-acc/scenarios/prepaid",
"prop-acc/concepts/prepaid",
"prop-acc/scenarios/deposit",

View File

@@ -0,0 +1,191 @@
---
title: prop-acc · meter · 场景 - 退役不换表(房屋拆除/业户永久弃用)
aliases:
- 退役不换表
- 永久弃用
- decommission-without-replacement
- 场景-计量表退役不换
tags:
- 场景
- prop-acc
- 计量表
- 表管理
audience:
- 业务人员
status: 已发布
sub_feature: meter
last_review: 2026-05-26
code_version: 2026-05-22
---
# 场景:退役不换表(房屋拆除/业户永久弃用)
物理表**不再需要**(房屋拆除 / 业户永久搬走 / 商铺撤店 / 法定使用年限到不续装),系统**只退役不建新表**。`decommission_reason``Removed` / `Expired` 等,不走 `ReplaceMeterAction`
## 典型情境
> [!example] 真实情境(一):房屋拆除
> 嘉禾花园三期某栋老楼要拆迁重建,3 单元 1-6 层 30 户业主已全部搬走,准备拆楼。该单元所有水电气表(共 90 张)需要**永久退役**(房子拆了表也没了)。
> [!example] 真实情境(二):商铺撤店
> 一楼商铺(原餐饮店)经营 5 年后结业,装修拆除,新承租人方向未定。原餐饮店专用商业电表 + 燃气表暂不需要,**退役归档**(等新承租人入驻再视情况建新表)。
> [!example] 真实情境(三):法定使用年限到
> 一批 2010 年装的电表到 2026 年达到法定 15 年使用年限。物业评估:
> - 部分表性能仍好 + 业户无投诉 → 送校验,合格继续用([[replace-broken-meter|换表]] 的 `Calibration` reason)
> - 部分表频繁出问题 + 业户投诉 → 退役换新表(`Replaced`)
> - 个别表所在房屋已无人居住 → **退役不换表**(`Expired`)
## 业务人员视角(王主管)
### 第 1 步:确认场景与原因
选择正确的 `decommission_reason`:
| 场景 | 推荐 reason |
|---|---|
| 房屋拆除 | `Removed` |
| 业户永久搬走且无新业户 | `Removed` |
| 法定年限到不续装 | `Expired` |
| 校验送检中暂停(后续可能恢复) | `Calibration`(暂停态,不算永久退役)|
| 损坏不修(整体弃用)| `Damaged` |
| (换新表替代) | `Replaced`**走 [[replace-broken-meter]] 不是本场景** |
### 第 2 步:打开表
后台 → 计量表 → 按 asset 查找 → 进 `ViewMeter`(`is_active=true`)。
### 第 3 步:`EditMeter` 改字段(或专用退役 Action,看 UI 设计)
> [!info] UI 实现注意
> 当前实现可能**没有专门的"退役不换表"按钮**,而是通过:
> - `EditMeter` 页直接改 `is_active=false` + 填 `decommissioned_at` + `decommission_reason` + `final_reading`
> - 或自定义"退役"Action(若实现了)
>
> 看具体 `MeterForm` 的字段开关。若没专用按钮,走 EditMeter。
填字段:
| 字段 | 填什么 |
|---|---|
| `is_active` | **false**(取消勾选)|
| `decommissioned_at` | 2026-05-26 |
| `decommission_reason` | `Removed`(本场景一) / `Expired` / `Damaged` |
| `final_reading` | 退役那天的物理读数(若可读)/ 0(若表已被拆除无法读)|
| 备注 | 关键说明,如 "三期 3-1-101 拆迁,房屋已拆,表无法回读" |
### 第 4 步:提交
系统:
1. 校验旧表 `is_active=true`(`EditMeter` 守护 `->visible(is_active)`)
2. update Meter:`is_active=false`, `decommissioned_at`, `decommission_reason`, `final_reading`
3. **不建新表**(与 [[replace-broken-meter|换表]] 的关键区别)
### 第 5 步:后续不抄表
`MetersNeedingReadingListWidget` 自动**不再显示**此表(`is_active=false` 过滤)。
历史 reading 保留,可查。
## 批量退役
如果是单元拆除(90 张表一起退役),逐张走太慢。可能的批量方案:
| 方案 | 实现 |
|---|---|
| **List 页批量 Edit**(若有 BulkAction) | 选中多张 → 批量改 `is_active=false` |
| **Excel 导入"退役清单"** | 类似 `MeterInitializationImporter`,但当前没此功能(可补)|
| **tinker 脚本**(运维)| `Meter::whereIn('id', [...])->update([...])`,**留 audit log** |
当前推荐:**业务量小的话逐张 EditMeter**;**业务量大的话联系运维**走 tinker(批量改字段 + 留事由记录)。
## 系统流程
```mermaid
sequenceDiagram
participant 王主管
participant Filament
participant EditMeter
participant 数据库
王主管->>Filament: 找到要退役的表 → ViewMeter → 编辑
Filament->>EditMeter: 渲染 form
王主管->>EditMeter: is_active=false + 填 decommissioned_at + reason + final_reading + 备注
EditMeter->>EditMeter: 校验 is_active=true(原状态)+ Policy update 权限
EditMeter->>数据库: update Meter 字段
数据库-->>Filament: ok
Filament-->>王主管: 跳回 ViewMeter,状态显示"已退役 - Removed - 2026-05-26"
```
## 退役后的状态
| 字段 | 退役后值 |
|---|---|
| `is_active` | false |
| `decommissioned_at` | 2026-05-26 |
| `decommission_reason` | `Removed` |
| `final_reading` | 退役当天读数(或 0)|
| `replaced_meter_id` | null(没有继任者)|
| 后续是否能改字段 | **几乎不能**(`EditMeter` `visible(is_active)` 守护 + Policy 拦截)|
| 后续是否能删 | 仅当**无任何 reading** 时(罕见)|
详见 [[decommission-and-locking]]"退役后的行为"段。
## 与"换表"的关键差异
| 维度 | [[replace-broken-meter|换表(Replaced)]] | **退役不换表(本场景)** |
|---|---|---|
| `decommission_reason` | `Replaced` | `Removed` / `Expired` / `Damaged` |
| 是否建新表 | ✅ 是,带 `-R1` 后缀 | ❌ 否 |
| 业户后续是否要付水电费 | 是(用新表继续计费)| 否(无表无计费)|
| 房屋状态 | 仍有人住 | 通常拆 / 撤 / 弃用 |
| 触发 UI | `ReplaceMeterAction`(专用)| `EditMeter`(改字段)|
## 业户视角
业户**通常感知不到**这条系统操作,因为业户本人也搬走 / 房屋已拆。
如果是商铺撤店 / 房屋暂时无人(后续可能有新业户),系统层面表已退役,**未来如有新业户入住要重新装表 → 建新表(走 [[register-single-meter]]),不是"复活"旧表**。
## 常见问题
> [!question] 退役表能"复活"吗(`is_active=false → true`)?
> Policy 设计上**不允许**(`EditMeter` 在 `is_active=false` 时隐藏所有编辑)。如果真需要复活:
>
> - tinker 改字段(运维,留事由)
> - 推荐做法:**建新表**替代,旧表保持退役状态(历史档案)
> [!question] 退役表如何处理未付的历史 Bill?
> 表退役**不影响**已生成的 Bill(Bill 关联 reading 关联表,数据完整)。业户该付的还是要付。
>
> 若业户也搬走联系不上:走逾期催收流程(不在本场景)。
> [!question] 退役不换表后,房屋还在但暂无业户用电怎么办?
> 房屋空置但有可能后续入驻 → **保留表 active 状态**,只是没有抄表数据(零用量)。每月账单可能仍生成(看 RatePlan 是否有 `min_amount`,详见 [[multiplier-and-tiered-pricing|min/max 封顶]])。
>
> 真的永久不需要 → 退役。
> [!question] `decommission_reason` 选错了能改吗?
> 退役后修改字段被 Policy 拦截。若改错只能 tinker 修(运维 + 留事由)。
>
> **预防**:退役前确认场景,选对 reason。
> [!question] 退役了但表上物理装着没拆走怎么办?
> 系统层面无区别(系统不管物理状态,只管"系统层面已退役")。物业人员需要**实际去现场拆表**(若房屋拆迁要拆楼)或**留存**(若只是商铺暂关)。系统不管理物理库存。
> [!question] 表的物理库存追踪?
> 当前系统**不涉及**。物业的"实物计量表"库存(回收、报废、复用)在 ERP / 资产管理系统里处理,不在本子模块。
## 异常分支
- 换表(有新表替代)→ [[replace-broken-meter]]
- 误退役想撤销 → 困难,见上方"复活"问题
- 退役后无历史读数想删表 → 见 [[decommission-and-locking]]"退役表的物理删除"段
## 相关文档
- [[decommission-and-locking]]
- [[replace-broken-meter]]
- [[meter-vs-meter-reading]]
- [[multiplier-and-tiered-pricing]]

View File

@@ -0,0 +1,228 @@
---
title: prop-acc · meter · 场景 - 一次导入整月所有读数(Excel 批量)
aliases:
- 批量抄表
- Excel 导入抄表
- read-batch-via-excel-import
- MeterReadingsImporter
- 场景-Excel 批量抄表
tags:
- 场景
- prop-acc
- 计量表
- 抄表
- 批量导入
audience:
- 业务人员
- 抄表员
status: 已发布
sub_feature: meter
last_review: 2026-05-26
code_version: 2026-05-22
---
# 场景:一次导入整月所有读数(Excel 批量)
中型物业(几百到上千张表)、未上集抄系统的,抄表员**月底一次性**把全社区抄表数据填入 Excel,业务人员**上传导入**。
## 典型情境
> [!example] 真实情境
> 嘉禾花园 300 户 + 公共部位 + 商铺,合计 1,200 张表(水电气混)。未上集抄。抄表员李师傅 + 团队**每月最后一周**集中抄表 5-7 天,完成后:
>
> 1. 整理 Excel(1,200 行,每行一张表的本月读数)
> 2. 王主管下载"抄表读数模板"
> 3. 抄表数据填入模板对应列
> 4. 上传导入
## 业务人员视角
### 第 1 步:下载抄表模板
后台 → 计量表 → 列表 → 顶部 **"下载抄表模板"** 按钮(`ExportMeterReadingsAction`,**注意**:命名误导,实际是下载模板)。
> [!info] 命名问题(issue.md Q5)
> `ExportMeterReadingsAction` 的名字让人以为是"导出读数"(把数据从系统导出来),实际是**"下载抄表模板"**(给抄表员填的空模板,预填上次读数)。issue.md Q5 待补:重命名为 `DownloadMeterReadingsTemplateAction`。
下载的 Excel 包含:
| 列 | 说明 | 预填 |
|---|---|---|
| 房号 | asset 编号 | ✅ 系统填(对应每张已建表) |
| 表编号 | meter code | ✅ 系统填 |
| 费用类型 | 水/电/燃气 | ✅ 系统填 |
| 上次读数 | 上月该表 reading | ✅ 系统填(供抄表员对比)|
| **本次读数** | 本月该表 reading | **❌ 空,抄表员填**|
| 抄表日期 | 本次 read_at | **❌ 空,抄表员填**(或默认月底)|
| 备注 | 选填 | ❌ |
预填上次读数让抄表员**对比时有参考**(本次 > 上次 才合理),也防止漏填行(看清楚每行该填什么)。
### 第 2 步:抄表员填本月数据
抄表员李师傅团队**整月**(或月末几天)做这事:
- 现场抄表 + 拍照(用 App / 纸质本)
- 数据填入模板
- 完成后交回王主管
### 第 3 步:上传导入
后台 → 计量表 → 列表 → 顶部 **"导入抄表数据"** 按钮(`ImportActionWithExcel` + `MeterReadingsImporter`)→ 选 asset_type → 上传 → 提交。
系统:
1. 解析 Excel(走 `BaseImporter`)
2. 每行校验(meter 存在?asset 匹配?读数合法?)
3. 批量建 MeterReading(每行一条,`source=manual`,`operated_by=导入操作员`,无 photo_url —— 走批量没拍照)
4. 算 consumption
5. 报告:成功 N 条,失败 M 条
> [!warning] 批量导入无 photo_url
> Excel 模板没法批量上传照片。批量导入的 reading **photo_url 为空**。
>
> 业务上推荐:**抄表员独立留照片**(手机相册按月归档),业户事后争议时翻照片(虽然不在系统里)。或者:
> - 高争议表(住宅)走 [[read-single-meter-manual|单录 + 拍照]]
> - 低争议表(商铺 / 公共)走批量导入(节省时间)
### 第 4 步:核对
导入后:
- `MetersNeedingReadingListWidget` 显示"本月未抄表"清单 → 此时应 0 条(或个别遗漏)
- 抽样验证几张表的 reading 数据正确
- `HighConsumptionReadingsListWidget` 看是否有异常用量
### 第 5 步:触发账单生成
导入完成后:
- **自动触发**(`MeterReadingsImporter` 完成后默认调 `GenerateBillsFromMeterReadingsAction`)
- 或**手动触发**(`ListMeters` 上的"生成账单"按钮,选刚导入的 readings)
详见 [[bill-generation-pipeline]]。
## 系统流程
```mermaid
sequenceDiagram
participant 李师傅
participant 王主管
participant Filament
participant MeterReadingsImporter
participant GenerateBills[GenerateBillsFromMeterReadingsAction]
participant 数据库
王主管->>Filament: 下载抄表模板(预填上次读数)
Filament-->>王主管: 模板.xlsx
王主管->>李师傅: 转发模板
李师傅->>李师傅: 现场抄表 + 填模板
李师傅->>王主管: 已填模板.xlsx
王主管->>Filament: 导入抄表数据 → 选 asset_type + 上传
Filament->>MeterReadingsImporter: parse + chunk 处理(100/批)
loop 每批
MeterReadingsImporter->>数据库: 开启事务
loop 每行
MeterReadingsImporter->>数据库: 校验 meter + asset
alt 通过
MeterReadingsImporter->>数据库: 建 MeterReading
else 失败
MeterReadingsImporter->>MeterReadingsImporter: 记失败行
end
end
alt 全成功
MeterReadingsImporter->>数据库: 提交
else 任一失败
MeterReadingsImporter->>数据库: 回滚整批
end
end
MeterReadingsImporter->>GenerateBills: handle(刚建的 readings)
GenerateBills->>数据库: 批量建 Bill + 回写 reading.bill_id
MeterReadingsImporter-->>Filament: 报告
Filament-->>王主管: 通知 + 失败行下载
```
## 抄表员视角(李师傅)
整月抄表流程:
1. **第 1-5 周(月初)**:正常工作 + 部分非紧急表抄(若有时间)
2. **第 25-30 日(月末)**:集中抄表
- 按楼栋 + 单元顺序(避免漏)
- 每户:开门(若有人)/ 看公共表(若装在外)
- 读表 → 拍照 → 填模板
3. **30 日 / 月底**:整理完整 Excel → 交回王主管
工作量:1,200 张表 / 月 / 1 抄表员 → 约 40 张 / 天 / 5 天工作。比单录(后台一张张点)快 3-5 倍。
## 业户视角
业户**无感知** —— 抄表员上门时业户可能在家也可能不在(公共表 / 燃气表通常装在楼道,不用进户)。
## `MeterReadingsImporter` 双义列名问题(issue.md Q5)
> [!warning] 已知 silent corruption 风险
> 当前 `MeterReadingsImporter` 用**一个 Importer 处理两种 Excel layout**(住宅单元 vs 商铺/公共),列 label 是"双义"形式(如 `'层编号/费用类型'`),靠 `$this->options['asset_type']` 决定列含义。
>
> **风险**:导入时用户选错 `asset_type`:
> - 系统不报错(列存在,数据有值,看着像合法)
> - 但数据写到**错误字段**(silent corruption)
> - 业务人员事后核对才发现数据错位
>
> **修复**(issue.md Q5 待补):拆成 `MeterReadingsImporterForUnit` + `MeterReadingsImporterForShop`,每个列含义固定。
>
> **当前预防**:导入时**务必仔细确认** asset_type 选项 + 抽样核对前几行数据。
## 常见问题
> [!question] 导入失败的常见原因?
> - meter 不存在(asset 或 code 找不到对应表)
> - meter 已退役(`is_active=false`)
> - 读数倒走(本次 < 上次)
> - 读数格式错(非数字 / 含中文)
> - asset_type 选错(silent corruption,不报错但数据错)
> [!question] 已导入想撤销?
> Reading 不可改 / 不可删(若已生成 Bill 更严)。撤销 = 走 [[exception-readings-locked-after-bill]] 流程,复杂。
>
> **预防**:导入前抽样核对 Excel + 选对 asset_type + 小批量先试。
> [!question] 同一张表本月被重复导入(填了两次 Excel)?
> 看 `MeterReadingsImporter` 是否有"同 meter + 同 read_at 不允许"的守护。如果没有 → 系统建两条 reading → 两条都算用量 → 业户被算两遍。**严重 bug**,需要业务人员核对避免重复导入。
> [!question] 部分表本月没抄怎么办?
> `MetersNeedingReadingListWidget` 会显示"本月未抄"清单。业务人员可:
> - 让抄表员补抄(走 [[read-single-meter-manual|单录]])
> - 让业户自报读数(部分物业接受,但需核对)
> - 跳过本月(下月一起算,业户账单可能突然变高)
> [!question] 商铺 / 公共表与住宅表能合并导入吗?
> 看 Excel 模板设计。当前**asset_type 是必选项**,即一次导入只能处理一种类型。要分两次导入(住宅一次,商铺一次,公共一次)。
> [!question] 导入完成后立即生成账单还是等月底?
> 取决于业务流程:
> - **导入即生成**:`MeterReadingsImporter` 完成后自动调 GenerateBills(默认推荐)
> - **手动触发**:业务人员后续审核 reading 后再触发生成
>
> 当前实现应是"自动生成"模式(看 Importer 配置)。
## 异常分支
- 单张表录入 → [[read-single-meter-manual]]
- 集抄自动 → [[read-via-iot-remote-source]]
- 已导入发现错 → [[exception-readings-locked-after-bill]]
- 高用量异常 → [[exception-high-consumption]]
- 抄表完成率审计 → [[audit-meters-needing-reading]]
## 相关文档
- [[meter-vs-meter-reading]]
- [[reading-source-and-photo-proof]]
- [[bill-generation-pipeline]]
- [[read-single-meter-manual]]
- [[read-via-iot-remote-source]]
- [[init-new-community-batch]]

View File

@@ -0,0 +1,170 @@
---
title: prop-acc · meter · 场景 - 单张表后台手动录入
aliases:
- 手动抄表单录
- 单张表录入
- read-single-meter-manual
- 场景-单张表手动抄表
tags:
- 场景
- prop-acc
- 计量表
- 抄表
audience:
- 业务人员
- 抄表员
status: 已发布
sub_feature: meter
last_review: 2026-05-26
code_version: 2026-05-22
---
# 场景:单张表后台手动录入
抄表员**单张表**录入读数。最基础的抄表方式 —— 适合小规模物业 / 个别补抄 / 集抄掉线的兜底。
## 典型情境
> [!example] 真实情境
> 嘉禾花园抄表员李师傅本月集抄系统**有 5 户掉线**(IoT 表故障 / 信号不好)。这 5 户他亲自上门读表,然后回办公室后台单条录入。
>
> 本场景:其中一户(张阿姨 12-3-501 的电表 E-501,本月读数 5,280)。
## 抄表员视角
### 第 1 步:现场读表
到张阿姨家:
1. 找到电表(通常装在玄关 / 门外配电箱)
2. **看清表头数字**:5280
3. **拍照存证**(`photo_url`,见 [[reading-source-and-photo-proof]] + [[read-with-photo-proof]])
4. 记录(写在抄表本子上或手机备忘)
### 第 2 步:回办公室录入
打开手机 App / 浏览器后台 → 计量表 → 找 E-501 → 进 `ViewMeter` → 滚动到下方"抄表读数"(`MeterReadingsRelationManager`)→ 点 **"新增"** 按钮。
> 替代路径:抄表员手机 App 直接现场录入,无需回办公室。当前若有此 App 走集成接口,无此 App 走后台。
### 第 3 步:填表单
| 字段 | 填什么 |
|---|---|
| **抄表日期(`read_at`)** | 2026-05-26(默认今天)|
| **当前读数(`current_reading`)** | 5280(物理表头数字)|
| **来源(`source`)** | `manual`(默认)|
| **拍照(`photo_url`)** | 上传现场照片(若有要求)|
| **操作员(`operated_by`)** | 自动填李师傅(当前登录用户)|
| **备注(`memo`)** | 选填,如 "集抄掉线手动补抄" |
> [!info] previous_reading 不用填
> 系统自动从该 meter 最近一条 reading 取(或从 `Meter.initial_reading` 取)作为 previous,自动算 `consumption = (current - previous) × multiplier`。
>
> 例:上次抄表 5050 → previous=5050 → consumption = (5280 - 5050) × 1 = 230 度
### 第 4 步:提交
系统:
1. 校验 `meter.is_active=true`(退役表不能抄,详见 [[decommission-and-locking]])
2. 校验 `current_reading >= previous_reading`(若有此守护;否则即"读数倒走"异常,见 [[exception-high-consumption]] 相关)
3. 算 consumption
4. 建 MeterReading(`bill_id=null`,即未生成账单)
5. (可选)若配置了"抄表即生成账单",自动调 `GenerateBillsFromMeterReadingsAction`(详见 [[bill-generation-pipeline]])
提交后跳回 `ViewMeter`,新 reading 显示在列表第一条。
## 业务人员视角
业务人员**通常不直接抄表**,但会做:
- **审核**抄表员录入的数据(看高用量异常,见 [[exception-high-consumption]])
- **生成账单**(批量,月底统一,走 `GenerateBillsFromMeterReadingsAction`)
- **核对**抄表完成率(`MetersNeedingReadingListWidget`)
## 系统流程
```mermaid
sequenceDiagram
participant 李师傅[抄表员]
participant Filament
participant MeterReadingsRelationManager
participant 数据库
Note over 李师傅: 现场读表 5280
李师傅->>Filament: ViewMeter(E-501) → 新增 reading
Filament->>MeterReadingsRelationManager: 渲染 form
李师傅->>MeterReadingsRelationManager: 填 current=5280 + 上传照片 + 提交
MeterReadingsRelationManager->>数据库: 校验 meter.is_active=true
MeterReadingsRelationManager->>数据库: 查 previous_reading(上次 5050)
MeterReadingsRelationManager->>数据库: 算 consumption=(5280-5050)*1=230
MeterReadingsRelationManager->>数据库: 建 MeterReading(source=manual, operated_by=李师傅, photo_url, bill_id=null)
Filament-->>李师傅: 显示新 reading 230 度
```
## 业户视角
业户**无感知** —— 张阿姨可能根本不知道李师傅上门读了表。
下个月物业账单出来,显示"5 月电费:用电 230 度,¥XXX",业户看明白即可。如果有异议 → 物业拿出 `photo_url` 照片证明。
## 何时用本场景
| 场景 | 用本场景? |
|---|---|
| 集抄系统正常运行 | ❌ 走 [[read-via-iot-remote-source]] |
| 集抄系统某户掉线 | ✅ 个别补抄 |
| 小规模物业未上集抄(全靠人工)| ✅ 但建议升级到 [[read-batch-via-excel-import]](效率高)|
| 抄表数据需要事后修正 | ❌ Reading 不可改,见 [[exception-readings-locked-after-bill]] |
| 业户家暂时无人,无法抄 | 抄表员标"未抄",月底再补 |
## 常见问题
> [!question] 读数倒走(current < previous)怎么办?
> 看 Form 是否有守护。若**允许提交**:系统建 reading,consumption 是负数 → 后续生成 Bill 时 Calculator 可能抛错 / 给 0 / 给 min_amount。
>
> **业务上**读数倒走通常意味着:
> - 表故障(乱跳)→ 走 [[replace-broken-meter|换表]]
> - 抄表录错(可能是本月录上月数 / 笔误)→ 立即改(若没生成 Bill 可改;若有 Bill 见 [[exception-readings-locked-after-bill]])
> - 业户偷电 / 物理改表 → 严重事件,法务介入
> [!question] 读完一户表才发现没拍照怎么办?
> 看物业政策严格度:
> - **必须拍照**:回去补拍 → 上传(理论上事后照片也是凭据,但不如当场)
> - **建议拍照**:本次录入时备注"未拍照" → 业户事后无异议则 OK
>
> 长期不拍照 = 业户争议时无凭证 = 物业被动。
> [!question] 抄表日期填错了能改吗?
> Reading 不可改([[decommission-and-locking]] 第 2 锁)。若 `read_at` 填错且**没生成 Bill** → 见 Policy 是否允许删 + 重建。若**已生成 Bill** → 复杂,走作废 Bill 流程([[exception-readings-locked-after-bill]])。
>
> **预防**:Form 默认带入今天日期,改之前确认。
> [!question] 一次录多张表(几十户)用单录效率太低?
> 是的。**走 [[read-batch-via-excel-import]] 批量导入**或 [[read-via-iot-remote-source]] IoT 自动。本场景适合 1-10 张表的个别情况。
> [!question] 录入后立即生成 Bill 吗?
> 看配置:
> - **抄表即生成 Bill**:Form 提交后自动调 `GenerateBillsFromMeterReadingsAction`(每条 reading 立即变 Bill)
> - **月底批量生成 Bill**:Form 提交后只建 reading,月底业务人员统一触发批量生成
>
> 当前实现看具体 Filament 配置,两种都支持。
## 异常分支
- 批量录入 → [[read-batch-via-excel-import]]
- 集抄自动 → [[read-via-iot-remote-source]]
- 拍照存证 → [[read-with-photo-proof]]
- 读完发现是错的 → 立即改(无 Bill)/ [[exception-readings-locked-after-bill]](有 Bill)
- 异常用量 → [[exception-high-consumption]]
## 相关文档
- [[meter-vs-meter-reading]]
- [[reading-source-and-photo-proof]]
- [[bill-generation-pipeline]]
- [[read-batch-via-excel-import]]
- [[read-via-iot-remote-source]]
- [[exception-high-consumption]]