Skip to main content

Detention report — one row per facility visit with billable overage

GET 

/api/v1/reports/detention

Per-VISIT detention rows: arrival, departure, time on site, billable overage.

A carrier bills detention when a truck waits at a shipper or receiver beyond the contracted free window. The claim needs a timestamped arrival and departure for a single visit, so this report emits exactly one row per visit — a visit that spans midnight, or several days, stays ONE row. (/reports/time-on-site buckets by (device, geofence, local day) instead, which is the right shape for job-site utilisation and the wrong shape for a claim.)

Visits come from GeofenceEvent entry/exit pairing (_pair_geofence_visits), which scans SYMMETRICALLY past both ends of the window: it seeds a visit that was already open when the window began, and resolves a visit that closes after the window ends. Both matter because a truck that arrives at 18:00 and leaves at 03:00 is one claim, and clipping either end understates the invoice. Dwell SignalEvents are deliberately NOT used here: a dwell signal has no departure event, and a claim without a departure timestamp is not payable.

NOT clipped to shift sessions. report_time_on_site applies _clip_to_shifts (PROD-3) because a van parked at a job site after the crew clocks out is not working time. Detention is the exact opposite: a truck still sitting at a receiver after the driver's shift ends is precisely the time being billed, and clipping it would delete the claim. Do not "consistency-fix" this to match time-on-site.

A visit with no exit event splits into two distinct states. If a later re-entry proves the truck left, the row is departure_inferred=true (NOT open) with seconds_on_site bounded at that re-entry. Otherwise it is is_open=true with seconds_on_site measured to the earlier of now and the range end — never a silent whole-day figure. Both keep exited_at=null, because there is no departure event to cite. summary.open_visit_count therefore counts only trucks still on site; inferred departures are counted separately.

Money is integer cents end to end. over_free_time_seconds is max(0, seconds_on_site - free_time_minutes * 60); amount_owed_cents applies rate_per_hour_cents to it under the rounding rule in _detention_amount_cents. The default rate of 0 yields 0 owed, so an unpriced report shows no dollar figure rather than a fabricated one.

Evidenced money and inferred money never mix. summary.total_amount_owed_cents and summary.total_over_free_time_seconds cover evidenced and open visits only; the excluded magnitude is reported as inferred_max_additional_cents / inferred_max_additional_over_free_time_seconds, which are CEILINGS. Per row the same split is amount_owed_cents (an upper bound when departure_inferred) versus evidenced_amount_owed_cents (0 when inferred), so a CSV export can be summed without silently billing a guess.

Request

Responses

OK