fix: coerce decimals in fuel-logs enhanced repository methods (refs #244)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 35s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 43s
Deploy to Staging / Verify Staging (pull_request) Successful in 3s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 3s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 35s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 43s
Deploy to Staging / Verify Staging (pull_request) Successful in 3s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 3s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped
The enhanced API path on FuelLogsRepository returned raw pg rows straight to the service layer, so DECIMAL columns (fuel_units, cost_per_unit, total_cost, gallons, price_per_gallon) arrived as strings instead of numbers. The service layer compensated with scattered Number() coercion in toEnhancedResponse and getVehicleStats, and EfficiencyCalculationService silently leaned on JS string-to- number coercion in division. Type signatures across the stack declared number and the runtime delivered string. Add a private mapEnhancedRow that coerces all DECIMAL columns with parseFloat while preserving snake_case keys (the convention the service layer already uses to access these rows). Apply it in every enhanced read/write path: createEnhanced, findByVehicleIdEnhanced, findByUserIdEnhanced, findByIdEnhanced, getPreviousLogByOdometer, getLatestLogForVehicle, updateEnhanced. Drop the now-redundant Number() wrappers in toEnhancedResponse and getVehicleStats. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -247,6 +247,20 @@ export class FuelLogsRepository {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Coerces DECIMAL columns from node-postgres strings to numbers while
|
||||||
|
// preserving snake_case row keys used by the enhanced API service callers.
|
||||||
|
private mapEnhancedRow(row: any): any {
|
||||||
|
if (!row) return null;
|
||||||
|
return {
|
||||||
|
...row,
|
||||||
|
fuel_units: row.fuel_units != null ? parseFloat(row.fuel_units) : row.fuel_units,
|
||||||
|
cost_per_unit: row.cost_per_unit != null ? parseFloat(row.cost_per_unit) : row.cost_per_unit,
|
||||||
|
total_cost: row.total_cost != null ? parseFloat(row.total_cost) : row.total_cost,
|
||||||
|
gallons: row.gallons != null ? parseFloat(row.gallons) : row.gallons,
|
||||||
|
price_per_gallon: row.price_per_gallon != null ? parseFloat(row.price_per_gallon) : row.price_per_gallon,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Enhanced API support (new schema)
|
// Enhanced API support (new schema)
|
||||||
async createEnhanced(data: {
|
async createEnhanced(data: {
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -293,7 +307,7 @@ export class FuelLogsRepository {
|
|||||||
data.notes ?? null
|
data.notes ?? null
|
||||||
];
|
];
|
||||||
const res = await this.pool.query(query, values);
|
const res = await this.pool.query(query, values);
|
||||||
return res.rows[0] ?? null;
|
return this.mapEnhancedRow(res.rows[0] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByVehicleIdEnhanced(vehicleId: string): Promise<any[]> {
|
async findByVehicleIdEnhanced(vehicleId: string): Promise<any[]> {
|
||||||
@@ -301,7 +315,7 @@ export class FuelLogsRepository {
|
|||||||
`SELECT * FROM fuel_logs WHERE vehicle_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC`,
|
`SELECT * FROM fuel_logs WHERE vehicle_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC`,
|
||||||
[vehicleId]
|
[vehicleId]
|
||||||
);
|
);
|
||||||
return res.rows;
|
return res.rows.map(r => this.mapEnhancedRow(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByUserIdEnhanced(userId: string): Promise<any[]> {
|
async findByUserIdEnhanced(userId: string): Promise<any[]> {
|
||||||
@@ -309,12 +323,12 @@ export class FuelLogsRepository {
|
|||||||
`SELECT * FROM fuel_logs WHERE user_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC`,
|
`SELECT * FROM fuel_logs WHERE user_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC`,
|
||||||
[userId]
|
[userId]
|
||||||
);
|
);
|
||||||
return res.rows;
|
return res.rows.map(r => this.mapEnhancedRow(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByIdEnhanced(id: string): Promise<any | null> {
|
async findByIdEnhanced(id: string): Promise<any | null> {
|
||||||
const res = await this.pool.query(`SELECT * FROM fuel_logs WHERE id = $1`, [id]);
|
const res = await this.pool.query(`SELECT * FROM fuel_logs WHERE id = $1`, [id]);
|
||||||
return res.rows[0] ?? null;
|
return this.mapEnhancedRow(res.rows[0] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPreviousLogByOdometer(vehicleId: string, odometerReading: number): Promise<any | null> {
|
async getPreviousLogByOdometer(vehicleId: string, odometerReading: number): Promise<any | null> {
|
||||||
@@ -322,7 +336,7 @@ export class FuelLogsRepository {
|
|||||||
`SELECT * FROM fuel_logs WHERE vehicle_id = $1 AND odometer IS NOT NULL AND odometer < $2 ORDER BY odometer DESC LIMIT 1`,
|
`SELECT * FROM fuel_logs WHERE vehicle_id = $1 AND odometer IS NOT NULL AND odometer < $2 ORDER BY odometer DESC LIMIT 1`,
|
||||||
[vehicleId, odometerReading]
|
[vehicleId, odometerReading]
|
||||||
);
|
);
|
||||||
return res.rows[0] ?? null;
|
return this.mapEnhancedRow(res.rows[0] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getLatestLogForVehicle(vehicleId: string): Promise<any | null> {
|
async getLatestLogForVehicle(vehicleId: string): Promise<any | null> {
|
||||||
@@ -330,7 +344,7 @@ export class FuelLogsRepository {
|
|||||||
`SELECT * FROM fuel_logs WHERE vehicle_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC LIMIT 1`,
|
`SELECT * FROM fuel_logs WHERE vehicle_id = $1 ORDER BY date_time DESC NULLS LAST, date DESC NULLS LAST, created_at DESC LIMIT 1`,
|
||||||
[vehicleId]
|
[vehicleId]
|
||||||
);
|
);
|
||||||
return res.rows[0] ?? null;
|
return this.mapEnhancedRow(res.rows[0] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateEnhanced(id: string, data: {
|
async updateEnhanced(id: string, data: {
|
||||||
@@ -416,6 +430,6 @@ export class FuelLogsRepository {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.rows[0];
|
return this.mapEnhancedRow(result.rows[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,8 +234,8 @@ export class FuelLogsService {
|
|||||||
return { logCount: 0, totalFuelUnits: 0, totalCost: 0, averageCostPerUnit: 0, totalDistance: 0, averageEfficiency: 0, unitLabels: labels };
|
return { logCount: 0, totalFuelUnits: 0, totalCost: 0, averageCostPerUnit: 0, totalDistance: 0, averageEfficiency: 0, unitLabels: labels };
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalFuelUnits = rows.reduce((s, r) => s + (Number(r.fuel_units) || 0), 0);
|
const totalFuelUnits = rows.reduce((s, r) => s + (r.fuel_units || 0), 0);
|
||||||
const totalCost = rows.reduce((s, r) => s + (Number(r.total_cost) || 0), 0);
|
const totalCost = rows.reduce((s, r) => s + (r.total_cost || 0), 0);
|
||||||
const averageCostPerUnit = totalFuelUnits > 0 ? totalCost / totalFuelUnits : 0;
|
const averageCostPerUnit = totalFuelUnits > 0 ? totalCost / totalFuelUnits : 0;
|
||||||
|
|
||||||
const sorted = [...rows].sort((a, b) => (new Date(b.date_time || b.date)).getTime() - (new Date(a.date_time || a.date)).getTime());
|
const sorted = [...rows].sort((a, b) => (new Date(b.date_time || b.date)).getTime() - (new Date(a.date_time || a.date)).getTime());
|
||||||
@@ -282,9 +282,9 @@ export class FuelLogsService {
|
|||||||
tripDistance: row.trip_distance ?? undefined,
|
tripDistance: row.trip_distance ?? undefined,
|
||||||
fuelType: row.fuel_type as FuelType,
|
fuelType: row.fuel_type as FuelType,
|
||||||
fuelGrade: row.fuel_grade ?? undefined,
|
fuelGrade: row.fuel_grade ?? undefined,
|
||||||
fuelUnits: Number(row.fuel_units),
|
fuelUnits: row.fuel_units ?? 0,
|
||||||
costPerUnit: Number(row.cost_per_unit),
|
costPerUnit: row.cost_per_unit ?? 0,
|
||||||
totalCost: Number(row.total_cost),
|
totalCost: row.total_cost ?? 0,
|
||||||
locationData: row.location_data ?? undefined,
|
locationData: row.location_data ?? undefined,
|
||||||
notes: row.notes ?? undefined,
|
notes: row.notes ?? undefined,
|
||||||
efficiency: efficiency,
|
efficiency: efficiency,
|
||||||
|
|||||||
Reference in New Issue
Block a user