fix(admin): keep co-load validation action scoped
This commit is contained in:
@@ -2,7 +2,7 @@ import { flushPromises, shallowMount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import { ApiResponseError } from '@/shared/api/http'
|
||||
import { listPlatformUnifiedOrders } from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { listPlatformUnifiedOrders, type UnifiedOrderResource } from '@/modules/unified-orders/api/unifiedOrders'
|
||||
import { cancelPlatformCoLoadGroup, createPlatformCoLoadGroup, getPlatformCoLoadGroupByInboundCode } from '@/modules/unified-orders/api/coLoadGroups'
|
||||
import UnifiedOrdersView from './UnifiedOrdersView.vue'
|
||||
|
||||
@@ -21,7 +21,7 @@ vi.mock('@/modules/unified-orders/api/coLoadGroups', () => ({
|
||||
cancelPlatformCoLoadGroup: vi.fn(), createPlatformCoLoadGroup: vi.fn(), getPlatformCoLoadGroupByInboundCode: vi.fn(),
|
||||
}))
|
||||
|
||||
const rows = ['5001', '5002'].map((orderId, index) => ({
|
||||
const rows: UnifiedOrderResource[] = ['5001', '5002'].map((orderId, index) => ({
|
||||
orderId, tenantId: `600${index}`, orderNo: `ORD-${index + 1}`, mainStatus: 'WAIT_SPLIT' as const, nextAction: null,
|
||||
relation: null, internalOrderStatus: 'WAIT_SPLIT', customerId: `600${index}`, customerName: `客户${index + 1}`,
|
||||
channelCode: 'NY_EXX', channelName: '纽约渠道', receivingWarehouseId: '8', receivingWarehouseName: '义乌仓', destinationWarehouseId: '9', destinationWarehouseName: 'ONT8',
|
||||
@@ -38,7 +38,7 @@ function mountView() {
|
||||
AdminPage: { template: '<main><slot /></main>' },
|
||||
ListPageLayout: { template: '<section><slot name="toolbar" /><slot name="state" /><slot /></section>' },
|
||||
QueryToolbar: { template: '<div />' },
|
||||
DataTable: { name: 'DataTable', props: ['data'], emits: ['selection-change'], template: '<section><slot name="toolbar-actions" /><button data-testid="select" @click="$emit(\'selection-change\', data)">勾选两票</button><template v-for="row in data" :key="row.orderId"><slot name="actions" :row="row" /><slot name="inboundCode" :row="row" /></template></section>' },
|
||||
DataTable: { name: 'DataTable', props: ['data', 'columns'], emits: ['selection-change'], template: '<section><slot name="toolbar-actions" /><button data-testid="select" @click="$emit(\'selection-change\', data)">勾选两票</button><template v-for="row in data" :key="row.orderId"><slot name="actions" :row="row" /><slot name="inboundCode" :row="row" /></template></section>' },
|
||||
AppDialog: { name: 'AppDialog', props: ['modelValue', 'confirmText', 'confirmDisabled', 'confirmLoading'], emits: ['confirm', 'update:modelValue'], methods: { close() { this.$emit('update:modelValue', false) } }, template: '<section v-if="modelValue"><slot /><slot name="footer" :close="close" :submitting="confirmLoading"><button data-testid="dialog-confirm" :disabled="confirmDisabled" @click="$emit(\'confirm\')">{{ confirmText }}</button></slot></section>' },
|
||||
ApiErrorMeta: { props: ['details'], template: '<div>{{ details.code }} {{ details.requestId }} {{ details.traceId }}</div>' },
|
||||
CopyButton: { props: ['value', 'label'], template: '<button :data-copy="value">复制 ID</button>' },
|
||||
@@ -76,28 +76,42 @@ describe('UnifiedOrdersView co-load group behavior', () => {
|
||||
it.each([
|
||||
['平台收货仓不同', [{ ...rows[0], receivingWarehouseId: '8', receivingWarehouseName: '义乌仓' }, { ...rows[1], receivingWarehouseId: '9', receivingWarehouseName: '东莞仓' }], '所选订单的平台收货仓不同:IN-1(仓库:义乌仓;渠道:纽约渠道)、IN-2(仓库:东莞仓;渠道:纽约渠道),不能设为同柜组'],
|
||||
['渠道不同', [{ ...rows[0], channelCode: 'NY_EXX', channelName: '纽约渠道' }, { ...rows[1], channelCode: 'LA_SEA', channelName: '洛杉矶渠道' }], '所选订单的渠道不同:IN-1(仓库:义乌仓;渠道:纽约渠道)、IN-2(仓库:义乌仓;渠道:洛杉矶渠道),不能设为同柜组'],
|
||||
['已有同柜组', [rows[0], { ...rows[1], coLoadGroup: { status: 'ACTIVE', memberCount: 2, memberInboundCodes: ['IN-2', 'IN-3'] } }], '所选订单中已有同柜组,请先解除原同柜组'],
|
||||
])('blocks create immediately when %s', async (_case, selectedRows, reason) => {
|
||||
['已有同柜组', [rows[0], { ...rows[1], coLoadGroup: { status: 'ACTIVE', memberCount: 2, memberInboundCodes: ['IN-2', 'IN-3'] } }], '以下入仓码已有同柜组:IN-2(仓库:义乌仓;渠道:纽约渠道),请先解除原同柜组'],
|
||||
])('blocks a bypassed invalid create when %s without rendering qualification errors', async (_case, selectedRows, reason) => {
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: selectedRows, page: 1, size: 20, total: 2, pages: 1 })
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
await wrapper.get('[data-testid="select"]').trigger('click')
|
||||
const createButton = wrapper.findAll('button').find((button) => button.text().includes('设为同柜不可拆组'))!
|
||||
expect(createButton.attributes('disabled')).toBeDefined()
|
||||
expect(wrapper.text()).toContain(reason)
|
||||
expect(wrapper.text()).not.toContain(reason)
|
||||
await createButton.trigger('click')
|
||||
expect(createPlatformCoLoadGroup).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps zero-selection and one-selection silent while the create button stays disabled', async () => {
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
expect(wrapper.text()).not.toContain('请至少勾选 2 票订单后再设为同柜组')
|
||||
expect(wrapper.findAll('button').find((button) => button.text().includes('设为同柜不可拆组'))!.attributes('disabled')).toBeDefined()
|
||||
|
||||
;(wrapper.vm as unknown as { handleOrderSelectionChange: (selected: typeof rows) => void }).handleOrderSelectionChange([rows[0]])
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.text()).not.toContain('当前仅选择 1 票')
|
||||
expect(wrapper.findAll('button').find((button) => button.text().includes('设为同柜不可拆组'))!.attributes('disabled')).toBeDefined()
|
||||
expect(wrapper.text()).not.toContain('5001')
|
||||
expect(wrapper.text()).not.toContain('7000')
|
||||
})
|
||||
|
||||
it.each([
|
||||
['平台收货仓', [{ ...rows[0], receivingWarehouseId: null, receivingWarehouseName: null }, rows[1]], '以下入仓码缺少平台收货仓:IN-1(仓库:未配置仓库;渠道:纽约渠道),请刷新后重试'],
|
||||
['渠道', [{ ...rows[0], channelCode: null, channelName: null }, rows[1]], '以下入仓码缺少渠道:IN-1(仓库:义乌仓;渠道:未配置渠道),请刷新后重试'],
|
||||
])('lists the affected inbound code and display names when %s is missing', async (_case, selectedRows, reason) => {
|
||||
])('keeps a bypassed missing-field selection disabled and silent when %s is missing', async (_case, selectedRows, reason) => {
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: selectedRows, page: 1, size: 20, total: 2, pages: 1 })
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
await wrapper.get('[data-testid="select"]').trigger('click')
|
||||
expect(wrapper.text()).toContain(reason)
|
||||
expect(wrapper.text()).not.toContain(reason)
|
||||
expect(createPlatformCoLoadGroup).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -118,10 +132,37 @@ describe('UnifiedOrdersView co-load group behavior', () => {
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
await wrapper.get('[data-testid="select"]').trigger('click')
|
||||
expect(wrapper.text()).toContain('所选订单的渠道不同:IN-1(仓库:义乌仓;渠道:纽约渠道)、IN-2(仓库:义乌仓;渠道:纽约渠道),不能设为同柜组')
|
||||
expect(wrapper.findAll('button').find((button) => button.text().includes('设为同柜不可拆组'))!.attributes('disabled')).toBeDefined()
|
||||
expect(wrapper.text()).not.toContain('所选订单的渠道不同')
|
||||
expect(createPlatformCoLoadGroup).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps every row selectable for shared batch actions while only disabling invalid co-load creation', async () => {
|
||||
const selectedRows: UnifiedOrderResource[] = [
|
||||
{ ...rows[0], mainStatus: 'PENDING_REVIEW', nextAction: 'APPROVE_ORDER', receivingWarehouseId: '8' },
|
||||
{ ...rows[1], mainStatus: 'PENDING_REVIEW', nextAction: 'APPROVE_ORDER', receivingWarehouseId: '9' },
|
||||
{ ...rows[1], orderId: '5003', inboundCode: 'IN-3', mainStatus: 'PENDING_REVIEW', nextAction: 'APPROVE_ORDER', receivingWarehouseId: '10', channelCode: 'LA_SEA' },
|
||||
]
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: selectedRows, page: 1, size: 20, total: 3, pages: 1 })
|
||||
const wrapper = mountView()
|
||||
await flushPromises()
|
||||
const table = wrapper.findComponent({ name: 'DataTable' })
|
||||
const selectable = (table.props('columns') as Array<{ key: string; selectable?: (row: UnifiedOrderResource) => boolean }>).find((column) => column.key === 'selection')!.selectable!
|
||||
const missingChannel = { ...rows[1], channelCode: null, channelName: null }
|
||||
const grouped = { ...rows[1], coLoadGroup: { status: 'ACTIVE', memberCount: 2, memberInboundCodes: ['IN-2', 'IN-3'] } }
|
||||
|
||||
expect(selectedRows.every((row) => selectable(row))).toBe(true)
|
||||
expect(selectable(missingChannel)).toBe(true)
|
||||
expect(selectable(grouped)).toBe(true)
|
||||
await table.vm.$emit('selection-change', selectedRows)
|
||||
await flushPromises()
|
||||
expect(wrapper.findAll('button').find((button) => button.text().includes('批量审核通过'))!.attributes('disabled')).toBeUndefined()
|
||||
expect(wrapper.findAll('button').find((button) => button.text().includes('批量审核通过'))!.text()).toContain('(3)')
|
||||
expect(wrapper.findAll('button').find((button) => button.text().includes('设为同柜不可拆组'))!.attributes('disabled')).toBeDefined()
|
||||
expect(wrapper.text()).not.toContain('平台收货仓不同')
|
||||
expect(wrapper.text()).not.toContain('渠道不同')
|
||||
})
|
||||
|
||||
it('uses inbound codes as the only row identifier and shows an explicit pending state', async () => {
|
||||
vi.mocked(listPlatformUnifiedOrders).mockResolvedValue({ records: [rows[0], { ...rows[1], inboundCode: null }], page: 1, size: 20, total: 2, pages: 1 })
|
||||
const wrapper = mountView()
|
||||
|
||||
Reference in New Issue
Block a user