fix(dispatch): align dispatch actions and native return workflows

This commit is contained in:
2026-10-18 16:37:37 +08:00
parent 4806f88d7c
commit a5a1976924
29 changed files with 854 additions and 150 deletions
@@ -45,13 +45,33 @@ describe('delivery order API', () => {
})
it('loads return options and marks a return in the explicit customer scope', async () => {
vi.mocked(http.get).mockResolvedValue({ code: '200', data: [{ inboundOrderId: '9', inboundCode: 'IN-1' }] })
vi.mocked(http.get).mockResolvedValue({ code: '200', data: [{ lastMileOrderId: '109', lastMileOrderNo: 'LM-1', inboundOrderId: '9', inboundCode: 'IN-1', returnSources: [] }] })
await listDeliveryReturnInboundOptions('DEL/1', '1001')
expect(http.get).toHaveBeenCalledWith('/platform/v1/last-leg/delivery-orders/DEL%2F1/return-inbound-options', { params: { customerId: '1001' } })
const payload = { inboundOrderId: '9', reason: '客户拒收', returnDirection: 'OVERSEAS_WAREHOUSE' as const, items: [{ outboundPalletItemId: '9007199254740995', inventoryPositionId: '9007199254740996', packageCount: 2 }], idempotencyKey: 'return-1' }
const payload = { lastMileOrderId: '109', reason: '客户拒收', returnDirection: 'OVERSEAS_WAREHOUSE' as const, items: [{ outboundPalletItemId: '9007199254740995', inventoryPositionId: '9007199254740996', packageCount: 2 }], idempotencyKey: 'return-1' }
vi.mocked(http.post).mockResolvedValue({ code: '200', data: { id: '19', exceptionNo: 'EXC-1', status: 'PENDING' } })
await markDeliveryReturn('DEL/1', '1001', payload)
expect(http.post).toHaveBeenCalledWith('/platform/v1/last-leg/delivery-orders/DEL%2F1/mark-return', payload, { params: { customerId: '1001' } })
})
it('preserves original return carton identity and submits it without inventing an inventory position', async () => {
const source = { outboundPalletItemId: '9007199254740995', inventoryPositionId: null, returnCartonId: '9007199254740997', cartonMark: 'RTN-CARTON-1', maxReturnPackages: 1 }
const relation = { lastMileOrderId: '9007199254741299', lastMileOrderNo: 'LM-RETURN-1', inboundOrderId: null, inboundCode: null, customerId: '1001', returnSources: [{ ...source, maxReturnPackages: '1' }] }
vi.mocked(http.get).mockResolvedValue({ code: '200', data: [relation] })
const options = await listDeliveryReturnInboundOptions('DEL/1', '1001')
expect(options[0]?.returnSources[0]).toEqual(source)
expect(options[0]).toMatchObject({ lastMileOrderId: relation.lastMileOrderId, inboundOrderId: null, inboundCode: null })
const payload = { lastMileOrderId: relation.lastMileOrderId, reason: '原箱拒收', returnDirection: 'OVERSEAS_WAREHOUSE' as const, items: [{ outboundPalletItemId: source.outboundPalletItemId, returnCartonId: source.returnCartonId, packageCount: 1 }], idempotencyKey: 'carton-return' }
vi.mocked(http.post).mockResolvedValue({ code: '200', data: { exceptionNo: 'EX-1' } })
await markDeliveryReturn('DEL/1', '1001', payload)
expect(http.post).toHaveBeenLastCalledWith('/platform/v1/last-leg/delivery-orders/DEL%2F1/mark-return', payload, { params: { customerId: '1001' } })
expect(vi.mocked(http.post).mock.calls[0]?.[1]).not.toHaveProperty('inboundOrderId')
})
it.each([undefined, null, '', '0', 'invalid'])('rejects a candidate with invalid master identity %s even if it has an inbound association', async lastMileOrderId => {
vi.mocked(http.get).mockResolvedValue({ code: '200', data: [{ lastMileOrderId, inboundOrderId: '9', inboundCode: 'IN-1', returnSources: [] }] })
await expect(listDeliveryReturnInboundOptions('DEL/1', '1001')).rejects.toThrow('尾程主单标识')
expect(http.post).not.toHaveBeenCalled()
})
})