Files
edr-platform/apps/edr-freight-web/backoffice/src/services/warehouse.service.ts
Marshal 4b7f6d2548 enhance contract and booking services with server-side search and validation improvements
- Added  parameter to  and  for server-side free-text search on contract reference, company name, and booking details.
- Introduced new validation errors in  for container clashes and space issues when creating bookings.
- Implemented paginated dropdown settings retrieval in .
- Updated  to fetch active yards using a new method that handles pagination.
- Enhanced  with a  method to fetch all records by walking through pages.
- Refactored  to support filtering and pagination in schedule listings.
- Improved  to return a paginated list of facilities.
- Updated UI components in  and  to utilize debounced search inputs for better performance.
- Added alerts in  to inform users about booking constraints related to splits and capacity.
- Enhanced  to display notifications for split bookings and capacity usage.
2026-07-12 10:51:31 +00:00

454 lines
21 KiB
TypeScript

import type { Freight } from '@edr/types';
import { api as apiClient } from '../auth/http';
import { URL_CONSTANTS } from '@/constants/URLS';
import type {
AllocationCriteria,
AllocationPreviewResult,
AllocationRule,
ArrivalQueueItem,
AutoLoadResult,
AutoUnloadResult,
FeePreview,
FeeRule,
InspectionAttachment,
InspectionReport,
InspectionReportPayload,
SaveAllocationRulePayload,
SaveFeeRulePayload,
WarehouseFeeInvoice,
WarehouseInvoiceFilter,
PayInvoicePayload,
InitiateWarehouseInvoicePaymentPayload,
WarehouseInvoicePaymentResponse,
BookingScheduleView,
InventoryFilter,
InventoryInquiryFilter,
InventoryInquiryResult,
InventoryMovement,
LoadableWagon,
LoadInventoryPayload,
MoveInventoryPayload,
StoreInventoryPayload,
ReceiveInventoryPayload,
ReleaseOrderPayload,
DeliverInventoryPayload,
EligibleBooking,
BulkReceivePayload,
BulkReceiveResult,
BulkInspectPayload,
BulkInspectResult,
ReadyToLoadRow,
BulkDispatchResult,
AutoUnloadExportDjiboutiResult,
ExportTrain,
ExportTrainItem,
ImportTrain,
ImportTrainItem,
ImportUnloadedItem,
AutoUnloadArrivedResult,
ReserveInventoryPayload,
SaveWarehousePayload,
SaveYardPayload,
SaveZonePayload,
Warehouse,
WarehouseActivityLog,
WarehouseDashboard,
WarehouseFacility,
WarehouseFilter,
WarehouseInventoryItem,
WarehouseLoading,
WarehouseYard,
WarehouseZone,
} from '@/types/warehouse';
export type ContainerItemStage = 'PENDING' | 'RECEIVED' | 'GRN' | 'ASSIGNED' | 'LOADED' | 'LEFT' | 'DELIVERED';
export interface ContainerItem {
containerNumber: string;
goods: string | null;
stage: ContainerItemStage;
grnNumber: string | null;
truckAssignmentId: string | null;
truckPlate: string | null;
truckArrived: boolean;
truckLeft: boolean;
/** Operator has loaded this container onto the truck (customer assignment alone is not "loaded"). */
loaded: boolean;
bookingReference: string | null;
contractId: string | null;
hasLastMile: boolean;
handoverSigned: boolean;
}
/** A pre-dispatch EXPORT train that has inventory waiting to be loaded. */
export interface LoadableTrain {
scheduleId: string;
trainNumber: string | null;
origin: string | null;
destination: string | null;
status: string;
departureTime: string | null;
readyCount: number;
loadedCount: number;
}
/** A container/cargo inventory item assigned to a train, with its allocated wagon. */
export interface TrainLoadableItem {
id: string;
bookingId: string | null;
bookingReference: string | null;
customerName: string | null;
containerNumber: string | null;
cargoType: string | null;
weight: number | null;
grnNumber: string | null;
inspectionStatus: string | null;
status: string;
wagonId: string | null;
wagonNumber: string | null;
sequenceNo: number | null;
loadable: boolean;
}
export interface TrainLoadResult {
loadedCount: number;
skippedCount: number;
results: { inventoryId: string; status: string; reason?: string }[];
}
const cleanParams = (params: object) =>
Object.fromEntries(
Object.entries(params).filter(([, value]) => value !== undefined && value !== '' && value !== null),
);
export const warehouseService = {
/** Customer self-haul trucks assigned to a booking (portal multi-truck). */
getCustomerTrucks: async (bookingId: string): Promise<Freight.ICustomerTruck[]> => {
const { data } = await apiClient.get(`/bookings/${bookingId}/customer-trucks`);
return data?.data ?? data ?? [];
},
/** Per-container/bulk items of a booking with lifecycle stage + refs. */
getContainerItems: async (bookingId: string): Promise<ContainerItem[]> => {
const { data } = await apiClient.get(
`/warehouse-inventory/bookings/${bookingId}/container-items`,
);
return data?.data ?? data ?? [];
},
/** Ask the customer to sign the booking's handover (creates one if none, then notifies). */
requestHandoverSignature: async (
bookingId: string,
): Promise<{ notified: boolean; reference: string | null; alreadySigned: boolean }> => {
const { data } = await apiClient.post(
`/warehouse-inventory/bookings/${bookingId}/request-handover-signature`,
);
return data?.data ?? data;
},
/** A booking's containers with VGM cargo weight (tonnes) for exit weighing. */
getContainerWeights: async (
bookingId: string,
): Promise<Array<{ containerNumber: string; weightTons: number }>> => {
const { data } = await apiClient.get(
`/warehouse-inventory/bookings/${bookingId}/container-weights`,
);
return data?.data ?? data ?? [];
},
/** Booking container numbers not yet loaded onto any truck. */
getLoadableContainers: async (bookingId: string): Promise<string[]> => {
const { data } = await apiClient.get(
`/bookings/${bookingId}/customer-trucks/loadable-containers`,
);
return data?.data ?? data ?? [];
},
/** Truck_dispatch: load selected containers onto a truck (after arrival). */
loadTruck: async (
bookingId: string,
assignmentId: string,
containerNumbers: string[],
): Promise<Freight.ICustomerTruck[]> => {
const { data } = await apiClient.post(
`/bookings/${bookingId}/customer-trucks/${assignmentId}/load`,
{ containerNumbers },
);
return data?.data ?? data ?? [];
},
// ── Load to Train ─────────────────────────────────────────────────────────
/** Pre-dispatch EXPORT trains with inventory waiting to be loaded. */
getLoadableTrains: async (): Promise<LoadableTrain[]> => {
const { data } = await apiClient.get('/warehouse-inventory/loadable-trains');
return data?.data ?? data ?? [];
},
/** Container/cargo items assigned to a train, with allocated wagon + stage. */
getTrainLoadableItems: async (scheduleId: string): Promise<TrainLoadableItem[]> => {
const { data } = await apiClient.get(
`/warehouse-inventory/train/${scheduleId}/loadable-items`,
);
return data?.data ?? data ?? [];
},
/** Load selected inventory items onto their allocated wagons for a train. */
loadItemsOntoTrain: async (
scheduleId: string,
inventoryIds: string[],
): Promise<TrainLoadResult> => {
const { data } = await apiClient.post(
`/warehouse-inventory/train/${scheduleId}/load`,
{ inventoryIds },
);
return data?.data ?? data ?? { loadedCount: 0, skippedCount: 0, results: [] };
},
// ── Warehouses ──────────────────────────────────────────────────────────
list: (filter?: WarehouseFilter) =>
apiClient.get<Warehouse[]>(URL_CONSTANTS.WAREHOUSES.BASE, {
params: cleanParams(filter ?? {}),
}),
dashboard: () => apiClient.get<WarehouseDashboard>(URL_CONSTANTS.WAREHOUSES.DASHBOARD),
getDashboardSummary: (_filter?: InventoryFilter) =>
apiClient.get<WarehouseDashboard>(URL_CONSTANTS.WAREHOUSES.DASHBOARD),
getById: (id: string) => apiClient.get<Warehouse>(URL_CONSTANTS.WAREHOUSES.BY_ID(id)),
create: (payload: SaveWarehousePayload) =>
apiClient.post<Warehouse>(URL_CONSTANTS.WAREHOUSES.BASE, payload),
update: (id: string, payload: Partial<SaveWarehousePayload>) =>
apiClient.patch<Warehouse>(URL_CONSTANTS.WAREHOUSES.BY_ID(id), payload),
// Yards list now returns the standard paginated envelope ({ items, meta }).
listFacilities: () =>
apiClient.get<{ items: WarehouseFacility[] }>(URL_CONSTANTS.RULE_ENGINE.YARDS, {
params: { pageSize: 100 },
}),
// ── Yards ────────────────────────────────────────────────────────────────
listYards: (warehouseId: string) =>
apiClient.get<WarehouseYard[]>(URL_CONSTANTS.WAREHOUSES.YARDS(warehouseId)),
listAllYards: () => apiClient.get<WarehouseYard[]>(URL_CONSTANTS.WAREHOUSE_YARDS.BASE),
createYard: (warehouseId: string, payload: SaveYardPayload) =>
apiClient.post<WarehouseYard>(URL_CONSTANTS.WAREHOUSES.YARDS(warehouseId), payload),
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),
// ── Zones ──────────────────────────────────────────────────────────────
listZones: (yardId: string) =>
apiClient.get<WarehouseZone[]>(URL_CONSTANTS.WAREHOUSE_YARDS.ZONES(yardId)),
listAllZones: () => apiClient.get<WarehouseZone[]>(URL_CONSTANTS.WAREHOUSE_ZONES.BASE),
createZone: (yardId: string, payload: SaveZonePayload) =>
apiClient.post<WarehouseZone>(URL_CONSTANTS.WAREHOUSE_YARDS.ZONES(yardId), payload),
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),
// ── Inventory ──────────────────────────────────────────────────────────
listInventory: (filter?: InventoryFilter) =>
apiClient.get<WarehouseInventoryItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.BASE, {
params: cleanParams(filter ?? {}),
}),
receiveInventory: (payload: ReceiveInventoryPayload) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RECEIVE, payload),
listReadyForLoading: (filter?: InventoryFilter) =>
apiClient.get<WarehouseInventoryItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.READY_FOR_LOADING, {
params: cleanParams(filter ?? {}),
}),
inquiry: (filter: InventoryInquiryFilter) =>
apiClient.get<InventoryInquiryResult[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.INQUIRY, {
params: cleanParams(filter ?? {}),
}),
// ── Lifecycle (Batch 2) ──────────────────────────────────────────────────
store: (id: string, payload?: StoreInventoryPayload) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.STORE(id), payload),
reserve: (payload: ReserveInventoryPayload) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RESERVE, payload),
markReadyForLoading: (id: string) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.MARK_READY(id)),
load: (id: string, payload: LoadInventoryPayload) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.LOAD(id), payload),
dispatch: (id: string) =>
apiClient.patch<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.DISPATCH(id)),
// ── Import branch (READY_FOR_PICKUP → DELIVERED) ─────────────────────────
markReadyForPickup: (id: string) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.MARK_READY_PICKUP(id)),
release: (id: string, payload: ReleaseOrderPayload) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RELEASE(id), payload),
downloadReleaseDocument: (id: string) =>
apiClient.get<Blob>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RELEASE_DOCUMENT(id), {
responseType: 'blob',
}),
downloadGrnDocument: (id: string) =>
apiClient.get<Blob>(URL_CONSTANTS.WAREHOUSE_INVENTORY.GRN_DOCUMENT(id), {
responseType: 'blob',
}),
downloadHandoverDocument: (id: string) =>
apiClient.get<Blob>(URL_CONSTANTS.WAREHOUSE_INVENTORY.HANDOVER_DOCUMENT(id), {
responseType: 'blob',
}),
/** Per-truck exit paper PDF (containers loaded on one customer truck). */
downloadTruckExitPaper: (assignmentId: string) =>
apiClient.get<Blob>(`/warehouse-inventory/customer-truck-exit-paper/${assignmentId}`, {
responseType: 'blob',
}),
deliver: (id: string, payload: DeliverInventoryPayload) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.DELIVER(id), payload),
// ── Receive (Import/Export bulk) ─────────────────────────────────────────
eligibleBookings: (direction?: 'IMPORT' | 'EXPORT') =>
apiClient.get<EligibleBooking[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.ELIGIBLE_BOOKINGS(direction)),
receiveBulk: (payload: BulkReceivePayload) =>
apiClient.post<BulkReceiveResult>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RECEIVE_BULK, payload),
bulkMarkInspected: (payload: BulkInspectPayload) =>
apiClient.post<BulkInspectResult>(URL_CONSTANTS.WAREHOUSE_INVENTORY.BULK_MARK_INSPECTED, payload),
receivedExport: () =>
apiClient.get<ReadyToLoadRow[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.RECEIVED_EXPORT),
readyToLoadExport: () =>
apiClient.get<ReadyToLoadRow[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.READY_TO_LOAD_EXPORT),
loadedExport: () =>
apiClient.get<ReadyToLoadRow[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.LOADED_EXPORT),
bulkDispatchExport: (inventoryIds: string[]) =>
apiClient.post<BulkDispatchResult>(URL_CONSTANTS.WAREHOUSE_INVENTORY.BULK_DISPATCH_EXPORT, {
inventoryIds,
}),
importArriveQueue: () =>
apiClient.get<ImportTrain[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_ARRIVE_QUEUE),
importTrainItems: (scheduleId: string) =>
apiClient.get<ImportTrainItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_TRAIN_ITEMS(scheduleId)),
autoUnloadArrivedBookings: (payload: {
scheduleId: string;
warehouseId?: string;
assignments?: { bookingId: string; warehouseId: string; yardId: string; zoneId: string }[];
}) =>
apiClient.post<AutoUnloadArrivedResult>(
URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_AUTO_UNLOAD_ARRIVED,
payload,
),
importUnloadedQueue: () =>
apiClient.get<ImportUnloadedItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_UNLOADED_QUEUE),
importPickupReadyQueue: () =>
apiClient.get<ImportUnloadedItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.IMPORT_PICKUP_READY_QUEUE),
exportDjiboutiArrivalQueue: () =>
apiClient.get<ExportTrain[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.EXPORT_DJIBOUTI_ARRIVAL_QUEUE),
exportDjiboutiTrainItems: (scheduleId: string) =>
apiClient.get<ExportTrainItem[]>(
URL_CONSTANTS.WAREHOUSE_INVENTORY.EXPORT_DJIBOUTI_TRAIN_ITEMS(scheduleId),
),
autoUnloadExportAtDjibouti: (scheduleId: string) =>
apiClient.post<AutoUnloadExportDjiboutiResult>(
URL_CONSTANTS.WAREHOUSE_INVENTORY.EXPORT_AUTO_UNLOAD_AT_DJIBOUTI,
{ scheduleId },
),
move: (id: string, payload: MoveInventoryPayload) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVENTORY.MOVE(id), payload),
movements: (id: string) =>
apiClient.get<InventoryMovement[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.MOVEMENTS(id)),
activity: (id: string) =>
apiClient.get<WarehouseActivityLog[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.ACTIVITY(id)),
// ── Loading (Batch 3) ─────────────────────────────────────────────────────
loadableWagons: () =>
apiClient.get<LoadableWagon[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.LOADABLE_WAGONS),
bookingSchedule: (bookingId: string) =>
apiClient.get<BookingScheduleView>(URL_CONSTANTS.WAREHOUSE_INVENTORY.BOOKING_SCHEDULE(bookingId)),
inventoryLoadings: (id: string) =>
apiClient.get<WarehouseLoading[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.LOADINGS(id)),
loadings: (params?: { bookingId?: string; wagonId?: string }) =>
apiClient.get<WarehouseLoading[]>(URL_CONSTANTS.WAREHOUSE_LOADINGS.BASE, {
params: cleanParams(params ?? {}),
}),
// ── Batch 4.5: Arrival / Unload / Load automation ──────────────────────────
arrivalQueue: () =>
apiClient.get<ArrivalQueueItem[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.ARRIVAL_QUEUE),
autoUnloadArrived: () =>
apiClient.post<AutoUnloadResult>(URL_CONSTANTS.WAREHOUSE_INVENTORY.AUTO_UNLOAD_ARRIVED),
autoLoadReady: () =>
apiClient.post<AutoLoadResult>(URL_CONSTANTS.WAREHOUSE_INVENTORY.AUTO_LOAD_READY),
unloadBooking: (bookingId: string, payload?: Record<string, unknown>) =>
apiClient.post<WarehouseInventoryItem>(
URL_CONSTANTS.WAREHOUSE_INVENTORY.UNLOAD_BOOKING(bookingId),
payload ?? {},
),
// ── Batch 4.5: Inspection reports ──────────────────────────────────────────
listInspectionReports: (inventoryId: string) =>
apiClient.get<InspectionReport[]>(URL_CONSTANTS.WAREHOUSE_INVENTORY.INSPECTION_REPORTS(inventoryId)),
createInspectionReport: (inventoryId: string, payload: InspectionReportPayload) =>
apiClient.post<InspectionReport>(
URL_CONSTANTS.WAREHOUSE_INVENTORY.INSPECTION_REPORTS(inventoryId),
payload,
),
getInspectionReport: (id: string) =>
apiClient.get<InspectionReport>(URL_CONSTANTS.WAREHOUSE_INSPECTION.BY_ID(id)),
uploadInspectionAttachments: (id: string, files: File[]) => {
const form = new FormData();
files.forEach((file) => form.append('files', file));
return apiClient.post<InspectionAttachment[]>(
URL_CONSTANTS.WAREHOUSE_INSPECTION.ATTACHMENTS(id),
form,
{ headers: { 'Content-Type': 'multipart/form-data' } },
);
},
// ── Batch 5: Allocation + Fee rules / previews ─────────────────────────────
listAllocationRules: () =>
apiClient.get<AllocationRule[]>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION),
createAllocationRule: (payload: SaveAllocationRulePayload) =>
apiClient.post<AllocationRule>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION, payload),
updateAllocationRule: (id: string, payload: Partial<SaveAllocationRulePayload>) =>
apiClient.patch<AllocationRule>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION_BY_ID(id), payload),
deleteAllocationRule: (id: string) =>
apiClient.delete(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION_BY_ID(id)),
previewAllocation: (criteria: AllocationCriteria) =>
apiClient.post<AllocationPreviewResult | null>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION_PREVIEW, criteria),
listFeeRules: () => apiClient.get<FeeRule[]>(URL_CONSTANTS.WAREHOUSE_RULES.FEES),
createFeeRule: (payload: SaveFeeRulePayload) =>
apiClient.post<FeeRule>(URL_CONSTANTS.WAREHOUSE_RULES.FEES, payload),
updateFeeRule: (id: string, payload: Partial<SaveFeeRulePayload>) =>
apiClient.patch<FeeRule>(URL_CONSTANTS.WAREHOUSE_RULES.FEES_BY_ID(id), payload),
deleteFeeRule: (id: string) => apiClient.delete(URL_CONSTANTS.WAREHOUSE_RULES.FEES_BY_ID(id)),
feePreview: (inventoryId: string, billingCurrency?: 'ETB' | 'USD') =>
apiClient.get<FeePreview[]>(URL_CONSTANTS.WAREHOUSE_RULES.FEE_PREVIEW(inventoryId), {
params: cleanParams({ billingCurrency }),
}),
// ── Batch 6: Warehouse fee invoices ────────────────────────────────────────
listInvoices: (filter?: WarehouseInvoiceFilter) =>
apiClient.get<WarehouseFeeInvoice[]>(URL_CONSTANTS.WAREHOUSE_INVOICES.BASE, {
params: cleanParams(filter ?? {}),
}),
getInvoice: (id: string) =>
apiClient.get<WarehouseFeeInvoice>(URL_CONSTANTS.WAREHOUSE_INVOICES.BY_ID(id)),
downloadInvoiceDocument: (id: string) =>
apiClient.get<Blob>(URL_CONSTANTS.WAREHOUSE_INVOICES.DOCUMENT(id), {
responseType: 'blob',
}),
downloadInvoiceReceipt: (id: string) =>
apiClient.get<Blob>(URL_CONSTANTS.WAREHOUSE_INVOICES.RECEIPT(id), {
responseType: 'blob',
}),
invoicesForInventory: (inventoryId: string) =>
apiClient.get<WarehouseFeeInvoice[]>(URL_CONSTANTS.WAREHOUSE_INVOICES.FOR_INVENTORY(inventoryId)),
invoicesForBooking: (bookingId: string) =>
apiClient.get<WarehouseFeeInvoice[]>(URL_CONSTANTS.WAREHOUSE_INVOICES.FOR_BOOKING(bookingId)),
generateInvoice: (inventoryId: string, confirmZero = false, billingCurrency?: 'ETB' | 'USD') =>
apiClient.post<WarehouseFeeInvoice>(URL_CONSTANTS.WAREHOUSE_INVOICES.GENERATE(inventoryId), {
confirmZero,
billingCurrency,
}),
cancelInvoice: (id: string) =>
apiClient.patch<WarehouseFeeInvoice>(URL_CONSTANTS.WAREHOUSE_INVOICES.CANCEL(id)),
payInvoice: (id: string, payload: PayInvoicePayload) =>
apiClient.post<WarehouseFeeInvoice>(URL_CONSTANTS.WAREHOUSE_INVOICES.PAY(id), payload),
payInvoiceOnline: (id: string, payload: InitiateWarehouseInvoicePaymentPayload) =>
apiClient.post<WarehouseInvoicePaymentResponse>(URL_CONSTANTS.WAREHOUSE_INVOICES.PAY_ONLINE(id), payload),
gateClearance: (inventoryId: string) =>
apiClient.post<WarehouseInventoryItem>(URL_CONSTANTS.WAREHOUSE_INVOICES.GATE_CLEARANCE(inventoryId), {}),
};