import mongoose from "mongoose"; import { Product } from "../Products/ProductModel.js"; import { PDStock } from "../Stock/PdStockModel.js"; import { RDStock } from "../Stock/RdStockModel.js"; import * as XLSX from "xlsx"; export const getProductsWithOpenInventoryInfo = async (req, res) => { try { const { page = 1, show = 10, name, category, brand } = req.query; // Pagination setup const limit = parseInt(show, 10); const skip = (parseInt(page, 10) - 1) * limit; // Search filters const searchCriteria = {}; if (name) searchCriteria.name = { $regex: name, $options: "i" }; if (category) searchCriteria.category = mongoose.Types.ObjectId(category); if (brand) searchCriteria.brand = mongoose.Types.ObjectId(brand); // Step 1: Fetch filtered products with population const products = await Product.find(searchCriteria) .skip(skip) .limit(limit) .populate("brand", "brandName") // Populate brandName .populate("category", "categoryName") // Populate categoryName .exec(); // Step 2: Collect all product SKUs to find corresponding PDs and RDs with stock const productSKUs = products.map((product) => product.SKU); // Step 3: Fetch all PDs with non-zero opening inventory for these products using SKU const pdOIs = await PDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.openingInventory": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalPdOpeningInventory: { $sum: "$products.openingInventory" }, }, }, ]); // Step 4: Fetch all RDs with non-zero opening inventory for these products using SKU const rdOIs = await RDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.openingInventory": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalRdOpeningInventory: { $sum: "$products.openingInventory" }, }, }, ]); // Step 5: Prepare a mapping of PD and RD copening inventories by SKU const pdMap = {}; pdOIs.forEach((pd) => { pdMap[pd._id] = pd.totalPdOpeningInventory; }); const rdMap = {}; rdOIs.forEach((rd) => { rdMap[rd._id] = rd.totalRdOpeningInventory; }); // Step 6: Combine product info with PD/RD counts using SKU const productData = products.map((product) => ({ SKU: product.SKU, name: product.name, brand: product.brand?.brandName || "N/A", // Access brandName here category: product.category?.categoryName || "N/A", // Access categoryName here allPDs: (pdMap[product.SKU] || 0).toFixed(2), allRDs: (rdMap[product.SKU] || 0).toFixed(2), allPdAndRd: ( (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0) ).toFixed(2), })); // Step 7: Get total count for pagination const totalCount = await Product.countDocuments(searchCriteria); // Step 8: Respond with paginated results and total count res.status(200).json({ success: true, data: productData, pagination: { total: totalCount, page: parseInt(page, 10), pages: Math.ceil(totalCount / limit), }, }); } catch (error) { res.status(500).json({ success: false, message: error.message }); } }; export const getProductsWithStockInfo = async (req, res) => { try { const { page = 1, show = 10, name, category, brand } = req.query; // Pagination setup const limit = parseInt(show, 10); const skip = (parseInt(page, 10) - 1) * limit; // Search filters const searchCriteria = {}; if (name) searchCriteria.name = { $regex: name, $options: "i" }; if (category) searchCriteria.category = mongoose.Types.ObjectId(category); if (brand) searchCriteria.brand = mongoose.Types.ObjectId(brand); // Step 1: Fetch filtered products with population const products = await Product.find(searchCriteria) .skip(skip) .limit(limit) .populate("brand", "brandName") // Populate brandName .populate("category", "categoryName") // Populate categoryName .exec(); // Step 2: Collect all product SKUs to find corresponding PDs and RDs with stock const productSKUs = products.map((product) => product.SKU); // Step 3: Fetch all PDs with non-zero Stock for these products using SKU const pdStocks = await PDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.Stock": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalpdStock: { $sum: "$products.Stock" }, }, }, ]); // Step 4: Fetch all RDs with non-zero Stock for these products using SKU const rdStocks = await RDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.Stock": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalrdStock: { $sum: "$products.Stock" }, }, }, ]); // Step 5: Prepare a mapping of PD and RD Stocks by SKU const pdMap = {}; pdStocks.forEach((pd) => { pdMap[pd._id] = pd.totalpdStock; }); const rdMap = {}; rdStocks.forEach((rd) => { rdMap[rd._id] = rd.totalrdStock; }); // Step 6: Combine product info with PD/RD counts using SKU const productData = products.map((product) => ({ SKU: product.SKU, name: product.name, brand: product.brand?.brandName || "N/A", // Access brandName here category: product.category?.categoryName || "N/A", // Access categoryName here allPDs: (pdMap[product.SKU] || 0).toFixed(2), allRDs: (rdMap[product.SKU] || 0).toFixed(2), allPdAndRd: ( (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0) ).toFixed(2), })); // Step 7: Get total count for pagination const totalCount = await Product.countDocuments(searchCriteria); // Step 8: Respond with paginated results and total count res.status(200).json({ success: true, data: productData, pagination: { total: totalCount, page: parseInt(page, 10), pages: Math.ceil(totalCount / limit), }, }); } catch (error) { res.status(500).json({ success: false, message: error.message }); } }; export const DownloadProductsWithOpenInventoryInfo = async (req, res) => { try { // Fetch all products with necessary population const products = await Product.find() .populate("brand", "brandName") // Populate brandName .populate("category", "categoryName") // Populate categoryName .exec(); // Collect all product SKUs const productSKUs = products.map((product) => product.SKU); // Fetch all PDs with non-zero opening inventory for these products using SKU const pdOIs = await PDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.openingInventory": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalPdOpeningInventory: { $sum: "$products.openingInventory" }, }, }, ]); // Fetch all RDs with non-zero opening inventory for these products using SKU const rdOIs = await RDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.openingInventory": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalRdOpeningInventory: { $sum: "$products.openingInventory" }, }, }, ]); // Prepare mappings of PD and RD opening inventories by SKU const pdMap = {}; pdOIs.forEach((pd) => { pdMap[pd._id] = pd.totalPdOpeningInventory; }); const rdMap = {}; rdOIs.forEach((rd) => { rdMap[rd._id] = rd.totalRdOpeningInventory; }); // Combine product info with PD/RD counts using SKU const productData = products.map((product) => ({ "SKU Code": product.SKU, "SKU Description": product.name, // SKU Description "Category Name": product.category?.categoryName || "N/A", // Category Name "Brand Name": product.brand?.brandName || "N/A", // Brand Name "Total At PDs And Retailers": ( (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0) ).toFixed(2), // Total At PDs & Retailers "All PDs": (pdMap[product.SKU] || 0).toFixed(2), // All PDs "All Retailers": (rdMap[product.SKU] || 0).toFixed(2), // All Retailers })); // Define headers (first row in the sheet) const headers = [ [ "SKU Code", "SKU Description", "Category Name", "Brand Name", "Total At PDs And Retailers", "All PDs", "All Retailers", ], ]; // Create worksheet with headers const worksheet = XLSX.utils.aoa_to_sheet(headers); // Append product data below headers XLSX.utils.sheet_add_json(worksheet, productData, { skipHeader: true, origin: "A2", }); // Calculate and set column widths based on content const columnWidths = headers[0].map((header, index) => { let maxLength = header.length; // Start with the length of the header for (const row of productData) { const cellContent = row[headers[0][index]]?.toString() || ""; maxLength = Math.max(maxLength, cellContent.length); } return { wch: maxLength + 2 }; // Add some padding }); worksheet["!cols"] = columnWidths; // Create workbook and append worksheet const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "OpeningInventoryReport"); // Write workbook to buffer const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer", }); // Send Excel file as response res.setHeader( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ); res.setHeader( "Content-Disposition", "attachment; filename=OpeningInventoryReport.xlsx" ); res.send(excelBuffer); } catch (error) { res.status(500).json({ success: false, message: error.message }); } }; export const DownloadProductsWithStockInfo = async (req, res) => { try { // Fetch all products with necessary population const products = await Product.find() .populate("brand", "brandName") // Populate brandName .populate("category", "categoryName") // Populate categoryName .exec(); // Collect all product SKUs const productSKUs = products.map((product) => product.SKU); // Fetch all PDs with non-zero Stock for these products using SKU const pdOIs = await PDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.Stock": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalPdStock: { $sum: "$products.Stock" }, }, }, ]); // Fetch all RDs with non-zero Stockfor these products using SKU const rdOIs = await RDStock.aggregate([ { $unwind: "$products" }, { $match: { "products.Stock": { $gt: 0 }, "products.SKU": { $in: productSKUs }, }, }, { $group: { _id: "$products.SKU", totalRdStock: { $sum: "$products.Stock" }, }, }, ]); // Prepare mappings of PD and RD Stocks by SKU const pdMap = {}; pdOIs.forEach((pd) => { pdMap[pd._id] = pd.totalPdStock; }); const rdMap = {}; rdOIs.forEach((rd) => { rdMap[rd._id] = rd.totalRdStock; }); // Combine product info with PD/RD counts using SKU const productData = products.map((product) => ({ "SKU Code": product.SKU, "SKU Description": product.name, // SKU Description "Category Name": product.category?.categoryName || "N/A", // Category Name "Brand Name": product.brand?.brandName || "N/A", // Brand Name "Total At PDs And Retailers": ( (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0) ).toFixed(2), // Total At PDs & Retailers "All PDs": (pdMap[product.SKU] || 0).toFixed(2), // All PDs "All Retailers": (rdMap[product.SKU] || 0).toFixed(2), // All Retailers })); // Define headers (first row in the sheet) const headers = [ [ "SKU Code", "SKU Description", "Category Name", "Brand Name", "Total At PDs And Retailers", "All PDs", "All Retailers", ], ]; // Create worksheet with headers const worksheet = XLSX.utils.aoa_to_sheet(headers); // Append product data below headers XLSX.utils.sheet_add_json(worksheet, productData, { skipHeader: true, origin: "A2", }); // Calculate and set column widths based on content const columnWidths = headers[0].map((header, index) => { let maxLength = header.length; // Start with the length of the header for (const row of productData) { const cellContent = row[headers[0][index]]?.toString() || ""; maxLength = Math.max(maxLength, cellContent.length); } return { wch: maxLength + 2 }; // Add some padding }); worksheet["!cols"] = columnWidths; // Create workbook and append worksheet const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "StockReport"); // Write workbook to buffer const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer", }); // Send Excel file as response res.setHeader( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ); res.setHeader( "Content-Disposition", "attachment; filename=StockReport.xlsx" ); res.send(excelBuffer); } catch (error) { res.status(500).json({ success: false, message: error.message }); } };