Update frontend to match part 4 - dashboard

This commit is contained in:
2025-01-28 13:57:44 -05:00
parent 25a0bc8d4c
commit 7e341a152c
2 changed files with 99 additions and 26 deletions

View File

@@ -3,7 +3,7 @@ const router = express.Router();
const db = require('../utils/db'); const db = require('../utils/db');
// Import status codes // Import status codes
const { RECEIVING_STATUS } = require('../types/status-codes'); const { ReceivingStatus } = require('../types/status-codes');
// Helper function to execute queries using the connection pool // Helper function to execute queries using the connection pool
async function executeQuery(sql, params = []) { async function executeQuery(sql, params = []) {
@@ -104,26 +104,26 @@ router.get('/purchase/metrics', async (req, res) => {
const [rows] = await executeQuery(` const [rows] = await executeQuery(`
SELECT SELECT
COALESCE(COUNT(DISTINCT CASE COALESCE(COUNT(DISTINCT CASE
WHEN po.receiving_status < ${RECEIVING_STATUS.PARTIAL_RECEIVED} WHEN po.receiving_status < ${ReceivingStatus.PartialReceived}
THEN po.po_id THEN po.po_id
END), 0) as active_pos, END), 0) as active_pos,
COALESCE(COUNT(DISTINCT CASE COALESCE(COUNT(DISTINCT CASE
WHEN po.receiving_status < ${RECEIVING_STATUS.PARTIAL_RECEIVED} WHEN po.receiving_status < ${ReceivingStatus.PartialReceived}
AND po.expected_date < CURDATE() AND po.expected_date < CURDATE()
THEN po.po_id THEN po.po_id
END), 0) as overdue_pos, END), 0) as overdue_pos,
COALESCE(SUM(CASE COALESCE(SUM(CASE
WHEN po.receiving_status < ${RECEIVING_STATUS.PARTIAL_RECEIVED} WHEN po.receiving_status < ${ReceivingStatus.PartialReceived}
THEN po.ordered THEN po.ordered
ELSE 0 ELSE 0
END), 0) as total_units, END), 0) as total_units,
CAST(COALESCE(SUM(CASE CAST(COALESCE(SUM(CASE
WHEN po.receiving_status < ${RECEIVING_STATUS.PARTIAL_RECEIVED} WHEN po.receiving_status < ${ReceivingStatus.PartialReceived}
THEN po.ordered * po.cost_price THEN po.ordered * po.cost_price
ELSE 0 ELSE 0
END), 0) AS DECIMAL(15,3)) as total_cost, END), 0) AS DECIMAL(15,3)) as total_cost,
CAST(COALESCE(SUM(CASE CAST(COALESCE(SUM(CASE
WHEN po.receiving_status < ${RECEIVING_STATUS.PARTIAL_RECEIVED} WHEN po.receiving_status < ${ReceivingStatus.PartialReceived}
THEN po.ordered * p.price THEN po.ordered * p.price
ELSE 0 ELSE 0
END), 0) AS DECIMAL(15,3)) as total_retail END), 0) AS DECIMAL(15,3)) as total_retail
@@ -132,28 +132,22 @@ router.get('/purchase/metrics', async (req, res) => {
`); `);
const poMetrics = rows[0]; const poMetrics = rows[0];
console.log('Raw poMetrics from database:', poMetrics);
console.log('poMetrics.active_pos:', poMetrics.active_pos);
console.log('poMetrics.overdue_pos:', poMetrics.overdue_pos);
console.log('poMetrics.total_units:', poMetrics.total_units);
console.log('poMetrics.total_cost:', poMetrics.total_cost);
console.log('poMetrics.total_retail:', poMetrics.total_retail);
const [vendorOrders] = await executeQuery(` const [vendorOrders] = await executeQuery(`
SELECT SELECT
po.vendor, po.vendor,
COUNT(DISTINCT po.po_id) as order_count, COUNT(DISTINCT po.po_id) as orders,
COALESCE(SUM(po.ordered), 0) as ordered_units, COALESCE(SUM(po.ordered), 0) as units,
CAST(COALESCE(SUM(po.ordered * po.cost_price), 0) AS DECIMAL(15,3)) as order_cost, CAST(COALESCE(SUM(po.ordered * po.cost_price), 0) AS DECIMAL(15,3)) as cost,
CAST(COALESCE(SUM(po.ordered * p.price), 0) AS DECIMAL(15,3)) as order_retail CAST(COALESCE(SUM(po.ordered * p.price), 0) AS DECIMAL(15,3)) as retail
FROM purchase_orders po FROM purchase_orders po
JOIN products p ON po.pid = p.pid JOIN products p ON po.pid = p.pid
WHERE po.receiving_status < ${RECEIVING_STATUS.PARTIAL_RECEIVED} WHERE po.receiving_status < ${ReceivingStatus.PartialReceived}
GROUP BY po.vendor GROUP BY po.vendor
HAVING order_cost > 0 HAVING cost > 0
ORDER BY order_cost DESC ORDER BY cost DESC
`); `);
// Format response to match PurchaseMetricsData interface
const response = { const response = {
activePurchaseOrders: parseInt(poMetrics.active_pos) || 0, activePurchaseOrders: parseInt(poMetrics.active_pos) || 0,
overduePurchaseOrders: parseInt(poMetrics.overdue_pos) || 0, overduePurchaseOrders: parseInt(poMetrics.overdue_pos) || 0,
@@ -162,10 +156,10 @@ router.get('/purchase/metrics', async (req, res) => {
onOrderRetail: parseFloat(poMetrics.total_retail) || 0, onOrderRetail: parseFloat(poMetrics.total_retail) || 0,
vendorOrders: vendorOrders.map(v => ({ vendorOrders: vendorOrders.map(v => ({
vendor: v.vendor, vendor: v.vendor,
orders: parseInt(v.order_count) || 0, orders: parseInt(v.orders) || 0,
units: parseInt(v.ordered_units) || 0, units: parseInt(v.units) || 0,
cost: parseFloat(v.order_cost) || 0, cost: parseFloat(v.cost) || 0,
retail: parseFloat(v.order_retail) || 0 retail: parseFloat(v.retail) || 0
})) }))
}; };
@@ -297,7 +291,7 @@ router.get('/forecast/metrics', async (req, res) => {
COALESCE(SUM(cf.forecast_revenue), 0) as revenue, COALESCE(SUM(cf.forecast_revenue), 0) as revenue,
COALESCE(AVG(cf.confidence_level), 0) as confidence COALESCE(AVG(cf.confidence_level), 0) as confidence
FROM category_forecasts cf FROM category_forecasts cf
JOIN categories c ON cf.cat_id = c.cat_id JOIN categories c ON cf.category_id = c.cat_id
WHERE cf.forecast_date BETWEEN ? AND ? WHERE cf.forecast_date BETWEEN ? AND ?
GROUP BY c.cat_id, c.name GROUP BY c.cat_id, c.name
ORDER BY revenue DESC ORDER BY revenue DESC
@@ -785,7 +779,7 @@ router.get('/trending/products', async (req, res) => {
HAVING velocity_change > 0 HAVING velocity_change > 0
ORDER BY velocity_change DESC ORDER BY velocity_change DESC
LIMIT ? LIMIT ?
`, [days, days, days, limit]); `, [days, days, limit]);
res.json(rows); res.json(rows);
} catch (err) { } catch (err) {
console.error('Error fetching trending products:', err); console.error('Error fetching trending products:', err);

View File

@@ -0,0 +1,79 @@
// Purchase Order Status Codes
const PurchaseOrderStatus = {
Canceled: 0,
Created: 1,
ElectronicallyReadySend: 10,
Ordered: 11,
Preordered: 12,
ElectronicallySent: 13,
ReceivingStarted: 15,
Done: 50
};
// Receiving Status Codes
const ReceivingStatus = {
Canceled: 0,
Created: 1,
PartialReceived: 30,
FullReceived: 40,
Paid: 50
};
// Status Code Display Names
const PurchaseOrderStatusLabels = {
[PurchaseOrderStatus.Canceled]: 'Canceled',
[PurchaseOrderStatus.Created]: 'Created',
[PurchaseOrderStatus.ElectronicallyReadySend]: 'Ready to Send',
[PurchaseOrderStatus.Ordered]: 'Ordered',
[PurchaseOrderStatus.Preordered]: 'Preordered',
[PurchaseOrderStatus.ElectronicallySent]: 'Sent',
[PurchaseOrderStatus.ReceivingStarted]: 'Receiving Started',
[PurchaseOrderStatus.Done]: 'Done'
};
const ReceivingStatusLabels = {
[ReceivingStatus.Canceled]: 'Canceled',
[ReceivingStatus.Created]: 'Created',
[ReceivingStatus.PartialReceived]: 'Partially Received',
[ReceivingStatus.FullReceived]: 'Fully Received',
[ReceivingStatus.Paid]: 'Paid'
};
// Helper functions
function getPurchaseOrderStatusLabel(status) {
return PurchaseOrderStatusLabels[status] || 'Unknown';
}
function getReceivingStatusLabel(status) {
return ReceivingStatusLabels[status] || 'Unknown';
}
// Status checks
function isReceivingComplete(status) {
return status >= ReceivingStatus.PartialReceived;
}
function isPurchaseOrderComplete(status) {
return status === PurchaseOrderStatus.Done;
}
function isPurchaseOrderCanceled(status) {
return status === PurchaseOrderStatus.Canceled;
}
function isReceivingCanceled(status) {
return status === ReceivingStatus.Canceled;
}
module.exports = {
PurchaseOrderStatus,
ReceivingStatus,
PurchaseOrderStatusLabels,
ReceivingStatusLabels,
getPurchaseOrderStatusLabel,
getReceivingStatusLabel,
isReceivingComplete,
isPurchaseOrderComplete,
isPurchaseOrderCanceled,
isReceivingCanceled
};