@golemio/energetics 1.13.0 → 1.13.1-dev.2769672204

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/db/example/07_enapo_om_detail.sql +15 -10
  2. package/db/example/09_enapo_consumption_history.sql +196 -0
  3. package/db/migrations/postgresql/20260813090000-enapo-consumption-history.js +53 -0
  4. package/db/migrations/postgresql/sqls/20260813090000-enapo-consumption-history-down.sql +12 -0
  5. package/db/migrations/postgresql/sqls/20260813090000-enapo-consumption-history-up.sql +504 -0
  6. package/dist/integration-engine/enapo/ioc/Di.js +5 -1
  7. package/dist/integration-engine/enapo/ioc/Di.js.map +1 -1
  8. package/dist/integration-engine/enapo/ioc/EnapoWorkerContainerToken.d.ts +2 -0
  9. package/dist/integration-engine/enapo/ioc/EnapoWorkerContainerToken.js +2 -0
  10. package/dist/integration-engine/enapo/ioc/EnapoWorkerContainerToken.js.map +1 -1
  11. package/dist/integration-engine/enapo/repositories/AbstractMaterializedViewIndexRepository.d.ts +8 -0
  12. package/dist/integration-engine/enapo/repositories/AbstractMaterializedViewIndexRepository.js +25 -0
  13. package/dist/integration-engine/enapo/repositories/AbstractMaterializedViewIndexRepository.js.map +1 -0
  14. package/dist/integration-engine/enapo/repositories/DedSearchIndexRepository.d.ts +2 -3
  15. package/dist/integration-engine/enapo/repositories/DedSearchIndexRepository.js +3 -23
  16. package/dist/integration-engine/enapo/repositories/DedSearchIndexRepository.js.map +1 -1
  17. package/dist/integration-engine/enapo/repositories/EnapoConsumptionHistoryIndexRepository.d.ts +6 -0
  18. package/dist/integration-engine/enapo/repositories/EnapoConsumptionHistoryIndexRepository.js +30 -0
  19. package/dist/integration-engine/enapo/repositories/EnapoConsumptionHistoryIndexRepository.js.map +1 -0
  20. package/dist/integration-engine/enapo/repositories/interfaces/IEnapoConsumptionHistoryIndexRepository.d.ts +7 -0
  21. package/dist/integration-engine/enapo/repositories/interfaces/IEnapoConsumptionHistoryIndexRepository.js +3 -0
  22. package/dist/integration-engine/enapo/repositories/interfaces/IEnapoConsumptionHistoryIndexRepository.js.map +1 -0
  23. package/dist/integration-engine/enapo/workers/EnapoWorker.js +1 -0
  24. package/dist/integration-engine/enapo/workers/EnapoWorker.js.map +1 -1
  25. package/dist/integration-engine/enapo/workers/task/EnapoConsumptionHistoryRefreshTask.d.ts +10 -0
  26. package/dist/integration-engine/enapo/workers/task/EnapoConsumptionHistoryRefreshTask.js +47 -0
  27. package/dist/integration-engine/enapo/workers/task/EnapoConsumptionHistoryRefreshTask.js.map +1 -0
  28. package/dist/output-gateway/constants/ConsumptionHistory.d.ts +2 -0
  29. package/dist/output-gateway/constants/ConsumptionHistory.js +6 -0
  30. package/dist/output-gateway/constants/ConsumptionHistory.js.map +1 -0
  31. package/dist/output-gateway/controllers/v2/EnoBuildingsController.js +3 -3
  32. package/dist/output-gateway/controllers/v2/EnoBuildingsController.js.map +1 -1
  33. package/dist/output-gateway/helpers/ConsumptionHistoryWindow.d.ts +12 -0
  34. package/dist/output-gateway/helpers/ConsumptionHistoryWindow.js +30 -0
  35. package/dist/output-gateway/helpers/ConsumptionHistoryWindow.js.map +1 -0
  36. package/dist/output-gateway/models/EnapoOmConsumptionViewModel.d.ts +23 -0
  37. package/dist/output-gateway/models/EnapoOmConsumptionViewModel.js +48 -0
  38. package/dist/output-gateway/models/EnapoOmConsumptionViewModel.js.map +1 -0
  39. package/dist/output-gateway/models/interfaces/IEnapoOmConsumptionView.d.ts +20 -0
  40. package/dist/output-gateway/models/interfaces/IEnapoOmConsumptionView.js +3 -0
  41. package/dist/output-gateway/models/interfaces/IEnapoOmConsumptionView.js.map +1 -0
  42. package/dist/output-gateway/repositories/EnapoOmConsumptionRepository.d.ts +8 -0
  43. package/dist/output-gateway/repositories/EnapoOmConsumptionRepository.js +59 -0
  44. package/dist/output-gateway/repositories/EnapoOmConsumptionRepository.js.map +1 -0
  45. package/dist/output-gateway/repositories/EnoBuildingDetailRepository.d.ts +2 -1
  46. package/dist/output-gateway/repositories/EnoBuildingDetailRepository.js +13 -3
  47. package/dist/output-gateway/repositories/EnoBuildingDetailRepository.js.map +1 -1
  48. package/dist/output-gateway/repositories/EnoConsumptionPointsRepository.d.ts +2 -0
  49. package/dist/output-gateway/repositories/EnoConsumptionPointsRepository.js +34 -0
  50. package/dist/output-gateway/repositories/EnoConsumptionPointsRepository.js.map +1 -1
  51. package/dist/output-gateway/repositories/interfaces/IEnoBuildingDetailAggregate.d.ts +6 -1
  52. package/dist/output-gateway/repositories/interfaces/IEnoSharedPointRow.d.ts +5 -0
  53. package/dist/output-gateway/repositories/interfaces/IEnoSharedPointRow.js +3 -0
  54. package/dist/output-gateway/repositories/interfaces/IEnoSharedPointRow.js.map +1 -0
  55. package/dist/output-gateway/routers/interfaces/IEnoBuildingDetailResponse.d.ts +43 -18
  56. package/dist/output-gateway/routers/v2/V2EnoBuildingsRouter.js +2 -1
  57. package/dist/output-gateway/routers/v2/V2EnoBuildingsRouter.js.map +1 -1
  58. package/dist/output-gateway/transformations/EnoBuildingDetailTransformation.d.ts +6 -0
  59. package/dist/output-gateway/transformations/EnoBuildingDetailTransformation.js +53 -14
  60. package/dist/output-gateway/transformations/EnoBuildingDetailTransformation.js.map +1 -1
  61. package/dist/output-gateway/transformations/helpers/ConsumptionHistoryBuilder.d.ts +16 -0
  62. package/dist/output-gateway/transformations/helpers/ConsumptionHistoryBuilder.js +133 -0
  63. package/dist/output-gateway/transformations/helpers/ConsumptionHistoryBuilder.js.map +1 -0
  64. package/dist/output-gateway/transformations/helpers/PorsennaCoverage.d.ts +5 -0
  65. package/dist/output-gateway/transformations/helpers/PorsennaCoverage.js +40 -0
  66. package/dist/output-gateway/transformations/helpers/PorsennaCoverage.js.map +1 -0
  67. package/docs/implementation_documentation.md +67 -1
  68. package/docs/openapi-output.yaml +663 -43
  69. package/package.json +1 -1
