179 行
5.8 KiB
TypeScript
179 行
5.8 KiB
TypeScript
import { apiRequestBlob, apiRequestData, type ApiBlobResponse } from "./http";
|
|
import { buildAttachmentFileUrl, type AttachmentGovernanceFields } from "../lib/attachmentGovernance";
|
|
import type { RiskFlag } from "./feeControl";
|
|
|
|
export type PaymentBatchEntityType = "reimbursement" | "travel_order";
|
|
export type PaymentBatchStatus = "draft" | "reviewed" | "executed" | "failed" | "cancelled";
|
|
export type PaymentChannel = "bank" | "alipay" | "wechat" | "cash" | "other";
|
|
|
|
export type PaymentBatchItem = {
|
|
id: string;
|
|
entityId: string;
|
|
entityCode: string;
|
|
entityTitle: string;
|
|
amount: string;
|
|
currencyCode: string;
|
|
paymentVoucherNoOverride: string | null;
|
|
paymentDateOverride: string | null;
|
|
paymentChannelOverride: PaymentChannel | null;
|
|
status: string;
|
|
failureCode: string | null;
|
|
failureMessage: string | null;
|
|
snapshotJson: Record<string, unknown>;
|
|
createdAt: string | null;
|
|
openRiskFlags: RiskFlag[];
|
|
};
|
|
|
|
export type PaymentBatch = {
|
|
id: string;
|
|
batchNo: string;
|
|
entityType: PaymentBatchEntityType;
|
|
name: string;
|
|
status: PaymentBatchStatus;
|
|
defaultPaymentDate: string | null;
|
|
defaultPaymentChannel: PaymentChannel | null;
|
|
voucherPrefix: string | null;
|
|
totalAmount: string;
|
|
highRisk: boolean;
|
|
reviewRequirement: {
|
|
thresholdAmount: string;
|
|
requiredReviewCount: number;
|
|
completedReviewCount: number;
|
|
nextStep: "first_review" | "second_review" | "ready_for_execute";
|
|
makerCheckerRequired: boolean;
|
|
reviewerPermission: string;
|
|
};
|
|
reviewComment: string | null;
|
|
secondaryReviewComment: string | null;
|
|
executionSummaryJson: Record<string, unknown> | null;
|
|
receiptAttachmentCount?: number;
|
|
createdAt: string | null;
|
|
createdBy: string | null;
|
|
reviewedAt: string | null;
|
|
reviewedBy: string | null;
|
|
secondaryReviewedAt: string | null;
|
|
secondaryReviewedBy: string | null;
|
|
executedAt: string | null;
|
|
executedBy: string | null;
|
|
cancelledAt: string | null;
|
|
cancelledBy: string | null;
|
|
items: PaymentBatchItem[];
|
|
};
|
|
|
|
export type PaymentBatchReceiptAttachment = {
|
|
id: string;
|
|
originalName: string;
|
|
mimeType: string;
|
|
fileSize: number;
|
|
fileSha256: string;
|
|
} & AttachmentGovernanceFields & {
|
|
uploadedBy: {
|
|
id: string;
|
|
employeeNo: string;
|
|
name: string;
|
|
} | null;
|
|
createdAt: string | null;
|
|
};
|
|
|
|
export type PaymentBatchListResponse = { items: PaymentBatch[] };
|
|
|
|
export async function fetchPaymentBatches(query: { entityType?: PaymentBatchEntityType; status?: PaymentBatchStatus } = {}) {
|
|
const params = new URLSearchParams();
|
|
if (query.entityType) params.set("entityType", query.entityType);
|
|
if (query.status) params.set("status", query.status);
|
|
const suffix = params.toString() ? `?${params.toString()}` : "";
|
|
return apiRequestData<PaymentBatchListResponse>(`/api/payment-batches${suffix}`);
|
|
}
|
|
|
|
export async function fetchPaymentBatchDetail(batchId: string) {
|
|
return apiRequestData<{ item: PaymentBatch }>(`/api/payment-batches/${batchId}`);
|
|
}
|
|
|
|
export async function createPaymentBatch(payload: {
|
|
entityType: PaymentBatchEntityType;
|
|
name: string;
|
|
entityIds: string[];
|
|
defaultPaymentDate?: string;
|
|
defaultPaymentChannel?: PaymentChannel;
|
|
voucherPrefix?: string;
|
|
}) {
|
|
return apiRequestData<{ item: PaymentBatch }>("/api/payment-batches", {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function updatePaymentBatchItem(batchId: string, itemId: string, payload: {
|
|
paymentVoucherNoOverride?: string | null;
|
|
paymentDateOverride?: string | null;
|
|
paymentChannelOverride?: PaymentChannel | null;
|
|
}) {
|
|
return apiRequestData<{ item: Pick<PaymentBatchItem, "id" | "paymentVoucherNoOverride" | "paymentDateOverride" | "paymentChannelOverride"> }>(
|
|
`/api/payment-batches/${batchId}/items/${itemId}`,
|
|
{
|
|
method: "PATCH",
|
|
body: JSON.stringify(payload),
|
|
},
|
|
);
|
|
}
|
|
|
|
export async function reviewPaymentBatch(batchId: string, payload: { comment?: string; resolveRiskFlagIds?: string[] } = {}) {
|
|
return apiRequestData<{ item: PaymentBatch }>(`/api/payment-batches/${batchId}/review`, {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function executePaymentBatch(batchId: string) {
|
|
return apiRequestData<{ item: PaymentBatch }>(`/api/payment-batches/${batchId}/execute`, {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
}
|
|
|
|
export async function cancelPaymentBatch(batchId: string) {
|
|
return apiRequestData<{ item: PaymentBatch }>(`/api/payment-batches/${batchId}/cancel`, {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
}
|
|
|
|
export async function fetchPaymentBatchReceiptAttachments(batchId: string) {
|
|
return apiRequestData<{ items: PaymentBatchReceiptAttachment[] }>(
|
|
`/api/payment-batches/${batchId}/receipt-attachments`,
|
|
);
|
|
}
|
|
|
|
export async function uploadPaymentBatchReceiptAttachments(batchId: string, files: File[]) {
|
|
const formData = new FormData();
|
|
for (const file of files) {
|
|
formData.append("files", file);
|
|
}
|
|
return apiRequestData<{ items: PaymentBatchReceiptAttachment[] }>(
|
|
`/api/payment-batches/${batchId}/receipt-attachments`,
|
|
{
|
|
method: "POST",
|
|
body: formData,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function paymentBatchReceiptAttachmentFileUrl(batchId: string, attachmentId: string) {
|
|
return `/api/payment-batches/${batchId}/receipt-attachments/${attachmentId}/file`;
|
|
}
|
|
|
|
export async function downloadPaymentBatchReceiptAttachment(
|
|
batchId: string,
|
|
attachmentId: string,
|
|
reason?: string,
|
|
): Promise<ApiBlobResponse> {
|
|
return apiRequestBlob(buildAttachmentFileUrl(paymentBatchReceiptAttachmentFileUrl(batchId, attachmentId), reason));
|
|
}
|
|
|
|
export async function deletePaymentBatchReceiptAttachment(batchId: string, attachmentId: string) {
|
|
return apiRequestData<{ attachmentId: string; outcome: "deleted" }>(
|
|
`/api/payment-batches/${batchId}/receipt-attachments/${attachmentId}`,
|
|
{ method: "DELETE" },
|
|
);
|
|
}
|