Skip to content
Updated Sep 28, 2026 by Pablo Coufal · Owner: analysisactiveuse-casestest-cases Edit on GitHub

Chart Engine — typové dotazy a odpovědi ​

Referenční dvojice request → response pro GET /v1/charts/series podle kontraktu v API Analysis a pravidel ve feature docu. Čísla jsou spočítaná ručně z fixtur, ne vygenerovaná — proto se dají použít jako test. Deset případů, U1 až U10, slouží třem místům najednou:

  • fixtury mocku obrazovky se berou odsud, verbatim;
  • testovací korpus pro testovou analýzu epicu;
  • referenční odpověď, proti které se píše backend.

Odpovědi jsou zkrácené v tom smyslu, že z dlouhých polí points je vypsaný začátek a konec; pravidlo pro dopočet zbytku je vždy uvedené. Nic jiného zkrácené není.

Fixtury, ze kterých se počítá ​

FixturaCo to jeData
CHART-E1elektroměr odběru, objekt CHART-B12,5 kWh v každé čtvrthodině března 2026, mimo výpadek 10. 3. 08:00–12:00 (16 intervalů chybí)
CHART-B1objekt se systémovým součtemelektřina: odběr CHART-E1 + výroba FVE 3 000 − dodávka do sítě 1 200 · plyn v kWh · teplo v GJ · voda v m³ · podružný elektroměr kuchyně (reportOnly, jen se zobrazuje)
CHART-B1-KITCHENpodružný elektroměr kuchyně pod CHART-E1, outputMode = reportOnly (výchozí pro nové podružné měřidlo)2 250 kWh — o příspěvku rozhoduje vlastní nastavení měřidla, ne poloha pod rodičem (rozhodnutí 28. 9., U2 a U9)
CHART-S1sektor školstvíobjekty CHART-B1, B2, B3, B4, B5
CHART-C1areálsektory CHART-S1 a CHART-S2
CHART-B9objekt bez systémového součtu pro teploslouží stavu noSystemTotal
CHART-B1-PV / -EXPORT / -CHECKvýroba FVE, dodávka do sítě, kontrolní měřidlo objektu CHART-B13 000 kWh (include+import) · 1 200 kWh (include+export) · 1 400 kWh (reportOnly) — znaménka pro U9
CHART-B2-SHOPnájemní prodejna v objektu CHART-B2, outputMode = subtract (přefakturovaná nájemci), v legacy navíc připočtená do CHART-B11 800 kWh — jediný subtract v sadě a případ přefakturace přes objekty (U9)
CHART-B2-OLDvyměněný elektroměr objektu CHART-B2, ponechaný kvůli historii, outputMode = exclude900 kWh — jediný exclude v sadě; do žádného součtu nevstupuje

Kalendář. Březen 2026 má 2 972 čtvrthodin (29. 3. má 92 místo 96), říjen 2026 má 2 980 (25. 10. má 100). Běžný den má 96 intervalů = 240 kWh při 2,5 kWh/interval.


U1 — Jedno měřidlo, měsíčně, kalendářní rok ​

Základní tvar odpovědi. Nejmenší dotaz, který má smysl ukázat testerovi.

GET /v1/charts/series
  ?metric=consumption
  &medium=electricity
  &scopeType=gauge&scopeId=CHART-E1
  &granularity=month
  &from=2026-01-01T00:00:00%2B01:00&to=2027-01-01T00:00:00%2B01:00
  &unit=kWh
