Add in forecasting, lifecycle phases, associated component and script changes

This commit is contained in:
2026-02-13 22:45:18 -05:00
parent f41b5ab0f6
commit 45ded53530
29 changed files with 3643 additions and 376 deletions

View File

@@ -58,8 +58,12 @@ async function importOrders(prodConnection, localConnection, incrementalUpdate =
"SELECT last_sync_timestamp FROM sync_status WHERE table_name = 'orders'"
);
const lastSyncTime = syncInfo?.rows?.[0]?.last_sync_timestamp || '1970-01-01';
// Adjust for mysql2 driver timezone vs MySQL server timezone mismatch
const mysqlSyncTime = prodConnection.adjustDateForMySQL
? prodConnection.adjustDateForMySQL(lastSyncTime)
: lastSyncTime;
console.log('Orders: Using last sync time:', lastSyncTime);
console.log('Orders: Using last sync time:', lastSyncTime, '(adjusted:', mysqlSyncTime, ')');
// First get count of order items - Keep MySQL compatible for production
const [[{ total }]] = await prodConnection.query(`
@@ -82,7 +86,7 @@ async function importOrders(prodConnection, localConnection, incrementalUpdate =
)
)
` : ''}
`, incrementalUpdate ? [lastSyncTime, lastSyncTime, lastSyncTime] : []);
`, incrementalUpdate ? [mysqlSyncTime, mysqlSyncTime, mysqlSyncTime] : []);
totalOrderItems = total;
console.log('Orders: Found changes:', totalOrderItems);
@@ -116,7 +120,7 @@ async function importOrders(prodConnection, localConnection, incrementalUpdate =
)
)
` : ''}
`, incrementalUpdate ? [lastSyncTime, lastSyncTime, lastSyncTime] : []);
`, incrementalUpdate ? [mysqlSyncTime, mysqlSyncTime, mysqlSyncTime] : []);
console.log('Orders: Found', orderItems.length, 'order items to process');

View File

@@ -669,8 +669,13 @@ async function importProducts(prodConnection, localConnection, incrementalUpdate
// Setup temporary tables
await setupTemporaryTables(localConnection);
// Adjust sync time for mysql2 driver timezone vs MySQL server timezone mismatch
const mysqlSyncTime = prodConnection.adjustDateForMySQL
? prodConnection.adjustDateForMySQL(lastSyncTime)
: lastSyncTime;
// Materialize calculations into temp table
const materializeResult = await materializeCalculations(prodConnection, localConnection, incrementalUpdate, lastSyncTime, startTime);
const materializeResult = await materializeCalculations(prodConnection, localConnection, incrementalUpdate, mysqlSyncTime, startTime);
// Get the list of products that need updating
const [products] = await localConnection.query(`

View File

@@ -65,8 +65,12 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
"SELECT last_sync_timestamp FROM sync_status WHERE table_name = 'purchase_orders'"
);
const lastSyncTime = syncInfo?.rows?.[0]?.last_sync_timestamp || '1970-01-01';
// Adjust for mysql2 driver timezone vs MySQL server timezone mismatch
const mysqlSyncTime = prodConnection.adjustDateForMySQL
? prodConnection.adjustDateForMySQL(lastSyncTime)
: lastSyncTime;
console.log('Purchase Orders: Using last sync time:', lastSyncTime);
console.log('Purchase Orders: Using last sync time:', lastSyncTime, '(adjusted:', mysqlSyncTime, ')');
// Create temp tables for processing
await localConnection.query(`
@@ -254,7 +258,7 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
OR p.date_estin > ?
)
` : ''}
`, incrementalUpdate ? [lastSyncTime, lastSyncTime, lastSyncTime] : []);
`, incrementalUpdate ? [mysqlSyncTime, mysqlSyncTime, mysqlSyncTime] : []);
const totalPOs = poCount[0].total;
console.log(`Found ${totalPOs} relevant purchase orders`);
@@ -291,7 +295,7 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
` : ''}
ORDER BY p.po_id
LIMIT ${PO_BATCH_SIZE} OFFSET ${offset}
`, incrementalUpdate ? [lastSyncTime, lastSyncTime, lastSyncTime] : []);
`, incrementalUpdate ? [mysqlSyncTime, mysqlSyncTime, mysqlSyncTime] : []);
if (poList.length === 0) {
allPOsProcessed = true;
@@ -426,7 +430,7 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
OR r.date_created > ?
)
` : ''}
`, incrementalUpdate ? [lastSyncTime, lastSyncTime] : []);
`, incrementalUpdate ? [mysqlSyncTime, mysqlSyncTime] : []);
const totalReceivings = receivingCount[0].total;
console.log(`Found ${totalReceivings} relevant receivings`);
@@ -463,7 +467,7 @@ async function importPurchaseOrders(prodConnection, localConnection, incremental
` : ''}
ORDER BY r.receiving_id
LIMIT ${PO_BATCH_SIZE} OFFSET ${offset}
`, incrementalUpdate ? [lastSyncTime, lastSyncTime] : []);
`, incrementalUpdate ? [mysqlSyncTime, mysqlSyncTime] : []);
if (receivingList.length === 0) {
allReceivingsProcessed = true;

View File

@@ -48,6 +48,37 @@ async function setupConnections(sshConfig) {
stream: tunnel.stream,
});
// Detect MySQL server timezone and calculate correction for the driver timezone mismatch.
// The mysql2 driver is configured with timezone: '-05:00' (EST), but the MySQL server
// may be in a different timezone (e.g., America/Chicago = CST/CDT). When the driver
// formats a JS Date as EST and MySQL interprets it in its own timezone, DATETIME
// comparisons can be off. This correction adjusts Date objects before they're passed
// to MySQL queries so the formatted string matches the server's local time.
const [[{ utcDiffSec }]] = await prodConnection.query(
"SELECT TIMESTAMPDIFF(SECOND, NOW(), UTC_TIMESTAMP()) as utcDiffSec"
);
const mysqlOffsetMs = -utcDiffSec * 1000; // MySQL UTC offset in ms (e.g., -21600000 for CST)
const driverOffsetMs = -5 * 3600 * 1000; // Driver's -05:00 in ms (-18000000)
const tzCorrectionMs = driverOffsetMs - mysqlOffsetMs;
// CST (winter): -18000000 - (-21600000) = +3600000 (1 hour correction needed)
// CDT (summer): -18000000 - (-18000000) = 0 (no correction needed)
if (tzCorrectionMs !== 0) {
console.log(`MySQL timezone correction: ${tzCorrectionMs / 1000}s (server offset: ${utcDiffSec}s from UTC)`);
}
/**
* Adjusts a Date/timestamp for the mysql2 driver timezone mismatch before
* passing it as a query parameter to MySQL. This ensures that the string
* mysql2 generates matches the timezone that DATETIME values are stored in.
*/
function adjustDateForMySQL(date) {
if (!date || tzCorrectionMs === 0) return date;
const d = date instanceof Date ? date : new Date(date);
return new Date(d.getTime() - tzCorrectionMs);
}
prodConnection.adjustDateForMySQL = adjustDateForMySQL;
// Setup PostgreSQL connection pool for local
const localPool = new Pool(sshConfig.localDbConfig);