Fixes for reset and calculate scripts
This commit is contained in:
@@ -256,53 +256,195 @@ async function calculateVendorMetrics(connection, startTime, totalProducts) {
|
|||||||
percentage: '70'
|
percentage: '70'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Calculate vendor performance metrics
|
||||||
await connection.query(`
|
await connection.query(`
|
||||||
INSERT INTO vendor_metrics (
|
INSERT INTO vendor_metrics (
|
||||||
vendor,
|
vendor,
|
||||||
last_calculated_at,
|
|
||||||
avg_lead_time_days,
|
avg_lead_time_days,
|
||||||
on_time_delivery_rate,
|
on_time_delivery_rate,
|
||||||
order_fill_rate,
|
order_fill_rate,
|
||||||
total_orders,
|
total_orders,
|
||||||
total_late_orders,
|
total_late_orders,
|
||||||
total_purchase_value,
|
total_purchase_value,
|
||||||
avg_order_value
|
avg_order_value,
|
||||||
|
active_products,
|
||||||
|
total_products,
|
||||||
|
total_revenue,
|
||||||
|
avg_margin_percent,
|
||||||
|
status
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
po.vendor,
|
vd.vendor,
|
||||||
NOW() as last_calculated_at,
|
-- Lead time metrics
|
||||||
AVG(
|
AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days,
|
||||||
CASE
|
-- On-time delivery rate
|
||||||
WHEN po.received_date IS NOT NULL
|
(COUNT(CASE WHEN po.received_date <= po.expected_date THEN 1 END) / COUNT(*)) * 100 as on_time_delivery_rate,
|
||||||
THEN DATEDIFF(po.received_date, po.date)
|
-- Order fill rate
|
||||||
ELSE NULL
|
(SUM(po.received) / SUM(po.ordered)) * 100 as order_fill_rate,
|
||||||
END
|
COUNT(DISTINCT po.po_id) as total_orders,
|
||||||
) as avg_lead_time_days,
|
COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as total_late_orders,
|
||||||
(
|
SUM(po.cost_price * po.ordered) as total_purchase_value,
|
||||||
COUNT(CASE WHEN po.received_date <= po.expected_date THEN 1 END) * 100.0 /
|
AVG(po.cost_price * po.ordered) as avg_order_value,
|
||||||
NULLIF(COUNT(*), 0)
|
-- Product counts
|
||||||
) as on_time_delivery_rate,
|
COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products,
|
||||||
(
|
COUNT(DISTINCT p.product_id) as total_products,
|
||||||
SUM(CASE WHEN po.received >= po.ordered THEN 1 ELSE 0 END) * 100.0 /
|
-- Financial metrics
|
||||||
NULLIF(COUNT(*), 0)
|
SUM(o.price * o.quantity) as total_revenue,
|
||||||
) as order_fill_rate,
|
AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent,
|
||||||
COUNT(*) as total_orders,
|
vd.status
|
||||||
COUNT(CASE WHEN po.received_date > po.expected_date THEN 1 END) as total_late_orders,
|
FROM vendor_details vd
|
||||||
SUM(po.ordered * po.cost_price) as total_purchase_value,
|
LEFT JOIN products p ON vd.vendor = p.vendor
|
||||||
AVG(po.ordered * po.cost_price) as avg_order_value
|
LEFT JOIN purchase_orders po ON p.product_id = po.product_id
|
||||||
FROM purchase_orders po
|
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
||||||
WHERE po.status = 'closed'
|
GROUP BY vd.vendor, vd.status
|
||||||
AND po.date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
|
||||||
GROUP BY po.vendor
|
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
last_calculated_at = VALUES(last_calculated_at),
|
|
||||||
avg_lead_time_days = VALUES(avg_lead_time_days),
|
avg_lead_time_days = VALUES(avg_lead_time_days),
|
||||||
on_time_delivery_rate = VALUES(on_time_delivery_rate),
|
on_time_delivery_rate = VALUES(on_time_delivery_rate),
|
||||||
order_fill_rate = VALUES(order_fill_rate),
|
order_fill_rate = VALUES(order_fill_rate),
|
||||||
total_orders = VALUES(total_orders),
|
total_orders = VALUES(total_orders),
|
||||||
total_late_orders = VALUES(total_late_orders),
|
total_late_orders = VALUES(total_late_orders),
|
||||||
total_purchase_value = VALUES(total_purchase_value),
|
total_purchase_value = VALUES(total_purchase_value),
|
||||||
avg_order_value = VALUES(avg_order_value)
|
avg_order_value = VALUES(avg_order_value),
|
||||||
|
active_products = VALUES(active_products),
|
||||||
|
total_products = VALUES(total_products),
|
||||||
|
total_revenue = VALUES(total_revenue),
|
||||||
|
avg_margin_percent = VALUES(avg_margin_percent),
|
||||||
|
status = VALUES(status),
|
||||||
|
last_calculated_at = CURRENT_TIMESTAMP
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Calculate vendor time-based metrics
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO vendor_time_metrics (
|
||||||
|
vendor,
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
total_orders,
|
||||||
|
late_orders,
|
||||||
|
avg_lead_time_days,
|
||||||
|
total_purchase_value,
|
||||||
|
total_revenue,
|
||||||
|
avg_margin_percent
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
vd.vendor,
|
||||||
|
YEAR(po.date) as year,
|
||||||
|
MONTH(po.date) as month,
|
||||||
|
COUNT(DISTINCT po.po_id) as total_orders,
|
||||||
|
COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as late_orders,
|
||||||
|
AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days,
|
||||||
|
SUM(po.cost_price * po.ordered) as total_purchase_value,
|
||||||
|
SUM(o.price * o.quantity) as total_revenue,
|
||||||
|
AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent
|
||||||
|
FROM vendor_details vd
|
||||||
|
LEFT JOIN products p ON vd.vendor = p.vendor
|
||||||
|
LEFT JOIN purchase_orders po ON p.product_id = po.product_id
|
||||||
|
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
||||||
|
WHERE po.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH)
|
||||||
|
GROUP BY vd.vendor, YEAR(po.date), MONTH(po.date)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
total_orders = VALUES(total_orders),
|
||||||
|
late_orders = VALUES(late_orders),
|
||||||
|
avg_lead_time_days = VALUES(avg_lead_time_days),
|
||||||
|
total_purchase_value = VALUES(total_purchase_value),
|
||||||
|
total_revenue = VALUES(total_revenue),
|
||||||
|
avg_margin_percent = VALUES(avg_margin_percent)
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function calculateCategoryMetrics(connection, startTime, totalProducts) {
|
||||||
|
outputProgress({
|
||||||
|
status: 'running',
|
||||||
|
operation: 'Calculating category metrics',
|
||||||
|
current: Math.floor(totalProducts * 0.85),
|
||||||
|
total: totalProducts,
|
||||||
|
elapsed: formatElapsedTime(startTime),
|
||||||
|
remaining: estimateRemaining(startTime, Math.floor(totalProducts * 0.85), totalProducts),
|
||||||
|
rate: calculateRate(startTime, Math.floor(totalProducts * 0.85)),
|
||||||
|
percentage: '85'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate category performance metrics
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO category_metrics (
|
||||||
|
category_id,
|
||||||
|
product_count,
|
||||||
|
active_products,
|
||||||
|
total_value,
|
||||||
|
avg_margin,
|
||||||
|
turnover_rate,
|
||||||
|
growth_rate,
|
||||||
|
status
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
c.id as category_id,
|
||||||
|
COUNT(DISTINCT p.product_id) as product_count,
|
||||||
|
COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products,
|
||||||
|
SUM(p.stock_quantity * p.cost_price) as total_value,
|
||||||
|
AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin,
|
||||||
|
-- Turnover rate calculation
|
||||||
|
SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate,
|
||||||
|
-- Growth rate calculation (comparing current month to previous month)
|
||||||
|
((
|
||||||
|
SUM(CASE WHEN o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END) -
|
||||||
|
SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END)
|
||||||
|
) / NULLIF(
|
||||||
|
SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END),
|
||||||
|
0
|
||||||
|
) * 100) as growth_rate,
|
||||||
|
c.status
|
||||||
|
FROM categories c
|
||||||
|
LEFT JOIN product_categories pc ON c.id = pc.category_id
|
||||||
|
LEFT JOIN products p ON pc.product_id = p.product_id
|
||||||
|
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
||||||
|
GROUP BY c.id, c.status
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
product_count = VALUES(product_count),
|
||||||
|
active_products = VALUES(active_products),
|
||||||
|
total_value = VALUES(total_value),
|
||||||
|
avg_margin = VALUES(avg_margin),
|
||||||
|
turnover_rate = VALUES(turnover_rate),
|
||||||
|
growth_rate = VALUES(growth_rate),
|
||||||
|
status = VALUES(status),
|
||||||
|
last_calculated_at = CURRENT_TIMESTAMP
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Calculate category time-based metrics
|
||||||
|
await connection.query(`
|
||||||
|
INSERT INTO category_time_metrics (
|
||||||
|
category_id,
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
product_count,
|
||||||
|
active_products,
|
||||||
|
total_value,
|
||||||
|
total_revenue,
|
||||||
|
avg_margin,
|
||||||
|
turnover_rate
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
c.id as category_id,
|
||||||
|
YEAR(o.date) as year,
|
||||||
|
MONTH(o.date) as month,
|
||||||
|
COUNT(DISTINCT p.product_id) as product_count,
|
||||||
|
COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products,
|
||||||
|
SUM(p.stock_quantity * p.cost_price) as total_value,
|
||||||
|
SUM(o.price * o.quantity) as total_revenue,
|
||||||
|
AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin,
|
||||||
|
SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate
|
||||||
|
FROM categories c
|
||||||
|
LEFT JOIN product_categories pc ON c.id = pc.category_id
|
||||||
|
LEFT JOIN products p ON pc.product_id = p.product_id
|
||||||
|
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
||||||
|
WHERE o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH)
|
||||||
|
GROUP BY c.id, YEAR(o.date), MONTH(o.date)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
product_count = VALUES(product_count),
|
||||||
|
active_products = VALUES(active_products),
|
||||||
|
total_value = VALUES(total_value),
|
||||||
|
total_revenue = VALUES(total_revenue),
|
||||||
|
avg_margin = VALUES(avg_margin),
|
||||||
|
turnover_rate = VALUES(turnover_rate)
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -547,193 +689,6 @@ async function calculateCategorySalesMetrics(connection, startTime, totalProduct
|
|||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add new functions for vendor and category metrics
|
|
||||||
async function calculateVendorMetrics(connection) {
|
|
||||||
console.log('Calculating vendor metrics...');
|
|
||||||
|
|
||||||
// Calculate vendor performance metrics
|
|
||||||
await connection.query(`
|
|
||||||
INSERT INTO vendor_metrics (
|
|
||||||
vendor,
|
|
||||||
avg_lead_time_days,
|
|
||||||
on_time_delivery_rate,
|
|
||||||
order_fill_rate,
|
|
||||||
total_orders,
|
|
||||||
total_late_orders,
|
|
||||||
total_purchase_value,
|
|
||||||
avg_order_value,
|
|
||||||
active_products,
|
|
||||||
total_products,
|
|
||||||
total_revenue,
|
|
||||||
avg_margin_percent,
|
|
||||||
status
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
vd.vendor,
|
|
||||||
-- Lead time metrics
|
|
||||||
AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days,
|
|
||||||
-- On-time delivery rate
|
|
||||||
(COUNT(CASE WHEN po.received_date <= po.expected_date THEN 1 END) / COUNT(*)) * 100 as on_time_delivery_rate,
|
|
||||||
-- Order fill rate
|
|
||||||
(SUM(po.received) / SUM(po.ordered)) * 100 as order_fill_rate,
|
|
||||||
COUNT(DISTINCT po.po_id) as total_orders,
|
|
||||||
COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as total_late_orders,
|
|
||||||
SUM(po.cost_price * po.ordered) as total_purchase_value,
|
|
||||||
AVG(po.cost_price * po.ordered) as avg_order_value,
|
|
||||||
-- Product counts
|
|
||||||
COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products,
|
|
||||||
COUNT(DISTINCT p.product_id) as total_products,
|
|
||||||
-- Financial metrics
|
|
||||||
SUM(o.price * o.quantity) as total_revenue,
|
|
||||||
AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent,
|
|
||||||
vd.status
|
|
||||||
FROM vendor_details vd
|
|
||||||
LEFT JOIN products p ON vd.vendor = p.vendor
|
|
||||||
LEFT JOIN purchase_orders po ON p.product_id = po.product_id
|
|
||||||
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
|
||||||
GROUP BY vd.vendor, vd.status
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
avg_lead_time_days = VALUES(avg_lead_time_days),
|
|
||||||
on_time_delivery_rate = VALUES(on_time_delivery_rate),
|
|
||||||
order_fill_rate = VALUES(order_fill_rate),
|
|
||||||
total_orders = VALUES(total_orders),
|
|
||||||
total_late_orders = VALUES(total_late_orders),
|
|
||||||
total_purchase_value = VALUES(total_purchase_value),
|
|
||||||
avg_order_value = VALUES(avg_order_value),
|
|
||||||
active_products = VALUES(active_products),
|
|
||||||
total_products = VALUES(total_products),
|
|
||||||
total_revenue = VALUES(total_revenue),
|
|
||||||
avg_margin_percent = VALUES(avg_margin_percent),
|
|
||||||
status = VALUES(status),
|
|
||||||
last_calculated_at = CURRENT_TIMESTAMP
|
|
||||||
`);
|
|
||||||
|
|
||||||
// Calculate vendor time-based metrics
|
|
||||||
await connection.query(`
|
|
||||||
INSERT INTO vendor_time_metrics (
|
|
||||||
vendor,
|
|
||||||
year,
|
|
||||||
month,
|
|
||||||
total_orders,
|
|
||||||
late_orders,
|
|
||||||
avg_lead_time_days,
|
|
||||||
total_purchase_value,
|
|
||||||
total_revenue,
|
|
||||||
avg_margin_percent
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
vd.vendor,
|
|
||||||
YEAR(po.date) as year,
|
|
||||||
MONTH(po.date) as month,
|
|
||||||
COUNT(DISTINCT po.po_id) as total_orders,
|
|
||||||
COUNT(DISTINCT CASE WHEN po.received_date > po.expected_date THEN po.po_id END) as late_orders,
|
|
||||||
AVG(DATEDIFF(po.received_date, po.date)) as avg_lead_time_days,
|
|
||||||
SUM(po.cost_price * po.ordered) as total_purchase_value,
|
|
||||||
SUM(o.price * o.quantity) as total_revenue,
|
|
||||||
AVG(((o.price - p.cost_price) / o.price) * 100) as avg_margin_percent
|
|
||||||
FROM vendor_details vd
|
|
||||||
LEFT JOIN products p ON vd.vendor = p.vendor
|
|
||||||
LEFT JOIN purchase_orders po ON p.product_id = po.product_id
|
|
||||||
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
|
||||||
WHERE po.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH)
|
|
||||||
GROUP BY vd.vendor, YEAR(po.date), MONTH(po.date)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
total_orders = VALUES(total_orders),
|
|
||||||
late_orders = VALUES(late_orders),
|
|
||||||
avg_lead_time_days = VALUES(avg_lead_time_days),
|
|
||||||
total_purchase_value = VALUES(total_purchase_value),
|
|
||||||
total_revenue = VALUES(total_revenue),
|
|
||||||
avg_margin_percent = VALUES(avg_margin_percent)
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function calculateCategoryMetrics(connection) {
|
|
||||||
console.log('Calculating category metrics...');
|
|
||||||
|
|
||||||
// Calculate category performance metrics
|
|
||||||
await connection.query(`
|
|
||||||
INSERT INTO category_metrics (
|
|
||||||
category_id,
|
|
||||||
product_count,
|
|
||||||
active_products,
|
|
||||||
total_value,
|
|
||||||
avg_margin,
|
|
||||||
turnover_rate,
|
|
||||||
growth_rate,
|
|
||||||
status
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
c.id as category_id,
|
|
||||||
COUNT(DISTINCT p.product_id) as product_count,
|
|
||||||
COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products,
|
|
||||||
SUM(p.stock_quantity * p.cost_price) as total_value,
|
|
||||||
AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin,
|
|
||||||
-- Turnover rate calculation
|
|
||||||
SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate,
|
|
||||||
-- Growth rate calculation (comparing current month to previous month)
|
|
||||||
((
|
|
||||||
SUM(CASE WHEN o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END) -
|
|
||||||
SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END)
|
|
||||||
) / NULLIF(
|
|
||||||
SUM(CASE WHEN o.date BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 2 MONTH) AND DATE_SUB(CURRENT_DATE, INTERVAL 1 MONTH) THEN o.quantity ELSE 0 END),
|
|
||||||
0
|
|
||||||
) * 100) as growth_rate,
|
|
||||||
c.status
|
|
||||||
FROM categories c
|
|
||||||
LEFT JOIN product_categories pc ON c.id = pc.category_id
|
|
||||||
LEFT JOIN products p ON pc.product_id = p.product_id
|
|
||||||
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
|
||||||
GROUP BY c.id, c.status
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
product_count = VALUES(product_count),
|
|
||||||
active_products = VALUES(active_products),
|
|
||||||
total_value = VALUES(total_value),
|
|
||||||
avg_margin = VALUES(avg_margin),
|
|
||||||
turnover_rate = VALUES(turnover_rate),
|
|
||||||
growth_rate = VALUES(growth_rate),
|
|
||||||
status = VALUES(status),
|
|
||||||
last_calculated_at = CURRENT_TIMESTAMP
|
|
||||||
`);
|
|
||||||
|
|
||||||
// Calculate category time-based metrics
|
|
||||||
await connection.query(`
|
|
||||||
INSERT INTO category_time_metrics (
|
|
||||||
category_id,
|
|
||||||
year,
|
|
||||||
month,
|
|
||||||
product_count,
|
|
||||||
active_products,
|
|
||||||
total_value,
|
|
||||||
total_revenue,
|
|
||||||
avg_margin,
|
|
||||||
turnover_rate
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
c.id as category_id,
|
|
||||||
YEAR(o.date) as year,
|
|
||||||
MONTH(o.date) as month,
|
|
||||||
COUNT(DISTINCT p.product_id) as product_count,
|
|
||||||
COUNT(DISTINCT CASE WHEN p.visible = true THEN p.product_id END) as active_products,
|
|
||||||
SUM(p.stock_quantity * p.cost_price) as total_value,
|
|
||||||
SUM(o.price * o.quantity) as total_revenue,
|
|
||||||
AVG(((p.price - p.cost_price) / p.price) * 100) as avg_margin,
|
|
||||||
SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0) as turnover_rate
|
|
||||||
FROM categories c
|
|
||||||
LEFT JOIN product_categories pc ON c.id = pc.category_id
|
|
||||||
LEFT JOIN products p ON pc.product_id = p.product_id
|
|
||||||
LEFT JOIN orders o ON p.product_id = o.product_id AND o.canceled = false
|
|
||||||
WHERE o.date >= DATE_SUB(CURRENT_DATE, INTERVAL 12 MONTH)
|
|
||||||
GROUP BY c.id, YEAR(o.date), MONTH(o.date)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
product_count = VALUES(product_count),
|
|
||||||
active_products = VALUES(active_products),
|
|
||||||
total_value = VALUES(total_value),
|
|
||||||
total_revenue = VALUES(total_revenue),
|
|
||||||
avg_margin = VALUES(avg_margin),
|
|
||||||
turnover_rate = VALUES(turnover_rate)
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the main calculation function to include the new metrics
|
// Update the main calculation function to include the new metrics
|
||||||
async function calculateMetrics() {
|
async function calculateMetrics() {
|
||||||
let pool;
|
let pool;
|
||||||
@@ -1339,27 +1294,13 @@ async function calculateMetrics() {
|
|||||||
});
|
});
|
||||||
await calculateLeadTimeMetrics(connection, startTime, totalProducts);
|
await calculateLeadTimeMetrics(connection, startTime, totalProducts);
|
||||||
|
|
||||||
// Add category sales metrics calculation
|
// Calculate category metrics
|
||||||
if (isCancelled) {
|
await calculateCategoryMetrics(connection, startTime, totalProducts);
|
||||||
throw new Error('Operation cancelled');
|
|
||||||
}
|
// Calculate category sales metrics
|
||||||
await calculateCategorySalesMetrics(connection, startTime, totalProducts);
|
await calculateCategorySalesMetrics(connection, startTime, totalProducts);
|
||||||
|
|
||||||
// Calculate ABC classification
|
// Calculate ABC classification
|
||||||
if (isCancelled) {
|
|
||||||
throw new Error('Operation cancelled');
|
|
||||||
}
|
|
||||||
outputProgress({
|
|
||||||
status: 'running',
|
|
||||||
operation: 'Starting ABC classification',
|
|
||||||
current: Math.floor(totalProducts * 0.7),
|
|
||||||
total: totalProducts,
|
|
||||||
elapsed: formatElapsedTime(startTime),
|
|
||||||
remaining: estimateRemaining(startTime, Math.floor(totalProducts * 0.7), totalProducts),
|
|
||||||
rate: calculateRate(startTime, Math.floor(totalProducts * 0.7)),
|
|
||||||
percentage: '70'
|
|
||||||
});
|
|
||||||
|
|
||||||
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 };
|
||||||
|
|
||||||
@@ -1388,9 +1329,6 @@ async function calculateMetrics() {
|
|||||||
`, [abcThresholds.a_threshold, abcThresholds.b_threshold]);
|
`, [abcThresholds.a_threshold, abcThresholds.b_threshold]);
|
||||||
|
|
||||||
// Calculate time-based aggregates
|
// Calculate time-based aggregates
|
||||||
if (isCancelled) {
|
|
||||||
throw new Error('Operation cancelled');
|
|
||||||
}
|
|
||||||
outputProgress({
|
outputProgress({
|
||||||
status: 'running',
|
status: 'running',
|
||||||
operation: 'Starting time-based aggregates calculation',
|
operation: 'Starting time-based aggregates calculation',
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ const METRICS_TABLES = [
|
|||||||
'product_metrics',
|
'product_metrics',
|
||||||
'product_time_aggregates',
|
'product_time_aggregates',
|
||||||
'vendor_metrics',
|
'vendor_metrics',
|
||||||
|
'vendor_time_metrics',
|
||||||
|
'category_metrics',
|
||||||
|
'category_time_metrics',
|
||||||
'category_sales_metrics'
|
'category_sales_metrics'
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -48,19 +51,17 @@ async function resetMetrics() {
|
|||||||
connection = await mysql.createConnection(dbConfig);
|
connection = await mysql.createConnection(dbConfig);
|
||||||
await connection.beginTransaction();
|
await connection.beginTransaction();
|
||||||
|
|
||||||
// Reset existing metrics tables
|
// Reset all metrics tables
|
||||||
await connection.query('TRUNCATE TABLE temp_sales_metrics');
|
for (const table of METRICS_TABLES) {
|
||||||
await connection.query('TRUNCATE TABLE temp_purchase_metrics');
|
console.log(`Truncating table: ${table}`);
|
||||||
await connection.query('TRUNCATE TABLE product_metrics');
|
try {
|
||||||
await connection.query('TRUNCATE TABLE product_time_aggregates');
|
await connection.query(`TRUNCATE TABLE ${table}`);
|
||||||
|
console.log(`Successfully truncated: ${table}`);
|
||||||
// Reset vendor metrics tables
|
} catch (err) {
|
||||||
await connection.query('TRUNCATE TABLE vendor_metrics');
|
console.error(`Error truncating ${table}:`, err.message);
|
||||||
await connection.query('TRUNCATE TABLE vendor_time_metrics');
|
throw err;
|
||||||
|
}
|
||||||
// Reset category metrics tables
|
}
|
||||||
await connection.query('TRUNCATE TABLE category_metrics');
|
|
||||||
await connection.query('TRUNCATE TABLE category_time_metrics');
|
|
||||||
|
|
||||||
await connection.commit();
|
await connection.commit();
|
||||||
console.log('All metrics tables reset successfully');
|
console.log('All metrics tables reset successfully');
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ router.get('/', async (req, res) => {
|
|||||||
const params = [];
|
const params = [];
|
||||||
|
|
||||||
if (search) {
|
if (search) {
|
||||||
whereConditions.push('(p.vendor LIKE ? OR vm.contact_name LIKE ?)');
|
whereConditions.push('(p.vendor LIKE ? OR vd.contact_name LIKE ?)');
|
||||||
params.push(`%${search}%`, `%${search}%`);
|
params.push(`%${search}%`, `%${search}%`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,9 +61,9 @@ router.get('/', async (req, res) => {
|
|||||||
const [vendors] = await pool.query(`
|
const [vendors] = await pool.query(`
|
||||||
SELECT
|
SELECT
|
||||||
p.vendor as name,
|
p.vendor as name,
|
||||||
vm.contact_name,
|
vd.contact_name,
|
||||||
vm.email,
|
vd.email,
|
||||||
vm.phone,
|
vd.phone,
|
||||||
vm.status,
|
vm.status,
|
||||||
vm.avg_lead_time_days,
|
vm.avg_lead_time_days,
|
||||||
vm.on_time_delivery_rate,
|
vm.on_time_delivery_rate,
|
||||||
@@ -72,6 +72,7 @@ router.get('/', async (req, res) => {
|
|||||||
COUNT(DISTINCT p.product_id) as active_products
|
COUNT(DISTINCT p.product_id) as active_products
|
||||||
FROM products p
|
FROM products p
|
||||||
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
|
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
|
||||||
|
LEFT JOIN vendor_details vd ON p.vendor = vd.vendor
|
||||||
${whereClause}
|
${whereClause}
|
||||||
GROUP BY p.vendor
|
GROUP BY p.vendor
|
||||||
ORDER BY ${sortColumn} ${sortDirection}
|
ORDER BY ${sortColumn} ${sortDirection}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/dashboard/inventoryhealthsummary.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/keymetricscharts.tsx","./src/components/dashboard/lowstockalerts.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/dashboard/vendorperformance.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/productdetail.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/products/productviews.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/configuration.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/command.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/dashboard.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx","./src/types/products.ts"],"version":"5.6.3"}
|
{"root":["./src/app.tsx","./src/config.ts","./src/main.tsx","./src/vite-env.d.ts","./src/components/analytics/categoryperformance.tsx","./src/components/analytics/priceanalysis.tsx","./src/components/analytics/profitanalysis.tsx","./src/components/analytics/stockanalysis.tsx","./src/components/analytics/vendorperformance.tsx","./src/components/auth/requireauth.tsx","./src/components/dashboard/inventoryhealthsummary.tsx","./src/components/dashboard/inventorystats.tsx","./src/components/dashboard/keymetricscharts.tsx","./src/components/dashboard/lowstockalerts.tsx","./src/components/dashboard/overview.tsx","./src/components/dashboard/recentsales.tsx","./src/components/dashboard/salesbycategory.tsx","./src/components/dashboard/trendingproducts.tsx","./src/components/dashboard/vendorperformance.tsx","./src/components/forecasting/columns.tsx","./src/components/layout/appsidebar.tsx","./src/components/layout/mainlayout.tsx","./src/components/products/productdetail.tsx","./src/components/products/productfilters.tsx","./src/components/products/producttable.tsx","./src/components/products/producttableskeleton.tsx","./src/components/products/productviews.tsx","./src/components/settings/calculationsettings.tsx","./src/components/settings/configuration.tsx","./src/components/settings/datamanagement.tsx","./src/components/settings/performancemetrics.tsx","./src/components/settings/stockmanagement.tsx","./src/components/ui/accordion.tsx","./src/components/ui/alert-dialog.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/command.tsx","./src/components/ui/date-range-picker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/drawer.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/progress.tsx","./src/components/ui/scroll-area.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/sheet.tsx","./src/components/ui/sidebar.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/switch.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/components/ui/toggle-group.tsx","./src/components/ui/toggle.tsx","./src/components/ui/tooltip.tsx","./src/hooks/use-mobile.tsx","./src/lib/utils.ts","./src/pages/analytics.tsx","./src/pages/categories.tsx","./src/pages/dashboard.tsx","./src/pages/forecasting.tsx","./src/pages/login.tsx","./src/pages/orders.tsx","./src/pages/products.tsx","./src/pages/purchaseorders.tsx","./src/pages/settings.tsx","./src/pages/vendors.tsx","./src/routes/forecasting.tsx","./src/types/products.ts"],"errors":true,"version":"5.6.3"}
|
||||||
Reference in New Issue
Block a user