json
{
  "data": {
    "query": { "metric": "consumption", "medium": ["electricity"], "scopeType": "gauge", "scopeId": ["CHART-E1"],
               "breakdown": "subject", "granularity": "month", "unit": "kWh", "sourceMode": "effective",
               "sortKey": "label", "sortDirection": "asc",
               "from": "2026-01-01T00:00:00+01:00", "to": "2027-01-01T00:00:00+01:00" },
    "served": { "from": "2026-01-01T00:00:00+01:00", "to": "2027-01-01T00:00:00+01:00",
                "timezone": "Europe/Prague", "clippedEdges": false },
    "provisional": false,
    "series": [
      {
        "id": "gauge:CHART-E1|electricity",
        "subject": { "type": "gauge", "id": "CHART-E1", "label": "ZŠ Komenského — hlavní elektroměr" },
        "medium": "electricity",
        "unit": "kWh",
        "total": 7390.000000,
        "sortValue": 7390.000000,
        "coverage": { "intervalsInPeriod": 35036, "intervalsCovered": 2956, "ratio": 0.0844 },
        "points": [
          { "from": "2026-01-01T00:00:00+01:00", "to": "2026-02-01T00:00:00+01:00", "value": null,
            "coverage": { "intervalsInPeriod": 2976, "intervalsCovered": 0, "ratio": 0.0 } },
          { "from": "2026-03-01T00:00:00+01:00", "to": "2026-04-01T00:00:00+02:00", "value": 7390.000000,
            "coverage": { "intervalsInPeriod": 2972, "intervalsCovered": 2956, "ratio": 0.9946 } }
        ]
      }
    ],
    "total": {
      "value": 7390.000000, "unit": "kWh", "rowSum": 7390.000000, "excludedSubjects": [],
      "byMedium": [ { "medium": "electricity", "value": 7390.000000 } ],
      "coverage": { "intervalsInPeriod": 35036, "intervalsCovered": 2956, "ratio": 0.0844 },
      "points": [
        { "from": "2026-03-01T00:00:00+01:00", "to": "2026-04-01T00:00:00+02:00", "value": 7390.000000,
          "coverage": { "intervalsInPeriod": 2972, "intervalsCovered": 2956, "ratio": 0.9946 } }
      ]
    },
    "unavailable": []
  },
  "status": 200, "message": "OK", "requestId": "018f9c4d-2c11-7a3e-9b77-4f0d3a5e6b21"
}

Ruční kontrola: 2 972 intervalů března − 16 chybějících = 2 956 × 2,5 kWh = 7 390 kWh. Ostatních jedenáct měsíců fixtura nemá, takže jsou null s nulovým pokrytím — ne nula. Roční pokrytí 2 956 / 35 036 je nízké právem: rok opravdu není pokrytý.

Co tím testujeme: tvar odpovědi, series.id, měsíční kbelík přes změnu času (1. 3. +01:00 → 1. 4. +02:00), rozdíl mezi null a nulou, pokrytí počítané ve čtvrthodinách i na měsíční podrobnosti.


U2 — Pět měřidel, jedno podružné pod jiným ​

Proč rowSum a value nejsou totéž. Čísla schválená v analýze: prostý součet 19 000, skutečný 16 750.

GET /v1/charts/series
  ?medium=electricity
  &scopeType=gauge&scopeId=B1-MAIN&scopeId=B1-KITCHEN&scopeId=B2-MAIN&scopeId=B3-MAIN&scopeId=B4-MAIN
  &granularity=month&from=2025-01-01T00:00:00%2B01:00&to=2025-04-01T00:00:00%2B02:00
  &unit=kWh&sortKey=total&sortDirection=desc

Odpověď (vypsané jen součty řad a rozhodující části):

json
{
  "series": [
    { "id": "gauge:B1-MAIN|electricity",    "subject": { "type": "gauge", "id": "B1-MAIN",    "label": "B1 — hlavní" },   "total": 6000.000000, "sortValue": 6000.000000, "contribution": "add" },
    { "id": "gauge:B2-MAIN|electricity",    "subject": { "type": "gauge", "id": "B2-MAIN",    "label": "B2 — hlavní" },   "total": 5000.000000, "sortValue": 5000.000000, "contribution": "add" },
    { "id": "gauge:B3-MAIN|electricity",    "subject": { "type": "gauge", "id": "B3-MAIN",    "label": "B3 — hlavní" },   "total": 3500.000000, "sortValue": 3500.000000, "contribution": "add" },
    { "id": "gauge:B4-MAIN|electricity",    "subject": { "type": "gauge", "id": "B4-MAIN",    "label": "B4 — hlavní" },   "total": 2250.000000, "sortValue": 2250.000000, "contribution": "add" },
    { "id": "gauge:B1-KITCHEN|electricity", "subject": { "type": "gauge", "id": "B1-KITCHEN", "label": "B1 — kuchyně" },  "total": 2250.000000, "sortValue": 2250.000000, "contribution": "none" }
  ],
  "total": {
    "value": 16750.000000,
    "rowSum": 19000.000000,
    "unit": "kWh",
    "excludedSubjects": [
      { "seriesId": "gauge:B1-KITCHEN|electricity", "medium": "electricity",
        "reason": "excludedByOutputMode", "outputMode": "reportOnly", "value": 2250.000000 }
    ],
    "byMedium": [ { "medium": "electricity", "value": 16750.000000 } ],
    "points": [
      { "from": "2025-01-01T00:00:00+01:00", "to": "2025-02-01T00:00:00+01:00", "value": 6000.000000 },
      { "from": "2025-02-01T00:00:00+01:00", "to": "2025-03-01T00:00:00+01:00", "value": 5500.000000 },
      { "from": "2025-03-01T00:00:00+01:00", "to": "2025-04-01T00:00:00+02:00", "value": 5250.000000 }
    ]
  }
}

