This commit is contained in:
@@ -127,7 +127,7 @@ export async function queryRangeMileage(
|
||||
]);
|
||||
const { summary, yesterdayRows } = cached.result;
|
||||
return {
|
||||
vehicles: buildSummaryMileage(summary, yesterdayRows, infoMap, targetRows, regionMap),
|
||||
vehicles: buildSummaryMileage(summary, yesterdayRows, infoMap, targetRows, regionMap, cached.result.priorOdometers),
|
||||
summary,
|
||||
start: startDate, end: endDate,
|
||||
queriedAt: cached.queriedAt,
|
||||
|
||||
@@ -54,11 +54,14 @@ test('summary integration preserves scope, valid partial mileage, trend nulls, r
|
||||
assert.equal(first.vehicles[0].plate,'TEST1');assert.equal(first.stats.totalToday,50);
|
||||
assert.equal(first.rangeDailyTotals?.[1].totalKm,null);
|
||||
assert.equal(requests.filter(r=>r.url.endsWith('/summary/query')).length,1);
|
||||
assert.equal(requests.filter(r=>r.url.endsWith('/range/query')).length,0);
|
||||
assert.deepEqual(requests.filter(r=>r.url.endsWith('/range/query')).map(r=>r.body.plateNumbers), [['TEST0']]);
|
||||
const second=await read('&page=2&limit=1&includeDailyTrend=0');assert.equal(second.vehicles[0].periodMileageKm,20);
|
||||
const sorted=await read('&sortOrder=asc&includeDailyTrend=0');assert.equal(sorted.vehicles[0].periodMileageKm,0);
|
||||
const exported=await read('&limit=9999&includeDailyTrend=0');
|
||||
assert.equal(exported.vehicles.length,4);assert.equal(exported.vehicles[3].periodMileageKm,null);
|
||||
const fallback = exported.vehicles.find(v => v.plate === 'TEST0')!;
|
||||
assert.equal(fallback.totalKm,100);
|
||||
assert.equal(fallback.totalKmFallbackDate,'2026-10-10');
|
||||
assert.equal(exported.queriedAt,first.queriedAt);assert.ok(!exported.vehicles[0].dailyMileage);
|
||||
assert.equal(requests.filter(r=>r.url.endsWith('/summary/query')).length,1);
|
||||
user={...user,permissionLevel:'department'};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fetchOneOsDailyMileage, type OneOsDailyMileage } from './oneos-api.js';
|
||||
import { previousMileageDate } from './cache-model.js';
|
||||
import { fetchOneOsDailyMileage, fetchOneOsMileageDates, type OneOsDailyMileage } from './oneos-api.js';
|
||||
import { mileageDatesBetween, previousMileageDate } from './cache-model.js';
|
||||
import { fetchMileageSummary, type MileageSummary } from './summary-api.js';
|
||||
import type { OneOsProtocol } from './source-policy.js';
|
||||
|
||||
@@ -10,6 +10,7 @@ const MAX_ENTRIES = 4;
|
||||
export interface RangeMileageSource {
|
||||
summary: MileageSummary;
|
||||
yesterdayRows: OneOsDailyMileage[];
|
||||
priorOdometers?: Map<string, { date: string; km: number }>;
|
||||
}
|
||||
|
||||
type Entry = { result: RangeMileageSource; queriedAt: string; expiresAt: number };
|
||||
@@ -19,7 +20,23 @@ async function loadSource(start: string, end: string, protocols: OneOsProtocol[]
|
||||
fetchMileageSummary(start, end, protocols, plates),
|
||||
plates ? Promise.resolve([]) : fetchOneOsDailyMileage(previousMileageDate(start), undefined, protocols, refresh),
|
||||
]);
|
||||
return { summary, yesterdayRows };
|
||||
// Plate-scoped summary requests are used only for filtered trends.
|
||||
if (plates || start === end) return { summary, yesterdayRows };
|
||||
const missing = summary.vehicles.filter(v => v.endDateTotalMileageKm === null && v.validDays + v.anomalyDays > 0);
|
||||
if (!missing.length) return { summary, yesterdayRows };
|
||||
const dates = mileageDatesBetween(start, previousMileageDate(end));
|
||||
const history = await fetchOneOsMileageDates(dates, missing.map(v => v.plateNumber), protocols);
|
||||
const priorOdometers = new Map<string, { date: string; km: number }>();
|
||||
for (const date of dates.reverse()) {
|
||||
for (const row of history.get(date) || []) {
|
||||
const plate = row.plateNumber.toUpperCase();
|
||||
const trusted = row.status === 'NORMAL' || row.dataQuality === 'BASELINE_MISSING' || row.dataQuality === 'ODOMETER_SOURCE_CHANGED';
|
||||
if (!priorOdometers.has(plate) && trusted && row.dataQuality !== 'CARRIED_FORWARD' && row.totalMileageKm != null) {
|
||||
priorOdometers.set(plate, { date, km: row.totalMileageKm });
|
||||
}
|
||||
}
|
||||
}
|
||||
return { summary, yesterdayRows, priorOdometers };
|
||||
}
|
||||
|
||||
export function createMonitoringQueryCache(
|
||||
|
||||
@@ -4,15 +4,17 @@ import { buildPlateTargetNamesMap, dailyMileageMap, mergeMonitoringVehicles, typ
|
||||
import type { CachedVehicle, VehicleInfoRow } from './types.js';
|
||||
import { sourceCategoryFromProtocol } from './source-policy.js';
|
||||
|
||||
export function buildSummaryMileage(summary: MileageSummary, yesterdayRows: OneOsDailyMileage[], infoMap: Map<string, VehicleInfoRow>, targets: TargetRow[], regions: Record<string, string>): CachedVehicle[] {
|
||||
export function buildSummaryMileage(summary: MileageSummary, yesterdayRows: OneOsDailyMileage[], infoMap: Map<string, VehicleInfoRow>, targets: TargetRow[], regions: Record<string, string>, priorOdometers?: Map<string, { date: string; km: number }>): CachedVehicle[] {
|
||||
const byPlate = new Map(summary.vehicles.map(v => [v.plateNumber, v]));
|
||||
return mergeMonitoringVehicles([], infoMap, dailyMileageMap(yesterdayRows), buildPlateTargetNamesMap(targets), regions).map(vehicle => {
|
||||
const row = byPlate.get(vehicle.plate.trim().toUpperCase());
|
||||
if (!row) return { ...vehicle, periodMileageKm: null };
|
||||
const prior = priorOdometers?.get(row.plateNumber);
|
||||
const categories = new Set(row.sourceProtocols.map(sourceCategoryFromProtocol));
|
||||
return { ...vehicle, vin: row.vin || vehicle.vin,
|
||||
dailyKm: row.periodMileageKm ?? 0, periodMileageKm: row.periodMileageKm,
|
||||
totalKm: row.endDateTotalMileageKm,
|
||||
totalKm: row.endDateTotalMileageKm ?? prior?.km ?? null,
|
||||
totalKmFallbackDate: prior?.date,
|
||||
validDays: row.validDays, missingDays: row.missingDays, anomalyDays: row.anomalyDays,
|
||||
qualityStatus: row.status, qualityReasons: row.qualityReasons, sourceProtocols: row.sourceProtocols,
|
||||
mileageAnomaly: row.anomalyDays ? row.qualityReasons.join(', ') || 'DATA_ANOMALY' : null,
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface CachedVehicle {
|
||||
mileageAnomaly?: string | null;
|
||||
dailyMileage?: Record<string, number | null>;
|
||||
totalKm: number | null;
|
||||
totalKmFallbackDate?: string;
|
||||
source: string;
|
||||
sourceProtocol: 'GB32960' | 'MQTT' | 'JT808' | null;
|
||||
sourceCategory: 'INSTRUMENT' | 'GPS' | 'MIXED' | null;
|
||||
|
||||
Reference in New Issue
Block a user