Update backend/frontend
This commit is contained in:
@@ -29,40 +29,46 @@ router.get('/', async (req, res) => {
|
||||
|
||||
let whereClause = '1=1';
|
||||
const params = [];
|
||||
let paramCounter = 1;
|
||||
|
||||
if (search) {
|
||||
whereClause += ' AND (po.po_id LIKE ? OR po.vendor LIKE ?)';
|
||||
params.push(`%${search}%`, `%${search}%`);
|
||||
whereClause += ` AND (po.po_id ILIKE $${paramCounter} OR po.vendor ILIKE $${paramCounter})`;
|
||||
params.push(`%${search}%`);
|
||||
paramCounter++;
|
||||
}
|
||||
|
||||
if (status && status !== 'all') {
|
||||
whereClause += ' AND po.status = ?';
|
||||
whereClause += ` AND po.status = $${paramCounter}`;
|
||||
params.push(Number(status));
|
||||
paramCounter++;
|
||||
}
|
||||
|
||||
if (vendor && vendor !== 'all') {
|
||||
whereClause += ' AND po.vendor = ?';
|
||||
whereClause += ` AND po.vendor = $${paramCounter}`;
|
||||
params.push(vendor);
|
||||
paramCounter++;
|
||||
}
|
||||
|
||||
if (startDate) {
|
||||
whereClause += ' AND po.date >= ?';
|
||||
whereClause += ` AND po.date >= $${paramCounter}`;
|
||||
params.push(startDate);
|
||||
paramCounter++;
|
||||
}
|
||||
|
||||
if (endDate) {
|
||||
whereClause += ' AND po.date <= ?';
|
||||
whereClause += ` AND po.date <= $${paramCounter}`;
|
||||
params.push(endDate);
|
||||
paramCounter++;
|
||||
}
|
||||
|
||||
// Get filtered summary metrics
|
||||
const [summary] = await pool.query(`
|
||||
const { rows: [summary] } = await pool.query(`
|
||||
WITH po_totals AS (
|
||||
SELECT
|
||||
po_id,
|
||||
SUM(ordered) as total_ordered,
|
||||
SUM(received) as total_received,
|
||||
CAST(SUM(ordered * cost_price) AS DECIMAL(15,3)) as total_cost
|
||||
ROUND(SUM(ordered * cost_price)::numeric, 3) as total_cost
|
||||
FROM purchase_orders po
|
||||
WHERE ${whereClause}
|
||||
GROUP BY po_id
|
||||
@@ -72,26 +78,26 @@ router.get('/', async (req, res) => {
|
||||
SUM(total_ordered) as total_ordered,
|
||||
SUM(total_received) as total_received,
|
||||
ROUND(
|
||||
SUM(total_received) / NULLIF(SUM(total_ordered), 0), 3
|
||||
(SUM(total_received)::numeric / NULLIF(SUM(total_ordered), 0)), 3
|
||||
) as fulfillment_rate,
|
||||
CAST(SUM(total_cost) AS DECIMAL(15,3)) as total_value,
|
||||
CAST(AVG(total_cost) AS DECIMAL(15,3)) as avg_cost
|
||||
ROUND(SUM(total_cost)::numeric, 3) as total_value,
|
||||
ROUND(AVG(total_cost)::numeric, 3) as avg_cost
|
||||
FROM po_totals
|
||||
`, params);
|
||||
|
||||
// Get total count for pagination
|
||||
const [countResult] = await pool.query(`
|
||||
const { rows: [countResult] } = await pool.query(`
|
||||
SELECT COUNT(DISTINCT po_id) as total
|
||||
FROM purchase_orders po
|
||||
WHERE ${whereClause}
|
||||
`, params);
|
||||
|
||||
const total = countResult[0].total;
|
||||
const total = countResult.total;
|
||||
const offset = (page - 1) * limit;
|
||||
const pages = Math.ceil(total / limit);
|
||||
|
||||
// Get recent purchase orders
|
||||
const [orders] = await pool.query(`
|
||||
const { rows: orders } = await pool.query(`
|
||||
WITH po_totals AS (
|
||||
SELECT
|
||||
po_id,
|
||||
@@ -101,10 +107,10 @@ router.get('/', async (req, res) => {
|
||||
receiving_status,
|
||||
COUNT(DISTINCT pid) as total_items,
|
||||
SUM(ordered) as total_quantity,
|
||||
CAST(SUM(ordered * cost_price) AS DECIMAL(15,3)) as total_cost,
|
||||
ROUND(SUM(ordered * cost_price)::numeric, 3) as total_cost,
|
||||
SUM(received) as total_received,
|
||||
ROUND(
|
||||
SUM(received) / NULLIF(SUM(ordered), 0), 3
|
||||
(SUM(received)::numeric / NULLIF(SUM(ordered), 0)), 3
|
||||
) as fulfillment_rate
|
||||
FROM purchase_orders po
|
||||
WHERE ${whereClause}
|
||||
@@ -113,7 +119,7 @@ router.get('/', async (req, res) => {
|
||||
SELECT
|
||||
po_id as id,
|
||||
vendor as vendor_name,
|
||||
DATE_FORMAT(date, '%Y-%m-%d') as order_date,
|
||||
to_char(date, 'YYYY-MM-DD') as order_date,
|
||||
status,
|
||||
receiving_status,
|
||||
total_items,
|
||||
@@ -124,21 +130,21 @@ router.get('/', async (req, res) => {
|
||||
FROM po_totals
|
||||
ORDER BY
|
||||
CASE
|
||||
WHEN ? = 'order_date' THEN date
|
||||
WHEN ? = 'vendor_name' THEN vendor
|
||||
WHEN ? = 'total_cost' THEN CAST(total_cost AS DECIMAL(15,3))
|
||||
WHEN ? = 'total_received' THEN CAST(total_received AS DECIMAL(15,3))
|
||||
WHEN ? = 'total_items' THEN CAST(total_items AS SIGNED)
|
||||
WHEN ? = 'total_quantity' THEN CAST(total_quantity AS SIGNED)
|
||||
WHEN ? = 'fulfillment_rate' THEN CAST(fulfillment_rate AS DECIMAL(5,3))
|
||||
WHEN ? = 'status' THEN status
|
||||
WHEN $${paramCounter} = 'order_date' THEN date
|
||||
WHEN $${paramCounter} = 'vendor_name' THEN vendor
|
||||
WHEN $${paramCounter} = 'total_cost' THEN total_cost
|
||||
WHEN $${paramCounter} = 'total_received' THEN total_received
|
||||
WHEN $${paramCounter} = 'total_items' THEN total_items
|
||||
WHEN $${paramCounter} = 'total_quantity' THEN total_quantity
|
||||
WHEN $${paramCounter} = 'fulfillment_rate' THEN fulfillment_rate
|
||||
WHEN $${paramCounter} = 'status' THEN status
|
||||
ELSE date
|
||||
END ${sortDirection === 'desc' ? 'DESC' : 'ASC'}
|
||||
LIMIT ? OFFSET ?
|
||||
`, [...params, sortColumn, sortColumn, sortColumn, sortColumn, sortColumn, sortColumn, sortColumn, sortColumn, Number(limit), offset]);
|
||||
LIMIT $${paramCounter + 1} OFFSET $${paramCounter + 2}
|
||||
`, [...params, sortColumn, Number(limit), offset]);
|
||||
|
||||
// Get unique vendors for filter options
|
||||
const [vendors] = await pool.query(`
|
||||
const { rows: vendors } = await pool.query(`
|
||||
SELECT DISTINCT vendor
|
||||
FROM purchase_orders
|
||||
WHERE vendor IS NOT NULL AND vendor != ''
|
||||
@@ -146,7 +152,7 @@ router.get('/', async (req, res) => {
|
||||
`);
|
||||
|
||||
// Get unique statuses for filter options
|
||||
const [statuses] = await pool.query(`
|
||||
const { rows: statuses } = await pool.query(`
|
||||
SELECT DISTINCT status
|
||||
FROM purchase_orders
|
||||
WHERE status IS NOT NULL
|
||||
@@ -169,12 +175,12 @@ router.get('/', async (req, res) => {
|
||||
|
||||
// Parse summary metrics
|
||||
const parsedSummary = {
|
||||
order_count: Number(summary[0].order_count) || 0,
|
||||
total_ordered: Number(summary[0].total_ordered) || 0,
|
||||
total_received: Number(summary[0].total_received) || 0,
|
||||
fulfillment_rate: Number(summary[0].fulfillment_rate) || 0,
|
||||
total_value: Number(summary[0].total_value) || 0,
|
||||
avg_cost: Number(summary[0].avg_cost) || 0
|
||||
order_count: Number(summary.order_count) || 0,
|
||||
total_ordered: Number(summary.total_ordered) || 0,
|
||||
total_received: Number(summary.total_received) || 0,
|
||||
fulfillment_rate: Number(summary.fulfillment_rate) || 0,
|
||||
total_value: Number(summary.total_value) || 0,
|
||||
avg_cost: Number(summary.avg_cost) || 0
|
||||
};
|
||||
|
||||
res.json({
|
||||
@@ -202,7 +208,7 @@ router.get('/vendor-metrics', async (req, res) => {
|
||||
try {
|
||||
const pool = req.app.locals.pool;
|
||||
|
||||
const [metrics] = await pool.query(`
|
||||
const { rows: metrics } = await pool.query(`
|
||||
WITH delivery_metrics AS (
|
||||
SELECT
|
||||
vendor,
|
||||
@@ -213,7 +219,7 @@ router.get('/vendor-metrics', async (req, res) => {
|
||||
CASE
|
||||
WHEN status >= ${STATUS.RECEIVING_STARTED} AND receiving_status >= ${RECEIVING_STATUS.PARTIAL_RECEIVED}
|
||||
AND received_date IS NOT NULL AND date IS NOT NULL
|
||||
THEN DATEDIFF(received_date, date)
|
||||
THEN (received_date - date)::integer
|
||||
ELSE NULL
|
||||
END as delivery_days
|
||||
FROM purchase_orders
|
||||
@@ -226,18 +232,18 @@ router.get('/vendor-metrics', async (req, res) => {
|
||||
SUM(ordered) as total_ordered,
|
||||
SUM(received) as total_received,
|
||||
ROUND(
|
||||
SUM(received) / NULLIF(SUM(ordered), 0), 3
|
||||
(SUM(received)::numeric / NULLIF(SUM(ordered), 0)), 3
|
||||
) as fulfillment_rate,
|
||||
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,
|
||||
ROUND(
|
||||
AVG(NULLIF(delivery_days, 0)), 1
|
||||
(SUM(ordered * cost_price)::numeric / NULLIF(SUM(ordered), 0)), 2
|
||||
) as avg_unit_cost,
|
||||
ROUND(SUM(ordered * cost_price)::numeric, 3) as total_spend,
|
||||
ROUND(
|
||||
AVG(NULLIF(delivery_days, 0))::numeric, 1
|
||||
) as avg_delivery_days
|
||||
FROM delivery_metrics
|
||||
GROUP BY vendor
|
||||
HAVING total_orders > 0
|
||||
HAVING COUNT(DISTINCT po_id) > 0
|
||||
ORDER BY total_spend DESC
|
||||
`);
|
||||
|
||||
@@ -251,7 +257,7 @@ router.get('/vendor-metrics', async (req, res) => {
|
||||
fulfillment_rate: Number(vendor.fulfillment_rate) || 0,
|
||||
avg_unit_cost: Number(vendor.avg_unit_cost) || 0,
|
||||
total_spend: Number(vendor.total_spend) || 0,
|
||||
avg_delivery_days: vendor.avg_delivery_days === null ? null : Number(vendor.avg_delivery_days)
|
||||
avg_delivery_days: Number(vendor.avg_delivery_days) || 0
|
||||
}));
|
||||
|
||||
res.json(parsedMetrics);
|
||||
|
||||
Reference in New Issue
Block a user