Ruční kontrola: 6 000 + 5 000 + 3 500 + 2 250 + 2 250 = 19 000 (rowSum). Kuchyně je podružná pod B1-MAIN a má outputMode = reportOnly (výchozí pro podružné měřidlo — Nezapočítávat), takže se do součtu nezapočítá dvakrát: 19 000 − 2 250 = 16 750 (value). Rozhoduje její vlastní nastavení, ne to, že je pod rodičem: kdyby ji uživatel přepnul na Přičíst (include), value by bylo 19 000 a engine by to nepřepsal — varování o dvojím započtení patří do formuláře měřidla, ne do grafu. Součet total.points: 6 000 + 5 500 + 5 250 = 16 750 — sedí.

Co tím testujeme: řazení podle součtu, dvojí započtení, excludedSubjects s důvodem excludedByOutputMode, a hlavně to, že řádek Celkem v tabulce nesmí být součet sloupce — je o 2 250 kWh nižší.


U3 — Tři energetická média na jednom grafu ​

GET /v1/charts/series
  ?medium=electricity&medium=gas&medium=heat
  &scopeType=building&scopeId=CHART-B1
  &granularity=month&from=2025-01-01T00:00:00%2B01:00&to=2025-04-01T00:00:00%2B02:00
  &unit=MWh
json
{
  "series": [
    { "id": "building:CHART-B1|electricity", "medium": "electricity", "unit": "MWh", "total": 9.800000,
      "points": [ { "value": 3.600000 }, { "value": 3.300000 }, { "value": 2.900000 } ] },
    { "id": "building:CHART-B1|gas",         "medium": "gas",         "unit": "MWh", "total": 24.000000,
      "points": [ { "value": 11.000000 }, { "value": 8.000000 }, { "value": 5.000000 } ] },
    { "id": "building:CHART-B1|heat",        "medium": "heat",        "unit": "MWh", "total": 15.000000,
      "points": [ { "value": 7.000000 }, { "value": 5.000000 }, { "value": 3.000000 } ] }
  ],
  "total": {
    "value": 48.800000, "unit": "MWh", "rowSum": 48.800000, "excludedSubjects": [],
    "byMedium": [
      { "medium": "electricity", "value": 9.800000 },
      { "medium": "gas",         "value": 24.000000 },
      { "medium": "heat",        "value": 15.000000 }
    ],
    "points": [ { "value": 21.600000 }, { "value": 16.300000 }, { "value": 10.900000 } ]
  }
}

Ruční kontrola: elektřina 9,8 = odběr 8,0 + FVE 3,0 − dodávka 1,2 (systémový součet objektu, ne součet měřidel). Teplo je ve fixtuře uložené v GJ: 54 GJ = 15 MWh (54 ÷ 3,6). Celkem 9,8 + 24 + 15 = 48,8 MWh.

Co tím testujeme: tři samostatné řady na jedné ose — nikdy jeden sloupec —, převod GJ → MWh v enginu, byMedium, a systémový součet objektu.


U4 — Voda k tomu: druhá odpověď, druhý graf ​

Tentýž výběr s vodou navíc. Obrazovka nepošle jeden dotaz — pošle druhý.

GET /v1/charts/series?medium=water&scopeType=building&scopeId=CHART-B1
  &granularity=month&from=2025-01-01T00:00:00%2B01:00&to=2025-04-01T00:00:00%2B02:00&unit=m3
json
{
  "series": [
    { "id": "building:CHART-B1|water", "medium": "water", "unit": "m³", "total": 312.000000,
      "points": [ { "value": 104.000000 }, { "value": 98.000000 }, { "value": 110.000000 } ] }
  ],
  "total": { "value": 312.000000, "unit": "m³", "rowSum": 312.000000, "byMedium": [ { "medium": "water", "value": 312.000000 } ] }
}

