fix(fleet): normalize nullable maintenance fields before editing

This commit is contained in:
2026-10-03 10:56:47 +08:00
parent fa82710e6b
commit 1a52cc0817
4 changed files with 32 additions and 8 deletions
@@ -8,6 +8,15 @@ const details: VehicleCareDraft = { recordType: 'FUEL', occurredAt: '2026-01-01T
const wire = { recordNo: 'VR-ONE', vehicleId: id, vehiclePlate: 'TEST', recordVersion: '2', details: { ...details, odometer: '100', fuelQuantity: '28.6' }, files: [{ fileId: '9223372036854775702', fileName: 'meter.png', contentType: 'image/png', sizeBytes: '8' }], updatedBy: 'operator', updatedAt: '2026-01-01T00:00:00Z' }
const ok = (data: unknown) => ({ code: '200', message: '成功', data })
beforeEach(() => vi.clearAllMocks())
it('normalizes omitted nullable incident fields without injecting maintenance defaults', async () => {
const repair = { ...wire, details: { recordType: 'REPAIR', occurredAt: details.occurredAt,
incident: { description: '轮胎维修', item: '换胎', place: '现场', processingStatus: 'PENDING', reportedCost: '125.50', currency: 'USD' } } }
vi.mocked(http.get).mockResolvedValue(ok({ records: [repair], page: 1, size: 10, total: 1, pages: 1 }))
const result = (await listVehicleCare(id, 'REPAIR')).records[0]!
expect(result.details).toMatchObject({ distanceUnit: null, odometer: null, fuelUnit: null, verificationStatus: null,
lastServiceOdometer: null, nextServiceOdometer: null, fuelQuantity: null, remark: null,
incident: { handler: null, exceptionType: null, reportedCost: 125.5, currency: 'USD' } })
})
it('keeps vehicle and file IDs exact while normalizing values without changing units', async () => {
vi.mocked(http.get).mockResolvedValue(ok({ records: [wire], page: '1', size: '10', total: '1', pages: '1' }))
const result = await listVehicleCare(id, 'FUEL')
@@ -6,9 +6,9 @@ import type { PageResult } from './overseasMasterData'
type Numeric = number | string
type Wire = Omit<VehicleCareRecord, 'recordVersion' | 'details' | 'files'> & {
recordVersion: Numeric
details: Omit<VehicleCareDraft, 'odometer' | 'lastServiceOdometer' | 'nextServiceOdometer' | 'fuelQuantity' | 'incident'> & {
odometer: Numeric | null; lastServiceOdometer: Numeric | null; nextServiceOdometer: Numeric | null; fuelQuantity: Numeric | null
incident: (Omit<VehicleIncident, 'reportedCost'> & { reportedCost: Numeric | null }) | null
details: Pick<VehicleCareDraft, 'recordType' | 'occurredAt'> & Partial<Omit<VehicleCareDraft, 'odometer' | 'lastServiceOdometer' | 'nextServiceOdometer' | 'fuelQuantity' | 'incident'>> & {
odometer?: Numeric | null; lastServiceOdometer?: Numeric | null; nextServiceOdometer?: Numeric | null; fuelQuantity?: Numeric | null
incident?: (Pick<VehicleIncident, 'description' | 'processingStatus'> & Partial<Omit<VehicleIncident, 'reportedCost'>> & { reportedCost?: Numeric | null }) | null
}
files: Array<Omit<VehicleCareRecord['files'][number], 'sizeBytes'> & { sizeBytes: Numeric }>
}
@@ -24,10 +24,16 @@ function decimal(value: Numeric): number {
return result
}
function record(value: Wire): VehicleCareRecord {
const optional = (number: Numeric | null) => number == null ? null : decimal(number)
return { ...value, recordVersion: integer(value.recordVersion), details: { ...value.details, odometer: optional(value.details.odometer),
lastServiceOdometer: optional(value.details.lastServiceOdometer), nextServiceOdometer: optional(value.details.nextServiceOdometer), fuelQuantity: optional(value.details.fuelQuantity),
incident: value.details.incident ? { ...value.details.incident, reportedCost: optional(value.details.incident.reportedCost) } : null },
const optional = (number: Numeric | null | undefined) => number == null ? null : decimal(number)
const details = value.details, incident = details.incident
return { ...value, recordVersion: integer(value.recordVersion), paymentStatus: value.paymentStatus ?? null, details: {
recordType: details.recordType, occurredAt: details.occurredAt, distanceUnit: details.distanceUnit ?? null,
odometer: optional(details.odometer), lastServiceOdometer: optional(details.lastServiceOdometer),
nextServiceOdometer: optional(details.nextServiceOdometer), fuelQuantity: optional(details.fuelQuantity),
fuelUnit: details.fuelUnit ?? null, verificationStatus: details.verificationStatus ?? null, remark: details.remark ?? null,
incident: incident ? { description: incident.description, processingStatus: incident.processingStatus,
item: incident.item ?? null, place: incident.place ?? null, handler: incident.handler ?? null,
exceptionType: incident.exceptionType ?? null, reportedCost: optional(incident.reportedCost), currency: incident.currency ?? null } : null },
files: value.files.map(file => ({ ...file, sizeBytes: integer(file.sizeBytes) })) }
}
export async function listVehicleCare(vehicleId: string, recordType: VehicleCareType, page = 1, size = 10): Promise<PageResult<VehicleCareRecord>> {