文件
hyb-oa/client/src/api/travelReconciliation.ts
T

300 行
7.8 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;
canRetryErp: 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 TravelReconciliationErpTrack = {
batch: {
id: string;
batchNo: string;
status: string;
periodStart: string | null;
periodEnd: string | null;
legalEntity: {
id: string;
code: string;
name: string;
} | null;
costCenter: {
id: string;
code: string;
name: string;
} | null;
};
stage: {
summaryExport: {
eligible: boolean;
reason: string | null;
status: string;
preview: {
exportType: string;
entityType: string;
entityId: string | null;
batchNo: string | null;
occurredAt: string | null;
};
outbox: {
id: string;
eventType: string;
status: string;
attempts: number;
lastError: string | null;
processedAt: string | null;
updatedAt: string | null;
} | null;
bindings: {
payableSummary: {
id: string;
provider: string;
entityType: string;
localId: string;
externalId: string | null;
externalCode: string | null;
lastSyncedAt: string | null;
} | null;
settlementBatch: {
id: string;
provider: string;
entityType: string;
localId: string;
externalId: string | null;
externalCode: string | null;
lastSyncedAt: string | null;
} | null;
};
};
};
permissions: {
canRetry: boolean;
canSeeExternalRefs: boolean;
};
};
export type TravelReconciliationFinalizeResponse = {
batchId: string;
batchNo: string;
status: TravelReconciliationBatchStatus;
erpExport: {
eventType: string;
outboxId: string | null;
duplicated: boolean;
requeued: boolean;
enqueued: boolean;
};
};
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({}),
},
);
}
export async function fetchTravelReconciliationErpTrack(batchId: string) {
return apiRequestData<TravelReconciliationErpTrack>(
`/api/integrations/erp/travel/reconciliation-batches/${batchId}/track`,
);
}
export async function requeueTravelReconciliationBatch(batchId: string) {
return apiRequestData<TravelReconciliationRequeueResponse>(
`/api/integrations/erp/travel/reconciliation-batches/${batchId}/requeue`,
{
method: 'POST',
body: JSON.stringify({}),
},
);
}