feat: model mixed-SKU dropship cartons
This commit is contained in:
@@ -54,7 +54,7 @@ function validDraft() {
|
||||
destinationType: 'FBA',
|
||||
amazonWarehouseCode: 'ONT8',
|
||||
shipmentId: 'FBA-001',
|
||||
items: [{ ...createEmptyCargoItem('cargo-1'), sku: 'SKU-001', upc: '012345678905', cargoNameCn: '家居用品', pieces: 100, grossWeightKg: 1200, volumeCbm: 3.5, shipmentId: 'FBA-001', referenceId: 'REF-001' }],
|
||||
items: [{ ...createEmptyCargoItem('cargo-1'), sku: 'SKU-001', upc: '012345678905', cargoNameCn: '家居用品', unitsPerPackage: 100, pieces: 100, grossWeightKg: 1200, volumeCbm: 3.5, shipmentId: 'FBA-001', referenceId: 'REF-001' }],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,17 @@ test('validates the confirmed minimum submission fields', () => {
|
||||
assert.ok(errors.includes('第 1 条货物毛重必须大于 0'))
|
||||
})
|
||||
|
||||
test('rejects a total quantity that does not equal units per package multiplied by package count', () => {
|
||||
const draft = validDraft()
|
||||
draft.items[0].unitsPerPackage = 24
|
||||
draft.items[0].packageCount = 5
|
||||
draft.items[0].pieces = 119
|
||||
|
||||
assert.ok(validateFirstLegOrderDraft(draft).includes('第 1 条货物总数量应等于每箱数量 × 箱数(120)'))
|
||||
draft.items[0].pieces = 120
|
||||
assert.ok(!validateFirstLegOrderDraft(draft).some((message) => message.includes('总数量应等于')))
|
||||
})
|
||||
|
||||
test('only requires sender for pickup and receiver for a private destination', () => {
|
||||
const publicDraft = validDraft()
|
||||
publicDraft.shipperPartySnapshot = null
|
||||
@@ -169,3 +180,15 @@ test('summarizes cargo values from line items', () => {
|
||||
volumeCbm: 4.75,
|
||||
})
|
||||
})
|
||||
|
||||
test('counts one physical carton series for SKU lines in the same mixed-carton group', () => {
|
||||
const first = { ...validDraft().items[0], packingGroupNo: 'A', packageCount: 10, pieces: 20, unitsPerPackage: 2 }
|
||||
const second = { ...first, id: 'cargo-2', sku: 'SKU-002', pieces: 30, unitsPerPackage: 3 }
|
||||
|
||||
assert.equal(summarizeFirstLegCargo([first, second]).packageCount, 10)
|
||||
const draft = { ...validDraft(), fulfillmentType: 'ONE_CLICK_DROPSHIP', items: [first, second] }
|
||||
assert.ok(!validateFirstLegOrderDraft(draft).some((message) => message.includes('混箱组合')))
|
||||
|
||||
second.packageCount = 9
|
||||
assert.ok(validateFirstLegOrderDraft(draft).includes('混箱组合 A 的箱数、包装类型和箱规必须一致'))
|
||||
})
|
||||
|
||||
@@ -65,6 +65,7 @@ export function createEmptyCargoItem(id = `cargo-${Date.now()}`): FirstLegCargoI
|
||||
unitsPerPackage: 1,
|
||||
pieces: 1,
|
||||
packageCount: 1,
|
||||
packingGroupNo: '',
|
||||
grossWeightKg: 0,
|
||||
volumeCbm: 0,
|
||||
netWeightKg: 0,
|
||||
@@ -130,22 +131,48 @@ export function validateFirstLegOrderDraft(draft: FirstLegOrderDraft) {
|
||||
if (!Number.isInteger(item.pieces) || item.pieces <= 0) errors.push(`${line}件数必须为正整数`)
|
||||
if (!Number.isInteger(item.packageCount) || item.packageCount <= 0) errors.push(`${line}箱数必须为正整数`)
|
||||
if (!Number.isInteger(item.unitsPerPackage) || item.unitsPerPackage <= 0) errors.push(`${line}每箱数量必须为正整数`)
|
||||
if (Number.isInteger(item.unitsPerPackage) && item.unitsPerPackage > 0
|
||||
&& Number.isInteger(item.packageCount) && item.packageCount > 0
|
||||
&& Number.isInteger(item.pieces) && item.pieces > 0
|
||||
&& item.pieces !== item.unitsPerPackage * item.packageCount) {
|
||||
errors.push(`${line}总数量应等于每箱数量 × 箱数(${item.unitsPerPackage * item.packageCount})`)
|
||||
}
|
||||
if (item.grossWeightKg <= 0) errors.push(`${line}毛重必须大于 0`)
|
||||
if (item.volumeCbm <= 0) errors.push(`${line}体积必须大于 0`)
|
||||
if (item.woodenProduct && draft.customsInspection !== true) errors.push(`${line}为木制品,“是否商检”必须选择“是”`)
|
||||
})
|
||||
|
||||
if (draft.fulfillmentType === 'ONE_CLICK_DROPSHIP') {
|
||||
const groups = new Map<string, FirstLegCargoItem>()
|
||||
draft.items.forEach((item) => {
|
||||
const group = item.packingGroupNo.trim()
|
||||
if (!group) return
|
||||
const first = groups.get(group)
|
||||
if (!first) groups.set(group, item)
|
||||
else if (first.packageCount !== item.packageCount || first.packageType !== item.packageType
|
||||
|| first.lengthCm !== item.lengthCm || first.widthCm !== item.widthCm || first.heightCm !== item.heightCm) {
|
||||
errors.push(`混箱组合 ${group} 的箱数、包装类型和箱规必须一致`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
export function summarizeFirstLegCargo(items: FirstLegCargoItem[]) {
|
||||
const countedGroups = new Set<string>()
|
||||
return items.reduce(
|
||||
(summary, item) => ({
|
||||
(summary, item) => {
|
||||
const group = item.packingGroupNo.trim()
|
||||
const packageCount = group && countedGroups.has(group) ? 0 : item.packageCount
|
||||
if (group) countedGroups.add(group)
|
||||
return ({
|
||||
pieces: summary.pieces + item.pieces,
|
||||
packageCount: summary.packageCount + item.packageCount,
|
||||
packageCount: summary.packageCount + packageCount,
|
||||
grossWeightKg: summary.grossWeightKg + item.grossWeightKg,
|
||||
volumeCbm: summary.volumeCbm + item.volumeCbm,
|
||||
}),
|
||||
})
|
||||
},
|
||||
{ pieces: 0, packageCount: 0, grossWeightKg: 0, volumeCbm: 0 },
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user