Add calculate time tracking
This commit is contained in:
@@ -171,14 +171,6 @@ ORDER BY
|
|||||||
c.name,
|
c.name,
|
||||||
st.vendor;
|
st.vendor;
|
||||||
|
|
||||||
-- Update calculate_history table to track all record types
|
|
||||||
ALTER TABLE calculate_history
|
|
||||||
ADD COLUMN total_orders INT DEFAULT 0 AFTER total_products,
|
|
||||||
ADD COLUMN total_purchase_orders INT DEFAULT 0 AFTER total_orders,
|
|
||||||
CHANGE COLUMN products_processed processed_products INT DEFAULT 0,
|
|
||||||
ADD COLUMN processed_orders INT DEFAULT 0 AFTER processed_products,
|
|
||||||
ADD COLUMN processed_purchase_orders INT DEFAULT 0 AFTER processed_orders;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS calculate_history (
|
CREATE TABLE IF NOT EXISTS calculate_history (
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
start_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
start_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
@@ -197,6 +189,21 @@ CREATE TABLE IF NOT EXISTS calculate_history (
|
|||||||
INDEX idx_status_time (status, start_time)
|
INDEX idx_status_time (status, start_time)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS calculate_status (
|
||||||
|
module_name ENUM(
|
||||||
|
'product_metrics',
|
||||||
|
'time_aggregates',
|
||||||
|
'financial_metrics',
|
||||||
|
'vendor_metrics',
|
||||||
|
'category_metrics',
|
||||||
|
'brand_metrics',
|
||||||
|
'sales_forecasts',
|
||||||
|
'abc_classification'
|
||||||
|
) PRIMARY KEY,
|
||||||
|
last_calculation_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
INDEX idx_last_calc (last_calculation_timestamp)
|
||||||
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS sync_status (
|
CREATE TABLE IF NOT EXISTS sync_status (
|
||||||
table_name VARCHAR(50) PRIMARY KEY,
|
table_name VARCHAR(50) PRIMARY KEY,
|
||||||
last_sync_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
last_sync_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|||||||
@@ -497,9 +497,9 @@ async function calculateMetrics() {
|
|||||||
WHERE id = ?
|
WHERE id = ?
|
||||||
`, [
|
`, [
|
||||||
totalElapsedSeconds,
|
totalElapsedSeconds,
|
||||||
processedProducts,
|
processedProducts || 0, // Ensure we have a valid number
|
||||||
processedOrders,
|
processedOrders || 0, // Ensure we have a valid number
|
||||||
processedPurchaseOrders,
|
processedPurchaseOrders || 0, // Ensure we have a valid number
|
||||||
isCancelled ? 'cancelled' : 'failed',
|
isCancelled ? 'cancelled' : 'failed',
|
||||||
error.message,
|
error.message,
|
||||||
calculateHistoryId
|
calculateHistoryId
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
||||||
const { getConnection } = require('./utils/db');
|
const { getConnection } = require('./utils/db');
|
||||||
|
|
||||||
async function calculateBrandMetrics(startTime, totalProducts, processedCount, isCancelled = false) {
|
async function calculateBrandMetrics(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
||||||
const connection = await getConnection();
|
const connection = await getConnection();
|
||||||
|
let success = false;
|
||||||
|
let processedOrders = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
outputProgress({
|
outputProgress({
|
||||||
@@ -20,9 +23,22 @@ async function calculateBrandMetrics(startTime, totalProducts, processedCount, i
|
|||||||
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return processedCount;
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders: 0,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get order count that will be processed
|
||||||
|
const [orderCount] = await connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM orders o
|
||||||
|
WHERE o.canceled = false
|
||||||
|
`);
|
||||||
|
processedOrders = orderCount[0].count;
|
||||||
|
|
||||||
outputProgress({
|
outputProgress({
|
||||||
status: 'running',
|
status: 'running',
|
||||||
operation: 'Starting brand metrics calculation',
|
operation: 'Starting brand metrics calculation',
|
||||||
@@ -178,7 +194,12 @@ async function calculateBrandMetrics(startTime, totalProducts, processedCount, i
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate brand time-based metrics with optimized query
|
// Calculate brand time-based metrics with optimized query
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -266,8 +287,25 @@ async function calculateBrandMetrics(startTime, totalProducts, processedCount, i
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return processedCount;
|
// If we get here, everything completed successfully
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Update calculate_status
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO calculate_status (module_name, last_calculation_timestamp)
|
||||||
|
VALUES ('brand_metrics', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE last_calculation_timestamp = NOW()
|
||||||
|
`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
success = false;
|
||||||
logError(error, 'Error calculating brand metrics');
|
logError(error, 'Error calculating brand metrics');
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
||||||
const { getConnection } = require('./utils/db');
|
const { getConnection } = require('./utils/db');
|
||||||
|
|
||||||
async function calculateCategoryMetrics(startTime, totalProducts, processedCount, isCancelled = false) {
|
async function calculateCategoryMetrics(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
||||||
const connection = await getConnection();
|
const connection = await getConnection();
|
||||||
|
let success = false;
|
||||||
|
let processedOrders = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
outputProgress({
|
outputProgress({
|
||||||
@@ -20,9 +23,22 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return processedCount;
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders: 0,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get order count that will be processed
|
||||||
|
const [orderCount] = await connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM orders o
|
||||||
|
WHERE o.canceled = false
|
||||||
|
`);
|
||||||
|
processedOrders = orderCount[0].count;
|
||||||
|
|
||||||
outputProgress({
|
outputProgress({
|
||||||
status: 'running',
|
status: 'running',
|
||||||
operation: 'Starting category metrics calculation',
|
operation: 'Starting category metrics calculation',
|
||||||
@@ -85,7 +101,12 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Then update with margin and turnover data
|
// Then update with margin and turnover data
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -144,7 +165,12 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Finally update growth rates
|
// Finally update growth rates
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -287,7 +313,12 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate time-based metrics
|
// Calculate time-based metrics
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -361,7 +392,12 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate category-sales metrics
|
// Calculate category-sales metrics
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -455,8 +491,25 @@ async function calculateCategoryMetrics(startTime, totalProducts, processedCount
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return processedCount;
|
// If we get here, everything completed successfully
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Update calculate_status
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO calculate_status (module_name, last_calculation_timestamp)
|
||||||
|
VALUES ('category_metrics', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE last_calculation_timestamp = NOW()
|
||||||
|
`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
success = false;
|
||||||
logError(error, 'Error calculating category metrics');
|
logError(error, 'Error calculating category metrics');
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
||||||
const { getConnection } = require('./utils/db');
|
const { getConnection } = require('./utils/db');
|
||||||
|
|
||||||
async function calculateFinancialMetrics(startTime, totalProducts, processedCount, isCancelled = false) {
|
async function calculateFinancialMetrics(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
||||||
const connection = await getConnection();
|
const connection = await getConnection();
|
||||||
|
let success = false;
|
||||||
|
let processedOrders = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
outputProgress({
|
outputProgress({
|
||||||
@@ -20,9 +23,23 @@ async function calculateFinancialMetrics(startTime, totalProducts, processedCoun
|
|||||||
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return processedCount;
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders: 0,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get order count that will be processed
|
||||||
|
const [orderCount] = await connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM orders o
|
||||||
|
WHERE o.canceled = false
|
||||||
|
AND DATE(o.date) >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
||||||
|
`);
|
||||||
|
processedOrders = orderCount[0].count;
|
||||||
|
|
||||||
outputProgress({
|
outputProgress({
|
||||||
status: 'running',
|
status: 'running',
|
||||||
operation: 'Starting financial metrics calculation',
|
operation: 'Starting financial metrics calculation',
|
||||||
@@ -90,7 +107,12 @@ async function calculateFinancialMetrics(startTime, totalProducts, processedCoun
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Update time-based aggregates with optimized query
|
// Update time-based aggregates with optimized query
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -139,8 +161,25 @@ async function calculateFinancialMetrics(startTime, totalProducts, processedCoun
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return processedCount;
|
// If we get here, everything completed successfully
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Update calculate_status
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO calculate_status (module_name, last_calculation_timestamp)
|
||||||
|
VALUES ('financial_metrics', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE last_calculation_timestamp = NOW()
|
||||||
|
`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
success = false;
|
||||||
logError(error, 'Error calculating financial metrics');
|
logError(error, 'Error calculating financial metrics');
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ function sanitizeValue(value) {
|
|||||||
|
|
||||||
async function calculateProductMetrics(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
async function calculateProductMetrics(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
||||||
const connection = await getConnection();
|
const connection = await getConnection();
|
||||||
|
let success = false;
|
||||||
|
let processedOrders = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Skip flags are inherited from the parent scope
|
// Skip flags are inherited from the parent scope
|
||||||
const SKIP_PRODUCT_BASE_METRICS = 0;
|
const SKIP_PRODUCT_BASE_METRICS = 0;
|
||||||
@@ -32,7 +35,12 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return processedCount;
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0, // This module doesn't process POs
|
||||||
|
success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// First ensure all products have a metrics record
|
// First ensure all products have a metrics record
|
||||||
@@ -60,6 +68,14 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get order count that will be processed
|
||||||
|
const [orderCount] = await connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM orders o
|
||||||
|
WHERE o.canceled = false
|
||||||
|
`);
|
||||||
|
processedOrders = orderCount[0].count;
|
||||||
|
|
||||||
// Calculate base metrics
|
// Calculate base metrics
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
UPDATE product_metrics pm
|
UPDATE product_metrics pm
|
||||||
@@ -181,7 +197,12 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0, // This module doesn't process POs
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate product time aggregates
|
// Calculate product time aggregates
|
||||||
if (!SKIP_PRODUCT_TIME_AGGREGATES) {
|
if (!SKIP_PRODUCT_TIME_AGGREGATES) {
|
||||||
@@ -303,7 +324,12 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0, // This module doesn't process POs
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
const [abcConfig] = await connection.query('SELECT a_threshold, b_threshold FROM abc_classification_config WHERE id = 1');
|
const [abcConfig] = await connection.query('SELECT a_threshold, b_threshold FROM abc_classification_config WHERE id = 1');
|
||||||
const abcThresholds = abcConfig[0] || { a_threshold: 20, b_threshold: 50 };
|
const abcThresholds = abcConfig[0] || { a_threshold: 20, b_threshold: 50 };
|
||||||
@@ -359,7 +385,12 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
const batchSize = 5000;
|
const batchSize = 5000;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0, // This module doesn't process POs
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Get a batch of PIDs that need updating
|
// Get a batch of PIDs that need updating
|
||||||
const [pids] = await connection.query(`
|
const [pids] = await connection.query(`
|
||||||
@@ -426,8 +457,25 @@ async function calculateProductMetrics(startTime, totalProducts, processedCount
|
|||||||
`, [pids.map(row => row.pid), pids.map(row => row.pid)]);
|
`, [pids.map(row => row.pid), pids.map(row => row.pid)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return processedCount;
|
// If we get here, everything completed successfully
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Update calculate_status
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO calculate_status (module_name, last_calculation_timestamp)
|
||||||
|
VALUES ('product_metrics', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE last_calculation_timestamp = NOW()
|
||||||
|
`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0, // This module doesn't process POs
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
success = false;
|
||||||
logError(error, 'Error calculating product metrics');
|
logError(error, 'Error calculating product metrics');
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
||||||
const { getConnection } = require('./utils/db');
|
const { getConnection } = require('./utils/db');
|
||||||
|
|
||||||
async function calculateSalesForecasts(startTime, totalProducts, processedCount, isCancelled = false) {
|
async function calculateSalesForecasts(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
||||||
const connection = await getConnection();
|
const connection = await getConnection();
|
||||||
|
let success = false;
|
||||||
|
let processedOrders = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
outputProgress({
|
outputProgress({
|
||||||
@@ -20,9 +23,23 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return processedCount;
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders: 0,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get order count that will be processed
|
||||||
|
const [orderCount] = await connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM orders o
|
||||||
|
WHERE o.canceled = false
|
||||||
|
AND o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 90 DAY)
|
||||||
|
`);
|
||||||
|
processedOrders = orderCount[0].count;
|
||||||
|
|
||||||
outputProgress({
|
outputProgress({
|
||||||
status: 'running',
|
status: 'running',
|
||||||
operation: 'Starting sales forecasts calculation',
|
operation: 'Starting sales forecasts calculation',
|
||||||
@@ -83,7 +100,12 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Create temporary table for daily sales stats
|
// Create temporary table for daily sales stats
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -117,7 +139,12 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Create temporary table for product stats
|
// Create temporary table for product stats
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -147,7 +174,12 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate product-level forecasts
|
// Calculate product-level forecasts
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -253,7 +285,12 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Create temporary table for category stats
|
// Create temporary table for category stats
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -298,7 +335,12 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate category-level forecasts
|
// Calculate category-level forecasts
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -374,8 +416,25 @@ async function calculateSalesForecasts(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return processedCount;
|
// If we get here, everything completed successfully
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Update calculate_status
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO calculate_status (module_name, last_calculation_timestamp)
|
||||||
|
VALUES ('sales_forecasts', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE last_calculation_timestamp = NOW()
|
||||||
|
`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
success = false;
|
||||||
logError(error, 'Error calculating sales forecasts');
|
logError(error, 'Error calculating sales forecasts');
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
||||||
const { getConnection } = require('./utils/db');
|
const { getConnection } = require('./utils/db');
|
||||||
|
|
||||||
async function calculateTimeAggregates(startTime, totalProducts, processedCount, isCancelled = false) {
|
async function calculateTimeAggregates(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
||||||
const connection = await getConnection();
|
const connection = await getConnection();
|
||||||
|
let success = false;
|
||||||
|
let processedOrders = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
outputProgress({
|
outputProgress({
|
||||||
@@ -20,9 +23,22 @@ async function calculateTimeAggregates(startTime, totalProducts, processedCount,
|
|||||||
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return processedCount;
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders: 0,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get order count that will be processed
|
||||||
|
const [orderCount] = await connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM orders o
|
||||||
|
WHERE o.canceled = false
|
||||||
|
`);
|
||||||
|
processedOrders = orderCount[0].count;
|
||||||
|
|
||||||
outputProgress({
|
outputProgress({
|
||||||
status: 'running',
|
status: 'running',
|
||||||
operation: 'Starting time aggregates calculation',
|
operation: 'Starting time aggregates calculation',
|
||||||
@@ -163,7 +179,12 @@ async function calculateTimeAggregates(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Update with financial metrics
|
// Update with financial metrics
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -209,8 +230,25 @@ async function calculateTimeAggregates(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return processedCount;
|
// If we get here, everything completed successfully
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Update calculate_status
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO calculate_status (module_name, last_calculation_timestamp)
|
||||||
|
VALUES ('time_aggregates', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE last_calculation_timestamp = NOW()
|
||||||
|
`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders: 0,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
success = false;
|
||||||
logError(error, 'Error calculating time aggregates');
|
logError(error, 'Error calculating time aggregates');
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
const { outputProgress, formatElapsedTime, estimateRemaining, calculateRate, logError } = require('./utils/progress');
|
||||||
const { getConnection } = require('./utils/db');
|
const { getConnection } = require('./utils/db');
|
||||||
|
|
||||||
async function calculateVendorMetrics(startTime, totalProducts, processedCount, isCancelled = false) {
|
async function calculateVendorMetrics(startTime, totalProducts, processedCount = 0, isCancelled = false) {
|
||||||
const connection = await getConnection();
|
const connection = await getConnection();
|
||||||
|
let success = false;
|
||||||
|
let processedOrders = 0;
|
||||||
|
let processedPurchaseOrders = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
outputProgress({
|
outputProgress({
|
||||||
@@ -20,9 +24,30 @@ async function calculateVendorMetrics(startTime, totalProducts, processedCount,
|
|||||||
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
elapsed_seconds: Math.round((Date.now() - startTime) / 1000)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return processedCount;
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders,
|
||||||
|
success
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get counts of records that will be processed
|
||||||
|
const [[orderCount], [poCount]] = await Promise.all([
|
||||||
|
connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM orders o
|
||||||
|
WHERE o.canceled = false
|
||||||
|
`),
|
||||||
|
connection.query(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM purchase_orders po
|
||||||
|
WHERE po.status != 0
|
||||||
|
`)
|
||||||
|
]);
|
||||||
|
processedOrders = orderCount.count;
|
||||||
|
processedPurchaseOrders = poCount.count;
|
||||||
|
|
||||||
outputProgress({
|
outputProgress({
|
||||||
status: 'running',
|
status: 'running',
|
||||||
operation: 'Starting vendor metrics calculation',
|
operation: 'Starting vendor metrics calculation',
|
||||||
@@ -68,7 +93,12 @@ async function calculateVendorMetrics(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Now calculate vendor metrics
|
// Now calculate vendor metrics
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -191,7 +221,12 @@ async function calculateVendorMetrics(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isCancelled) return processedCount;
|
if (isCancelled) return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate time-based metrics
|
// Calculate time-based metrics
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
@@ -302,8 +337,25 @@ async function calculateVendorMetrics(startTime, totalProducts, processedCount,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return processedCount;
|
// If we get here, everything completed successfully
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
// Update calculate_status
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO calculate_status (module_name, last_calculation_timestamp)
|
||||||
|
VALUES ('vendor_metrics', NOW())
|
||||||
|
ON DUPLICATE KEY UPDATE last_calculation_timestamp = NOW()
|
||||||
|
`);
|
||||||
|
|
||||||
|
return {
|
||||||
|
processedProducts: processedCount,
|
||||||
|
processedOrders,
|
||||||
|
processedPurchaseOrders,
|
||||||
|
success
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
success = false;
|
||||||
logError(error, 'Error calculating vendor metrics');
|
logError(error, 'Error calculating vendor metrics');
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user