260 行
7.0 KiB
TypeScript
260 行
7.0 KiB
TypeScript
import { apiRequestData } from "./http";
|
|
|
|
export type InvoicePoolStatus = "pending_ocr" | "ocr_failed" | "available" | "bound" | "archived";
|
|
|
|
export type InvoiceVerificationRecord = {
|
|
id: string;
|
|
provider: string;
|
|
status: string;
|
|
resultCode: string | null;
|
|
resultMessage: string | null;
|
|
errorCode: string | null;
|
|
errorMessage: string | null;
|
|
durationMs: number | null;
|
|
requestedBy: string | null;
|
|
requestedAt: string | null;
|
|
completedAt: string | null;
|
|
retryOfId: string | null;
|
|
attempts: number;
|
|
};
|
|
|
|
export type InvoicePoolListItem = {
|
|
id: string;
|
|
status: InvoicePoolStatus;
|
|
ocrType: string;
|
|
ocrProvider: string;
|
|
ocrAttempts: number;
|
|
ocrLastError: string | null;
|
|
ocrCompletedAt: string | null;
|
|
invoiceCode: string | null;
|
|
invoiceNumber: string | null;
|
|
invoiceDate: string | null;
|
|
amount: string | null;
|
|
taxAmount: string | null;
|
|
totalAmount: string | null;
|
|
linkedReimbursementId: string | null;
|
|
linkedItemId: string | null;
|
|
linkedTravelOrderId: string | null;
|
|
originalName: string;
|
|
mimeType: string;
|
|
fileSize: number;
|
|
verification: InvoiceVerificationRecord | null;
|
|
owner: {
|
|
id: string;
|
|
employeeNo: string;
|
|
name: string;
|
|
} | null;
|
|
uploadedBy: {
|
|
id: string;
|
|
employeeNo: string;
|
|
name: string;
|
|
} | null;
|
|
createdAt: string | null;
|
|
updatedAt: string | null;
|
|
};
|
|
|
|
export type InvoicePoolListResponse = {
|
|
page: number;
|
|
pageSize: number;
|
|
total: number;
|
|
ownerScope: "own" | "all";
|
|
items: InvoicePoolListItem[];
|
|
};
|
|
|
|
export type InvoicePoolDetail = {
|
|
id: string;
|
|
status: InvoicePoolStatus;
|
|
archivedReason: string | null;
|
|
archivedAt: string | null;
|
|
ownerEmployeeId: string;
|
|
uploadedByEmployeeId: string;
|
|
originalName: string | null;
|
|
mimeType: string | null;
|
|
fileSize: number | null;
|
|
fileSha256: string | null;
|
|
remarks: string | null;
|
|
ocrType: string;
|
|
ocrProvider: string;
|
|
ocrAttempts: number;
|
|
ocrLastRequestedAt: string | null;
|
|
ocrLastError: string | null;
|
|
ocrCompletedAt: string | null;
|
|
invoiceCode: string | null;
|
|
invoiceNumber: string | null;
|
|
invoiceDate: string | null;
|
|
sellerName: string | null;
|
|
buyerName: string | null;
|
|
amount: string | null;
|
|
taxAmount: string | null;
|
|
totalAmount: string | null;
|
|
linkedReimbursementId: string | null;
|
|
linkedItemId: string | null;
|
|
linkedTravelOrderId: string | null;
|
|
isDuplicateOverride: boolean;
|
|
overrideRefFormNumber: string | null;
|
|
overrideReason: string | null;
|
|
overriddenBy: string | null;
|
|
verification: InvoiceVerificationRecord | null;
|
|
createdAt: string | null;
|
|
updatedAt: string | null;
|
|
};
|
|
|
|
export type InvoicePoolListQuery = {
|
|
page?: number;
|
|
pageSize?: number;
|
|
status?: InvoicePoolStatus;
|
|
ownerScope?: "own" | "all";
|
|
};
|
|
|
|
export type InvoiceBindPayload =
|
|
| {
|
|
reimbursementId: string;
|
|
linkedItemId?: string;
|
|
}
|
|
| {
|
|
travelOrderId: string;
|
|
};
|
|
|
|
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 encoded = params.toString();
|
|
return encoded ? `?${encoded}` : "";
|
|
}
|
|
|
|
export async function fetchInvoicePoolList(query: InvoicePoolListQuery = {}) {
|
|
return apiRequestData<InvoicePoolListResponse>(
|
|
`/api/invoice-pool${toQueryString(query as Record<string, string | number | undefined>)}`,
|
|
);
|
|
}
|
|
|
|
export async function fetchInvoicePoolDetail(invoiceId: string) {
|
|
return apiRequestData<InvoicePoolDetail>(`/api/invoice-pool/${invoiceId}`);
|
|
}
|
|
|
|
export async function uploadInvoice(file: File, ocrType?: string, ownerEmployeeId?: string) {
|
|
const formData = new FormData();
|
|
formData.append("file", file);
|
|
if (ocrType) {
|
|
formData.append("ocr_type", ocrType);
|
|
}
|
|
if (ownerEmployeeId) {
|
|
formData.append("owner_employee_id", ownerEmployeeId);
|
|
}
|
|
|
|
return apiRequestData<{ invoicePoolId: string; status: string; fileSha256: string }>(
|
|
"/api/invoice-pool/upload",
|
|
{
|
|
method: "POST",
|
|
body: formData,
|
|
},
|
|
);
|
|
}
|
|
|
|
export async function bindInvoice(
|
|
invoiceId: string,
|
|
reimbursementIdOrPayload: string | InvoiceBindPayload,
|
|
linkedItemId?: string,
|
|
) {
|
|
const payload =
|
|
typeof reimbursementIdOrPayload === "string"
|
|
? { reimbursementId: reimbursementIdOrPayload, linkedItemId }
|
|
: reimbursementIdOrPayload;
|
|
|
|
return apiRequestData<Record<string, unknown>>(`/api/invoice-pool/${invoiceId}/bind`, {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function fetchInvoiceSignedUrl(invoiceId: string) {
|
|
return apiRequestData<{ signedUrl: string; expiresAt: string }>(
|
|
`/api/invoice-pool/${invoiceId}/signed-url`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
},
|
|
);
|
|
}
|
|
|
|
export type InvoicePatchPayload = Partial<{
|
|
status: "available";
|
|
remarks: string | null;
|
|
invoiceCode: string | null;
|
|
invoiceNumber: string | null;
|
|
invoiceDate: string | null;
|
|
sellerName: string | null;
|
|
buyerName: string | null;
|
|
amount: string | null;
|
|
taxAmount: string | null;
|
|
totalAmount: string | null;
|
|
}>;
|
|
|
|
export async function patchInvoice(invoiceId: string, payload: InvoicePatchPayload) {
|
|
return apiRequestData<InvoicePoolDetail>(`/api/invoice-pool/${invoiceId}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function archiveInvoice(invoiceId: string) {
|
|
return apiRequestData<InvoicePoolDetail>(`/api/invoice-pool/${invoiceId}/archive`, {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
}
|
|
|
|
export async function unbindInvoice(invoiceId: string) {
|
|
return apiRequestData<InvoicePoolDetail>(`/api/invoice-pool/${invoiceId}/unbind`, {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
}
|
|
|
|
export async function retryInvoiceOcr(invoiceId: string) {
|
|
return apiRequestData<InvoicePoolDetail>(`/api/invoice-pool/${invoiceId}/retry-ocr`, {
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
});
|
|
}
|
|
|
|
export async function overrideInvoiceDuplicate(invoiceId: string, payload: { refFormNumber: string; reason: string }) {
|
|
return apiRequestData<InvoicePoolDetail>(`/api/invoice-pool/${invoiceId}/override-duplicate`, {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function triggerInvoiceVerification(invoiceId: string) {
|
|
return apiRequestData<{ invoiceId: string; verificationId: string; provider: string; triggerMode: string }>(
|
|
`/api/invoice-pool/${invoiceId}/verify`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
},
|
|
);
|
|
}
|
|
|
|
export async function fetchInvoiceVerifications(invoiceId: string, limit = 20) {
|
|
return apiRequestData<{ invoiceId: string; items: InvoiceVerificationRecord[] }>(
|
|
`/api/invoice-pool/${invoiceId}/verification?limit=${limit}`,
|
|
);
|
|
}
|
|
|
|
export async function retryInvoiceVerification(invoiceId: string) {
|
|
return apiRequestData<{ invoiceId: string; verificationId: string; status: string }>(
|
|
`/api/invoice-pool/${invoiceId}/verification/retry`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({}),
|
|
},
|
|
);
|
|
}
|