--- title: prop-acc · meter · 计量表与抄表流水 aliases: - 计量表与抄表流水 - Meter 与 MeterReading - 计量表的双对象模式 tags: - 概念 - prop-acc - 计量表 - 核心概念 audience: - 业务人员 - 抄表员 - 财务 status: 已发布 sub_feature: meter last_review: 2026-05-25 code_version: 2026-05-22 --- # 计量表与抄表流水 计量表模块底层是**两个对象**配合:**Meter**(物理表的配置)+ **MeterReading**(每次抄表的不可变流水)。模式上与 [[../deposit/deposit-account-vs-transaction|押金账户+流水]]同构,但**对象的物理性**让它有几条独特特征: - **每张表是真实硬件**(电表 / 水表 / 燃气表),挂在具体房屋(`asset_id`)上 - **不直接产收据** —— 抄表产 `Bill`,后续业户付账单再走 [[../adhoc/collection-order-and-receipt|CollectionOrder + Receipt]] - **没有"账户余额"概念**(没有 balance 字段),业户余额追踪在 [[../prepaid/prepaid-account-vs-transaction|预存款]] ## 为什么用双对象 > [!info] 类比:实体水表 > - **Meter** = 表面板,上面写编号、安装时间、倍率 > - **MeterReading** = 每次抄表的本子记录:几号几月几日读了多少、谁抄的、有没有拍照 如果只有 Meter 没有 Reading → 不知道每月用了多少,无法算账单。 如果只有 Reading 没有 Meter → 不知道表的物理属性(倍率、装在哪、什么费用),Reading 是孤立数字。 所以两个都要。 ## 字段速查 ### Meter(计量表) | 字段 | 含义 | |---|---| | `id` | 表 ID | | `community_id` | 所属物业项目 | | **`asset_id`** | **绑定的房屋资产**(必填,通过 asset 找业户)| | **`fee_type_id`** | **费用类型**(水费 / 电费 / 燃气费,决定单价)| | `replaced_meter_id` | 上一代表(`null` = 原生新表,见 [[replacement-chain]]) | | `code` | 表编号(物理表牌号,通常物业自编)| | **`multiplier`** | **倍率**(decimal(10,4),工业表 10/100/1000)| | `initial_reading` | 初始读数(decimal(12,2)) | | `is_active` | 是否在役 | | `installed_at` | 安装日期 | | `decommissioned_at` | 退役日期(null = 在役)| | `decommission_reason` | 退役原因(5 种枚举,详见 [[decommission-and-locking]])| | `final_reading` | 退役时最终读数 | ### MeterReading(抄表流水) | 字段 | 含义 | |---|---| | `id` | 流水 ID | | `meter_id` | 归属表 | | `read_at` | 抄表日期 | | `current_reading` | 本次读数(物理表头数字)| | `previous_reading` | 上次读数(自动从最近一条 reading 取)| | **`consumption`** | **用量**(=(current - previous) × multiplier,自动算)| | `source` | 来源:`manual`(手抄) / `remote`(集抄,详见 [[reading-source-and-photo-proof]])| | `photo_url` | 拍照存证 URL(可选 / 集抄无)| | `operated_by` | 抄表员 ID(manual 类型必填) | | `bill_id` | 关联生成的账单(若已生成 Bill,null = 未生成)| | `memo` | 备注 | | 创建后**不可变** | 一旦生成只读;若 `bill_id != null` 更不可改(双锁)| ## 两者的契约 - **每张 Meter 可有多条 MeterReading**(理想情况是每月一条) - **MeterReading 必属于一张 Meter**(`meter_id` NOT NULL) - **最新 reading 的 current_reading = 该 meter 的"当前累计读数"**(Meter 自身不存 current,从 reading 推出) - **consumption = (current - previous) × multiplier**:倍率乘进去就是真实用量(度 / 吨 / 立方米) ## 与"账户+流水"型模块的本质差异 | 维度 | Account + Transaction(deposit / prepaid)| **Meter + MeterReading** | |---|---|---| | 主对象表达什么 | 账户余额 | **物理表的配置** | | 流水表达什么 | 资金变动(deposit / refund / consume) | **抄表读数(用量计算源)** | | 主对象有 balance 吗 | ✅ 有 | **❌ 无**(余额是用量,在 Reading 里) | | 流水方向 | + / -(余额加减)| **无方向**(只有"本月读了多少") | | 自动关账 | 看模块(deposit 自动,prepaid 不) | 走"退役 (decommission)" 不是"关账" | | 写入操作种类 | 多种(deposit / consume / refund / forfeit) | **单一**(抄表 record_reading)+ 换表 / 退役 | | 直接产 Receipt | 是 | **否,通过 Bill 中转** | ## 业户视角 业户**通常不直接接触 Meter / MeterReading 概念**。看到的是: - 月底物业 App 推送账单"5 月电费 ¥168,用电 280 度" - 收据"水费 ¥54" - 偶尔小程序"我的计量"页可查看本月用量趋势(若开启) 底下的 Meter 和 MeterReading 是后台运营的事。**唯一**会接触的:**业户对账单金额有异议**时,可申请看历次抄表记录(系统应能展示该业户表的全部 reading 历史)。 ## 抄表员视角 李师傅是物业聘的抄表员,每月固定时间挨家挨户(或集中点位)读表头数字: - 用物业 App / 抄表机 录入 → 系统建 MeterReading(`source=manual`,`operated_by=李师傅`) - 拍照可选(详见 [[reading-source-and-photo-proof]]) - 如果该社区接入集抄系统(IoT 表),系统自动生成 reading(`source=remote`),李师傅不用跑 ## 业务人员视角 物业财务的工作: - 看 `MeterDashboard`:本月待抄表清单、高用量异常、按费用类型用量走势 - 录入 / 校验 reading(若有异议) - 触发 `GenerateBillsFromMeterReadingsAction` → 自动生成所有未结账的 reading 对应的 Bill - 月底对账:每张 reading 是否都有对应 Bill,有 Bill 是否已 Paid ## 系统视角:Bill 生成是单独一步 ```mermaid flowchart LR A[抄表员录入 MeterReading] -.第一步.-> B[MeterReading
bill_id=null] B -->|GenerateBillsFromMeterReadingsAction| C[Bill 生成
+ MeterReading.bill_id 回写] C -.业户付账单.-> D[CollectionOrder + Receipt
付款方式可选现金/微信/预存款] ``` **关键**:MeterReading **不直接**产 CollectionOrder + Receipt。要先经过 **Bill 生成步骤**(可能是月底批量,可能是抄表后立即),然后业户**付账单**才走收款流程。计量表是**计费源**,不是收款源。 ## 相关文档 - [[replacement-chain]] - [[multiplier-and-tiered-pricing]] - [[bill-generation-pipeline]] - [[reading-source-and-photo-proof]] - [[decommission-and-locking]] - [[../prepaid/consume-via-bill-collection-type]](账单视角的收款,与本模块呼应) - [[../deposit/deposit-account-vs-transaction]](账户+流水模式对比)