Use new categories correctly in existing components and handle category names with commas
This commit is contained in:
@@ -65,7 +65,7 @@ router.get('/profit', async (req, res) => {
|
||||
// Get profit margins by category
|
||||
const [byCategory] = await pool.query(`
|
||||
SELECT
|
||||
COALESCE(p.categories, 'Uncategorized') as category,
|
||||
c.name as category,
|
||||
ROUND(
|
||||
(SUM(o.price * o.quantity - p.cost_price * o.quantity) /
|
||||
NULLIF(SUM(o.price * o.quantity), 0)) * 100, 1
|
||||
@@ -74,8 +74,10 @@ router.get('/profit', async (req, res) => {
|
||||
SUM(p.cost_price * o.quantity) as cost
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
GROUP BY p.categories
|
||||
GROUP BY c.name
|
||||
ORDER BY profitMargin DESC
|
||||
LIMIT 10
|
||||
`);
|
||||
@@ -190,14 +192,16 @@ router.get('/stock', async (req, res) => {
|
||||
// Get turnover by category
|
||||
const [turnoverByCategory] = await pool.query(`
|
||||
SELECT
|
||||
COALESCE(p.categories, 'Uncategorized') as category,
|
||||
c.name as category,
|
||||
ROUND(SUM(o.quantity) / NULLIF(AVG(p.stock_quantity), 0), 1) as turnoverRate,
|
||||
ROUND(AVG(p.stock_quantity), 0) as averageStock,
|
||||
SUM(o.quantity) as totalSales
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
GROUP BY p.categories
|
||||
GROUP BY c.name
|
||||
HAVING turnoverRate > 0
|
||||
ORDER BY turnoverRate DESC
|
||||
LIMIT 10
|
||||
@@ -328,7 +332,7 @@ router.get('/categories', async (req, res) => {
|
||||
// Get category performance metrics
|
||||
const [performance] = await pool.query(`
|
||||
SELECT
|
||||
COALESCE(p.categories, 'Uncategorized') as category,
|
||||
c.name as category,
|
||||
SUM(o.price * o.quantity) as revenue,
|
||||
SUM(o.price * o.quantity - p.cost_price * o.quantity) as profit,
|
||||
ROUND(
|
||||
@@ -348,8 +352,10 @@ router.get('/categories', async (req, res) => {
|
||||
COUNT(DISTINCT p.product_id) as productCount
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 60 DAY)
|
||||
GROUP BY p.categories
|
||||
GROUP BY c.name
|
||||
HAVING revenue > 0
|
||||
ORDER BY revenue DESC
|
||||
LIMIT 10
|
||||
@@ -358,12 +364,14 @@ router.get('/categories', async (req, res) => {
|
||||
// Get category revenue distribution
|
||||
const [distribution] = await pool.query(`
|
||||
SELECT
|
||||
COALESCE(p.categories, 'Uncategorized') as category,
|
||||
c.name as category,
|
||||
SUM(o.price * o.quantity) as value
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.date >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
GROUP BY p.categories
|
||||
GROUP BY c.name
|
||||
HAVING value > 0
|
||||
ORDER BY value DESC
|
||||
LIMIT 6
|
||||
|
||||
@@ -123,11 +123,13 @@ router.get('/category-stats', async (req, res) => {
|
||||
try {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT
|
||||
categories,
|
||||
COUNT(*) as count
|
||||
FROM products
|
||||
WHERE visible = true
|
||||
GROUP BY categories
|
||||
c.name as category,
|
||||
COUNT(DISTINCT pc.product_id) as count
|
||||
FROM categories c
|
||||
LEFT JOIN product_categories pc ON c.id = pc.category_id
|
||||
LEFT JOIN products p ON pc.product_id = p.product_id
|
||||
WHERE p.visible = true
|
||||
GROUP BY c.name
|
||||
ORDER BY count DESC
|
||||
LIMIT 10
|
||||
`);
|
||||
@@ -164,13 +166,15 @@ router.get('/sales-by-category', async (req, res) => {
|
||||
try {
|
||||
const [rows] = await pool.query(`
|
||||
SELECT
|
||||
p.categories as category,
|
||||
c.name as category,
|
||||
SUM(o.price * o.quantity) as total
|
||||
FROM orders o
|
||||
JOIN products p ON o.product_id = p.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.canceled = false
|
||||
AND DATE(o.date) >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
GROUP BY p.categories
|
||||
GROUP BY c.name
|
||||
ORDER BY total DESC
|
||||
LIMIT 6
|
||||
`);
|
||||
@@ -266,14 +270,16 @@ router.get('/inventory-metrics', async (req, res) => {
|
||||
// Get stock levels by category
|
||||
const [stockLevels] = await pool.query(`
|
||||
SELECT
|
||||
categories as category,
|
||||
c.name as category,
|
||||
SUM(CASE WHEN stock_quantity > 5 THEN 1 ELSE 0 END) as inStock,
|
||||
SUM(CASE WHEN stock_quantity > 0 AND stock_quantity <= 5 THEN 1 ELSE 0 END) as lowStock,
|
||||
SUM(CASE WHEN stock_quantity = 0 THEN 1 ELSE 0 END) as outOfStock
|
||||
FROM products
|
||||
FROM products p
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE visible = true
|
||||
GROUP BY categories
|
||||
ORDER BY categories ASC
|
||||
GROUP BY c.name
|
||||
ORDER BY c.name ASC
|
||||
`);
|
||||
|
||||
// Get top vendors with product counts and average stock
|
||||
@@ -296,21 +302,25 @@ router.get('/inventory-metrics', async (req, res) => {
|
||||
const [stockTurnover] = await pool.query(`
|
||||
WITH CategorySales AS (
|
||||
SELECT
|
||||
p.categories as category,
|
||||
c.name as category,
|
||||
SUM(o.quantity) as units_sold
|
||||
FROM products p
|
||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE o.canceled = false
|
||||
AND DATE(o.date) >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
|
||||
GROUP BY p.categories
|
||||
GROUP BY c.name
|
||||
),
|
||||
CategoryStock AS (
|
||||
SELECT
|
||||
categories as category,
|
||||
AVG(stock_quantity) as avg_stock
|
||||
FROM products
|
||||
WHERE visible = true
|
||||
GROUP BY categories
|
||||
c.name as category,
|
||||
AVG(p.stock_quantity) as avg_stock
|
||||
FROM products p
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE p.visible = true
|
||||
GROUP BY c.name
|
||||
)
|
||||
SELECT
|
||||
cs.category,
|
||||
|
||||
@@ -32,7 +32,14 @@ router.get('/', async (req, res) => {
|
||||
}
|
||||
|
||||
if (category !== 'all') {
|
||||
conditions.push('categories = ?');
|
||||
conditions.push(`
|
||||
product_id IN (
|
||||
SELECT pc.product_id
|
||||
FROM product_categories pc
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
WHERE c.name = ?
|
||||
)
|
||||
`);
|
||||
params.push(category);
|
||||
}
|
||||
|
||||
@@ -75,37 +82,46 @@ router.get('/', async (req, res) => {
|
||||
// Get paginated results
|
||||
const query = `
|
||||
SELECT
|
||||
product_id,
|
||||
title,
|
||||
SKU,
|
||||
stock_quantity,
|
||||
price,
|
||||
regular_price,
|
||||
cost_price,
|
||||
vendor,
|
||||
brand,
|
||||
categories,
|
||||
visible,
|
||||
managing_stock,
|
||||
image
|
||||
FROM products
|
||||
p.product_id,
|
||||
p.title,
|
||||
p.SKU,
|
||||
p.stock_quantity,
|
||||
p.price,
|
||||
p.regular_price,
|
||||
p.cost_price,
|
||||
p.vendor,
|
||||
p.brand,
|
||||
p.visible,
|
||||
p.managing_stock,
|
||||
p.image,
|
||||
GROUP_CONCAT(c.name) as categories
|
||||
FROM products p
|
||||
LEFT JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
LEFT JOIN categories c ON pc.category_id = c.id
|
||||
WHERE ${conditions.join(' AND ')}
|
||||
GROUP BY p.product_id
|
||||
ORDER BY ${sortColumn} ${sortDirection}
|
||||
LIMIT ? OFFSET ?
|
||||
`;
|
||||
|
||||
const [rows] = await pool.query(query, [...params, limit, offset]);
|
||||
|
||||
// Transform the categories string into an array
|
||||
const productsWithCategories = rows.map(product => ({
|
||||
...product,
|
||||
categories: product.categories ? product.categories.split(',') : []
|
||||
}));
|
||||
|
||||
// Get unique categories and vendors for filters
|
||||
const [categories] = await pool.query(
|
||||
'SELECT DISTINCT categories FROM products WHERE visible = true AND categories IS NOT NULL AND categories != "" ORDER BY categories'
|
||||
'SELECT name FROM categories ORDER BY name'
|
||||
);
|
||||
const [vendors] = await pool.query(
|
||||
'SELECT DISTINCT vendor FROM products WHERE visible = true AND vendor IS NOT NULL AND vendor != "" ORDER BY vendor'
|
||||
);
|
||||
|
||||
res.json({
|
||||
products: rows,
|
||||
products: productsWithCategories,
|
||||
pagination: {
|
||||
total,
|
||||
pages: Math.ceil(total / limit),
|
||||
@@ -113,7 +129,7 @@ router.get('/', async (req, res) => {
|
||||
limit
|
||||
},
|
||||
filters: {
|
||||
categories: categories.map(c => c.categories),
|
||||
categories: categories.map(c => c.name),
|
||||
vendors: vendors.map(v => v.vendor)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -243,7 +243,7 @@ router.get('/cost-analysis', async (req, res) => {
|
||||
|
||||
const [analysis] = await pool.query(`
|
||||
SELECT
|
||||
p.categories,
|
||||
c.name as categories,
|
||||
COUNT(DISTINCT po.product_id) as unique_products,
|
||||
ROUND(AVG(po.cost_price), 2) as avg_cost,
|
||||
MIN(po.cost_price) as min_cost,
|
||||
@@ -254,7 +254,9 @@ router.get('/cost-analysis', async (req, res) => {
|
||||
SUM(po.ordered * po.cost_price) as total_spend
|
||||
FROM purchase_orders po
|
||||
JOIN products p ON po.product_id = p.product_id
|
||||
GROUP BY p.categories
|
||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||
JOIN categories c ON pc.category_id = c.id
|
||||
GROUP BY c.name
|
||||
ORDER BY total_spend DESC
|
||||
`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user