Files
admin-web/src/modules/overseas-business/overseasFeePresentation.test.ts
T

92 lines
7.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { feeActions, feePermissions, feeProcessingLabel, feeProcessingStatus, feeStatus, feeTime, sameProjectFees, sameProjectFeeSummary } from './overseasFeePresentation'
import type { OverseasFee } from './api/overseasFees'
const fee = (overrides: Partial<OverseasFee> = {}): OverseasFee => ({
direction: 'RECEIVABLE',
id: '9007199254740993', sourceFeeId: '9007199254740993', sourceVersion: 1, customerId: '1', customerName: '客户',
sourceType: 'OVERSEAS_WAREHOUSE_FEE', businessType: 'WAREHOUSE_TRANSFER', businessId: '2', businessNo: 'WT1',
orderId: null, warehouseId: '3', feeItemCode: 'FEE1', feeItemName: '操作费', businessDate: '2026-10-22',
currency: 'CNY', amount: 70, originalCurrency: 'USD', originalAmount: 10, chargeableQuantity: null, quantityUnit: null,
unitPrice: null, periodStart: null, periodEnd: null, reviewStatus: 'PENDING', sourceStatus: 'ACTIVE', publicationStatus: 'PENDING',
collectionMode: 'WALLET', walletStatus: 'FROZEN', walletErrorMessage: null, reviewReason: null, reviewedAt: null,
createdAt: '', updatedAt: '', allowedActions: [], ...overrides,
})
describe('海外费用动作资格', () => {
it.each(['DRAFT', 'PENDING'])('审核前按服务端资格和独立权限展示调整、作废:%s', reviewStatus => {
const pending = fee({ reviewStatus, allowedActions: ['ADJUST', 'VOID', 'APPROVE', 'REJECT'] })
expect(feeActions(pending, () => true).map(row => row.key)).toEqual(['DETAIL', 'ADJUST', 'VOID', 'REVIEW'])
expect(feeActions(pending, permission => permission === feePermissions.edit).map(row => row.key)).toEqual(['DETAIL', 'ADJUST'])
expect(feeActions(pending, permission => permission === feePermissions.review).map(row => row.key)).toEqual(['DETAIL', 'VOID', 'REVIEW'])
expect(feeActions(fee({ reviewStatus }), () => true).map(row => row.key)).toEqual(['DETAIL'])
})
it.each(['BILL', 'WALLET'])('审核通过后即使旧白名单仍包含调整、作废也不展示:%s', collectionMode => {
const approved = fee({ reviewStatus: 'APPROVED', collectionMode, walletStatus: collectionMode === 'WALLET' ? 'SETTLED' : 'NOT_REQUIRED', allowedActions: ['ADJUST', 'VOID'] })
expect(feeActions(approved, () => true).map(row => row.key)).toEqual(['DETAIL'])
})
it('明确时区的同一时刻显示一致,旧无时区时间不猜测转换', () => {
expect(feeTime('2026-10-23T10:43:25Z')).toBe(feeTime('2026-10-23T18:43:25+08:00'))
const date = new Date('2026-10-23T10:43:25Z')
expect(feeTime('2026-10-23T10:43:25Z')).toContain(String(date.getHours()).padStart(2, '0') + ':43:25')
expect(feeTime('2026-10-23T10:43:25')).toBe('2026-10-23 10:43:25')
expect(feeTime(null)).toBe('—')
})
it('以服务端动作白名单为准,不根据待审核状态自行开放审核', () => {
expect(feeActions(fee(), () => true).map(row => row.key)).toEqual(['DETAIL'])
expect(feeActions(fee({ allowedActions: ['APPROVE', 'REJECT', 'CANCEL'] }), permission => permission === feePermissions.review).map(row => row.key)).toEqual(['DETAIL', 'REVIEW'])
expect(feeActions(fee({ allowedActions: ['APPROVE', 'CANCEL'] }), permission => permission === feePermissions.edit).map(row => row.key)).toEqual(['DETAIL'])
expect(feeActions(fee({ allowedActions: ['REJECT', 'CANCEL'] }), permission => permission === feePermissions.review).map(row => row.key)).toEqual(['DETAIL', 'REVIEW'])
})
it('不为未知动作暴露按钮,请求中全部动作禁用', () => {
const actions = feeActions(fee({ allowedActions: ['DELETE', 'RETRY_FREEZE'] }), () => true, true)
expect(actions.map(row => row.key)).toEqual(['DETAIL', 'RETRY_FREEZE'])
expect(actions.every(row => row.disabled)).toBe(true)
})
it.each([{ reviewStatus: 'REJECTED' }, { sourceStatus: 'VOIDED' }])('关闭费用仅保留服务端允许的资金释放恢复 %o', overrides => {
const closedFee = fee({ ...overrides, walletStatus: 'RELEASE_FAILED', allowedActions: ['APPROVE', 'ADJUST', 'RETRY_SETTLE', 'RETRY_FREEZE', 'RETRY_RELEASE', 'RETRY_FUNDING_DELIVERY'] })
expect(feeActions(closedFee, () => true).map(row => row.key)).toEqual(['DETAIL', 'RETRY_RELEASE', 'RETRY_FUNDING_DELIVERY'])
expect(feeActions(closedFee, () => false).map(row => row.key)).toEqual(['DETAIL'])
expect(feeProcessingLabel(closedFee)).toBe('处理失败')
})
it('区分账单推送与实际余额扣款', () => {
expect(feeStatus(fee({ walletStatus: 'SETTLE_PENDING' }))).toBe('扣费中')
expect(feeStatus(fee({ walletStatus: 'SETTLED' }))).toBe('已扣费')
expect(feeStatus(fee({ collectionMode: 'BILL', publicationStatus: 'PUBLISHED' }))).toBe('已推送结算')
expect(feeStatus(fee({ sourceStatus: 'CANCELLED' }))).toBe('已取消')
})
it.each([
[{ walletStatus: 'FROZEN' }, 'WAITING'], [{ walletStatus: 'FREEZE_PENDING' }, 'PROCESSING'],
[{ walletStatus: 'SETTLE_FAILED' }, 'FAILED'], [{ walletStatus: 'RELEASE_FAILED' }, 'FAILED'],
[{ walletStatus: 'SETTLED' }, 'COMPLETED'], [{ collectionMode: 'BILL', publicationStatus: 'PUBLISHED', deliveryStatus: 'PENDING' }, 'PROCESSING'],
[{ publicationStatus: 'PUBLISHED', deliveryStatus: 'DEAD' }, 'FAILED'], [{ publicationStatus: 'PUBLISHED', deliveryStatus: 'PUBLISHED' }, 'COMPLETED'],
[{ publicationStatus: 'PUBLISHED', walletStatus: 'SETTLED', deliveryStatus: 'PENDING', deliveryErrorMessage: 'S0500' }, 'COMPLETED'],
[{ publicationStatus: 'PUBLISHED', walletStatus: 'SETTLED', deliveryStatus: 'PROCESSING', deliveryErrorMessage: 'S0500' }, 'COMPLETED'],
[{ publicationStatus: 'PUBLISHED', deliveryStatus: 'PUBLISHED', deliveryErrorMessage: '旧错误' }, 'COMPLETED'],
[{ reviewStatus: 'REJECTED', walletStatus: 'RELEASE_PENDING' }, 'PROCESSING'], [{ sourceStatus: 'VOIDED' }, 'CLOSED'],
] as Array<[Partial<OverseasFee>, string]>)('业务状态归并 %o → %s', (overrides, status) => {
expect(feeProcessingStatus(fee(overrides))).toBe(status)
})
it('业务界面只显示完成或失败,审核前及短暂处理过程不显示额外状态', () => {
for (const walletStatus of ['FREEZE_PENDING', 'FROZEN', 'SETTLE_PENDING', 'RELEASE_PENDING', 'RELEASED']) {
expect(feeProcessingLabel(fee({ walletStatus }))).toBe('—')
}
expect(feeProcessingLabel(fee({ reviewStatus: 'REJECTED' }))).toBe('—')
expect(feeProcessingLabel(fee({ walletStatus: 'SETTLE_FAILED' }))).toBe('处理失败')
expect(feeProcessingLabel(fee({ walletStatus: 'SETTLED' }))).toBe('已完成')
})
it('应付成功只表示推送成功,失败或等待回执时不能显示已付款', () => {
expect(feeProcessingLabel(fee({ direction: 'PAYABLE', collectionMode: 'BILL', walletStatus: 'NOT_REQUIRED', publicationStatus: 'PUBLISHED', deliveryStatus: 'PUBLISHED' }))).toBe('推送成功')
expect(feeProcessingLabel(fee({ direction: 'PAYABLE', collectionMode: 'BILL', publicationStatus: 'PUBLISHED', deliveryStatus: 'DEAD' }))).toBe('处理失败')
expect(feeProcessingLabel(fee({ direction: 'PAYABLE', collectionMode: 'BILL', publicationStatus: 'PUBLISHED', deliveryStatus: 'PENDING' }))).toBe('—')
})
it('重复提醒包含驳回记录且区分收付方向,金额按原币展示', () => {
const original = fee({ sourceFeeId: 'A', reviewStatus: 'REJECTED' })
const payable = fee({ sourceFeeId: 'B', direction: 'PAYABLE' })
const voided = fee({ sourceFeeId: 'C', sourceStatus: 'VOIDED' })
const differentItem = fee({ sourceFeeId: 'D', feeItemCode: 'OTHER' })
expect(sameProjectFees([original, payable, voided, differentItem], 'FEE1', 'RECEIVABLE')).toEqual([original])
expect(sameProjectFees([original], 'FEE1', 'RECEIVABLE', 'A')).toEqual([])
expect(sameProjectFeeSummary(original)).toBe('USD 10.00 · 已驳回')
})
})