Files
edr-platform/apps/edr-freight-web/backoffice/src/services/last-mile-requests.service.ts
Hagernesh 4786c896b5 feat(last-mile): customer-signed LM contract gates the advance invoice
Approval now snapshots the rate estimate and generates a last-mile
contract instead of invoicing immediately. The customer picks a delivery
date on the confirm form, then reviews and signs the contract in the
portal (saved signature or drawn); the signed PDF is stored as
LM_<CustomerName>.pdf and only then is the advance invoice issued.
Backoffice shows signature status and the contract download.
2026-08-06 07:11:23 +00:00

67 lines
2.3 KiB
TypeScript

import { api } from '../auth/http';
import { URL_CONSTANTS } from '@/constants/URLS';
export const LAST_MILE_REQUEST_STATUSES = [
'AWAITING_CONFIRMATION',
'SUBMITTED',
'APPROVED',
'REJECTED',
] as const;
export type LastMileRequestStatus = (typeof LAST_MILE_REQUEST_STATUSES)[number];
export interface LastMileRequest {
id: string;
bookingId: string;
booking?: {
id: string;
reference?: string;
companyId?: string;
company?: { id: string; name?: string } | null;
} | null;
trainScheduleId: string;
status: LastMileRequestStatus;
requestedContainerNumbers?: string[] | null;
reminderSentAt?: string | null;
submittedByUserId?: string | null;
submittedAt?: string | null;
reviewedByStaffId?: string | null;
reviewedAt?: string | null;
rejectionReason?: string | null;
resultingLastMileId?: string | null;
requestedDeliveryDate?: string | null;
customerSignedAt?: string | null;
signerDisplayName?: string | null;
createdAt: string;
updatedAt: string;
}
export interface LastMileRequestListResponse {
data: LastMileRequest[];
meta: { total: number; page: number; pageSize: number; totalPages: number };
}
/** Rule-based estimate for the approve dialog — all nulls when no rule covers the job. */
export interface LastMilePriceEstimate {
estimatedKm: number | null;
mode: 'BULK' | 'CONTAINER' | null;
currency: string | null;
total: number | null;
lines: Array<{ description: string; amount: number }>;
}
const LMR = URL_CONSTANTS.LAST_MILE_REQUESTS;
export const lastMileRequestsService = {
list: (params?: { status?: LastMileRequestStatus; bookingId?: string; page?: number; pageSize?: number }) =>
api.get<LastMileRequestListResponse>(LMR.BASE, { params }),
getById: (id: string) => api.get<LastMileRequest>(LMR.BY_ID(id)),
freeTruckCount: () => api.get<{ count: number }>(LMR.FREE_TRUCK_COUNT),
priceEstimate: (id: string) => api.get<LastMilePriceEstimate>(LMR.PRICE_ESTIMATE(id)),
approve: (id: string, advanceAmount: number) =>
api.post<LastMileRequest>(LMR.APPROVE(id), { advanceAmount }),
reject: (id: string, reason: string) =>
api.post<LastMileRequest>(LMR.REJECT(id), { reason }),
contractDocument: (id: string) =>
api.get<Blob>(LMR.CONTRACT_DOCUMENT(id), { responseType: 'blob' }),
};