A kdyby to obrazovka zkusila jedním dotazem:

GET /v1/charts/series?medium=electricity&medium=water&…   →   400
{ "errorCode": "ERR_CHART_SERIES_MEDIUM_MIX",
  "errorMessage": "Media must belong to one quantity family: electricity is energy, water is volume.",
  "status": 400 }

Co tím testujeme: dvě odpovědi vedle sebe, každá s vlastní jednotkou a vlastním součtem, a odmítnutí kombinace, ke které se uživatel přes UI nesmí dostat.


U5 — Čtvrthodiny přes den se změnou času ​

GET /v1/charts/series?medium=electricity&scopeType=gauge&scopeId=CHART-E1
  &granularity=15min&from=2026-03-28T00:00:00%2B01:00&to=2026-03-31T00:00:00%2B02:00&unit=kWh
json
{
  "served": { "from": "2026-03-28T00:00:00+01:00", "to": "2026-03-31T00:00:00+02:00",
              "timezone": "Europe/Prague", "clippedEdges": false },
  "series": [
    {
      "id": "gauge:CHART-E1|electricity", "medium": "electricity", "unit": "kWh",
      "total": 710.000000,
      "coverage": { "intervalsInPeriod": 284, "intervalsCovered": 284, "ratio": 1.0 },
      "points": [
        { "from": "2026-03-28T00:00:00+01:00", "to": "2026-03-28T00:15:00+01:00", "value": 2.500000 },
        { "from": "2026-03-29T01:30:00+01:00", "to": "2026-03-29T01:45:00+01:00", "value": 2.500000 },
        { "from": "2026-03-29T01:45:00+01:00", "to": "2026-03-29T03:00:00+02:00", "value": 2.500000 },
        { "from": "2026-03-29T03:00:00+02:00", "to": "2026-03-29T03:15:00+02:00", "value": 2.500000 },
        { "from": "2026-03-30T23:45:00+02:00", "to": "2026-03-31T00:00:00+02:00", "value": 2.500000 }
      ]
    }
  ],
  "total": { "value": 710.000000, "unit": "kWh", "rowSum": 710.000000 }
}

Ruční kontrola: 28. 3. má 96 intervalů, 29. 3. jen 92 (hodina 02:00–03:00 neexistuje), 30. 3. má 96 → 284 × 2,5 = 710 kWh. Kdyby engine počítal 3 × 96, vyšlo by 720 — a právě to je chyba, kterou tenhle případ chytá.

Týž rozsah po dnech vrátí tři kbelíky: 240, 230, 240 kWh.

U5b — výpadek a částečně pokrytý kbelík (denní podrobnost) ​

GET /v1/charts/series?…&granularity=day&from=2026-03-09T00:00:00%2B01:00&to=2026-03-12T00:00:00%2B01:00
json
"points": [
  { "from": "2026-03-09T00:00:00+01:00", "to": "2026-03-10T00:00:00+01:00", "value": 240.000000,
    "coverage": { "intervalsInPeriod": 96, "intervalsCovered": 96, "ratio": 1.0 } },
  { "from": "2026-03-10T00:00:00+01:00", "to": "2026-03-11T00:00:00+01:00", "value": 200.000000,
    "coverage": { "intervalsInPeriod": 96, "intervalsCovered": 80, "ratio": 0.8333 } },
  { "from": "2026-03-11T00:00:00+01:00", "to": "2026-03-12T00:00:00+01:00", "value": 240.000000,
    "coverage": { "intervalsInPeriod": 96, "intervalsCovered": 96, "ratio": 1.0 } }
]

Ruční kontrola: 10. 3. chybí 16 intervalů (08:00–12:00), zbylo 80 × 2,5 = 200 kWh při pokrytí 83 %. Sloupec je nižší, ale není to úspora — a odpověď to říká číslem, ne poznámkou pod grafem.


U6 — Vlastní období 15. 1. – 15. 2., měsíčně: ořezané okraje ​

Fixtura B2-MAIN má rovnoměrných 10 kWh/den v lednu i únoru 2025.

GET /v1/charts/series?medium=electricity&scopeType=gauge&scopeId=B2-MAIN
  &granularity=month&from=2025-01-15T00:00:00%2B01:00&to=2025-02-16T00:00:00%2B01:00&unit=kWh
