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

462 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).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 });
}
};