mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
feat(reports): carry time of day on report date columns
Eleven report columns bucketed their timestamp to a bare day with to_char, which is wrong for anything a user reads as an event rather than a period: two departures on the same date, or a wagon request fulfilled hours after it was raised, were indistinguishable in the output. The renderer only shows the time when the value actually has one, keyed off the string rather than a per-column flag — a genuine day bucket would otherwise render as 12:00 AM, which reads as data rather than as absence.
This commit is contained in:
@@ -68,7 +68,7 @@ export const chargedVsActualVolumeReport: ReportDefinition = {
|
||||
query(ctx) {
|
||||
return baseQuery(ctx)
|
||||
.select("COALESCE(ts.train_number, '—')", 'trainNumber')
|
||||
.addSelect(`to_char(COALESCE(ts.actual_departure_at, ts.scheduled_departure_date), 'YYYY-MM-DD')`, 'departedAt')
|
||||
.addSelect(`to_char(COALESCE(ts.actual_departure_at, ts.scheduled_departure_date), 'YYYY-MM-DD HH24:MI')`, 'departedAt')
|
||||
.addSelect("COALESCE(oy.label, oy.code, '?') || ' → ' || COALESCE(dy.label, dy.code, '?')", 'station')
|
||||
.addSelect(CARGO_CATEGORY_LABEL_EXPR, 'category')
|
||||
.addSelect(`ROUND((${CHARGED_TONS_EXPR})::numeric, 2)::float8`, 'chargedTons')
|
||||
|
||||
@@ -91,8 +91,8 @@ export const contractUtilizationReport: ReportDefinition = {
|
||||
.addSelect('c.name', 'customer')
|
||||
.addSelect('ct.status', 'status')
|
||||
.addSelect('ct.contract_kind', 'kind')
|
||||
.addSelect(`to_char(ct.contract_valid_from, 'YYYY-MM-DD')`, 'validFrom')
|
||||
.addSelect(`to_char(ct.contract_valid_until, 'YYYY-MM-DD')`, 'validUntil')
|
||||
.addSelect(`to_char(ct.contract_valid_from, 'YYYY-MM-DD HH24:MI')`, 'validFrom')
|
||||
.addSelect(`to_char(ct.contract_valid_until, 'YYYY-MM-DD HH24:MI')`, 'validUntil')
|
||||
.addSelect('COALESCE(cap.committed, 0)::float8', 'committed')
|
||||
.addSelect('COALESCE(booked.tons, 0)::float8', 'bookedTons')
|
||||
.addSelect('COALESCE(booked.cnt, 0)', 'bookings')
|
||||
|
||||
@@ -72,7 +72,7 @@ export const firstLastMileBookingsReport: ReportDefinition = {
|
||||
.addSelect('fl.status', 'status')
|
||||
.addSelect("COALESCE(v.plate_number, '—')", 'truck')
|
||||
.addSelect("CASE WHEN fl.vehicle_id IS NOT NULL THEN 'Assigned' ELSE 'Unassigned' END", 'assigned')
|
||||
.addSelect(`to_char(fl.created_at, 'YYYY-MM-DD')`, 'createdAt');
|
||||
.addSelect(`to_char(fl.created_at, 'YYYY-MM-DD HH24:MI')`, 'createdAt');
|
||||
},
|
||||
async summary(ctx) {
|
||||
const row = await baseQuery(ctx)
|
||||
|
||||
@@ -49,7 +49,7 @@ export const loadedCapacityReport: ReportDefinition = {
|
||||
query(ctx) {
|
||||
return baseQuery(ctx)
|
||||
.select('ts.train_number', 'trainNumber')
|
||||
.addSelect(`to_char(ts.scheduled_departure_date, 'YYYY-MM-DD')`, 'departureDate')
|
||||
.addSelect(`to_char(ts.scheduled_departure_date, 'YYYY-MM-DD HH24:MI')`, 'departureDate')
|
||||
.addSelect("COALESCE(wt.name, 'Unknown')", 'wagonType')
|
||||
.addSelect('COUNT(*)::int', 'wagons')
|
||||
.addSelect('COALESCE(SUM(tsw.capacity_tons), 0)::float8', 'capacityTons')
|
||||
|
||||
@@ -378,7 +378,7 @@ export const receivablesPayablesReport: ReportDefinition = {
|
||||
query(ctx) {
|
||||
return baseQuery(ctx)
|
||||
.select(SIDE_LABEL_OF('r.side_key'), 'side')
|
||||
.addSelect("to_char(r.txn_date, 'YYYY-MM-DD')", 'issuedAt')
|
||||
.addSelect("to_char(r.txn_date, 'YYYY-MM-DD HH24:MI')", 'issuedAt')
|
||||
.addSelect('r.doc_ref', 'invoiceNumber')
|
||||
.addSelect('r.booking_ref', 'bookingRef')
|
||||
.addSelect('r.booking_status', 'bookingStatus')
|
||||
|
||||
@@ -63,7 +63,7 @@ export const revenueReconciliationReport: ReportDefinition = {
|
||||
defaultSort: { key: 'variance', dir: 'DESC' },
|
||||
query(ctx) {
|
||||
return baseQuery(ctx)
|
||||
.select(`to_char(${REVENUE_DATE}, 'YYYY-MM-DD')`, 'issuedAt')
|
||||
.select(`to_char(${REVENUE_DATE}, 'YYYY-MM-DD HH24:MI')`, 'issuedAt')
|
||||
.addSelect('i.invoice_number', 'invoiceNumber')
|
||||
.addSelect("COALESCE(b.reference, '—')", 'bookingRef')
|
||||
.addSelect(PAYER_EXPR, 'customer')
|
||||
|
||||
@@ -93,7 +93,7 @@ export const revenueTransactionsReport: ReportDefinition = {
|
||||
defaultSort: { key: 'issuedAt', dir: 'DESC' },
|
||||
query(ctx) {
|
||||
return baseQuery(ctx)
|
||||
.select(`to_char(${REVENUE_DATE}, 'YYYY-MM-DD')`, 'issuedAt')
|
||||
.select(`to_char(${REVENUE_DATE}, 'YYYY-MM-DD HH24:MI')`, 'issuedAt')
|
||||
.addSelect('i.invoice_number', 'invoiceNumber')
|
||||
.addSelect("COALESCE(b.reference, '—')", 'bookingRef')
|
||||
.addSelect("COALESCE(b.id::text, '')", 'bookingId')
|
||||
|
||||
@@ -76,7 +76,7 @@ export const trainScheduleStatusReport: ReportDefinition = {
|
||||
.addSelect('ts.direction', 'direction')
|
||||
.addSelect("COALESCE(o.label, 'Unknown')", 'origin')
|
||||
.addSelect("COALESCE(d.label, 'Unknown')", 'destination')
|
||||
.addSelect(`to_char(ts.scheduled_departure_date, 'YYYY-MM-DD')`, 'scheduledDeparture')
|
||||
.addSelect(`to_char(ts.scheduled_departure_date, 'YYYY-MM-DD HH24:MI')`, 'scheduledDeparture')
|
||||
.addSelect(`to_char(ts.actual_departure_at, 'YYYY-MM-DD HH24:MI')`, 'actualDeparture')
|
||||
.addSelect(`to_char(ts.actual_arrival_at, 'YYYY-MM-DD HH24:MI')`, 'actualArrival');
|
||||
},
|
||||
|
||||
@@ -57,8 +57,8 @@ export const wagonRequestsReport: ReportDefinition = {
|
||||
.addSelect('r.quantity', 'quantity')
|
||||
.addSelect('r.fulfilled_quantity', 'fulfilledQuantity')
|
||||
.addSelect('r.status', 'status')
|
||||
.addSelect(`to_char(r.created_at, 'YYYY-MM-DD')`, 'requestedAt')
|
||||
.addSelect(`to_char(r.fulfilled_at, 'YYYY-MM-DD')`, 'fulfilledAt')
|
||||
.addSelect(`to_char(r.created_at, 'YYYY-MM-DD HH24:MI')`, 'requestedAt')
|
||||
.addSelect(`to_char(r.fulfilled_at, 'YYYY-MM-DD HH24:MI')`, 'fulfilledAt')
|
||||
.addSelect(
|
||||
`ROUND(EXTRACT(EPOCH FROM (COALESCE(r.fulfilled_at, now()) - r.created_at))::numeric / 86400, 1)::float8`,
|
||||
'delayDays',
|
||||
|
||||
@@ -69,7 +69,7 @@ export const wagonStatusDurationReport: ReportDefinition = {
|
||||
.addSelect("COALESCE(wt.name, 'Unknown')", 'wagonType')
|
||||
.addSelect("COALESCE(y.label, 'Unassigned')", 'station')
|
||||
.addSelect('w.status', 'status')
|
||||
.addSelect(`to_char(COALESCE(log.since, w.updated_at), 'YYYY-MM-DD')`, 'since')
|
||||
.addSelect(`to_char(COALESCE(log.since, w.updated_at), 'YYYY-MM-DD HH24:MI')`, 'since')
|
||||
.addSelect(
|
||||
`FLOOR(EXTRACT(EPOCH FROM (now() - COALESCE(log.since, w.updated_at))) / 86400)::int`,
|
||||
'daysInStatus',
|
||||
|
||||
@@ -53,7 +53,7 @@ export const wagonTeuUtilizationReport: ReportDefinition = {
|
||||
.select('w.wagon_number', 'wagonNumber')
|
||||
.addSelect("COALESCE(wt.name, 'Unknown')", 'wagonType')
|
||||
.addSelect('ts.train_number', 'trainNumber')
|
||||
.addSelect(`to_char(ts.scheduled_departure_date, 'YYYY-MM-DD')`, 'departureDate')
|
||||
.addSelect(`to_char(ts.scheduled_departure_date, 'YYYY-MM-DD HH24:MI')`, 'departureDate')
|
||||
.addSelect('COUNT(c.id)::int', 'containers')
|
||||
.addSelect('(COALESCE(SUM(ct.size_ft), 0) / 20.0)::float8', 'teu')
|
||||
.groupBy('w.wagon_number')
|
||||
|
||||
@@ -17,10 +17,17 @@ export function formatReportCell(value: unknown, type: ReportColumnType): string
|
||||
case "number":
|
||||
return Number(value).toLocaleString();
|
||||
case "date": {
|
||||
const d = new Date(String(value));
|
||||
return Number.isNaN(d.getTime())
|
||||
? String(value)
|
||||
: d.toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" });
|
||||
const raw = String(value);
|
||||
const d = new Date(raw);
|
||||
if (Number.isNaN(d.getTime())) return raw;
|
||||
// Day-bucket columns carry no time part — don't invent a 12:00 AM for them.
|
||||
const hasTime = /\d:\d/.test(raw);
|
||||
return d.toLocaleString(undefined, {
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
...(hasTime ? { hour: "2-digit", minute: "2-digit" } : {}),
|
||||
});
|
||||
}
|
||||
default:
|
||||
return String(value);
|
||||
|
||||
Reference in New Issue
Block a user