Add operators for numerical filters

This commit is contained in:
2025-01-15 15:36:08 -05:00
parent 9e21d593f1
commit 82da563ee1
7 changed files with 530 additions and 238 deletions

View File

@@ -16,7 +16,6 @@ router.get('/', async (req, res) => {
const sortColumn = req.query.sort || 'title';
const sortDirection = req.query.order === 'desc' ? 'DESC' : 'ASC';
// Build the WHERE clause
const conditions = ['p.visible = true'];
const params = [];
@@ -25,185 +24,89 @@ router.get('/', async (req, res) => {
conditions.push('p.replenishable = true');
}
// Handle text search filters
// Handle search filter
if (req.query.search) {
conditions.push('(p.title LIKE ? OR p.SKU LIKE ?)');
params.push(`%${req.query.search}%`, `%${req.query.search}%`);
conditions.push('(p.title LIKE ? OR p.SKU LIKE ? OR p.barcode LIKE ?)');
const searchTerm = `%${req.query.search}%`;
params.push(searchTerm, searchTerm, searchTerm);
}
if (req.query.sku) {
conditions.push('p.SKU LIKE ?');
params.push(`%${req.query.sku}%`);
}
// Handle numeric filters with operators
const numericFields = {
stock: 'p.stock_quantity',
price: 'p.price',
costPrice: 'p.cost_price',
landingCost: 'p.landing_cost_price',
dailySalesAvg: 'pm.daily_sales_avg',
weeklySalesAvg: 'pm.weekly_sales_avg',
monthlySalesAvg: 'pm.monthly_sales_avg',
margin: 'pm.avg_margin_percent',
gmroi: 'pm.gmroi',
leadTime: 'pm.current_lead_time',
stockCoverage: 'pm.days_of_inventory',
daysOfStock: 'pm.days_of_inventory'
};
Object.entries(req.query).forEach(([key, value]) => {
const field = numericFields[key];
if (field) {
const operator = req.query[`${key}_operator`] || '=';
if (operator === 'between') {
// Handle between operator
try {
const [min, max] = JSON.parse(value);
conditions.push(`${field} BETWEEN ? AND ?`);
params.push(min, max);
} catch (e) {
console.error(`Invalid between value for ${key}:`, value);
}
} else {
// Handle other operators
conditions.push(`${field} ${operator} ?`);
params.push(parseFloat(value));
}
}
});
// Handle select filters
if (req.query.category && req.query.category !== 'all') {
conditions.push(`
p.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(req.query.category);
}
if (req.query.vendor && req.query.vendor !== 'all') {
if (req.query.vendor) {
conditions.push('p.vendor = ?');
params.push(req.query.vendor);
}
if (req.query.brand && req.query.brand !== 'all') {
if (req.query.brand) {
conditions.push('p.brand = ?');
params.push(req.query.brand);
}
if (req.query.category) {
conditions.push('p.categories LIKE ?');
params.push(`%${req.query.category}%`);
}
if (req.query.stockStatus && req.query.stockStatus !== 'all') {
conditions.push('pm.stock_status = ?');
params.push(req.query.stockStatus);
}
if (req.query.abcClass) {
conditions.push('pm.abc_class = ?');
params.push(req.query.abcClass);
}
// Handle numeric range filters
if (req.query.minStock) {
conditions.push('p.stock_quantity >= ?');
params.push(parseFloat(req.query.minStock));
}
if (req.query.maxStock) {
conditions.push('p.stock_quantity <= ?');
params.push(parseFloat(req.query.maxStock));
}
if (req.query.daysOfStock) {
conditions.push('pm.days_of_inventory >= ?');
params.push(parseFloat(req.query.daysOfStock));
}
// Handle boolean filters
if (req.query.replenishable === 'true' || req.query.replenishable === 'false') {
conditions.push('p.replenishable = ?');
params.push(req.query.replenishable === 'true');
}
if (req.query.managingStock === 'true' || req.query.managingStock === 'false') {
conditions.push('p.managing_stock = ?');
params.push(req.query.managingStock === 'true');
}
// Handle price filters
if (req.query.minPrice) {
conditions.push('p.price >= ?');
params.push(parseFloat(req.query.minPrice));
}
if (req.query.maxPrice) {
conditions.push('p.price <= ?');
params.push(parseFloat(req.query.maxPrice));
}
if (req.query.minCostPrice) {
conditions.push('p.cost_price >= ?');
params.push(parseFloat(req.query.minCostPrice));
}
if (req.query.maxCostPrice) {
conditions.push('p.cost_price <= ?');
params.push(parseFloat(req.query.maxCostPrice));
}
if (req.query.minLandingCost) {
conditions.push('p.landing_cost_price >= ?');
params.push(parseFloat(req.query.minLandingCost));
}
if (req.query.maxLandingCost) {
conditions.push('p.landing_cost_price <= ?');
params.push(parseFloat(req.query.maxLandingCost));
}
// Handle sales metrics filters
if (req.query.minSalesAvg) {
conditions.push('pm.daily_sales_avg >= ?');
params.push(parseFloat(req.query.minSalesAvg));
}
if (req.query.maxSalesAvg) {
conditions.push('pm.daily_sales_avg <= ?');
params.push(parseFloat(req.query.maxSalesAvg));
}
if (req.query.minWeeklySales) {
conditions.push('pm.weekly_sales_avg >= ?');
params.push(parseFloat(req.query.minWeeklySales));
}
if (req.query.maxWeeklySales) {
conditions.push('pm.weekly_sales_avg <= ?');
params.push(parseFloat(req.query.maxWeeklySales));
}
if (req.query.minMonthlySales) {
conditions.push('pm.monthly_sales_avg >= ?');
params.push(parseFloat(req.query.minMonthlySales));
}
if (req.query.maxMonthlySales) {
conditions.push('pm.monthly_sales_avg <= ?');
params.push(parseFloat(req.query.maxMonthlySales));
}
// Handle financial metrics filters
if (req.query.minMargin) {
conditions.push('pm.avg_margin_percent >= ?');
params.push(parseFloat(req.query.minMargin));
}
if (req.query.maxMargin) {
conditions.push('pm.avg_margin_percent <= ?');
params.push(parseFloat(req.query.maxMargin));
}
if (req.query.minGMROI) {
conditions.push('pm.gmroi >= ?');
params.push(parseFloat(req.query.minGMROI));
}
if (req.query.maxGMROI) {
conditions.push('pm.gmroi <= ?');
params.push(parseFloat(req.query.maxGMROI));
}
// Handle lead time and coverage filters
if (req.query.minLeadTime) {
conditions.push('pm.avg_lead_time_days >= ?');
params.push(parseFloat(req.query.minLeadTime));
}
if (req.query.maxLeadTime) {
conditions.push('pm.avg_lead_time_days <= ?');
params.push(parseFloat(req.query.maxLeadTime));
}
if (req.query.leadTimeStatus) {
conditions.push('pm.lead_time_status = ?');
params.push(req.query.leadTimeStatus);
}
if (req.query.minStockCoverage) {
conditions.push('(pm.days_of_inventory / pt.target_days) >= ?');
params.push(parseFloat(req.query.minStockCoverage));
if (req.query.replenishable !== undefined) {
conditions.push('p.replenishable = ?');
params.push(req.query.replenishable === 'true' ? 1 : 0);
}
if (req.query.maxStockCoverage) {
conditions.push('(pm.days_of_inventory / pt.target_days) <= ?');
params.push(parseFloat(req.query.maxStockCoverage));
}
// Handle stock status filter
if (req.query.stockStatus && req.query.stockStatus !== 'all') {
conditions.push('pm.stock_status = ?');
params.push(req.query.stockStatus);
if (req.query.managingStock !== undefined) {
conditions.push('p.managing_stock = ?');
params.push(req.query.managingStock === 'true' ? 1 : 0);
}
// Combine all conditions with AND
@@ -359,7 +262,7 @@ router.get('/', async (req, res) => {
});
} catch (error) {
console.error('Error fetching products:', error);
res.status(500).json({ error: 'Internal server error' });
res.status(500).json({ error: 'Failed to fetch products' });
}
});