Add product search and template creation functionality to validation step

This commit is contained in:
2025-03-01 12:24:04 -05:00
parent e0a7787139
commit f7bdefb0a3
7 changed files with 2855 additions and 560 deletions

View File

@@ -566,6 +566,9 @@ router.get('/field-options', async (req, res) => {
};
});
// Add debugging to verify category types
console.log(`Returning ${result.categories.length} categories with types: ${Array.from(new Set(result.categories.map(c => c.type))).join(', ')}`);
res.json(result);
} catch (error) {
console.error('Error fetching import field options:', error);
@@ -715,4 +718,232 @@ router.get('/list-uploads', (req, res) => {
}
});
// Search products from production database
router.get('/search-products', async (req, res) => {
const { q, company, dateRange } = req.query;
if (!q) {
return res.status(400).json({ error: 'Search term is required' });
}
try {
const { connection } = await getDbConnection();
// Build WHERE clause with additional filters
let whereClause = `
WHERE (
p.description LIKE ? OR
p.itemnumber LIKE ? OR
p.upc LIKE ? OR
pc1.name LIKE ? OR
s.companyname LIKE ?
)`;
// Add company filter if provided
if (company) {
whereClause += ` AND p.company = ${connection.escape(company)}`;
}
// Add date range filter if provided
if (dateRange) {
let dateCondition;
const now = new Date();
switch(dateRange) {
case '1week':
// Last week: date is after (current date - 7 days)
const weekAgo = new Date(now);
weekAgo.setDate(now.getDate() - 7);
dateCondition = `p.datein >= ${connection.escape(weekAgo.toISOString().slice(0, 10))}`;
break;
case '1month':
// Last month: date is after (current date - 30 days)
const monthAgo = new Date(now);
monthAgo.setDate(now.getDate() - 30);
dateCondition = `p.datein >= ${connection.escape(monthAgo.toISOString().slice(0, 10))}`;
break;
case '2months':
// Last 2 months: date is after (current date - 60 days)
const twoMonthsAgo = new Date(now);
twoMonthsAgo.setDate(now.getDate() - 60);
dateCondition = `p.datein >= ${connection.escape(twoMonthsAgo.toISOString().slice(0, 10))}`;
break;
case '3months':
// Last 3 months: date is after (current date - 90 days)
const threeMonthsAgo = new Date(now);
threeMonthsAgo.setDate(now.getDate() - 90);
dateCondition = `p.datein >= ${connection.escape(threeMonthsAgo.toISOString().slice(0, 10))}`;
break;
case '6months':
// Last 6 months: date is after (current date - 180 days)
const sixMonthsAgo = new Date(now);
sixMonthsAgo.setDate(now.getDate() - 180);
dateCondition = `p.datein >= ${connection.escape(sixMonthsAgo.toISOString().slice(0, 10))}`;
break;
case '1year':
// Last year: date is after (current date - 365 days)
const yearAgo = new Date(now);
yearAgo.setDate(now.getDate() - 365);
dateCondition = `p.datein >= ${connection.escape(yearAgo.toISOString().slice(0, 10))}`;
break;
default:
// If an unrecognized value is provided, don't add a date condition
dateCondition = null;
}
if (dateCondition) {
whereClause += ` AND ${dateCondition}`;
}
}
// Special case for wildcard search
const isWildcardSearch = q === '*';
const searchPattern = isWildcardSearch ? '%' : `%${q}%`;
const exactPattern = isWildcardSearch ? '%' : q;
// Search for products based on various fields
const query = `
SELECT
p.pid,
p.description AS title,
p.notes AS description,
p.itemnumber AS sku,
p.upc AS barcode,
p.harmonized_tariff_code,
pcp.price_each AS price,
p.sellingprice AS regular_price,
CASE
WHEN EXISTS (SELECT 1 FROM product_inventory WHERE pid = p.pid AND count > 0)
THEN (SELECT ROUND(AVG(costeach), 5) FROM product_inventory WHERE pid = p.pid AND count > 0)
ELSE (SELECT costeach FROM product_inventory WHERE pid = p.pid ORDER BY daterec DESC LIMIT 1)
END AS cost_price,
s.companyname AS vendor,
sid.supplier_itemnumber AS vendor_reference,
sid.notions_itemnumber AS notions_reference,
pc1.name AS brand,
p.company AS brand_id,
pc2.name AS line,
p.line AS line_id,
pc3.name AS subline,
p.subline AS subline_id,
pc4.name AS artist,
p.artist AS artist_id,
COALESCE(CASE
WHEN sid.supplier_id = 92 THEN sid.notions_qty_per_unit
ELSE sid.supplier_qty_per_unit
END, sid.notions_qty_per_unit) AS moq,
p.weight,
p.length,
p.width,
p.height,
p.country_of_origin,
p.totalsold AS total_sold,
p.datein AS first_received,
pls.date_sold AS date_last_sold
FROM products p
LEFT JOIN product_current_prices pcp ON p.pid = pcp.pid AND pcp.active = 1
LEFT JOIN supplier_item_data sid ON p.pid = sid.pid
LEFT JOIN suppliers s ON sid.supplier_id = s.supplierid
LEFT JOIN product_categories pc1 ON p.company = pc1.cat_id
LEFT JOIN product_categories pc2 ON p.line = pc2.cat_id
LEFT JOIN product_categories pc3 ON p.subline = pc3.cat_id
LEFT JOIN product_categories pc4 ON p.artist = pc4.cat_id
LEFT JOIN product_last_sold pls ON p.pid = pls.pid
${whereClause}
GROUP BY p.pid
${isWildcardSearch ? 'ORDER BY p.datein DESC' : `
ORDER BY
CASE
WHEN p.description LIKE ? THEN 1
WHEN p.itemnumber = ? THEN 2
WHEN p.upc = ? THEN 3
WHEN pc1.name LIKE ? THEN 4
WHEN s.companyname LIKE ? THEN 5
ELSE 6
END
`}
`;
// Prepare query parameters based on whether it's a wildcard search
let queryParams;
if (isWildcardSearch) {
queryParams = [
searchPattern, // LIKE for description
searchPattern, // LIKE for itemnumber
searchPattern, // LIKE for upc
searchPattern, // LIKE for brand name
searchPattern // LIKE for company name
];
} else {
queryParams = [
searchPattern, // LIKE for description
searchPattern, // LIKE for itemnumber
searchPattern, // LIKE for upc
searchPattern, // LIKE for brand name
searchPattern, // LIKE for company name
// For ORDER BY clause
searchPattern, // LIKE for description
exactPattern, // Exact match for itemnumber
exactPattern, // Exact match for upc
searchPattern, // LIKE for brand name
searchPattern // LIKE for company name
];
}
const [results] = await connection.query(query, queryParams);
res.json(results);
} catch (error) {
console.error('Error searching products:', error);
res.status(500).json({ error: 'Failed to search products' });
}
});
// Get product categories for a specific product
router.get('/product-categories/:pid', async (req, res) => {
try {
const { pid } = req.params;
if (!pid || isNaN(parseInt(pid))) {
return res.status(400).json({ error: 'Valid product ID is required' });
}
// Use the getDbConnection function instead of getPool
const { connection } = await getDbConnection();
// Query to get categories for a specific product
const query = `
SELECT pc.cat_id, pc.name, pc.type, pc.combined_name
FROM product_category_index pci
JOIN product_categories pc ON pci.cat_id = pc.cat_id
WHERE pci.pid = ?
ORDER BY pc.type, pc.name
`;
const [rows] = await connection.query(query, [pid]);
// Add debugging to log category types
const categoryTypes = rows.map(row => row.type);
const uniqueTypes = [...new Set(categoryTypes)];
console.log(`Product ${pid} has ${rows.length} categories with types: ${uniqueTypes.join(', ')}`);
console.log('Categories:', rows.map(row => ({ id: row.cat_id, name: row.name, type: row.type })));
// Format the response to match the expected format in the frontend
const categories = rows.map(category => ({
value: category.cat_id.toString(),
label: category.name,
type: category.type,
combined_name: category.combined_name
}));
res.json(categories);
} catch (error) {
console.error('Error fetching product categories:', error);
res.status(500).json({
error: 'Failed to fetch product categories',
details: error.message
});
}
});
module.exports = router;