import { apiRequestData } from './http'; export type TravelReconciliationBatchStatus = 'draft' | 'reconciled'; export type TravelReconciliationItemKind = 'order' | 'orphan_transaction'; export type TravelReconciliationMatchStatus = | 'matched' | 'amount_mismatch' | 'currency_mismatch' | 'missing_transaction' | 'duplicate_charge' | 'orphan_transaction' | 'reversed_after_payment'; export type TravelReconciliationResolutionStatus = 'pending' | 'confirmed'; export type TravelReconciliationSummary = { totalOrders: number; totalTransactions: number; matchedItems: number; pendingItems: number; anomalyItems: number; totalOrderAmount: string; totalSettledAmount: string; totalReversedAmount: string; matchRate: string; }; export type TravelReconciliationBatchSummary = { id: string; batchNo: string; legalEntityId: string; costCenterId: string | null; periodStart: string | null; periodEnd: string | null; status: TravelReconciliationBatchStatus; summary: TravelReconciliationSummary; confirmedAt: string | null; confirmedBy: string | null; createdAt: string | null; updatedAt: string | null; legalEntity: { id: string; code: string; name: string; } | null; costCenter: { id: string; code: string; name: string; legalEntityId: string; } | null; confirmedByEmployee: { id: string; employeeNo: string; name: string; } | null; }; export type TravelReconciliationItem = { id: string; itemKind: TravelReconciliationItemKind; travelRequestId: string | null; travelOrderId: string | null; cardTransactionId: string | null; matchStatus: TravelReconciliationMatchStatus; resolutionStatus: TravelReconciliationResolutionStatus; orderAmount: string | null; transactionAmount: string | null; currency: string | null; anomalyCode: string | null; confirmedAt: string | null; confirmedBy: string | null; confirmNote: string | null; snapshot: { requestNo: string | null; externalOrderNo: string | null; externalTxnId: string | null; travelOrderStatus: string | null; transactionStatus: string | null; linkedReimbursementId: string | null; boundInvoiceCount: number; boundInvoiceAmount: string; matchedTransactionIds: string[]; reasonDetail: string | null; }; travelRequest: { id: string; requestNo: string | null; } | null; travelOrder: { id: string; externalOrderNo: string; status: string; } | null; cardTransaction: { id: string; externalTxnId: string; status: string; txnType: string; } | null; actions: { canConfirm: boolean; canReopen: boolean; }; }; export type TravelReconciliationBatchDetail = TravelReconciliationBatchSummary & { items: TravelReconciliationItem[]; permissions: { canManagePayments: boolean; }; }; export type TravelReconciliationListResponse = { page: number; pageSize: number; total: number; items: TravelReconciliationBatchSummary[]; }; export type TravelReconciliationListQuery = { status?: TravelReconciliationBatchStatus; legalEntityId?: string; costCenterId?: string; periodStart?: string; periodEnd?: string; page?: number; pageSize?: number; }; export type TravelReconciliationCreatePayload = { legalEntityId: string; costCenterId?: string; periodStart: string; periodEnd: string; }; export type TravelReconciliationFinalizeResponse = { batchId: string; batchNo: string; status: TravelReconciliationBatchStatus; }; export type TravelReconciliationRequeueResponse = { outboxId: string; status: string; duplicated: boolean; requeued: boolean; eventType: string; }; function toQueryString(query: Record) { const params = new URLSearchParams(); for (const [key, value] of Object.entries(query)) { if (value == null || value === '') { continue; } params.set(key, typeof value === 'boolean' ? (value ? '1' : '0') : String(value)); } const encoded = params.toString(); return encoded ? `?${encoded}` : ''; } export async function fetchTravelReconciliationBatches(query: TravelReconciliationListQuery = {}) { return apiRequestData( `/api/travel/reconciliation/batches${toQueryString(query as Record)}`, ); } export async function createTravelReconciliationBatch(payload: TravelReconciliationCreatePayload) { return apiRequestData('/api/travel/reconciliation/batches', { method: 'POST', body: JSON.stringify(payload), }); } export async function fetchTravelReconciliationBatchDetail(batchId: string) { return apiRequestData(`/api/travel/reconciliation/batches/${batchId}`); } export async function confirmTravelReconciliationItem(itemId: string, payload: { note?: string | null } = {}) { return apiRequestData<{ batchId: string; summary: TravelReconciliationSummary; item: TravelReconciliationItem }>( `/api/travel/reconciliation/items/${itemId}/confirm`, { method: 'POST', body: JSON.stringify(payload), }, ); } export async function reopenTravelReconciliationItem(itemId: string) { return apiRequestData<{ batchId: string; summary: TravelReconciliationSummary; item: TravelReconciliationItem }>( `/api/travel/reconciliation/items/${itemId}/reopen`, { method: 'POST', body: JSON.stringify({}), }, ); } export async function finalizeTravelReconciliationBatch(batchId: string) { return apiRequestData( `/api/travel/reconciliation/batches/${batchId}/finalize`, { method: 'POST', body: JSON.stringify({}), }, ); }