fix(warehouses): detention groups by canonical truck type

Join truck_types via vehicles.truck_type_id (normalized legacy
vehicle_type only as fallback) so type renames can't unmatch detention
rules and FK-less vehicles keep billing.
This commit is contained in:
Hagernesh
2026-07-23 13:40:14 +00:00
parent 227a561e89
commit cf8a2e928d
41 changed files with 1495 additions and 109 deletions

View File

@@ -10,6 +10,16 @@ const PLATE_PATTERN = {
message: "Use letters and numbers like ET-9875 or AA-8642",
};
/**
* Legacy static list. Truck configurations now live in `freight.truck_types`
* and are edited in the back office (Rule Engine → Truck Types), so the form
* loads them through `dynamicOptions: "truckTypes"` instead.
*
* Kept only for the driver "authorized vehicle types" multiselect, which stores
* free-text categories rather than truck-type ids.
*
* @deprecated prefer the managed truck types
*/
const VEHICLE_TYPE_OPTIONS = [
{ label: "Truck", value: "TRUCK" },
{ label: "Van", value: "VAN" },
@@ -20,6 +30,11 @@ const VEHICLE_TYPE_OPTIONS = [
{ label: "Flatbed", value: "FLATBED" },
];
const OWNERSHIP_OPTIONS = [
{ label: "Owned", value: "OWNED" },
{ label: "Outsourced", value: "OUTSOURCED" },
];
const FUEL_TYPE_OPTIONS = [
{ label: "Petrol", value: "PETROL" },
{ label: "Diesel", value: "DIESEL" },
@@ -39,11 +54,6 @@ const VEHICLE_AVAILABILITY_OPTIONS = [
{ label: "Busy", value: "BUSY" },
];
const CURRENCY_OPTIONS = [
{ label: "ETB", value: "ETB" },
{ label: "USD", value: "USD" },
];
export const vehiclesConfig: FleetResourceConfig = {
slug: "vehicles",
label: "Vehicles",
@@ -72,35 +82,65 @@ export const vehiclesConfig: FleetResourceConfig = {
options: VEHICLE_AVAILABILITY_OPTIONS,
},
],
searchKeys: ["plateNumber", "registrationNumber", "manufacturer", "model", "vehicleType", "status"],
searchKeys: ["plateNumber", "registrationNumber", "manufacturer", "model", "vehicleType", "vin", "status"],
columns: [
{ id: "code", header: "Code", accessorKey: "code", size: 90 },
{ id: "plateNumber", header: "Plate Number", accessorKey: "plateNumber", size: 120 },
{ id: "trailerPlateNo", header: "Trailer Plate No", accessorKey: "trailerPlateNo", size: 130 },
{ id: "manufacturer", header: "Manufacturer", accessorKey: "manufacturer", size: 120 },
{ id: "model", header: "Model", accessorKey: "model", size: 100 },
{ id: "vehicleType", header: "Type", accessorKey: "vehicleType", size: 75 },
{ id: "truckTypeId", header: "Truck Type", accessorKey: "truckTypeId", format: "entityLabel", size: 130 },
{ id: "capacity", header: "Capacity (tons)", accessorKey: "capacity", format: "number", size: 100 },
{ id: "locationId", header: "Location", accessorKey: "locationId", size: 140 },
{ id: "ownership", header: "Ownership", accessorKey: "ownership", size: 100 },
{ id: "status", header: "Status", accessorKey: "status", format: "statusBadge", size: 100 },
{ id: "availability", header: "Availability", accessorKey: "availability", format: "statusBadge", size: 100 },
],
// Registration captures what the vehicle IS. Location, distances and haulage
// pricing are per-trip operational data and live on the vehicle's Operations
// tab instead (VehicleDetailPage) — they are not part of registering a truck.
formFields: [
{ name: "code", label: "Code", type: "text" },
{ name: "plateNumber", label: "Power Plate No", type: "text", required: true, pattern: PLATE_PATTERN },
// { name: "powerPlateNo", label: "Power Plate No", type: "text" },
{ name: "trailerPlateNo", label: "Trailer Plate No", type: "text", pattern: PLATE_PATTERN },
{ name: "vehicleType", label: "Vehicle Type", type: "select", required: true, options: VEHICLE_TYPE_OPTIONS },
// The truck type decides whether a trailer exists at all: a rigid truck
// (Casoni) has none, so the field disappears and submits null. Mirrored
// server-side in VehiclesService — hiding a field is not enforcement.
{
name: "trailerPlateNo",
label: "Trailer Plate No",
type: "text",
pattern: PLATE_PATTERN,
showIf: (values) => values._hasTrailer !== false,
},
{
name: "truckTypeId",
label: "Truck Type",
type: "select",
required: true,
dynamicOptions: "truckTypes",
description: "Managed in Rule Engine → Truck Types",
// Capacity is a property of the type, not of each individual truck.
// `_hasTrailer` is form-local scratch (stripped before submit) that drives
// the trailer plate's visibility.
onOptionSelected: (option) => ({
_hasTrailer: option?.meta?.hasTrailer ?? true,
...(option?.meta?.capacityTons != null
? { capacity: option.meta.capacityTons }
: {}),
}),
},
{ name: "vin", label: "VIN", type: "text", description: "Vehicle Identification Number" },
{ name: "ownership", label: "Ownership", type: "radio", options: OWNERSHIP_OPTIONS },
{ name: "manufacturer", label: "Manufacturer", type: "text", required: true },
{ name: "model", label: "Model", type: "text", required: true },
{ name: "year", label: "Year", type: "number", required: true },
{ name: "fuelType", label: "Fuel Type", type: "select", required: true, options: FUEL_TYPE_OPTIONS },
{ name: "capacity", label: "Capacity", type: "number", required: true },
{ name: "locationId", label: "Location", type: "select", dynamicOptions: "yards" },
{ name: "estimatedDistanceKm", label: "Estimated Distance (KM)", type: "number" },
{ name: "actualDistanceKm", label: "Actual Distance (KM)", type: "number" },
{ name: "pricePerKm", label: "Price per KM", type: "number" },
{ name: "currency", label: "Currency", type: "radio", options: CURRENCY_OPTIONS },
{
name: "capacity",
label: "Capacity (tons)",
type: "number",
required: true,
description: "Pre-filled from the truck type — override only for a one-off",
},
{ name: "status", label: "Status", type: "select", required: true, options: VEHICLE_STATUS_OPTIONS },
{ name: "availability", label: "Availability", type: "select", required: true, options: VEHICLE_AVAILABILITY_OPTIONS },
{ name: "description", label: "Description", type: "textarea" },
@@ -108,19 +148,15 @@ export const vehiclesConfig: FleetResourceConfig = {
emptyValues: {
code: "03-ET",
plateNumber: "",
powerPlateNo: "",
trailerPlateNo: "",
vehicleType: "TRUCK",
truckTypeId: "",
vin: "",
ownership: "OWNED",
manufacturer: "",
model: "",
year: new Date().getFullYear(),
fuelType: "DIESEL",
capacity: 0,
locationId: null,
estimatedDistanceKm: "",
actualDistanceKm: "",
pricePerKm: "",
currency: "ETB",
status: "ACTIVE",
availability: "FREE",
description: "",