diff --git a/app.js b/app.js
index 3b306e0..63a657f 100644
--- a/app.js
+++ b/app.js
@@ -132,6 +132,7 @@ app.use(
//auth
import user from "./resources/user/userRoute.js";
import ProductRouter from "./resources/Products/ProductRoute.js";
+import ProductManualRouter from "./resources/ProductMannual/ProductManualRoute.js";
//Businesses
// import BusinessRoute from "./resources/Businesses/BusinessRoute.js";
@@ -199,6 +200,8 @@ app.use("/api/v1", user);
//Product
app.use("/api", ProductRouter);
+//Product Manual
+app.use("/api/productmanual", ProductManualRouter);
//businesses
// app.use("/api/businesses", BusinessRoute);
// Design
diff --git a/public/uploads/Add-PD.xlsx b/public/uploads/Add-PD.xlsx
index b7a1ef1..21ce549 100644
Binary files a/public/uploads/Add-PD.xlsx and b/public/uploads/Add-PD.xlsx differ
diff --git a/resources/ProductMannual/ProductManualController.js b/resources/ProductMannual/ProductManualController.js
new file mode 100644
index 0000000..870273f
--- /dev/null
+++ b/resources/ProductMannual/ProductManualController.js
@@ -0,0 +1,181 @@
+import ProductManual from "./ProductManualModel.js";
+import cloudinary from "../../Utils/cloudinary.js";
+
+// Create a new product manual
+export const createProductManual = async (req, res) => {
+ const { title } = req.body;
+
+ try {
+ let productManualDetails;
+
+ // Check if a file is provided
+ if (req.files && req.files.product_manual) {
+ const pdfFile = req.files.product_manual;
+ const result = await cloudinary.v2.uploader.upload(pdfFile.tempFilePath, {
+ folder: "chemiNova/ProductManuals",
+ resource_type: "raw", // For PDF or other non-image files
+ });
+
+ productManualDetails = {
+ public_id: result.public_id,
+ url: result.secure_url,
+ };
+ }
+
+ // Create the product manual
+ const productManual = await ProductManual.create({
+ title,
+ product_manual: productManualDetails,
+ });
+
+ res.status(201).json({
+ success: true,
+ productManual,
+ message: "Product manual created successfully",
+ });
+ } catch (error) {
+ console.error("Error creating product manual:", error);
+ res.status(500).json({
+ success: false,
+ message: error.message || "Internal server error",
+ });
+ }
+};
+
+// Get all product manuals
+export const getAllProductManuals = async (req, res) => {
+ try {
+ const productManuals = await ProductManual.find();
+ res.status(200).json({
+ success: true,
+ productManuals,
+ });
+ } catch (error) {
+ console.error("Error fetching product manuals:", error);
+ res.status(500).json({
+ success: false,
+ message: error.message || "Internal server error",
+ });
+ }
+};
+
+// Get a single product manual by ID
+export const getSingleProductManual = async (req, res) => {
+ const { id } = req.params;
+
+ try {
+ const productManual = await ProductManual.findById(id);
+
+ if (!productManual) {
+ return res.status(404).json({
+ success: false,
+ message: "Product manual not found",
+ });
+ }
+
+ res.status(200).json({
+ success: true,
+ productManual,
+ });
+ } catch (error) {
+ console.error("Error fetching product manual:", error);
+ res.status(500).json({
+ success: false,
+ message: error.message || "Internal server error",
+ });
+ }
+};
+
+// Update a product manual
+export const updateProductManual = async (req, res) => {
+ const { id } = req.params;
+ const { title } = req.body;
+
+ try {
+ const productManual = await ProductManual.findById(id);
+
+ if (!productManual) {
+ return res.status(404).json({
+ success: false,
+ message: "Product manual not found",
+ });
+ }
+
+ // Check if a new file is provided
+ if (req.files && req.files.product_manual) {
+ // Delete the old file from Cloudinary
+ if (productManual.product_manual.public_id) {
+ await cloudinary.v2.uploader.destroy(productManual.product_manual.public_id, {
+ resource_type: "raw",
+ });
+ }
+
+ // Upload the new file to Cloudinary
+ const pdfFile = req.files.product_manual;
+ const result = await cloudinary.v2.uploader.upload(pdfFile.tempFilePath, {
+ folder: "chemiNova/ProductManuals",
+ resource_type: "raw",
+ });
+
+ // Update the product manual details
+ productManual.product_manual = {
+ public_id: result.public_id,
+ url: result.secure_url,
+ };
+ }
+
+ // Update the title
+ productManual.title = title || productManual.title;
+
+ await productManual.save();
+
+ res.status(200).json({
+ success: true,
+ productManual,
+ message: "Product manual updated successfully",
+ });
+ } catch (error) {
+ console.error("Error updating product manual:", error);
+ res.status(500).json({
+ success: false,
+ message: error.message || "Internal server error",
+ });
+ }
+};
+
+// Delete a product manual
+export const deleteProductManual = async (req, res) => {
+ const { id } = req.params;
+
+ try {
+ const productManual = await ProductManual.findById(id);
+
+ if (!productManual) {
+ return res.status(404).json({
+ success: false,
+ message: "Product manual not found",
+ });
+ }
+
+ // Delete the file from Cloudinary
+ if (productManual.product_manual.public_id) {
+ await cloudinary.v2.uploader.destroy(productManual.product_manual.public_id, {
+ resource_type: "raw",
+ });
+ }
+
+ // Delete the product manual from the database
+ await ProductManual.findByIdAndDelete(id);
+
+ res.status(200).json({
+ success: true,
+ message: "Product manual deleted successfully",
+ });
+ } catch (error) {
+ console.error("Error deleting product manual:", error);
+ res.status(500).json({
+ success: false,
+ message: error.message || "Internal server error",
+ });
+ }
+};
\ No newline at end of file
diff --git a/resources/ProductMannual/ProductManualModel.js b/resources/ProductMannual/ProductManualModel.js
new file mode 100644
index 0000000..a21de31
--- /dev/null
+++ b/resources/ProductMannual/ProductManualModel.js
@@ -0,0 +1,25 @@
+import mongoose from "mongoose";
+
+const ProductManualSchema = new mongoose.Schema(
+ {
+ title: {
+ type: String,
+ required: true,
+ },
+ product_manual: {
+ public_id: {
+ type: String,
+ required: true,
+ },
+ url: {
+ type: String,
+ required: true,
+ },
+ },
+ },
+ { timestamps: true }
+);
+
+const ProductManual = mongoose.model("ProductManual", ProductManualSchema);
+
+export default ProductManual;
diff --git a/resources/ProductMannual/ProductManualRoute.js b/resources/ProductMannual/ProductManualRoute.js
new file mode 100644
index 0000000..d48d6c3
--- /dev/null
+++ b/resources/ProductMannual/ProductManualRoute.js
@@ -0,0 +1,90 @@
+import express from "express";
+import {
+ createProductManual,
+ getAllProductManuals,
+ getSingleProductManual,
+ updateProductManual,
+ deleteProductManual,
+} from "./ProductManualController.js";
+import {
+ isAuthenticatedUser,
+ authorizeRoles,
+} from "../../middlewares/auth.js";
+import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
+import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
+
+const router = express.Router();
+
+// Route for creating a product manual (Only Admin can create)
+router
+ .route("/create")
+ .post(isAuthenticatedUser, authorizeRoles("admin"), createProductManual);
+
+// Route for getting all product manuals (accessible to Sales Coordinator, Territory Manager, and Admin)
+router.route("/").get(
+ (req, res, next) => {
+ // Allow access if the user is a sales coordinator, territory manager, or admin
+ isAuthenticatedSalesCoOrdinator(req, res, (err) => {
+ if (err) {
+ isAuthenticatedTerritoryManager(req, res, (err) => {
+ if (err) {
+ isAuthenticatedUser(req, res, (err) => {
+ if (err || !["admin"].includes(req.user.role)) {
+ return res.status(403).json({
+ success: false,
+ message: "Access denied. Unauthorized role.",
+ });
+ }
+ next();
+ });
+ } else {
+ next();
+ }
+ });
+ } else {
+ next();
+ }
+ });
+ },
+ getAllProductManuals
+);
+
+// Route for getting a single product manual by ID (accessible to Sales Coordinator, Territory Manager, and Admin)
+router.route("/:id").get(
+ (req, res, next) => {
+ // Allow access if the user is a sales coordinator, territory manager, or admin
+ isAuthenticatedSalesCoOrdinator(req, res, (err) => {
+ if (err) {
+ isAuthenticatedTerritoryManager(req, res, (err) => {
+ if (err) {
+ isAuthenticatedUser(req, res, (err) => {
+ if (err || !["admin"].includes(req.user.role)) {
+ return res.status(403).json({
+ success: false,
+ message: "Access denied. Unauthorized role.",
+ });
+ }
+ next();
+ });
+ } else {
+ next();
+ }
+ });
+ } else {
+ next();
+ }
+ });
+ },
+ getSingleProductManual
+);
+// Route to update a product manual by ID
+router
+ .route("/update/:id")
+ .put(isAuthenticatedUser, authorizeRoles("admin"), updateProductManual);
+
+// Route to delete a product manual by ID
+router
+ .route("/delete/:id")
+ .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteProductManual);
+export default router;
+// /api/productmanual
\ No newline at end of file
diff --git a/resources/Task/TaskController.js b/resources/Task/TaskController.js
index bc382f0..e6ca2a4 100644
--- a/resources/Task/TaskController.js
+++ b/resources/Task/TaskController.js
@@ -12,6 +12,7 @@ export const assignTask = async (req, res) => {
taskAssignedTo,
addedFor,
addedForId,
+ tradename,
} = req.body;
const currentYear = new Date().getFullYear().toString().slice(-2);
@@ -29,6 +30,7 @@ export const assignTask = async (req, res) => {
taskAssignedBy: req.user._id,
addedFor,
addedForId,
+ tradename,
});
res.status(201).json({
@@ -73,8 +75,33 @@ export const getTasksByStatus = async (req, res) => {
});
}
};
+export const getTasksbytask = async (req, res) => {
+ try {
+ const { task } = req.params;
+ // Validate the provided status
+ if (!["Visit RD/PD", "Update Sales Data", "Update Inventory Data", "Collect KYC"].includes(task)) {
+ return res.status(400).json({
+ success: false,
+ message: "Invalid task type provided.",
+ });
+ }
+ const tasks = await Task.find({
+ taskAssignedTo: req.user._id,
+ task: task,
+ }).sort({ createdAt: -1 });
+ res.status(200).json({
+ success: true,
+ tasks,
+ });
+ } catch (error) {
+ res.status(500).json({
+ success: false,
+ message: error.message,
+ });
+ }
+};
export const updateTaskStatus = async (req, res) => {
try {
const { taskId } = req.params;
diff --git a/resources/Task/TaskModel.js b/resources/Task/TaskModel.js
index 988d8b5..0cf0be3 100644
--- a/resources/Task/TaskModel.js
+++ b/resources/Task/TaskModel.js
@@ -12,12 +12,12 @@ const TaskSchema = new mongoose.Schema(
task: {
type: String,
required: true,
- enum: ["Visit Retailers", "Update Sales Data", "Update Inventory Data", "Collect KYC"],
+ enum: ["Visit RD/PD", "Update Sales Data", "Update Inventory Data", "Collect KYC"],
},
note: {
type: String,
required: function () {
- return this.task === "Collect KYC";
+ return this.task === "Collect KYC" || this.task === "Visit RD/PD";
},
},
taskStatus: {
@@ -60,6 +60,12 @@ const TaskSchema = new mongoose.Schema(
return this.task === "Update Inventory Data";
},
},
+ tradename: {
+ type: String,
+ required: function () {
+ return this.task === "Update Inventory Data";
+ },
+ },
},
{ timestamps: true }
);
diff --git a/resources/Task/TaskRoute.js b/resources/Task/TaskRoute.js
index c0eb6ab..a2b639e 100644
--- a/resources/Task/TaskRoute.js
+++ b/resources/Task/TaskRoute.js
@@ -3,6 +3,7 @@ import {
assignTask,
getTasksByStatus,
updateTaskStatus,
+ getTasksbytask,
} from "./TaskController.js";
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
@@ -22,7 +23,11 @@ router.get(
isAuthenticatedSalesCoOrdinator,
getTasksByStatus
);
-
+router.get(
+ "/task/type/:task",
+ isAuthenticatedSalesCoOrdinator,
+ getTasksbytask
+);
// Route to update task status
router.put(
"/update-task-status/:taskId",
diff --git a/resources/user/userController.js b/resources/user/userController.js
index 620b722..2e2247d 100644
--- a/resources/user/userController.js
+++ b/resources/user/userController.js
@@ -563,7 +563,19 @@ export const uploadPrincipaldistributors = async (req, res) => {
uniqueId: item.uniqueId,
});
await distributor.save();
-
+ await sendEmail({
+ to: distributor.email,
+ from: process.env.SEND_EMAIL_FROM,
+ subject: `Cheminova Account Created`,
+ html: `
+ Your Principal Distributor Account is created successfully.
+
Name: ${distributor.name}
+
Mobile Number: ${distributor.phone}
+
Password: ${password}
+ Click here to login
+ If you have not requested this email, please ignore it.
+ `,
+ });
// Now create the address for the new user
const addressData = {
street: item.street,