108 lines
4.1 KiB
JavaScript
108 lines
4.1 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
|
|
// Get vendors with pagination, filtering, and sorting
|
|
router.get('/', async (req, res) => {
|
|
const pool = req.app.locals.pool;
|
|
try {
|
|
// Get all vendors with metrics
|
|
const [vendors] = await pool.query(`
|
|
SELECT DISTINCT
|
|
p.vendor as name,
|
|
COALESCE(vm.active_products, 0) as active_products,
|
|
COALESCE(vm.total_orders, 0) as total_orders,
|
|
COALESCE(vm.avg_lead_time_days, 0) as avg_lead_time_days,
|
|
COALESCE(vm.on_time_delivery_rate, 0) as on_time_delivery_rate,
|
|
COALESCE(vm.order_fill_rate, 0) as order_fill_rate,
|
|
CASE
|
|
WHEN COALESCE(vm.total_orders, 0) > 0 AND COALESCE(vm.order_fill_rate, 0) >= 75 THEN 'active'
|
|
WHEN COALESCE(vm.total_orders, 0) > 0 THEN 'inactive'
|
|
ELSE 'pending'
|
|
END as status
|
|
FROM products p
|
|
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
|
|
WHERE p.vendor IS NOT NULL AND p.vendor != ''
|
|
`);
|
|
|
|
// Get cost metrics for all vendors
|
|
const vendorNames = vendors.map(v => v.name);
|
|
const [costMetrics] = await pool.query(`
|
|
SELECT
|
|
vendor,
|
|
CAST(ROUND(SUM(ordered * cost_price) / NULLIF(SUM(ordered), 0), 2) AS DECIMAL(15,3)) as avg_unit_cost,
|
|
CAST(SUM(ordered * cost_price) AS DECIMAL(15,3)) as total_spend
|
|
FROM purchase_orders
|
|
WHERE status = 'closed'
|
|
AND cost_price IS NOT NULL
|
|
AND ordered > 0
|
|
AND vendor IN (?)
|
|
GROUP BY vendor
|
|
`, [vendorNames]);
|
|
|
|
// Create a map of cost metrics by vendor
|
|
const costMetricsMap = costMetrics.reduce((acc, curr) => {
|
|
acc[curr.vendor] = {
|
|
avg_unit_cost: curr.avg_unit_cost,
|
|
total_spend: curr.total_spend
|
|
};
|
|
return acc;
|
|
}, {});
|
|
|
|
// Get overall stats
|
|
const [stats] = await pool.query(`
|
|
SELECT
|
|
COUNT(DISTINCT p.vendor) as totalVendors,
|
|
COUNT(DISTINCT CASE
|
|
WHEN COALESCE(vm.total_orders, 0) > 0 AND COALESCE(vm.order_fill_rate, 0) >= 75
|
|
THEN p.vendor
|
|
END) as activeVendors,
|
|
COALESCE(ROUND(AVG(NULLIF(vm.avg_lead_time_days, 0)), 1), 0) as avgLeadTime,
|
|
COALESCE(ROUND(AVG(NULLIF(vm.order_fill_rate, 0)), 1), 0) as avgFillRate,
|
|
COALESCE(ROUND(AVG(NULLIF(vm.on_time_delivery_rate, 0)), 1), 0) as avgOnTimeDelivery
|
|
FROM products p
|
|
LEFT JOIN vendor_metrics vm ON p.vendor = vm.vendor
|
|
WHERE p.vendor IS NOT NULL AND p.vendor != ''
|
|
`);
|
|
|
|
// Get overall cost metrics
|
|
const [overallCostMetrics] = await pool.query(`
|
|
SELECT
|
|
CAST(ROUND(SUM(ordered * cost_price) / NULLIF(SUM(ordered), 0), 2) AS DECIMAL(15,3)) as avg_unit_cost,
|
|
CAST(SUM(ordered * cost_price) AS DECIMAL(15,3)) as total_spend
|
|
FROM purchase_orders
|
|
WHERE status = 'closed'
|
|
AND cost_price IS NOT NULL
|
|
AND ordered > 0
|
|
AND vendor IS NOT NULL AND vendor != ''
|
|
`);
|
|
|
|
res.json({
|
|
vendors: vendors.map(vendor => ({
|
|
vendor_id: vendor.name,
|
|
name: vendor.name,
|
|
status: vendor.status,
|
|
avg_lead_time_days: parseFloat(vendor.avg_lead_time_days),
|
|
on_time_delivery_rate: parseFloat(vendor.on_time_delivery_rate),
|
|
order_fill_rate: parseFloat(vendor.order_fill_rate),
|
|
total_orders: parseInt(vendor.total_orders),
|
|
active_products: parseInt(vendor.active_products),
|
|
avg_unit_cost: parseFloat(costMetricsMap[vendor.name]?.avg_unit_cost || 0),
|
|
total_spend: parseFloat(costMetricsMap[vendor.name]?.total_spend || 0)
|
|
})),
|
|
stats: {
|
|
totalVendors: parseInt(stats[0].totalVendors),
|
|
activeVendors: parseInt(stats[0].activeVendors),
|
|
avgLeadTime: parseFloat(stats[0].avgLeadTime),
|
|
avgFillRate: parseFloat(stats[0].avgFillRate),
|
|
avgOnTimeDelivery: parseFloat(stats[0].avgOnTimeDelivery),
|
|
avgUnitCost: parseFloat(overallCostMetrics[0].avg_unit_cost),
|
|
totalSpend: parseFloat(overallCostMetrics[0].total_spend)
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching vendors:', error);
|
|
res.status(500).json({ error: 'Failed to fetch vendors' });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|