This page explains four parts of the fleet module that read across its ledgers. src/cluster_erp/modules/fleet/tco/ computes total cost of ownership (TCO), ranks assets for renewal, and keeps tires, campaigns, and emission factors. src/cluster_erp/modules/fleet/telematics.py ingests counter batches, src/cluster_erp/modules/fleet/analytics_facts.py publishes fleet facts to the analytics module, and src/cluster_erp/modules/fleet/validation.py holds shared input checks.
In short¶
- TCO sums what the ledgers recorded over a window of months and divides by the kilometres the odometer moved (
src/cluster_erp/modules/fleet/tco/services.py:1-8). - The renewal ranking lists active and in-maintenance assets by cost per kilometre, highest first (
src/cluster_erp/modules/fleet/tco/services.py:63-91). - The telematics feed turns device counters into ordinary reading documents with source
telematics(src/cluster_erp/modules/fleet/telematics.py:1-16). - Four fact sets go to analytics through the
DescribeAnalyticsFactscontract (src/cluster_erp/modules/fleet/analytics_facts.py:300-310,src/cluster_erp/modules/fleet/module.py:338-346).
How it works¶
Total cost of ownership¶
GET /tco/assets/{asset_id} and GET /tco/renewal take months between 1 and 120, default 12, and need fleet_asset.read (src/cluster_erp/modules/fleet/tco/routes.py:48-66). For each asset the service adds:
| Cost part | Rows counted | Source |
|---|---|---|
| Maintenance | total_cost of completed work orders completed in the window |
src/cluster_erp/modules/fleet/tco/services.py:113-124 |
| Fuel | total_cost of verified fuel entries filled in the window |
src/cluster_erp/modules/fleet/tco/services.py:125-136 |
| Penalties | amount of penalties not objected, occurred in the window |
src/cluster_erp/modules/fleet/tco/services.py:152-164 |
| Compliance | amount of completed compliance items completed in the window |
src/cluster_erp/modules/fleet/tco/services.py:165-177 |
Kilometres used are the difference between the first and the latest life-to-date value of the odometer meter in the window. Cost per kilometre is the total divided by that difference, quantized to two decimals; with no kilometres it is empty. CO2 is the fuel quantity per fuel type times the emission factor of that type (src/cluster_erp/modules/fleet/tco/services.py:179-226, src/cluster_erp/modules/fleet/tco/services.py:293-325, src/cluster_erp/modules/fleet/tco/services.py:327-360). The renewal board computes all aggregates in a fixed number of grouped queries, not one query set per asset (src/cluster_erp/modules/fleet/tco/services.py:94-98).
Tires, campaigns, and emission factors¶
- A tire (
FleetTire) is identified by serial number and isin_storage,in_use, orscrapped(src/cluster_erp/modules/fleet/models.py:125,src/cluster_erp/modules/fleet/models.py:705-737). Mount needs a stored tire, an asset, and a free position. Unmount adds the odometer distance since mounting toaccumulated_km. Scrap needs a stored tire (src/cluster_erp/modules/fleet/tco/services.py:439-521,src/cluster_erp/modules/fleet/tco/metadata.py:77-113). - A campaign (
FleetCampaign) lists assets that need the same work, for example a recall. A campaign item can open one work order or be marked done; a closed campaign takes no new assets (src/cluster_erp/modules/fleet/tco/services.py:617-760,src/cluster_erp/modules/fleet/tco/metadata.py:287-427). - An emission factor (
FleetEmissionFactor) gives kilograms of CO2 per unit of one fuel type, unique among live rows. The table starts empty and the organisation fills it; the code comment states that the module computes a report and never a certification (src/cluster_erp/modules/fleet/models.py:773-789).
Telematics feed¶
flowchart LR
A["POST /telematics/feed rows"] --> B{"reading_at has offset"}
B -->|no| R1["rejected"]
B -->|yes| C["asset by telematics_device_id"]
C -->|none| U["unmatched"]
C -->|found| D["odometer_km and engine_hours targets"]
D -->|none| R2["rejected"]
D --> E["FleetReadingService.create source telematics"]
E -->|all landed| OK["accepted"]
E -->|some landed| P["partial"]
E -->|none landed| R3["rejected"]In prose: POST /telematics/feed takes 1 to 1000 rows, each with a device id, a time, and optional odometer and engine hour values (src/cluster_erp/modules/fleet/telematics.py:40-57). A row without a UTC offset is rejected. The device id resolves to a live asset through telematics_device_id; a row without a match is reported as unmatched. Each counter value becomes a reading on the meter of the same kind, so the normal ledger rules apply. The row lands in exactly one bucket: accepted, partial, unmatched, or rejected, with reasons (src/cluster_erp/modules/fleet/telematics.py:58-148).
Analytics fact sets¶
| Fact set | Source table | Dimensions | Measures | Source |
|---|---|---|---|---|
fleet_fuel |
fleet_fuel_entries |
asset, fuel type, entry source, match status, consumption alert, non-UTTS risk, fill date | fill count, total litres, total fuel cost, average consumption (non-additive) | src/cluster_erp/modules/fleet/analytics_facts.py:35-118 |
fleet_work_order |
fleet_work_orders |
asset, status, priority, external flag, completion date | order count, total, labour, parts, and external cost | src/cluster_erp/modules/fleet/analytics_facts.py:120-195 |
fleet_penalty |
fleet_penalties |
asset, kind, status, driver, occurrence date | penalty count, total amount | src/cluster_erp/modules/fleet/analytics_facts.py:197-249 |
fleet_compliance |
fleet_compliance_items |
asset, kind, status, due date | item count, total amount | src/cluster_erp/modules/fleet/analytics_facts.py:251-299 |
Every fact set is read with fleet_asset.read and filters out rows with deleted_at set (src/cluster_erp/modules/fleet/analytics_facts.py:116-117). The date dimensions offer the grains day, week, month, quarter, and year (src/cluster_erp/modules/fleet/analytics_facts.py:24).
Shared validators¶
require_tz refuses a datetime without a UTC offset, and scrub_blank_strings turns blank strings into empty values before validation (src/cluster_erp/modules/fleet/validation.py:15-37).
Why it is built this way¶
- The docstring of
analytics_facts.pykeeps cost per kilometre and CO2 out of analytics, because a ratio of two aggregates is not an additive measure (src/cluster_erp/modules/fleet/analytics_facts.py:1-11). - The telematics docstring states that no raw GPS traces or positions are stored, because the retention question for that data is an open decision of the product owner (
src/cluster_erp/modules/fleet/telematics.py:11-15). validation.pystates that a naive datetime would crash the comparison with aware values, and that guessing a time zone for the caller is forbidden (src/cluster_erp/modules/fleet/validation.py:1-8).
Rules and boundaries¶
- The renewal ranking puts assets without a cost per kilometre at the end (
src/cluster_erp/modules/fleet/tco/services.py:85-90). - The telematics feed needs
fleet_asset.update(src/cluster_erp/modules/fleet/telematics.py:55). - A tire cannot be mounted at a position that already holds a mounted tire on the same asset (
src/cluster_erp/modules/fleet/tco/services.py:439-487).
Interaction with other parts¶
| Contract | Direction | Purpose | Source |
|---|---|---|---|
DescribeAnalyticsFacts |
fleet answers | returns the four fact sets | src/cluster_erp/modules/fleet/module.py:338-346 |
AnalyticsFactSet, AnalyticsDimension, AnalyticsMeasure |
fleet builds | the fact set definitions | src/cluster_erp/modules/fleet/analytics_facts.py:18-22 |
How analytics collects these answers is on analytics facts. The contract list is on the contracts reference.
Limits and open points¶
- TCO sums
total_cost,amount, and fuel cost columns without reading theircurrencycolumns (src/cluster_erp/modules/fleet/tco/services.py:113-177,src/cluster_erp/modules/fleet/models.py:409). The fleet fact sets have no currency dimension (src/cluster_erp/modules/fleet/analytics_facts.py:35-299). - Acquisition value is not part of TCO; the summed parts are maintenance, fuel, penalties, and compliance (
src/cluster_erp/modules/fleet/tco/services.py:293). - Readings created from inspections and reservations carry the source
form, one of the values inFLEET_READING_SOURCES(src/cluster_erp/modules/fleet/operations/services.py:494,src/cluster_erp/modules/fleet/models.py:61-62).
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| total cost of ownership | FleetTcoService |
The recorded costs of an asset over a window, also per kilometre and as CO2. | Toplam sahip olma maliyeti | src/cluster_erp/modules/fleet/tco/services.py:55 |
| renewal ranking | FleetTcoService.renewal |
Active assets ordered by cost per kilometre, highest first. | Yenileme sıralaması | src/cluster_erp/modules/fleet/tco/services.py:63 |
| emission factor | FleetEmissionFactor |
Kilograms of CO2 per unit of one fuel type, entered by the organisation. | Emisyon katsayısı | src/cluster_erp/modules/fleet/models.py:773 |
| telematics feed | ingest_feed |
The batch endpoint that turns device counters into readings. | Telematik beslemesi | src/cluster_erp/modules/fleet/telematics.py:52-53 |
Related pages¶
Verified against commit 92f8051 on 2026-11-13.