fix(admin): validate explicit booking container limits
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { containerRequirementError, createBlankContainerRequirement } from './containerRequirements'
|
||||
|
||||
describe('ocean booking container requirements', () => {
|
||||
it('does not guess capacity or weight for a new container type', () => {
|
||||
expect(createBlankContainerRequirement()).toEqual({
|
||||
containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('rejects missing or non-positive capacity, weight and quantity', () => {
|
||||
expect(containerRequirementError([createBlankContainerRequirement()])).toContain('容量、限重和数量必须大于 0')
|
||||
expect(containerRequirementError([{ containerType: 'UNKNOWN', containerKind: 'FCL', capacityCbm: 1, maxWeightKg: 1, quantity: 0 }])).toContain('UNKNOWN')
|
||||
expect(containerRequirementError([{ containerType: 'UNKNOWN', containerKind: 'FCL', capacityCbm: 1, maxWeightKg: 1, quantity: 1 }])).toBeNull()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { OceanBookingContainer } from './types'
|
||||
|
||||
export function createBlankContainerRequirement(): OceanBookingContainer {
|
||||
return { containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1 }
|
||||
}
|
||||
|
||||
export function containerRequirementError(requirements: OceanBookingContainer[]) {
|
||||
const invalid = requirements.find((item) => !(Number(item.capacityCbm) > 0) || !(Number(item.maxWeightKg) > 0) || !(Number(item.quantity) > 0))
|
||||
return invalid ? `箱型 ${invalid.containerType || '未命名'} 的容量、限重和数量必须大于 0,请补充后再提交` : null
|
||||
}
|
||||
@@ -48,10 +48,9 @@ describe('OceanBookingDetailView interaction contract', () => {
|
||||
expect(source).toContain('v-model="container.capacityCbm"')
|
||||
expect(source).toContain('v-model="container.maxWeightKg"')
|
||||
expect(source).toContain('releaseOceanBooking(id.value, releaseRequest)')
|
||||
expect(source).toContain('capacityCbm: null')
|
||||
expect(source).toContain('maxWeightKg: null')
|
||||
expect(source).toContain('createBlankContainerRequirement()')
|
||||
expect(source).toContain('containerRequirementError(form.data.containerRequirements)')
|
||||
expect(source).toContain('function validateContainerRequirements()')
|
||||
expect(source).toContain('容量、限重和数量必须大于 0')
|
||||
expect(source).not.toContain('capacityCbm: 68')
|
||||
expect(source).not.toContain('maxWeightKg: 28000')
|
||||
})
|
||||
|
||||
@@ -32,6 +32,7 @@ import type {
|
||||
OceanBookingStatus,
|
||||
} from '../types'
|
||||
import { oceanBookingStatusLabel } from '../status'
|
||||
import { containerRequirementError, createBlankContainerRequirement } from '../containerRequirements'
|
||||
|
||||
const partyTypes = [
|
||||
{ value: 'SHIPPER', label: '发货人' },
|
||||
@@ -85,7 +86,7 @@ function blankParties(): OceanBookingParty[] {
|
||||
function blankData(): OceanBookingDraftData {
|
||||
return {
|
||||
customerId: '', customerName: '', channelCode: '', channelName: '', warehouseId: '', warehouseName: '', parties: blankParties(),
|
||||
containerRequirements: [{ containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1 }],
|
||||
containerRequirements: [createBlankContainerRequirement()],
|
||||
}
|
||||
}
|
||||
const form = reactive<OceanBookingSaveRequest>({ expectedVersion: 0, data: blankData() })
|
||||
@@ -151,7 +152,7 @@ function applyDetail(value: OceanBookingDetail) {
|
||||
estimatedSailingAt: value.estimatedSailingAt, estimatedArrivalAt: value.estimatedArrivalAt,
|
||||
remark: value.remark,
|
||||
parties: value.parties.length ? value.parties.map(({ partyType, partyName, contactName, contactPhone, address }) => ({ partyType, partyName, contactName, contactPhone, address })) : blankParties(),
|
||||
containerRequirements: value.containerRequirements.length ? value.containerRequirements.map(({ containerType, containerKind, capacityCbm, maxWeightKg, quantity }) => ({ containerType, containerKind, capacityCbm: capacityCbm ?? null, maxWeightKg: maxWeightKg ?? null, quantity: quantity ?? null })) : [{ containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1 }],
|
||||
containerRequirements: value.containerRequirements.length ? value.containerRequirements.map(({ containerType, containerKind, capacityCbm, maxWeightKg, quantity }) => ({ containerType, containerKind, capacityCbm: capacityCbm ?? null, maxWeightKg: maxWeightKg ?? null, quantity: quantity ?? null })) : [createBlankContainerRequirement()],
|
||||
}
|
||||
}
|
||||
async function load() {
|
||||
@@ -230,14 +231,14 @@ async function runAction(action: OceanBookingAction) {
|
||||
} finally { actionSaving.value = false }
|
||||
}
|
||||
function validateContainerRequirements() {
|
||||
const invalid = form.data.containerRequirements.find((container) => !(Number(container.capacityCbm) > 0) || !(Number(container.maxWeightKg) > 0) || !(Number(container.quantity) > 0))
|
||||
if (invalid) {
|
||||
actionError.value = { message: `箱型 ${invalid.containerType || '未命名'} 的容量、限重和数量必须大于 0,请补充后再提交` }
|
||||
const message = containerRequirementError(form.data.containerRequirements)
|
||||
if (message) {
|
||||
actionError.value = { message }
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
function addContainer() { form.data.containerRequirements.push({ containerType: '40HC', containerKind: 'FCL', capacityCbm: null, maxWeightKg: null, quantity: 1 }) }
|
||||
function addContainer() { form.data.containerRequirements.push(createBlankContainerRequirement()) }
|
||||
function removeContainer(index: number) { if (form.data.containerRequirements.length > 1) form.data.containerRequirements.splice(index, 1) }
|
||||
|
||||
onMounted(() => { void load(); void loadOptions() })
|
||||
|
||||
Reference in New Issue
Block a user