json
{
  "served": { "from": "2025-01-15T00:00:00+01:00", "to": "2025-02-16T00:00:00+01:00",
              "timezone": "Europe/Prague", "clippedEdges": true },
  "series": [
    {
      "id": "gauge:B2-MAIN|electricity", "unit": "kWh", "total": 320.000000,
      "points": [
        { "from": "2025-01-15T00:00:00+01:00", "to": "2025-02-01T00:00:00+01:00", "value": 170.000000,
          "clipped": true,
          "coverage": { "intervalsInPeriod": 1632, "intervalsCovered": 1632, "ratio": 1.0 } },
        { "from": "2025-02-01T00:00:00+01:00", "to": "2025-02-16T00:00:00+01:00", "value": 150.000000,
          "clipped": true,
          "coverage": { "intervalsInPeriod": 1440, "intervalsCovered": 1440, "ratio": 1.0 } }
      ]
    }
  ],
  "total": { "value": 320.000000, "unit": "kWh" }
}

Ruční kontrola: 15.–31. 1. je 17 dní = 170 kWh, 1.–15. 2. je 15 dní = 150 kWh, celkem 320 kWh. Starý SQL podle začátku kbelíku by vrátil celý leden (310) a celý únor (280) = 590 kWh za období, které tolik nemá. Týž dotaz na GET /v1/consumption/summary musí vrátit 320.

Co tím testujeme: ořez na obou koncích, clippedEdges, skutečné hranice v bodech, a shodu s agregačním endpointem.


U7 — Areál: rozpad po objektech × jeden součet ​

GET /v1/charts/series?medium=electricity&scopeType=campusNode&scopeId=CHART-C1
  &breakdown=building&granularity=month&from=2025-01-01T00:00:00%2B01:00&to=2025-02-01T00:00:00%2B01:00&unit=kWh
json
"series": [
  { "id": "building:CHART-B1|electricity", "subject": { "type": "building", "id": "CHART-B1", "label": "ZŠ Komenského" }, "total": 9800.000000 },
  { "id": "building:CHART-B2|electricity", "subject": { "type": "building", "id": "CHART-B2", "label": "ZŠ Nádražní" },   "total": 5000.000000 },
  { "id": "building:CHART-B3|electricity", "subject": { "type": "building", "id": "CHART-B3", "label": "MŠ Sluníčko" },   "total": 3500.000000 },
  { "id": "building:CHART-B4|electricity", "subject": { "type": "building", "id": "CHART-B4", "label": "Sokolovna" },     "total": 2250.000000 },
  { "id": "building:CHART-B5|electricity", "subject": { "type": "building", "id": "CHART-B5", "label": "Knihovna" },      "total": 1450.000000 }
],
"total": { "value": 22000.000000, "rowSum": 22000.000000, "unit": "kWh", "excludedSubjects": [] }

Týž dotaz s breakdown=none:

json
"series": [
  { "id": "campusNode:CHART-C1|electricity", "subject": { "type": "campusNode", "id": "CHART-C1", "label": "Areál Jih" },
    "medium": "electricity", "total": 22000.000000 }
],
"total": { "value": 22000.000000, "rowSum": 22000.000000, "unit": "kWh" }

Ruční kontrola: 9 800 + 5 000 + 3 500 + 2 250 + 1 450 = 22 000 kWh; jeden součet i pět řad dávají totéž číslo. Kdyby byl CHART-B3 zároveň pod druhým vybraným uzlem, objeví se jednou a druhá cesta je v excludedSubjects s důvodem alreadyCounted.

Co tím testujeme: rozsah není totéž co řada (4.1 OR-2), a překryv se hlídá i mezi podstromy.


U8 — Tři stavy, které nejsou prázdný graf ​

Jeden dotaz přes objekty, kde každý selže jinak.

GET /v1/charts/series?medium=heat&scopeType=building&scopeId=CHART-B1&scopeId=CHART-B9&scopeId=CHART-B7
  &granularity=month&from=2025-01-01T00:00:00%2B01:00&to=2025-02-01T00:00:00%2B01:00&unit=kWh
