import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import test from 'node:test' const source = readFileSync(new URL('./QuickOrderView.vue', import.meta.url), 'utf8') const routesSource = readFileSync(new URL('../routes.ts', import.meta.url), 'utf8') const contractsSource = readFileSync(new URL('../../tenant-management/contracts.ts', import.meta.url), 'utf8') const orderViewSource = readFileSync(new URL('./FirstLegOrdersView.vue', import.meta.url), 'utf8') test('exposes quick order as an active first-leg route with its dedicated permission', () => { assert.match(routesSource, /path: 'first-leg\/quick-order'[\s\S]*?release: 'active'[\s\S]*?permission: 'order:first-leg:quick-create'/) assert.match(contractsSource, /code: 'order:first-leg:quick-create'[\s\S]*?label: '快速下单'/) assert.match(contractsSource, /'order:first-leg:quick-create': \['merchant:order:quick-create', 'merchant:order:create'\]/) assert.match(contractsSource, /FRONTEND_PERMISSIONS_REQUIRING_ALL_BACKEND_CODES[\s\S]*?'order:first-leg:quick-create'/) assert.match(routesSource, /path: 'orders\/first-leg\/:draftId\/edit'[\s\S]*?name: 'first-leg-order-edit'[\s\S]*?permission: 'order:first-leg:edit'/) assert.match(orderViewSource, /store\.loadDraftDetail\(id\)/) }) test('keeps the primary quick-order table focused on inbound code policy, channel, destination and cargo', () => { for (const label of ['选择渠道', '目的仓', '产品信息:产品 SKU / 箱数', '新增一行', '生成草稿单']) { assert.match(source, new RegExp(label)) } assert.match(source, /getMerchantPricingChannels/) assert.match(source, /destinationType: 'FBA'/) assert.match(source, /function createProduct\(\): QuickOrderProduct/) assert.match(source, /products: \[createProduct\(\)\]/) assert.match(source, /getMerchantInboundCodePolicy/) assert.match(source, /manualInboundCodeRequired/) assert.match(source, /v-if="manualInboundCodeRequired"[^>]*>旧系统入仓码/) assert.match(source, /inboundCode: row\.inboundCode\.trim\(\) \|\| undefined/) }) test('creates one real backend draft per order row and keeps its products together', () => { assert.match(source, /function buildOrderDraft\(row: QuickOrderRow, channel: QuickChannelOption\)/) assert.match(source, /items: row\.products\.map/) assert.match(source, /const drafts = rows\.flatMap\(\(row\) =>/) assert.match(source, /clientRowId: row\.id, draft: buildOrderDraft\(row, channel\)/) assert.match(source, /store\.saveDraftBatch\(drafts, customerName, activeBatchKey\.value\)/) assert.match(source, /response\.items\.flatMap/) assert.match(source, /inboundCode: item\.inboundCode\?\.trim\(\) \?\? ''/) assert.match(source, /已生成 \$\{successes\.length\} 张草稿单/) assert.match(source, /successfulIds\.has\(rows\[index\]\.id\)/) assert.match(source, /response\.items\.filter/) assert.match(source, /activeBatchKey\.value/) assert.match(source, /name: 'first-leg-order-edit'/) assert.match(source, /params: \{ draftId: result\.draftId \}/) assert.doesNotMatch(source, /getMerchantUnifiedOrder/) assert.doesNotMatch(source, /store\.saveDraft\(/) assert.doesNotMatch(source, /store\.submit\(|store\.submitSavedDraft\(|submitMerchantFirstLegOrder/) assert.doesNotMatch(source, /name: 'first-leg-order-create'/) }) test('requires the old inbound code globally only while legacy manual mode is active', () => { assert.match(source, /manualInboundCodeRequired\.value[\s\S]*?rows\.findIndex\(\(row\) => !row\.inboundCode\.trim\(\)\)/) assert.match(source, /请填写第 \$\{missingInboundCodeIndex \+ 1\} 行的旧系统入仓码/) assert.match(source, /!row\.channelKey \|\| !row\.destinationWarehouseName/) assert.match(source, /!product\.sku\.trim\(\) \|\| product\.packageCount < 1/) assert.match(source, /渠道、目的仓、产品 SKU 和箱数/) assert.match(source, /createEmptyCargoItem/) assert.match(source, /packageCount: product\.packageCount/) assert.match(source, /sku: product\.sku\.trim\(\)/) assert.match(source, /destinationWarehouseId: row\.destinationWarehouseId/) assert.match(source, /destinationWarehouseName: row\.destinationWarehouseName/) assert.match(source, /destinationCountryCode: row\.destinationCountryCode/) assert.doesNotMatch(source, /mixedChannel/) assert.doesNotMatch(source, /confirmationDialogOpen|QuickCargoConfirmation|confirmAndCreateQuickDraft/) assert.doesNotMatch(source, /确认草稿资料|确认并生成草稿/) assert.doesNotMatch(source, /validateFirstLegOrderDraft/) assert.match(source, /@click="submitQuickOrder"/) assert.ok( source.indexOf('async function submitQuickOrder()') < source.indexOf('store.saveDraftBatch(drafts'), 'the primary quick-order action must save drafts directly', ) }) test('shows every created draft and keeps failed rows available for retry', () => { assert.match(source, /v-for="result in draftResults"/) assert.match(source, /入仓码将在提交订单时生成/) assert.match(source, /@click="editDraft\(result\)"/) assert.match(source, /if \(rows\.length === 0\) rows\.push\(createRow\(\)\)/) assert.match(source, /resultDialogOpen\.value = successes\.length > 0/) }) test('adds products inside one order row and only the footer adds a new draft row', () => { assert.match(source, /function addProduct\(row: QuickOrderRow\)[\s\S]*?row\.products\.push\(createProduct\(\)\)/) assert.match(source, /function addOrderRow\(\)[\s\S]*?rows\.push\(createRow\(\)\)/) assert.match(source, /@click="addProduct\(row\)"[^>]*>]*\/>添加产品行/) assert.match(source, /@click="addOrderRow"[^>]*>]*\/>新增一行/) assert.match(source, /v-for="\(product, productIndex\) in row\.products"/) assert.match(source, /@click="removeProduct\(row, productIndex\)"/) }) test('binds exactly one destination warehouse through a destination dialog', () => { assert.match(source, /