mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 04:50:54 +00:00
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:
@@ -195,6 +195,7 @@ import {
|
||||
type UsedTrainNumbers,
|
||||
} from "./trainBuilder.service";
|
||||
import { trainSchedulingService } from "./trainScheduling.service";
|
||||
import { truckTypesService, type TruckType } from "./truck-types.service";
|
||||
import { wagonTypesService, type WagonType } from "./wagon-types.service";
|
||||
import {
|
||||
wagonService,
|
||||
@@ -2073,6 +2074,36 @@ export const api = {
|
||||
),
|
||||
},
|
||||
|
||||
truckTypes: {
|
||||
list: endpoint<void, TruckType[]>("truck-types", "list", () =>
|
||||
truckTypesService.getTruckTypes(),
|
||||
),
|
||||
|
||||
create: endpoint<Partial<TruckType>, TruckType>(
|
||||
"truck-types",
|
||||
"create",
|
||||
(payload) => truckTypesService.create(payload).then((r) => r.data),
|
||||
undefined,
|
||||
() => [["truck-types"]],
|
||||
),
|
||||
|
||||
update: endpoint<{ id: string; data: Partial<TruckType> }, TruckType>(
|
||||
"truck-types",
|
||||
"update",
|
||||
({ id, data }) => truckTypesService.update(id, data).then((r) => r.data),
|
||||
undefined,
|
||||
() => [["truck-types"]],
|
||||
),
|
||||
|
||||
remove: endpoint<string, void>(
|
||||
"truck-types",
|
||||
"remove",
|
||||
(id) => truckTypesService.delete(id).then(() => undefined),
|
||||
undefined,
|
||||
() => [["truck-types"]],
|
||||
),
|
||||
},
|
||||
|
||||
wagonTypes: {
|
||||
list: endpoint<void, WagonType[]>("wagon-types", "list", () =>
|
||||
wagonTypesService.getWagonTypes(),
|
||||
|
||||
@@ -22,7 +22,10 @@ export interface FirstMileBooking {
|
||||
serviceType?: { id: string; label?: string } | null;
|
||||
originYard?: { id: string; label?: string } | null;
|
||||
destinationYard?: { id: string; label?: string } | null;
|
||||
cargoType?: { id: string; label?: string } | null;
|
||||
cargoType?: { id: string; label?: string; cargoTypeName?: string; name?: string } | null;
|
||||
freightType?: string | null;
|
||||
/** Attached server-side: the train schedule this booking rides. */
|
||||
trainSchedule?: { trainNumber: string | null; departureDate: string | null } | null;
|
||||
/** Container lines — total container count drives how many trucks are needed. */
|
||||
bookingContainers?: Array<{
|
||||
id: string;
|
||||
@@ -68,6 +71,8 @@ export interface FirstMileRecord {
|
||||
vehicleId: string;
|
||||
containerNumber?: string | null;
|
||||
distanceKm?: number | null;
|
||||
tons?: number | null;
|
||||
quantity?: number | null;
|
||||
vehicle?: FirstMileVehicle | null;
|
||||
}>;
|
||||
/** Present only when an invoice has actually been generated (not on distance). */
|
||||
@@ -95,7 +100,12 @@ export const firstMileService = {
|
||||
api.delete<void>(FM.BY_ID(id)),
|
||||
setVehicles: (
|
||||
id: string,
|
||||
vehicles: Array<{ vehicleId: string; containerNumber?: string | null }>,
|
||||
vehicles: Array<{
|
||||
vehicleId: string;
|
||||
containerNumber?: string | null;
|
||||
tons?: number | null;
|
||||
quantity?: number | null;
|
||||
}>,
|
||||
) => api.post<FirstMileRecord>(`${FM.BASE}/${id}/vehicles`, { vehicles }),
|
||||
setDistances: (
|
||||
id: string,
|
||||
|
||||
@@ -85,6 +85,7 @@ const RESOURCE_BASE: Record<RuleEngineResourceSlug, string> = {
|
||||
"cargo-types": URL_CONSTANTS.RULE_ENGINE.CARGO_TYPES,
|
||||
"container-types": URL_CONSTANTS.RULE_ENGINE.CONTAINER_TYPES,
|
||||
"wagon-types": URL_CONSTANTS.RULE_ENGINE.WAGON_TYPES,
|
||||
"truck-types": URL_CONSTANTS.RULE_ENGINE.TRUCK_TYPES,
|
||||
"priority-configs": URL_CONSTANTS.RULE_ENGINE.PRIORITY_CONFIGS,
|
||||
"service-types": URL_CONSTANTS.RULE_ENGINE.SERVICE_TYPES,
|
||||
"weight-limit-rules": URL_CONSTANTS.RULE_ENGINE.WEIGHT_LIMIT_RULES,
|
||||
@@ -103,6 +104,8 @@ const byIdPath = (resource: RuleEngineResourceSlug, id: string): string => {
|
||||
return URL_CONSTANTS.RULE_ENGINE.CONTAINER_TYPE_BY_ID(id);
|
||||
case "wagon-types":
|
||||
return URL_CONSTANTS.RULE_ENGINE.WAGON_TYPE_BY_ID(id);
|
||||
case "truck-types":
|
||||
return URL_CONSTANTS.RULE_ENGINE.TRUCK_TYPE_BY_ID(id);
|
||||
case "priority-configs":
|
||||
return URL_CONSTANTS.RULE_ENGINE.PRIORITY_CONFIG_BY_ID(id);
|
||||
case "service-types":
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { api } from "../auth/http";
|
||||
|
||||
type ListResponse<T> = T[] | { data: T[] };
|
||||
|
||||
export interface TruckType {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
/** Pre-fills a vehicle's capacity — capacity belongs to the type, not each truck. */
|
||||
capacityTons: number | null;
|
||||
/** False for a rigid truck (e.g. Casoni), which has no trailer plate at all. */
|
||||
hasTrailer: boolean;
|
||||
description?: string | null;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
const asList = <T>(payload: ListResponse<T>): T[] =>
|
||||
Array.isArray(payload) ? payload : payload.data;
|
||||
|
||||
export const truckTypesService = {
|
||||
async getTruckTypes() {
|
||||
const response = await api.get<ListResponse<TruckType>>('/truck-types', {
|
||||
params: { isActive: 'all', pageSize: 500 },
|
||||
});
|
||||
return asList(response.data);
|
||||
},
|
||||
create: (data: Partial<TruckType>) => api.post('/truck-types', data),
|
||||
update: (id: string, data: Partial<TruckType>) => api.patch(`/truck-types/${id}`, data),
|
||||
delete: (id: string) => api.delete(`/truck-types/${id}`),
|
||||
};
|
||||
@@ -20,7 +20,14 @@ export interface Vehicle {
|
||||
id: string;
|
||||
plateNumber: string;
|
||||
registrationNumber: string;
|
||||
/** Denormalised truck-type code, written server-side. Register with `truckTypeId`. */
|
||||
vehicleType: VehicleType;
|
||||
/** Truck configuration from the managed truck types. */
|
||||
truckTypeId?: string | null;
|
||||
/** Vehicle Identification Number — unique across the fleet. */
|
||||
vin?: string | null;
|
||||
/** OWNED | OUTSOURCED. */
|
||||
ownership?: string | null;
|
||||
manufacturer: string;
|
||||
model: string;
|
||||
year: number;
|
||||
|
||||
Reference in New Issue
Block a user