58 lines
3.4 KiB
JavaScript
58 lines
3.4 KiB
JavaScript
import test from 'node:test'
|
|||
|
|
import assert from 'node:assert/strict'
|
||
|
|
import { cartonTotals, normalizeOrderInput, measurementPlan, assertConfirmedWeight } from './prod-release-measurement-plan.mjs'
|
||
|
|
|
||
|
|
const pallet = { id: '10', warehouseId: '20', palletCode: 'FIXTURE-A', capacityCbm: '1.200000', weightKg: '8.000' }
|
||
|
|
const plan = (patch = {}) => measurementPlan({ packageCount: 2, warehouseId: '20', pallets: [pallet], allowedCodes: ['FIXTURE-A'], ...patch })
|
||
|
|
test('agreed carton profile: 7.5kg, 40x30x30cm', () => {
|
||
|
|
assert.deepEqual(cartonTotals(2), { packageCount: 2, lengthCm: 40, widthCm: 30, heightCm: 30, grossWeightKg: 15, volumeCbm: 0.072 })
|
||
|
|
assert.equal(cartonTotals(1).volumeCbm, 0.036)
|
||
|
|
for (const count of [0, -1, 3, 1.5, '2', NaN]) assert.throws(() => cartonTotals(count))
|
||
|
|
})
|
||
|
|
test('declaration normalization preserves product facts and never mutates source', () => {
|
||
|
|
const input = { payload: { cargoes: [{ packageCount: 2, skuNo: 'fixture', grossWeightKg: 500, netWeightKg: 12 }] } }
|
||
|
|
const normalized = normalizeOrderInput(input)
|
||
|
|
assert.equal(normalized.payload.cargoes[0].grossWeightKg, 15)
|
||
|
|
assert.equal(normalized.payload.cargoes[0].skuNo, 'fixture')
|
||
|
|
assert.equal(normalized.payload.cargoes[0].netWeightKg, 12)
|
||
|
|
assert.equal(input.payload.cargoes[0].grossWeightKg, 500)
|
||
|
|
assert.throws(() => normalizeOrderInput({ payload: { cargoes: [{ packageCount: 1, netWeightKg: 8 }] } }), /净重/)
|
||
|
|
})
|
||
|
|
test('two cartons on existing 8kg tare pallet: scale 23kg, cargo 15kg', () => {
|
||
|
|
const result = plan()
|
||
|
|
assert.equal(result.palletCount, 1)
|
||
|
|
assert.equal(result.weightGrossTotalKg, 23)
|
||
|
|
assert.equal(result.weights[0].netWeightKg, 15)
|
||
|
|
assert.equal(result.measurementWritten, false)
|
||
|
|
assert.equal(result.fullFlowVerified, false)
|
||
|
|
})
|
||
|
|
test('exact volume boundaries use decimal integer arithmetic', () => {
|
||
|
|
assert.equal(plan({ pallets: [{ ...pallet, capacityCbm: '0.072000' }] }).palletCount, 1)
|
||
|
|
const result = plan({ pallets: [{ ...pallet, capacityCbm: '0.071999' }] })
|
||
|
|
assert.equal(result.palletCount, 2)
|
||
|
|
assert.equal(result.weights.reduce((sum, item) => sum + item.packageCount, 0), 2)
|
||
|
|
assert.equal(result.weightGrossTotalKg, 31)
|
||
|
|
assert.ok(result.weights.every(w => w.palletId === pallet.id))
|
||
|
|
})
|
||
|
|
test('missing capacity or tare never falls back to a fabricated default', () => {
|
||
|
|
for (const changes of [{ capacityCbm: 0 }, { capacityCbm: undefined }, { capacityCbm: '0.0000001' },
|
||
|
|
{ weightKg: null }, { weightKg: -1 }, { warehouseId: '99' }, { id: 10 }]) {
|
||
|
|
assert.throws(() => plan({ pallets: [{ ...pallet, ...changes }] }))
|
||
|
|
}
|
||
|
|
assert.equal(plan({ pallets: [{ ...pallet, weightKg: 0 }] }).weightGrossTotalKg, 15)
|
||
|
|
})
|
||
|
|
test('missing/duplicate allowlisted pallet fails; no automatic substitute', () => {
|
||
|
|
assert.throws(() => plan({ allowedCodes: [] }))
|
||
|
|
assert.throws(() => plan({ allowedCodes: ['FIXTURE-A', 'MISSING'] }))
|
||
|
|
assert.throws(() => plan({ pallets: [pallet, pallet] }))
|
||
|
|
assert.throws(() => plan({ pallets: [{ ...pallet, capacityCbm: '0.035' }] }), /完整箱/)
|
||
|
|
})
|
||
|
|
test('confirmed weights must preserve tare and cargo totals', () => {
|
||
|
|
const expected = plan().weights[0]
|
||
|
|
assert.doesNotThrow(() => assertConfirmedWeight({ ...expected, recognitionStatus: 'CONFIRMED' }, expected))
|
||
|
|
for (const patch of [{ palletId: '99' }, { recognitionStatus: 'NEEDS_MANUAL' }, { grossWeightKg: 15 },
|
||
|
|
{ netWeightKg: 7 }, { palletTareWeightKg: 0 }]) {
|
||
|
|
assert.throws(() => assertConfirmedWeight({ ...expected, recognitionStatus: 'CONFIRMED', ...patch }, expected))
|
||
|
|
}
|
||
|
|
})
|