feat: add tenant finance operations UI

This commit is contained in:
Codex
2026-06-29 23:31:38 +08:00
parent 620f3ac5ed
commit c6a21d94bb
12 changed files with 1022 additions and 13 deletions

View File

@@ -0,0 +1,381 @@
import { apiRequest } from './api';
export type PaymentProvider = 'wechat_pay' | 'alipay' | 'manual' | string;
export type BillType = 'payment' | 'refund' | 'combined';
export interface TenantRefundItem {
id: string;
refundNo?: string;
orderId?: string;
paymentId?: string | null;
orderNo?: string;
orderStatus?: string;
paymentProvider?: string | null;
provider?: string | null;
providerRefundNo?: string | null;
status?: string;
amountCents?: number;
amount?: number;
reason?: string | null;
entitlementAction?: string;
requestedBy?: string | null;
reviewedBy?: string | null;
processedBy?: string | null;
requestedAt?: string;
reviewedAt?: string | null;
processedAt?: string | null;
succeededAt?: string | null;
failedAt?: string | null;
cancelledAt?: string | null;
failureReason?: string | null;
metadata?: Record<string, unknown>;
createdAt?: string;
updatedAt?: string;
}
export interface ReconciliationBatchItem {
id: string;
provider?: PaymentProvider;
billDate?: string;
billType?: BillType;
source?: string;
sourceName?: string | null;
sourceHash?: string | null;
status?: string;
totalCount?: number;
matchedCount?: number;
mismatchCount?: number;
missingLocalCount?: number;
missingProviderCount?: number;
duplicateCount?: number;
ignoredCount?: number;
amountCents?: number;
refundAmountCents?: number;
feeCents?: number;
error?: string | null;
createdAt?: string;
updatedAt?: string;
}
export interface ReconciliationItem {
id: string;
batchId?: string;
rowNo?: number;
provider?: PaymentProvider;
transactionType?: string;
providerTradeNo?: string | null;
providerRefundNo?: string | null;
orderNo?: string | null;
refundNo?: string | null;
amountCents?: number;
refundAmountCents?: number;
feeCents?: number;
providerStatus?: string | null;
localStatus?: string | null;
orderId?: string | null;
paymentId?: string | null;
refundRequestId?: string | null;
matchStatus?: string;
severity?: string;
issueCode?: string | null;
details?: Record<string, unknown>;
createdAt?: string;
}
export interface ReconciliationIssueItem {
id: string;
issueNo?: string;
batchId?: string;
itemId?: string;
provider?: PaymentProvider;
transactionType?: string;
issueCode?: string | null;
matchStatus?: string;
severity?: string;
status?: string;
resolutionType?: string;
orderId?: string | null;
paymentId?: string | null;
refundRequestId?: string | null;
orderNo?: string | null;
refundNo?: string | null;
providerTradeNo?: string | null;
providerRefundNo?: string | null;
amountCents?: number;
refundAmountCents?: number;
assignedTo?: string | null;
assignedToName?: string | null;
createdByName?: string | null;
resolvedByName?: string | null;
resolvedAt?: string | null;
dueAt?: string | null;
summary?: string | null;
resolutionNote?: string | null;
metadata?: Record<string, unknown>;
createdAt?: string;
updatedAt?: string;
}
export interface ProviderBillJobItem {
id: string;
provider?: PaymentProvider;
billDate?: string;
billType?: BillType;
status?: string;
sourceName?: string | null;
sourceHash?: string | null;
rowCount?: number | null;
downloadHashType?: string | null;
downloadHashValue?: string | null;
downloadUrlHost?: string | null;
reconciliationBatchId?: string | null;
requestedBy?: string | null;
claimedBy?: string | null;
claimedAt?: string | null;
completedAt?: string | null;
failedAt?: string | null;
errorCode?: string | null;
errorMessage?: string | null;
metadata?: Record<string, unknown>;
createdAt?: string;
updatedAt?: string;
}
export interface CommerceOperationAnomaly {
type?: string;
id: string;
severity?: 'info' | 'warning' | 'error' | 'critical' | string;
status?: string;
title?: string;
provider?: PaymentProvider | null;
orderNo?: string | null;
refundNo?: string | null;
amountCents?: number;
createdAt?: string;
updatedAt?: string;
source?: Record<string, unknown>;
}
export interface AdjustmentVoucherItem {
id: string;
voucherNo?: string;
sourceType?: string;
sourceId?: string | null;
reconciliationIssueId?: string | null;
reconciliationItemId?: string | null;
orderId?: string | null;
paymentId?: string | null;
refundRequestId?: string | null;
orderNo?: string | null;
refundNo?: string | null;
provider?: PaymentProvider | null;
providerTradeNo?: string | null;
providerRefundNo?: string | null;
adjustmentType?: string;
direction?: string;
amountCents?: number;
status?: string;
title?: string | null;
description?: string | null;
assetId?: string | null;
externalUrl?: string | null;
submittedByName?: string | null;
submittedAt?: string | null;
reviewedByName?: string | null;
reviewedAt?: string | null;
reviewNote?: string | null;
metadata?: Record<string, unknown>;
createdByName?: string | null;
createdAt?: string;
updatedAt?: string;
}
export interface AdjustmentVoucherReport {
startDate?: string;
endDate?: string;
totalCount?: number;
totalAmountCents?: number;
pendingReviewCount?: number;
byStatus?: Record<string, unknown>[];
byType?: Record<string, unknown>[];
bySource?: Record<string, unknown>[];
daily?: Record<string, unknown>[];
}
export async function loadTenantRefunds(query: { status?: string; orderNo?: string; limit?: number } = {}) {
return apiRequest<{ items?: TenantRefundItem[] }>('/api/commerce/refunds', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function createTenantRefund(input: {
orderNo: string;
amountCents?: number;
reason?: string | null;
refundNo?: string;
entitlementAction?: 'none' | 'revoke_on_success';
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: TenantRefundItem }>('/api/commerce/refunds', {
method: 'POST',
body: input,
});
}
export async function updateTenantRefundStatus(input: {
refundId: string;
action: string;
note?: string | null;
providerRefundNo?: string | null;
providerNotifyUrl?: string | null;
failureReason?: string | null;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: TenantRefundItem }>('/api/commerce/refunds/status', {
method: 'POST',
body: input,
});
}
export async function loadReconciliationBatches(query: { provider?: string; status?: string; billDate?: string; limit?: number } = {}) {
return apiRequest<{ items?: ReconciliationBatchItem[] }>('/api/commerce/reconciliation/batches', {
query: { ...query, limit: query.limit || 30 },
});
}
export async function loadReconciliationItems(query: {
batchId?: string;
matchStatus?: string;
severity?: string;
orderNo?: string;
limit?: number;
} = {}) {
return apiRequest<{ items?: ReconciliationItem[] }>('/api/commerce/reconciliation/items', {
query: { ...query, limit: query.limit || 80 },
});
}
export async function createReconciliationIssue(input: {
itemId: string;
assignedTo?: string | null;
assignedToUserId?: string | null;
dueAt?: string | null;
summary?: string | null;
note?: string | null;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: ReconciliationIssueItem; idempotent?: boolean }>('/api/commerce/reconciliation/issues/create', {
method: 'POST',
body: input,
});
}
export async function loadReconciliationIssues(query: {
status?: string;
severity?: string;
assignedTo?: string;
batchId?: string;
orderNo?: string;
limit?: number;
} = {}) {
return apiRequest<{ items?: ReconciliationIssueItem[] }>('/api/commerce/reconciliation/issues', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function updateReconciliationIssueStatus(input: {
issueId: string;
action: 'start' | 'assign' | 'resolve' | 'ignore' | 'escalate' | 'reopen';
note?: string | null;
dueAt?: string | null;
assignedTo?: string | null;
resolutionType?: string | null;
resolutionNote?: string | null;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: ReconciliationIssueItem }>('/api/commerce/reconciliation/issues/status', {
method: 'POST',
body: input,
});
}
export async function requestProviderBillDownload(input: {
provider: Exclude<PaymentProvider, 'manual'>;
billDate: string;
billType?: BillType;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: ProviderBillJobItem; idempotent?: boolean }>('/api/commerce/reconciliation/provider-bills/request', {
method: 'POST',
body: input,
});
}
export async function loadProviderBillJobs(query: { provider?: string; status?: string; billDate?: string; limit?: number } = {}) {
return apiRequest<{ items?: ProviderBillJobItem[] }>('/api/commerce/reconciliation/provider-bills/jobs', {
query: { ...query, limit: query.limit || 30 },
});
}
export async function loadCommerceOperationAnomalies(query: { provider?: string; limit?: number } = {}) {
return apiRequest<{ summary?: Record<string, unknown>; items?: CommerceOperationAnomaly[] }>('/api/commerce/operations/anomalies', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function loadAdjustmentVouchers(query: {
status?: string;
sourceType?: string;
orderNo?: string;
voucherNo?: string;
reconciliationIssueId?: string;
limit?: number;
} = {}) {
return apiRequest<{ items?: AdjustmentVoucherItem[] }>('/api/commerce/adjustment-vouchers', {
query: { ...query, limit: query.limit || 50 },
});
}
export async function createAdjustmentVoucher(input: {
sourceType?: string;
sourceId?: string | null;
reconciliationIssueId?: string | null;
reconciliationItemId?: string | null;
orderId?: string | null;
paymentId?: string | null;
refundRequestId?: string | null;
orderNo?: string | null;
refundNo?: string | null;
adjustmentType?: string;
direction?: string;
amountCents?: number;
status?: 'draft' | 'submitted';
title?: string | null;
description?: string | null;
assetId?: string | null;
externalUrl?: string | null;
note?: string | null;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: AdjustmentVoucherItem }>('/api/commerce/adjustment-vouchers', {
method: 'POST',
body: input,
});
}
export async function updateAdjustmentVoucherStatus(input: {
voucherId: string;
status: 'submitted' | 'approved' | 'rejected' | 'voided';
reviewNote?: string | null;
note?: string | null;
metadata?: Record<string, unknown>;
}) {
return apiRequest<{ item?: AdjustmentVoucherItem; idempotent?: boolean }>('/api/commerce/adjustment-vouchers/status', {
method: 'POST',
body: input,
});
}
export async function loadAdjustmentVoucherReport(query: { startDate?: string; endDate?: string } = {}) {
return apiRequest<{ item?: AdjustmentVoucherReport }>('/api/commerce/adjustment-vouchers/report', { query });
}