json
{
  "series": [
    { "id": "building:CHART-B1|heat", "subject": { "type": "building", "id": "CHART-B1", "label": "ZŠ Komenského" },
      "medium": "heat", "unit": "kWh", "total": 7000.000000 },
    { "id": "building:CHART-B7|heat", "subject": { "type": "building", "id": "CHART-B7", "label": "Hasičská zbrojnice" },
      "medium": "heat", "unit": "kWh", "total": null,
      "coverage": { "intervalsInPeriod": 2976, "intervalsCovered": 0, "ratio": 0.0 },
      "points": [ { "from": "2025-01-01T00:00:00+01:00", "to": "2025-02-01T00:00:00+01:00", "value": null } ] }
  ],
  "unavailable": [
    { "subject": { "type": "building", "id": "CHART-B9", "label": "Sklad Průmyslová" }, "medium": "heat",
      "reason": "noSystemTotal", "detail": "objekt nemá systémový součet pro teplo" }
  ],
  "total": { "value": 7000.000000, "rowSum": 7000.000000, "unit": "kWh" }
}

Dvě varianty téhož principu:

json
// metric=cost
"unavailable": [ { "metric": "cost", "reason": "capabilityDeferred", "pendingDependency": "pricing-data-source" } ]

// plynoměr uložený v m³, dotaz v kWh, bez výhřevnosti
"unavailable": [ { "subject": { "type": "gauge", "id": "B5-GAS-M3", "label": "Knihovna — plynoměr" },
                   "medium": "gas", "reason": "unsupportedConversion",
                   "detail": "m³ → kWh vyžaduje výhřevnost, která pro toto měřidlo není zadaná" } ]

Co tím testujeme: CHART-B7 je vybraný, použitelný a nemá data → zůstává v odpovědi jako řada samých null, v legendě i v tabulce. CHART-B9 je vybraný, ale otázka na něj nedává smysl → je v unavailable se svým jménem. Ani jeden prostě nezmizí. To je rozdíl mezi „nic jsme nenaměřili" a „takový objekt neznáme".


U9 — Znaménka: výroba, dodávka do sítě, kontrolní a přefakturační měřidlo ​

Proč rowSum a value nejsou totéž podruhé — a tentokrát to není podružným měřidlem, ale tím, jak měřidlo účtuje. Pět měřidel objektu CHART-B1, březen 2026:

MěřidlooutputModedirectionHodnota řadycontribution
CHART-E1 — odběr ze sítěincludeimport7 390 kWhadd
CHART-B1-PV — výroba FVEincludeimport3 000 kWhadd
CHART-B1-EXPORT — dodávka do sítěincludeexport1 200 kWhsubtract
CHART-B1-KITCHEN — podružné pod CHART-E1reportOnlyimport2 250 kWhnone
CHART-B1-CHECK — kontrolní, paralelně k hlavnímureportOnlyimport1 400 kWhnone
GET /v1/charts/series
  ?medium=electricity
  &scopeType=gauge&scopeId=CHART-E1&scopeId=CHART-B1-PV&scopeId=CHART-B1-EXPORT
  &scopeId=CHART-B1-KITCHEN&scopeId=CHART-B1-CHECK
  &granularity=month&from=2026-03-01T00:00:00%2B01:00&to=2026-04-01T00:00:00%2B02:00
  &unit=kWh

Odpověď (vypsané jen součty řad a součet výběru):

json
{
  "series": [
    { "id": "gauge:CHART-E1|electricity",            "subject": { "type": "gauge", "id": "CHART-E1",            "label": "B1 — odběr ze sítě" },   "total": 7390.000000, "contribution": "add" },
    { "id": "gauge:CHART-B1-PV|electricity",         "subject": { "type": "gauge", "id": "CHART-B1-PV",         "label": "B1 — výroba FVE" },      "total": 3000.000000, "contribution": "add" },
    { "id": "gauge:CHART-B1-EXPORT|electricity",     "subject": { "type": "gauge", "id": "CHART-B1-EXPORT",     "label": "B1 — dodávka do sítě" }, "total": 1200.000000, "contribution": "subtract" },
    { "id": "gauge:CHART-B1-KITCHEN|electricity",    "subject": { "type": "gauge", "id": "CHART-B1-KITCHEN",    "label": "B1 — kuchyně" },         "total": 2250.000000, "contribution": "none" },
    { "id": "gauge:CHART-B1-CHECK|electricity",      "subject": { "type": "gauge", "id": "CHART-B1-CHECK",      "label": "B1 — kontrolní" },       "total": 1400.000000, "contribution": "none" }
  ],
  "total": {
    "value": 9190.000000,
    "rowSum": 15240.000000,
    "unit": "kWh",
    "excludedSubjects": [
      { "seriesId": "gauge:CHART-B1-KITCHEN|electricity", "medium": "electricity",
        "reason": "excludedByOutputMode", "outputMode": "reportOnly", "value": 2250.000000 },
      { "seriesId": "gauge:CHART-B1-CHECK|electricity", "medium": "electricity",
        "reason": "excludedByOutputMode", "outputMode": "reportOnly", "value": 1400.000000 }
    ],
    "byMedium": [ { "medium": "electricity", "value": 9190.000000 } ]
  }
}

