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 { rows: 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 { rows: costMetrics } = await pool.query(` SELECT vendor, ROUND((SUM(ordered * cost_price)::numeric / NULLIF(SUM(ordered), 0)), 2) as avg_unit_cost, ROUND(SUM(ordered * cost_price)::numeric, 3) as total_spend FROM purchase_orders WHERE status = 'closed' AND cost_price IS NOT NULL AND ordered > 0 AND vendor = ANY($1) 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 { rows: [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))::numeric, 1), 0) as avgLeadTime, COALESCE(ROUND(AVG(NULLIF(vm.order_fill_rate, 0))::numeric, 1), 0) as avgFillRate, COALESCE(ROUND(AVG(NULLIF(vm.on_time_delivery_rate, 0))::numeric, 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 { rows: [overallCostMetrics] } = await pool.query(` SELECT ROUND((SUM(ordered * cost_price)::numeric / NULLIF(SUM(ordered), 0)), 2) as avg_unit_cost, ROUND(SUM(ordered * cost_price)::numeric, 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.totalvendors), activeVendors: parseInt(stats.activevendors), avgLeadTime: parseFloat(stats.avgleadtime), avgFillRate: parseFloat(stats.avgfillrate), avgOnTimeDelivery: parseFloat(stats.avgontimedelivery), avgUnitCost: parseFloat(overallCostMetrics.avg_unit_cost), totalSpend: parseFloat(overallCostMetrics.total_spend) } }); } catch (error) { console.error('Error fetching vendors:', error); res.status(500).json({ error: 'Failed to fetch vendors' }); } }); module.exports = router;