@@ -166,6 +166,25 @@ paths:
166
166
  address, or consumption point, the response is 200 with the missing parts as `null` or
167
167
  empty arrays (e.g. `building: null` when only consumption points or addresses are known).
168
168
  404 is returned only when no source holds any data for the GID.
169
+
170
+ `consumption_history` carries monthly and yearly consumption per commodity, at building
171
+ level and again per consumption point. Sources are returned **in parallel** rather than
172
+ merged into one canonical series: metered readings and invoiced amounts legitimately
173
+ disagree, and which one a client wants depends on what it shows. `is_metered`
174
+ distinguishes them.
175
+
176
+ Coverage differs sharply by commodity and is a property of the data, not of the endpoint.
177
+ Gas has roughly three years of invoice history for about three quarters of its points.
178
+ Electricity is metered-only and starts in 2025, covering under a tenth of its points, so
179
+ most months come back `null`. Heat and water exist only as Porsenna yearly figures.
180
+ `measurement_data_from` and the per-series `data_from` say when data actually begins, so a
181
+ run of leading `null`s is explicable rather than looking like a fault.
182
+
183
+ Every aggregate carries its own provenance, so a client never has to present a summed
184
+ figure as if it were exact: `points_total`/`points_with_data` say how many consumption
185
+ points stand behind a period, `invoice_ids` names the billing documents an allocated
186
+ period came from, `coverage_count`/`expected_count` mark a partial period, and a point's
187
+ `shared_with` names the other buildings its meters also serve.
169
188
  parameters:
170
189
  - in: path
171
190
  name: gid
@@ -173,6 +192,19 @@ paths:
173
192
  schema:
174
193
  type: string
175
194
  example: GID100
195
+ - in: query
196
+ name: months
197
+ required: false
198
+ description: >-
199
+ Months of consumption history to return, counted back from the last **complete**
200
+ month. The current, partial month is always excluded: it is mid-accumulation and on a
201
+ trend chart reads as a collapse rather than as incomplete data. The maximum matches
202
+ the window the underlying index is built over.
203
+ schema:
204
+ type: integer
205
+ minimum: 1
206
+ maximum: 48
207
+ default: 36
176
208
  responses:
177
209
  "200":
178
210
  description: OK
@@ -603,7 +635,6 @@ components:
603
635
  registration_unit:
