diff --git a/src/modules/overseas-business/api/platformOverseasBusiness.test.ts b/src/modules/overseas-business/api/platformOverseasBusiness.test.ts index 41892075..134c073c 100644 --- a/src/modules/overseas-business/api/platformOverseasBusiness.test.ts +++ b/src/modules/overseas-business/api/platformOverseasBusiness.test.ts @@ -107,4 +107,12 @@ describe('platform overseas business API adapter', () => { expect(overseas.post).not.toHaveBeenCalled() expect(overseas.put).not.toHaveBeenCalled() }) + + it('preserves unknown pool pallet service and container type in list and detail responses', async () => { + const summary = { ...looseboardSummary, palletRequired: null, containerType: null } + overseas.get.mockResolvedValueOnce(envelope({ records: [summary], total: 1, page: 1, size: 20, pages: 1 })).mockResolvedValueOnce(envelope({ summary, histories: [] })) + expect((await listLocalLooseboardOrders({})).records[0]).toMatchObject({ palletRequired: null, containerType: null }) + expect(await getLocalLooseboardOrder('LB-1', '10003')).toMatchObject({ palletRequired: null, containerType: null }) + expect(overseas.get).toHaveBeenCalledTimes(2) + }) }) diff --git a/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts b/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts index 7d0664dd..b93c2f1e 100644 --- a/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts +++ b/src/modules/overseas-business/pages/OverseasBusinessViews.test.ts @@ -87,6 +87,29 @@ describe('overseas business mounted pages', () => { expect(wrapper.find('form').exists()).toBe(false); expect(wrapper.findAll('button').some(b => b.text() === '保存修改')).toBe(false) expect(updateUsTruckOrder).not.toHaveBeenCalled(); expect(getUsTruckOrder).toHaveBeenCalledOnce() }) + it('distinguishes unknown pool pallet service from explicit no and yes in actual list cells', async () => { + const records = ([null, 'NO', 'YES'] as const).map((palletRequired, index) => ({ ...looseboard, id: String(100 + index), orderNo: `LB-NULL-${index}`, palletRequired, containerType: null, fleetArrangement: { ...arrangement, businessType: 'LOCAL_LOOSEBOARD' as const } })) + vi.mocked(listLocalLooseboardOrders).mockResolvedValue({ records, total: 3, page: 1, size: 20, pages: 1 }) + const wrapper = mountPage(TransportOrdersView, { domain: 'looseboard' }); await flushPromises() + const rows = wrapper.findAll('.el-table__body-wrapper tbody .el-table__row') + expect(rows.map(row => row.findAll('td')[3]!.text())).toEqual(['—', '否', '是']) + expect(listLocalLooseboardOrders).toHaveBeenCalledOnce() + }) + it('shows an unknown pool container type as a dash without inventing 40HQ', async () => { + vi.mocked(listUsTruckOrders).mockResolvedValue({ records: [{ ...truck, containerType: null, fleetArrangement: arrangement }], total: 1, page: 1, size: 20, pages: 1 }) + const wrapper = mountPage(TransportOrdersView, { domain: 'truck' }); await flushPromises() + expect(wrapper.find('.el-table__body-wrapper tbody .el-table__row').findAll('td')[7]!.text()).toBe('—') + expect(wrapper.find('.el-table__body-wrapper').text()).not.toContain('40HQ') + }) + it('still rejects an ordinary looseboard edit when its required pallet service is absent', async () => { + routing.route.params = { orderId: looseboard.orderNo }; routing.route.query = { customerId: looseboard.customerId } + vi.mocked(getLocalLooseboardOrder).mockResolvedValue({ ...looseboardDetail, palletRequired: null, fleetArrangement: null }) + const wrapper = mountPage(TransportOrderFormView, { domain: 'looseboard' }); await flushPromises() + expect(wrapper.findComponent({ name: 'ElForm' }).props('model').palletRequired).toBeNull() + await wrapper.findAll('button').find(button => button.text().includes('保存修改'))!.trigger('click'); await flushPromises() + expect(updateLocalLooseboardOrder).not.toHaveBeenCalled() + expect(document.body.textContent).toContain('请选择是否打盘') + }) it('keeps all procurement facts in dense groups and business references in the heading', async () => { routing.route.params = { purchaseId: 'internal-purchase-route' }; routing.route.query = { customerId: '10001' } const wrapper = mountPage(PurchaseOrderDetailView) diff --git a/src/modules/overseas-business/pages/TransportOrderFormView.vue b/src/modules/overseas-business/pages/TransportOrderFormView.vue index 3e55f1c5..482764f8 100644 --- a/src/modules/overseas-business/pages/TransportOrderFormView.vue +++ b/src/modules/overseas-business/pages/TransportOrderFormView.vue @@ -91,6 +91,7 @@ async function load() { async function save() { if (!editableStatus.value) { submitError.value = { message: `当前${domainLabel.value}订单状态不可编辑` }; return } if (!editing.value && !form.customerId) { submitError.value = { message: '请选择客户' }; return } + if (isLooseboard.value && form.palletRequired !== 'YES' && form.palletRequired !== 'NO') { submitError.value = { message: '请选择是否打盘' }; return } if (!await formRef.value?.validate().catch(() => false)) return const common = { transportType: form.transportType as LocalTransportType, origin: form.origin.trim(), originAddress: optional(form.originAddress), destination: form.destination.trim(), destinationAddress: optional(form.destinationAddress), containerType: optional(form.containerType), packageCount: form.packageCount, totalWeightKg: Number(form.totalWeightKg), totalVolumeCbm: Number(form.totalVolumeCbm), budgetAmount: amount(form.budgetAmount), currency: form.currency, contactName: optional(form.contactName), contactInfo: optional(form.contactInfo), requestedDate: form.requestedDate, remark: optional(form.remark) } saving.value = true; submitError.value = null diff --git a/src/modules/overseas-business/pages/TransportOrdersView.vue b/src/modules/overseas-business/pages/TransportOrdersView.vue index 386c1e7a..f732665d 100644 --- a/src/modules/overseas-business/pages/TransportOrdersView.vue +++ b/src/modules/overseas-business/pages/TransportOrdersView.vue @@ -42,7 +42,7 @@ const truckColumns: DataTableColumn[] = [ { key: 'transportType', label: '运输类型', minWidth: 120, formatter: (row: LocalTransportOrder) => row.transportType ? transportTypeLabels[row.transportType] : '—' }, { key: 'origin', prop: 'origin', label: '起运地', minWidth: 145 }, { key: 'destination', prop: 'destination', label: '目的地', minWidth: 145 }, { key: 'contactName', prop: 'contactName', label: '联系人', minWidth: 110 }, { key: 'contactInfo', prop: 'contactInfo', label: '联系方式', minWidth: 145 }, - { key: 'containerType', prop: 'containerType', label: '柜型', width: 90 }, { key: 'palletCount', prop: 'palletCount', label: '托数', width: 80, align: 'right' }, + { key: 'containerType', prop: 'containerType', label: '柜型', width: 90, formatter: (row: LocalTransportOrder) => row.containerType || '—' }, { key: 'palletCount', prop: 'palletCount', label: '托数', width: 80, align: 'right' }, { key: 'packageCount', prop: 'packageCount', label: '件数', width: 80, align: 'right' }, { key: 'totalWeightKg', prop: 'totalWeightKg', label: '总重量(kg)', width: 115, align: 'right' }, { key: 'budgetAmount', label: '预算总价', width: 120, align: 'right', formatter: (row: LocalTransportOrder) => row.budgetAmount == null ? '—' : `${row.budgetAmount} ${row.currency}` }, { key: 'requestedDate', prop: 'requestedDate', label: '要求时间', width: 120 }, { key: 'status', label: '状态', width: 120, formatter: (row: LocalTransportOrder) => truckStatusLabels[row.status] }, @@ -50,7 +50,7 @@ const truckColumns: DataTableColumn[] = [ ] const looseboardColumns: DataTableColumn[] = [ { key: 'actions', label: '操作', width: 110, fixed: 'left', slotName: 'actions' }, { key: 'orderNo', prop: 'orderNo', label: '订单编号', minWidth: 170 }, - { key: 'transportMode', label: '运输方式', minWidth: 120, formatter: (row: LocalLooseboardOrder) => transportModeLabels[row.transportMode] }, { key: 'palletRequired', label: '是否打盘', width: 100, formatter: (row: LocalLooseboardOrder) => row.palletRequired === 'YES' ? '是' : '否' }, + { key: 'transportMode', label: '运输方式', minWidth: 120, formatter: (row: LocalLooseboardOrder) => transportModeLabels[row.transportMode] }, { key: 'palletRequired', label: '是否打盘', width: 100, formatter: (row: LocalLooseboardOrder) => row.palletRequired === 'YES' ? '是' : row.palletRequired === 'NO' ? '否' : '—' }, { key: 'transportType', label: '运输类型', minWidth: 120, formatter: (row: LocalLooseboardOrder) => transportTypeLabels[row.transportType] }, { key: 'origin', prop: 'origin', label: '起运地', minWidth: 145 }, { key: 'destination', prop: 'destination', label: '目的地', minWidth: 145 }, { key: 'contactName', prop: 'contactName', label: '联系人', minWidth: 110 }, { key: 'contactInfo', prop: 'contactInfo', label: '联系方式', minWidth: 145 }, { key: 'packageCount', prop: 'packageCount', label: '件数', width: 80, align: 'right' }, diff --git a/src/modules/overseas-business/types.ts b/src/modules/overseas-business/types.ts index 27c2df5a..9f276182 100644 --- a/src/modules/overseas-business/types.ts +++ b/src/modules/overseas-business/types.ts @@ -91,7 +91,7 @@ export interface LocalLooseboardQuery { } export interface LocalLooseboardOrder { fleetArrangement: import('./fleetArrangement').FleetArrangement | null - id: string; customerId: string; orderNo: string; transportMode: LooseboardTransportMode; palletRequired: PalletRequired + id: string; customerId: string; orderNo: string; transportMode: LooseboardTransportMode; palletRequired: PalletRequired | null transportType: LocalTransportType; origin: string; destination: string; containerType: string | null; packageCount: number totalWeightKg: string; totalVolumeCbm: string; budgetAmount: string | null; currency: string contactName: string | null; contactInfo: string | null; requestedDate: string | null; status: LocalLooseboardStatus