Files
edr-platform/apps/edr-freight-web/backoffice/src/services/first-mile.service.ts
Hagernesh cf8a2e928d 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.
2026-07-24 11:55:05 +00:00

118 lines
4.2 KiB
TypeScript

import { api } from '../auth/http';
import { URL_CONSTANTS } from '@/constants/URLS';
export const FIRST_MILE_STATUSES = [
'PAYMENT_PENDING',
'READY_TO_TRANSIT',
'IN_TRANSIT',
'RECEIVED_TO_PORT',
] as const;
export type FirstMileApiStatus = (typeof FIRST_MILE_STATUSES)[number];
export interface FirstMileBooking {
id: string;
reference: string;
firstMilePickupAddress?: string | null;
cargoFreeText?: string | null;
cargoTotalWeightVgm: number;
totalAmount: number;
paymentCurrency?: string | null;
scheduledDate?: string | null;
company?: { id: string; name?: string; phone?: string | null; contactPersonName?: string | null; contactPersonPhone?: string | null } | null;
serviceType?: { id: string; label?: string } | null;
originYard?: { id: string; label?: string } | null;
destinationYard?: { 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;
quantity: number;
containerNumber?: string | null;
containerSize?: string | null;
containerType?: { id: string; name?: string; label?: string; code?: string } | null;
/** Physical containers under this line — their real numbers (line-level
* containerNumber is often a TBD placeholder). */
units?: Array<{ id: string; containerNumber?: string | null; sortOrder?: number }>;
}>;
}
export interface FirstMileVehicle {
id: string;
plateNumber: string;
manufacturer: string;
model: string;
vehicleType?: string | null;
code?: string | null;
powerPlateNo?: string | null;
trailerPlateNo?: string | null;
assignedDriverId?: string | null;
assignedDriverName?: string | null;
pricePerKm?: number | string | null;
currency?: string | null;
}
export interface FirstMileRecord {
id: string;
bookingId: string;
status: FirstMileApiStatus;
advancedPayment: number;
remainingPayment: number;
estimatedKm?: number | null;
exactKm?: number | null;
vehicleId?: string | null;
booking?: FirstMileBooking | null;
vehicle?: FirstMileVehicle | null;
/** Full set of vehicles serving this pickup (multi-truck). */
vehicleAssignments?: Array<{
id: string;
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). */
invoice?: { id: string; number: string; status: string } | null;
createdAt: string;
updatedAt: string;
}
export interface FirstMileListResponse {
data: FirstMileRecord[];
meta: { total: number; page: number; pageSize: number; totalPages: number };
}
const FM = URL_CONSTANTS.FIRST_MILE;
export const firstMileService = {
list: (pageSize = 1000) =>
api.get<FirstMileListResponse>(`${FM.BASE}?pageSize=${pageSize}`),
getById: (id: string) => api.get<FirstMileRecord>(FM.BY_ID(id)),
update: (id: string, data: { status?: FirstMileApiStatus; vehicleId?: string | null; estimatedKm?: number | null; exactKm?: number | null; paid?: boolean }) =>
api.patch<FirstMileRecord>(FM.BY_ID(id), data),
accept: (bookingReference: string) =>
api.post<FirstMileRecord>(FM.ACCEPT(bookingReference)),
remove: (id: string) =>
api.delete<void>(FM.BY_ID(id)),
setVehicles: (
id: string,
vehicles: Array<{
vehicleId: string;
containerNumber?: string | null;
tons?: number | null;
quantity?: number | null;
}>,
) => api.post<FirstMileRecord>(`${FM.BASE}/${id}/vehicles`, { vehicles }),
setDistances: (
id: string,
distances: Array<{ vehicleId: string; distanceKm: number }>,
remainingPayment?: number,
) => api.post<FirstMileRecord>(`${FM.BASE}/${id}/distances`, { distances, remainingPayment }),
generateInvoice: (id: string) =>
api.post<{ id: string; invoiceNumber?: string } | null>(`${FM.BASE}/${id}/invoice`),
};