604
636
  allOf:
605
637
  - $ref: "#/components/schemas/EnergeticsEnoRegistrationUnit"
606
- nullable: true
607
638
  description: >-
608
639
  Evidenční jednotka (eno_ciselnik_evidencni_jednotka) resolved through eno_majetek;
609
640
  null when the building has no majetek record. Its name is part of the search vector.
@@ -714,9 +745,13 @@ components:
714
745
 
715
746
  EnergeticsEnoRegistrationUnit:
716
747
  type: object
748
+ # nullable lives here, next to `type`: OpenAPI 3.0 ignores a `nullable` that sits beside
749
+ # an allOf reference, and portman turns that shape into an invalid JSON Schema.
750
+ nullable: true
717
751
  description: >-
718
752
  Evidenční jednotka (eno_ciselnik_evidencni_jednotka) resolved through the building's
719
753
  eno_majetek record. The same shape is returned by the search and the detail endpoint.
754
+ Null when the building has no majetek record.
720
755
  properties:
721
756
  id:
722
757
  type: number
@@ -746,17 +781,37 @@ components:
746
781
  gid:
747
782
  type: string
748
783
  example: GID100
784
+ name:
785
+ type: string
786
+ nullable: true
787
+ description: >-
788
+ Display name of the building: `building.nazev`, then the Porsenna building name,
789
+ then null. `building` is null for a substantial share of GIDs and the card still
790
+ has to render a header, so the fallback is resolved here — the same chain the
791
+ search endpoint uses — rather than in every client, where the versions would
792
+ disagree.
793
+ example: ZŠ Testovací
794
+ address:
795
+ type: string
796
+ nullable: true
797
+ description: >-
798
+ Main address of the building: the first described `addresses[]` entry (they are
799
+ ordered main-address first), then the Porsenna address, then null.
800
+ example: Testovací 123/4, 100 00 Praha 10
749
801
  building:
750
802
  type: object
751
803
  nullable: true
752
804
  description: >-
753
805
  All available eno_budova attributes (Czech identifiers mirror the upstream ENO source).
754
806
  Null when the GID has no ENO building record and is known only through the
755
- consumption-point mapping.
807
+ consumption-point mapping. Every property below is always present when the object
808
+ is; `additionalProperties` stays open so an upstream ENO addition is not breaking.
756
809
  properties:
757
810
  zdroj:
758
811
  type: string
759
812
  nullable: true
813
+ description: ENO source system the row was read from (mhmp / stat).
814
+ example: mhmp
760
815
  nazev:
761
816
  type: string
762
817
  nullable: true
@@ -766,6 +821,8 @@ components:
766
821
  druh_vytapeni:
767
822
  type: string
768
823
  nullable: true
824
+ description: Heating type as recorded in ENO.
825
+ example: ústřední dálkové
769
826
  pripojka_elektro:
770
827
  type: string
771
828
  nullable: true
@@ -775,6 +832,71 @@ components:
775
832
  pripojka_kanalizace:
776
833
  type: string
777
834
  nullable: true
835
+ budova_rozdelena_byt_nebyt:
836
+ type: string
837
+ nullable: true
838
+ celkova_plocha_budova:
839
+ type: number
840
+ nullable: true
841
+ description: Total floor area, m².
842
+ celkova_plocha_byt_budova:
843
+ type: number
844
+ nullable: true
845
+ celkova_plocha_nebyt_budova:
846
+ type: number
847
+ nullable: true
848
+ zastavena_plocha:
849
+ type: number
850
+ nullable: true
851
+ description: Built-up area, m².
852
+ obestaveny_prostor:
853
+ type: number
854
+ nullable: true
855
+ description: Enclosed volume, m³.
856
+ pocet_byt:
857
+ type: number
858
+ nullable: true
859
+ pocet_nebyt_budova:
860
+ type: number
861
+ nullable: true
862
+ pocet_nadzem_podlazi:
863
+ type: number
864
+ nullable: true
865
+ pocet_podzem_podlazi:
866
+ type: number
867
+ nullable: true
868
+ pocet_podkrovi:
869
+ type: number
870
+ nullable: true
871
+ vytah:
872
+ type: boolean
873
+ nullable: true
874
+ czcc:
875
+ type: number
876
+ nullable: true
877
+ id_cuzk:
878
+ type: number
879
+ nullable: true
880
+ id_objekt:
881
+ type: number
882
+ nullable: true
883
+ kod_vyuziti:
884
+ type: string
885
+ nullable: true
886
+ nazev_vyuziti:
887
+ type: string
888
+ nullable: true
889
+ example: budova občanské vybavenosti
890
+ kod_ochrana:
891
+ type: string
892
+ nullable: true
893
+ nazev_ochrana:
894
+ type: string
895
+ nullable: true
896
+ platnost_od:
897
+ type: string
898
+ format: date-time
899
+ nullable: true
778
900
  additionalProperties: true
