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; canRetryErp: 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 TravelReconciliationErpTrack = { batch: { id: string; batchNo: string; status: string; periodStart: string | null; periodEnd: string | null; legalEntity: { id: string; code: string; name: string; } | null; costCenter: { id: string; code: string; name: string; } | null; }; stage: { summaryExport: { eligible: boolean; reason: string | null; status: string; preview: { exportType: string; entityType: string; entityId: string | null; batchNo: string | null; occurredAt: string | null; }; outbox: { id: string; eventType: string; status: string; attempts: number; lastError: string | null; processedAt: string | null; updatedAt: string | null; } | null; bindings: { payableSummary: { id: string; provider: string; entityType: string; localId: string; externalId: string | null; externalCode: string | null; lastSyncedAt: string | null; } | null; settlementBatch: { id: string; provider: string; entityType: string; localId: string; externalId: string | null; externalCode: string | null; lastSyncedAt: string | null; } | null; }; }; }; permissions: { canRetry: boolean; canSeeExternalRefs: boolean; }; }; export type TravelReconciliationFinalizeResponse = { batchId: string; batchNo: string; status: TravelReconciliationBatchStatus; erpExport: { eventType: string; outboxId: string | null; duplicated: boolean; requeued: boolean; enqueued: boolean; }; }; 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({}), }, ); } export async function fetchTravelReconciliationErpTrack(batchId: string) { return apiRequestData( `/api/integrations/erp/travel/reconciliation-batches/${batchId}/track`, ); } export async function requeueTravelReconciliationBatch(batchId: string) { return apiRequestData( `/api/integrations/erp/travel/reconciliation-batches/${batchId}/requeue`, { method: 'POST', body: JSON.stringify({}), }, ); }