Ruční kontrola: 7 390 + 3 000 + 1 200 + 2 250 + 1 400 = 15 240 (rowSum, prostý součet sloupce). Se znaménky a vyloučeními: 7 390 + 3 000 − 1 200 + 0 + 0 = 9 190 (value). Kuchyně i kontrolní měřidlo vypadávají ze stejného důvodu — jejich vlastní outputMode; to, že kuchyně visí pod CHART-E1, engine nezajímá. A to je přesně systémový součet objektu: dotaz scopeType=building&scopeId=CHART-B1 na stejné období vrátí jednu řadu o součtu 9 190 kWh. Dvě cesty ke stejnému číslu — to je ten test.

Co tím testujeme: že kontrakt nese znaménko, ne jen hodnotu. Kontrolní měřidlo se v grafu vykreslí (uživatel si ho vybral, chce ho vidět), ale součtem nehne. Dodávka do sítě se kreslí kladně a součet snižuje. Bez pole contribution by FE nedokázalo vysvětlit, proč řádek Celkem není součtem sloupce — a uživatel by to bral jako chybu.

Přefakturace přes dva objekty — co první řez nedělá ​

Legacy umí zavěsit podružné měřidlo pod hlavní měřidlo jiného objektu: energie se v místě měření odečte a v místě fakturace přičte. EM3 počítá systémový součet objektu nad měřidly téhož objektu, takže tento převod nikde nevzniká a chart engine si ho nevymýšlí.

GET /v1/charts/series
  ?medium=electricity
  &scopeType=building&scopeId=CHART-B1&scopeId=CHART-B2
  &scopeType=gauge&scopeId=CHART-B2-SHOP
  &granularity=month&from=2026-03-01T00:00:00%2B01:00&to=2026-04-01T00:00:00%2B02:00&unit=kWh

CHART-B2-SHOP je nájemní prodejna v objektu B2 s outputMode = subtract: v systémovém součtu B2 se její spotřeba odečítá, protože ji platí nájemce. Legacy ji navíc připočítává do B1 — a právě tuhle druhou polovinu převodu EM3 nedělá. V odpovědi:

json
"excludedSubjects": [
  { "seriesId": "gauge:CHART-B2-SHOP|electricity", "medium": "electricity",
    "containedIn": { "type": "building", "id": "CHART-B2", "label": "B2" },
    "reason": "containedInSubject", "value": 1800.000000 }
]

Prodejna se vykreslí jako vlastní řada, ale do součtu výběru vstupuje jen přes objekt B2, v jehož systémovém součtu už je (se záporným znaménkem, které jí dává subtract). Žádná část její spotřeby se nepřesouvá do B1. Stejně tak CHART-B2-OLD — vyměněný elektroměr s outputMode = exclude — se vykreslí, když si ho uživatel vybere, a do žádného součtu nevstoupí; v excludedSubjects má důvod excludedByOutputMode. Jestli se převod mezi objekty má modelovat vůbec, je otázka na Consumption & Cost Calculation Core, ne na graf — v Chart Engine docu je to vedeno jako odložené.


U10 — Záhlaví hodnotové tabulky podle zvoleného filtru ​

Úrovně ve složeném záhlaví nejsou pevná šablona — jsou to ty, které uživatel v kaskádě skutečně zvolil, v jejím pořadí, plus typ hodnoty jako poslední řádek. Stejná data, tři hloubky filtru. Areál C-JIH, elektřina, leden–březen 2025, celkem 24 000 kWh ve všech třech.

a) Vybraný jen areál. Filtr: areál = Areál Jih, objekt = —, měřidlo = —.