779
901
  property:
780
902
  type: object
@@ -786,7 +908,6 @@ components:
786
908
  registration_unit:
787
909
  allOf:
788
910
  - $ref: "#/components/schemas/EnergeticsEnoRegistrationUnit"
789
- nullable: true
790
911
  description: Evidenční jednotka (eno_ciselnik_evidencni_jednotka).
791
912
  managers:
792
913
  type: object
@@ -852,7 +973,7 @@ components:
852
973
  properties:
853
974
  source:
854
975
  type: string
855
- example: porsenna_p7
976
+ example: prague-7
856
977
  building_name:
857
978
  type: string
858
979
  nullable: true
@@ -863,13 +984,180 @@ components:
863
984
  type: number
864
985
  devices_active:
865
986
  type: number
987
+ data_to:
988
+ type: string
989
+ format: date
990
+ nullable: true
991
+ description: >-
992
+ Most recent date any of this building's Porsenna devices reported; null when
993
+ none of them has consumption. Porsenna aggregates carry a covered-day count
994
+ rather than a reading timestamp, so this is the period start plus its covered
995
+ days, clamped to the end of the period. A meter count without an as-of date is
996
+ a claim with no expiry.
997
+ example: 2026-08-12
998
+ consumption_history:
999
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionHistory"
866
1000
  consumption_points:
867
1001
  type: array
868
1002
  items:
869
1003
  $ref: "#/components/schemas/EnergeticsEnoConsumptionPoint"
1004
+ EnergeticsEnoConsumptionEntry:
1005
+ type: object
1006
+ description: >-
1007
+ One period of one series. `value: null` means the source has no data for that period -
1008
+ missing periods are always null, never an error and never a zero.
1009
+ properties:
1010
+ period:
1011
+ type: string
1012
+ description: "`YYYY-MM` for monthly entries, `YYYY` for yearly ones."
1013
+ example: 2026-07
1014
+ value:
1015
+ type: number
1016
+ nullable: true
1017
+ value_kwh:
1018
+ type: number
1019
+ nullable: true
1020
+ description: >-
1021
+ Comparable energy. For gas this is derived (normometers_nm3 * combustion_heat) and is
1022
+ null whenever the conversion inputs are missing; `value` + `unit` stay authoritative.
1023
+ A yearly entry withholds it unless every contributing month has one, so it can never
1024
+ imply a conversion rate that did not exist.
1025
+ coverage_count:
1026
+ type: number
1027
+ description: Days of the period carrying data.
1028
+ expected_count:
1029
+ type: number
1030
+ description: Days in the period, so a partial period is recognisable as one.
1031
+ is_estimated:
1032
+ type: boolean
1033
+ description: >-
1034
+ True when the value was allocated pro rata from a billing period spanning more than
1035
+ one month, rather than read for the period itself.
1036
+ points_total:
1037
+ type: number
1038
+ description: >-
1039
+ Consumption points that contributed this period at all. `0` for a period no point
1040
+ reported — never null, so a gap is a number a client can compare.
1041
+ points_with_data:
1042
+ type: number
1043
+ description: >-
1044
+ Of those, how many carried a value. A shortfall against the series' `points` is how a
1045
+ client learns a building summary is missing an OM for this period rather than being
1046
+ genuinely lower — at point grain the counts are 1/1 or 0/0.
1047
+ invoice_ids:
1048
+ type: array
1049
+ description: >-
1050
+ Billing documents this period was allocated from, sorted. Empty for metered readings,
1051
+ for Porsenna and for periods with no value. An allocated month is a number the
1052
+ platform computed rather than one anyone read, so naming the invoice is what makes it
1053
+ auditable. At building level a period can be allocated from several invoices (one per
1054
+ point), and a yearly entry usually is; at point grain a month is normally one.
1055
+ items:
1056
+ type: string
1057
+ example: ["DI-2026-001"]
1058
+ EnergeticsEnoConsumptionSeries:
1059
+ type: object
1060
+ description: One source's view of one commodity, for one unit.
1061
+ properties:
1062
+ source:
1063
+ type: string
1064
+ enum:
1065
+ - predi_input
1066
+ - ppas_ave_api
1067
+ - ppas_distribution_invoice
1068
+ - ppas_commercial_invoice
1069
+ - porsenna
1070
+ unit:
1071
+ type: string
1072
+ description: >-
1073
+ Electricity is kWh. Metered gas is m3 (from the PPAS operating difference) or Nm3
1074
+ where only the converted difference exists - a point whose unit changes mid-window
1075
+ appears as two series with complementary gaps, which is why unit is part of the key.
1076
+ Porsenna carries its own lowercase unit.
1077
+ example: m3
1078
+ is_metered:
1079
+ type: boolean
1080
+ description: False for invoiced (billed and allocated) figures, true for meter readings.
1081
+ points:
1082
+ type: array
1083
+ description: Consumption points contributing to this series.
1084
+ items:
1085
+ type: string
1086
+ data_from:
1087
+ type: string
1088
+ nullable: true
1089
+ description: Earliest period this series has any value for; null when it has none.
1090
+ monthly:
1091
+ type: array
1092
+ description: >-
1093
+ `months` entries, chronological, null-filled where the source has nothing. Empty
1094
+ when the source reports only yearly aggregates (Porsenna) - its data is in
1095
+ `yearly`.
1096
+ items:
1097
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionEntry"
1098
+ yearly:
1099
+ type: array
1100
+ description: >-
1101
+ One entry per calendar year the window touches, plus any earlier year that has data -
1102
+ Porsenna reaches back further than any monthly source and is not truncated to the
1103
+ months window.
1104
+ items:
1105
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionEntry"
1106
+ EnergeticsEnoConsumptionHistory:
1107
+ type: object
1108
+ description: >-
1109
+ Consumption per commodity per source. An empty array for a commodity means no source knows
1110
+ this building at all, which is distinct from a series of nulls - that means the source knows
1111
+ the point but not those periods.
1112
+ properties:
1113
+ period_from:
1114
+ type: string
1115
+ example: 2023-08
1116
+ period_to:
1117
+ type: string
1118
+ description: Always the last complete month.
1119
+ example: 2026-07
1120
+ months:
1121
+ type: number
1122
+ example: 36
1123
+ measurement_data_from:
1124
+ type: string
1125
+ nullable: true
1126
+ description: >-
1127
+ Earliest month (`YYYY-MM`) with data from a metered source; null when no metered
1128
+ source has monthly data. Explains the leading nulls - meter data starts in 2025
1129
+ platform-wide. A yearly-only series does not count; its reach-back stays visible
1130
+ in the series' own `data_from`.
1131
+ example: 2025-01
1132
+ has_shared_points:
1133
+ type: boolean
1134
+ description: >-
1135
+ True when a contributing consumption point is also mapped to another building. The
1136
+ totals still count such a point in full - that is what its meters measured - so this
1137
+ flag is how a client learns the figure is not exclusive to this building. It is true
1138
+ for both sides of a shared point, primary or not; the points' `shared_with` says
1139
+ which buildings those are.
1140
+ electricity:
1141
+ type: array
1142
+ items:
1143
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
1144
+ gas:
1145
+ type: array
1146
+ items:
1147
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
1148
+ heat:
1149
+ type: array
1150
+ items:
1151
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
1152
+ water:
1153
+ type: array
1154
+ items:
1155
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
870
1156
  EnergeticsEnoPorsennaBlock:
