fix(overseas): synchronize receipt input accessibility with command lock
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { flushPromises, mount } from '@vue/test-utils'
|
import { flushPromises, mount } from '@vue/test-utils'
|
||||||
import { defineComponent, h } from 'vue'
|
import { defineComponent, h } from 'vue'
|
||||||
|
import { ElForm, ElFormItem, ElInput, ElInputNumber } from 'element-plus'
|
||||||
import { beforeEach, expect, it, vi } from 'vitest'
|
import { beforeEach, expect, it, vi } from 'vitest'
|
||||||
import { ApiResponseError } from '@/shared/api/http'
|
import { ApiResponseError } from '@/shared/api/http'
|
||||||
import ExceptionProcessingPanel from './ExceptionProcessingPanel.vue'
|
import ExceptionProcessingPanel from './ExceptionProcessingPanel.vue'
|
||||||
@@ -25,9 +26,10 @@ function snapshot(number = 'EX-1', version = 1, remaining = 4): ExceptionDetail
|
|||||||
reason: '拒收', exceptionAt: '', updatedAt: '', evidenceFileIds: ['91'] },
|
reason: '拒收', exceptionAt: '', updatedAt: '', evidenceFileIds: ['91'] },
|
||||||
returnSources: remaining ? [{ outboundPalletItemId: '10', inventoryPositionId: '20', palletNo: 'ORIGINAL', skuCode: 'SKU', maxReturnPackages: String(remaining) }] : [], returnReceipts: [] } as unknown as ExceptionDetail)
|
returnSources: remaining ? [{ outboundPalletItemId: '10', inventoryPositionId: '20', palletNo: 'ORIGINAL', skuCode: 'SKU', maxReturnPackages: String(remaining) }] : [], returnReceipts: [] } as unknown as ExceptionDetail)
|
||||||
}
|
}
|
||||||
function open() { return mount(ExceptionProcessingPanel, { props: { customerId: 'C', exceptionNo: 'EX-1', active: true }, global: {
|
function open(realControls = false) { return mount(ExceptionProcessingPanel, { props: { customerId: 'C', exceptionNo: 'EX-1', active: true }, global: {
|
||||||
directives: { loading: () => {} }, stubs: { ElDescriptions: shell, ElDescriptionsItem: shell, ElForm: shell, ElFormItem: shell,
|
components: { ElForm, ElFormItem, ElInput, ElInputNumber },
|
||||||
ElInput: field, ElInputNumber: quantity, ElSelect: field, ElOption: true, RouterLink: { template: '<a><slot /></a>' }, ElButton: { template: '<button><slot /></button>' } },
|
directives: { loading: () => {} }, stubs: { ElDescriptions: shell, ElDescriptionsItem: shell, ElForm: realControls ? false : shell, ElFormItem: realControls ? false : shell,
|
||||||
|
ElInput: realControls ? false : field, ElInputNumber: realControls ? false : quantity, ElSelect: field, ElOption: true, RouterLink: { template: '<a><slot /></a>' }, ElButton: { template: '<button><slot /></button>' } },
|
||||||
} }) }
|
} }) }
|
||||||
async function fill(wrapper: ReturnType<typeof open>, amount = 2) {
|
async function fill(wrapper: ReturnType<typeof open>, amount = 2) {
|
||||||
if (wrapper.find('[data-test="history-0"]').exists()) await wrapper.get('[data-test="history-0"]').setValue(true)
|
if (wrapper.find('[data-test="history-0"]').exists()) await wrapper.get('[data-test="history-0"]').setValue(true)
|
||||||
@@ -75,6 +77,37 @@ beforeEach(() => {
|
|||||||
vi.mocked(getPallet).mockResolvedValue({ id: '30', warehouseId: '92', locationId: '40', palletCode: 'TARGET' } as Awaited<ReturnType<typeof getPallet>>)
|
vi.mocked(getPallet).mockResolvedValue({ id: '30', warehouseId: '92', locationId: '40', palletCode: 'TARGET' } as Awaited<ReturnType<typeof getPallet>>)
|
||||||
vi.mocked(getOverseasLocation).mockResolvedValue({ id: '40', warehouseId: '92', locationCode: 'LOC' } as Awaited<ReturnType<typeof getOverseasLocation>>)
|
vi.mocked(getOverseasLocation).mockResolvedValue({ id: '40', warehouseId: '92', locationCode: 'LOC' } as Awaited<ReturnType<typeof getOverseasLocation>>)
|
||||||
})
|
})
|
||||||
|
it('real receipt quantity control becomes accessible after starting processing', async () => {
|
||||||
|
const pending = snapshot('EX-1', 0, 2)
|
||||||
|
pending.order.status = 'PENDING'; pending.order.allowedActions = ['START_PROCESSING']; pending.returnSources = []
|
||||||
|
vi.mocked(getExceptionDetail).mockResolvedValueOnce(pending).mockResolvedValueOnce(snapshot('EX-1', 1, 2))
|
||||||
|
vi.mocked(commandException).mockResolvedValue({ exceptionNo: 'EX-1', version: 1, status: 'PROCESSING', restoredPackages: 0, replayed: false })
|
||||||
|
const wrapper = open(true); try { await flushPromises()
|
||||||
|
await wrapper.get('[data-test="start"]').trigger('click'); await flushPromises()
|
||||||
|
const input = wrapper.getComponent(ElInputNumber).get('input')
|
||||||
|
expect((input.element as HTMLInputElement).disabled).toBe(false)
|
||||||
|
expect(input.attributes('aria-disabled')).toBe('false')
|
||||||
|
} finally { wrapper.unmount() }
|
||||||
|
})
|
||||||
|
it('real quantity control reflects in-flight lock and unlocks after a partial receipt reload', async () => {
|
||||||
|
vi.mocked(getExceptionDetail).mockResolvedValueOnce(snapshot('EX-1', 1, 2)).mockResolvedValueOnce(snapshot('EX-1', 2, 1))
|
||||||
|
let finish!: (value: Awaited<ReturnType<typeof commandException>>) => void
|
||||||
|
vi.mocked(commandException).mockReturnValue(new Promise(resolve => { finish = resolve }))
|
||||||
|
const wrapper = open(true); try { await flushPromises()
|
||||||
|
await wrapper.get('[data-test="history-0"]').setValue(true)
|
||||||
|
wrapper.getComponent(ElInputNumber).vm.$emit('update:modelValue', 1)
|
||||||
|
await wrapper.get('[data-test="target-0"]').setValue('30'); await flushPromises()
|
||||||
|
await wrapper.get('[data-test="complete"]').trigger('click'); await flushPromises()
|
||||||
|
expect((wrapper.getComponent(ElInputNumber).get('input').element as HTMLInputElement).disabled).toBe(true)
|
||||||
|
expect(wrapper.getComponent(ElInputNumber).get('input').attributes('aria-disabled')).toBe('true')
|
||||||
|
finish({ exceptionNo: 'EX-1', version: 2, status: 'PROCESSING', restoredPackages: 1, replayed: false }); await flushPromises()
|
||||||
|
const input = wrapper.getComponent(ElInputNumber).get('input')
|
||||||
|
expect((input.element as HTMLInputElement).disabled).toBe(false)
|
||||||
|
expect(input.attributes('aria-disabled')).toBe('false')
|
||||||
|
expect(wrapper.text()).toContain('剩余可收 1 箱')
|
||||||
|
expect(commandException).toHaveBeenCalledTimes(1)
|
||||||
|
} finally { wrapper.unmount() }
|
||||||
|
})
|
||||||
it('submits two actual batches using refreshed remaining sources and independent command keys', async () => {
|
it('submits two actual batches using refreshed remaining sources and independent command keys', async () => {
|
||||||
vi.mocked(getExceptionDetail).mockResolvedValueOnce(snapshot()).mockResolvedValueOnce(snapshot('EX-1', 2, 2)).mockResolvedValueOnce(snapshot('EX-1', 3, 0))
|
vi.mocked(getExceptionDetail).mockResolvedValueOnce(snapshot()).mockResolvedValueOnce(snapshot('EX-1', 2, 2)).mockResolvedValueOnce(snapshot('EX-1', 3, 0))
|
||||||
vi.mocked(commandException).mockResolvedValueOnce({ exceptionNo: 'EX-1', version: 2, status: 'PROCESSING', restoredPackages: 2, replayed: false })
|
vi.mocked(commandException).mockResolvedValueOnce({ exceptionNo: 'EX-1', version: 2, status: 'PROCESSING', restoredPackages: 2, replayed: false })
|
||||||
|
|||||||
@@ -208,7 +208,8 @@ watch(() => [props.customerId, props.exceptionNo, props.active], () => { ++seque
|
|||||||
<p>只填写本批实际收到的箱数;未收齐保持处理中。目标库位取所选托盘的真实库位。</p>
|
<p>只填写本批实际收到的箱数;未收齐保持处理中。目标库位取所选托盘的真实库位。</p>
|
||||||
<div v-for="(row, index) in receipts" :key="`${row.outboundPalletItemId}:${row.inventoryPositionId}`">
|
<div v-for="(row, index) in receipts" :key="`${row.outboundPalletItemId}:${row.inventoryPositionId}`">
|
||||||
<p>{{ row.sourcePalletNo }} · SKU {{ row.skuCode || '—' }} · 剩余可收 {{ row.maxReturnPackages }} 箱</p>
|
<p>{{ row.sourcePalletNo }} · SKU {{ row.skuCode || '—' }} · 剩余可收 {{ row.maxReturnPackages }} 箱</p>
|
||||||
<el-form-item label="本批实收箱数"><el-input-number v-model="row.quantity" :data-test="`quantity-${index}`" :min="0" :max="row.maxReturnPackages" :precision="0" placeholder="未收到可留空" /></el-form-item>
|
<!-- Element Plus initializes number-input ARIA on mount; remount when the form lock changes. -->
|
||||||
|
<el-form-item label="本批实收箱数"><el-input-number :key="locked ? 'locked' : 'editable'" v-model="row.quantity" :data-test="`quantity-${index}`" :min="0" :max="row.maxReturnPackages" :precision="0" placeholder="未收到可留空" /></el-form-item>
|
||||||
<el-form-item label="实际收货目标托盘"><el-select :model-value="row.target" :data-test="`target-${index}`" filterable remote :remote-method="searchTargets" placeholder="输入托盘码查询" @visible-change="onTargetVisibility" @update:model-value="onTargetChange(index, $event)"><el-option v-for="target in targets" :key="target.id" :value="String(target.id)" :label="target.palletCode" /></el-select></el-form-item>
|
<el-form-item label="实际收货目标托盘"><el-select :model-value="row.target" :data-test="`target-${index}`" filterable remote :remote-method="searchTargets" placeholder="输入托盘码查询" @visible-change="onTargetVisibility" @update:model-value="onTargetChange(index, $event)"><el-option v-for="target in targets" :key="target.id" :value="String(target.id)" :label="target.palletCode" /></el-select></el-form-item>
|
||||||
<p>目标托盘:{{ row.palletNo || '—' }};实际库位:{{ row.locationCode || '—' }}</p>
|
<p>目标托盘:{{ row.palletNo || '—' }};实际库位:{{ row.locationCode || '—' }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user