diff --git a/app.js b/app.js index c5a134b..706415e 100644 --- a/app.js +++ b/app.js @@ -208,6 +208,8 @@ import AnnouncementRoute from './resources/Announcement/announcementRouter.js' //Stock import Stock from "./resources/Stock/StockRoute.js"; +//Reports +import Report from "./resources/Reports/ReportRoute.js"; app.use("/api/v1", user); //Product @@ -311,4 +313,6 @@ app.use('/api',AnnouncementRoute) app.use("/api", Stock); // Email CMS // app.use("/api", RegisterEmail); +//Reports +app.use("/api/report",Report); export default app; diff --git a/resources/Brands/BrandsController.js b/resources/Brands/BrandsController.js index 0d1c73e..f3fe3cb 100644 --- a/resources/Brands/BrandsController.js +++ b/resources/Brands/BrandsController.js @@ -35,9 +35,9 @@ export const addBrand = async (req, res) => { // Get all Brands export const getBrands = async (req, res) => { try { - const PAGE_SIZE = parseInt(req.query.show) || 10; - const page = parseInt(req.query.page) || 1; - const skip = (page - 1) * PAGE_SIZE; + // const PAGE_SIZE = parseInt(req.query.show) || 10; + // const page = parseInt(req.query.page) || 1; + // const skip = (page - 1) * PAGE_SIZE; let filter = {}; // Search by brandName if provided @@ -52,8 +52,8 @@ export const getBrands = async (req, res) => { // Fetch brands with pagination and filtering const brands = await BrandModel.find(filter) - .limit(PAGE_SIZE) - .skip(skip) + // .limit(PAGE_SIZE) + // .skip(skip) .sort({ createdAt: -1 }) .exec(); @@ -61,8 +61,8 @@ export const getBrands = async (req, res) => { res.status(200).json({ success: true, total_data: total, - total_pages: Math.ceil(total / PAGE_SIZE), - current_page: page, + // total_pages: Math.ceil(total / PAGE_SIZE), + // current_page: page, brands, }); } catch (error) { diff --git a/resources/Category/categoryController.js b/resources/Category/categoryController.js index 9c43f21..62edad6 100644 --- a/resources/Category/categoryController.js +++ b/resources/Category/categoryController.js @@ -35,9 +35,9 @@ export const addCategory = async (req, res) => { // Get all Categories export const getCategories = async (req, res) => { try { - const PAGE_SIZE = parseInt(req.query.show) || 10; - const page = parseInt(req.query.page) || 1; - const skip = (page - 1) * PAGE_SIZE; + // const PAGE_SIZE = parseInt(req.query.show) || 10; + // const page = parseInt(req.query.page) || 1; + // const skip = (page - 1) * PAGE_SIZE; let filter = {}; // Handle filtering by categoryName @@ -52,8 +52,8 @@ export const getCategories = async (req, res) => { // Fetch the categories with pagination const categories = await CategoryModel.find(filter) - .limit(PAGE_SIZE) - .skip(skip) + // .limit(PAGE_SIZE) + // .skip(skip) .sort({ createdAt: -1 }) .exec(); @@ -61,8 +61,8 @@ export const getCategories = async (req, res) => { res.status(200).json({ success: true, total_data: total, - total_pages: Math.ceil(total / PAGE_SIZE), - current_page: page, + // total_pages: Math.ceil(total / PAGE_SIZE), + // current_page: page, categories, }); } catch (error) { diff --git a/resources/Reports/OpeningInventoryReports.js b/resources/Reports/OpeningInventoryReports.js new file mode 100644 index 0000000..44e89e2 --- /dev/null +++ b/resources/Reports/OpeningInventoryReports.js @@ -0,0 +1,103 @@ +import mongoose from "mongoose"; +import { Product } from "../Products/ProductModel.js"; +import { PDStock } from "../Stock/PdStockModel.js"; +import { RDStock } from "../Stock/RdStockModel.js"; + +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 opening inventory for these products using SKU + const pdStocks = await PDStock.aggregate([ + { $unwind: "$products" }, + { + $match: { + "products.openingInventory": { $gt: 0 }, + "products.SKU": { $in: productSKUs }, + }, + }, + { + $group: { + _id: "$products.SKU", + pdCount: { $sum: 1 }, + }, + }, + ]); + + // Step 4: Fetch all RDs with non-zero opening inventory for these products using SKU + const rdStocks = await RDStock.aggregate([ + { $unwind: "$products" }, + { + $match: { + "products.openingInventory": { $gt: 0 }, + "products.SKU": { $in: productSKUs }, + }, + }, + { + $group: { + _id: "$products.SKU", + rdCount: { $sum: 1 }, + }, + }, + ]); + + // Step 5: Prepare a mapping of PD and RD counts by SKU + const pdMap = {}; + pdStocks.forEach((pd) => { + pdMap[pd._id] = pd.pdCount; + }); + + const rdMap = {}; + rdStocks.forEach((rd) => { + rdMap[rd._id] = rd.rdCount; + }); + + // 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 }); + } +}; diff --git a/resources/Reports/ReportRoute.js b/resources/Reports/ReportRoute.js new file mode 100644 index 0000000..979740a --- /dev/null +++ b/resources/Reports/ReportRoute.js @@ -0,0 +1,12 @@ +import express from "express"; +import { getProductsWithStockInfo } from "./OpeningInventoryReports.js"; + +import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; + +const router = express.Router(); + +router + .route("/opening-inventory") + .get(isAuthenticatedUser, authorizeRoles("admin"), getProductsWithStockInfo); + +export default router;