fix(fleet): distinguish signed returned and remaining trip cargo

This commit is contained in:
2026-10-03 15:00:10 +08:00
parent 5641ea0f7e
commit 6a135c3982
6 changed files with 36 additions and 9 deletions
@@ -20,7 +20,7 @@ const load = { palletSpaces: '1', weightKg: '100.125', volumeCbm: '1.005' }
const confirmed = { manifestHash: 'a'.repeat(64), cargo: [cargo], pallets: [{ ...load, warehousePalletId: longId, deliveryIndex: '1' }],
segments: [{ fromIndex: '0', toIndex: '1', occupied: load, remaining: { palletSpaces: '29', weightKg: '17899.875', volumeCbm: '88.995' } }], operatorId: longId, confirmedAt: trip.updatedAt }
const loading = { tripNo: trip.tripNo, recordVersion: '3', cargo: [cargo], manifestHash: confirmed.manifestHash, confirmedLoad: confirmed, blockReason: null }
const outcome = { dispatchNo: 'DP-1', deliveryNo: 'DL-1', customerId: longId, destination: 'ONT8', status: 'DELIVERED', packages: '12', terminalAt: trip.updatedAt }
const outcome = { dispatchNo: 'DP-1', deliveryNo: 'DL-1', customerId: longId, destination: 'ONT8', status: 'DELIVERED', packages: '12', signedPackages: '8', returnedPackages: '4', remainingPackages: '0', terminalAt: trip.updatedAt }
const envelope = <T>(data: T) => ({ code: '200', message: '成功', data })
describe('fleet trip API contract', () => {
@@ -93,12 +93,13 @@ describe('fleet trip API contract', () => {
it('loads completion and immutable history only when explicitly requested', async () => {
vi.mocked(http.get).mockResolvedValueOnce(envelope({ tripNo: trip.tripNo, recordVersion: '5', canComplete: false, blockReason: '另一票未签收', outcomes: [outcome] }))
expect(await getTripCompletion(trip.tripNo)).toMatchObject({ recordVersion: 5, canComplete: false, blockReason: '另一票未签收', outcomes: [{ packages: 12 }] })
expect(await getTripCompletion(trip.tripNo)).toMatchObject({ recordVersion: 5, canComplete: false, blockReason: '另一票未签收', outcomes: [{ packages: 12, signedPackages: 8, returnedPackages: 4, remainingPackages: 0 }] })
vi.mocked(http.get).mockResolvedValueOnce(envelope({ page: '2', size: '10', total: '12', pages: '2', records: [{ recordVersion: '5', action: 'COMPLETE', reason: null, operatorId: longId, occurredAt: trip.updatedAt,
snapshot: { trip, externalBookings: [], capacityPlan: null, loadingReview: reviewState, dispatches: detail.dispatches, confirmedLoad: confirmed, completion: { idempotencyKey: 'key', requestHash: 'hash', completedAt: trip.updatedAt, recordedAt: trip.updatedAt, operatorId: longId, outcomes: [outcome] } } }] }))
const history = await listTripHistory(trip.tripNo, 2, 10)
expect(http.get).toHaveBeenLastCalledWith(`${base}/TR%2FTEST/history`, { params: { page: 2, size: 10 } })
expect(history.records[0]?.snapshot.completion?.outcomes[0]?.packages).toBe(12)
expect(history.records[0]?.snapshot.completion?.outcomes[0]).toMatchObject({ signedPackages: 8, returnedPackages: 4, remainingPackages: 0 })
expect(history.records[0]?.snapshot.trip.recordVersion).toBe(0)
})
@@ -26,7 +26,7 @@ type WireConfirmed = Omit<TripConfirmedLoad, 'cargo' | 'pallets' | 'segments'> &
}
type WireLoading = Omit<TripLoading, 'recordVersion' | 'cargo' | 'confirmedLoad'> & { recordVersion: Numeric; cargo: WireCargo[]; confirmedLoad: WireConfirmed | null }
type WirePlanning = Omit<TripCapacityPlanning, 'recordVersion' | 'cargo' | 'capacityPlan' | 'availableSegments'> & { recordVersion: Numeric; cargo: WireCargo[]; capacityPlan: WireConfirmed | null; availableSegments: WireConfirmed['segments'] }
type WireOutcome = Omit<TripOutcome, 'packages'> & { packages: Numeric }
type WireOutcome = Omit<TripOutcome, 'packages' | 'signedPackages' | 'returnedPackages' | 'remainingPackages'> & { packages: Numeric; signedPackages: Numeric; returnedPackages: Numeric; remainingPackages: Numeric }
type WireInspection = Omit<TripCompletionInspection, 'recordVersion' | 'outcomes'> & { recordVersion: Numeric; outcomes: WireOutcome[] }
type WireHistory = Omit<TripHistory, 'recordVersion' | 'snapshot'> & {
recordVersion: Numeric
@@ -52,7 +52,8 @@ const review = (value: WireReview): TripReviewState => ({ ...value, recordVersio
const binding = (value: WireBinding): TripBinding => ({ ...value, packageCount: integer(value.packageCount), review: review(value.review) })
const detail = (value: WireDetail): TripDetail => ({ ...value, trip: trip(value.trip), dispatches: value.dispatches.map(binding), loadingReview: review(value.loadingReview) })
const cargo = (value: WireCargo): TripCargo => ({ ...value, packages: integer(value.packages) })
const outcome = (value: WireOutcome): TripOutcome => ({ ...value, packages: integer(value.packages) })
const outcome = (value: WireOutcome): TripOutcome => ({ ...value, packages: integer(value.packages),
signedPackages: integer(value.signedPackages), returnedPackages: integer(value.returnedPackages), remainingPackages: integer(value.remainingPackages) })
const load = (value: WireLoad): TripLoad => ({ palletSpaces: integer(value.palletSpaces), weightKg: number(value.weightKg), volumeCbm: number(value.volumeCbm) })
function confirmed(value: WireConfirmed | null): TripConfirmedLoad | null {
return value == null ? null : { ...value, cargo: value.cargo.map(cargo),