From 1d73a40a07dbf02ea29e105b1e0a59770bf603bd Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sun, 4 Oct 2026 12:43:07 +0800 Subject: [PATCH] fix(overseas): synchronize receipt input accessibility with command lock --- .../ExceptionProcessingPanel.test.ts | 39 +++++++++++++++++-- .../components/ExceptionProcessingPanel.vue | 3 +- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.test.ts b/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.test.ts index c22a8ef6..401be105 100644 --- a/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.test.ts +++ b/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.test.ts @@ -1,5 +1,6 @@ import { flushPromises, mount } from '@vue/test-utils' import { defineComponent, h } from 'vue' +import { ElForm, ElFormItem, ElInput, ElInputNumber } from 'element-plus' import { beforeEach, expect, it, vi } from 'vitest' import { ApiResponseError } from '@/shared/api/http' import ExceptionProcessingPanel from './ExceptionProcessingPanel.vue' @@ -25,9 +26,10 @@ function snapshot(number = 'EX-1', version = 1, remaining = 4): ExceptionDetail reason: '拒收', exceptionAt: '', updatedAt: '', evidenceFileIds: ['91'] }, 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: { - directives: { loading: () => {} }, stubs: { ElDescriptions: shell, ElDescriptionsItem: shell, ElForm: shell, ElFormItem: shell, - ElInput: field, ElInputNumber: quantity, ElSelect: field, ElOption: true, RouterLink: { template: '' }, ElButton: { template: '' } }, +function open(realControls = false) { return mount(ExceptionProcessingPanel, { props: { customerId: 'C', exceptionNo: 'EX-1', active: true }, global: { + components: { ElForm, ElFormItem, ElInput, ElInputNumber }, + 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: '' }, ElButton: { template: '' } }, } }) } async function fill(wrapper: ReturnType, amount = 2) { 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>) vi.mocked(getOverseasLocation).mockResolvedValue({ id: '40', warehouseId: '92', locationCode: 'LOC' } as Awaited>) }) +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>) => 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 () => { 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 }) diff --git a/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.vue b/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.vue index 53fb65f0..91661d25 100644 --- a/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.vue +++ b/src/modules/overseas-warehouse/components/ExceptionProcessingPanel.vue @@ -208,7 +208,8 @@ watch(() => [props.customerId, props.exceptionNo, props.active], () => { ++seque

只填写本批实际收到的箱数;未收齐保持处理中。目标库位取所选托盘的真实库位。

{{ row.sourcePalletNo }} · SKU {{ row.skuCode || '—' }} · 剩余可收 {{ row.maxReturnPackages }} 箱

- + +

目标托盘:{{ row.palletNo || '—' }};实际库位:{{ row.locationCode || '—' }}