mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 15:25:45 +00:00
merge conflict resolved
This commit is contained in:
@@ -13,6 +13,7 @@ import type {
|
||||
CustomerBooking,
|
||||
CustomerDocument,
|
||||
CustomerPayment,
|
||||
CustomerAccount,
|
||||
CustomerResetTarget,
|
||||
PaginatedCompanies,
|
||||
ProfileStatus,
|
||||
@@ -84,6 +85,7 @@ import type {
|
||||
UpdateCheckpointPayload,
|
||||
DispatchSchedulePayload,
|
||||
StaffBookingWindow,
|
||||
MarshallingStop,
|
||||
ScheduleMergePreview,
|
||||
TrainScheduleDetail,
|
||||
TrainScheduleFilters,
|
||||
@@ -151,6 +153,15 @@ import type {
|
||||
WarehouseLoading,
|
||||
WarehouseYard,
|
||||
WarehouseZone,
|
||||
ZoneContentItem,
|
||||
ZoneLayout,
|
||||
ZoneSlotSummary,
|
||||
WarehouseZoneStack,
|
||||
WarehouseZoneSlot,
|
||||
SaveStackPayload,
|
||||
AvailableSlot,
|
||||
ContainerAccessibility,
|
||||
FindSlotPayload,
|
||||
} from "@/types/warehouse";
|
||||
import { endpoint } from "@/utils/endpoint";
|
||||
import {
|
||||
@@ -285,6 +296,11 @@ const INVENTORY_INVALIDATIONS: ReadonlyArray<readonly unknown[]> = [
|
||||
const TRAIN_SCHEDULING_INVALIDATIONS: ReadonlyArray<readonly unknown[]> = [
|
||||
QUERY_KEYS.TRAIN_SCHEDULING.ROOT,
|
||||
QUERY_KEYS.BOOKINGS.ROOT,
|
||||
// A station's loading window gates the warehouse loading queues too — they
|
||||
// render the same Start/End controls, so they must refresh on the same click.
|
||||
["loadable-trains"],
|
||||
["train-loadable-items"],
|
||||
["warehouse-inventory"],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -595,6 +611,13 @@ export const api = {
|
||||
({ id }) => QUERY_KEYS.TRAIN_SCHEDULING.track(id),
|
||||
),
|
||||
|
||||
marshallingStops: endpoint<{ id: string }, MarshallingStop[]>(
|
||||
"train-scheduling",
|
||||
"marshalling-stops",
|
||||
({ id }) => trainSchedulingService.getMarshallingStops(id),
|
||||
({ id }) => QUERY_KEYS.TRAIN_SCHEDULING.marshallingStops(id),
|
||||
),
|
||||
|
||||
unassignedBookings: endpoint<
|
||||
{ scheduleId: string },
|
||||
UnassignedBookingsResponse
|
||||
@@ -1215,6 +1238,14 @@ export const api = {
|
||||
() => [["warehouses"], ["warehouse-yards"]],
|
||||
),
|
||||
|
||||
deleteYard: endpoint<{ id: string }, unknown>(
|
||||
"warehouses",
|
||||
"deleteYard",
|
||||
({ id }) => warehouseService.removeYard(id).then((r) => r.data),
|
||||
undefined,
|
||||
() => [["warehouses"], ["warehouse-yards"]],
|
||||
),
|
||||
|
||||
// ── Zones ──────────────────────────────────────────────────────────────
|
||||
listZones: endpoint<{ yardId: string }, WarehouseZone[]>(
|
||||
"warehouses",
|
||||
@@ -1247,6 +1278,100 @@ export const api = {
|
||||
() => [["warehouse-yards"]],
|
||||
),
|
||||
|
||||
zoneContents: endpoint<{ zoneId: string }, ZoneContentItem[]>(
|
||||
"warehouse-zones",
|
||||
"contents",
|
||||
({ zoneId }) => warehouseService.zoneContents(zoneId).then((r) => r.data),
|
||||
({ zoneId }) => ["warehouse-zones", zoneId, "contents"],
|
||||
),
|
||||
|
||||
zoneLayout: endpoint<{ zoneId: string }, ZoneLayout>(
|
||||
"warehouse-zones",
|
||||
"layout",
|
||||
({ zoneId }) => warehouseService.zoneLayout(zoneId).then((r) => r.data),
|
||||
({ zoneId }) => ["warehouse-zones", zoneId, "layout"],
|
||||
),
|
||||
|
||||
zoneSlotSummary: endpoint<{ zoneId: string }, ZoneSlotSummary>(
|
||||
"warehouse-zones",
|
||||
"slot-summary",
|
||||
({ zoneId }) => warehouseService.zoneSlotSummary(zoneId).then((r) => r.data),
|
||||
({ zoneId }) => ["warehouse-zones", zoneId, "slot-summary"],
|
||||
),
|
||||
|
||||
// ── Ground stacks and slots ────────────────────────────────────────────
|
||||
listStacks: endpoint<{ zoneId: string }, WarehouseZoneStack[]>(
|
||||
"warehouse-zone-stacks",
|
||||
"list",
|
||||
({ zoneId }) => warehouseService.listStacks(zoneId).then((r) => r.data),
|
||||
({ zoneId }) => ["warehouse-zone-stacks", zoneId],
|
||||
),
|
||||
|
||||
createStack: endpoint<
|
||||
{ zoneId: string; payload: SaveStackPayload },
|
||||
WarehouseZoneStack
|
||||
>(
|
||||
"warehouse-zone-stacks",
|
||||
"create",
|
||||
({ zoneId, payload }) =>
|
||||
warehouseService.createStack(zoneId, payload).then((r) => r.data),
|
||||
undefined,
|
||||
({ zoneId }) => [
|
||||
["warehouse-zone-stacks", zoneId],
|
||||
["warehouse-zones", zoneId, "layout"],
|
||||
["warehouse-zones", zoneId, "slot-summary"],
|
||||
],
|
||||
),
|
||||
|
||||
updateStack: endpoint<
|
||||
{ id: string; zoneId: string; payload: Partial<SaveStackPayload> },
|
||||
WarehouseZoneStack
|
||||
>(
|
||||
"warehouse-zone-stacks",
|
||||
"update",
|
||||
({ id, payload }) => warehouseService.updateStack(id, payload).then((r) => r.data),
|
||||
undefined,
|
||||
({ zoneId }) => [
|
||||
["warehouse-zone-stacks", zoneId],
|
||||
["warehouse-zones", zoneId, "layout"],
|
||||
["warehouse-zones", zoneId, "slot-summary"],
|
||||
],
|
||||
),
|
||||
|
||||
deleteStack: endpoint<{ id: string; zoneId: string }, unknown>(
|
||||
"warehouse-zone-stacks",
|
||||
"delete",
|
||||
({ id }) => warehouseService.removeStack(id).then((r) => r.data),
|
||||
undefined,
|
||||
({ zoneId }) => [
|
||||
["warehouse-zone-stacks", zoneId],
|
||||
["warehouse-zones", zoneId, "layout"],
|
||||
["warehouse-zones", zoneId, "slot-summary"],
|
||||
],
|
||||
),
|
||||
|
||||
updateSlot: endpoint<
|
||||
{ slotId: string; zoneId: string; payload: { status?: string; isActive?: boolean } },
|
||||
WarehouseZoneSlot
|
||||
>(
|
||||
"warehouse-zone-stacks",
|
||||
"update-slot",
|
||||
({ slotId, payload }) => warehouseService.updateSlot(slotId, payload).then((r) => r.data),
|
||||
undefined,
|
||||
({ zoneId }) => [
|
||||
["warehouse-zones", zoneId, "layout"],
|
||||
["warehouse-zones", zoneId, "slot-summary"],
|
||||
],
|
||||
),
|
||||
|
||||
deleteZone: endpoint<{ id: string }, unknown>(
|
||||
"warehouses",
|
||||
"deleteZone",
|
||||
({ id }) => warehouseService.removeZone(id).then((r) => r.data),
|
||||
undefined,
|
||||
() => [["warehouses"], ["warehouse-yards"]],
|
||||
),
|
||||
|
||||
// ── Inventory (queries) ────────────────────────────────────────────────
|
||||
listInventory: endpoint<
|
||||
{ filter?: InventoryFilter },
|
||||
@@ -1470,7 +1595,7 @@ export const api = {
|
||||
"store",
|
||||
({ id, payload }) => warehouseService.store(id, payload).then((r) => r.data),
|
||||
undefined,
|
||||
() => INVENTORY_INVALIDATIONS,
|
||||
() => [...INVENTORY_INVALIDATIONS, ["warehouse-zones"]],
|
||||
),
|
||||
|
||||
reserve: endpoint<ReserveInventoryPayload, WarehouseInventoryItem>(
|
||||
@@ -1509,6 +1634,36 @@ export const api = {
|
||||
() => INVENTORY_INVALIDATIONS,
|
||||
),
|
||||
|
||||
findAvailableSlot: endpoint<FindSlotPayload, AvailableSlot | null>(
|
||||
"warehouse-inventory",
|
||||
"find-slot",
|
||||
(payload) => warehouseService.findAvailableSlot(payload).then((r) => r.data),
|
||||
(payload) => ["warehouse-inventory", "find-slot", payload],
|
||||
),
|
||||
|
||||
containerAccessibility: endpoint<{ id: string }, ContainerAccessibility>(
|
||||
"warehouse-inventory",
|
||||
"accessibility",
|
||||
({ id }) => warehouseService.containerAccessibility(id).then((r) => r.data),
|
||||
({ id }) => ["warehouse-inventory", id, "accessibility"],
|
||||
),
|
||||
|
||||
assignSlot: endpoint<{ id: string; slotId?: string }, WarehouseInventoryItem>(
|
||||
"warehouse-inventory",
|
||||
"assign-slot",
|
||||
({ id, slotId }) => warehouseService.assignSlot(id, slotId).then((r) => r.data),
|
||||
undefined,
|
||||
() => [...INVENTORY_INVALIDATIONS, ["warehouse-zones"]],
|
||||
),
|
||||
|
||||
releaseSlot: endpoint<{ id: string }, WarehouseInventoryItem>(
|
||||
"warehouse-inventory",
|
||||
"release-slot",
|
||||
({ id }) => warehouseService.releaseSlot(id).then((r) => r.data),
|
||||
undefined,
|
||||
() => [...INVENTORY_INVALIDATIONS, ["warehouse-zones"]],
|
||||
),
|
||||
|
||||
move: endpoint<
|
||||
{ id: string; payload: MoveInventoryPayload },
|
||||
WarehouseInventoryItem
|
||||
@@ -1518,7 +1673,7 @@ export const api = {
|
||||
({ id, payload }) =>
|
||||
warehouseService.move(id, payload).then((r) => r.data),
|
||||
undefined,
|
||||
() => INVENTORY_INVALIDATIONS,
|
||||
() => [...INVENTORY_INVALIDATIONS, ["warehouse-zones"]],
|
||||
),
|
||||
|
||||
markReadyForPickup: endpoint<string, WarehouseInventoryItem>(
|
||||
@@ -3267,6 +3422,13 @@ export const api = {
|
||||
({ id }) => QUERY_KEYS.CUSTOMERS.payments(id),
|
||||
),
|
||||
|
||||
accounts: endpoint<{ companyId: string }, CustomerAccount[]>(
|
||||
"customers",
|
||||
"accounts",
|
||||
({ companyId }) => customersService.accounts(companyId),
|
||||
({ companyId }) => QUERY_KEYS.CUSTOMERS.accounts(companyId),
|
||||
),
|
||||
|
||||
resetTarget: endpoint<{ companyId: string }, CustomerResetTarget>(
|
||||
"customers",
|
||||
"resetTarget",
|
||||
|
||||
@@ -73,6 +73,18 @@ export interface BookingListFilter {
|
||||
freightType?: string;
|
||||
/** Service type (rule-engine service_types.id). */
|
||||
serviceTypeId?: string;
|
||||
/** Cargo type OR cargo group — a group matches every commodity beneath it. */
|
||||
cargoTypeId?: string;
|
||||
/** Contains-search over content: cargo description, commodity, container types. */
|
||||
cargoText?: string;
|
||||
/** Bookings carrying this container type; also scopes containersMin/Max to it. */
|
||||
containerTypeId?: string;
|
||||
/** Container count bounds — of containerTypeId when set, else of all types. */
|
||||
containersMin?: string;
|
||||
containersMax?: string;
|
||||
/** Bounds on containers declared on the shipment request behind the booking. */
|
||||
requestedContainersMin?: string;
|
||||
requestedContainersMax?: string;
|
||||
/** ONE_TIME | GENERAL_CONTRACT — the booking-kind tab filter. */
|
||||
bookingType?: string;
|
||||
/** 'true' → customs bookings, 'false' → self-clearance (non-customs). */
|
||||
@@ -199,6 +211,15 @@ export const bookingsService = {
|
||||
if (filter.companyId) params.companyId = filter.companyId;
|
||||
if (filter.freightType) params.freightType = filter.freightType;
|
||||
if (filter.serviceTypeId) params.serviceTypeId = filter.serviceTypeId;
|
||||
if (filter.cargoTypeId) params.cargoTypeId = filter.cargoTypeId;
|
||||
if (filter.cargoText) params.cargoText = filter.cargoText;
|
||||
if (filter.containerTypeId) params.containerTypeId = filter.containerTypeId;
|
||||
if (filter.containersMin) params.containersMin = filter.containersMin;
|
||||
if (filter.containersMax) params.containersMax = filter.containersMax;
|
||||
if (filter.requestedContainersMin)
|
||||
params.requestedContainersMin = filter.requestedContainersMin;
|
||||
if (filter.requestedContainersMax)
|
||||
params.requestedContainersMax = filter.requestedContainersMax;
|
||||
if (filter.bookingType) params.bookingType = filter.bookingType;
|
||||
if (filter.tradeDirection) params.tradeDirection = filter.tradeDirection;
|
||||
if (filter.paymentCurrency)
|
||||
@@ -238,6 +259,15 @@ export const bookingsService = {
|
||||
if (filter.contractId) params.contractId = filter.contractId;
|
||||
if (filter.freightType) params.freightType = filter.freightType;
|
||||
if (filter.serviceTypeId) params.serviceTypeId = filter.serviceTypeId;
|
||||
if (filter.cargoTypeId) params.cargoTypeId = filter.cargoTypeId;
|
||||
if (filter.cargoText) params.cargoText = filter.cargoText;
|
||||
if (filter.containerTypeId) params.containerTypeId = filter.containerTypeId;
|
||||
if (filter.containersMin) params.containersMin = filter.containersMin;
|
||||
if (filter.containersMax) params.containersMax = filter.containersMax;
|
||||
if (filter.requestedContainersMin)
|
||||
params.requestedContainersMin = filter.requestedContainersMin;
|
||||
if (filter.requestedContainersMax)
|
||||
params.requestedContainersMax = filter.requestedContainersMax;
|
||||
if (filter.bookingType) params.bookingType = filter.bookingType;
|
||||
if (filter.tradeDirection) params.tradeDirection = filter.tradeDirection;
|
||||
if (filter.paymentCurrency)
|
||||
|
||||
@@ -10,6 +10,7 @@ import type {
|
||||
CustomerBooking,
|
||||
CustomerDocument,
|
||||
CustomerPayment,
|
||||
CustomerAccount,
|
||||
CustomerResetTarget,
|
||||
PaginatedCompanies,
|
||||
ProfileStatus,
|
||||
@@ -90,6 +91,18 @@ export const customersService = {
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Every portal login belonging to this customer, primary contact first.
|
||||
*
|
||||
* Not filtered to active accounts — a suspended or never-activated login is
|
||||
* exactly what staff are checking when a customer says they cannot sign in.
|
||||
*/
|
||||
accounts(companyId: string): Promise<CustomerAccount[]> {
|
||||
return apiClient
|
||||
.get<CustomerAccount[]>(URL_CONSTANTS.COMPANIES.ACCOUNTS(companyId))
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/**
|
||||
* The IAM account a reset link would go to. Read before offering the action
|
||||
* so staff see the credentials the link actually reaches, not the company's
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { api as client } from '../auth/http';
|
||||
import { URL_CONSTANTS } from '@/constants/URLS';
|
||||
import { unwrap } from '@/utils/endpoint';
|
||||
import type {
|
||||
EmptyReturnQuote,
|
||||
EmptyReturnRequest,
|
||||
EmptyReturnRequestStatus,
|
||||
PlannedEmptyReturn,
|
||||
} from '@/types/importOperations';
|
||||
|
||||
/**
|
||||
* Empty container return requests — the customer-initiated path for a booking
|
||||
* that was sold WITHOUT the return service. Staff price and approve them here;
|
||||
* the customer pays and books the truck from the portal.
|
||||
*/
|
||||
export const emptyReturnRequestsService = {
|
||||
list: async (params: {
|
||||
status?: EmptyReturnRequestStatus;
|
||||
bookingId?: string;
|
||||
} = {}): Promise<EmptyReturnRequest[]> => {
|
||||
const response = await client.get<EmptyReturnRequest[]>(
|
||||
URL_CONSTANTS.EMPTY_RETURN_REQUESTS.BASE,
|
||||
{ params },
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
/** Scheduled returns the warehouse is expecting, with date and truck. */
|
||||
planned: async (): Promise<PlannedEmptyReturn[]> => {
|
||||
const response = await client.get<PlannedEmptyReturn[]>(
|
||||
URL_CONSTANTS.EMPTY_RETURN_REQUESTS.PLANNED,
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
/** The route price staff see prefilled at approval. */
|
||||
quote: async (bookingId: string): Promise<EmptyReturnQuote> => {
|
||||
const response = await client.get<{ quote: EmptyReturnQuote }>(
|
||||
URL_CONSTANTS.EMPTY_RETURN_REQUESTS.ELIGIBILITY(bookingId),
|
||||
);
|
||||
return unwrap(response.data).quote;
|
||||
},
|
||||
|
||||
approve: async (
|
||||
id: string,
|
||||
payload: { unitAmount?: number; currency?: string } = {},
|
||||
): Promise<EmptyReturnRequest> => {
|
||||
const response = await client.post<EmptyReturnRequest>(
|
||||
URL_CONSTANTS.EMPTY_RETURN_REQUESTS.APPROVE(id),
|
||||
payload,
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
reject: async (id: string, reason: string): Promise<EmptyReturnRequest> => {
|
||||
const response = await client.post<EmptyReturnRequest>(
|
||||
URL_CONSTANTS.EMPTY_RETURN_REQUESTS.REJECT(id),
|
||||
{ reason },
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import type {
|
||||
LoadEmptyContainersOnTrainPayload,
|
||||
DjiboutiIncident,
|
||||
EmptyContainerReturn,
|
||||
EmptyReturnBooking,
|
||||
ImportCustomsFinalization,
|
||||
ImportOperationActionPayload,
|
||||
RecordDeclarationPayload,
|
||||
@@ -114,6 +115,14 @@ export const importOperationsService = {
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
/** Bookings that ship with empty-container return and still owe empties back. */
|
||||
listEmptyReturnBookings: async (): Promise<EmptyReturnBooking[]> => {
|
||||
const response = await client.get<EmptyReturnBooking[]>(
|
||||
URL_CONSTANTS.IMPORT_OPERATIONS.EMPTY_RETURN_BOOKINGS,
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
createEmptyReturn: async (
|
||||
payload: CreateEmptyContainerReturnPayload,
|
||||
): Promise<EmptyContainerReturn> => {
|
||||
@@ -124,6 +133,17 @@ export const importOperationsService = {
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
/** Backfill empties already in the yard. All-or-nothing on the server. */
|
||||
bulkCreateEmptyReturns: async (
|
||||
returns: CreateEmptyContainerReturnPayload[],
|
||||
): Promise<EmptyContainerReturn[]> => {
|
||||
const response = await client.post<EmptyContainerReturn[]>(
|
||||
URL_CONSTANTS.IMPORT_OPERATIONS.EMPTY_CONTAINER_RETURNS_BULK,
|
||||
{ returns },
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
loadEmptyContainersOnTrain: async (
|
||||
payload: LoadEmptyContainersOnTrainPayload,
|
||||
): Promise<EmptyContainerReturn[]> => {
|
||||
|
||||
@@ -33,6 +33,7 @@ import type {
|
||||
UpdateCheckpointPayload,
|
||||
DispatchSchedulePayload,
|
||||
StaffBookingWindow,
|
||||
MarshallingStop,
|
||||
ScheduleMergePreview,
|
||||
TrainScheduleDetail,
|
||||
UpdateScheduleWindowRulePayload,
|
||||
@@ -765,6 +766,24 @@ export const trainSchedulingService = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getMarshallingStops: async (scheduleId: string): Promise<MarshallingStop[]> => {
|
||||
const response = await client.get<MarshallingStop[]>(
|
||||
URL_CONSTANTS.TRAIN_SCHEDULING.MARSHALLING_STOPS(scheduleId),
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
downloadMarshallingDocumentAt: async (
|
||||
scheduleId: string,
|
||||
stopIndex: number,
|
||||
): Promise<Blob> => {
|
||||
const response = await client.get(
|
||||
URL_CONSTANTS.TRAIN_SCHEDULING.MARSHALLING_DOCUMENT_AT(scheduleId, stopIndex),
|
||||
{ responseType: "blob" },
|
||||
);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
getTrack: async (scheduleId: string): Promise<TrainTrackResponse> => {
|
||||
const response = await client.get<TrainTrackResponse>(
|
||||
URL_CONSTANTS.TRAIN_SCHEDULING.CHECKPOINTS(scheduleId),
|
||||
|
||||
@@ -5,6 +5,14 @@ import { api as apiClient } from '../auth/http';
|
||||
import { URL_CONSTANTS } from '@/constants/URLS';
|
||||
import type {
|
||||
ZoneOccupancy,
|
||||
ZoneLayout,
|
||||
ZoneSlotSummary,
|
||||
WarehouseZoneStack,
|
||||
WarehouseZoneSlot,
|
||||
SaveStackPayload,
|
||||
AvailableSlot,
|
||||
ContainerAccessibility,
|
||||
FindSlotPayload,
|
||||
TruckOnSite,
|
||||
WarehouseOpsStats,
|
||||
WarehouseThroughputPoint,
|
||||
@@ -41,6 +49,7 @@ import type {
|
||||
MoveInventoryPayload,
|
||||
StoreInventoryPayload,
|
||||
ReceiveInventoryPayload,
|
||||
RegisterBacklogContainerPayload,
|
||||
ReleaseOrderPayload,
|
||||
DeliverInventoryPayload,
|
||||
EligibleBooking,
|
||||
@@ -71,7 +80,9 @@ import type {
|
||||
WarehouseLoading,
|
||||
WarehouseYard,
|
||||
WarehouseZone,
|
||||
ZoneContentItem,
|
||||
} from '@/types/warehouse';
|
||||
import type { StationWorkLog } from '@/types/trainScheduling';
|
||||
|
||||
export type ContainerItemStage = 'PENDING' | 'RECEIVED' | 'GRN' | 'ASSIGNED' | 'LOADED' | 'LEFT' | 'DELIVERED';
|
||||
|
||||
@@ -105,6 +116,10 @@ export interface LoadableTrain {
|
||||
departureTime: string | null;
|
||||
readyCount: number;
|
||||
loadedCount: number;
|
||||
/** freight.yards.id the train departs from. */
|
||||
originStationId: string | null;
|
||||
/** The schedule's per-yard loading/unloading windows — same store the train schedule page writes. */
|
||||
stationWorkLogs: Record<string, StationWorkLog> | null;
|
||||
}
|
||||
|
||||
/** A container/cargo inventory item assigned to a train, with its allocated wagon. */
|
||||
@@ -122,6 +137,11 @@ export interface TrainLoadableItem {
|
||||
wagonId: string | null;
|
||||
wagonNumber: string | null;
|
||||
sequenceNo: number | null;
|
||||
/** The booking's boarding yard — the yard whose loading window gates this item. */
|
||||
originYardId: string | null;
|
||||
originYardLabel: string | null;
|
||||
/** True once "Start loading" was clicked for this item's boarding yard on this train. */
|
||||
loadingWindowStarted: boolean;
|
||||
loadable: boolean;
|
||||
}
|
||||
|
||||
@@ -186,7 +206,7 @@ export const warehouseService = {
|
||||
/** A booking's containers with VGM cargo weight (tonnes) for exit weighing. */
|
||||
getContainerWeights: async (
|
||||
bookingId: string,
|
||||
): Promise<Array<{ containerNumber: string; weightTons: number }>> => {
|
||||
): Promise<Array<{ containerNumber: string; weightTons: number; containerSize: string | null }>> => {
|
||||
const { data } = await apiClient.get(
|
||||
`/warehouse-inventory/bookings/${bookingId}/container-weights`,
|
||||
);
|
||||
@@ -257,6 +277,7 @@ export const warehouseService = {
|
||||
apiClient.post<Warehouse>(URL_CONSTANTS.WAREHOUSES.BASE, payload),
|
||||
update: (id: string, payload: Partial<SaveWarehousePayload>) =>
|
||||
apiClient.patch<Warehouse>(URL_CONSTANTS.WAREHOUSES.BY_ID(id), payload),
|
||||
remove: (id: string) => apiClient.delete(URL_CONSTANTS.WAREHOUSES.BY_ID(id)),
|
||||
// Yards list now returns the standard paginated envelope ({ items, meta }).
|
||||
listFacilities: () =>
|
||||
apiClient.get<{ items: WarehouseFacility[] }>(URL_CONSTANTS.RULE_ENGINE.YARDS, {
|
||||
@@ -272,6 +293,7 @@ export const warehouseService = {
|
||||
getYard: (id: string) => apiClient.get<WarehouseYard>(URL_CONSTANTS.WAREHOUSE_YARDS.BY_ID(id)),
|
||||
updateYard: (id: string, payload: Partial<SaveYardPayload>) =>
|
||||
apiClient.patch<WarehouseYard>(URL_CONSTANTS.WAREHOUSE_YARDS.BY_ID(id), payload),
|
||||
removeYard: (id: string) => apiClient.delete(URL_CONSTANTS.WAREHOUSE_YARDS.BY_ID(id)),
|
||||
|
||||
// ── Zones ──────────────────────────────────────────────────────────────
|
||||
listZones: (yardId: string) =>
|
||||
@@ -282,6 +304,40 @@ export const warehouseService = {
|
||||
getZone: (id: string) => apiClient.get<WarehouseZone>(URL_CONSTANTS.WAREHOUSE_ZONES.BY_ID(id)),
|
||||
updateZone: (id: string, payload: Partial<SaveZonePayload>) =>
|
||||
apiClient.patch<WarehouseZone>(URL_CONSTANTS.WAREHOUSE_ZONES.BY_ID(id), payload),
|
||||
removeZone: (id: string) => apiClient.delete(URL_CONSTANTS.WAREHOUSE_ZONES.BY_ID(id)),
|
||||
zoneContents: (zoneId: string) =>
|
||||
apiClient.get<ZoneContentItem[]>(`${URL_CONSTANTS.WAREHOUSE_ZONES.BY_ID(zoneId)}/contents`),
|
||||
zoneLayout: (zoneId: string) =>
|
||||
apiClient.get<ZoneLayout>(URL_CONSTANTS.WAREHOUSE_ZONES.LAYOUT(zoneId)),
|
||||
zoneSlotSummary: (zoneId: string) =>
|
||||
apiClient.get<ZoneSlotSummary>(URL_CONSTANTS.WAREHOUSE_ZONES.SLOT_SUMMARY(zoneId)),
|
||||
|
||||
// ── Ground stacks and slots ────────────────────────────────────────────
|
||||
listStacks: (zoneId: string) =>
|
||||
apiClient.get<WarehouseZoneStack[]>(URL_CONSTANTS.WAREHOUSE_ZONE_STACKS.BY_ZONE(zoneId)),
|
||||
createStack: (zoneId: string, payload: SaveStackPayload) =>
|
||||
apiClient.post<WarehouseZoneStack>(URL_CONSTANTS.WAREHOUSE_ZONE_STACKS.BASE, {
|
||||
...payload,
|
||||
zoneId,
|
||||
}),
|
||||
updateStack: (id: string, payload: Partial<SaveStackPayload>) =>
|
||||
apiClient.patch<WarehouseZoneStack>(URL_CONSTANTS.WAREHOUSE_ZONE_STACKS.BY_ID(id), payload),
|
||||
removeStack: (id: string) =>
|
||||
apiClient.delete(URL_CONSTANTS.WAREHOUSE_ZONE_STACKS.BY_ID(id)),
|
||||
updateSlot: (slotId: string, payload: { status?: string; isActive?: boolean }) =>
|
||||
apiClient.patch<WarehouseZoneSlot>(URL_CONSTANTS.WAREHOUSE_ZONE_STACKS.SLOT(slotId), payload),
|
||||
|
||||
// ── Physical placement ─────────────────────────────────────────────────
|
||||
findAvailableSlot: (payload: FindSlotPayload) =>
|
||||
apiClient.post<AvailableSlot | null>(URL_CONSTANTS.WAREHOUSE_INVENTORY.FIND_SLOT, payload),
|
||||
assignSlot: (id: string, slotId?: string) =>
|
||||
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.ASSIGN_SLOT(id), {
|
||||
slotId,
|
||||
}),
|
||||
releaseSlot: (id: string) =>
|
||||
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RELEASE_SLOT(id), {}),
|
||||
containerAccessibility: (id: string) =>
|
||||
apiClient.get<ContainerAccessibility>(URL_CONSTANTS.WAREHOUSE_INVENTORY.ACCESSIBILITY(id)),
|
||||
|
||||
// ── Inventory ──────────────────────────────────────────────────────────
|
||||
listInventory: (filter?: InventoryFilter) =>
|
||||
@@ -351,6 +407,20 @@ export const warehouseService = {
|
||||
// ── Receive (Import/Export bulk) ─────────────────────────────────────────
|
||||
eligibleBookings: (direction?: 'IMPORT' | 'EXPORT') =>
|
||||
apiClient.get<EligibleBooking[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.ELIGIBLE_BOOKINGS(direction)),
|
||||
/** Register one loaded container already in the yard but never entered. */
|
||||
registerBacklogContainer: (payload: RegisterBacklogContainerPayload) =>
|
||||
apiClient.post<WarehouseInventoryItem>(
|
||||
URL_CONSTANTS.WAREHOUSE_INVENTORY.REGISTER_BACKLOG,
|
||||
payload,
|
||||
),
|
||||
|
||||
/** Bulk backlog registration — all-or-nothing on the server. */
|
||||
registerBacklogContainersBulk: (containers: RegisterBacklogContainerPayload[]) =>
|
||||
apiClient.post<WarehouseInventoryItem[]>(
|
||||
URL_CONSTANTS.WAREHOUSE_INVENTORY.REGISTER_BACKLOG_BULK,
|
||||
{ containers },
|
||||
),
|
||||
|
||||
receiveBulk: (payload: BulkReceivePayload) =>
|
||||
apiClient.post<BulkReceiveResult>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RECEIVE_BULK, payload),
|
||||
bulkMarkInspected: (payload: BulkInspectPayload) =>
|
||||
|
||||
Reference in New Issue
Block a user