Appearance
Calorific Value Management — API Analysis
Three surfaces, one store. The platform default catalogue lives on the platform administration application (/v1/admin/…), following the pattern the other cross-tenant catalogues use. The client and gauge scopes live on the tenant backend under the resource that owns them — the client tab reads and writes /v1/client/calorific-values, the gauge detail reads and writes /v1/gauges/{id}/calorific-values. The client scope carries no client id: on the tenant backend every request is already in a client context, because a tenant is exactly one client (platform.tenants.client_id is NOT NULL and UNIQUE) — see Client scope = the request's tenant. All three write the same four facts (combination, value, units, validity date) and go through the same resolution rule at the end of this document. Screens: index.md — UI/UX Design.
Values are dated and append-only at every scope: a correction is a new row with its own validFrom, never an in-place update of the number. That is why the tenant endpoints below are POST to add, PATCH only for the fields that do not change energy (note), and DELETE to withdraw a row entered in error. Four endpoints on the platform administration application for the default catalogue, following the pattern the other cross-tenant catalogues use. The client and gauge scopes are edited through endpoints their own features own — /v1/client/calorific-values in Client Management and the gauge coefficient endpoints in Gauge Management — and are not redocumented here; what this analysis adds to them is the validity date on every value and the resolution rule below.
The project has no shared API-conventions document yet, so the two envelope shapes are stated once here. Success:
json
{
"data": {},
"status": 200,
"message": "OK",
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b21"
}Error:
json
{
"errorCode": "ERR_CALORIFIC_VALUE_DUPLICATE",
"errorMessage": "A default for this combination already starts on this date.",
"status": 409,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b21"
}Authentication and permission failures are answered by the platform application's shared handling and are not restated per endpoint.
📥 GET /v1/admin/calorific-values
Returns the whole default catalogue — every medium and primary source, including superseded values — so the administration screen can show each combination's history.
Authorization
Requires permission code platform.calorific-values.read, platform-scoped.
Request Headers
None beyond the platform application's standard bearer authentication.
Request Body
None.
Request Parameters
| Name | In | Type | Required | Default | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|---|---|
medium | Query | Enum | No | all media | Limits the result to one medium. | An entry from Enum - GaugeMedium. | defaultCalorificValue.medium |
primarySource | Query | Integer | No | all sources | Limits the result to one primary source. | An entry from Enum - PrimarySource; requires medium. | defaultCalorificValue.primarySource |
Request Logic
- Reads
shared.default_calorific_value; writes nothing. - Returns withdrawn rows as well, flagged, because the screen shows the history including corrections.
sql
SELECT id, medium, primary_source, value, unit_from, unit_to, valid_from, note, deleted_at
FROM shared.default_calorific_value
WHERE (@medium IS NULL OR medium = @medium)
AND (@primarySource IS NULL OR primary_source = @primarySource)
ORDER BY medium ASC, primary_source ASC, valid_from DESC;Transactional Operations
N/A — read-only.
✅ Success Response (200)
json
{
"data": [
{
"id": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b41",
"medium": "gas",
"primarySource": 1,
"value": 10.550000,
"unitFrom": "m3",
"unitTo": "kWh",
"validFrom": "2024-01-01",
"note": "ČSN 38 5502",
"inForce": true,
"withdrawn": false
}
],
"status": 200,
"message": "OK",
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b71"
}Response Data Mapping
| Response Field | Source / Value |
|---|---|
id | defaultCalorificValue.id |
medium | defaultCalorificValue.medium |
primarySource | defaultCalorificValue.primarySource |
value | defaultCalorificValue.value |
unitFrom | defaultCalorificValue.unitFrom |
unitTo | defaultCalorificValue.unitTo |
validFrom | defaultCalorificValue.validFrom |
note | defaultCalorificValue.note |
inForce | Derived: the row is the latest non-withdrawn one for its combination whose validFrom is not in the future |
withdrawn | Derived: defaultCalorificValue.deletedAt is set |
❌ Error Responses
400 ERR_CALORIFIC_VALUE_INVALID_FILTER
Thrown when medium or primarySource is not a value of its enum, or primarySource is given without medium.
json
{
"errorCode": "ERR_CALORIFIC_VALUE_INVALID_FILTER",
"errorMessage": "primarySource requires medium.",
"status": 400,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b71"
}📥 POST /v1/admin/calorific-values
Adds a platform default for one medium and primary source from one date, including a date in the future.
Authorization
Requires permission code platform.calorific-values.write, platform-scoped.
Request Headers
None beyond the platform application's standard bearer authentication.
Request Body
json
{
"medium": "gas",
"primarySource": 1,
"value": 10.550000,
"unitFrom": "m3",
"unitTo": "kWh",
"validFrom": "2026-01-01",
"note": "ČSN 38 5502, revize 2026"
}Request Parameters
| Name | In | Type | Required | Default | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|---|---|
medium | Body | Enum | Yes | - | Medium the default applies to. | An entry from Enum - GaugeMedium; media that never convert to energy are rejected. | defaultCalorificValue.medium |
primarySource | Body | Integer | Yes | - | Fuel or source within the medium. | An entry from Enum - PrimarySource. | defaultCalorificValue.primarySource |
value | Body | Decimal | Yes | - | Energy yielded by one unit of unitFrom. | Must be > 0; at most six decimal places. | defaultCalorificValue.value |
unitFrom | Body | Enum | Yes | - | Unit converted from. | An entry from Enum - Unit; must be a volume or mass unit. | defaultCalorificValue.unitFrom |
unitTo | Body | Enum | Yes | - | Unit converted to. | An entry from Enum - Unit; must be an energy unit. | defaultCalorificValue.unitTo |
validFrom | Body | Date | Yes | - | First day the default applies. | YYYY-MM-DD; may be in the future. | defaultCalorificValue.validFrom |
note | Body | String | No | null | The standard or measurement the value was taken from. | Max 500 characters. | defaultCalorificValue.note |
Request Logic
- Inserts one row into
shared.default_calorific_valuewithtenant_idnull, so the default is readable by every tenant. - Collision is left to the partial unique index rather than a preceding read.
- Writes one platform audit entry, and enqueues recalculation for gauges that resolve to the platform scope for this combination from the validity date.
sql
INSERT INTO shared.default_calorific_value
(id, tenant_id, medium, primary_source, value, unit_from, unit_to, valid_from, note, created_by, updated_by)
VALUES
(@id, NULL, @medium, @primarySource, @value, @unitFrom, @unitTo, @validFrom, @note, @actor, @actor);Transactional Operations
The row, its audit entry and the recalculation request commit together; a duplicate rejected by the unique index rolls back all three, so no recalculation is scheduled for a value that was not stored.
✅ Success Response (201)
json
{
"data": {
"id": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b43"
},
"status": 201,
"message": "Created",
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b72"
}Response Data Mapping
| Response Field | Source / Value |
|---|---|
id | defaultCalorificValue.id of the created row |
❌ Error Responses
400 ERR_CALORIFIC_VALUE_INVALID
Thrown when the combination is not one that converts to energy, the value is not positive, the units are not a volume-or-mass to energy pair, or the date is not a valid calendar date.
json
{
"errorCode": "ERR_CALORIFIC_VALUE_INVALID",
"errorMessage": "unitTo must be an energy unit.",
"status": 400,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b72"
}409 ERR_CALORIFIC_VALUE_DUPLICATE
Thrown when a default for the same medium and primary source already starts on the same date.
json
{
"errorCode": "ERR_CALORIFIC_VALUE_DUPLICATE",
"errorMessage": "A default for this combination already starts on this date.",
"status": 409,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b72"
}📥 PATCH /v1/admin/calorific-values/{id}
Corrects a default that was entered wrongly. The combination is not correctable — a value filed against the wrong medium or source is withdrawn and re-entered, so the history shows what happened.
Authorization
Requires permission code platform.calorific-values.write, platform-scoped.
Request Headers
None beyond the platform application's standard bearer authentication.
Request Body
json
{
"value": 10.480000,
"validFrom": "2026-01-01",
"note": "ČSN 38 5502, revize 2026 — oprava"
}Request Parameters
| Name | In | Type | Required | Default | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|---|---|
id | Path | UUID | Yes | - | The default being corrected. | Must exist and not be withdrawn. | defaultCalorificValue.id |
value | Body | Decimal | No | unchanged | Corrected value. | Must be > 0; at most six decimal places. | defaultCalorificValue.value |
unitFrom | Body | Enum | No | unchanged | Corrected source unit. | Volume or mass unit. | defaultCalorificValue.unitFrom |
unitTo | Body | Enum | No | unchanged | Corrected target unit. | Energy unit. | defaultCalorificValue.unitTo |
validFrom | Body | Date | No | unchanged | Corrected validity date. | YYYY-MM-DD. | defaultCalorificValue.validFrom |
note | Body | String | No | unchanged | Corrected source reference. | Max 500 characters. | defaultCalorificValue.note |
Request Logic
- Updates the named row, leaving
mediumandprimary_sourceuntouched. - Writes one platform audit entry carrying the changed fields only.
- Enqueues recalculation from the earlier of the old and new validity dates, so a date moved backwards also recomputes the period it now covers.
sql
UPDATE shared.default_calorific_value
SET value = COALESCE(@value, value),
unit_from = COALESCE(@unitFrom, unit_from),
unit_to = COALESCE(@unitTo, unit_to),
valid_from = COALESCE(@validFrom, valid_from),
note = COALESCE(@note, note),
updated_at = now(),
updated_by = @actor
WHERE id = @id
AND deleted_at IS NULL;Transactional Operations
The update, its audit entry and the recalculation request commit together. A corrected validity date that collides with another row for the same combination is rejected by the unique index and rolls all three back.
✅ Success Response (200)
json
{
"data": {
"id": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b43"
},
"status": 200,
"message": "OK",
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b73"
}Response Data Mapping
| Response Field | Source / Value |
|---|---|
id | defaultCalorificValue.id of the corrected row |
❌ Error Responses
400 ERR_CALORIFIC_VALUE_INVALID
Thrown for the same validation failures as on creation.
json
{
"errorCode": "ERR_CALORIFIC_VALUE_INVALID",
"errorMessage": "value must be greater than zero.",
"status": 400,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b73"
}404 ERR_CALORIFIC_VALUE_NOT_FOUND
Thrown when no such default exists, or it has already been withdrawn.
json
{
"errorCode": "ERR_CALORIFIC_VALUE_NOT_FOUND",
"errorMessage": "Default not found.",
"status": 404,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b73"
}409 ERR_CALORIFIC_VALUE_DUPLICATE
Thrown when the corrected validity date collides with another default for the same combination.
json
{
"errorCode": "ERR_CALORIFIC_VALUE_DUPLICATE",
"errorMessage": "A default for this combination already starts on this date.",
"status": 409,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b73"
}📥 DELETE /v1/admin/calorific-values/{id}
Withdraws a default entered in error. The row stops applying and stays in the history; a superseded default is never withdrawn, because it is what reproduces the energy it converted.
Authorization
Requires permission code platform.calorific-values.write, platform-scoped.
Request Headers
None beyond the platform application's standard bearer authentication.
Request Body
None.
Request Parameters
| Name | In | Type | Required | Default | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|---|---|
id | Path | UUID | Yes | - | The default being withdrawn. | Must exist and not already be withdrawn. | defaultCalorificValue.id |
Request Logic
- Sets
deleted_at; nothing is physically removed. - Frees the combination and date for re-entry, because the unique index ignores withdrawn rows.
- Writes one platform audit entry carrying the row in full, and enqueues recalculation from its validity date — gauges that resolved to it now resolve to the previous default, or to none.
sql
UPDATE shared.default_calorific_value
SET deleted_at = now(),
updated_at = now(),
updated_by = @actor
WHERE id = @id
AND deleted_at IS NULL;Transactional Operations
The withdrawal, its audit entry and the recalculation request commit together.
✅ Success Response (204)
No payload.
Response Data Mapping
N/A — no payload.
❌ Error Responses
404 ERR_CALORIFIC_VALUE_NOT_FOUND
Thrown when no such default exists, or it is already withdrawn.
json
{
"errorCode": "ERR_CALORIFIC_VALUE_NOT_FOUND",
"errorMessage": "Default not found.",
"status": 404,
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b74"
}Client scope = the request's tenant
The client scope is a business notion — nastavení klienta — and keeps the word client in the URL, but it is identified by the tenant of the request, not by an id the caller passes:
platform.tenants.client_idis NOT NULL and UNIQUE, so a tenant is exactly one client and every tenant-backend request is already in that client's context.- The tenant backend has no
/v1/clientscontroller andgaugehas noclient_id; rows are scoped bytenant_idthrough the request context (RLS,app.tenant_id). - Hence the singular path
/v1/client/calorific-values(+/history,/{id}) with no client id in the path or query. AclientIdparameter must never be added — the same rule the tenant-scoped tolerance read follows. - Where a row stores
clientId(calorificValue.clientId), the server fills it from the request's tenant; the caller never supplies it.
Response shapes, permissions and rules below are the same for every client-scope endpoint.
📥 GET /v1/client/calorific-values
The client tab. One row per combination of medium and primary source that the client's gauges actually use, with the value that resolves for it today, where that value comes from, and how many gauges it covers.
Authorization
Permission code tenant.clients.read. Tenant-scoped through the request context; the client is the request's tenant, so no client id is passed.
Request Parameters
| Name | In | Type | Required | Default | Description | Validation / Notes |
|---|---|---|---|---|---|---|
onDate | Query | Date | No | today | The day the resolution is evaluated for — the screen's "Platnost" filter. | YYYY-MM-DD |
medium | Query | Enum | No | all | Limits the rows to one medium. | Enum - GaugeMedium |
scope | Query | Enum | No | all | own — only combinations with a client value; missing — only combinations with no value at any scope. | all | own | missing |
Request Logic
- Combinations come from the client's active gauges whose medium converts volume or mass to energy (
gas,fuel,phm, heat delivered as GJ) — grouped by (medium,primarySource). - For each combination, three reads with the same validity rule as the resolver: the client value valid on
onDate, the platform default valid ononDate, and the gauge values valid ononDatefor the gauges in the combination. resolvedis the client value when one exists, else the platform default, else none;gaugesIndividualandgaugesWithoutValueare counted per gauge throughcalorificValueFor.summaryrepeats the four numbers the header shows so the screen needs one request.
sql
SELECT g.medium, g.primary_source, count(*) AS gauge_count
FROM gauge g
-- no client filter: the request's tenant is the client; RLS scopes gauge rows by tenant_id = app.tenant_id
WHERE g.is_archived = false AND g.medium IN ('gas','fuel','phm','heat')
GROUP BY g.medium, g.primary_source
ORDER BY g.medium, g.primary_source;✅ Success Response (200)
json
{
"data": {
"summary": { "combinations": 8, "ownValues": 1, "individualGauges": 2, "gaugesWithoutValue": 3 },
"items": [
{
"medium": "gas",
"primarySource": 1,
"gaugeCount": 14,
"resolved": { "scope": "client", "valueId": "018f9c4d-…-6b51", "value": 10.620000, "unitFrom": "m3", "unitTo": "kWh", "validFrom": "2026-01-01", "note": "Prohlášení GasNet Q1/2026" },
"tiers": {
"client": { "valueId": "018f9c4d-…-6b51", "value": 10.620000, "unitFrom": "m3", "unitTo": "kWh", "validFrom": "2026-01-01", "note": "Prohlášení GasNet Q1/2026", "updatedBy": "user:018ed0b3-…" },
"platform": { "valueId": "018f9c4d-…-6b21", "value": 10.550000, "unitFrom": "m3", "unitTo": "kWh", "validFrom": "2024-01-01", "note": "ČSN 38 5502" }
},
"gauges": {
"individual": [ { "gaugeId": "018f6e2a-…", "gaugeName": "Plynoměr G6", "buildingName": "Kotelna ZŠ Komenského", "value": 10.480000, "validFrom": "2026-03-01" } ],
"withoutValue": []
}
},
{
"medium": "fuel",
"primarySource": 4,
"gaugeCount": 3,
"resolved": null,
"tiers": { "client": null, "platform": null },
"gauges": { "individual": [], "withoutValue": [ { "gaugeId": "018f6e2b-…", "gaugeName": "Kotel K1", "buildingName": "MŠ Sluníčko" } ] }
}
]
},
"status": 200,
"message": "OK",
"requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b61"
}Response Data Mapping
| Response Field | Source / Value |
|---|---|
items[].medium, primarySource, gaugeCount | Grouped from gauge |
items[].resolved | Derived: the tier the resolver would pick for a gauge of this combination with no value of its own |
items[].tiers.client | calorificValue with gaugeId null, valid on onDate |
items[].tiers.platform | defaultCalorificValue valid on onDate |
items[].gauges.individual[] | calorificValue with gaugeId set, valid on onDate |
items[].gauges.withoutValue[] | Gauges for which calorificValueFor returns none on onDate |
summary.gaugesWithoutValue | gaugesWithoutValue() for the request's tenant |
📥 GET /v1/client/calorific-values/history
History of one combination at the client scope — every client value ever stated for it, withdrawn ones flagged — for the "Historie" action on a row.
Request Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
medium | Query | Enum | Yes | Combination's medium. |
primarySource | Query | Integer | Yes | Combination's primary source. |
Returns data: [ { valueId, value, unitFrom, unitTo, validFrom, note, withdrawn, createdBy, createdAt } ], newest validFrom first. Read-only, permission tenant.clients.read.
📥 POST /v1/client/calorific-values
Adds a client value for one combination from one date — "Nastavit vlastní" and every later correction alike.
Authorization
Permission code tenant.clients.write.
Request Body
| Name | Type | Required | Description | Validation / Notes | DB Mapping |
|---|---|---|---|---|---|
medium | Enum | Yes | Combination. | Must be a medium that converts to energy. | calorificValue.medium |
primarySource | Integer | Yes | Combination. | An entry from Enum - PrimarySource. | calorificValue.primarySource |
value | Decimal | Yes | The factor. | > 0, six decimals. | calorificValue.value |
unitFrom | Enum | Yes | Volume or mass unit. | Must match the unit the combination's gauges read in. | calorificValue.unitFrom |
unitTo | Enum | Yes | Energy unit. | calorificValue.unitTo | |
validFrom | Date | Yes | First day it applies. | May be in the future. Unique per combination at the client scope → 409 ERR_CALORIFIC_VALUE_DUPLICATE. | calorificValue.validFrom |
note | String | No | Where it comes from. | ≤ 500 characters. | calorificValue.note |
Request Logic
One transaction: insert the row with gaugeId = null and clientId taken from the request's tenant (platform.tenants.client_id), write the audit entry, enqueue recalculation from validFrom for the client's gauges of this combination that have no gauge value valid in the affected period. The response carries that gauge count so the screen can confirm what the dialog promised.
✅ Success Response (201)
json
{ "data": { "id": "018f9c4d-…-6b52", "recalculatedGauges": 12 }, "status": 201, "message": "Created", "requestId": "…" }❌ Error Responses
400 ERR_VALIDATION— body outside the schema;unitFromnot a volume/mass unit;unitTonot an energy unit.409 ERR_CALORIFIC_VALUE_DUPLICATE— a client value for this combination already starts onvalidFrom;detailscarries the existingvalueIdso the form can point at it.
📥 PATCH /v1/client/calorific-values/{id}
Corrects the citation of a client value. The number, units, combination and date are not correctable — a wrong one is withdrawn and re-entered, so the history shows what happened.
Body: { "note": "…" } only. Permission tenant.clients.write. 404 when the value belongs to another client or is withdrawn.
📥 DELETE /v1/client/calorific-values/{id}
Withdraws a client value entered in error. The row stops applying, stays in the history, and the affected gauges fall back to the next scope from its
validFrom.
Permission tenant.clients.write. Sets deletedAt, writes the audit entry, enqueues recalculation from the withdrawn row's validFrom for the gauges it resolved to. 409 ERR_CALORIFIC_VALUE_SUPERSEDED when a later client value for the same combination exists — a superseded value reproduces the energy of the period it covered and is never withdrawn.
📥 GET /v1/gauges/{gaugeId}/calorific-values
The card on the gauge detail: what applies to this gauge today, from which scope, and what would apply without the gauge's own value.
Authorization
Permission code tenant.gauges.read.
Request Parameters
| Name | In | Type | Required | Default | Description |
|---|---|---|---|---|---|
onDate | Query | Date | No | today | Day the resolution is evaluated for. |
Request Logic
calorificValueFor(gaugeId, onDate) plus the two tiers below it, and the gauge's own history. 404 when the gauge's medium does not convert to energy — the card is not shown for such gauges.
✅ Success Response (200)
json
{
"data": {
"medium": "gas", "primarySource": 1,
"resolved": { "scope": "gauge", "valueId": "018f9c4d-…-6b70", "value": 10.480000, "unitFrom": "m3", "unitTo": "kWh", "validFrom": "2026-03-01", "note": "Rozbor plynu kotelna 02/2026", "updatedBy": "user:018ed0b3-…" },
"tiers": {
"gauge": { "valueId": "018f9c4d-…-6b70", "value": 10.480000, "validFrom": "2026-03-01", "note": "Rozbor plynu kotelna 02/2026" },
"client": { "valueId": "018f9c4d-…-6b51", "value": 10.620000, "validFrom": "2026-01-01", "note": "Prohlášení GasNet Q1/2026" },
"platform": { "valueId": "018f9c4d-…-6b21", "value": 10.550000, "validFrom": "2024-01-01", "note": "ČSN 38 5502" }
},
"history": [ { "valueId": "018f9c4d-…-6b70", "value": 10.480000, "validFrom": "2026-03-01", "withdrawn": false, "createdBy": "user:018ed0b3-…", "createdAt": "2026-02-27T09:12:00Z" } ]
},
"status": 200, "message": "OK", "requestId": "…"
}📥 POST /v1/gauges/{gaugeId}/calorific-values
Adds a gauge value from one date — "Nastavit individuální".
Permission tenant.gauges.write. Body as the client POST without medium and primarySource — both are taken from the gauge, so a gauge value can never be filed against a combination the gauge does not have. Inserts the row with gaugeId and the gauge's clientId, writes the audit entry, enqueues recalculation for this gauge from validFrom. 409 ERR_CALORIFIC_VALUE_DUPLICATE on the same validFrom; 404 ERR_GAUGE_NOT_ENERGY_CONVERTIBLE when the medium does not convert.
PATCH /v1/gauges/{gaugeId}/calorific-values/{id} (note only) and DELETE /v1/gauges/{gaugeId}/calorific-values/{id} (withdraw; falls back to the client or platform tier from validFrom) follow the client-scope rules above.
Tenant endpoint summary
| Endpoint | Screen element | Permission |
|---|---|---|
GET /v1/client/calorific-values | Client tab: rows, tiers, gap count, filters | tenant.clients.read |
GET /v1/client/calorific-values/history | "Historie" on a row | tenant.clients.read |
POST /v1/client/calorific-values | "Nastavit vlastní" / "Upravit vlastní" (new dated row) | tenant.clients.write |
PATCH …/calorific-values/{id} | Fix the citation | tenant.clients.write |
DELETE …/calorific-values/{id} | Withdraw a mistaken value | tenant.clients.write |
GET /v1/gauges/{id}/calorific-values | Gauge detail card | tenant.gauges.read |
POST /v1/gauges/{id}/calorific-values | "Nastavit individuální" | tenant.gauges.write |
Emission factors are expected to reuse this shape one-to-one (/emission-factors), which is why the response carries tiers generically rather than calorific-specific names.
Internal access — resolving a value
The consumption calculation does not go through HTTP: it reads through a port with one operation, which is the only place the three scopes are compared.
calorificValueFor(gaugeId, onDate) -> { value, unitFrom, unitTo, scope, valueId } | none- Read-only, no transaction of its own; it participates in the caller's.
- Resolves in order gauge, client, platform default, taking at each step the latest non-withdrawn value whose
validFromis on or beforeonDate, and returns the first scope that has one. - A client or platform value is considered only when its medium and primary source match the gauge's.
- Returns
nonewhere no scope holds a value — never a constant. scopeandvalueIdcome back with the number so the caller can record and explain which value produced a figure.
A second operation reports the gap the screens and the monitor both use:
gaugesWithoutValue(clientId?) -> count- Counts gauges whose medium converts to energy and for which
calorificValueForreturnsnonetoday.