Appearance
Calorific Value Management
Business Context
Business-Level Definition
A gas meter counts cubic metres. An invoice, a budget and a comparison with last year are all in kilowatt-hours. The number that bridges the two is the calorific value of what was burned, and it is not a property of the meter — it is a property of the fuel, declared by whoever supplied it, and it changes.
This feature is where that number is kept. It can be stated in three places: once for the whole platform as the value from the national standard, once for a customer who has a supplier declaration of their own, and once for a single metering point that is measured more precisely than either. The most specific statement that was valid on the day being calculated is the one that applies.
Requirements Definition
Every energy figure derived from a volume depends on this number, and getting it from the wrong place is invisible: a value that is 10 % out produces a consumption figure that is 10 % out and looks entirely ordinary.
Three things make it more than a settings field. It has to be stated at the right level — a national default is fine for most customers, useless for one burning biogas from their own digester. It has to be dated, because consumption is recalculated for past periods and a value with no validity date reprices history the moment somebody corrects it. And it has to be visibly absent when nobody has stated one: the previous system quietly substituted a constant for gas meters, which is how a wrong figure survives for years without anyone asking.
Technical Context
User Stories / Use Cases
- As the platform operator, I want to keep the default values from the national standard, so every customer starts from a defensible number.
- As the platform operator, I want a new standard to apply from its effective date without touching past figures.
- As a client manager, I want to enter our supplier's declared value for our fuel, so our figures use what we are actually billed for.
- As an energy manager, I want to set a value for one metering point that differs from the rest, without affecting anything else.
- As an energy manager, I want to see which values are missing, so I know where energy cannot be calculated at all.
- As an energy manager, I want a corrected value to correct past figures too, and to know that it is happening.
UI/UX Design
Every surface shows the same four facts about a value and names the scope it came from with one of three badges: Výchozí (platform), Vlastní (client), Individuální (gauge). A combination with no value at any scope is shown, marked Chybí, never omitted. "Vzít zpět" is the screen name for withdrawing a value entered in error (deletedAt): the number stops applying, stays in the history, and energy from its date is recomputed with the next scope — a superseded value is never withdrawn. Screen drafts: design/.
Platform catalogue — administration application, navigation entry Výhřevnosti beside the other cross-tenant catalogues.

| State | What is shown |
|---|---|
| Catalogue populated | One row per combination and validity date; today's row Platná, a future one Od {date}, a superseded one Nahrazená (hidden unless "Zobrazit nahrazené"); column "clients using / total" = clients with no own value for the combination |
| Combination with no value | Row marked Bez hodnoty with the gauge count across clients and the action Přidat hodnotu |
| Adding a value | Dialog: medium, primary source, value, unit from → to, valid from (future allowed), source standard; a notice states how many clients and gauges the value will reach and that energy is recomputed from the date |
| Validity date collides | The dialog refuses and points at the existing row for that combination and date |
| Withdrawing | Confirmation naming the combination and date; refused for a superseded value |
| Change history | Tab "Historie změn": the platform audit trail for defaults — when, who, action (Přidána / Citace / Vzata zpět), combination, change, impact (clients reached, recomputed from) |

Client tab — Klient › Výhřevnost, owned by Client Management. One row per combination of medium and primary source the client's gauges use; the badge says where today's value comes from; the row expands to the three tiers (individual gauges → client → platform) with the applying one highlighted. The client edits only its own tier; the platform tier is read-only; the gauge tier links to the gauge detail.

| State | What is shown |
|---|---|
| Header | Counts: combinations, own values, gauges with an individual value, gauges without any value |
| Gauges without any value | Red banner naming the combination, with a link to the gauges; the row itself is marked Chybí |
| Setting or correcting the client value | Dialog "Nastavit vlastní výhřevnost": combination read-only, value, units, valid from, source; a notice states how many gauges are recomputed and that gauges with an individual value are untouched |
| History | Per row, dialog "Historie": every value of every scope for the combination, newest first — valid from → to, scope, value, citation, actor and time, state Platná / Budoucí / Nahrazená / Vzata zpět; withdrawn rows struck through and hidden unless "Zobrazit vzaté zpět"; filters by scope; CSV export |
| Withdrawing ("Vzít zpět") | Offered only on the client's own value that is applying or future; confirmation names the combination and date; the row stays in the history struck through and the gauges fall back to the next scope from its date. Not offered on a superseded value — it reproduces the energy of its period |
| Filters | Vše / Jen vlastní / Chybí, medium, and the day the resolution is evaluated for (default today) |