GET /v1/charts/series?medium=electricity&scopeType=campusNode&scopeId=C-JIH
  &granularity=month&from=2025-01-01T00:00:00%2B01:00&to=2025-04-01T00:00:00%2B02:00&unit=kWh

Jedna řada (campusNode:C-JIH|electricity, 24 000 kWh). Záhlaví má dva řádky:

ObdobíAreál: Areál Jih
Jednotka: kWh
leden 20259 000
únor 20258 000
březen 20257 000
Celkem24 000

b) Areál + rozpad po objektech. &breakdown=building → dvě řady (13 300 a 10 700 kWh). Záhlaví má tři řádky: areál (spojený nad oběma sloupci) / objekt / jednotka.

c) Areál + objekt + měřidlo. &breakdown=gauge → čtyři řady (9 300 + 4 000 + 7 500 + 3 200 kWh). Záhlaví má čtyři řádky: areál / objekt (každý spojený nad svými měřidly) / měřidlo / jednotka.

Ruční kontrola: 13 300 + 10 700 = 24 000 · 9 300 + 4 000 = 13 300 · 7 500 + 3 200 = 10 700. Součet výběru je ve všech třech hloubkách stejný; mění se jen to, jak jemně je rozepsaný.

Co tím testujeme: že se v záhlaví neobjeví úroveň, kterou uživatel nezúžil — žádné prázdné „objekt" a „měřidlo" u výběru samotného areálu — a že každé zúžení přidá právě jeden řádek, v pořadí kaskády. Typ hodnoty (jednotka, později Kč) je vždy poslední řádek, protože je to to, čím to číslo je.


Napojení na Highcharts ​

Adapter nic nepočítá; překládá. Pravidla podle review:

Z odpovědiDo Highcharts
series[].idseries.id — klíč viditelnosti, ne index ani popisek
series[].subject.label + mediumseries.name („ZŠ Komenského — plyn")
points[].from, valuedata: [[Date.parse(from), value], …] na datetime ose; hodnota null se posílá, nevynechává
points[].to, coverage, clippedsidecar podle id + časové značky pro tooltip a tabulku
jednotkayAxis.title.text, tooltip.valueSuffix
pořadí řadzachovat; nikdy neřadit v knihovně
total.pointsřádek Celkem v tabulce, ne série v grafu
js
Highcharts.setOptions({ time: { timezone: 'Europe/Prague' } });   // ne prohlížečové pásmo

const options = {
  chart: { type: 'column' },
  xAxis: { type: 'datetime' },
  yAxis: { title: { text: response.total.unit } },
  plotOptions: { series: { connectNulls: false, turboThreshold: 0 } },
  series: response.series.map(s => ({
    id: s.id,
    name: `${s.subject.label} — ${mediumLabel(s.medium)}`,
    color: mediumColor(s.medium),
    data: s.points.map(p => [Date.parse(p.from), p.value])
  }))
};

Na co si dát pozor: turboThreshold je ve výchozím stavu 1 000 bodů a formát [x, y] ho přežije, ale u delších řad ho raději vypnout; dataGrouping se nesmí zapnout (je součástí Highcharts Stock a mění hodnoty podle šířky v pixelech); accessibility modul načíst, protože tabulka je textová alternativa grafu a legenda musí jít ovládat klávesnicí.

Co který případ ověřuje ​

PřípadOvěřujePravidlo
U1tvar odpovědi, series.id, null × nula, měsíční pokrytí ve čtvrthodináchFR1, FR11
U2řazení, dvojí započtení, excludedSubjects, total.pointsFR9, FR10
U3tři řady na jedné ose, GJ → MWh, byMedium, systémový součetFR8, FR9
U4dvě odpovědi, dva grafy, ERR_CHART_SERIES_MEDIUM_MIXFR9, API-A → Error Responses
U592 intervalů v den změny času, částečné pokrytí, 15 min bez stropuFR2, FR11
U6ořezané okraje, shoda se /consumption/summaryFR2, FR11
U7scope × breakdown, překryv podstromů areáluFR1, FR9
U8noData × noSystemTotal × capabilityDeferred × unsupportedConversionFR11
U9contribution podle outputMode × direction, přefakturace přes objektyFR9, Deferred
U10záhlaví hodnotové tabulky podle zvoleného filtruChart Types → FR3