Appearance
Meter Replacement Flow
Business Context
Business-Level Definition
When a physical meter is replaced, the system must record the transition without losing continuity: the outgoing meter's final readings, the incoming meter's serial number, and its initial readings all need to be captured together — across every channel of that physical meter, not just one. A meter with several channels (e.g. a bidirectional electricity meter with import and export, or several tariff registers) is grouped by physicalMeterId; a replacement is recorded once and applies uniformly to every gaugeId sharing that physicalMeterId. No gaugeId in the group is created, changed, or reassigned — only the shared serial number changes. Without this, either the replacement goes unrecorded (the system has no way to know a physical swap happened), or downstream consumption calculations misinterpret the new meter's low starting value as an anomalous drop.
For remote (DO) sources, a replacement usually shows up as a new data channel appearing rather than as a change on the existing one. Where the source reports enough identity to recognise this, such a channel is flagged for review — see Remote Connection Management (FR8). Recording the replacement itself still happens through this flow.
Requirements Definition
- Recording a reading offers a "final reading / meter replacement" option.
- When selected, the user provides one date and time of the replacement, the old meter's final values and the new meter's initial values.
- The new serial number is optional at this point and may be added at any time directly on the gauge detail.
- The new meter may count in a different unit (for example MWh instead of kWh); the user picks it per measured parameter, and it applies to manual readings from the replacement on.
- Confirming writes the final and initial readings for every series of every gauge sharing the replaced physical meter's
physicalMeterId, and updates the shared serial number, as a single, atomic operation — never a partial result. - No metering point's identity (
gaugeId) is affected by this flow, for any gauge in the group — only the physical device reference (serialNumber), shared across the group, changes. - The reading write-path's monotonicity check must not reject the new meter's initial reading as "lower than the previous value".
- An admin can correct a mistakenly-created
finalreading by clearing the flag (reverting it to a normalreading), rather than requiring a full data-fix outside the application.
Acceptance Criteria
- Checking "meter replacement" while recording a reading reveals one date and time field, the final and initial value per series, the new serial number (optional) and the new meter's unit per parameter.
- Confirming writes the final reading, updates the serial number, and writes the initial reading — all within a single transaction.
- If any step fails, nothing is persisted — no partial state.
- Every
gaugeIdsharing the replaced meter'sphysicalMeterIdis identical before and after the replacement. - A meter with several channels (import/export, multiple tariffs) has all of them replaced together, in the same transaction, with the same serial number and the same date — never one channel at a time.
- No code path other than this flow can create a reading with
lifecycleState = final, except the correction action in FR6, which only ever clears an existingfinal— it never sets one. - A
finalreading written by this flow is always immediately followed, in the same transaction, by aninitialreading for the same series — never left without one. - An admin can clear a mistaken
finalflag, reverting the reading tolifecycleState = reading.
Loom Link
N/A — not available in the source material.
Technical Context
User Stories / Use Cases
- As a client user, I want to record that a meter has been physically replaced, so that the system reflects the new device without breaking consumption calculations for that metering point.
- As an admin, I want to undo a mistakenly-flagged "final reading / meter replacement", so that monotonicity protection for that series isn't permanently disabled by a mistake.
UI/UX Design
Dialog Výměna měřidla, opened from the reading form of the gauge or from the reason dialog when a value goes backwards. Screen 11 in reading-management/design/.
| State | What is shown |
|---|---|
| Opened | Date and time of the replacement (both required); per hand-read parameter of the physical meter a group headed by the parameter name with the unit of the new meter (preset to the parameter's current manual unit) and, per series, the old meter's final value (with the last stored value as a hint) and the new meter's initial value; new serial number (optional) |
| Unit changed | A notice under the parameter that its manual readings from the replacement on are recorded in the new unit, older values keep theirs and the remote source is not affected |
| Replacement protocol | Button Přiložit protokol o výměně (optional); the document is attached to the final reading of the old meter |
| User without the replacement permission, or gauge reading remotely only | The Výměna měřidla button is not shown |
| Confirm | Zrušit / Uložit výměnu; no close icon |
| Opened from Upravit on a Konečný or Počáteční reading | The same dialog filled with the recorded replacement — moment, final and initial values, unit, serial number, protocol; saving replaces the recorded replacement as a whole |
Functional Requirements
FR1 — Replacement action: available as part of recording a reading. It takes one date and time for the whole replacement, the final and initial value of every series, the new meter's unit per parameter, and optionally the new serial number.
FR1a — One moment, two records: the final reading is stored at the entered moment and the initial reading one second later, so the two never share a moment in the same series and the initial one always follows the final one.
FR1b — Changing a recorded replacement: a replacement is corrected only as a whole: the dialog opens filled from the two recorded readings and saving rewrites both, the source settings and the serial number in one transaction, with the same checks as when recording. The readings of a replacement cannot be changed or deleted one at a time in Reading Management.
FR2 — Atomic write, whole-meter scope: the replacement is recorded once, for the physical meter, not per channel. On confirmation, in a single transaction, for every gaugeId sharing the replaced meter's physicalMeterId, and for every series within each such gauge (each distinct type/direction/tariff/source combination):
- write a reading with
lifecycleState = finalfor that series (the outgoing meter's last reading) - serial number — applied once, uniformly across the whole group:
- if a new serial number was provided, insert one new gaugeSerialHistory record per gauge in the group (same
validFrom= now, same new serial number) and update each gauge'sserialNumberto match; - if the serial number was not provided, no history record is written for any gauge in the group (the serial number can be added or corrected at any time afterward directly on the gauge detail)
- if a new serial number was provided, insert one new gaugeSerialHistory record per gauge in the group (same
- write a reading with
lifecycleState = initialfor that same series — the incoming meter's first reading - for every parameter (gauge) in the group, insert a new manual gaugeSourceSetting with
validFrom= the replacement moment, carrying the chosen unit and the other values of the setting valid before; where the gauge's canonicalunitequalled the old manual unit, set it to the new one, otherwise leave it; the remote source setting is not touched; readings stored before keep the unit they carry - write one readingEntry for the final and one for the initial reading of each gauge, so both appear in the Readings tab as readings with a history; the replacement protocol, when attached, is linked to the entry of the final reading
A meter with a single channel is simply a group of one — the same logic applies without a special case.
FR3 — Metering point stability: no gaugeId is ever created, changed, or reassigned by this flow, for any gauge in the physicalMeterId group. Every metering point in the group is the same before and after — only the physical device (serial number), shared across the group, changes.
FR4 — Monotonicity boundary: the reading write-path's monotonicity check skips its predecessor comparison when the immediate predecessor in the series has lifecycleState = final. Without this, the new meter's initial reading would almost always be rejected as lower than the outgoing meter's final reading. This applies independently to every series in every gauge in the group — each series gets its own final/initial pair and its own skip, exactly as in the single-gauge case, just repeated across the group.
FR5 — Invariant enforcement: a reading with lifecycleState = final can only be created by this flow — the standard reading-entry and bulk-import paths must reject an explicit final value. A final reading is always immediately followed, in the same transaction, by a corresponding initial reading for the same series — this guarantee holds independently for every series in every gauge that shares the replaced meter's physicalMeterId, not only for the gauge the user initiated the replacement from. This is what makes FR4 safe: without this guarantee, a mistaken final written through an ordinary path would silently and permanently disable monotonicity protection for that series.
FR6 — Correction of a mistaken final: an admin-only action clears an existing lifecycleState = final flag, reverting the reading to the normal reading state. This is the only way a final value can change after being written by FR2 — it only ever clears the flag, never sets it (setting final remains exclusive to FR2). Restores normal monotonicity protection for the series immediately. Does not delete or alter the reading's value — only its lifecycleState.
Non-Functional Requirements
- This is a cross-domain write (reading data belongs to the ingestion domain, serial number belongs to the asset/gauge domain) implemented as a single database transaction — both domains share the same physical database and schema, so this does not require a distributed transaction or saga. The transaction spans every gauge and every series in the replaced meter's
physicalMeterIdgroup, not a single gauge — still a single database transaction, since group size is bounded and known at write time. - Architecturally significant: the gauge lookup used elsewhere in reading ingestion is deliberately read-only, to keep the domains decoupled. This flow is a specific, deliberate exception to that boundary and should be reviewed and approved at the architecture level before implementation, not discovered during code review.
API Analysis
POST /v1/readings/meter-replacement— takes agaugeIdresolving thephysicalMeterId, one replacement moment, the final and initial value per series, the new unit per parameter (units[]ofgaugeId+unit), an optional serial number and optional document ids of the protocol, and records everything for every gauge in the group atomically (FR1a, FR2). The series of each parameter aretariffSeries(gauge, moment)of Gauge Management.PUT /v1/readings/meter-replacement/{id}—idis the reading entry of the final reading; takes the same body as thePOSTand rewrites the recorded replacement as a whole — both readings of every gauge in the group, the source settings written by the replacement, the serial number and the protocol documents — atomically, with the audit entries carrying before and after (FR1b).POST /v1/readings/:id/clear-final— admin-only; clears a mistakenlifecycleState = finalflag on a specific reading (FR6).
Domain Model (ER diagram) & Data Attribute Table
No new entity, and no ER diagram in source material. This flow writes to the existing reading entity (using lifecycleState), across every series of every gauge sharing the replaced meter's physicalMeterId, and updates the serialNumber (writing a gaugeSerialHistory record) on every such gauge; a changed unit is a new manual gaugeSourceSetting per gauge, not a change of the stored readings — see the gauge and reading pages for the attributes.
Data
N/A — not applicable per source material.
Test Data
N/A — not covered in source material.
Logging
.info— successful meter replacement (old serial number, new serial number, both reading values)..info—finalflag cleared by an admin (reading id, admin actor)..error— replacement transaction failed (nothing persisted).
Monitoring
N/A — not covered in source material beyond the logging entries above.
Caching
N/A — not covered in source material.
Backward Compatibility and Migration
N/A — not applicable; this is a new EM3 process, not a migration of existing data.
Legal Context
N/A — not addressed in the source Confluence page; migrated as a reference copy without new legal analysis.
Cybersecurity Considerations
N/A — not addressed in the source Confluence page; migrated as a reference copy without new security analysis.
Risk Assessment
N/A — not addressed in the source Confluence page; migrated as a reference copy without new risk analysis.
Auditing, Reporting & Measurement
N/A — not addressed in the source Confluence page beyond the Logging entries above.
Migrated as a reference copy from Confluence page "Meter Replacement Flow" (id 697958401). Content reorganized to fit this repo's feature-doc template; not re-analyzed against the current codebase. Cross-referenced source pages: Reading Management (id 609288207), Gauge Management (id 524222465), and a gaugeSerialHistory entity page (Confluence id 585990146).