Gauge card — Majetek › Měřidlo › Detail, owned by Gauge Management, shown only for gauges whose medium converts volume or mass to energy. The applying value with its scope and citation, the two tiers beneath it ("what would apply without the individual value"), and Nastavit individuální (the same dialog, combination fixed).
This feature owns one screen — the platform default catalogue, in the administration application, beside the other cross-tenant catalogues. The other two scopes are edited where their owner already puts them: the client tab described by Client Management, and the gauge detail described by Gauge Management. All three show the same four facts about a value: what it converts, to what, how much, and from when.
| State | What is shown |
|---|---|
| Catalogue populated | One group per medium; within it a row per primary source, ordered by validity date, newest first, with the currently applying row marked |
| Combination with no value | The combination is listed and marked as having none, rather than being omitted |
| Adding a value | Medium, primary source, value, the two units, validity date and the source it was taken from; the date may be in the future |
| Validity date collides | The form refuses and points at the existing row for that combination and date |
| Correcting a value | The same form, pre-filled, stating that the correction recomputes energy from that date onwards |
| Withdrawing a value entered in error | Confirmation naming the combination and date; the row stops applying and stays in the history |
Functional Requirements
Stating a value
- Hold a calorific value at three scopes: platform default, client, and single gauge.
- Key every value by medium and primary source, and give every value a validity date.
- Record the unit converted from and the unit converted to on every value, at every scope.
- Record where a value came from — the standard, or the supplier declaration that states it.
- Accept a validity date in the future and begin applying it on that date.
- Reject a second value for the same scope, combination and validity date.
- Allow a value to be corrected or withdrawn; never delete a superseded one.
Resolving a value
- Resolve for a gauge and a date by taking the most specific scope that holds a value valid on that date: gauge, then client, then platform default.
- Compare scopes on the same fields: a client or platform value applies to a gauge only when its medium and primary source match the gauge's.
- Report that no value exists where no scope holds one, rather than substituting a constant.
- Make the resolved value's scope visible to the caller, so a figure can be explained.
Consequences of a change
- Recompute the energy derived from a value when it is added, corrected or withdrawn, from its validity date forward, for the gauges the change resolves to.
- Restrict platform defaults to the platform administration surface; a customer reads them and cannot change them.
- Count the gauges for which no value resolves, and expose that count where values are edited.
Internationalization & Localization
Screen labels come from the platform's labelling mechanism, as in the rest of the administration application — see GUI Labelling. A value's recorded source is free text in the language of the standard or the declaration, and is not translated: it is a citation.
Non-Functional Requirements
- Reproducibility. Any historical energy figure can be recomputed to the same number, because the value that was valid then is still stored and still dated.
- Durability. No value is ever physically removed, at any scope.
Performance
A resolution reads at most three small sets of rows for one gauge and date, on the path that computes consumption. The work that has to stay bounded is the recalculation a change triggers: it is limited to the gauges the changed scope actually resolves to, and to periods from the validity date forward — a platform default changing does not recompute customers who have their own value.
Transactional Operations
Each value is written with its audit entry in one transaction. Collision on the same scope, combination and date is prevented by the unique key rather than a read-then-write check. The recalculation a change triggers is enqueued in the same transaction as the write, so a stored change can never lose its recalculation, and a rolled-back one never schedules work.
Processes & Related Systems / Components
| Trigger | Owner of the trigger | What this feature does |
|---|---|---|
| Platform default entered or corrected | platform operator | Store the value; recompute for gauges that resolve to the platform scope |
| Client value entered or corrected | Client Management | Store the value; recompute for the client's gauges that have no value of their own |
| Gauge value entered or corrected | Gauge Management | Store the value; recompute for that gauge |
| Energy computed from a volume | consumption calculation | Resolve the value for the gauge and the period being computed |
| Gauge's medium or primary source changes | Gauge Management | The gauge resolves against its new combination from that point |
Diagrams & Models
Resolving a value for one gauge and date:
flowchart TD
A[Gauge and date] --> B{Gauge-scope value<br/>valid on the date?}
B -->|yes| C[Use it]
B -->|no| D{Client-scope value for the<br/>gauge's medium and source?}
D -->|yes| E[Use it]
D -->|no| F{Platform default for the<br/>same combination?}
F -->|yes| G[Use it]
F -->|no| H[No value — energy is not computed<br/>and the gap is counted]
Entering a client value and what follows:
sequenceDiagram
actor M as Client manager
participant C as Client Management
participant V as Calorific value store
participant R as Recalculation
M->>C: Value, units, valid from
C->>V: Store the value and its audit entry
V->>R: Recalculate from the validity date
R->>V: Resolve per gauge and period
V-->>R: Client value, or a gauge value where one exists
R-->>M: Affected figures recomputed
API Analysis
See api-a.md.
Domain Model (ER diagram) & Data Attribute Table
erDiagram
DEFAULT_CALORIFIC_VALUE ||--o{ GAUGE : "applies to by default"
CLIENT ||--o{ CALORIFIC_VALUE : "states"
GAUGE ||--o{ CALORIFIC_VALUE : "may override"
GAUGE ||--o{ VOLUME_COEFFICIENT : "is corrected by"
- defaultCalorificValue — created by this feature; the platform defaults, in the cross-tenant schema.
- calorificValue — created by this feature; client and gauge scope, distinguished by whether a gauge is set.
- volumeCoefficient — the meter correction, separated from the calorific value by this analysis; a different question with a different answer.
- gauge — supplies the medium and primary source a value is matched on.
- client — owns the client scope.
Data
The platform catalogue ships seeded from the national standard, one row per medium and primary source:
sql
INSERT INTO shared.default_calorific_value
(medium, primary_source, value, unit_from, unit_to, valid_from, note)
VALUES
('gas', 1, 10.550000, 'm3', 'kWh', '2024-01-01', 'ČSN 38 5502'),
('gas', 3, 6.500000, 'm3', 'kWh', '2024-01-01', 'Bioplyn — ČSN 38 5502'),
('fuel', 2, 4.100000, 'kg', 'kWh', '2024-01-01', 'Biomasa — dřevní štěpka, 30 % vlhkost');A worked example, so the scope rule is unambiguous: a customer burning biogas has a client value of 6.20 kWh/m³ from their supplier's declaration; one of their digesters is metered separately and has a gauge value of 6.05. A month of 1 000 m³ on that digester is 6 050 kWh; the same month on any other gauge of theirs is 6 200 kWh; and for a customer with no value of their own it would be 6 500 kWh from the platform default. Same volume, three legitimate answers — which is exactly why the scope and the date are stored with the figure.
Test Data
Use the project's Test Data location named in the overlay. The cases worth seeding are a gauge whose client has a value and whose own value differs, a gauge whose combination has only the platform default, and a gauge whose combination has no value at any scope — the last is the one that proves energy is left uncomputed rather than defaulted.
Logging
The project has no shared logging-conventions document yet, so this feature's events are stated in full here.
.info— value created, corrected or withdrawn: scope, medium, primary source, value, validity date, actor,requestId..info— recalculation triggered by a value change: scope, validity date, gauge count..debug— resolution trace for one gauge and date: the scope that won and the scopes that were checked..warn— no value resolved: gauge, medium, primary source, date..error— a write was rejected by the unique key: scope, combination, validity date.
Monitoring
One feature-level need: the number of gauges for which no value resolves. It is the measure of whether the catalogue is actually complete, and unlike a failure count it can be non-zero from day one without anything having broken — which is why it belongs on a screen as well as in monitoring.
Caching
None. A resolution is three small indexed reads, and a cached value that outlives a correction produces exactly the silent historical drift this feature exists to prevent.
Backward Compatibility and Migration
The previous system held two tiers, not three: a platform default table and a client override, with a per-gauge value available only for gas meters. None of the three carried validity dates, and the lookup was keyed by the legacy meter type rather than by medium.
Migration therefore does three things. It maps the legacy meter type onto medium and primary source. It gives every migrated value a validity date — the earliest date from which consumption exists for it — so that recomputing migrated history reproduces the migrated figures rather than shifting them. And it stops substituting the fixed gas constant: a combination that had no stated value migrates as absent, so it surfaces in the missing-value count instead of quietly producing numbers.
Legal Context
N/A — no contract, licence or regulation governs this feature. The default values are taken from published national standards, and the standard each value came from is recorded on the row so a figure can be traced to its source; the standards themselves are not licensed material this product redistributes.
Cybersecurity Considerations
Platform defaults are writable only from the administration surface and are gated by a permission distinct from the read permission; they live in the cross-tenant schema, where the write policy admits only a context with no tenant set, so a customer can read every default and change none. Client and gauge values are ordinary tenant data behind the tenant isolation policy, and are editable by the roles that already own those screens.
Data Privacy. No personal data beyond the actor recorded on each change.
Risk Assessment
Business Risks
The expensive failure is quiet: a value stated at the wrong scope, or corrected without its validity date being understood, changes energy figures that nobody re-reads. The mitigations are that every value carries its scope, its date and its stated source; that resolution is one rule rather than three; and that a correction recomputes the affected period instead of leaving old figures standing.
The second is the opposite — an absent value stopping energy from being computed at all. That is deliberate: a counted, visible gap is recoverable, a plausible wrong number is not. The gap count is on the screen and in monitoring.
Technical Risks
| Risk | Consequence | Mitigation |
|---|---|---|
| A platform default is corrected | Recalculation across many tenants at once | Recalculation is limited to gauges that actually resolve to the platform scope, and to periods from the validity date |
| Scopes drift apart in shape again | Resolution needs special cases and stops being explainable | The two tables carry identical columns; they are separate only because of the schema boundary |
| A gauge's medium or primary source changes after values exist | The gauge silently resolves to a different value | The gauge-scope value is matched on the combination, so a mismatch resolves to none rather than to the wrong value, and surfaces in the gap count |
| Migrated values dated wrongly | Recomputed history disagrees with migrated figures | Each migrated value is dated from the earliest consumption that depends on it |
| Cascade — resolution unavailable | Energy cannot be derived from volumes; energy read directly from meters is unaffected | Values are stored data with no external dependency; a failed recalculation leaves previous figures intact |
Auditing, Reporting & Measurement
Every creation, correction and withdrawal is recorded against the value it changed, with the actor and the time; the audited field set is on each entity's page, and platform-scope changes are filed in the platform trail. Together with the source recorded on the row, that answers the question this feature exists to answer after the fact: which number was used for this period, at which scope, on whose authority. The gap count named under Monitoring is the only measurement the feature reports itself.