mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-02 19:43:39 +00:00
Merge branch 'dev' into freight/nati-2
Conflict in ClearanceDocumentsPage: this branch migrated the page to the pill FilterBar, dev added filters to the Select stack it replaced. Kept the FilterBar and carried dev's additions across as a "Booked by" (customerKind) FilterDef plus the shipping-line search placeholder; dev's startOfDayIso/endOfDayIso went away because dateRangeParams already does that. The Ship icon import is needed by dev's shipping-line customer cell, which merged cleanly on its own.
This commit is contained in:
@@ -39,6 +39,7 @@ import type {
|
||||
} from "@/types/fileUploadSettings";
|
||||
import type {
|
||||
Invoice,
|
||||
InvoiceCollectedSummary,
|
||||
InvoiceListFilter,
|
||||
PaginatedInvoices,
|
||||
PaginatedOfflineUsdInvoices,
|
||||
@@ -80,6 +81,8 @@ import type {
|
||||
LocomotiveRecord,
|
||||
PinWagonsPayload,
|
||||
RecordCheckpointPayload,
|
||||
UpdateCheckpointPayload,
|
||||
DispatchSchedulePayload,
|
||||
StaffBookingWindow,
|
||||
ScheduleMergePreview,
|
||||
TrainScheduleDetail,
|
||||
@@ -173,7 +176,7 @@ import { customersService } from "./customers.service";
|
||||
import { shippingLineCompaniesService } from "./shippingLineCompanies.service";
|
||||
import { shippingLineCreditsService } from "./shippingLineCredits.service";
|
||||
import { eimsService } from "./eims.service";
|
||||
import type { EimsInvoiceStatusView, EimsVerifyResult } from "@/types/eims";
|
||||
import type { EimsInvoiceStatusView, EimsModeOfPayment, EimsReceiptView, EimsVerifyResult } from "@/types/eims";
|
||||
import { invoicesService } from "./invoices.service";
|
||||
import { dropdownSettingsService } from "./dropdownSettings.service";
|
||||
import { fileUploadSettingsService } from "./fileUploadSettings.service";
|
||||
@@ -796,10 +799,13 @@ export const api = {
|
||||
],
|
||||
),
|
||||
|
||||
dispatchSchedule: endpoint<string, TrainScheduleDetail>(
|
||||
dispatchSchedule: endpoint<
|
||||
{ id: string; payload?: DispatchSchedulePayload },
|
||||
TrainScheduleDetail
|
||||
>(
|
||||
"train-scheduling",
|
||||
"dispatch-schedule",
|
||||
(id) => trainSchedulingService.dispatchSchedule(id),
|
||||
({ id, payload }) => trainSchedulingService.dispatchSchedule(id, payload),
|
||||
undefined,
|
||||
() => TRAIN_SCHEDULING_INVALIDATIONS,
|
||||
),
|
||||
@@ -917,6 +923,18 @@ export const api = {
|
||||
() => TRAIN_SCHEDULING_INVALIDATIONS,
|
||||
),
|
||||
|
||||
updateCheckpoint: endpoint<
|
||||
{ id: string; sequenceNo: number; payload: UpdateCheckpointPayload },
|
||||
TrainTrackResponse
|
||||
>(
|
||||
"train-scheduling",
|
||||
"update-checkpoint",
|
||||
({ id, sequenceNo, payload }) =>
|
||||
trainSchedulingService.updateCheckpoint(id, sequenceNo, payload),
|
||||
undefined,
|
||||
() => TRAIN_SCHEDULING_INVALIDATIONS,
|
||||
),
|
||||
|
||||
arriveSchedule: endpoint<string, TrainScheduleDetail>(
|
||||
"train-scheduling",
|
||||
"arrive-schedule",
|
||||
@@ -3137,6 +3155,16 @@ export const api = {
|
||||
({ id }) => QUERY_KEYS.INVOICES.byId(id),
|
||||
),
|
||||
|
||||
collectedSummary: endpoint<
|
||||
{ filter: Omit<InvoiceListFilter, "page" | "pageSize"> },
|
||||
InvoiceCollectedSummary
|
||||
>(
|
||||
"invoices",
|
||||
"collectedSummary",
|
||||
({ filter }) => invoicesService.collectedSummary(filter),
|
||||
({ filter }) => QUERY_KEYS.INVOICES.summary(filter),
|
||||
),
|
||||
|
||||
listOfflineUsd: endpoint<
|
||||
{ filter: InvoiceListFilter },
|
||||
PaginatedOfflineUsdInvoices
|
||||
@@ -3189,6 +3217,51 @@ export const api = {
|
||||
undefined,
|
||||
({ id }) => [QUERY_KEYS.INVOICES.eimsStatus(id), QUERY_KEYS.INVOICES.byId(id)],
|
||||
),
|
||||
|
||||
eimsCancel: endpoint<{ id: string; reasonCode: string; remark?: string }, EimsInvoiceStatusView>(
|
||||
"invoices",
|
||||
"eimsCancel",
|
||||
({ id, reasonCode, remark }) => eimsService.cancel(id, { reasonCode, remark }),
|
||||
undefined,
|
||||
({ id }) => [QUERY_KEYS.INVOICES.eimsStatus(id), QUERY_KEYS.INVOICES.byId(id)],
|
||||
),
|
||||
|
||||
eimsReceipts: endpoint<{ id: string }, EimsReceiptView[]>(
|
||||
"invoices",
|
||||
"eimsReceipts",
|
||||
({ id }) => eimsService.listReceipts(id),
|
||||
({ id }) => QUERY_KEYS.INVOICES.eimsReceipts(id),
|
||||
),
|
||||
|
||||
eimsRegisterSalesReceipt: endpoint<
|
||||
{ id: string; modeOfPayment: EimsModeOfPayment; reason?: string; collectedAmount?: number },
|
||||
EimsReceiptView
|
||||
>(
|
||||
"invoices",
|
||||
"eimsRegisterSalesReceipt",
|
||||
({ id, ...input }) => eimsService.registerSalesReceipt(id, input),
|
||||
undefined,
|
||||
({ id }) => [QUERY_KEYS.INVOICES.eimsReceipts(id)],
|
||||
),
|
||||
|
||||
eimsRegisterWithholdingReceipt: endpoint<
|
||||
{ id: string; type: string; preTaxAmount: number; withholdingAmount: number; reason?: string },
|
||||
EimsReceiptView
|
||||
>(
|
||||
"invoices",
|
||||
"eimsRegisterWithholdingReceipt",
|
||||
({ id, ...input }) => eimsService.registerWithholdingReceipt(id, input),
|
||||
undefined,
|
||||
({ id }) => [QUERY_KEYS.INVOICES.eimsReceipts(id)],
|
||||
),
|
||||
|
||||
issueMemo: endpoint<{ id: string; type: "CRE" | "DEB"; reason: string }, Invoice>(
|
||||
"invoices",
|
||||
"issueMemo",
|
||||
({ id, ...input }) => invoicesService.issueMemo(id, input),
|
||||
undefined,
|
||||
() => [QUERY_KEYS.INVOICES.ROOT],
|
||||
),
|
||||
},
|
||||
|
||||
overview: {
|
||||
|
||||
@@ -36,6 +36,8 @@ export interface BookingListFilter {
|
||||
scheduledTo?: string;
|
||||
originYardId?: string;
|
||||
destinationYardId?: string;
|
||||
/** SHIPPING_LINE = booked by a shipping line; CUSTOMER = ordinary customer company. */
|
||||
customerKind?: "SHIPPING_LINE" | "CUSTOMER";
|
||||
/** "true" = government bookings only, "false" = private only. */
|
||||
isGovernment?: "true" | "false";
|
||||
/** Free-text search: booking reference, customer name, contract reference (server-side). */
|
||||
@@ -151,6 +153,7 @@ export const bookingsService = {
|
||||
if (filter.originYardId) params.originYardId = filter.originYardId;
|
||||
if (filter.destinationYardId) params.destinationYardId = filter.destinationYardId;
|
||||
if (filter.isGovernment) params.isGovernment = filter.isGovernment;
|
||||
if (filter.customerKind) params.customerKind = filter.customerKind;
|
||||
}
|
||||
const response = await client.get<BookingListSummary>(B.LIST_SUMMARY, {
|
||||
params,
|
||||
@@ -184,6 +187,7 @@ export const bookingsService = {
|
||||
if (filter.originYardId) params.originYardId = filter.originYardId;
|
||||
if (filter.destinationYardId) params.destinationYardId = filter.destinationYardId;
|
||||
if (filter.isGovernment) params.isGovernment = filter.isGovernment;
|
||||
if (filter.customerKind) params.customerKind = filter.customerKind;
|
||||
if (filter.customsClearingEnabled)
|
||||
params.customsClearingEnabled = filter.customsClearingEnabled;
|
||||
if (filter.search) params.search = filter.search;
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { api as apiClient } from "@/auth/http";
|
||||
import { URL_CONSTANTS } from "@/constants/URLS";
|
||||
import type { EimsInvoiceStatusView, EimsVerifyResult } from "@/types/eims";
|
||||
import type {
|
||||
EimsInvoiceStatusView,
|
||||
EimsModeOfPayment,
|
||||
EimsReceiptView,
|
||||
EimsVerifyResult,
|
||||
} from "@/types/eims";
|
||||
|
||||
/**
|
||||
* MoR EIMS filing actions on an invoice.
|
||||
@@ -36,4 +41,45 @@ export const eimsService = {
|
||||
.post<EimsInvoiceStatusView>(URL_CONSTANTS.EIMS.RESOLVE(invoiceId), input)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/** Cancel the invoice's registered EIMS document. Refuses (409) an already-cancelled one. */
|
||||
cancel(
|
||||
invoiceId: string,
|
||||
input: { reasonCode: string; remark?: string },
|
||||
): Promise<EimsInvoiceStatusView> {
|
||||
return apiClient
|
||||
.post<EimsInvoiceStatusView>(URL_CONSTANTS.EIMS.CANCEL(invoiceId), input)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
registerSalesReceipt(
|
||||
invoiceId: string,
|
||||
input: { modeOfPayment: EimsModeOfPayment; reason?: string; collectedAmount?: number },
|
||||
): Promise<EimsReceiptView> {
|
||||
return apiClient
|
||||
.post<EimsReceiptView>(URL_CONSTANTS.EIMS.RECEIPT_SALES(invoiceId), input)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
registerWithholdingReceipt(
|
||||
invoiceId: string,
|
||||
input: { type: string; preTaxAmount: number; withholdingAmount: number; reason?: string },
|
||||
): Promise<EimsReceiptView> {
|
||||
return apiClient
|
||||
.post<EimsReceiptView>(URL_CONSTANTS.EIMS.RECEIPT_WITHHOLDING(invoiceId), input)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
listReceipts(invoiceId: string): Promise<EimsReceiptView[]> {
|
||||
return apiClient
|
||||
.get<EimsReceiptView[]>(URL_CONSTANTS.EIMS.RECEIPTS(invoiceId))
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/** Blob download — same pattern as `invoicesService.downloadDocument`. */
|
||||
downloadReceiptDocument(invoiceId: string, receiptId: string) {
|
||||
return apiClient.get<Blob>(URL_CONSTANTS.EIMS.RECEIPT_DOCUMENT(invoiceId, receiptId), {
|
||||
responseType: "blob",
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ import { api as apiClient } from "@/auth/http";
|
||||
import { URL_CONSTANTS } from "@/constants/URLS";
|
||||
import type {
|
||||
Invoice,
|
||||
InvoiceCollectedSummary,
|
||||
InvoiceListFilter,
|
||||
PaginatedInvoices,
|
||||
PaginatedOfflineUsdInvoices,
|
||||
@@ -23,19 +24,42 @@ export const invoicesService = {
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/** Total collected (paidAmount) across every filtered invoice, by currency. */
|
||||
collectedSummary(
|
||||
filter: Omit<InvoiceListFilter, "page" | "pageSize">,
|
||||
): Promise<InvoiceCollectedSummary> {
|
||||
return apiClient
|
||||
.get<InvoiceCollectedSummary>(URL_CONSTANTS.BILLING.INVOICES_SUMMARY, {
|
||||
params: cleanParams(filter),
|
||||
})
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
getById(id: string): Promise<Invoice> {
|
||||
return apiClient
|
||||
.get<Invoice>(URL_CONSTANTS.BILLING.INVOICE_BY_ID(id))
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
downloadDocument(id: string) {
|
||||
/** `format` omitted or "a4" → standard A4 PDF; "thermal" → 80mm thermal layout (ADD-P001). */
|
||||
downloadDocument(id: string, format?: "a4" | "thermal") {
|
||||
return apiClient.get<Blob>(URL_CONSTANTS.BILLING.INVOICE_DOCUMENT(id), {
|
||||
responseType: "blob",
|
||||
params: format ? { format } : undefined,
|
||||
});
|
||||
},
|
||||
|
||||
/** Finance worklist: USD invoices awaiting bank-transfer confirmation. */
|
||||
/** Issue a credit/debit memo against a registered invoice (MoR DEB/CRE) — filing-equivalent. */
|
||||
issueMemo(
|
||||
id: string,
|
||||
input: { type: "CRE" | "DEB"; reason: string },
|
||||
): Promise<Invoice> {
|
||||
return apiClient
|
||||
.post<Invoice>(URL_CONSTANTS.BILLING.INVOICE_MEMO(id), input)
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/** Finance worklist: USD and ETB invoices awaiting manual payment confirmation. */
|
||||
listOfflineUsd(
|
||||
filter: InvoiceListFilter,
|
||||
): Promise<PaginatedOfflineUsdInvoices> {
|
||||
@@ -46,7 +70,7 @@ export const invoicesService = {
|
||||
.then((r) => r.data);
|
||||
},
|
||||
|
||||
/** Confirm a USD invoice paid by bank transfer — the slip file is required. */
|
||||
/** Confirm an invoice (USD or ETB) paid manually — the slip file is required. */
|
||||
confirmOffline(id: string, file: File, reference?: string): Promise<Invoice> {
|
||||
const body = new FormData();
|
||||
body.append("file", file);
|
||||
|
||||
@@ -28,6 +28,8 @@ import type {
|
||||
LocomotiveRecord,
|
||||
PinWagonsPayload,
|
||||
RecordCheckpointPayload,
|
||||
UpdateCheckpointPayload,
|
||||
DispatchSchedulePayload,
|
||||
StaffBookingWindow,
|
||||
ScheduleMergePreview,
|
||||
TrainScheduleDetail,
|
||||
@@ -524,10 +526,11 @@ export const trainSchedulingService = {
|
||||
|
||||
dispatchSchedule: async (
|
||||
scheduleId: string,
|
||||
payload: DispatchSchedulePayload = {},
|
||||
): Promise<TrainScheduleDetail> => {
|
||||
const response = await client.post<TrainScheduleDetail>(
|
||||
URL_CONSTANTS.TRAIN_SCHEDULING.DISPATCH(scheduleId),
|
||||
{},
|
||||
payload,
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
@@ -696,6 +699,18 @@ export const trainSchedulingService = {
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
updateCheckpoint: async (
|
||||
scheduleId: string,
|
||||
sequenceNo: number,
|
||||
payload: UpdateCheckpointPayload,
|
||||
): Promise<TrainTrackResponse> => {
|
||||
const response = await client.patch<TrainTrackResponse>(
|
||||
URL_CONSTANTS.TRAIN_SCHEDULING.CHECKPOINT(scheduleId, sequenceNo),
|
||||
payload,
|
||||
);
|
||||
return unwrap(response.data);
|
||||
},
|
||||
|
||||
arriveSchedule: async (scheduleId: string): Promise<TrainScheduleDetail> => {
|
||||
const response = await client.post<TrainScheduleDetail>(
|
||||
URL_CONSTANTS.TRAIN_SCHEDULING.ARRIVE(scheduleId),
|
||||
|
||||
@@ -26,6 +26,9 @@ export interface Wagon {
|
||||
lengthMeters?: number;
|
||||
} | null;
|
||||
status: Freight.WagonStatus;
|
||||
/** Latest status-log flip to MAINTENANCE / to AVAILABLE (list endpoint only). */
|
||||
lastMaintenanceAt?: string | null;
|
||||
lastAvailableAt?: string | null;
|
||||
currentYardId: string | null;
|
||||
currentYard?: { id: string; label?: string; code?: string } | null;
|
||||
/** Odd EXPORT run (Ethiopia → Djibouti); null when the wagon is not on a run. */
|
||||
|
||||
Reference in New Issue
Block a user