This is the most misunderstood part of the job, so let me start at the beginning.
A building burns 40,000 lira of gas over a winter. That figure is certain: there is an invoice. Now it has to be split across 60 flats.
“Everyone pays for their own meter,” you say. You try it. It does not add up. Understanding why is understanding the whole system.
Why it does not add up
Three reasons, all physical:
- Heat crosses walls. Even with your radiators shut you take heat from the flats above, below and beside you. Your meter reads zero and your flat is 19 °C.
- Pipes heat things. The runs from the boiler to the flats lose heat through basements and shafts. That heat appears on nobody’s meter but has been paid for.
- Common areas warm up. Stairwells, the entrance hall, the shelter. Nobody’s flat, everybody’s cost.
So the sum of 60 meters does not equal the building’s real consumption. Meters do not measure total cost; they measure the flats’ proportion relative to each other.
fixed share is the portion from the building’s common cost, distributed by floor area. consumption share follows the ratio between meters. The split is set by regulation; in buildings using heat cost allocators a typical distribution is 30% fixed / 70% consumption.
A heat cost allocator has no unit
The most counter-intuitive thing here: the small box on the radiator does not measure energy.
It measures the temperature difference between the radiator surface and the room, integrated over time. The result has no unit. A flat reading 1,240 means nothing on its own.
It only acquires meaning inside its own building:
flat A : 1,240 units
flat B : 890 units
flat C : 2,010 units
…
total : 74,500 units
flat A's consumption share = 1,240 / 74,500 = 1.66%
Comparing one building’s number with another’s is therefore meaningless. This is the thing I most often have to explain to residents — and the reason the portal has a “building average” box at all.
The engine’s two phases
It cannot be done in one pass, because knowing a flat’s share requires knowing the building’s total first. So the engine is two-phase:
PHASE 1 — collect
for each unit:
reading delta = last_reading − first_reading
apply corrections (meter swap, fault, estimation)
derive the building total
PHASE 2 — distribute
for each unit:
consumption share = (unit units / building total) × distributable amount
fixed share = (unit area / building area) × fixed amount
apply exemptions, minimum shares and common-area rules
write the bill lines
Then real life
That is the clean version. On site, this lands on top of it:
| situation | what happens |
|---|---|
| Meter replaced mid-period | Both meters’ readings are joined; the chart must show no break |
| Meter faulty | Estimated from the unit’s own history or the building average |
| Flat empty | Not zero: a minimum share applies (it takes heat from neighbours) |
| Exemption | A unit exempt from a line is removed from that line’s distribution base |
| Several boilers | Each boiler feeds its own group of blocks; the calculation is built per boiler |
| Common areas | Modelled as their own “unit”, then redistributed across all units |
None of these is an if in the code — they all live in the database as formula definitions. A site’s bill type describes which lines are distributed over which base. When a new rule arrives, the definition changes, not the code.
The hardest part: back-adjustment
A period closes, bills go out, and then an error surfaces — a meter was misread, a flat was linked to the wrong block.
You cannot recalculate a closed period and reissue its bills; those were paid and booked. What you do instead is a back-adjustment: carry the difference into the next period’s bill as its own line.
The adjustment record is anchored to the source period, not the target. The difference is based on that period’s figures. Confuse the two and, when a third correction arrives, you can never again work out which difference was layered on what. This field is marked “do not touch” in the system.
What I learned
Understanding this engine taught me something unexpected about software architecture: separating rule from data is the only way a regulated system survives.
The allocation rules changed several times over five years. The code was never rewritten once — because what changed was never how it is calculated, only at what ratio it is distributed. One is code; the other is a row.