871
1157
  type: object
872
- description: Porsenna (e-manazer) device detail with sub-meters and yearly consumption aggregates
1158
+ description: >-
1159
+ Porsenna (e-manazer) device detail with sub-meters. The device's consumption is in the
1160
+ point's `consumption_history` as the `porsenna` series.
873
1161
  properties:
874
1162
  device:
875
1163
  type: object
@@ -879,22 +1167,310 @@ components:
879
1167
  items:
880
1168
  type: object
881
1169
  additionalProperties: true
882
- yearly_consumption:
883
- type: array
884
- items:
885
- type: object
886
- properties:
887
- period:
888
- type: string
889
- example: "2024"
890
- value:
891
- type: number
892
- unit:
893
- type: string
894
- example: GJ
895
- days_covered:
896
- type: number
897
- nullable: true
1170
+ EnergeticsEnoElectricityMetadata:
1171
+ type: object
1172
+ # nullable lives here, next to `type`: OpenAPI 3.0 ignores a `nullable` that sits beside
1173
+ # an allOf reference, and portman turns that shape into an invalid JSON Schema.
1174
+ nullable: true
1175
+ description: >-
1176
+ Latest available month of PRE metadata for the EAN, i.e. the technical parameters of
1177
+ the delivery point as PRE last reported them. Null when PRE has never reported it.
1178
+ Every property is always present; `additionalProperties` stays open so an upstream PRE
1179
+ addition is not breaking.
1180
+ properties:
1181
+ year:
1182
+ type: number
1183
+ nullable: true
1184
+ description: Year and month of the metadata row these values come from.
1185
+ example: 2026
1186
+ month:
1187
+ type: number
1188
+ nullable: true
1189
+ example: 6
1190
+ month_name:
1191
+ type: string
1192
+ nullable: true
1193
+ example: červen
1194
+ days_in_stored_month:
1195
+ type: number
1196
+ nullable: true
1197
+ consumption_point:
1198
+ type: string
1199
+ nullable: true
1200
+ example: OM Testovací 123
1201
+ address:
1202
+ type: string
1203
+ nullable: true
1204
+ location_type:
1205
+ type: string
1206
+ nullable: true
1207
+ example: škola
1208
+ company_name:
1209
+ type: string
1210
+ nullable: true
1211
+ company_id:
1212
+ type: string
1213
+ nullable: true
1214
+ tarif_type:
1215
+ type: string
1216
+ nullable: true
1217
+ example: C02d
1218
+ tarif_1t2t:
1219
+ type: string
1220
+ nullable: true
1221
+ example: 1T
1222
+ phases:
1223
+ type: string
1224
+ nullable: true
1225
+ example: "3"
1226
+ circuit_breaker:
1227
+ type: string
1228
+ nullable: true
1229
+ example: 3x125A
1230
+ type_b_meter:
1231
+ type: string
1232
+ nullable: true
1233
+ meter_replaced:
1234
+ type: string
1235
+ nullable: true
1236
+ description: >-
1237
+ Meter number, or a pipe-separated `date|old|new` triple for the month the meter was
1238
+ replaced. Free text as PRE supplies it, not a parsed structure.
1239
+ example: 2026-06-19|53435572|54292510
1240
+ additionalProperties: true
1241
+ EnergeticsEnoGasInvoice:
1242
+ type: object
1243
+ description: >-
1244
+ PPAS invoice header. The distribution and commercial invoices carry the same properties;
1245
+ only their sources differ.
1246
+ properties:
1247
+ id:
1248
+ type: string
1249
+ example: DI-2026-001
1250
+ preceding_id:
1251
+ type: string
1252
+ nullable: true
1253
+ description: The invoice this one supersedes, when the two are linked upstream.
1254
+ customer_id:
1255
+ type: string
1256
+ nullable: true
1257
+ customer_company_id:
1258
+ type: string
1259
+ nullable: true
1260
+ customer_name:
1261
+ type: string
1262
+ nullable: true
1263
+ customer_contract_account_id:
1264
+ type: string
1265
+ nullable: true
1266
+ customer_address_street:
1267
+ type: string
1268
+ nullable: true
1269
+ customer_address_house_number:
1270
+ type: string
1271
+ nullable: true
1272
+ customer_address_house_org_number:
1273
+ type: string
1274
+ nullable: true
1275
+ customer_address_city:
1276
+ type: string
1277
+ nullable: true
1278
+ customer_address_city_part:
1279
+ type: string
1280
+ nullable: true
1281
+ customer_address_post_code:
1282
+ type: string
1283
+ nullable: true
1284
+ customer_address_country:
1285
+ type: string
1286
+ nullable: true
1287
+ customer_address_ruian_id:
1288
+ type: number
1289
+ nullable: true
1290
+ facts_doc_date:
1291
+ type: string
1292
+ format: date
1293
+ nullable: true
1294
+ facts_net_date:
1295
+ type: string
1296
+ format: date
1297
+ nullable: true
1298
+ facts_billing_transaction:
1299
+ type: string
1300
+ nullable: true
1301
+ example: FAKTURACE
1302
+ facts_to_pay_amount:
1303
+ type: number
1304
+ nullable: true
1305
+ facts_currency:
1306
+ type: string
1307
+ nullable: true
1308
+ example: CZK
1309
+ facts_price_brutto:
1310
+ type: number
1311
+ nullable: true
1312
+ is_canceled:
1313
+ type: boolean
1314
+ nullable: true
1315
+ description: Always false here — canceled invoices are not served.
1316
+ canceled_reason:
1317
+ type: string
1318
+ nullable: true
1319
+ additionalProperties: true
1320
+ EnergeticsEnoGasInstallation:
1321
+ type: object
1322
+ description: >-
1323
+ The installation (odběrné místo) the invoice bills, as recorded on it. The PPAS-internal
1324
+ place id is deliberately not exposed; it only scopes `devices` and `prices` server-side.
1325
+ properties:
1326
+ billing_class:
1327
+ type: string
1328
+ nullable: true
1329
+ example: MO
1330
+ measurement_type:
1331
+ type: string
1332
+ nullable: true
1333
+ contract_contract_id:
1334
+ type: string
1335
+ nullable: true
1336
+ contract_move_in_date:
1337
+ type: string
1338
+ format: date
1339
+ nullable: true
1340
+ contract_move_out_date:
1341
+ type: string
1342
+ format: date
1343
+ nullable: true
1344
+ tdd_class:
1345
+ type: string
1346
+ nullable: true
1347
+ description: Type-day-diagram class used for the allocation of unmetered consumption.
1348
+ example: DOM4
1349
+ address_street:
1350
+ type: string
1351
+ nullable: true
1352
+ address_house_number:
1353
+ type: string
1354
+ nullable: true
1355
+ address_house_org_number:
1356
+ type: string
1357
+ nullable: true
1358
+ address_city:
1359
+ type: string
1360
+ nullable: true
1361
+ address_city_part:
1362
+ type: string
1363
+ nullable: true
1364
+ address_post_code:
1365
+ type: string
1366
+ nullable: true
1367
+ address_country:
1368
+ type: string
1369
+ nullable: true
1370
+ address_ruian_id:
1371
+ type: number
1372
+ nullable: true
1373
+ additionalProperties: true
1374
+ EnergeticsEnoGasInvoiceDevice:
1375
+ type: object
1376
+ description: >-
1377
+ One billed meter period of the invoice. `kind` is a SAP code and the same gas can appear
1378
+ under several of them in different dimensions, so these rows are the billing detail, not a
1379
+ series to sum — the summed and month-allocated figures are in `consumption_history`.
1380
+ properties:
1381
+ device_serial_number:
1382
+ type: string
1383
+ example: PLYN-001
1384
+ date_from:
1385
+ type: string
1386
+ format: date
1387
+ date_to:
1388
+ type: string
1389
+ format: date
1390
+ reading_type:
1391
+ type: string
1392
+ description: STANDARD or CORRECTION.
1393
+ example: STANDARD
1394
+ type:
1395
+ type: string
1396
+ description: Meter type.
1397
+ example: G4
1398
+ kind:
1399
+ type: string
1400
+ description: SAP code of the billed quantity (ZIZWC, ZIABN3, …).
1401
+ example: ZIZWC
1402
+ reading_from:
1403
+ type: number
1404
+ nullable: true
1405
+ reading_to:
1406
+ type: number
1407
+ nullable: true
1408
+ consumption:
1409
+ type: number
1410
+ nullable: true
1411
+ description: Denominated by `unit`, which is not always m³.
1412
+ unit:
1413
+ type: string
1414
+ nullable: true
1415
+ example: M3
1416
+ meter_reading_type:
1417
+ type: string
1418
+ nullable: true
1419
+ example: dálkový
1420
+ gas_consumption_kwh:
1421
+ type: number
1422
+ nullable: true
1423
+ description: 0 means "not supplied" rather than zero.
1424
+ volume_coefficient:
1425
+ type: number
1426
+ nullable: true
1427
+ combustion_heat:
1428
+ type: number
1429
+ nullable: true
1430
+ description: kWh/m³; 0 means "not supplied" rather than zero.
1431
+ normometers_nm3:
1432
+ type: number
1433
+ nullable: true
1434
+ additionalProperties: true
1435
+ EnergeticsEnoGasInvoicePrice:
1436
+ type: object
1437
+ description: One priced line of the invoice for this installation.
1438
+ properties:
1439
+ date_from:
1440
+ type: string
1441
+ format: date
1442
+ date_to:
1443
+ type: string
1444
+ format: date
1445
+ kind:
1446
+ type: string
1447
+ example: distribuce
1448
+ description:
1449
+ type: string
1450
+ example: Distribuce plynu
1451
+ price_group:
1452
+ type: string
1453
+ quantity:
1454
+ type: number
1455
+ unit:
1456
+ type: string
1457
+ example: kWh
1458
+ unit_price:
1459
+ type: number
1460
+ time_slot:
1461
+ type: number
1462
+ nullable: true
1463
+ price_netto:
1464
+ type: number
1465
+ price_type:
1466
+ type: string
1467
+ example: variabilní
1468
+ currency:
1469
+ type: string
1470
+ example: CZK
1471
+ tax_rate:
1472
+ type: number
1473
+ additionalProperties: true
898
1474
  EnergeticsEnoConsumptionPoint:
