Fix forecasting brand list and filter on products received during period selected
This commit is contained in:
@@ -547,10 +547,12 @@ router.get('/forecast', async (req, res) => {
|
|||||||
FROM categories c
|
FROM categories c
|
||||||
JOIN product_categories pc ON c.id = pc.category_id
|
JOIN product_categories pc ON c.id = pc.category_id
|
||||||
JOIN products p ON pc.product_id = p.product_id
|
JOIN products p ON pc.product_id = p.product_id
|
||||||
|
LEFT JOIN product_metrics pm ON p.product_id = pm.product_id
|
||||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||||
AND o.date BETWEEN ? AND ?
|
AND o.date BETWEEN ? AND ?
|
||||||
AND o.canceled = false
|
AND o.canceled = false
|
||||||
WHERE p.brand = ?
|
WHERE p.brand = ?
|
||||||
|
AND pm.first_received_date BETWEEN ? AND ?
|
||||||
GROUP BY c.id, c.name, p.brand
|
GROUP BY c.id, c.name, p.brand
|
||||||
),
|
),
|
||||||
product_metrics AS (
|
product_metrics AS (
|
||||||
@@ -560,15 +562,18 @@ router.get('/forecast', async (req, res) => {
|
|||||||
p.sku,
|
p.sku,
|
||||||
p.stock_quantity,
|
p.stock_quantity,
|
||||||
pc.category_id,
|
pc.category_id,
|
||||||
|
pm.first_received_date,
|
||||||
COALESCE(SUM(o.quantity), 0) as total_sold,
|
COALESCE(SUM(o.quantity), 0) as total_sold,
|
||||||
COALESCE(ROUND(AVG(o.price), 2), 0) as avg_price
|
COALESCE(ROUND(AVG(o.price), 2), 0) as avg_price
|
||||||
FROM products p
|
FROM products p
|
||||||
JOIN product_categories pc ON p.product_id = pc.product_id
|
JOIN product_categories pc ON p.product_id = pc.product_id
|
||||||
|
JOIN product_metrics pm ON p.product_id = pm.product_id
|
||||||
LEFT JOIN orders o ON p.product_id = o.product_id
|
LEFT JOIN orders o ON p.product_id = o.product_id
|
||||||
AND o.date BETWEEN ? AND ?
|
AND o.date BETWEEN ? AND ?
|
||||||
AND o.canceled = false
|
AND o.canceled = false
|
||||||
WHERE p.brand = ?
|
WHERE p.brand = ?
|
||||||
GROUP BY p.product_id, p.title, p.sku, p.stock_quantity, pc.category_id
|
AND pm.first_received_date BETWEEN ? AND ?
|
||||||
|
GROUP BY p.product_id, p.title, p.sku, p.stock_quantity, pc.category_id, pm.first_received_date
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
cm.*,
|
cm.*,
|
||||||
@@ -579,14 +584,15 @@ router.get('/forecast', async (req, res) => {
|
|||||||
'sku', pm.sku,
|
'sku', pm.sku,
|
||||||
'stock_quantity', pm.stock_quantity,
|
'stock_quantity', pm.stock_quantity,
|
||||||
'total_sold', pm.total_sold,
|
'total_sold', pm.total_sold,
|
||||||
'avg_price', pm.avg_price
|
'avg_price', pm.avg_price,
|
||||||
|
'first_received_date', DATE_FORMAT(pm.first_received_date, '%Y-%m-%d')
|
||||||
)
|
)
|
||||||
) as products
|
) as products
|
||||||
FROM category_metrics cm
|
FROM category_metrics cm
|
||||||
JOIN product_metrics pm ON cm.category_id = pm.category_id
|
JOIN product_metrics pm ON cm.category_id = pm.category_id
|
||||||
GROUP BY cm.category_id, cm.category_name, cm.brand, cm.num_products, cm.avg_daily_sales, cm.total_sold, cm.avgTotalSold, cm.avg_price
|
GROUP BY cm.category_id, cm.category_name, cm.brand, cm.num_products, cm.avg_daily_sales, cm.total_sold, cm.avgTotalSold, cm.avg_price
|
||||||
ORDER BY cm.total_sold DESC
|
ORDER BY cm.total_sold DESC
|
||||||
`, [startDate, endDate, startDate, endDate, brand, startDate, endDate, brand]);
|
`, [startDate, endDate, startDate, endDate, brand, startDate, endDate, startDate, endDate, brand, startDate, endDate]);
|
||||||
|
|
||||||
res.json(results);
|
res.json(results);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -20,12 +20,15 @@ router.get('/brands', async (req, res) => {
|
|||||||
console.log('Fetching brands from database...');
|
console.log('Fetching brands from database...');
|
||||||
|
|
||||||
const [results] = await pool.query(`
|
const [results] = await pool.query(`
|
||||||
SELECT DISTINCT brand
|
SELECT DISTINCT p.brand
|
||||||
FROM products
|
FROM products p
|
||||||
WHERE brand IS NOT NULL
|
JOIN purchase_orders po ON p.product_id = po.product_id
|
||||||
AND brand != ''
|
WHERE p.brand IS NOT NULL
|
||||||
AND visible = true
|
AND p.brand != ''
|
||||||
ORDER BY brand
|
AND p.visible = true
|
||||||
|
GROUP BY p.brand
|
||||||
|
HAVING SUM(po.cost_price * po.received) >= 500
|
||||||
|
ORDER BY p.brand
|
||||||
`);
|
`);
|
||||||
|
|
||||||
console.log(`Found ${results.length} brands:`, results.slice(0, 3));
|
console.log(`Found ${results.length} brands:`, results.slice(0, 3));
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ interface ProductDetail {
|
|||||||
stock_quantity: number;
|
stock_quantity: number;
|
||||||
total_sold: number;
|
total_sold: number;
|
||||||
avg_price: number;
|
avg_price: number;
|
||||||
|
first_received_date: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ForecastItem {
|
export interface ForecastItem {
|
||||||
@@ -148,6 +149,7 @@ export const renderSubComponent = ({ row }: { row: any }) => {
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead>Product Name</TableHead>
|
<TableHead>Product Name</TableHead>
|
||||||
<TableHead>SKU</TableHead>
|
<TableHead>SKU</TableHead>
|
||||||
|
<TableHead>First Received</TableHead>
|
||||||
<TableHead>Stock Quantity</TableHead>
|
<TableHead>Stock Quantity</TableHead>
|
||||||
<TableHead>Total Sold</TableHead>
|
<TableHead>Total Sold</TableHead>
|
||||||
<TableHead>Average Price</TableHead>
|
<TableHead>Average Price</TableHead>
|
||||||
@@ -158,6 +160,7 @@ export const renderSubComponent = ({ row }: { row: any }) => {
|
|||||||
<TableRow key={product.product_id}>
|
<TableRow key={product.product_id}>
|
||||||
<TableCell>{product.name}</TableCell>
|
<TableCell>{product.name}</TableCell>
|
||||||
<TableCell>{product.sku}</TableCell>
|
<TableCell>{product.sku}</TableCell>
|
||||||
|
<TableCell>{product.first_received_date}</TableCell>
|
||||||
<TableCell>{product.stock_quantity.toLocaleString()}</TableCell>
|
<TableCell>{product.stock_quantity.toLocaleString()}</TableCell>
|
||||||
<TableCell>{product.total_sold.toLocaleString()}</TableCell>
|
<TableCell>{product.total_sold.toLocaleString()}</TableCell>
|
||||||
<TableCell>${product.avg_price.toFixed(2)}</TableCell>
|
<TableCell>${product.avg_price.toFixed(2)}</TableCell>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from "react";
|
import { useState, Fragment } from "react";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
@@ -72,6 +72,7 @@ export default function Forecasting() {
|
|||||||
stock_quantity: Number(p.stock_quantity) || 0,
|
stock_quantity: Number(p.stock_quantity) || 0,
|
||||||
total_sold: Number(p.total_sold) || 0,
|
total_sold: Number(p.total_sold) || 0,
|
||||||
avg_price: Number(p.avg_price) || 0,
|
avg_price: Number(p.avg_price) || 0,
|
||||||
|
first_received_date: p.first_received_date,
|
||||||
}))
|
}))
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
@@ -145,9 +146,8 @@ export default function Forecasting() {
|
|||||||
<TableBody>
|
<TableBody>
|
||||||
{table.getRowModel().rows?.length ? (
|
{table.getRowModel().rows?.length ? (
|
||||||
table.getRowModel().rows.map((row: Row<ForecastItem>) => (
|
table.getRowModel().rows.map((row: Row<ForecastItem>) => (
|
||||||
<>
|
<Fragment key={row.id}>
|
||||||
<TableRow
|
<TableRow
|
||||||
key={row.id}
|
|
||||||
data-state={row.getIsSelected() && "selected"}
|
data-state={row.getIsSelected() && "selected"}
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map((cell) => (
|
||||||
@@ -163,7 +163,7 @@ export default function Forecasting() {
|
|||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
)}
|
||||||
</>
|
</Fragment>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|||||||
Reference in New Issue
Block a user