> ## 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.

# measurements

> Condition measurements. The most strictly validated file.

Condition measurements — wheel diameter, flange thickness and similar. Delivered daily.

**Update key:** `external_system_id`

This is the most strictly validated file in the import. Four separate rules can reject it
beyond the usual reference checks, and they abort in stages, so fixing one class of problem
can reveal another.

## Columns

| Column                    | Type          | Required | Notes                                               |
| ------------------------- | ------------- | :------: | --------------------------------------------------- |
| `external_system_id`      | text(100)     |     ●    | Update key                                          |
| `measurement_type_code`   | text(50)      |     ●    | Must be a configured measurement type               |
| `vehicle_number`          | text(100)     |     ○    | Required *unless* `component_serial_number` is set  |
| `component_serial_number` | text(100)     |     ○    | Must exist in project                               |
| `component_type_code`     | text(50)      |     ●    | Must match the component's actual type              |
| `position`                | text(50)      |     ○    | A string, not a number. See the position rule below |
| `measured_at`             | timestamptz   |     ●    |                                                     |
| `performance_km`          | integer       |     ○    |                                                     |
| `operating_hours`         | integer       |     ○    |                                                     |
| `value`                   | decimal(10,2) |     ●    |                                                     |
| `action_code`             | text(50)      |     ○    | e.g. `REFRAMING`                                    |
| `action_description`      | text(255)     |     ○    |                                                     |
| `reason_code`             | text(50)      |     ○    |                                                     |
| `reason_description`      | text(255)     |     ○    |                                                     |
| `notes`                   | text          |     ○    |                                                     |
| `last_updated`            | timestamptz   |     ●    | Staleness key                                       |

## Rules

Every row needs at least one of `vehicle_number` / `component_serial_number`.

### A threshold must be configured

A threshold must exist for the (`measurement_type_code`, `component_type_code`) pair, or the
measurement is refused outright. This is a configuration step done in the application — the
pair's existence is what registers it as importable at all.

### The value must be within bounds

`value` must fall inside that threshold's minimum and maximum. Bounds are **inclusive**: a
value exactly equal to the limit passes. Each bound is only enforced when it is configured,
so a threshold with neither set enforces nothing while still satisfying the check above.

### Position must match the active mount

`position` must equal the slot position of the component's active mount at `measured_at`.
Send it when the component was mounted; leave it **empty** when it was not. A mismatch, a
missing position on a mounted component, and a position on an unmounted one all reject the
file.

<Note>
  This check runs **only for rows that resolve to a component**. On a vehicle-only row,
  `position` is accepted without being checked. Note also that "empty" must mean a genuinely
  empty field — a whitespace-only value is treated as set, and will fail.
</Note>

### Values may not move against the progression rule

A measurement type may declare a progression rule. A `DESCENDING` type such as wheel
diameter may not increase; an `ASCENDING` type may not decrease. An equal value never
violates the rule.

The exception is a **reset action**: if `action_code` is one of that type's configured reset
actions — a reframing resetting a wheel diameter, for instance — the movement is allowed.

Comparison is per component and measurement type, across both the file and existing data,
and deliberately **follows the component between slots**: position is not part of the
comparison, so a wheelset moved to another position is still compared against its own
history.

<Warning>
  "Prior" means the single most recent measurement with a strictly earlier `measured_at`.
  Delivering measurements out of chronological order therefore compares against the wrong
  neighbour and can reject a perfectly valid file. Send them in order where you can.
</Warning>

### Action codes must be allowed

When `action_code` is given it must be one of the measurement type's allowed action codes.
The check is skipped entirely if the type has no allowed list configured.

## Example

A mounted wheelset, with `position` matching the active mount:

```csv theme={null}
external_system_id,measurement_type_code,vehicle_number,component_serial_number,component_type_code,position,measured_at,performance_km,operating_hours,value,action_code,action_description,reason_code,reason_description,notes,last_updated
MEAS-171,WHEEL_DIAMETER,X61001,0789-D-1,0789-D,1,2026-07-10T09:30:00Z,203440,,827.09,LATHE,Svarv,81,Hjulplatta,Driving wheelset re-profiled on lathe — flat spot,2026-07-16T10:00:00Z
MEAS-172,WHEEL_DIAMETER,X61001,0789-T-5,0789-T,2,2026-07-15T09:30:00Z,211157,,830.40,LATHE,Svarv,83,Förebyggande,Trailing wheelset re-profiled on lathe — preventive,2026-07-16T10:00:00Z
```

An unmounted component — no `vehicle_number`, empty `position` — and a reset action that
permits the diameter to increase:

```csv theme={null}
MEAS-174,WHEEL_DIAMETER,,0789-D-1,0789-D,,2026-07-28T15:00:00Z,228800,,850.00,REFRAMING,Omsvarvning,83,Förebyggande,,2026-08-01T10:08:04Z
```

## Why a file gets rejected

Required fields first:

```
Row 3: missing required field measurement_type_code
Row 4: missing required field component_type_code
Row 5: missing required field value
Row 6: must have at least one of vehicle_number or component_serial_number
```

Then reference resolution:

```
Row 8: measurement_type_code 'WHEEL_DIA' not found in project
Row 9: component_serial_number '0789-D-9' not found in project
Row 10: component_type_code '0789-X' not found in project
```

Then type matching and the threshold gate:

```
Row 12: component '0789-D-1' is type 0789-D but CSV declared 0789-T
Row 13: no threshold configured for measurement_type WHEEL_DIAMETER on component_type 0789-T
```

Finally position, bounds, action codes and progression together:

```
Row 15: position required when component is mounted on slot '1'
Row 16: position '3' set but component is not mounted at measured_at
Row 17: position '3' does not match active mount slot '1'
Row 18: value 780.00 is below min_value 790.00 for WHEEL_DIAMETER × 0789-D
Row 19: value 870.00 is above max_value 860.00 for WHEEL_DIAMETER × 0789-D
Row 20: action_code 'GRIND' is not in allowed_action_codes [LATHE, REFRAMING] for WHEEL_DIAMETER
Row 21: value 830.00 increased from prior 827.09 without a reset action — DESCENDING rule violated for WHEEL_DIAMETER
```