899
1475
  type: object
900
1476
  properties:
@@ -909,6 +1485,31 @@ components:
909
1485
  enum: [electricity, gas, heat, water]
910
1486
  is_first_gid:
911
1487
  type: boolean
1488
+ description: >-
1489
+ Whether this building is the primary holder of the point in the mapping. It says
1490
+ nothing about the other side — see `shared_with`, which lists the buildings the point
1491
+ is shared with whether this one is primary or not.
1492
+ shared_with:
1493
+ type: array
1494
+ description: >-
1495
+ Other buildings the same consumption point is mapped to. Empty when the point belongs
1496
+ to this building alone, which is the common case. The totals still count the point in
1497
+ full — that is what its meters measured — so this is how a client learns the figure is
1498
+ not exclusive to this building, and which building it is shared with.
1499
+ items:
1500
+ type: object
1501
+ properties:
1502
+ gid:
1503
+ type: string
1504
+ example: GID200
1505
+ name:
1506
+ type: string
1507
+ nullable: true
1508
+ description: Resolved as the detail's top-level `name` is; null when neither source names it.
1509
+ example: MŠ Vedlejší
1510
+ required:
1511
+ - gid
1512
+ - name
912
1513
  is_active:
913
1514
  type: boolean
914
1515
  description: Derived from valid_to (null or in the future means the mapping is active)
