Time unification
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
import express from 'express';
|
||||
import { DateTime } from 'luxon';
|
||||
import { getDbConnection, getPoolStatus } from '../db/connection.js';
|
||||
import { getTimeRangeConditions } from '../utils/timeUtils.js';
|
||||
import {
|
||||
getTimeRangeConditions,
|
||||
customRange,
|
||||
previousRange,
|
||||
BUSINESS_TZ,
|
||||
} from '../../../shared/business-time/index.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -170,9 +175,10 @@ router.get('/', async (req, res) => {
|
||||
const ordersPerHour = totalPickingHours > 0 ? totalOrdersPicked / totalPickingHours : 0;
|
||||
const piecesPerHour = totalPickingHours > 0 ? totalPiecesPicked / totalPickingHours : 0;
|
||||
|
||||
// Get daily trend data for picking
|
||||
// Use DATE_FORMAT to get date string in Eastern timezone
|
||||
// Business day starts at 1 AM, so subtract 1 hour before taking the date
|
||||
// Get daily trend data for picking.
|
||||
// Columns store Central wall-clock literals, and the business date IS
|
||||
// the Central calendar date — so DATE(col) is the business date with
|
||||
// no hour shift.
|
||||
const pickingTrendWhere = whereClause.replace(/date_placed/g, 'pt.createddate');
|
||||
const pickingTrendQuery = `
|
||||
SELECT
|
||||
@@ -181,22 +187,22 @@ router.get('/', async (req, res) => {
|
||||
pt_agg.piecesPicked
|
||||
FROM (
|
||||
SELECT
|
||||
DATE_FORMAT(DATE_SUB(pt.createddate, INTERVAL 1 HOUR), '%Y-%m-%d') as date,
|
||||
DATE_FORMAT(pt.createddate, '%Y-%m-%d') as date,
|
||||
COALESCE(SUM(pt.totalpieces_picked), 0) as piecesPicked
|
||||
FROM picking_ticket pt
|
||||
WHERE ${pickingTrendWhere}
|
||||
AND pt.closeddate IS NOT NULL
|
||||
GROUP BY DATE_FORMAT(DATE_SUB(pt.createddate, INTERVAL 1 HOUR), '%Y-%m-%d')
|
||||
GROUP BY DATE_FORMAT(pt.createddate, '%Y-%m-%d')
|
||||
) pt_agg
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
DATE_FORMAT(DATE_SUB(pt.createddate, INTERVAL 1 HOUR), '%Y-%m-%d') as date,
|
||||
DATE_FORMAT(pt.createddate, '%Y-%m-%d') as date,
|
||||
COUNT(DISTINCT CASE WHEN ptb.is_sub = 0 OR ptb.is_sub IS NULL THEN ptb.orderid END) as ordersPicked
|
||||
FROM picking_ticket pt
|
||||
LEFT JOIN picking_ticket_buckets ptb ON pt.pickingid = ptb.pickingid
|
||||
WHERE ${pickingTrendWhere}
|
||||
AND pt.closeddate IS NOT NULL
|
||||
GROUP BY DATE_FORMAT(DATE_SUB(pt.createddate, INTERVAL 1 HOUR), '%Y-%m-%d')
|
||||
GROUP BY DATE_FORMAT(pt.createddate, '%Y-%m-%d')
|
||||
) order_counts ON pt_agg.date = order_counts.date
|
||||
ORDER BY pt_agg.date
|
||||
`;
|
||||
@@ -205,13 +211,13 @@ router.get('/', async (req, res) => {
|
||||
const shippingTrendWhere = whereClause.replace(/date_placed/g, 'o.date_shipped');
|
||||
const shippingTrendQuery = `
|
||||
SELECT
|
||||
DATE_FORMAT(DATE_SUB(o.date_shipped, INTERVAL 1 HOUR), '%Y-%m-%d') as date,
|
||||
DATE_FORMAT(o.date_shipped, '%Y-%m-%d') as date,
|
||||
COUNT(DISTINCT CASE WHEN o.order_type != 8 OR o.order_type IS NULL THEN o.order_id END) as ordersShipped,
|
||||
COALESCE(SUM(o.stats_prod_pieces), 0) as piecesShipped
|
||||
FROM _order o
|
||||
WHERE ${shippingTrendWhere}
|
||||
AND o.order_status IN (100, 92)
|
||||
GROUP BY DATE_FORMAT(DATE_SUB(o.date_shipped, INTERVAL 1 HOUR), '%Y-%m-%d')
|
||||
GROUP BY DATE_FORMAT(o.date_shipped, '%Y-%m-%d')
|
||||
ORDER BY date
|
||||
`;
|
||||
|
||||
@@ -239,13 +245,14 @@ router.get('/', async (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
// Generate all dates in the period range for complete trend data
|
||||
// Generate all business dates in the half-open period [start, end) for
|
||||
// complete trend data. Business date = Chicago calendar date.
|
||||
const allDatesInRange = [];
|
||||
const startDt = DateTime.fromJSDate(periodStart).setZone(TIMEZONE).startOf('day');
|
||||
const endDt = DateTime.fromJSDate(periodEnd).setZone(TIMEZONE).startOf('day');
|
||||
const startDt = DateTime.fromJSDate(periodStart).setZone(BUSINESS_TZ).startOf('day');
|
||||
const endDt = DateTime.fromJSDate(periodEnd).setZone(BUSINESS_TZ);
|
||||
|
||||
let currentDt = startDt;
|
||||
while (currentDt <= endDt) {
|
||||
while (currentDt < endDt) {
|
||||
allDatesInRange.push(currentDt.toFormat('yyyy-MM-dd'));
|
||||
currentDt = currentDt.plus({ days: 1 });
|
||||
}
|
||||
@@ -255,7 +262,8 @@ router.get('/', async (req, res) => {
|
||||
const picking = pickingByDate.get(date) || { ordersPicked: 0, piecesPicked: 0 };
|
||||
const shippingData = shippingByDate.get(date) || { ordersShipped: 0, piecesShipped: 0 };
|
||||
|
||||
// Parse date string in Eastern timezone to get proper ISO timestamp
|
||||
// The string is already the business date; the zone here is only for
|
||||
// the display timestamp label (office time).
|
||||
const dateDt = DateTime.fromFormat(date, 'yyyy-MM-dd', { zone: TIMEZONE });
|
||||
|
||||
return {
|
||||
@@ -446,22 +454,13 @@ function getPreviousPeriodRange(timeRange, startDate, endDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
if (Number.isNaN(start.getTime()) || Number.isNaN(end.getTime())) {
|
||||
try {
|
||||
const prev = previousRange(customRange(startDate, endDate));
|
||||
if (!prev) return null;
|
||||
return getTimeRangeConditions('custom', prev.start.toISO(), prev.end.toISO());
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
const duration = end.getTime() - start.getTime();
|
||||
if (!Number.isFinite(duration) || duration <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const prevEnd = new Date(start.getTime() - 1);
|
||||
const prevStart = new Date(prevEnd.getTime() - duration);
|
||||
|
||||
return getTimeRangeConditions('custom', prevStart.toISOString(), prevEnd.toISOString());
|
||||
}
|
||||
|
||||
function getPreviousTimeRange(timeRange) {
|
||||
|
||||
Reference in New Issue
Block a user