汇总已落盘的借款本金与还款、兼容历史编号的周期化编号、付款责任与回单核验、报销进度和排序、PWA/采购事务、错误原因与日期校验及ERP退役。 统一共享预算池锁序和锁后判断,恢复差旅原预算选择,补原因码固定夹具及反向完整性检查。 纳入配套迁移、测试、受管QA、分层验收合同、规约及任务记录;原始证据留在ignored artifacts。 关联任务:09-17-company-loan-principal-repayment、09-17-erp-exclusive-retirement、09-18-reimbursement-periodic-numbering、09-08-reimbursement-payment-closeout、09-18-local-concurrency-product-fixes、09-19-budget-pool-concurrency、09-05-error-explainability-payment-readiness。 预算补审由Claude定向验证2文件928项通过,原4446/28及r6结果仍仅对应各自历史输入;本轮未重跑测试。 完整新候选门禁、R1真实入口接入与12项公网义务仍开放。生产编号未启用、差旅仍冻结;不推送、不部署,不宣称发布资格。
226 行
5.9 KiB
TypeScript
226 行
5.9 KiB
TypeScript
import { apiRequestData } from "./http";
|
|
|
|
export type TravelPaymentChannel =
|
|
| "bank_transfer"
|
|
| "corporate_card"
|
|
| "virtual_card"
|
|
| "manual"
|
|
| "other";
|
|
|
|
export type TravelPaymentBudgetSummaryItem = {
|
|
budgetPoolId: string;
|
|
budgetPoolCode: string | null;
|
|
budgetPoolName: string | null;
|
|
outstanding: string;
|
|
reserve?: string;
|
|
confirm?: string;
|
|
release?: string;
|
|
confirmPending?: string;
|
|
authorize?: string;
|
|
settle?: string;
|
|
reverse?: string;
|
|
settlePending?: string;
|
|
};
|
|
|
|
export type TravelPaymentAuthorization = {
|
|
id: string;
|
|
source: string;
|
|
externalAuthNo: string;
|
|
employeeId: string;
|
|
departmentId: string | null;
|
|
travelRequestId: string;
|
|
budgetPoolId: string | null;
|
|
legalEntityId: string;
|
|
costCenterId: string;
|
|
authorizedAmount: string;
|
|
settledAmount: string;
|
|
reversedAmount: string;
|
|
currency: string;
|
|
occurredAt: string | null;
|
|
expiresAt: string | null;
|
|
status: string;
|
|
createdAt: string | null;
|
|
updatedAt: string | null;
|
|
};
|
|
|
|
export type TravelPaymentMatchedOrder = {
|
|
id: string;
|
|
externalOrderNo: string;
|
|
status: string;
|
|
amount: string;
|
|
currency: string;
|
|
exactMatch: boolean;
|
|
};
|
|
|
|
export type TravelPaymentTransaction = {
|
|
id: string;
|
|
source: string;
|
|
externalTxnId: string;
|
|
cardAuthorizationId: string | null;
|
|
employeeId: string;
|
|
departmentId: string | null;
|
|
travelRequestId: string;
|
|
travelOrderId: string | null;
|
|
amount: string;
|
|
currency: string;
|
|
txnType: string;
|
|
status: string;
|
|
occurredAt: string | null;
|
|
freezeSuggestedAt: string | null;
|
|
freezeSuggestedBy: string | null;
|
|
freezeReasonCode: string | null;
|
|
freezeReasonNote: string | null;
|
|
matchedOrder: TravelPaymentMatchedOrder | null;
|
|
createdAt: string | null;
|
|
updatedAt: string | null;
|
|
};
|
|
|
|
export type TravelPaymentBoundInvoice = {
|
|
id: string;
|
|
status: string;
|
|
originalName: string | null;
|
|
totalAmount: string | null;
|
|
invoiceCode: string | null;
|
|
invoiceNumber: string | null;
|
|
invoiceDate: string | null;
|
|
linkedReimbursementId: string | null;
|
|
linkedTravelOrderId: string | null;
|
|
};
|
|
|
|
export type TravelPaymentOrder = {
|
|
id: string;
|
|
externalOrderNo: string;
|
|
source: string;
|
|
amount: string;
|
|
currency: string;
|
|
status: string;
|
|
paymentVoucherNo: string | null;
|
|
paymentDate: string | null;
|
|
paymentChannel: TravelPaymentChannel | null;
|
|
paidAt: string | null;
|
|
paidBy: string | null;
|
|
boundInvoices: TravelPaymentBoundInvoice[];
|
|
matchedTransactions: TravelPaymentTransaction[];
|
|
actions: {
|
|
canMarkPaid: boolean;
|
|
canBindInvoice: boolean;
|
|
};
|
|
};
|
|
|
|
export type TravelPaymentAnomaly = {
|
|
code: string;
|
|
level: "low" | "medium" | "high";
|
|
title: string;
|
|
detail: string;
|
|
transactionId: string;
|
|
travelOrderId: string | null;
|
|
};
|
|
|
|
export type TravelPaymentTrack = {
|
|
travelRequestId: string;
|
|
requestNo: string | null;
|
|
requestBudgetTrack: {
|
|
items: TravelPaymentBudgetSummaryItem[];
|
|
};
|
|
corporateBudgetTrack: {
|
|
items: TravelPaymentBudgetSummaryItem[];
|
|
};
|
|
authorizations: TravelPaymentAuthorization[];
|
|
transactions: TravelPaymentTransaction[];
|
|
travelOrders: TravelPaymentOrder[];
|
|
anomalies: TravelPaymentAnomaly[];
|
|
permissions: {
|
|
canManagePayments: boolean;
|
|
};
|
|
};
|
|
|
|
export type TravelAuthorizationListResponse = {
|
|
page: number;
|
|
pageSize: number;
|
|
total: number;
|
|
items: TravelPaymentAuthorization[];
|
|
};
|
|
|
|
export type TravelTransactionListResponse = {
|
|
page: number;
|
|
pageSize: number;
|
|
total: number;
|
|
items: TravelPaymentTransaction[];
|
|
};
|
|
|
|
function toQueryString(query: Record<string, string | number | boolean | undefined>) {
|
|
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 fetchTravelAuthorizations(query: Record<string, string | number | boolean | undefined> = {}) {
|
|
return apiRequestData<TravelAuthorizationListResponse>(
|
|
`/api/travel/authorizations${toQueryString(query)}`,
|
|
);
|
|
}
|
|
|
|
export async function importTravelAuthorizations(payload: { authorizations: Array<Record<string, unknown>> }) {
|
|
return apiRequestData<Record<string, unknown>>("/api/travel/authorizations/import", {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function fetchTravelTransactions(query: Record<string, string | number | boolean | undefined> = {}) {
|
|
return apiRequestData<TravelTransactionListResponse>(
|
|
`/api/travel/transactions${toQueryString(query)}`,
|
|
);
|
|
}
|
|
|
|
export async function importTravelTransactions(payload: { transactions: Array<Record<string, unknown>> }) {
|
|
return apiRequestData<Record<string, unknown>>("/api/travel/transactions/import", {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function suggestFreezeTravelTransaction(
|
|
transactionId: string,
|
|
payload: {
|
|
reasonCode: "amount_mismatch" | "currency_mismatch" | "missing_order" | "duplicate" | "manual_review";
|
|
note?: string | null;
|
|
},
|
|
) {
|
|
return apiRequestData<TravelPaymentTransaction>(`/api/travel/transactions/${transactionId}/freeze-suggestion`, {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function markTravelOrderPaid(
|
|
travelOrderId: string,
|
|
payload: {
|
|
paymentVoucherNo?: string | null;
|
|
paymentDate?: string | null;
|
|
paymentChannel?: TravelPaymentChannel | null;
|
|
},
|
|
) {
|
|
return apiRequestData<{
|
|
travelOrderId: string;
|
|
travelRequestId: string | null;
|
|
status: string;
|
|
paymentVoucherNo: string | null;
|
|
paymentDate: string | null;
|
|
paymentChannel: TravelPaymentChannel | null;
|
|
}>(`/api/travel-orders/${travelOrderId}/mark-paid`, {
|
|
method: "POST",
|
|
body: JSON.stringify(payload),
|
|
});
|
|
}
|
|
|
|
export async function fetchTravelPaymentTrack(travelRequestId: string) {
|
|
return apiRequestData<TravelPaymentTrack>(`/api/travel-requests/${travelRequestId}/payment-track`);
|
|
}
|