Update import tables schema with minor changes, add new metrics schema

This commit is contained in:
2025-03-29 16:46:31 -04:00
parent 0796518e26
commit f4854423ab
8 changed files with 380 additions and 208 deletions

View File

@@ -102,35 +102,40 @@ router.get('/stock/metrics', async (req, res) => {
// Returns purchase order metrics by vendor
router.get('/purchase/metrics', async (req, res) => {
try {
// First check if there are any purchase orders in the database
const { rows: [poCount] } = await executeQuery(`
SELECT COUNT(*) as count FROM purchase_orders
`);
const { rows: [poMetrics] } = await executeQuery(`
SELECT
COALESCE(COUNT(DISTINCT CASE
WHEN po.receiving_status < $1
WHEN po.receiving_status NOT IN ('partial_received', 'full_received', 'paid')
THEN po.po_id
END), 0)::integer as active_pos,
COALESCE(COUNT(DISTINCT CASE
WHEN po.receiving_status < $1
WHEN po.receiving_status NOT IN ('partial_received', 'full_received', 'paid')
AND po.expected_date < CURRENT_DATE
THEN po.po_id
END), 0)::integer as overdue_pos,
COALESCE(SUM(CASE
WHEN po.receiving_status < $1
WHEN po.receiving_status NOT IN ('partial_received', 'full_received', 'paid')
THEN po.ordered
ELSE 0
END), 0)::integer as total_units,
ROUND(COALESCE(SUM(CASE
WHEN po.receiving_status < $1
WHEN po.receiving_status NOT IN ('partial_received', 'full_received', 'paid')
THEN po.ordered * po.cost_price
ELSE 0
END), 0)::numeric, 3) as total_cost,
ROUND(COALESCE(SUM(CASE
WHEN po.receiving_status < $1
WHEN po.receiving_status NOT IN ('partial_received', 'full_received', 'paid')
THEN po.ordered * p.price
ELSE 0
END), 0)::numeric, 3) as total_retail
FROM purchase_orders po
JOIN products p ON po.pid = p.pid
`, [ReceivingStatus.PartialReceived]);
`);
const { rows: vendorOrders } = await executeQuery(`
SELECT
@@ -141,15 +146,15 @@ router.get('/purchase/metrics', async (req, res) => {
ROUND(COALESCE(SUM(po.ordered * p.price), 0)::numeric, 3) as retail
FROM purchase_orders po
JOIN products p ON po.pid = p.pid
WHERE po.receiving_status < $1
WHERE po.receiving_status NOT IN ('partial_received', 'full_received', 'paid')
GROUP BY po.vendor
HAVING ROUND(COALESCE(SUM(po.ordered * po.cost_price), 0)::numeric, 3) > 0
ORDER BY cost DESC
`, [ReceivingStatus.PartialReceived]);
`);
// If no data or missing metrics, provide dummy data
if (!poMetrics || vendorOrders.length === 0) {
console.log('No purchase metrics found, returning dummy data');
// If no purchase orders exist at all in the database, return dummy data
if (parseInt(poCount.count) === 0) {
console.log('No purchase orders found in database, returning dummy data');
return res.json({
activePurchaseOrders: 12,
@@ -164,6 +169,20 @@ router.get('/purchase/metrics', async (req, res) => {
]
});
}
// If no active purchase orders match the criteria, return zeros instead of dummy data
if (vendorOrders.length === 0) {
console.log('No active purchase orders matching criteria, returning zeros');
return res.json({
activePurchaseOrders: parseInt(poMetrics.active_pos) || 0,
overduePurchaseOrders: parseInt(poMetrics.overdue_pos) || 0,
onOrderUnits: parseInt(poMetrics.total_units) || 0,
onOrderCost: parseFloat(poMetrics.total_cost) || 0,
onOrderRetail: parseFloat(poMetrics.total_retail) || 0,
vendorOrders: []
});
}
// Format response to match PurchaseMetricsData interface
const response = {
@@ -184,19 +203,15 @@ router.get('/purchase/metrics', async (req, res) => {
res.json(response);
} catch (err) {
console.error('Error fetching purchase metrics:', err);
// Return dummy data on error
res.json({
activePurchaseOrders: 12,
overduePurchaseOrders: 3,
onOrderUnits: 1250,
onOrderCost: 12500,
onOrderRetail: 25000,
vendorOrders: [
{ vendor: "Test Vendor 1", orders: 5, units: 500, cost: 5000, retail: 10000 },
{ vendor: "Test Vendor 2", orders: 4, units: 400, cost: 4000, retail: 8000 },
{ vendor: "Test Vendor 3", orders: 3, units: 350, cost: 3500, retail: 7000 }
]
res.status(500).json({
error: 'Failed to fetch purchase metrics',
details: err.message,
activePurchaseOrders: 0,
overduePurchaseOrders: 0,
onOrderUnits: 0,
onOrderCost: 0,
onOrderRetail: 0,
vendorOrders: []
});
}
});
@@ -1018,17 +1033,17 @@ router.get('/vendor/performance', async (req, res) => {
THEN EXTRACT(EPOCH FROM (po.received_date - po.date))/86400
ELSE NULL END)::numeric, 2), 0) as avg_lead_time,
COALESCE(ROUND(SUM(CASE
WHEN po.status = 'completed' AND po.received_date <= po.expected_date
WHEN po.status = 'done' AND po.received_date <= po.expected_date
THEN 1
ELSE 0
END)::numeric * 100.0 / NULLIF(COUNT(*)::numeric, 0), 2), 0) as on_time_delivery_rate,
COALESCE(ROUND(AVG(CASE
WHEN po.status = 'completed'
WHEN po.status = 'done'
THEN po.received::numeric / NULLIF(po.ordered::numeric, 0) * 100
ELSE NULL
END)::numeric, 2), 0) as avg_fill_rate,
COUNT(CASE WHEN po.status = 'open' THEN 1 END)::integer as active_orders,
COUNT(CASE WHEN po.status = 'open' AND po.expected_date < CURRENT_DATE THEN 1 END)::integer as overdue_orders
COUNT(CASE WHEN po.status IN ('created', 'electronically_ready_send', 'ordered', 'preordered', 'electronically_sent', 'receiving_started') THEN 1 END)::integer as active_orders,
COUNT(CASE WHEN po.status IN ('created', 'electronically_ready_send', 'ordered', 'preordered', 'electronically_sent', 'receiving_started') AND po.expected_date < CURRENT_DATE THEN 1 END)::integer as overdue_orders
FROM purchase_orders po
WHERE po.date >= CURRENT_DATE - INTERVAL '180 days'
GROUP BY po.vendor
@@ -1165,7 +1180,7 @@ router.get('/key-metrics', async (req, res) => {
SELECT
COUNT(DISTINCT po_id) as total_pos,
SUM(ordered * cost_price) as total_po_value,
COUNT(CASE WHEN status = 'open' THEN 1 END) as open_pos
COUNT(CASE WHEN status IN ('created', 'electronically_ready_send', 'ordered', 'preordered', 'electronically_sent', 'receiving_started') THEN 1 END) as open_pos
FROM purchase_orders
WHERE order_date >= CURRENT_DATE - INTERVAL '${days} days'
)