api/resources/Reports/OpeningInventoryReports.js
2024-11-06 14:05:16 +05:30

416 lines
14 KiB
JavaScript

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,
allRDs: rdMap[product.SKU] || 0,
allPdAndRd: (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0),
}));
// 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,
allRDs: rdMap[product.SKU] || 0,
allPdAndRd: (pdMap[product.SKU] || 0) + (rdMap[product.SKU] || 0),
}));
// 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), // Total At PDs & Retailers
"All PDs": pdMap[product.SKU] || 0, // All PDs
"All Retailers": rdMap[product.SKU] || 0, // 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), // Total At PDs & Retailers
"All PDs": pdMap[product.SKU] || 0, // All PDs
"All Retailers": rdMap[product.SKU] || 0, // 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 });
}
};