@@ -932,72 +1533,91 @@ components:
932
1533
  description: Electricity-specific data (present only for commodity = electricity, omitted otherwise)
933
1534
  properties:
934
1535
  metadata:
935
- type: object
936
- nullable: true
937
- description: Latest available month of PRE metadata for the EAN
938
- additionalProperties: true
1536
+ allOf:
1537
+ - $ref: "#/components/schemas/EnergeticsEnoElectricityMetadata"
1538
+ description: >-
1539
+ Latest available month of PRE metadata for the EAN; null when PRE has never
1540
+ reported it.
939
1541
  porsenna:
940
1542
  $ref: "#/components/schemas/EnergeticsEnoPorsennaBlock"
1543
+ consumption_history:
1544
+ type: array
1545
+ description: Per-source series for this point; empty when no source knows it.
1546
+ items:
1547
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
941
1548
  gas:
942
1549
  type: object
943
1550
  description: Gas-specific data (present only for commodity = gas, omitted otherwise)
944
1551
  properties:
945
1552
  porsenna:
946
1553
  $ref: "#/components/schemas/EnergeticsEnoPorsennaBlock"
1554
+ consumption_history:
1555
+ type: array
1556
+ description: Per-source series for this point; empty when no source knows it.
1557
+ items:
1558
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
947
1559
  distribution:
