补齐海外仓和私人地址正常及短少收货真实验收
This commit is contained in:
@@ -2,13 +2,13 @@ import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { normalizeModuleOrder } from '../admin-test-env-acceptance.mjs'
|
||||
import { mixedReceivingSchedule, receiveMixedContainer } from './mixed-container-receiving.mjs'
|
||||
import { mixedReceivingSchedule, receiveMixedContainer, receiveOverseasComplete, receiveOverseasShortage } from './mixed-container-receiving.mjs'
|
||||
|
||||
function fixtures() {
|
||||
const record = (type, fulfillmentType = 'STANDARD') => ({ label: type,
|
||||
order: { destinationType: type, fulfillmentType }, source: { inboundId: type, inboundCode: type },
|
||||
expectedCartonMarks: [1, 2, 3, 4].map(i => `${type}#4-${i}`),
|
||||
overseas: { plan: { pallets: [{ palletNo: `PAL-${type}` }] } } })
|
||||
overseas: { plan: { pallets: [{ palletNo: `PAL-${type}` }, ...(type === 'OVERSEAS' ? [{ palletNo: 'PAL-OVERSEAS-2' }] : [])] } } })
|
||||
const d = record('DROPSHIP', 'ONE_CLICK_DROPSHIP')
|
||||
d.task = { pallets: [{ palletNo: 'DP-1', cartons: [{ inboundId: 'DROPSHIP', boxCode: 'DROPSHIP#4-1' }] },
|
||||
{ palletNo: 'DP-2', cartons: [2, 3, 4].map(i => ({ inboundId: 'DROPSHIP', boxCode: `DROPSHIP#4-${i}` })) }] }
|
||||
@@ -18,6 +18,7 @@ function fixtures() {
|
||||
test('mixed receiving is registered with prerequisites, not transfer or downstream delivery', () => {
|
||||
assert.deepEqual(normalizeModuleOrder(['mixed-container-receiving']), ['merchant-qualification-fixture', 'mixed-container-receiving'])
|
||||
const source = readFileSync(new URL('dropship-pallet-matrix.mjs', import.meta.url), 'utf8')
|
||||
assert.match(source, /await\s+adminCall\('回查普通海外仓现场逐托实收'/)
|
||||
const start = source.indexOf("const dropship = await prepare('MIXED-DROPSHIP'")
|
||||
const branch = source.slice(start, source.indexOf('if (stopBeforeOverseasPda)', start))
|
||||
for (const entry of ["'MIXED-FBA', 1, {}, 'FBA'", "'MIXED-OVERSEAS', 1, {}, 'OVERSEAS'",
|
||||
@@ -27,12 +28,19 @@ test('mixed receiving is registered with prerequisites, not transfer or downstre
|
||||
assert.match(source, /混柜完成后按入仓码查询库存/)
|
||||
assert.match(source, /String\(row.customerId\) === String\(item.order.tenantId\)/)
|
||||
assert.match(source, /String\(p.warehousePalletId\) === String\(row.palletId\)/)
|
||||
assert.match(source, /String\(item.sourceContainerBookingId\) === sourceContainerBookingId/)
|
||||
assert.match(source, /\/prepare\?sourceContainerBookingId=\$\{encodeURIComponent\(sourceContainerBookingId\)\}/)
|
||||
assert.match(source, /普通海外仓四箱预留两托/)
|
||||
assert.match(source, /普通海外仓计划二加二可按三加一实收/)
|
||||
assert.match(source, /普通海外仓预留托盘标签返回有效PDF/)
|
||||
})
|
||||
|
||||
test('schedule interleaves three modes and uses FBA cumulative count rather than delta', () => {
|
||||
const steps = mixedReceivingSchedule(fixtures())
|
||||
assert.deepEqual(steps.map(s => s.path), ['bulk', 'pallet', 'carton', 'carton', 'carton', 'carton', 'bulk', 'pallet'])
|
||||
assert.deepEqual(steps.filter(s => s.path === 'bulk').map(s => [s.count, s.body.packageCount]), [[1, 1], [3, 4]])
|
||||
assert.deepEqual(steps.filter(s => s.path === 'carton').map(s => s.body.palletCode),
|
||||
['PAL-OVERSEAS', 'PAL-OVERSEAS', 'PAL-OVERSEAS', 'PAL-OVERSEAS-2'])
|
||||
assert.equal(steps.reduce((sum, s) => sum + s.count, 0), 12)
|
||||
assert.throws(() => mixedReceivingSchedule(fixtures().slice(0, 2)))
|
||||
})
|
||||
@@ -96,3 +104,83 @@ test('mixed receiving rejects a new-key rescan that accepts another box', async
|
||||
test('mixed receiving rejects same-key different-payload success', async () => {
|
||||
await assert.rejects(receiveMixedContainer(driver({ changedPayloadAccepted: true })))
|
||||
})
|
||||
|
||||
function shortageDriver({ stockPackages = 3, wrongPalletAccepted = false } = {}) {
|
||||
const record = { order: { tenantId: 'CUSTOMER' }, source: { inboundCode: 'IN-OVERSEAS' },
|
||||
expectedCartonMarks: [1, 2, 3, 4].map(index => `IN-OVERSEAS#4-${index}`),
|
||||
overseas: { plan: { planNo: 'IP-SHORTAGE', pallets: [{ palletNo: 'PAL-1', warehousePalletId: '900' }] } } }
|
||||
const receipts = new Map()
|
||||
const confirmation = new Map()
|
||||
const state = { received: 0, status: 'SIGNED', planStatus: 'PENDING_RECONCILIATION', confirmations: 0 }
|
||||
const snapshot = () => ({ status: state.status, receivedPackages: state.received, pendingPackages: 4 - state.received })
|
||||
return { record, marker: 'SHORTAGE-TEST', state, check: (name, _response, passed) => assert.ok(passed, name),
|
||||
request: async (_method, path) => ({ body: { code: path.endsWith('/pallets/resolve') && wrongPalletAccepted ? '200' : 'A0409' } }),
|
||||
call: async (_name, method, path, body) => {
|
||||
if (method === 'GET') return snapshot()
|
||||
if (path.endsWith('/cartons/resolve')) return { inboundCode: record.source.inboundCode, destinationType: 'OVERSEAS' }
|
||||
if (path.endsWith('/pallets/resolve')) return { compatible: true, palletCode: 'PAL-1' }
|
||||
if (path.endsWith('/inventory/resolve')) return { queryCode: record.source.inboundCode, items: [{
|
||||
inboundCode: record.source.inboundCode, customerId: 'CUSTOMER', palletId: '900', locationId: '800', availablePackages: stockPackages }] }
|
||||
if (receipts.has(body.idempotencyKey)) return structuredClone(receipts.get(body.idempotencyKey))
|
||||
if (path.endsWith('/receipts/carton')) {
|
||||
state.received++
|
||||
const result = { acceptedPackages: 1 }; receipts.set(body.idempotencyKey, result); return result
|
||||
}
|
||||
assert.ok(path.endsWith('/complete'))
|
||||
assert.equal(body.allowDifference, true)
|
||||
state.status = 'COMPLETED_WITH_DIFFERENCE'
|
||||
const result = snapshot(); receipts.set(body.idempotencyKey, result); return result
|
||||
},
|
||||
adminCall: async (_name, method, path, body, headers) => {
|
||||
if (method === 'GET') return { status: state.planStatus, version: 4, expectedPackageCount: 4, pickedPackageCount: state.received }
|
||||
assert.ok(path.endsWith('/confirm-shortage'))
|
||||
assert.equal(body.expectedPackageCount, 4); assert.equal(body.receivedPackageCount, 3)
|
||||
const key = headers['Idempotency-Key']
|
||||
if (confirmation.has(key)) return confirmation.get(key)
|
||||
state.planStatus = 'PARTIAL_INBOUND'; state.confirmations++
|
||||
const result = { status: state.planStatus }; confirmation.set(key, result); return result
|
||||
} }
|
||||
}
|
||||
|
||||
test('ordinary overseas shortage preserves four expected cartons and stocks three exactly once', async () => {
|
||||
const input = shortageDriver()
|
||||
const result = await receiveOverseasShortage(input)
|
||||
assert.equal(result.shortageVerified, true)
|
||||
assert.equal(result.plan.expectedPackageCount, 4)
|
||||
assert.equal(result.plan.pickedPackageCount, 3)
|
||||
assert.equal(input.state.confirmations, 1)
|
||||
})
|
||||
test('ordinary overseas shortage fails if an unreserved pallet passes validation', async () => {
|
||||
await assert.rejects(receiveOverseasShortage(shortageDriver({ wrongPalletAccepted: true })))
|
||||
})
|
||||
test('ordinary overseas shortage fails if stock includes an unreceived carton', async () => {
|
||||
await assert.rejects(receiveOverseasShortage(shortageDriver({ stockPackages: 4 })))
|
||||
})
|
||||
|
||||
function completeDriver(stockPackages = 4) {
|
||||
const input = shortageDriver({ stockPackages })
|
||||
const call = input.call
|
||||
input.call = async (name, method, path, body) => {
|
||||
if (path.endsWith('/complete')) {
|
||||
assert.equal(body.allowDifference, false)
|
||||
assert.equal(input.state.received, 4)
|
||||
return { status: 'COMPLETED', receivedPackages: 4, pendingPackages: 0 }
|
||||
}
|
||||
return call(name, method, path, body)
|
||||
}
|
||||
input.adminCall = async (_name, method) => {
|
||||
assert.equal(method, 'GET')
|
||||
return { status: 'COMPLETED', expectedPackageCount: 4, pickedPackageCount: input.state.received }
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
test('ordinary overseas full receipt uses normal completion and stocks all four cartons', async () => {
|
||||
const result = await receiveOverseasComplete(completeDriver())
|
||||
assert.equal(result.normalCompletionVerified, true)
|
||||
assert.equal(result.stock.items[0].availablePackages, 4)
|
||||
})
|
||||
|
||||
test('ordinary overseas full receipt rejects stock inconsistent with actual received cartons', async () => {
|
||||
await assert.rejects(receiveOverseasComplete(completeDriver(3)))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user