文件
hyb-oa/client/src/api/travelReconciliation.ts
T
wangsli 1f8d5b658a 完善借款、编号、付款与预算并发及验收(09-06-release-gate-integrity)
汇总已落盘的借款本金与还款、兼容历史编号的周期化编号、付款责任与回单核验、报销进度和排序、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项公网义务仍开放。生产编号未启用、差旅仍冻结;不推送、不部署,不宣称发布资格。
2026-09-19 13:11:04 +08:00

209 行
5.7 KiB
TypeScript

import { apiRequestData } from './http';
export type TravelReconciliationBatchStatus = 'draft' | 'reconciled';
export type TravelReconciliationItemKind = 'order' | 'orphan_transaction';
export type TravelReconciliationMatchStatus =
| 'matched'
| 'amount_mismatch'
| 'currency_mismatch'
| 'missing_transaction'
| 'duplicate_charge'
| 'orphan_transaction'
| 'reversed_after_payment';
export type TravelReconciliationResolutionStatus = 'pending' | 'confirmed';
export type TravelReconciliationSummary = {
totalOrders: number;
totalTransactions: number;
matchedItems: number;
pendingItems: number;
anomalyItems: number;
totalOrderAmount: string;
totalSettledAmount: string;
totalReversedAmount: string;
matchRate: string;
};
export type TravelReconciliationBatchSummary = {
id: string;
batchNo: string;
legalEntityId: string;
costCenterId: string | null;
periodStart: string | null;
periodEnd: string | null;
status: TravelReconciliationBatchStatus;
summary: TravelReconciliationSummary;
confirmedAt: string | null;
confirmedBy: string | null;
createdAt: string | null;
updatedAt: string | null;
legalEntity: {
id: string;
code: string;
name: string;
} | null;
costCenter: {
id: string;
code: string;
name: string;
legalEntityId: string;
} | null;
confirmedByEmployee: {
id: string;
employeeNo: string;
name: string;
} | null;
};
export type TravelReconciliationItem = {
id: string;
itemKind: TravelReconciliationItemKind;
travelRequestId: string | null;
travelOrderId: string | null;
cardTransactionId: string | null;
matchStatus: TravelReconciliationMatchStatus;
resolutionStatus: TravelReconciliationResolutionStatus;
orderAmount: string | null;
transactionAmount: string | null;
currency: string | null;
anomalyCode: string | null;
confirmedAt: string | null;
confirmedBy: string | null;
confirmNote: string | null;
snapshot: {
requestNo: string | null;
externalOrderNo: string | null;
externalTxnId: string | null;
travelOrderStatus: string | null;
transactionStatus: string | null;
linkedReimbursementId: string | null;
boundInvoiceCount: number;
boundInvoiceAmount: string;
matchedTransactionIds: string[];
reasonDetail: string | null;
};
travelRequest: {
id: string;
requestNo: string | null;
} | null;
travelOrder: {
id: string;
externalOrderNo: string;
status: string;
} | null;
cardTransaction: {
id: string;
externalTxnId: string;
status: string;
txnType: string;
} | null;
actions: {
canConfirm: boolean;
canReopen: boolean;
};
};
export type TravelReconciliationBatchDetail = TravelReconciliationBatchSummary & {
items: TravelReconciliationItem[];
permissions: {
canManagePayments: boolean;
};
};
export type TravelReconciliationListResponse = {
page: number;
pageSize: number;
total: number;
items: TravelReconciliationBatchSummary[];
};
export type TravelReconciliationListQuery = {
status?: TravelReconciliationBatchStatus;
legalEntityId?: string;
costCenterId?: string;
periodStart?: string;
periodEnd?: string;
page?: number;
pageSize?: number;
};
export type TravelReconciliationCreatePayload = {
legalEntityId: string;
costCenterId?: string;
periodStart: string;
periodEnd: string;
};
export type TravelReconciliationFinalizeResponse = {
batchId: string;
batchNo: string;
status: TravelReconciliationBatchStatus;
};
export type TravelReconciliationRequeueResponse = {
outboxId: string;
status: string;
duplicated: boolean;
requeued: boolean;
eventType: string;
};
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 fetchTravelReconciliationBatches(query: TravelReconciliationListQuery = {}) {
return apiRequestData<TravelReconciliationListResponse>(
`/api/travel/reconciliation/batches${toQueryString(query as Record<string, string | number | boolean | undefined>)}`,
);
}
export async function createTravelReconciliationBatch(payload: TravelReconciliationCreatePayload) {
return apiRequestData<TravelReconciliationBatchDetail>('/api/travel/reconciliation/batches', {
method: 'POST',
body: JSON.stringify(payload),
});
}
export async function fetchTravelReconciliationBatchDetail(batchId: string) {
return apiRequestData<TravelReconciliationBatchDetail>(`/api/travel/reconciliation/batches/${batchId}`);
}
export async function confirmTravelReconciliationItem(itemId: string, payload: { note?: string | null } = {}) {
return apiRequestData<{ batchId: string; summary: TravelReconciliationSummary; item: TravelReconciliationItem }>(
`/api/travel/reconciliation/items/${itemId}/confirm`,
{
method: 'POST',
body: JSON.stringify(payload),
},
);
}
export async function reopenTravelReconciliationItem(itemId: string) {
return apiRequestData<{ batchId: string; summary: TravelReconciliationSummary; item: TravelReconciliationItem }>(
`/api/travel/reconciliation/items/${itemId}/reopen`,
{
method: 'POST',
body: JSON.stringify({}),
},
);
}
export async function finalizeTravelReconciliationBatch(batchId: string) {
return apiRequestData<TravelReconciliationFinalizeResponse>(
`/api/travel/reconciliation/batches/${batchId}/finalize`,
{
method: 'POST',
body: JSON.stringify({}),
},
);
}