diff --git a/app/controllers/products.controller.js b/app/controllers/products.controller.js index 9f084ce..c957ef6 100644 --- a/app/controllers/products.controller.js +++ b/app/controllers/products.controller.js @@ -78,42 +78,120 @@ exports.deleteProduct = async (req, res) => { res.status(500).send({'status':"failed",'message':err.message }); } }; - exports.uploadProductsFromCSV = async (req, res) => { try { + // Check if file exists if (!req.file) { return res.status(400).send({ status: "failed", message: "No file uploaded" }); } - const results = []; const filePath = req.file.path; - // Read CSV and store rows + // Validate file type + if (!req.file.originalname.endsWith(".csv")) { + fs.unlinkSync(filePath); + return res.status(400).send({ + status: "failed", + message: "Invalid file type. Only CSV files are allowed.", + }); + } + + // Validate userId + if (!req.body.userId || isNaN(req.body.userId)) { + fs.unlinkSync(filePath); + return res.status(400).send({ + status: "failed", + message: "Invalid or missing userId. Must be an integer.", + }); + } + + const results = []; + const userId = parseInt(req.body.userId); + + console.log(userId, "userId"); + + // Read CSV and clean headers/values fs.createReadStream(filePath) .pipe(csv()) .on("data", (row) => { - results.push(row); + // Trim all keys and values to handle spaces in header names or values + const cleanRow = {}; + for (const key in row) { + cleanRow[key.trim()] = row[key] ? row[key].trim() : null; + } + results.push(cleanRow); }) .on("end", async () => { try { - // Insert all rows into Product table - const inserted = await Product.bulkCreate(results, { validate: true }); - fs.unlinkSync(filePath); // delete file after processing + // Check for empty CSV + if (results.length === 0) { + fs.unlinkSync(filePath); + return res.status(400).send({ + status: "failed", + message: "CSV file is empty or invalid.", + }); + } - res.status(201).send({ - status: "success", - message: `${inserted.length} products inserted successfully`, - data: inserted, + console.log(results, "results"); + + // Fetch existing products by hs_code + const hsCodes = results.map((r) => r.hs_code); + const existingProducts = await Product.findAll({ + where: { hs_code: hsCodes }, }); - } catch (dbErr) { - res.status(500).send({ + + const existingHsCodes = existingProducts.map((p) => p.hs_code); + + const toInsert = []; + const duplicates = []; + + // Process CSV rows + for (const row of results) { + const hsCode = row.hs_code?.trim(); + const productName = row.product_name?.trim(); + const description = row.hs_description?.trim(); + + if (!hsCode || !productName) continue; // skip invalid rows + + if (existingHsCodes.includes(hsCode)) { + duplicates.push(hsCode); + } else { + toInsert.push({ + hs_code: hsCode, + product_name: productName, + hs_description: description, + created_by: userId, + created_at: new Date(), + }); + } + } + + // Bulk insert new records + let inserted = []; + if (toInsert.length > 0) { + inserted = await Product.bulkCreate(toInsert, { validate: true }); + } + + // Delete file after processing + fs.unlinkSync(filePath); + + // Send success response + return res.status(200).send({ + status: "success", + message: `${inserted.length} products inserted successfully.`, + inserted_count: inserted.length, + duplicate_hs_codes: duplicates, + }); + } catch (err) { + if (fs.existsSync(filePath)) fs.unlinkSync(filePath); + return res.status(500).send({ status: "failed", - message: dbErr.message, + message: err.message, }); } }); } catch (error) { - res.status(500).send({ status: "failed", message: error.message }); + return res.status(500).send({ status: "failed", message: error.message }); } };