100 lines
8.1 KiB
TypeScript
100 lines
8.1 KiB
TypeScript
import { http, unwrapApiResponse, type ApiEnvelope } from '@/shared/api/http'
|
|
|
|
export interface FeePage<T> { records: T[]; page: number; size: number; total: number; pages: number }
|
|
export type OverseasFeeDirection = 'RECEIVABLE' | 'PAYABLE'
|
|
export interface OverseasFee {
|
|
storageRent?: { outboundNo: string; outboundAt: string; calculatedAmount: number; chargedAmount: number | null;
|
|
lines: Array<{ lotId: string; inboundAt: string; quantity: number; days: number; calculatedAmount: number;
|
|
sourceBusinessType?: string | null; sourceBusinessNo?: string | null; batchNo?: string | null }> } | null
|
|
id: string; sourceFeeId: string; sourceVersion: number; customerId: string; customerName: string
|
|
sourceType: string; businessType: string; businessId: string | null; businessNo: string
|
|
displayBusinessType?: string
|
|
direction: OverseasFeeDirection
|
|
supplierId?: string | null; supplierName?: string | null; supplierCategory?: string | null
|
|
settlementMode?: 'BILL' | 'WALLET' | 'ON_ACCOUNT' | 'DIRECT_PAID' | null
|
|
paidAt?: string | null; paymentMethod?: string | null; paymentReferenceNo?: string | null; paymentVoucherFileIds?: string[] | null
|
|
orderId: string | null; warehouseId: string | null; feeItemCode: string; feeItemName: string
|
|
feeRemark?: string | null
|
|
businessDate: string; currency: string; amount: number; originalCurrency: string; originalAmount: number
|
|
exchangeRate?: number | null; exchangeRateMonth?: string | null
|
|
chargeableQuantity: number | null; quantityUnit: string | null; unitPrice: number | null
|
|
periodStart: string | null; periodEnd: string | null; reviewStatus: string; sourceStatus: string
|
|
publicationStatus: string; collectionMode: 'BILL' | 'WALLET'; walletStatus: string | null
|
|
deliveryStatus?: 'PENDING' | 'PROCESSING' | 'PUBLISHED' | 'DEAD' | null
|
|
deliveryKind?: 'FEE' | 'FUNDING' | null; deliveryEventId?: string | null; deliveryErrorMessage?: string | null
|
|
walletErrorMessage: string | null; reviewReason: string | null; reviewerId?: string | null; reviewerName?: string | null; reviewedAt: string | null
|
|
createdAt: string; updatedAt: string; allowedActions: string[]
|
|
}
|
|
export interface BusinessFeeOption {
|
|
businessId: string; businessType: string; businessNo: string; customerId: string; customerName: string
|
|
warehouseId: string | null; orderId: string | null; orderType?: string
|
|
}
|
|
export interface SaveOverseasFee {
|
|
customerId: string; customerName: string; sourceType: string; businessType: string
|
|
businessId: string; businessNo: string; orderId: string | null; warehouseId: string | null
|
|
feeItemCode: string; feeItemName: string; direction: OverseasFeeDirection; businessDate: string
|
|
feeRemark?: string | null
|
|
supplierId?: string | null; supplierName?: string | null; supplierCategory?: string | null
|
|
settlementMode?: 'BILL' | 'WALLET' | 'ON_ACCOUNT' | 'DIRECT_PAID' | null
|
|
paidAt?: string | null; paymentMethod?: string | null; paymentReferenceNo?: string | null; paymentVoucherFileIds?: string[] | null
|
|
currency: 'CNY' | 'USD'; amount: number
|
|
chargeableQuantity?: number; quantityUnit?: string; unitPrice?: number
|
|
}
|
|
const base = '/platform/v1/last-leg/finance-fee-sources'
|
|
export interface OverseasFeeRetryRecord {
|
|
requestId: string; requestedAt: string; operatorName: string; action: string
|
|
result: 'SUCCESS' | 'FAILED' | 'SUBMITTED'; errorMessage: string | null
|
|
}
|
|
export async function listOverseasFeeRetries(id: string): Promise<OverseasFeeRetryRecord[]> {
|
|
return unwrapApiResponse(await http.get<ApiEnvelope<OverseasFeeRetryRecord[]>, ApiEnvelope<OverseasFeeRetryRecord[]>>(`${base}/${encodeURIComponent(id)}/retries`))
|
|
}
|
|
export async function listOverseasFeeItems(direction: 'RECEIVABLE' | 'PAYABLE' = 'RECEIVABLE') {
|
|
const response = await http.get<ApiEnvelope<Array<{ code: string; name: string; defaultPrice: number | null }>>, ApiEnvelope<Array<{ code: string; name: string; defaultPrice: number | null }>>>(`${base}/fee-items`, { params: { direction } })
|
|
return unwrapApiResponse(response).map(item => ({ feeCode: item.code, feeTypeName: item.name, defaultPrice: item.defaultPrice }))
|
|
}
|
|
export async function listOverseasFeeCustomers() {
|
|
const response = await http.get<ApiEnvelope<Array<{ customerId: string; customerName: string }>>, ApiEnvelope<Array<{ customerId: string; customerName: string }>>>(`${base}/customer-options`)
|
|
return unwrapApiResponse(response).map(row => ({ ...row, customerId: String(row.customerId) }))
|
|
}
|
|
function normalizeFee(value: OverseasFee): OverseasFee {
|
|
return { ...value, id: String(value.id), sourceFeeId: String(value.sourceFeeId), customerId: String(value.customerId),
|
|
businessId: value.businessId == null ? null : String(value.businessId), orderId: value.orderId == null ? null : String(value.orderId),
|
|
warehouseId: value.warehouseId == null ? null : String(value.warehouseId), allowedActions: value.allowedActions || [] }
|
|
}
|
|
export async function listOverseasFees(query: Record<string, string | number | undefined>): Promise<FeePage<OverseasFee>> {
|
|
const response = await http.get<ApiEnvelope<FeePage<OverseasFee>>, ApiEnvelope<FeePage<OverseasFee>>>(`${base}/page`, { params: query })
|
|
const data = unwrapApiResponse(response)
|
|
return { ...data, records: data.records.map(normalizeFee), total: Number(data.total), pages: Number(data.pages) }
|
|
}
|
|
export async function listBusinessOverseasFees(business: BusinessFeeOption | BusinessFeeOption[], pagination: { page: number; size: number }, direction: OverseasFeeDirection = 'RECEIVABLE'): Promise<FeePage<OverseasFee>> {
|
|
const { page, size } = pagination
|
|
const scopes = Array.isArray(business) ? business : [business]
|
|
if (!scopes.length || scopes.some(scope => !scope.businessId || !scope.customerId || !scope.businessType)) throw new Error('业务单据信息不完整')
|
|
// 非分页接口返回两种方向;先按客户、业务身份及收付方向匹配,再合并分页。
|
|
const groups = await Promise.all(scopes.map(async scope => {
|
|
const response = await http.get<ApiEnvelope<OverseasFee[]>, ApiEnvelope<OverseasFee[]>>(base, {
|
|
params: { customerId: scope.customerId, businessType: scope.businessType, businessNo: scope.businessNo },
|
|
})
|
|
return unwrapApiResponse(response).map(normalizeFee).filter(row => row.customerId === scope.customerId && row.businessType === scope.businessType && row.businessId === scope.businessId && row.direction === direction)
|
|
}))
|
|
const records = [...new Map(groups.flat().map(row => [row.sourceFeeId, row])).values()]
|
|
.sort((left, right) => right.createdAt.localeCompare(left.createdAt) || right.sourceFeeId.localeCompare(left.sourceFeeId))
|
|
return { records: records.slice((page - 1) * size, page * size), total: records.length, pages: Math.ceil(records.length / size), page, size }
|
|
}
|
|
export async function getOverseasFee(id: string) {
|
|
return normalizeFee(unwrapApiResponse(await http.get<ApiEnvelope<OverseasFee>, ApiEnvelope<OverseasFee>>(`${base}/${encodeURIComponent(id)}`)))
|
|
}
|
|
export async function listBusinessFeeOptions(params: { businessType: string; keyword?: string; customerId?: string }) {
|
|
const response = await http.get<ApiEnvelope<BusinessFeeOption[]>, ApiEnvelope<BusinessFeeOption[]>>(`${base}/business-options`, { params })
|
|
return unwrapApiResponse(response).map(row => ({ ...row, businessId: String(row.businessId), customerId: String(row.customerId), orderId: row.orderId == null ? null : String(row.orderId), warehouseId: row.warehouseId == null ? null : String(row.warehouseId) }))
|
|
}
|
|
export async function createOverseasFee(payload: SaveOverseasFee, key: string) {
|
|
return normalizeFee(unwrapApiResponse(await http.post<ApiEnvelope<OverseasFee>, ApiEnvelope<OverseasFee>>(base, payload, { headers: { 'Idempotency-Key': key } })))
|
|
}
|
|
export async function actOnOverseasFee(id: string, action: string, body: Record<string, unknown>) {
|
|
const endpoint = action === 'APPROVE' || action === 'REJECT' ? 'review'
|
|
: ['RETRY_PUBLISH', 'RETRY_FUNDING_DELIVERY'].includes(action) ? 'retry-delivery'
|
|
: action.startsWith('RETRY_') ? 'retry-funding' : action.toLowerCase()
|
|
return normalizeFee(unwrapApiResponse(await http.post<ApiEnvelope<OverseasFee>, ApiEnvelope<OverseasFee>>(`${base}/${encodeURIComponent(id)}/${endpoint}`, body)))
|
|
}
|