From 2f1babae45c5aa3ca57fa6fb48e1d1215c76e4a9 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 2 Oct 2026 12:38:28 +0800 Subject: [PATCH] fix(client): count only pending customs uploads --- .../utils/customsEligibility.test.mjs | 33 +++++++++++++++++++ .../first-leg/utils/customsEligibility.ts | 5 +++ .../first-leg/views/FirstLegOrdersView.vue | 6 ++-- 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 src/modules/first-leg/utils/customsEligibility.test.mjs diff --git a/src/modules/first-leg/utils/customsEligibility.test.mjs b/src/modules/first-leg/utils/customsEligibility.test.mjs new file mode 100644 index 0000000..c81a6de --- /dev/null +++ b/src/modules/first-leg/utils/customsEligibility.test.mjs @@ -0,0 +1,33 @@ +import test from 'node:test' +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import ts from 'typescript' +import { computed, ref } from 'vue' + +const source = readFileSync(new URL('./customsEligibility.ts', import.meta.url), 'utf8') +const exports = {} +new Function('exports', ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS } }).outputText)(exports) + +test('upload queue contains only customer uploads still required, while completed documents stay accessible', () => { + const states = [null, {}, { status: 'WAITING_CUSTOMER_DOCUMENTS', merchantStatus: 'DOCUMENT_UPLOADED' }, + { status: 'READY_TO_START', merchantStatus: 'DOCUMENT_UPLOADED' }, + { status: 'DECLARING', merchantStatus: 'DECLARING' }, { status: 'COMPLETED', merchantStatus: 'CUSTOMS_COMPLETED' }] + for (const detail of states) assert.equal(exports.needsCustomsUpload(detail), false) + assert.equal(exports.needsCustomsUpload({ status: 'WAITING_CUSTOMER_DOCUMENTS', merchantStatus: 'WAITING_UPLOAD' }), true) + for (const status of ['LOADED', 'DELIVERED', 'COMPLETED']) assert.equal(exports.isCustomsOrderCandidate({ status, taxMode: 'TAX_REFUND' }), true) +}) + +test('actual page count and queue filter both drop a record when its upload is complete', () => { + const page = readFileSync(new URL('../views/FirstLegOrdersView.vue', import.meta.url), 'utf8') + const block = page.slice(page.indexOf('const orderQueueCounts ='), page.indexOf('const pagedOrders =')) + const rows = ref([{ id: 'waiting', businessSummary: {} }, { id: 'completed', businessSummary: {} }]) + const states = ref({ waiting: { status: 'WAITING_CUSTOMER_DOCUMENTS', merchantStatus: 'WAITING_UPLOAD' }, completed: { status: 'COMPLETED', merchantStatus: 'CUSTOMS_COMPLETED' } }) + const queue = ref('customs') + const read = new Function('computed', 'queryMatchedOrders', 'activeOrderQueue', 'eligibleCustomsDetail', 'needsCustomsUpload', block + '\nreturn { orderQueueCounts, filteredOrders }') + const view = read(computed, rows, queue, order => states.value[order.id], exports.needsCustomsUpload) + assert.equal(view.orderQueueCounts.value.customs, 1) + assert.deepEqual(view.filteredOrders.value.map(row => row.id), ['waiting']) + states.value.waiting.merchantStatus = 'DOCUMENT_UPLOADED' + assert.equal(view.orderQueueCounts.value.customs, 0); assert.equal(view.filteredOrders.value.length, 0) + queue.value = 'all'; assert.equal(view.filteredOrders.value.length, 2) +}) diff --git a/src/modules/first-leg/utils/customsEligibility.ts b/src/modules/first-leg/utils/customsEligibility.ts index f766f37..43de102 100644 --- a/src/modules/first-leg/utils/customsEligibility.ts +++ b/src/modules/first-leg/utils/customsEligibility.ts @@ -1,6 +1,11 @@ import type { FirstLegOrder } from '../types/firstLegOrder' +import type { MerchantCustomsContainer } from '../api/customs' export function isCustomsOrderCandidate(order: Pick): boolean { return order.taxMode === 'TAX_REFUND' && (order.status === 'LOADED' || order.status === 'DELIVERED' || order.status === 'COMPLETED') } + +export function needsCustomsUpload(detail: MerchantCustomsContainer | null | undefined): boolean { + return detail?.status === 'WAITING_CUSTOMER_DOCUMENTS' && detail.merchantStatus === 'WAITING_UPLOAD' +} diff --git a/src/modules/first-leg/views/FirstLegOrdersView.vue b/src/modules/first-leg/views/FirstLegOrdersView.vue index 862ee54..bfef232 100644 --- a/src/modules/first-leg/views/FirstLegOrdersView.vue +++ b/src/modules/first-leg/views/FirstLegOrdersView.vue @@ -15,7 +15,7 @@ import OrderProgressPanel from '@/modules/first-leg/components/OrderProgressPane import CustomsDocumentsDialog from '@/modules/first-leg/components/CustomsDocumentsDialog.vue' import { getMerchantInboundBilling } from '@/modules/first-leg/api/inboundBilling' import { getMerchantCustomsForOrder, isMerchantCustomsUnavailable, type MerchantCustomsContainer } from '@/modules/first-leg/api/customs' -import { isCustomsOrderCandidate } from '../utils/customsEligibility' +import { isCustomsOrderCandidate, needsCustomsUpload } from '../utils/customsEligibility' import { estimateMerchantFirstLegPrice, createIdempotencyKey, @@ -477,12 +477,12 @@ const queryMatchedOrders = computed(() => store.orders.filter(matchesAppliedFilt const orderQueueCounts = computed(() => ({ all: queryMatchedOrders.value.length, fee: queryMatchedOrders.value.filter((order) => order.businessSummary.customerFeeConfirmationRequired).length, - customs: queryMatchedOrders.value.filter((order) => Boolean(eligibleCustomsDetail(order))).length, + customs: queryMatchedOrders.value.filter((order) => needsCustomsUpload(eligibleCustomsDetail(order))).length, draft: queryMatchedOrders.value.filter((order) => order.status === 'DRAFT').length, })) const filteredOrders = computed(() => queryMatchedOrders.value.filter((order) => { if (activeOrderQueue.value === 'fee') return order.businessSummary.customerFeeConfirmationRequired - if (activeOrderQueue.value === 'customs') return Boolean(eligibleCustomsDetail(order)) + if (activeOrderQueue.value === 'customs') return needsCustomsUpload(eligibleCustomsDetail(order)) if (activeOrderQueue.value === 'draft') return order.status === 'DRAFT' return true }))