Files
uniprop-manual/prop-acc/scenarios/meter/init-new-community-batch.md
2026-05-26 00:13:05 +08:00

186 lines
8.1 KiB
Markdown
Raw Permalink 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.
---
title: prop-acc · meter · 场景 - 新社区批量建表 + 初始读数 Excel 导入
aliases:
- 批量建表
- 新社区计量表初始化
- init-new-community-batch
- 场景-新社区批量建表
tags:
- 场景
- prop-acc
- 计量表
- 表管理
- 批量导入
audience:
- 业务人员
- 抄表员
status: 已发布
sub_feature: meter
last_review: 2026-05-26
code_version: 2026-05-22
---
# 场景:新社区批量建表 + 初始读数 Excel 导入
物业**新接管社区**(或老社区从 0 接入本系统),需要**批量建表 + 录初始读数**。通过 `MeterInitializationImporter` + `ImportActionWithExcel` 一次性导入。
## 典型情境
> [!example] 真实情境
> 平台新签了"嘉禾花园"社区,本月底接管。该社区有 300 户业主 + 公共部位 + 商铺,合计约 1,200 张表(每户水电气 3 张 + 公共部位 + 商铺各类)。
>
> 物业财务王主管不可能手工建 1,200 张表,**走批量导入**:
>
> 1. 抄表员李师傅 + 物业前任团队 出 Excel 表(包含每张表的房号、表号、初始读数)
> 2. 王主管在系统下载"建表初始化模板"
> 3. 把数据填入模板 → 上传 → 系统批量建表
## 业务人员视角
### 第 1 步:下载初始化模板
后台 → 计量表 → 列表 → 顶部 **"下载初始化模板"** 按钮(`ExportMeterInitializationTemplateAction`)。
下载到的 Excel 包含:
| 列 | 说明 | 示例 |
|---|---|---|
| 房号 / 资产编号 | 关联 asset(必填,系统按房号查 asset_id) | 12-3-501 |
| 费用类型 | 水费 / 电费 / 燃气费(必填) | 电费 |
| 表编号 | 物理表牌号(必填) | E-501 |
| 倍率 | multiplier(可选,默认 1)| 1 |
| 初始读数 | initial_reading(必填,首次接管时表上的读数)| 0 / 8523 / etc. |
| 安装日期 | installed_at(可选,默认导入日)| 2026-05-26 |
| 备注 | (可选) | "新装" |
模板列名清晰,业务人员 / 抄表员看得懂。
> [!warning] 模板列含义"双义"问题(已知 issue)
> 当前 `MeterInitializationImporter` 用**一个 Importer 处理两种 Excel layout**(住宅单元 vs 商铺/公共)。某些列 label 是"双义"形式,导入选项里选错 `asset_type` 不报错,只是数据写到错误字段(silent corruption)。issue.md Q5 已记录,待拆成两个独立 Importer。**当前预防**:务必仔细选 asset_type。
### 第 2 步:填写数据
物业 / 抄表员把 1,200 张表的信息填入模板。
关键字段对齐:
- 房号:对应系统里 asset 表已存在的编号(若不存在,先到 community 模块建 asset)
- 费用类型:对应系统配置的 FeeType(水/电/燃气,各社区独立配置)
- 表编号:物业自编(常见 `<费用类型简写>-<房号>` 模式)
- 初始读数:**接管当天**的物理表读数(关键!首次接管之前的用量物业不管)
### 第 3 步:上传 + 导入
后台 → 计量表 → 列表 → 顶部 **"导入初始化"** 按钮(`ImportActionWithExcel` + `MeterInitializationImporter`)→ 选 asset_type(住宅 / 商铺)→ 上传 Excel → 提交。
系统:
1. **解析 Excel**(走 `BaseImporter` + `ImportActionWithExcel`,支持 .xlsx / .xls / .csv)
2. **每行校验**(房号 asset 存在?费用类型存在?表编号是否在该社区重复?)
3. **批量建 Meter**(每行一张 Meter 记录)
4. **可选:同时建第一条 MeterReading**(若模板含"初始读数",建初始 reading 来锁定 `previous_reading` 起点)
5. **报告**:成功 N 张,失败 M 张 + 每条失败的原因
> [!info] BaseImporter + chunk rollback
> 走 `App\Filament\Importers\BaseImporter`(host 基类,详见 saas-baseline 规范)+ `TransactionalImportCsv` job。一批 100 行任意一行失败 → 该批全回滚。
>
> 这避免"部分建好部分没建"的脏中间态。失败的批可下载"失败行" Excel,修复后再导入。
### 第 4 步:核对
导入后:
- 后台 → 计量表 → 按社区过滤 → 看是否 1,200 张表都在
- 抽样核对:打开几张表看初始读数对不对
### 第 5 步:启动月度抄表
接管下一个月 → 抄表员去现场抄读数(走 [[read-batch-via-excel-import]] 或 [[read-single-meter-manual]])→ 系统按 `current - initial × multiplier` 算用量 → 生成第一份账单(走 [[bill-generation-pipeline]])。
## 系统流程
```mermaid
sequenceDiagram
participant 王主管
participant Filament
participant ImportActionWithExcel
participant MeterInitializationImporter
participant 数据库
Note over 王主管: 已填好 1200 行 Excel
王主管->>Filament: ListMeters → 导入初始化 → 选 asset_type + 上传
Filament->>ImportActionWithExcel: parse .xlsx
ImportActionWithExcel->>MeterInitializationImporter: 按 chunk 处理(100 行/批)
loop 每批
MeterInitializationImporter->>数据库: 开启事务
loop 每行
MeterInitializationImporter->>数据库: 校验 asset / fee_type / code 唯一
alt 校验通过
MeterInitializationImporter->>数据库: 建 Meter + 可选 initial MeterReading
else 校验失败
MeterInitializationImporter->>MeterInitializationImporter: 收集失败行
end
end
alt 全成功
MeterInitializationImporter->>数据库: 提交
else 任一失败
MeterInitializationImporter->>数据库: 回滚整批
end
end
MeterInitializationImporter-->>Filament: 报告 "成功 1198,失败 2"
Filament-->>王主管: 通知 + 下载失败行 Excel
```
## 业户视角
业户**不感知**这一步。新接管社区会发个公告"本月起本物业系统升级,各位业户的水电气计量将从 X 月 X 日起按本系统记账"。具体业户看到的:
- 接管前最后一份账单(由前任物业 / 自建系统出)
- 接管后第一份账单(由本系统出,用量从接管那天起算)
中间**绝不能有"重复账单"或"漏账"** —— 接管时的 `initial_reading` 必须准确反映物理表当时读数。
## 常见问题
> [!question] 为什么需要"初始读数"?
> 系统计算用量公式是 `(current - previous) × multiplier`。新表的"上一次读数"在系统里没有,所以接管时存的 `initial_reading` 就是"`previous_reading` 的起点"。后续每月抄表 → 当前 - 上次 = 用量。
>
> 如果不填初始读数 → 第一次抄表算用量会爆炸(`current - 0 = 所有历史用量`),业户被收一笔巨账,投诉。
> [!question] 导入失败的常见原因?
> - **房号(asset)不存在**:在 community 模块的 asset 还没建好。先建 asset 再导入表
> - **费用类型不存在**:RatePlan 没配置。先到 FeeType 配置
> - **表编号在该社区重复**(社区内 code 应唯一,虽然 issue.md Q5 提到目前是 nullable + 非 unique 的"待治理"状态)
> - **倍率格式错**(非数字 / 负数)
> - **初始读数格式错**(非数字 / 负数)
> [!question] 失败的行可以单独处理吗?
> 可以。导入完成后系统提供"下载失败行"Excel(走 host 的 `TransactionalImportCsv` 机制),业务人员修复后单独导入失败行。已成功的不影响。
> [!question] 同一社区多次导入会重复建表吗?
> 视 Importer 实现。若有 unique 校验(asset_id + fee_type_id 不可重复)→ 重复行会失败,需手工合并。若无校验 → 重复建,**灾难**。
> [!question] 老社区已经有一年的抄表历史,接管时怎么办?
> 简化做法:**只导入接管那天的状态**(initial_reading = 接管那天的物理读数),历史数据**不进系统**(在 Excel 备查)。
>
> 复杂做法:**导入历史 reading 数据**,让本系统有完整历史。需要业务方决定(用户对账复杂度 vs 系统数据完整度的权衡)。
> [!question] 商铺表 / 公共部位表怎么导入?
> 同样走 `MeterInitializationImporter`,但**选不同 asset_type**(public / shop)。导入时系统按 asset_type 决定列含义。详见 issue.md Q5"双义列名"问题。
## 异常分支
- 单张新表(后续装机 / 个别加表)→ [[register-single-meter]]
- 老表换新表 → [[replace-broken-meter]]
- 抄表(初始化后日常)→ [[read-batch-via-excel-import]] / [[read-single-meter-manual]]
## 相关文档
- [[meter-vs-meter-reading]]
- [[register-single-meter]]
- [[read-batch-via-excel-import]]
- [[bill-generation-pipeline]]