This commit is contained in:
2026-09-15 12:35:46 +08:00
parent ca90b0bc75
commit 47f2bfdd23
14 changed files with 239 additions and 30 deletions
@@ -102,6 +102,7 @@ function selectedRow(overrides: Record<string, unknown> = {}) {
id: '2092873463568596994', customerId, sourceContainerBookingId: bookingId, orderNo: 'LMO-260827-0001',
overseasWarehouseId: summary.overseasWarehouseId, overseasWarehouseCode: summary.overseasWarehouseCode,
warehouse: summary.overseasWarehouseName, inboundCode: 'IN-LOCKED', version: 3, pallets: 2,
estimatedWarehouseAt: '2026-09-01T08:00:00', warehouseEta: '2026-09-01 08:00',
...overrides,
}
}
@@ -261,7 +262,7 @@ describe('LastMileOrdersView pending order and container conversion', () => {
await button(wrapper, '生成入库计划')!.trigger('click')
await flushPromises()
expect(document.body.textContent).toContain('计划入库仓')
expect(document.body.textContent).toContain('预计到仓日期')
expect(document.body.textContent).toContain('预计到仓时间')
expect(document.body.textContent).toContain('托盘类型')
const inboundCodeInput = wrapper.findAllComponents({ name: 'ElInput' })
.find((input) => input.props('placeholder') === '订单冻结入仓码')
@@ -270,6 +271,11 @@ describe('LastMileOrdersView pending order and container conversion', () => {
.find((input) => input.props('placeholder') === '当前柜接柜仓')
expect(receivingWarehouseInput?.props('disabled')).toBe(true)
expect(receivingWarehouseInput?.props('modelValue')).toBe('LAX01 · 洛杉矶接柜仓')
const expectedArrivalInput = wrapper.findAllComponents({ name: 'ElInput' })
.find((input) => input.props('placeholder') === '当前柜预计到仓时间')
expect(expectedArrivalInput?.props('disabled')).toBe(true)
expect(expectedArrivalInput?.props('modelValue')).toBe('2026-09-01 08:00')
expect(document.body.textContent).toContain('同柜所有入库计划保持一致')
expect(document.body.textContent).toContain('接柜仓按柜统一')
expect(listAllPlatformWarehouses).not.toHaveBeenCalled()
expect(createManualInboundPlan).not.toHaveBeenCalled()
@@ -277,14 +283,11 @@ describe('LastMileOrdersView pending order and container conversion', () => {
expect(listContainerPlanProgress).not.toHaveBeenCalled()
})
it('shows the order-frozen inbound code but never sends it or a per-order receiving warehouse override', async () => {
it('shows cabinet-frozen facts but never sends arrival time or per-order warehouse overrides', async () => {
const wrapper = await openPending()
await selectRows(wrapper, [selectedRow()])
await button(wrapper, '生成入库计划')!.trigger('click')
await flushPromises()
const datePicker = wrapper.find('.command-form').findComponent({ name: 'ElDatePicker' })
datePicker.vm.$emit('update:modelValue', '2026-09-08')
await wrapper.vm.$nextTick()
const confirm = [...document.body.querySelectorAll('button')]
.find((item) => item.textContent?.includes('保存并生成入库单')) as HTMLButtonElement
confirm.click()
@@ -292,15 +295,28 @@ describe('LastMileOrdersView pending order and container conversion', () => {
expect(createManualInboundPlan).toHaveBeenCalledWith(expect.objectContaining({
customerId, orderId: '2092873463568596994', expectedOrderVersion: 3,
plannedArrivalDate: '2026-09-08', palletCount: 5, palletType: 'WOOD',
palletCount: 5, palletType: 'WOOD',
}), expect.stringContaining('manual-inbound-plan-'))
const payload = vi.mocked(createManualInboundPlan).mock.calls[0][0] as unknown as Record<string, unknown>
expect(payload).not.toHaveProperty('inboundCode')
expect(payload).not.toHaveProperty('plannedArrivalDate')
expect(payload).not.toHaveProperty('plannedWarehouseId')
expect(payload).not.toHaveProperty('plannedWarehouseCode')
expect(payload).not.toHaveProperty('plannedWarehouseName')
})
it('blocks manual plan creation when the cabinet has no frozen expected arrival time', async () => {
const wrapper = await openPending()
await selectRows(wrapper, [selectedRow({ estimatedWarehouseAt: '', warehouseEta: '—' })])
await button(wrapper, '生成入库计划')!.trigger('click')
await flushPromises()
const confirm = [...document.body.querySelectorAll('button')]
.find((item) => item.textContent?.includes('保存并生成入库单')) as HTMLButtonElement
expect(confirm.disabled).toBe(true)
expect(createManualInboundPlan).not.toHaveBeenCalled()
})
it('locks one-click dropship manual plans to the frozen source pallet count', async () => {
vi.mocked(getContainerInboundPlanPreview).mockResolvedValue({
...preview,
@@ -324,9 +340,6 @@ describe('LastMileOrdersView pending order and container conversion', () => {
expect(palletInput.props('modelValue')).toBe(1)
expect(document.body.textContent).toContain('固定按头程已封托托盘数整体入库')
wrapper.find('.command-form').findComponent({ name: 'ElDatePicker' })
.vm.$emit('update:modelValue', '2026-09-08')
await wrapper.vm.$nextTick()
;([...document.body.querySelectorAll('button')]
.find((item) => item.textContent?.includes('保存并生成入库单')) as HTMLButtonElement).click()
await flushPromises()
@@ -54,10 +54,12 @@ const inboundPlanSourcePalletCount = ref<number | null>(null)
const inboundPlanPreviewLoading = ref(false)
const inboundPlanPreviewError = ref('')
const inboundPlanIsDropship = computed(() => actionTarget.value?.orderTypeCode === 'ONE_CLICK_DROPSHIP')
const inboundPlanSubmitDisabled = computed(() => inboundPlanIsDropship.value
&& (inboundPlanPreviewLoading.value || inboundPlanSourcePalletCount.value == null
|| inboundPlanSourcePalletCount.value <= 0))
const inboundPlanForm = reactive({ plannedArrivalDate: '', inboundCode: '', palletCount: 1, palletType: 'WOOD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
const inboundPlanSubmitDisabled = computed(() => !actionTarget.value?.estimatedWarehouseAt
|| (inboundPlanIsDropship.value
&& (inboundPlanPreviewLoading.value || inboundPlanSourcePalletCount.value == null
|| inboundPlanSourcePalletCount.value <= 0)))
const inboundPlanForm = reactive({ inboundCode: '', palletCount: 1, palletType: 'WOOD' as 'STANDARD' | 'WOOD' | 'PAPER', assigned: false, mergeRequired: false, urgent: false, remark: '' })
const inboundPlanExpectedArrival = computed(() => actionTarget.value?.warehouseEta || '—')
const holdOpen = ref(false)
const holdForm = reactive({ held: true, chargeable: true, reason: '' })
const manualNodesOpen = ref(false)
@@ -491,7 +493,7 @@ function openInboundPlan(source?: object) {
typeof target.pallets === 'number' ? target.pallets : 0)
inboundPlanSourcePalletCount.value = null
inboundPlanPreviewError.value = ''
Object.assign(inboundPlanForm, { plannedArrivalDate: '', inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: inboundPlanMinimumPalletCount.value, palletType: 'WOOD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
Object.assign(inboundPlanForm, { inboundCode: target.inboundCode === '—' ? '' : target.inboundCode, palletCount: inboundPlanMinimumPalletCount.value, palletType: 'WOOD', assigned: false, mergeRequired: false, urgent: false, remark: '' })
actionError.value = null
inboundPlanOpen.value = true
void refreshManualPalletMinimum(target)
@@ -531,15 +533,14 @@ async function submitInboundPlan() {
ElMessage.warning('一件代发必须按头程已封托托盘数整体生成入库计划')
return
}
if (!row || !row.overseasWarehouseId || !inboundPlanForm.plannedArrivalDate || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < inboundPlanMinimumPalletCount.value) {
ElMessage.warning('当前柜须已分配接柜仓,并完整填写预计到仓日期和托数')
if (!row || !row.overseasWarehouseId || !row.estimatedWarehouseAt || !frozenInboundCode || frozenInboundCode === '—' || inboundPlanForm.palletCount < inboundPlanMinimumPalletCount.value) {
ElMessage.warning('当前柜须已分配接柜仓、具备预计到仓时间,并完整填写托数')
return
}
actionLoading.value = true; actionError.value = null
try {
const result = await createManualInboundPlan({
customerId: row.customerId, orderId: row.id, expectedOrderVersion: row.version,
plannedArrivalDate: inboundPlanForm.plannedArrivalDate,
palletCount: inboundPlanForm.palletCount,
palletType: inboundPlanForm.palletType, assigned: inboundPlanForm.assigned,
mergeRequired: inboundPlanForm.mergeRequired, urgent: inboundPlanForm.urgent,
@@ -967,7 +968,7 @@ onMounted(() => { void load() })
<el-alert title="保存后生成入库单;清关完成后入库计划不可更改。" type="info" :closable="false" show-icon />
<el-alert v-if="inboundPlanPreviewError" class="container-convert-alert" :title="inboundPlanPreviewError" type="error" :closable="false" show-icon />
<el-form class="command-form density-form" label-position="top">
<div class="command-form__grid"><el-form-item label="计划入库仓"><el-input :model-value="inboundPlanWarehouseLabel" disabled placeholder="当前柜接柜仓" /><small>接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。</small></el-form-item><el-form-item label="预计到仓日期" required><el-date-picker v-model="inboundPlanForm.plannedArrivalDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择日期" /></el-form-item><el-form-item label="入仓码"><el-input :model-value="inboundPlanForm.inboundCode" disabled placeholder="订单冻结入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="inboundPlanMinimumPalletCount" :max="inboundPlanIsDropship ? inboundPlanMinimumPalletCount : 999" :disabled="inboundPlanIsDropship" controls-position="right" placeholder="请输入托数" /><small v-if="inboundPlanIsDropship">一件代发固定按头程已封托托盘数整体入库,不可增减。</small><small v-else>按柜级预览至少 {{ inboundPlanMinimumPalletCount }} 托,可增加,不能减少。</small></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="允许合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /><small>仅作为现场可选提示,不强制工人合托。</small></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" /></el-form-item></div>
<div class="command-form__grid"><el-form-item label="预计到仓时间"><el-input :model-value="inboundPlanExpectedArrival" disabled placeholder="当前柜预计到仓时间" /><small>取整柜预计到仓时间,同柜所有入库计划保持一致。</small></el-form-item><el-form-item label="计划入库仓"><el-input :model-value="inboundPlanWarehouseLabel" disabled placeholder="当前柜接柜仓" /><small>接柜仓按柜统一;如需变更,请在柜子工作台执行“变更接柜仓”。</small></el-form-item><el-form-item label="入仓码"><el-input :model-value="inboundPlanForm.inboundCode" disabled placeholder="订单冻结入仓码" /></el-form-item><el-form-item label="托数" required><el-input-number v-model="inboundPlanForm.palletCount" :min="inboundPlanMinimumPalletCount" :max="inboundPlanIsDropship ? inboundPlanMinimumPalletCount : 999" :disabled="inboundPlanIsDropship" controls-position="right" placeholder="请输入托数" /><small v-if="inboundPlanIsDropship">一件代发固定按头程已封托托盘数整体入库,不可增减。</small><small v-else>按柜级预览至少 {{ inboundPlanMinimumPalletCount }} 托,可增加,不能减少。</small></el-form-item><el-form-item label="托盘类型"><el-select v-model="inboundPlanForm.palletType" placeholder="请选择托盘类型"><el-option label="标准托盘" value="STANDARD" /><el-option label="木托盘" value="WOOD" /><el-option label="纸托盘" value="PAPER" /></el-select></el-form-item><el-form-item label="是否分配"><el-switch v-model="inboundPlanForm.assigned" inline-prompt active-text="是" inactive-text="否" /></el-form-item><el-form-item label="允许合托"><el-switch v-model="inboundPlanForm.mergeRequired" inline-prompt active-text="是" inactive-text="否" /><small>仅作为现场可选提示,不强制工人合托。</small></el-form-item><el-form-item label="是否加急"><el-switch v-model="inboundPlanForm.urgent" inline-prompt active-text="是" inactive-text="否" /></el-form-item></div>
<el-form-item label="备注"><el-input v-model="inboundPlanForm.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入备注(选填)" /></el-form-item>
</el-form>
</AppDialog>