修复采购审核竞态并整理管理页面展示
This commit is contained in:
@@ -4,6 +4,8 @@ import { ApiResponseError } from '@/shared/api/http'
|
||||
import { getLoadingAssociationSummary, getOverseasInboundCodeRelationship } from '@/shared/api/inboundCodeRelations'
|
||||
import { downloadPlatformProductImage, getPlatformUnifiedOrder, listPlatformUnifiedOrders } from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { adjustPublishedInboundFee, calculateInboundFee, confirmInboundFee, downloadPlatformInboundHoldEvidence, getInboundMeasurement, getLatestInboundFee, getPlatformInboundHold, holdPlatformInbound, previewInboundFee, releasePlatformInbound, sendInboundFee, updateMeasurementOuterDimensions, uploadPlatformInboundHoldEvidence, type InboundFeePreview } from '@/modules/inbounds'
|
||||
import { listCustomerChargeRules } from '@/modules/fba-pricing'
|
||||
import { getCustomerDimensionConfig } from '@/modules/dimension-config'
|
||||
import UnifiedOrdersView from './UnifiedOrdersView.vue'
|
||||
|
||||
const { routeQuery, routerPush } = vi.hoisted(() => ({ routeQuery: { tenantId: '6001', from: '' }, routerPush: vi.fn() }))
|
||||
@@ -16,6 +18,8 @@ vi.mock('@/core/access/accessPreview', () => ({ useAccessPreviewStore: () => ({
|
||||
vi.mock('@/modules/iam', async importOriginal => ({
|
||||
...await importOriginal<typeof import('@/modules/iam')>(), listPlatformTenants: vi.fn().mockResolvedValue({ records: [], page: 1, size: 200, total: 0, pages: 0 }) }))
|
||||
vi.mock('@/modules/warehouses', () => ({ listAllPlatformWarehouses: vi.fn().mockResolvedValue([]) }))
|
||||
vi.mock('@/modules/fba-pricing', () => ({ listCustomerChargeRules: vi.fn() }))
|
||||
vi.mock('@/modules/dimension-config', () => ({ getCustomerDimensionConfig: vi.fn() }))
|
||||
vi.mock('@/modules/inbounds', () => ({
|
||||
downloadWeightImage: vi.fn(), getInboundMeasurement: vi.fn(),
|
||||
updateMeasurementOuterDimensions: vi.fn(),
|
||||
@@ -117,6 +121,8 @@ describe('UnifiedOrdersView downstream fulfillment detail', () => {
|
||||
afterEach(() => vi.useRealTimers())
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.mocked(getCustomerDimensionConfig).mockReset().mockResolvedValue({ tenantId: '6001', defaultCalculationMode: 'ACTUAL', rules: [] })
|
||||
vi.mocked(listCustomerChargeRules).mockReset().mockResolvedValue([])
|
||||
vi.mocked(previewInboundFee).mockReset().mockResolvedValue(feePreview())
|
||||
routeQuery.from = ''
|
||||
canPermission.mockImplementation(() => true)
|
||||
@@ -160,6 +166,55 @@ describe('UnifiedOrdersView downstream fulfillment detail', () => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ defaultMode: 'ADD' as const, outerDimensions: [66, 56, 56], outerUnitVolume: 0.207, outerVolume: 4.554 },
|
||||
{ defaultMode: 'MULTIPLY' as const, outerDimensions: [77.5, 65, 65], outerUnitVolume: 0.3275, outerVolume: 7.205 },
|
||||
])('keeps declared dimensions and volume original when the customer default is $defaultMode', async ({ defaultMode, outerDimensions, outerUnitVolume, outerVolume }) => {
|
||||
const cargo = { itemNo: 1, lengthCm: 60, widthCm: 50, heightCm: 50, packageCount: 22, grossWeightKg: 200, volumeCbm: 3.3 }
|
||||
vi.mocked(getPlatformUnifiedOrder).mockResolvedValue({ ...detail, order: { ...detail.order, cargoes: [cargo] } } as never)
|
||||
vi.mocked(getCustomerDimensionConfig).mockResolvedValue({
|
||||
tenantId: '6001', defaultCalculationMode: defaultMode,
|
||||
rules: [{ id: 'dimension-1', boxType: 'EXTRA_LARGE', boxTypeName: '特大箱',
|
||||
innerLengthCm: 60, innerWidthCm: 50, innerHeightCm: 50, edgeAdditionCm: 2,
|
||||
outerLengthCm: 64, outerWidthCm: 54, outerHeightCm: 54, volumeCbm: 0.187,
|
||||
remark: null, version: 1, createdAt: null, updatedAt: null }],
|
||||
})
|
||||
vi.mocked(listCustomerChargeRules).mockResolvedValue([{
|
||||
id: 'rule-1', tenantId: '6001', ruleCode: 'DEFAULT', ruleVersion: 1, customerId: '6001', customerName: '测试客户',
|
||||
businessType: 'FBA', chargeMode: 'KG', taxMode: 'TAX_INCLUDED', conversionFactor: 167,
|
||||
lengthFactor: 1.25, widthFactor: 1.25, heightFactor: 1.25, defaultRule: true,
|
||||
roundingScale: 4, roundingMode: 'CEILING', currency: 'CNY', effectiveFrom: '2026-01-01',
|
||||
effectiveTo: null, status: 'ACTIVE', remark: null,
|
||||
}])
|
||||
const measurement = savedOuterMeasurement()
|
||||
vi.mocked(getInboundMeasurement).mockResolvedValue({ ...measurement, items: [{ ...measurement.items[0],
|
||||
outerLengthCm: null, outerWidthCm: null, outerHeightCm: null,
|
||||
calculatedOuterLengthCm: outerDimensions[0], calculatedOuterWidthCm: outerDimensions[1], calculatedOuterHeightCm: outerDimensions[2],
|
||||
coefficientMode: defaultMode, calculationRuleVersion: 'CUSTOMER_DIMENSION_V1',
|
||||
unitVolumeCbm: outerUnitVolume, totalVolumeCbm: outerVolume, volumeWeightKg: outerVolume * 167,
|
||||
}] } as never)
|
||||
const wrapper = mountDetail()
|
||||
await flushPromises()
|
||||
const vm = wrapper.vm as unknown as {
|
||||
customerMeasurementRows: Record<string, unknown>[]
|
||||
outerMeasurementRows: Record<string, unknown>[]
|
||||
actualMeasurementRows: Record<string, unknown>[]
|
||||
measurementSectionModes: { customer: string; outer: string; actual: string }
|
||||
feeConfirmRows: { source: string; count: number; weight: number; volume: number }[]
|
||||
loadMeasurementDetails(): Promise<void>
|
||||
}
|
||||
await vm.loadMeasurementDetails()
|
||||
expect(vm.customerMeasurementRows[0]).toMatchObject({ lengthCm: 60, widthCm: 50, heightCm: 50, unitVolumeCbm: 0.15, totalVolumeCbm: 3.3 })
|
||||
const customerReference = vm.feeConfirmRows.find(row => row.source === '客户下单数据')!
|
||||
expect(customerReference).toMatchObject({ count: 22, volume: 3.3 })
|
||||
expect(customerReference.weight).toBeCloseTo(200)
|
||||
expect(vm.measurementSectionModes).toEqual({ customer: 'ACTUAL', outer: defaultMode, actual: 'ACTUAL' })
|
||||
expect(vm.outerMeasurementRows[0]).toMatchObject({ lengthCm: outerDimensions[0], widthCm: outerDimensions[1], heightCm: outerDimensions[2], totalVolumeCbm: outerVolume })
|
||||
expect(vm.actualMeasurementRows[0]).toMatchObject({ lengthCm: 62, widthCm: 52, heightCm: 52, totalVolumeCbm: 3.6894 })
|
||||
expect(cargo).toEqual({ itemNo: 1, lengthCm: 60, widthCm: 50, heightCm: 50, packageCount: 22, grossWeightKg: 200, volumeCbm: 3.3 })
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('shows actual and outer unit volumes and totals consistently with four decimal places', async () => {
|
||||
const wrapper = mountDetail()
|
||||
await flushPromises()
|
||||
@@ -246,6 +301,41 @@ describe('UnifiedOrdersView downstream fulfillment detail', () => {
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('keeps confirm-and-send available after sending until the customer confirms, without reloading the page', async () => {
|
||||
const fee = { unitPrice: 13.6, chargeableQuantity: 650.298, chargeUnit: 'KG', roundingScale: 4, rateCode: 'RATE', feeStatus: 'CALCULATED' }
|
||||
vi.mocked(calculateInboundFee).mockResolvedValue(fee as never)
|
||||
vi.mocked(confirmInboundFee).mockResolvedValue({ ...fee, feeStatus: 'CONFIRMED' } as never)
|
||||
vi.mocked(sendInboundFee).mockResolvedValue({ ...fee, feeStatus: 'SENT' } as never)
|
||||
const wrapper = mountDetail()
|
||||
await flushPromises()
|
||||
const vm = wrapper.vm as unknown as FeeConfirmVm & {
|
||||
activeDetailTab: string
|
||||
automaticFee: { feeStatus: string } | null
|
||||
}
|
||||
vm.activeDetailTab = 'measurement'
|
||||
vm.measurementDetail = { ...savedOuterMeasurement(), customerFeeConfirmed: false }
|
||||
await flushPromises()
|
||||
expect(wrapper.find('[data-testid="confirm-send-fee-button"]').exists()).toBe(true)
|
||||
|
||||
await vm.openFeeConfirmDialog()
|
||||
await vm.executeConfirmAndSendFee()
|
||||
await flushPromises()
|
||||
expect(vm.automaticFee?.feeStatus).toBe('SENT')
|
||||
expect(wrapper.find('[data-testid="confirm-send-fee-button"]').exists()).toBe(true)
|
||||
|
||||
await wrapper.get('[data-testid="confirm-send-fee-button"]').trigger('click')
|
||||
await flushPromises()
|
||||
await vm.executeConfirmAndSendFee()
|
||||
await flushPromises()
|
||||
expect(sendInboundFee).toHaveBeenCalledTimes(2)
|
||||
expect(wrapper.find('[data-testid="confirm-send-fee-button"]').exists()).toBe(true)
|
||||
|
||||
vm.measurementDetail = { ...vm.measurementDetail, customerFeeConfirmed: true }
|
||||
await flushPromises()
|
||||
expect(wrapper.find('[data-testid="confirm-send-fee-button"]').exists()).toBe(false)
|
||||
wrapper.unmount()
|
||||
})
|
||||
|
||||
it('refreshes the amount from current outer values instead of an older fee snapshot', async () => {
|
||||
vi.mocked(getLatestInboundFee).mockResolvedValue({ unitPrice: 13.6, chargeableQuantity: 616.1298, totalAmount: 8379.37 } as never)
|
||||
const wrapper = mountDetail()
|
||||
@@ -257,6 +347,8 @@ describe('UnifiedOrdersView downstream fulfillment detail', () => {
|
||||
expect(vm.feeConfirmVolume).toBe(3.894)
|
||||
expect(vm.feeConfirmReference?.chargeableQuantity).toBe(650.298)
|
||||
expect(vm.feeConfirmEstimatedTotal).toBe(8844.05)
|
||||
const referenceRow = wrapper.findAll('tr').find(row => row.text().startsWith('参考计费量'))!
|
||||
expect(referenceRow.findAll('td')[0]!.text()).toBe('650.3 KG')
|
||||
expect(getLatestInboundFee).not.toHaveBeenCalled()
|
||||
expect(calculateInboundFee).not.toHaveBeenCalled()
|
||||
wrapper.unmount()
|
||||
|
||||
Reference in New Issue
Block a user