From 45e54f6c744d543b8fe87960f3c74717b73d9692 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Thu, 10 Oct 2024 10:54:56 +0530 Subject: [PATCH 1/6] rd stock calculation done using the inventory --- resources/Inventory/InventoryController.js | 56 ++++++++++++-- resources/Inventory/InventoryModel.js | 74 +++++++++++------- resources/Inventory/InventoryRoute.js | 5 +- resources/RD_Ordes/rdOrderController.js | 60 +++++++-------- resources/Stock/StockController.js | 90 ++++++++++++++++++++++ resources/Stock/StockRoute.js | 8 +- 6 files changed, 224 insertions(+), 69 deletions(-) diff --git a/resources/Inventory/InventoryController.js b/resources/Inventory/InventoryController.js index c2a7e8b..7e104bc 100644 --- a/resources/Inventory/InventoryController.js +++ b/resources/Inventory/InventoryController.js @@ -6,32 +6,74 @@ import TerritoryManager from "../TerritoryManagers/TerritoryManagerModel.js"; import SalesCoordinator from "../SalesCoOrdinators/SalesCoOrdinatorModel.js"; import crypto from "crypto"; import RetailDistributor from "../RetailDistributor/RetailDistributorModel.js"; +import { RDStock } from "../Stock/RdStockModel.js"; // Add inventory data export const addInventory = async (req, res) => { try { const { products, addedFor, addedForId } = req.body; const userId = req.user._id; const userType = req.userType; - // console.log("req.user", req.user); - const currentYear = new Date().getFullYear().toString().slice(-2); - const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase(); - const uniqueId = `${currentYear}-${randomChars}`; - // console.log("uniqueId", uniqueId); + + if (addedFor === "RetailDistributor") { + // Find the RetailDistributor stock + const rdStock = await RDStock.findOne({ userId: addedForId }); + if (!rdStock) { + return res.status(404).json({ error: "Stock not available for the RetailDistributor" }); + } + + // Array to hold product names that have issues + let invalidProducts = []; + + // Loop through products in the request and check stock availability + for (const product of products) { + const { productid, Sale, ProductName } = product; + + // Find product in the RetailDistributor's stock + const rdProduct = rdStock.products.find(p => p.productid.toString() === productid.toString()); + + if (rdProduct) { + // Check if sales quantity is less than or equal to the stock + if (Sale <= rdProduct.Stock) { + // Decrease the stock by sales amount + rdProduct.Stock -= Sale; + } else { + // If sales > stock, add to invalid products list + invalidProducts.push(ProductName); + } + } else { + // Product not found in stock, add to invalid products list + invalidProducts.push(ProductName); + } + } + + // If there are any invalid products, return error message + if (invalidProducts.length > 0) { + return res.status(400).json({ + success: false, + message: `Insufficient stock or product not found for: ${invalidProducts.join(", ")}`, + }); + } + + // Save updated stock if all products are valid + await rdStock.save(); + } + + // Create and save new inventory record const newInventory = new Inventory({ userId, userType, addedFor, addedForId, products, - uniqueId, }); - // console.log("newInventory", newInventory); + await newInventory.save(); res.status(201).json({ success: true, message: "Inventory added successfully", data: newInventory, }); + } catch (error) { res.status(500).json({ success: false, message: "Server error", error }); } diff --git a/resources/Inventory/InventoryModel.js b/resources/Inventory/InventoryModel.js index 31fd7c0..d8003e3 100644 --- a/resources/Inventory/InventoryModel.js +++ b/resources/Inventory/InventoryModel.js @@ -1,7 +1,12 @@ -import mongoose from 'mongoose'; +import mongoose from "mongoose"; // Define Product record schema const ProductRecordSchema = new mongoose.Schema({ + productid: { + type: mongoose.Schema.Types.ObjectId, + ref: "Product", + required: true, + }, SKU: { type: String, required: true, @@ -21,33 +26,44 @@ const ProductRecordSchema = new mongoose.Schema({ }); // Define main Inventory schema -const InventorySchema = new mongoose.Schema({ - uniqueId: { - type: String, - required: true, - unique: true, +const InventorySchema = new mongoose.Schema( + { + uniqueId: { + type: String, + required: true, + unique: true, + }, + userId: { + type: mongoose.Schema.Types.ObjectId, + refPath: "userType", + required: true, + }, + userType: { + type: String, + required: true, + enum: ["SalesCoOrdinator", "TerritoryManager"], + }, + addedFor: { + type: String, + required: true, + enum: ["PrincipalDistributor", "RetailDistributor"], + }, + addedForId: { + type: mongoose.Schema.Types.ObjectId, + refPath: "addedFor", + required: true, + }, + products: [ProductRecordSchema], }, - userId: { - type: mongoose.Schema.Types.ObjectId, - refPath: 'userType', - required: true, - }, - userType: { - type: String, - required: true, - enum: ['SalesCoOrdinator', 'TerritoryManager'], - }, - addedFor: { - type: String, - required: true, - enum: ['PrincipalDistributor', 'RetailDistributor'], - }, - addedForId: { - type: mongoose.Schema.Types.ObjectId, - refPath: 'addedFor', - required: true, - }, - products: [ProductRecordSchema], -}, { timestamps: true, versionKey: false }); + { timestamps: true, versionKey: false } +); -export const Inventory = mongoose.model('Inventory', InventorySchema); +InventorySchema.pre("save", function (next) { + if (!this.uniqueId) { + const currentYear = new Date().getFullYear().toString().slice(-2); + const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase(); + this.uniqueId = `${currentYear}-${randomChars}`; + } + next(); +}); +export const Inventory = mongoose.model("Inventory", InventorySchema); \ No newline at end of file diff --git a/resources/Inventory/InventoryRoute.js b/resources/Inventory/InventoryRoute.js index 36e64b7..ca8944c 100644 --- a/resources/Inventory/InventoryRoute.js +++ b/resources/Inventory/InventoryRoute.js @@ -3,11 +3,12 @@ import { addInventory, getDistributors,getAllInventories,getSingleInventory } fr import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js"; import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js"; import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; +import {isAuthenticated_SC_TM } from "../../middlewares/generalAuth.js"; const router = express.Router(); // Route to add inventory data -router.post("/add-SC", isAuthenticatedSalesCoOrdinator, addInventory); -router.post("/add-TM", isAuthenticatedTerritoryManager, addInventory); +router.post("/add", isAuthenticated_SC_TM, addInventory); +// router.post("/add-TM", isAuthenticatedTerritoryManager, addInventory); // Route to get all PD or RD names based on type router.get( "/distributors-SC/:type", diff --git a/resources/RD_Ordes/rdOrderController.js b/resources/RD_Ordes/rdOrderController.js index 3fca418..1d2b24c 100644 --- a/resources/RD_Ordes/rdOrderController.js +++ b/resources/RD_Ordes/rdOrderController.js @@ -7,7 +7,7 @@ import { PDStock } from "../Stock/PdStockModel.js"; import { createKYC } from "../../Utils/rejectKyc.js"; import { Notification } from "../Notification/notificationModal.js"; import { sendPushNotification } from "../../Utils/sendPushNotification.js"; - +import {RDStock} from "../Stock/RdStockModel.js"; // Controller to create a new order by RD export const createOrderRD = async (req, res) => { try { @@ -973,38 +973,38 @@ export const updateCourierStatusToDeliveredForPD = async (req, res) => { if (!userId) { return res.status(400).json({ error: "User not found for the order" }); } - // Check if PDStock exists for the user - // let pdStock = await PDStock.findOne({ userId }); + // Check if RDStock exists for the user + let rdStock = await RDStock.findOne({ userId }); + + if (!rdStock) { + // If no stock record exists, create a new one + rdStock = new RDStock({ + userId, + products: [], // Initialize with empty products array + }); + } + // Iterate over each item in the invoice + for (let item of invoice.items) { + const { productId, processquantity } = item; - // if (!pdStock) { - // // If no stock record exists, create a new one - // pdStock = new PDStock({ - // userId, - // products: [], // Initialize with empty products array - // }); - // } - // // Iterate over each item in the invoice - // for (let item of invoice.items) { - // const { productId, processquantity } = item; + // Check if the product already exists in the PDStock for the user + const existingProduct = rdStock.products.find( + (p) => p.productid.toString() === productId.toString() + ); - // // Check if the product already exists in the PDStock for the user - // const existingProduct = pdStock.products.find( - // (p) => p.productid.toString() === productId.toString() - // ); - - // if (existingProduct) { - // // If the product exists, update the stock by adding the processquantity - // existingProduct.Stock += processquantity; - // } else { - // // If the product doesn't exist, add a new entry for the product - // pdStock.products.push({ - // productid: productId, - // Stock: processquantity, - // }); - // } - // } + if (existingProduct) { + // If the product exists, update the stock by adding the processquantity + existingProduct.Stock += processquantity; + } else { + // If the product doesn't exist, add a new entry for the product + rdStock.products.push({ + productid: productId, + Stock: processquantity, + }); + } + } // Save the updated PDStock - // await pdStock.save(); + await rdStock.save(); // Format the current date for display const formattedDate = formatDate(new Date()); diff --git a/resources/Stock/StockController.js b/resources/Stock/StockController.js index 2a4b9b1..7bfe281 100644 --- a/resources/Stock/StockController.js +++ b/resources/Stock/StockController.js @@ -199,4 +199,94 @@ export const getProductsAndStockByRD = async (req, res) => { message: "Error fetching products and stock", }); } +}; + +export const getProductsAndStockForRD = async (req, res) => { + try { + const { userId } = req.user._id; + + // Filtering criteria + const filter = {}; + if (req.query.name) { + filter.name = { + $regex: new RegExp(req.query.name, "i"), + }; + } + if (req.query.category) { + filter.category = mongoose.Types.ObjectId(req.query.category); + } + if (req.query.brand) { + filter.brand = mongoose.Types.ObjectId(req.query.brand); + } + + // Fetch user's RDStock data and products concurrently + const [userStock, products] = await Promise.all([ + RDStock.findOne({ userId: mongoose.Types.ObjectId(userId) }), + Product.aggregate([ + { $match: filter }, + { + $lookup: { + from: "categorymodels", + localField: "category", + foreignField: "_id", + as: "categoryDetails", + }, + }, + { + $lookup: { + from: "brandmodels", + localField: "brand", + foreignField: "_id", + as: "brandDetails", + }, + }, + { + $project: { + category: { $arrayElemAt: ["$categoryDetails.categoryName", 0] }, + brand: { $arrayElemAt: ["$brandDetails.brandName", 0] }, + GST: 1, + HSN_Code: 1, + SKU: 1, + addedBy: 1, + createdAt: 1, + description: 1, + image: 1, + name: 1, + price: 1, + product_Status: 1, + updatedAt: 1, + }, + }, + ]), + ]); + + // Create a stock map for easy lookup + const stockMap = {}; + if (userStock && userStock.products) { + userStock.products.forEach((product) => { + stockMap[product.productid.toString()] = product.Stock; + }); + } + + // Combine products with their respective stock + const productsWithStock = products.map((product) => ({ + ...product, + stock: stockMap[product._id.toString()] || 0, + })); + + // Get total count for pagination purposes + const total = await Product.countDocuments(filter); + + return res.status(200).json({ + success: true, + totalProducts: total, + products: productsWithStock, + }); + } catch (error) { + console.error("Error fetching products with stock:", error); + return res.status(500).json({ + success: false, + message: "Error fetching products and stock", + }); + } }; \ No newline at end of file diff --git a/resources/Stock/StockRoute.js b/resources/Stock/StockRoute.js index 4d014c6..e5e674b 100644 --- a/resources/Stock/StockRoute.js +++ b/resources/Stock/StockRoute.js @@ -1,6 +1,11 @@ import express from "express"; -import { getProductsAndStockByPD ,getProductsAndStockByRD} from "./StockController.js"; +import { + getProductsAndStockByPD, + getProductsAndStockByRD, + getProductsAndStockForRD, +} from "./StockController.js"; import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; +import {isAuthenticatedRD} from "../../middlewares/rdAuth.js"; const router = express.Router(); router.get( @@ -15,4 +20,5 @@ router.get( authorizeRoles("admin"), getProductsAndStockByRD ); +router.get("/stock", isAuthenticatedRD, getProductsAndStockForRD); export default router; From 5fe1da6c98d2cbdd27044e5eb80da23e3bbddc9c Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Thu, 10 Oct 2024 11:53:55 +0530 Subject: [PATCH 2/6] get distributor in sorted format --- resources/Inventory/InventoryController.js | 29 ++++++++++++++-------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/resources/Inventory/InventoryController.js b/resources/Inventory/InventoryController.js index 7e104bc..fd1fc7b 100644 --- a/resources/Inventory/InventoryController.js +++ b/resources/Inventory/InventoryController.js @@ -13,12 +13,14 @@ export const addInventory = async (req, res) => { const { products, addedFor, addedForId } = req.body; const userId = req.user._id; const userType = req.userType; - + if (addedFor === "RetailDistributor") { // Find the RetailDistributor stock const rdStock = await RDStock.findOne({ userId: addedForId }); if (!rdStock) { - return res.status(404).json({ error: "Stock not available for the RetailDistributor" }); + return res + .status(404) + .json({ error: "Stock not available for the RetailDistributor" }); } // Array to hold product names that have issues @@ -29,7 +31,9 @@ export const addInventory = async (req, res) => { const { productid, Sale, ProductName } = product; // Find product in the RetailDistributor's stock - const rdProduct = rdStock.products.find(p => p.productid.toString() === productid.toString()); + const rdProduct = rdStock.products.find( + (p) => p.productid.toString() === productid.toString() + ); if (rdProduct) { // Check if sales quantity is less than or equal to the stock @@ -50,7 +54,9 @@ export const addInventory = async (req, res) => { if (invalidProducts.length > 0) { return res.status(400).json({ success: false, - message: `Insufficient stock or product not found for: ${invalidProducts.join(", ")}`, + message: `Insufficient stock or product not found for: ${invalidProducts.join( + ", " + )}`, }); } @@ -73,7 +79,6 @@ export const addInventory = async (req, res) => { message: "Inventory added successfully", data: newInventory, }); - } catch (error) { res.status(500).json({ success: false, message: "Server error", error }); } @@ -88,7 +93,7 @@ export const getDistributors = async (req, res) => { return res.status(400).json({ message: "Invalid distributor type" }); } let filter = { role: "principal-Distributor" }; - let query={}; + let query = {}; // Check the user type and adjust the filter accordingly if (req.userType === "SalesCoOrdinator") { // If userType is "SalesCoOrdinator", filter by req.user.mappedBy @@ -103,7 +108,9 @@ export const getDistributors = async (req, res) => { // console.log("type",type); if (type === "PrincipalDistributor") { // Fetch all PrincipalDistributors - const principalDistributors = await User.find(filter); + const principalDistributors = await User.find(filter).sort({ + createdAt: -1, + }); // console.log("principalDistributors",principalDistributors); // Map each PrincipalDistributor to include their ShippingAddress distributors = await Promise.all( @@ -119,7 +126,9 @@ export const getDistributors = async (req, res) => { ); } else { // For RetailDistributor, fetch approved KYC documents - distributors = await RetailDistributor.find(query).populate("kyc"); + distributors = await RetailDistributor.find(query) + .populate("kyc") + .sort({ createdAt: -1 }); } res.status(200).json(distributors); @@ -148,12 +157,12 @@ export const getAllInventories = async (req, res) => { $lte: new Date(end).setDate(end.getDate() + 1), }; } - } else if (startDate && endDate==='') { + } else if (startDate && endDate === "") { query.createdAt = { $gte: new Date(startDate), $lte: new Date(), }; - } else if (endDate && startDate==='') { + } else if (endDate && startDate === "") { query.createdAt = { $lte: new Date(endDate), }; From 632d05f6565c9335d41281a92d5a8975bd84d441 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Fri, 11 Oct 2024 10:16:21 +0530 Subject: [PATCH 3/6] fixing --- resources/Inventory/InventoryModel.js | 1 - resources/Stock/StockController.js | 92 +-------------------------- resources/Stock/StockRoute.js | 3 +- 3 files changed, 2 insertions(+), 94 deletions(-) diff --git a/resources/Inventory/InventoryModel.js b/resources/Inventory/InventoryModel.js index d8003e3..b8610d7 100644 --- a/resources/Inventory/InventoryModel.js +++ b/resources/Inventory/InventoryModel.js @@ -30,7 +30,6 @@ const InventorySchema = new mongoose.Schema( { uniqueId: { type: String, - required: true, unique: true, }, userId: { diff --git a/resources/Stock/StockController.js b/resources/Stock/StockController.js index 7bfe281..ca45b65 100644 --- a/resources/Stock/StockController.js +++ b/resources/Stock/StockController.js @@ -104,7 +104,7 @@ export const getProductsAndStockByPD = async (req, res) => { export const getProductsAndStockByRD = async (req, res) => { try { - const { userId } = req.params; + const userId = req.params.userId || req.user._id; // Pagination parameters const PAGE_SIZE = parseInt(req.query.show) || 10; @@ -200,93 +200,3 @@ export const getProductsAndStockByRD = async (req, res) => { }); } }; - -export const getProductsAndStockForRD = async (req, res) => { - try { - const { userId } = req.user._id; - - // Filtering criteria - const filter = {}; - if (req.query.name) { - filter.name = { - $regex: new RegExp(req.query.name, "i"), - }; - } - if (req.query.category) { - filter.category = mongoose.Types.ObjectId(req.query.category); - } - if (req.query.brand) { - filter.brand = mongoose.Types.ObjectId(req.query.brand); - } - - // Fetch user's RDStock data and products concurrently - const [userStock, products] = await Promise.all([ - RDStock.findOne({ userId: mongoose.Types.ObjectId(userId) }), - Product.aggregate([ - { $match: filter }, - { - $lookup: { - from: "categorymodels", - localField: "category", - foreignField: "_id", - as: "categoryDetails", - }, - }, - { - $lookup: { - from: "brandmodels", - localField: "brand", - foreignField: "_id", - as: "brandDetails", - }, - }, - { - $project: { - category: { $arrayElemAt: ["$categoryDetails.categoryName", 0] }, - brand: { $arrayElemAt: ["$brandDetails.brandName", 0] }, - GST: 1, - HSN_Code: 1, - SKU: 1, - addedBy: 1, - createdAt: 1, - description: 1, - image: 1, - name: 1, - price: 1, - product_Status: 1, - updatedAt: 1, - }, - }, - ]), - ]); - - // Create a stock map for easy lookup - const stockMap = {}; - if (userStock && userStock.products) { - userStock.products.forEach((product) => { - stockMap[product.productid.toString()] = product.Stock; - }); - } - - // Combine products with their respective stock - const productsWithStock = products.map((product) => ({ - ...product, - stock: stockMap[product._id.toString()] || 0, - })); - - // Get total count for pagination purposes - const total = await Product.countDocuments(filter); - - return res.status(200).json({ - success: true, - totalProducts: total, - products: productsWithStock, - }); - } catch (error) { - console.error("Error fetching products with stock:", error); - return res.status(500).json({ - success: false, - message: "Error fetching products and stock", - }); - } -}; \ No newline at end of file diff --git a/resources/Stock/StockRoute.js b/resources/Stock/StockRoute.js index e5e674b..4e21b56 100644 --- a/resources/Stock/StockRoute.js +++ b/resources/Stock/StockRoute.js @@ -2,7 +2,6 @@ import express from "express"; import { getProductsAndStockByPD, getProductsAndStockByRD, - getProductsAndStockForRD, } from "./StockController.js"; import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js"; import {isAuthenticatedRD} from "../../middlewares/rdAuth.js"; @@ -20,5 +19,5 @@ router.get( authorizeRoles("admin"), getProductsAndStockByRD ); -router.get("/stock", isAuthenticatedRD, getProductsAndStockForRD); +router.get("/stock", isAuthenticatedRD, getProductsAndStockByRD); export default router; From ab65a0b3c0aef1d42df972c53d3e80bacb29d30b Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Fri, 11 Oct 2024 10:20:49 +0530 Subject: [PATCH 4/6] fixing --- resources/Inventory/InventoryController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/Inventory/InventoryController.js b/resources/Inventory/InventoryController.js index fd1fc7b..596cb73 100644 --- a/resources/Inventory/InventoryController.js +++ b/resources/Inventory/InventoryController.js @@ -19,7 +19,7 @@ export const addInventory = async (req, res) => { const rdStock = await RDStock.findOne({ userId: addedForId }); if (!rdStock) { return res - .status(404) + .status(400) .json({ error: "Stock not available for the RetailDistributor" }); } From 291eb172387565214fb8d35fe0d721899257a32f Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Fri, 11 Oct 2024 11:02:19 +0530 Subject: [PATCH 5/6] fixing --- resources/Inventory/InventoryModel.js | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/Inventory/InventoryModel.js b/resources/Inventory/InventoryModel.js index b8610d7..59d890c 100644 --- a/resources/Inventory/InventoryModel.js +++ b/resources/Inventory/InventoryModel.js @@ -1,4 +1,5 @@ import mongoose from "mongoose"; +import crypto from 'crypto'; // Define Product record schema const ProductRecordSchema = new mongoose.Schema({ From 2c6e38094ca78c0f414825f2375794237913162a Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Fri, 11 Oct 2024 11:42:21 +0530 Subject: [PATCH 6/6] fixing --- resources/Inventory/InventoryController.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/resources/Inventory/InventoryController.js b/resources/Inventory/InventoryController.js index 596cb73..aff5e9c 100644 --- a/resources/Inventory/InventoryController.js +++ b/resources/Inventory/InventoryController.js @@ -1,10 +1,8 @@ import { Inventory } from "../Inventory/InventoryModel.js"; import User from "../user/userModel.js"; -import { KYC } from "../KYC/KycModel.js"; import ShippingAddress from "../ShippingAddresses/ShippingAddressModel.js"; import TerritoryManager from "../TerritoryManagers/TerritoryManagerModel.js"; import SalesCoordinator from "../SalesCoOrdinators/SalesCoOrdinatorModel.js"; -import crypto from "crypto"; import RetailDistributor from "../RetailDistributor/RetailDistributorModel.js"; import { RDStock } from "../Stock/RdStockModel.js"; // Add inventory data @@ -125,7 +123,7 @@ export const getDistributors = async (req, res) => { }) ); } else { - // For RetailDistributor, fetch approved KYC documents + // For RetailDistributor distributors = await RetailDistributor.find(query) .populate("kyc") .sort({ createdAt: -1 }); @@ -197,8 +195,8 @@ export const getAllInventories = async (req, res) => { addedForData.shippingAddress?.tradeName?.toLowerCase() || ""; } } else if (inventory.addedFor === "RetailDistributor") { - addedForData = await KYC.findById(inventory.addedForId); - tradeName = addedForData?.trade_name?.toLowerCase() || ""; + addedForData = await RetailDistributor.findById(inventory.addedForId).populate("kyc"); + tradeName = addedForData?.kyc?.trade_name?.toLowerCase() || ""; } return { @@ -275,7 +273,7 @@ export const getSingleInventory = async (req, res) => { shippingAddress, }; } else if (inventory.addedFor === "RetailDistributor") { - addedForData = await KYC.findById(inventory.addedForId); + addedForData = await RetailDistributor.findById(inventory.addedForId).populate("kyc"); } res.status(200).json({