import { apiRequestBlob, apiRequestData } from "./http"; import type { RiskFlag } from "./feeControl"; export type ApOperationsExportFormat = "csv" | "xlsx"; export type ApOperationsRiskBucket = "all" | "exception" | "budget_overspend"; export type ApOperationsPendingEntityType = "reimbursement" | "travel_order"; export type FinanceLedgerStatus = "draft" | "reviewing" | "approved" | "paid" | "rejected" | "cancelled"; export type FinanceLedgerPaymentStatus = "unpaid" | "paid"; export type FinanceLedgerInvoiceStatus = "none" | "pending_ocr" | "ocr_failed" | "available" | "bound" | "archived"; export type ApPendingPaymentBatchSummary = { id: string; batchNo: string; entityType: string; name: string; status: string; totalAmount: string; highRisk: boolean; createdAt: string | null; }; export type ApFailedPaymentBatchItemSummary = { id: string; paymentBatchId: string; entityId: string; entityCode: string; entityTitle: string; amount: string; currencyCode: string; failureCode: string | null; failureMessage: string | null; createdAt: string | null; }; export type ApPartialMatchItemSummary = { id: string; travelReconciliationBatchId: string; travelOrderId: string | null; travelRequestId: string | null; matchStatus: string; resolutionStatus: string; anomalyCode: string | null; orderAmount: string | null; transactionAmount: string | null; currency: string; createdAt: string | null; }; export type ApOperationsSummary = { summary: { pendingPaymentCount: number; partialMatchCount: number; exceptionCount: number; budgetOverspendCount: number; }; pendingPaymentBatches: ApPendingPaymentBatchSummary[]; failedPaymentBatchItems: ApFailedPaymentBatchItemSummary[]; exceptionRiskFlags: RiskFlag[]; budgetOverspendRisks: RiskFlag[]; partialMatchItems: ApPartialMatchItemSummary[]; }; export async function fetchApOperations() { return apiRequestData("/api/ap-operations"); } export type FinanceLedgerItem = { id: string; formNumber: string | null; reimbursementDate: string | null; status: FinanceLedgerStatus; totalAmount: string; description: string | null; owner: { id: string; employeeNo: string; name: string } | null; submitter: { id: string; employeeNo: string; name: string } | null; department: { id: string; name: string } | null; project: { id: string; projectNo: string | null; name: string } | null; payment: { status: FinanceLedgerPaymentStatus; paymentVoucherNo: string | null; paymentDate: string | null; paymentChannel: string | null; paidAt: string | null; paidBy: { id: string; employeeNo: string; name: string } | null; }; invoice: { status: FinanceLedgerInvoiceStatus | "mixed"; counts: { total: number; pendingOcr: number; ocrFailed: number; available: number; bound: number; archived: number; usedComplete: number; }; }; attachmentCount: number; createdAt: string | null; updatedAt: string | null; }; export type FinanceLedgerQuery = { page?: number; pageSize?: number; status?: FinanceLedgerStatus; paymentStatus?: FinanceLedgerPaymentStatus; invoiceStatus?: FinanceLedgerInvoiceStatus; dateFrom?: string; dateTo?: string; q?: string; }; export type FinanceLedgerResponse = { page: number; pageSize: number; total: number; items: FinanceLedgerItem[]; }; function toQueryString(query: Record) { const params = new URLSearchParams(); for (const [key, value] of Object.entries(query)) { if (value == null || value === "") continue; params.set(key, String(value)); } const suffix = params.toString(); return suffix ? `?${suffix}` : ""; } export async function fetchFinanceLedger(query: FinanceLedgerQuery = {}) { return apiRequestData( `/api/ap-operations/reimbursement-ledger${toQueryString(query as Record)}`, ); } export async function exportApOperations(params: { format: ApOperationsExportFormat; pendingEntityType?: ApOperationsPendingEntityType; pendingCreatedAtFrom?: string; pendingCreatedAtTo?: string; riskBucket?: ApOperationsRiskBucket; ledgerStatus?: FinanceLedgerStatus; ledgerPaymentStatus?: FinanceLedgerPaymentStatus; ledgerInvoiceStatus?: FinanceLedgerInvoiceStatus; ledgerDateFrom?: string; ledgerDateTo?: string; ledgerSearch?: string; }) { return apiRequestBlob("/api/exports/ap-operations", { method: "POST", headers: { "content-type": "application/json", }, body: JSON.stringify(params), }); }