158 行
4.6 KiB
TypeScript
158 行
4.6 KiB
TypeScript
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<ApOperationsSummary>("/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<string, string | number | undefined>) {
|
|
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<FinanceLedgerResponse>(
|
|
`/api/ap-operations/reimbursement-ledger${toQueryString(query as Record<string, string | number | undefined>)}`,
|
|
);
|
|
}
|
|
|
|
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),
|
|
});
|
|
}
|