948
1560
  type: object
949
1561
  nullable: true
950
- description: Latest non-canceled PPAS distribution invoice with its installation, devices and prices
1562
+ description: >-
1563
+ Latest non-canceled PPAS distribution invoice for this EIC with its
1564
+ installation, billed meter periods and priced lines. Devices and prices are
1565
+ scoped to this point's installation, so another installation on the same
1566
+ invoice does not leak in. Null when no distribution invoice knows the point.
951
1567
  properties:
952
1568
  invoice:
953
- type: object
954
- additionalProperties: true
1569
+ $ref: "#/components/schemas/EnergeticsEnoGasInvoice"
955
1570
  installation:
956
- type: object
957
- additionalProperties: true
1571
+ $ref: "#/components/schemas/EnergeticsEnoGasInstallation"
958
1572
  devices:
959
1573
  type: array
960
1574
  items:
961
- type: object
962
- additionalProperties: true
1575
+ $ref: "#/components/schemas/EnergeticsEnoGasInvoiceDevice"
963
1576
  prices:
964
1577
  type: array
965
1578
  items:
966
- type: object
967
- additionalProperties: true
1579
+ $ref: "#/components/schemas/EnergeticsEnoGasInvoicePrice"
968
1580
  commercial:
969
1581
  type: object
970
1582
  nullable: true
971
- description: Latest non-canceled PPAS commercial invoice with its installation, devices and prices
1583
+ description: >-
1584
+ Latest non-canceled PPAS commercial invoice for this EIC, in the same shape as
1585
+ `distribution`. Null when no commercial invoice knows the point.
972
1586
  properties:
973
1587
  invoice:
974
- type: object
975
- additionalProperties: true
1588
+ $ref: "#/components/schemas/EnergeticsEnoGasInvoice"
976
1589
  installation:
977
- type: object
978
- additionalProperties: true
1590
+ $ref: "#/components/schemas/EnergeticsEnoGasInstallation"
979
1591
  devices:
980
1592
  type: array
981
1593
  items:
982
- type: object
983
- additionalProperties: true
1594
+ $ref: "#/components/schemas/EnergeticsEnoGasInvoiceDevice"
984
1595
  prices:
985
1596
  type: array
986
1597
  items:
987
- type: object
988
- additionalProperties: true
1598
+ $ref: "#/components/schemas/EnergeticsEnoGasInvoicePrice"
989
1599
  heat:
990
1600
  type: object
991
1601
  description: Heat-specific data (present only for commodity = heat, omitted otherwise; Porsenna-sourced)
992
1602
  properties:
993
1603
  porsenna:
994
1604
  $ref: "#/components/schemas/EnergeticsEnoPorsennaBlock"
1605
+ consumption_history:
1606
+ type: array
1607
+ description: Per-source series for this point; empty when no source knows it.
1608
+ items:
1609
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
995
1610
  water:
996
1611
  type: object
997
1612
  description: Water-specific data (present only for commodity = water, omitted otherwise; Porsenna-sourced)
998
1613
  properties:
999
1614
  porsenna:
1000
1615
  $ref: "#/components/schemas/EnergeticsEnoPorsennaBlock"
1616
+ consumption_history:
1617
+ type: array
1618
+ description: Per-source series for this point; empty when no source knows it.
1619
+ items:
1620
+ $ref: "#/components/schemas/EnergeticsEnoConsumptionSeries"
1001
1621
  EnergeticsBuildingShort:
1002
1622
  type: object
1003
1623
  properties: