const ProductService = require("../services/ProductService"); const CategoryService = require("../services/CategoryService"); const parseCsv = require("../utils/parseCsv"); class ProductController { index(req, res) { const products = ProductService.getProducts(req.user.household_id); const categories = CategoryService.getCategories(req.user.household_id); res.render( "products/index", { title:"Producten", products, categories } ); } create(req, res) { const name = (req.body.name || "").trim(); const categoryId = req.body.category_id ? Number.parseInt(req.body.category_id, 10) : null; if (!name || (categoryId !== null && !Number.isInteger(categoryId))) { req.flash("error", "Geef een geldige productnaam en categorie op."); return res.redirect("/products"); } if (categoryId !== null && !CategoryService.getCategory(categoryId, req.user.household_id)) { req.flash("error", "De gekozen categorie bestaat niet."); return res.redirect("/products"); } ProductService.createProduct({ name, category_id: categoryId, household_id: req.user.household_id }); req.flash("success", "Product toegevoegd."); res.redirect("/products"); } import(req, res) { if (!req.file) { req.flash("error", "Kies eerst een CSV-bestand."); return res.redirect("/products"); } let rows; try { rows = parseCsv(req.file.buffer.toString("utf8").replace(/^\uFEFF/, "")); } catch (error) { req.flash("error", `CSV kan niet worden gelezen: ${error.message}`); return res.redirect("/products"); } if (rows.length < 2) { req.flash("error", "Het CSV-bestand bevat geen producten."); return res.redirect("/products"); } const headers = rows[0].map(header => header.trim().toLowerCase()); const nameIndex = headers.indexOf("name"); const categoryIndex = headers.includes("category") ? headers.indexOf("category") : headers.indexOf("category_name"); const barcodeIndex = headers.indexOf("barcode"); if (nameIndex === -1) { req.flash("error", "De CSV moet een kolom 'name' bevatten."); return res.redirect("/products"); } const categories = CategoryService.getCategories(req.user.household_id); const categoryIds = new Map( categories.map(category => [category.name.trim().toLowerCase(), category.id]) ); const products = []; const errors = []; rows.slice(1).forEach((row, rowIndex) => { const lineNumber = rowIndex + 2; const name = (row[nameIndex] || "").trim(); const categoryName = categoryIndex === -1 ? "" : (row[categoryIndex] || "").trim(); const categoryId = categoryName ? categoryIds.get(categoryName.toLowerCase()) : null; if (!name) errors.push(`regel ${lineNumber}: productnaam ontbreekt`); if (categoryName && !categoryId) { errors.push(`regel ${lineNumber}: categorie '${categoryName}' bestaat niet`); } products.push({ name, category_id: categoryId, barcode: barcodeIndex === -1 ? "" : (row[barcodeIndex] || "").trim(), household_id: req.user.household_id }); }); if (errors.length > 0) { req.flash("error", `Import afgebroken: ${errors.slice(0, 5).join("; ")}`); return res.redirect("/products"); } ProductService.createProducts(products); req.flash("success", `${products.length} producten geïmporteerd.`); res.redirect("/products"); } editForm(req, res) { const productId = Number.parseInt(req.params.id, 10); const product = Number.isInteger(productId) ? ProductService.getProduct(productId, req.user.household_id) : null; if (!product) { req.flash("error", "Product niet gevonden."); return res.redirect("/products"); } const categories = CategoryService.getCategories(req.user.household_id); res.render("products/edit", { title: "Product wijzigen", product, categories }); } update(req, res) { const productId = Number.parseInt(req.params.id, 10); const name = (req.body.name || "").trim(); const categoryId = req.body.category_id ? Number.parseInt(req.body.category_id, 10) : null; if (!Number.isInteger(productId) || !name || (categoryId !== null && !Number.isInteger(categoryId))) { req.flash("error", "Geef een geldige productnaam en categorie op."); return res.redirect("/products"); } if (categoryId !== null && !CategoryService.getCategory(categoryId, req.user.household_id)) { req.flash("error", "De gekozen categorie bestaat niet."); return res.redirect(`/products/${productId}/edit`); } const result = ProductService.updateProduct(productId, req.user.household_id, { name, category_id: categoryId }); if (result.changes === 0) { req.flash("error", "Product niet gevonden."); return res.redirect("/products"); } req.flash("success", "Product gewijzigd."); res.redirect("/products"); } remove(req, res) { const productId = Number.parseInt(req.params.id, 10); if (!Number.isInteger(productId)) { req.flash("error", "Product niet gevonden."); return res.redirect("/products"); } if (ProductService.productHasListItems(productId, req.user.household_id)) { req.flash( "error", "Dit product staat nog op een boodschappenlijst en kan niet worden verwijderd." ); return res.redirect(`/products/${productId}/edit`); } const result = ProductService.deleteProduct(productId, req.user.household_id); if (result.changes === 0) { req.flash("error", "Product niet gevonden."); return res.redirect("/products"); } req.flash("success", "Product verwijderd."); res.redirect("/products"); } } module.exports = new ProductController();