Update frontend to match part 4 - dashboard
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
79
inventory-server/src/types/status-codes.js
Normal file
79
inventory-server/src/types/status-codes.js
Normal 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
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user