> ## Documentation Index
> Fetch the complete documentation index at: https://docs.a7manager.se/llms.txt
> Use this file to discover all available pages before exploring further.

# vehicle_readings

> Daily absolute odometer snapshots.

Daily absolute odometer snapshots. Delivered daily.

**Update key:** `(vehicle_number, reading_date)` — **not** `external_system_id`

## Columns

| Column               | Type        | Required | Notes                                         |
| -------------------- | ----------- | :------: | --------------------------------------------- |
| `external_system_id` | text(100)   |     ○    | Traceability only — *not* the update key here |
| `vehicle_number`     | text(100)   |     ●    | Must exist in project                         |
| `reading_date`       | date        |     ●    | One reading per vehicle per date              |
| `mileage_km`         | integer     |     ●    | Absolute odometer value, **not** a delta      |
| `operating_hours`    | integer     |     ○    | Absolute value                                |
| `last_updated`       | timestamptz |     ●    | Staleness key                                 |

## Rules

* The update key is the **vehicle and date**, not `external_system_id`. Re-sending the same
  vehicle and date overwrites the earlier value.
* **Values are absolute, not deltas.** Missing a day is harmless — the next delivery
  self-corrects.
* **The odometer must not go backwards.** `mileage_km` must be greater than or equal to the
  highest stored mileage for that vehicle on any *earlier* date. A lower value rejects the
  whole file.

<Note>
  The monotonic check compares against **stored** readings only, not against other rows in the
  same file. Two conflicting readings delivered together will both pass validation, and the
  one with the later `reading_date` becomes the vehicle's current mileage. There is also no
  upper-bound check, so an erroneously large value imports without complaint and will not be
  correctable by a later, smaller one.
</Note>

## Side effects

This is the file with the widest reach. Importing it:

1. Updates the vehicle's `current_mileage_km`, `current_operating_hours` and
   `last_reading_update`.
2. **Recomputes lifetime mileage for every component currently mounted on that vehicle**,
   as the component's mileage at mount plus the distance the vehicle has travelled since.

That second effect is why load order matters. If readings are imported before
`vehicle_configurations` in a batch where a component was swapped, the mileage is attributed
to the wrong component.

## Example

```csv theme={null}
external_system_id,vehicle_number,reading_date,mileage_km,operating_hours,last_updated
RDG-001,X61001,2026-03-20,16500,,2026-03-20T06:00:00Z
RDG-002,X61002,2026-03-20,6300,,2026-03-20T06:00:00Z
```

## Why a file gets rejected

Validation stops at the first failing stage, so you may see only one of these groups at a
time.

```
Row 3: missing or empty vehicle_number
Row 4: missing or empty reading_date
Row 5: missing or empty mileage_km
```

```
Row 9: vehicle_number 'X61099' not found in project
```

```
Row 11: mileage 16500 km is less than existing 17000 km for vehicle X61001
```
