conflict resolved
This commit is contained in:
commit
afbc17cee7
6
app.js
6
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";
|
||||
|
||||
@ -163,7 +164,6 @@ import CurrencyRoute from "./resources/Currency/CurrencyRoute.js";
|
||||
|
||||
import ConfigRouter from "./resources/setting/Configration/Config_routes.js";
|
||||
|
||||
import TaxRouter from "./resources/Tax/tax_routes.js";
|
||||
//specialties
|
||||
import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js";
|
||||
import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js";
|
||||
@ -200,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
|
||||
@ -239,8 +241,6 @@ app.use("/api/language", LanguageRoute);
|
||||
//Purpose
|
||||
app.use("/api/purpose", PurposeRoute);
|
||||
app.use("/api/business", orderRoute);
|
||||
//Tax
|
||||
app.use("/api/tax", TaxRouter);
|
||||
//Currency Route
|
||||
app.use("/api/currency", CurrencyRoute);
|
||||
//config
|
||||
|
56
middlewares/generalAuth.js
Normal file
56
middlewares/generalAuth.js
Normal file
@ -0,0 +1,56 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import SalesCoOrdinator from "../resources/SalesCoOrdinators/SalesCoOrdinatorModel.js";
|
||||
import TerritoryManager from "../resources/TerritoryManagers/TerritoryManagerModel.js";
|
||||
|
||||
export const isAuthenticated_SC_TM = async (req, res, next) => {
|
||||
try {
|
||||
if (!req.headers.authorization) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Please login to access this resource",
|
||||
});
|
||||
}
|
||||
|
||||
const getToken = req.headers.authorization;
|
||||
const token = getToken.slice(7);
|
||||
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
if (!decoded) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Incorrect token",
|
||||
});
|
||||
}
|
||||
|
||||
let user = await SalesCoOrdinator.findById(decoded.id);
|
||||
if (user) {
|
||||
req.user = user;
|
||||
req.userType = "SalesCoOrdinator";
|
||||
} else {
|
||||
user = await TerritoryManager.findById(decoded.id);
|
||||
if (user) {
|
||||
req.user = user;
|
||||
req.userType = "TerritoryManager";
|
||||
}
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Unauthorized",
|
||||
});
|
||||
}
|
||||
|
||||
return next();
|
||||
} catch (error) {
|
||||
if (error.name === "TokenExpiredError") {
|
||||
return res.status(401).json({ message: "Token has expired." });
|
||||
} else if (error.name === "JsonWebTokenError") {
|
||||
return res.status(401).json({ message: "Invalid token." });
|
||||
} else {
|
||||
return res.status(500).json({
|
||||
message: "An internal error occurred while verifying the token.",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
22
package-lock.json
generated
22
package-lock.json
generated
@ -28,6 +28,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-storage-cloudinary": "^4.0.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.4",
|
||||
"nodemon": "^3.0.1",
|
||||
"pm2": "^5.3.1",
|
||||
@ -4825,6 +4826,27 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-cron": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz",
|
||||
"integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"uuid": "8.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-cron/node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
|
@ -33,6 +33,7 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer-storage-cloudinary": "^4.0.0",
|
||||
"nanoid": "^5.0.7",
|
||||
"node-cron": "^3.0.3",
|
||||
"nodemailer": "^6.9.4",
|
||||
"nodemon": "^3.0.1",
|
||||
"pm2": "^5.3.1",
|
||||
|
BIN
public/temp/tmp-1-1724746788677
Normal file
BIN
public/temp/tmp-1-1724746788677
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724746833987
Normal file
BIN
public/temp/tmp-1-1724746833987
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724747670176
Normal file
6006
public/temp/tmp-1-1724747670176
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724748558089
Normal file
6006
public/temp/tmp-1-1724748558089
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724749456516
Normal file
6006
public/temp/tmp-1-1724749456516
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724749570807
Normal file
6006
public/temp/tmp-1-1724749570807
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724750263481
Normal file
6006
public/temp/tmp-1-1724750263481
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724750281823
Normal file
6006
public/temp/tmp-1-1724750281823
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724750304440
Normal file
6006
public/temp/tmp-1-1724750304440
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-1-1724750599576
Normal file
BIN
public/temp/tmp-1-1724750599576
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724757542930
Normal file
6006
public/temp/tmp-1-1724757542930
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-1-1724760618075
Normal file
BIN
public/temp/tmp-1-1724760618075
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724760822218
Normal file
6006
public/temp/tmp-1-1724760822218
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-1-1724760880967
Normal file
BIN
public/temp/tmp-1-1724760880967
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724761063195
Normal file
6006
public/temp/tmp-1-1724761063195
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-1-1724761125087
Normal file
BIN
public/temp/tmp-1-1724761125087
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724761165388
Normal file
BIN
public/temp/tmp-1-1724761165388
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724761216665
Normal file
BIN
public/temp/tmp-1-1724761216665
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724761272232
Normal file
BIN
public/temp/tmp-1-1724761272232
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724761386239
Normal file
BIN
public/temp/tmp-1-1724761386239
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724762193202
Normal file
BIN
public/temp/tmp-1-1724762193202
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724764554211
Normal file
BIN
public/temp/tmp-1-1724764554211
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724764884872
Normal file
BIN
public/temp/tmp-1-1724764884872
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724765240957
Normal file
BIN
public/temp/tmp-1-1724765240957
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724765320856
Normal file
BIN
public/temp/tmp-1-1724765320856
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724765615231
Normal file
6006
public/temp/tmp-1-1724765615231
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724766268456
Normal file
6006
public/temp/tmp-1-1724766268456
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724766379146
Normal file
6006
public/temp/tmp-1-1724766379146
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-1-1724766492550
Normal file
BIN
public/temp/tmp-1-1724766492550
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724767466175
Normal file
6006
public/temp/tmp-1-1724767466175
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-1-1724767958384
Normal file
BIN
public/temp/tmp-1-1724767958384
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724768128492
Normal file
6006
public/temp/tmp-1-1724768128492
Normal file
File diff suppressed because it is too large
Load Diff
12933
public/temp/tmp-1-1724768256080
Normal file
12933
public/temp/tmp-1-1724768256080
Normal file
File diff suppressed because one or more lines are too long
BIN
public/temp/tmp-1-1724768571993
Normal file
BIN
public/temp/tmp-1-1724768571993
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724769239158
Normal file
BIN
public/temp/tmp-1-1724769239158
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724770285115
Normal file
BIN
public/temp/tmp-1-1724770285115
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724773346278
Normal file
BIN
public/temp/tmp-1-1724773346278
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724773410425
Normal file
BIN
public/temp/tmp-1-1724773410425
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724774155797
Normal file
BIN
public/temp/tmp-1-1724774155797
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724774178540
Normal file
BIN
public/temp/tmp-1-1724774178540
Normal file
Binary file not shown.
BIN
public/temp/tmp-1-1724774207152
Normal file
BIN
public/temp/tmp-1-1724774207152
Normal file
Binary file not shown.
6006
public/temp/tmp-1-1724774414898
Normal file
6006
public/temp/tmp-1-1724774414898
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-1-1724774467807
Normal file
6006
public/temp/tmp-1-1724774467807
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-1-1724818772218
Normal file
BIN
public/temp/tmp-1-1724818772218
Normal file
Binary file not shown.
BIN
public/temp/tmp-2-1724746910741
Normal file
BIN
public/temp/tmp-2-1724746910741
Normal file
Binary file not shown.
6006
public/temp/tmp-2-1724748970338
Normal file
6006
public/temp/tmp-2-1724748970338
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-2-1724760775832
Normal file
6006
public/temp/tmp-2-1724760775832
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-2-1724760927098
Normal file
6006
public/temp/tmp-2-1724760927098
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-2-1724766506639
Normal file
BIN
public/temp/tmp-2-1724766506639
Normal file
Binary file not shown.
BIN
public/temp/tmp-2-1724774608644
Normal file
BIN
public/temp/tmp-2-1724774608644
Normal file
Binary file not shown.
BIN
public/temp/tmp-2-1724818809400
Normal file
BIN
public/temp/tmp-2-1724818809400
Normal file
Binary file not shown.
6006
public/temp/tmp-3-1724746924114
Normal file
6006
public/temp/tmp-3-1724746924114
Normal file
File diff suppressed because it is too large
Load Diff
6006
public/temp/tmp-3-1724760961910
Normal file
6006
public/temp/tmp-3-1724760961910
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/temp/tmp-3-1724766538042
Normal file
BIN
public/temp/tmp-3-1724766538042
Normal file
Binary file not shown.
BIN
public/temp/tmp-3-1724819002571
Normal file
BIN
public/temp/tmp-3-1724819002571
Normal file
Binary file not shown.
BIN
public/temp/tmp-4-1724819035891
Normal file
BIN
public/temp/tmp-4-1724819035891
Normal file
Binary file not shown.
BIN
public/uploads/Add-PD.xlsx
Normal file
BIN
public/uploads/Add-PD.xlsx
Normal file
Binary file not shown.
232
resources/ProductMannual/ProductManualController.js
Normal file
232
resources/ProductMannual/ProductManualController.js
Normal file
@ -0,0 +1,232 @@
|
||||
import ProductManual from "./ProductManualModel.js";
|
||||
import cloudinary from "../../Utils/cloudinary.js";
|
||||
import path from "path";
|
||||
// Create a new product manual
|
||||
export const createProductManual = async (req, res) => {
|
||||
const { title } = req.body;
|
||||
const pdfFile = req.files ? req.files.pdfFile : null;
|
||||
|
||||
// Check if title is provided
|
||||
if (!title) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Title is required",
|
||||
});
|
||||
}
|
||||
|
||||
// Check if a file is provided
|
||||
if (!pdfFile) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "File is required",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
let productManualDetails = null;
|
||||
let filename = "";
|
||||
|
||||
// Upload the file to Cloudinary
|
||||
if (pdfFile) {
|
||||
filename = pdfFile.name;
|
||||
// console.log(pdfFile);
|
||||
const originalFilename = path.basename(pdfFile.name, path.extname(pdfFile.name));
|
||||
|
||||
const result = await cloudinary.v2.uploader.upload(pdfFile.tempFilePath, {
|
||||
folder: "chemiNova/ProductManuals",
|
||||
public_id: originalFilename,
|
||||
});
|
||||
// console.log(result);
|
||||
productManualDetails = {
|
||||
public_id: result.public_id,
|
||||
url: result.secure_url,
|
||||
filename: filename,
|
||||
};
|
||||
}
|
||||
|
||||
// Create the product manual
|
||||
const productManual = await ProductManual.create({
|
||||
title,
|
||||
product_manual: productManualDetails || {}, // Ensure product_manual is an empty object if no file is provided
|
||||
});
|
||||
|
||||
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 PAGE_SIZE = parseInt(req.query.show) || 10;
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const skip = (page - 1) * PAGE_SIZE;
|
||||
let filter = {};
|
||||
if (req.query.title) {
|
||||
filter.title = {
|
||||
$regex: new RegExp(req.query.title, "i"),
|
||||
};
|
||||
}
|
||||
|
||||
// Fetch total count of documents
|
||||
const total = await ProductManual.countDocuments(filter);
|
||||
|
||||
// Fetch paginated data
|
||||
const productManuals = await ProductManual.find(filter)
|
||||
.limit(PAGE_SIZE)
|
||||
.skip(skip)
|
||||
.sort({ createdAt: -1 })
|
||||
.exec();
|
||||
|
||||
// Send response
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
productManuals,
|
||||
total,
|
||||
});
|
||||
} 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",
|
||||
});
|
||||
}
|
||||
let filename = "";
|
||||
// Check if a new file is provided
|
||||
if (req.files && req.files.pdfFile) {
|
||||
// Delete the old file from Cloudinary
|
||||
if (productManual.product_manual.public_id) {
|
||||
await cloudinary.v2.uploader.destroy(
|
||||
productManual.product_manual.public_id,
|
||||
{
|
||||
folder: "chemiNova/ProductManuals",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Upload the new file to Cloudinary
|
||||
const pdfFile = req.files.pdfFile;
|
||||
// console.log(pdfFile);
|
||||
filename = pdfFile.name;
|
||||
const originalFilename = path.basename(pdfFile.name, path.extname(pdfFile.name));
|
||||
|
||||
const result = await cloudinary.v2.uploader.upload(pdfFile.tempFilePath, {
|
||||
folder: "chemiNova/ProductManuals",
|
||||
public_id: originalFilename,
|
||||
});
|
||||
// console.log(result);
|
||||
// Update the product manual details
|
||||
productManual.product_manual = {
|
||||
public_id: result.public_id,
|
||||
url: result.secure_url,
|
||||
filename: filename,
|
||||
};
|
||||
}
|
||||
|
||||
// Update the title
|
||||
productManual.title = title || productManual.title;
|
||||
// console.log(productManual);
|
||||
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,
|
||||
{
|
||||
folder: "chemiNova/ProductManuals",
|
||||
});
|
||||
}
|
||||
|
||||
// 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",
|
||||
});
|
||||
}
|
||||
};
|
26
resources/ProductMannual/ProductManualModel.js
Normal file
26
resources/ProductMannual/ProductManualModel.js
Normal file
@ -0,0 +1,26 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
const ProductManualSchema = new mongoose.Schema(
|
||||
{
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
product_manual: {
|
||||
public_id: {
|
||||
type: String,
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
},
|
||||
filename: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
const ProductManual = mongoose.model('ProductManual', ProductManualSchema);
|
||||
|
||||
export default ProductManual;
|
39
resources/ProductMannual/ProductManualRoute.js
Normal file
39
resources/ProductMannual/ProductManualRoute.js
Normal file
@ -0,0 +1,39 @@
|
||||
import express from "express";
|
||||
import {
|
||||
createProductManual,
|
||||
getAllProductManuals,
|
||||
getSingleProductManual,
|
||||
updateProductManual,
|
||||
deleteProductManual,
|
||||
} from "./ProductManualController.js";
|
||||
import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js";
|
||||
import { isAuthenticated_SC_TM } from "../../middlewares/generalAuth.js";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Route for creating a product manual (Only Admin can create)
|
||||
router
|
||||
.route("/create")
|
||||
.post(isAuthenticatedUser, authorizeRoles("admin"), createProductManual);
|
||||
router
|
||||
.route("/")
|
||||
.get(isAuthenticatedUser, authorizeRoles("admin"), getAllProductManuals);
|
||||
|
||||
router.route("/getall").get(isAuthenticatedUser, getAllProductManuals);
|
||||
|
||||
router
|
||||
.route("/:id")
|
||||
.get(isAuthenticatedUser, authorizeRoles("admin"), getSingleProductManual);
|
||||
|
||||
router.route("/getone/:id").get(isAuthenticatedUser, 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/update
|
@ -3,275 +3,12 @@ import cloudinary from "../../Utils/cloudinary.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { CategoryModel } from "../Category/CategoryModel.js";
|
||||
import { BrandModel } from "../Brands/BrandsModel.js";
|
||||
import User from "../user/userModel.js";
|
||||
import { Tax } from "../Tax/tax_model.js";
|
||||
import XLSX from "xlsx";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import mongoose from "mongoose";
|
||||
|
||||
// Function to handle product upload
|
||||
// export const uploadProducts = async (req, res) => {
|
||||
// try {
|
||||
// if (!req.files || !req.files.file) {
|
||||
// return res.status(400).json({ message: "No file uploaded" });
|
||||
// }
|
||||
|
||||
// const file = req.files.file;
|
||||
// const filePath = path.join("public", "uploads", file.name);
|
||||
|
||||
// // Ensure 'uploads' directory exists
|
||||
// if (!fs.existsSync(path.dirname(filePath))) {
|
||||
// fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
// }
|
||||
|
||||
// // Move the file from temp to the uploads directory
|
||||
// await file.mv(filePath);
|
||||
|
||||
// // Process the file
|
||||
// const fileBuffer = fs.readFileSync(filePath);
|
||||
// const workbook = XLSX.read(fileBuffer, { type: "buffer" });
|
||||
// const sheetName = workbook.SheetNames[0];
|
||||
// const worksheet = workbook.Sheets[sheetName];
|
||||
// const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||
|
||||
// if (data.length <= 1) {
|
||||
// return res.status(400).json({ message: "Empty spreadsheet or no data found" });
|
||||
// }
|
||||
|
||||
// const headers = data[0];
|
||||
|
||||
// // Map headers from the Excel file to your schema
|
||||
// const headerMapping = {
|
||||
// SKU: "SKU",
|
||||
// "Product Name": "name",
|
||||
// "Category Name": "category",
|
||||
// "Brand Name": "brand",
|
||||
// Price: "price",
|
||||
// "GST (in %)": "GST",
|
||||
// "HSN Code": "HSN_Code",
|
||||
// "Description (Optional)": "description",
|
||||
// };
|
||||
|
||||
// const requiredHeaders = Object.keys(headerMapping);
|
||||
|
||||
// if (!requiredHeaders.every((header) => headers.includes(header))) {
|
||||
// return res.status(400).json({ message: "Missing required columns in spreadsheet" });
|
||||
// }
|
||||
|
||||
// const errors = [];
|
||||
// const newlyCreated = [];
|
||||
// const updatedProducts = [];
|
||||
|
||||
// for (let i = 1; i < data.length; i++) {
|
||||
// const row = data[i];
|
||||
// const item = {};
|
||||
|
||||
// headers.forEach((header, index) => {
|
||||
// if (headerMapping[header]) {
|
||||
// item[headerMapping[header]] = row[index] !== undefined ? row[index] : "";
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Initialize error tracking for each item
|
||||
// const missingFields = new Set();
|
||||
// const notFoundErrors = new Set();
|
||||
|
||||
// let { SKU, name, category, brand, price, GST, HSN_Code, description } = item;
|
||||
|
||||
// // Trim leading and trailing spaces from product name and GST
|
||||
// name = name ? name.trim() : "";
|
||||
|
||||
// // Validate required fields
|
||||
// if (!SKU) missingFields.add("SKU");
|
||||
// if (!name) missingFields.add("name");
|
||||
// if (!category) missingFields.add("category");
|
||||
// if (!brand) missingFields.add("brand");
|
||||
// if (price === undefined || price === "") missingFields.add("price");
|
||||
// if (!GST) missingFields.add("GST");
|
||||
// if (!HSN_Code) missingFields.add("HSN_Code");
|
||||
|
||||
// // Validate or create category
|
||||
// let categoryName = "";
|
||||
// if (category) {
|
||||
// let categoryDoc = await CategoryModel.findOne({
|
||||
// categoryName: { $regex: new RegExp(`^${category.trim()}$`, "i") },
|
||||
// }).exec();
|
||||
// if (!categoryDoc) {
|
||||
// // If category not found, create a new one
|
||||
// categoryDoc = await CategoryModel.create({
|
||||
// categoryName: category.trim(),
|
||||
// addedBy: req.user._id,
|
||||
// });
|
||||
// }
|
||||
// item.category = categoryDoc._id;
|
||||
// categoryName = categoryDoc.categoryName;
|
||||
// }
|
||||
|
||||
// // Validate or create brand
|
||||
// let brandName = "";
|
||||
// if (brand) {
|
||||
// let brandDoc = await BrandModel.findOne({
|
||||
// brandName: { $regex: new RegExp(`^${brand.trim()}$`, "i") },
|
||||
// }).exec();
|
||||
// if (!brandDoc) {
|
||||
// // If brand not found, create a new one
|
||||
// brandDoc = await BrandModel.create({
|
||||
// brandName: brand.trim(),
|
||||
// addedBy: req.user._id,
|
||||
// });
|
||||
// }
|
||||
// item.brand = brandDoc._id;
|
||||
// brandName = brandDoc.brandName;
|
||||
// }
|
||||
|
||||
// // Combine all errors into a single message
|
||||
// let errorMessage = "";
|
||||
// if (missingFields.size > 0) {
|
||||
// errorMessage += `Missing fields: ${Array.from(missingFields).join(", ")}. `;
|
||||
// }
|
||||
// if (notFoundErrors.size > 0) {
|
||||
// errorMessage += `Not found: ${Array.from(notFoundErrors).join(", ")}.`;
|
||||
// }
|
||||
|
||||
// // If there are errors, push them to the errors array
|
||||
// if (errorMessage.trim()) {
|
||||
// errors.push({
|
||||
// SKU: SKU || "N/A",
|
||||
// productName: name || "N/A",
|
||||
// category: category || "N/A",
|
||||
// brand: brand || "N/A",
|
||||
// GST: GST || "N/A",
|
||||
// HSN_Code: HSN_Code || "N/A",
|
||||
// price: price || "N/A",
|
||||
// message: errorMessage.trim(),
|
||||
// });
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Ensure fields are set to empty strings if not provided
|
||||
// description = description !== undefined ? description : "";
|
||||
|
||||
// // Check for existing product by SKU
|
||||
// let existingProduct = await Product.findOne({ SKU }).exec();
|
||||
|
||||
// if (existingProduct) {
|
||||
// // Track changes
|
||||
// const updatedFields = [];
|
||||
// let updatedProduct = { ...existingProduct._doc };
|
||||
|
||||
// // Fetch existing category name and brand name
|
||||
// const existingCategory = await CategoryModel.findById(existingProduct.category).exec();
|
||||
// const existingBrand = await BrandModel.findById(existingProduct.brand).exec();
|
||||
|
||||
// // Update product fields if they have changed
|
||||
// if (name !== existingProduct.name) {
|
||||
// updatedFields.push("name");
|
||||
// updatedProduct.name = name;
|
||||
// }
|
||||
// if (category && existingProduct.category.toString() !== item.category.toString()) {
|
||||
// updatedFields.push("category");
|
||||
// updatedProduct.category = categoryName;
|
||||
// } else {
|
||||
// updatedProduct.category = existingCategory ? existingCategory.categoryName : "";
|
||||
// }
|
||||
// if (price !== undefined && price !== "" && existingProduct.price !== price) {
|
||||
// updatedFields.push("price");
|
||||
// updatedProduct.price = price;
|
||||
// }
|
||||
// if (brand && existingProduct.brand.toString() !== item.brand.toString()) {
|
||||
// updatedFields.push("brand");
|
||||
// updatedProduct.brand = brandName;
|
||||
// } else {
|
||||
// updatedProduct.brand = existingBrand ? existingBrand.brandName : "";
|
||||
// }
|
||||
// if (HSN_Code !== existingProduct.HSN_Code) {
|
||||
// updatedFields.push("HSN_Code");
|
||||
// updatedProduct.HSN_Code = HSN_Code;
|
||||
// }
|
||||
// if (GST !== existingProduct.GST) {
|
||||
// updatedFields.push("GST");
|
||||
// updatedProduct.GST = GST;
|
||||
// }
|
||||
// if (description !== existingProduct.description) {
|
||||
// updatedFields.push("description");
|
||||
// updatedProduct.description = description;
|
||||
// }
|
||||
|
||||
// // Only update if there are changes
|
||||
// if (updatedFields.length > 0) {
|
||||
// try {
|
||||
// await Product.updateOne(
|
||||
// { SKU: existingProduct.SKU },
|
||||
// {
|
||||
// $set: {
|
||||
// category: item.category || existingProduct.category,
|
||||
// price: price !== undefined && price !== "" ? price : existingProduct.price,
|
||||
// GST: GST || existingProduct.GST,
|
||||
// HSN_Code: HSN_Code || existingProduct.HSN_Code,
|
||||
// name: name,
|
||||
// description: description,
|
||||
// product_Status: item.product_Status || existingProduct.product_Status || "Active",
|
||||
// },
|
||||
// }
|
||||
// );
|
||||
// updatedProducts.push({
|
||||
// ...updatedProduct,
|
||||
// updatedFields: updatedFields.join(", "), // Track updated fields
|
||||
// });
|
||||
// } catch (error) {
|
||||
// errors.push({
|
||||
// SKU,
|
||||
// message: "Failed to update product",
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Create new product
|
||||
// if (item.category && item.brand) {
|
||||
// const productData = {
|
||||
// SKU,
|
||||
// name,
|
||||
// category: item.category,
|
||||
// brand: item.brand,
|
||||
// price,
|
||||
// GST,
|
||||
// HSN_Code,
|
||||
// description: description,
|
||||
// product_Status: item.product_Status || "Active",
|
||||
// addedBy: req.user._id,
|
||||
// };
|
||||
// try {
|
||||
// const newProduct = await Product.create(productData);
|
||||
// newlyCreated.push({
|
||||
// ...newProduct._doc,
|
||||
// category: categoryName,
|
||||
// brand: brandName,
|
||||
// });
|
||||
// } catch (error) {
|
||||
// errors.push({
|
||||
// SKU,
|
||||
// message: "Failed to create product",
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// fs.unlinkSync(filePath); // Clean up uploaded file
|
||||
|
||||
// res.status(201).json({
|
||||
// message: errors.length > 0 ? "Products processed with errors!" : "Products processed successfully!",
|
||||
// newlyCreated: newlyCreated,
|
||||
// updatedProducts: updatedProducts,
|
||||
// errors,
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error("Error:", error);
|
||||
// res.status(500).json({ message: "Internal server error" });
|
||||
// }
|
||||
// };
|
||||
export const uploadProducts = async (req, res) => {
|
||||
try {
|
||||
if (!req.files || !req.files.file) {
|
||||
|
@ -7,7 +7,7 @@ import password from "secure-random-password";
|
||||
import catchAsyncErrors from "../../middlewares/catchAsyncErrors.js";
|
||||
|
||||
export const register = async (req, res) => {
|
||||
let { name, email, countryCode, mobileNumber,territoryManager } = req.body;
|
||||
let { name, email, countryCode, mobileNumber, territoryManager } = req.body;
|
||||
// console.log(req.body);
|
||||
countryCode = countryCode?.trim();
|
||||
mobileNumber = mobileNumber?.trim();
|
||||
@ -178,6 +178,12 @@ export const getAllSalesCoOrdinator = async (req, res) => {
|
||||
try {
|
||||
const PAGE_SIZE = parseInt(req.query?.show || "10");
|
||||
const page = parseInt(req.query?.page || "1") - 1;
|
||||
if (!req.user || !req.user._id) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Please login to a TM account",
|
||||
});
|
||||
}
|
||||
let filter = {};
|
||||
if (req.query?.name) {
|
||||
filter.name = {
|
||||
@ -192,6 +198,90 @@ export const getAllSalesCoOrdinator = async (req, res) => {
|
||||
if (req.query?.isVerified) {
|
||||
filter.isVerified = req.query.isVerified === "true";
|
||||
}
|
||||
const total = await SalesCoOrdinator.countDocuments(filter);
|
||||
const salesCoOrinators = await SalesCoOrdinator.find(filter)
|
||||
.limit(PAGE_SIZE)
|
||||
.skip(PAGE_SIZE * page)
|
||||
.sort({ createdAt: -1 });
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
total_data: total,
|
||||
total_pages: Math.ceil(total / PAGE_SIZE),
|
||||
salesCoOrinators,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message ? error.message : "Something went wrong!",
|
||||
});
|
||||
}
|
||||
};
|
||||
export const getAllSalesCoOrdinatorforTM_App = async (req, res) => {
|
||||
try {
|
||||
const PAGE_SIZE = parseInt(req.query?.show || "10");
|
||||
const page = parseInt(req.query?.page || "1") - 1;
|
||||
if (!req.user || !req.user._id) {
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
message: "Please login to a TM account",
|
||||
});
|
||||
}
|
||||
let filter = {};
|
||||
if (req.query?.name) {
|
||||
filter.name = {
|
||||
$regex: new RegExp(req.query.name, "i"),
|
||||
};
|
||||
}
|
||||
if (req.query?.mobileNumber) {
|
||||
filter.mobileNumber = {
|
||||
$regex: new RegExp(req.query.mobileNumber, "i"),
|
||||
};
|
||||
}
|
||||
if (req.query?.isVerified) {
|
||||
filter.isVerified = req.query.isVerified === "true";
|
||||
}
|
||||
// Mandatory filter for mappedby
|
||||
filter.mappedby = req.user._id;
|
||||
const total = await SalesCoOrdinator.countDocuments(filter);
|
||||
const salesCoOrinators = await SalesCoOrdinator.find(filter)
|
||||
.limit(PAGE_SIZE)
|
||||
.skip(PAGE_SIZE * page)
|
||||
.sort({ createdAt: -1 });
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
total_data: total,
|
||||
total_pages: Math.ceil(total / PAGE_SIZE),
|
||||
salesCoOrinators,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message ? error.message : "Something went wrong!",
|
||||
});
|
||||
}
|
||||
};
|
||||
export const getAllSalesCoOrdinatorbytmId = async (req, res) => {
|
||||
try {
|
||||
const PAGE_SIZE = parseInt(req.query?.show || "10");
|
||||
const page = parseInt(req.query?.page || "1") - 1;
|
||||
let filter = { mappedby: req.params.id }; // Include the mappedby field in the filter
|
||||
|
||||
// Adding optional filters
|
||||
if (req.query?.name) {
|
||||
filter.name = {
|
||||
$regex: new RegExp(req.query.name, "i"),
|
||||
};
|
||||
}
|
||||
if (req.query?.mobileNumber) {
|
||||
filter.mobileNumber = {
|
||||
$regex: new RegExp(req.query.mobileNumber, "i"),
|
||||
};
|
||||
}
|
||||
if (req.query?.isVerified) {
|
||||
filter.isVerified = req.query.isVerified === "true";
|
||||
}
|
||||
|
||||
const total = await SalesCoOrdinator.countDocuments(filter);
|
||||
const salesCoOrinators = await SalesCoOrdinator.find(filter)
|
||||
@ -212,7 +302,82 @@ export const getAllSalesCoOrdinator = async (req, res) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
export const mappedbyTM = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params; // SalesCoOrdinator ID from URL parameters
|
||||
const { mappedby } = req.body; // TerritoryManager ID from request body
|
||||
// console.log(id, mappedby);
|
||||
// Validate that the TerritoryManager ID is provided
|
||||
if (!mappedby) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Territory Manager ID (mappedby) is required.",
|
||||
});
|
||||
}
|
||||
|
||||
// Find the SalesCoOrdinator by ID
|
||||
const salesCoordinator = await SalesCoOrdinator.findById(id);
|
||||
|
||||
// If no SalesCoOrdinator is found
|
||||
if (!salesCoordinator) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Sales Coordinator not found.",
|
||||
});
|
||||
}
|
||||
|
||||
// Update the mappedby field
|
||||
salesCoordinator.mappedby = mappedby;
|
||||
|
||||
// Save the updated SalesCoOrdinator
|
||||
await salesCoordinator.save();
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Sales Coordinator successfully updated.",
|
||||
salesCoordinator,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message ? error.message : "Something went wrong!",
|
||||
});
|
||||
}
|
||||
};
|
||||
export const unmapSalesCoOrdinator = async (req, res) => {
|
||||
try {
|
||||
if (!req.params.id) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Please provide SalesCoOrdinator ID!",
|
||||
});
|
||||
}
|
||||
|
||||
const getSalesCoOrdinator = await SalesCoOrdinator.findById(req.params.id);
|
||||
if (!getSalesCoOrdinator) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Sales Coordinator not found!",
|
||||
});
|
||||
}
|
||||
|
||||
// Set mappedby field to null
|
||||
getSalesCoOrdinator.mappedby = null;
|
||||
|
||||
// Save the updated sales coordinator
|
||||
await getSalesCoOrdinator.save();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "Sales Coordinator unmapped successfully!",
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message ? error.message : "Something went wrong!",
|
||||
});
|
||||
}
|
||||
};
|
||||
export const getOneSalesCoOrdinator = async (req, res) => {
|
||||
try {
|
||||
if (!req.params.id) {
|
||||
|
@ -8,6 +8,11 @@ import crypto from "crypto";
|
||||
|
||||
const salescoordinatorSchema = new mongoose.Schema(
|
||||
{
|
||||
designation: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "Sales Coordinator",
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
@ -54,7 +59,6 @@ const salescoordinatorSchema = new mongoose.Schema(
|
||||
mappedby: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "TerritoryManager",
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
{ timestamps: true }
|
||||
|
@ -16,6 +16,10 @@ import {
|
||||
ChangePassword,
|
||||
getOneSalesCoOrdinator,
|
||||
logout,
|
||||
getAllSalesCoOrdinatorbytmId,
|
||||
mappedbyTM,
|
||||
unmapSalesCoOrdinator,
|
||||
getAllSalesCoOrdinatorforTM_App,
|
||||
} from "./SalesCoOrdinatorController.js";
|
||||
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
|
||||
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
|
||||
@ -35,7 +39,25 @@ router.get(
|
||||
router.get(
|
||||
"/getAll-TM",
|
||||
isAuthenticatedTerritoryManager,
|
||||
getAllSalesCoOrdinator
|
||||
getAllSalesCoOrdinatorforTM_App
|
||||
);
|
||||
router.get(
|
||||
"/getbyTmId/:id",
|
||||
isAuthenticatedUser,
|
||||
authorizeRoles("admin"),
|
||||
getAllSalesCoOrdinatorbytmId
|
||||
);
|
||||
router.put(
|
||||
"/mappedtm/:id",
|
||||
isAuthenticatedUser,
|
||||
authorizeRoles("admin"),
|
||||
mappedbyTM
|
||||
);
|
||||
router.delete(
|
||||
"/unmap/:id",
|
||||
isAuthenticatedUser,
|
||||
authorizeRoles("admin"),
|
||||
unmapSalesCoOrdinator
|
||||
);
|
||||
router.get(
|
||||
"/getOne/:id",
|
||||
@ -75,11 +97,7 @@ router.patch(
|
||||
authorizeRoles("admin"),
|
||||
UpdateProfile
|
||||
);
|
||||
router.patch(
|
||||
"/profile/update",
|
||||
isAuthenticatedSalesCoOrdinator,
|
||||
UpdateProfile
|
||||
);
|
||||
router.patch("/profile/update", isAuthenticatedSalesCoOrdinator, UpdateProfile);
|
||||
//change password
|
||||
router.put(
|
||||
"/password/update/:id",
|
||||
|
@ -1,6 +1,44 @@
|
||||
import Task from "./TaskModel.js";
|
||||
import SalesCoOrdinator from "../SalesCoOrdinators/SalesCoOrdinatorModel.js";
|
||||
import crypto from "crypto";
|
||||
import cron from "node-cron";
|
||||
// Function to update task statuses
|
||||
const updateOverdueTasks = async () => {
|
||||
try {
|
||||
const currentDate = new Date();
|
||||
const currentDateOnly = new Date(currentDate.setHours(0, 0, 0, 0));
|
||||
|
||||
// Find tasks where dueDate is before the current date and status is "New"
|
||||
const overdueTasks = await Task.find({
|
||||
taskDueDate: { $lt: currentDateOnly },
|
||||
taskStatus: "New",
|
||||
});
|
||||
|
||||
// Update tasks to "Pending"
|
||||
for (const task of overdueTasks) {
|
||||
task.taskStatus = "Pending";
|
||||
await task.save();
|
||||
}
|
||||
|
||||
console.log('Overdue tasks updated to "Pending".');
|
||||
} catch (error) {
|
||||
console.error("Error updating overdue tasks:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// Schedule the cron job to run daily at midnight
|
||||
cron.schedule("10 0 * * *", updateOverdueTasks, {
|
||||
timezone: "Asia/Kolkata",
|
||||
});
|
||||
|
||||
// cron.schedule("30 9 * * *", updateOverdueTasks);
|
||||
|
||||
const parseDate = (dateStr) => {
|
||||
const [day, month, year] = dateStr.split("/").map(Number);
|
||||
// Create a date object in local timezone
|
||||
const localDate = new Date(year, month - 1, day);
|
||||
// Convert to a date with time set to the start of the day in UTC
|
||||
return new Date(Date.UTC(year, month - 1, day));
|
||||
};
|
||||
|
||||
export const assignTask = async (req, res) => {
|
||||
try {
|
||||
@ -12,23 +50,47 @@ export const assignTask = async (req, res) => {
|
||||
taskAssignedTo,
|
||||
addedFor,
|
||||
addedForId,
|
||||
tradename,
|
||||
} = req.body;
|
||||
// console.log(req.body);
|
||||
|
||||
// Convert the taskDueDate from DD/MM/YYYY string to Date object
|
||||
const dueDate = parseDate(taskDueDate);
|
||||
const currentDate = new Date();
|
||||
|
||||
// Set the time of the currentDate to the start of the day for comparison
|
||||
const currentDateOnly = new Date(
|
||||
Date.UTC(
|
||||
currentDate.getUTCFullYear(),
|
||||
currentDate.getUTCMonth(),
|
||||
currentDate.getUTCDate()
|
||||
)
|
||||
);
|
||||
|
||||
// Check if the due date is in the past
|
||||
if (dueDate < currentDateOnly) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Due date cannot be earlier than the current date.",
|
||||
});
|
||||
}
|
||||
|
||||
const currentYear = new Date().getFullYear().toString().slice(-2);
|
||||
const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||
const uniqueId = `${currentYear}-${randomChars}`;
|
||||
|
||||
// Create a new task
|
||||
const newTask = await Task.create({
|
||||
taskId: uniqueId,
|
||||
task,
|
||||
note,
|
||||
taskStatus: "Pending",
|
||||
taskStatus: "New",
|
||||
taskPriority,
|
||||
taskDueDate,
|
||||
taskDueDate: dueDate, // Save the date as a Date object
|
||||
taskAssignedTo,
|
||||
taskAssignedBy: req.user._id, // The Territory Manager's ID
|
||||
taskAssignedBy: req.user._id,
|
||||
addedFor,
|
||||
addedForId,
|
||||
tradename,
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
@ -44,28 +106,111 @@ export const assignTask = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getTasksForSalesCoordinator = async (req, res) => {
|
||||
export const getTasksByStatus = async (req, res) => {
|
||||
try {
|
||||
const tasks = await Task.find({ taskAssignedTo: req.user._id });
|
||||
const { status } = req.params; // This should be "New", "Pending", or "Completed"
|
||||
|
||||
// Validate the provided status
|
||||
if (!["New", "Pending", "Completed"].includes(status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid status type provided.",
|
||||
});
|
||||
}
|
||||
|
||||
// Find tasks assigned to the user, filtered by status, and sorted by creation date (newest to oldest)
|
||||
const tasks = await Task.find({
|
||||
taskAssignedTo: req.user._id,
|
||||
taskStatus: status,
|
||||
}).sort({ createdAt: -1 }); // Sort by createdAt in descending order (-1 means newest first)
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
tasks,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(400).json({
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
export const getAllTasksByStatus = async (req, res) => {
|
||||
try {
|
||||
const { status } = req.params; // This should be "New", "Pending", or "Completed"
|
||||
|
||||
// Validate the provided status
|
||||
if (!["New", "Pending", "Completed"].includes(status)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid status type provided.",
|
||||
});
|
||||
}
|
||||
|
||||
// Find tasks assigned to the user, filtered by status, and sorted by creation date (newest to oldest)
|
||||
const tasks = await Task.find({
|
||||
taskAssignedBy: req.user._id,
|
||||
taskStatus: status,
|
||||
})
|
||||
.populate({
|
||||
path: "taskAssignedTo",
|
||||
select: "name mobileNumber email",
|
||||
})
|
||||
.sort({ createdAt: -1 }); // Sort by createdAt in descending order (-1 means newest first)
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
tasks,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
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;
|
||||
|
||||
// Find the task to ensure it belongs to the logged-in Sales Coordinator
|
||||
const task = await Task.findOne({ _id: taskId, taskAssignedTo: req.user._id });
|
||||
const task = await Task.findOne({
|
||||
_id: taskId,
|
||||
taskAssignedTo: req.user._id,
|
||||
});
|
||||
|
||||
if (!task) {
|
||||
return res.status(404).json({
|
||||
|
@ -12,18 +12,19 @@ const TaskSchema = new mongoose.Schema(
|
||||
task: {
|
||||
type: String,
|
||||
required: true,
|
||||
enum: ["Visit Retailers", "Update Sales Data", "Update Inventory Data", "Collect KYC"], // Restrict to specific tasks
|
||||
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: {
|
||||
type: String,
|
||||
required: true,
|
||||
enum: ["Pending", "In Progress", "Completed"],
|
||||
enum: ["New", "Pending", "Completed"],
|
||||
default: "New",
|
||||
},
|
||||
taskPriority: {
|
||||
type: String,
|
||||
@ -31,9 +32,8 @@ const TaskSchema = new mongoose.Schema(
|
||||
enum: ["Low", "Medium", "High"],
|
||||
},
|
||||
taskDueDate: {
|
||||
type: String,
|
||||
type: Date, // Change to Date
|
||||
required: true,
|
||||
match: /^\d{2}\/\d{2}\/\d{4}$/,
|
||||
},
|
||||
taskAssignedTo: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
@ -49,14 +49,20 @@ const TaskSchema = new mongoose.Schema(
|
||||
type: String,
|
||||
enum: ['PrincipalDistributor', 'RetailDistributor'],
|
||||
required: function () {
|
||||
return this.task === "Update Inventory Data";
|
||||
return this.task === "Update Inventory Data" || this.task === "Visit RD/PD";
|
||||
},
|
||||
},
|
||||
addedForId: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
refPath: 'addedFor',
|
||||
required: function () {
|
||||
return this.task === "Update Inventory Data";
|
||||
return this.task === "Update Inventory Data" || this.task === "Visit RD/PD";
|
||||
},
|
||||
},
|
||||
tradename: {
|
||||
type: String,
|
||||
required: function () {
|
||||
return this.task === "Update Inventory Data" || this.task === "Visit RD/PD";
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1,8 +1,10 @@
|
||||
import express from "express";
|
||||
import {
|
||||
assignTask,
|
||||
getTasksForSalesCoordinator,
|
||||
getTasksByStatus,
|
||||
updateTaskStatus,
|
||||
getTasksbytask,
|
||||
getAllTasksByStatus,
|
||||
} from "./TaskController.js";
|
||||
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
|
||||
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
|
||||
@ -16,13 +18,23 @@ router.post(
|
||||
assignTask
|
||||
);
|
||||
|
||||
// Route for Sales Coordinator to view their tasks
|
||||
// Route for Sales Coordinator to view their tasks by status
|
||||
router.get(
|
||||
"/tasks",
|
||||
"/tasks/:status",
|
||||
isAuthenticatedSalesCoOrdinator,
|
||||
getTasksForSalesCoordinator
|
||||
getTasksByStatus
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/alltasks/:status",
|
||||
isAuthenticatedTerritoryManager,
|
||||
getAllTasksByStatus
|
||||
);
|
||||
router.get(
|
||||
"/task/type/:task",
|
||||
isAuthenticatedSalesCoOrdinator,
|
||||
getTasksbytask
|
||||
);
|
||||
// Route to update task status
|
||||
router.put(
|
||||
"/update-task-status/:taskId",
|
||||
isAuthenticatedSalesCoOrdinator,
|
||||
|
@ -1,77 +0,0 @@
|
||||
import { Tax } from "./tax_model.js";
|
||||
|
||||
export const addTax = async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(400).json({ message: "User Not Found" });
|
||||
}
|
||||
const tax = new Tax({
|
||||
name: req.body.name,
|
||||
tax: req.body.tax,
|
||||
hsn_code: req.body.hsn_code,
|
||||
});
|
||||
try {
|
||||
const data = await tax.save();
|
||||
res.status(201).json({ message: "Success", data: data });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message ? error.message : "Something went Wrong" });
|
||||
}
|
||||
};
|
||||
|
||||
export const updateTax = async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(400).json({ message: "User Not Found" });
|
||||
}
|
||||
const id = req.params.id;
|
||||
const queryObj = req.body;
|
||||
|
||||
try {
|
||||
const data = await Tax.findByIdAndUpdate(id, queryObj, {
|
||||
new: true,
|
||||
});
|
||||
res.status(200).json({ message: "Success", data: data });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message, message: "failed" });
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteTax = async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(400).json({ message: "User Not Found" });
|
||||
}
|
||||
const id = req.params.id;
|
||||
try {
|
||||
const data = await Tax.findByIdAndDelete(id);
|
||||
res.status(200).json({ message: "Success" });
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message, message: "failed" });
|
||||
}
|
||||
};
|
||||
|
||||
export const getTaxes = async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(400).json({ message: "User Not Found" });
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await Tax.find().sort({ createdAt: -1 });
|
||||
res.status(200).json(data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ message: error.message, message: "failed" });
|
||||
}
|
||||
};
|
||||
|
||||
export const getTax = async (req, res) => {
|
||||
if (!req.user) {
|
||||
return res.status(400).json({ message: "User Not Found" });
|
||||
}
|
||||
try {
|
||||
let { id } = req.params;
|
||||
const tax = await Tax.findById({ _id: id });
|
||||
return res.status(200).json(tax);
|
||||
} catch (error) {
|
||||
return res.status(504).json({
|
||||
status: "failed",
|
||||
error,
|
||||
});
|
||||
}
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
import mongoose from "mongoose";
|
||||
|
||||
const { Schema, model } = mongoose;
|
||||
|
||||
const TaxSchema = new Schema(
|
||||
{
|
||||
name: String,
|
||||
hsn_code: Number,
|
||||
tax: Number,
|
||||
},
|
||||
{ timestamps: true }
|
||||
);
|
||||
|
||||
export const Tax = model("Tax", TaxSchema);
|
@ -1,23 +0,0 @@
|
||||
import { Router } from "express";
|
||||
import { authorizeRoles, isAuthenticatedUser } from "../../middlewares/auth.js";
|
||||
import {
|
||||
addTax,
|
||||
updateTax,
|
||||
deleteTax,
|
||||
getTaxes,
|
||||
getTax,
|
||||
} from "./tax_controller.js";
|
||||
const router = Router();
|
||||
|
||||
router
|
||||
.route("/add_tax")
|
||||
.post(isAuthenticatedUser, authorizeRoles("admin", "Employee"), addTax);
|
||||
router
|
||||
.route("/update_tax/:id")
|
||||
.patch(isAuthenticatedUser, authorizeRoles("admin", "Employee"), updateTax);
|
||||
router
|
||||
.route("/delete_tax/:id")
|
||||
.delete(isAuthenticatedUser, authorizeRoles("admin", "Employee"), deleteTax);
|
||||
router.route("/view_tax/:id").get(isAuthenticatedUser, getTax);
|
||||
router.route("/view_tax").get(isAuthenticatedUser, getTaxes);
|
||||
export default router;
|
@ -8,6 +8,11 @@ import crypto from "crypto";
|
||||
|
||||
const territorymanagerSchema = new mongoose.Schema(
|
||||
{
|
||||
designation: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "Territory Manager",
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
@ -59,6 +59,278 @@ const generatePassword = (name, email) => {
|
||||
return password;
|
||||
};
|
||||
|
||||
// export const uploadPrincipaldistributors = async (req, res) => {
|
||||
// try {
|
||||
// if (!req.files || !req.files.file) {
|
||||
// return res.status(400).json({ message: "No file uploaded" });
|
||||
// }
|
||||
|
||||
// const file = req.files.file;
|
||||
// const filePath = path.join("public", "uploads", file.name);
|
||||
|
||||
// // Ensure 'uploads' directory exists
|
||||
// if (!fs.existsSync(path.dirname(filePath))) {
|
||||
// fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
// }
|
||||
|
||||
// // Move the file from temp to the uploads directory
|
||||
// await file.mv(filePath);
|
||||
|
||||
// // Process the file
|
||||
// const fileBuffer = fs.readFileSync(filePath);
|
||||
// const workbook = XLSX.read(fileBuffer, { type: "buffer" });
|
||||
// const sheetName = workbook.SheetNames[0];
|
||||
// const worksheet = workbook.Sheets[sheetName];
|
||||
// const data = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||
|
||||
// if (data.length <= 1) {
|
||||
// return res.status(400).json({ message: "Empty spreadsheet or no data found" });
|
||||
// }
|
||||
|
||||
// const headers = data[0];
|
||||
|
||||
// // Map headers from the Excel file to your schema
|
||||
// const headerMapping = {
|
||||
// "PD ID (From SAP)": "uniqueId",
|
||||
// "SBU":"SBU",
|
||||
// "Principal Distributor Name": "name",
|
||||
// "Email": "email",
|
||||
// "Phone Number": "phone",
|
||||
// "PAN Number": "panNumber",
|
||||
// "Trade Name": "tradeName",
|
||||
// "GST Number": "gstNumber",
|
||||
// "State": "state",
|
||||
// "City": "city",
|
||||
// "Street": "street",
|
||||
// "Pincode": "postalCode",
|
||||
// };
|
||||
|
||||
// const requiredHeaders = Object.keys(headerMapping);
|
||||
|
||||
// if (!requiredHeaders.every((header) => headers.includes(header))) {
|
||||
// return res.status(400).json({ message: "Missing required columns in spreadsheet" });
|
||||
// }
|
||||
|
||||
// const errors = [];
|
||||
// const newlyCreated = [];
|
||||
// const updatedDistributors = [];
|
||||
|
||||
// for (let i = 1; i < data.length; i++) {
|
||||
// const row = data[i];
|
||||
// const item = {};
|
||||
|
||||
// headers.forEach((header, index) => {
|
||||
// if (headerMapping[header]) {
|
||||
// item[headerMapping[header]] = row[index] !== undefined ? row[index] : "";
|
||||
// }
|
||||
// });
|
||||
|
||||
// // Initialize error tracking for each item
|
||||
// const missingFields = new Set();
|
||||
// const validationErrors = new Set();
|
||||
|
||||
// // Validate required fields
|
||||
// if (!item.uniqueId) missingFields.add("uniqueId");
|
||||
// if(!item.SBU) missingFields.add("SBU");
|
||||
// if (!item.name) missingFields.add("name");
|
||||
// if (!item.email) missingFields.add("email");
|
||||
// if (!item.phone) missingFields.add("phone");
|
||||
// if (!item.panNumber) missingFields.add("panNumber");
|
||||
// if (!item.tradeName) missingFields.add("tradeName");
|
||||
// if (!item.gstNumber) missingFields.add("gstNumber");
|
||||
// if (!item.state) missingFields.add("state");
|
||||
// if (!item.city) missingFields.add("city");
|
||||
// if (!item.street) missingFields.add("street");
|
||||
// if (!item.postalCode) missingFields.add("postalCode");
|
||||
|
||||
// // Check email validity
|
||||
// if (item.email && !validator.isEmail(item.email)) {
|
||||
// validationErrors.add("incorrect mail");
|
||||
// }
|
||||
|
||||
// // Validate mobile number
|
||||
// if (item.phone && !/^\d{10}$/.test(item.phone)) {
|
||||
// validationErrors.add("Invalid Mobile Number (should be 10 digits)");
|
||||
// }
|
||||
|
||||
// // Check GST, PAN, and postal code validation
|
||||
// item.panNumber = item.panNumber ? item.panNumber.toUpperCase() : "";
|
||||
// item.gstNumber = item.gstNumber ? item.gstNumber.toUpperCase() : "";
|
||||
|
||||
// // Validate PAN Number
|
||||
// if (item.panNumber && !/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(item.panNumber)) {
|
||||
// validationErrors.add("Invalid PAN Number");
|
||||
// }
|
||||
|
||||
// // Validate GST Number
|
||||
// if (item.gstNumber && !/^(\d{2}[A-Z]{5}\d{4}[A-Z]{1}\d[Z]{1}[A-Z\d]{1})$/.test(item.gstNumber)) {
|
||||
// validationErrors.add("Invalid GST Number");
|
||||
// }
|
||||
|
||||
// // Validate Postal Code
|
||||
// if (item.postalCode && !/^\d{6}$/.test(item.postalCode)) {
|
||||
// validationErrors.add("Invalid Postal Code");
|
||||
// }
|
||||
|
||||
// // Combine all errors into a single message
|
||||
// let errorMessage = "";
|
||||
// if (missingFields.size > 0) {
|
||||
// errorMessage += `Missing fields: ${Array.from(missingFields).join(", ")}. `;
|
||||
// }
|
||||
// if (validationErrors.size > 0) {
|
||||
// errorMessage += `Validation errors: ${Array.from(validationErrors).join(", ")}.`;
|
||||
// }
|
||||
|
||||
// // If there are errors, push them to the errors array
|
||||
// if (errorMessage.trim()) {
|
||||
// errors.push({
|
||||
// uniqueId: item.uniqueId || "N/A",
|
||||
// SBU:item.SBU || "N/A",
|
||||
// name: item.name || "N/A",
|
||||
// email: item.email || "N/A",
|
||||
// phone: item.phone || "N/A",
|
||||
// panNumber: item.panNumber || "N/A",
|
||||
// gstNumber: item.gstNumber || "N/A",
|
||||
// message: errorMessage.trim(),
|
||||
// });
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // Generate a password
|
||||
// const password = generatePassword(item.name, item.email);
|
||||
// item.role = "principal-Distributor";
|
||||
|
||||
// // Check for existing user by uniqueId
|
||||
// let distributor = await User.findOne({ uniqueId: item.uniqueId });
|
||||
|
||||
// if (distributor) {
|
||||
// // Track updated fields
|
||||
// const updatedFields = [];
|
||||
// const addressFields = ['panNumber', 'gstNumber', 'state', 'city', 'street', 'tradeName', 'postalCode'];
|
||||
// const existingAddress = await ShippingAddress.findOne({ user: distributor._id });
|
||||
|
||||
// // Check for changes in user details
|
||||
// let userUpdated = false;
|
||||
// if (distributor.name !== item.name) {
|
||||
// updatedFields.push("name");
|
||||
// distributor.name = item.name;
|
||||
// userUpdated = true;
|
||||
// }
|
||||
// if (distributor.email !== item.email) {
|
||||
// updatedFields.push("email");
|
||||
// distributor.email = item.email;
|
||||
// userUpdated = true;
|
||||
// }
|
||||
// if(distributor.SBU !== item.SBU){
|
||||
// updatedFields.push("SBU");
|
||||
// distributor.SBU = item.SBU;
|
||||
// userUpdated = true;
|
||||
// }
|
||||
// if (distributor.phone !== item.phone.toString()) {
|
||||
// updatedFields.push("phone");
|
||||
// distributor.phone = item.phone;
|
||||
// userUpdated = true;
|
||||
// }
|
||||
|
||||
// // Update user
|
||||
// if (userUpdated) {
|
||||
// await distributor.save();
|
||||
// }
|
||||
|
||||
// // Check for changes in address details
|
||||
// const addressData = {
|
||||
// street: item.street,
|
||||
// city: item.city,
|
||||
// state: item.state,
|
||||
// postalCode: item.postalCode.toString(),
|
||||
// country: "India", // Default country
|
||||
// panNumber: item.panNumber,
|
||||
// tradeName: item.tradeName,
|
||||
// gstNumber: item.gstNumber,
|
||||
// user: distributor._id,
|
||||
// };
|
||||
|
||||
// let addressUpdated = false;
|
||||
// if (existingAddress) {
|
||||
// const addressUpdates = [];
|
||||
// addressFields.forEach(field => {
|
||||
// if (existingAddress[field] !== addressData[field]) {
|
||||
// addressUpdates.push(field);
|
||||
// addressUpdated = true;
|
||||
// }
|
||||
// });
|
||||
|
||||
// if (addressUpdated) {
|
||||
// await ShippingAddress.updateOne({ user: distributor._id }, addressData);
|
||||
// if (addressUpdates.length > 0) {
|
||||
// updatedFields.push(`Address fields: ${addressUpdates.join(", ")}`);
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // Create new address
|
||||
// await ShippingAddress.create(addressData);
|
||||
// updatedFields.push("New address created");
|
||||
// }
|
||||
|
||||
// // Add to updatedDistributors only if there are updated fields
|
||||
// if (updatedFields.length > 0) {
|
||||
// updatedDistributors.push({
|
||||
// ...distributor._doc,
|
||||
// updatedFields: updatedFields.join(", ")
|
||||
// });
|
||||
// }
|
||||
// } else {
|
||||
// // Create a new user
|
||||
// distributor = new User({
|
||||
// name: item.name,
|
||||
// SBU:item.SBU,
|
||||
// email: item.email,
|
||||
// phone: item.phone,
|
||||
// password,
|
||||
// role: item.role,
|
||||
// uniqueId: item.uniqueId,
|
||||
// });
|
||||
// await distributor.save();
|
||||
|
||||
// // Send email with the new user details
|
||||
// await sendEmail({
|
||||
// to: item.email,
|
||||
// from: process.env.SEND_EMAIL_FROM,
|
||||
// subject: `Cheminova Account Created`,
|
||||
// html: `
|
||||
// Your Principal Distributor Account is created successfully.
|
||||
// <br/>Name: <strong>${item.name}</strong><br/>
|
||||
// <br/>Mobile Number: <strong>${item.phone}</strong><br/>
|
||||
// <br/>Password: <strong>${password}</strong><br/><br/>
|
||||
// <a href="${process.env.PD_APP_URL}/login">Click here to login</a><br/><br/>
|
||||
// If you have not requested this email, please ignore it.
|
||||
// `,
|
||||
// });
|
||||
|
||||
// newlyCreated.push(distributor._doc);
|
||||
// }
|
||||
// }
|
||||
|
||||
// fs.unlinkSync(filePath); // Clean up uploaded file
|
||||
|
||||
// res.status(201).json({
|
||||
// message:
|
||||
// errors.length > 0
|
||||
// ? "File processed with errors!"
|
||||
// : "File processed successfully!",
|
||||
// processedUsers: {
|
||||
// newlyCreated: newlyCreated.length,
|
||||
// updatedDistributors: updatedDistributors.length
|
||||
// },
|
||||
// errors,
|
||||
// newlyCreated,
|
||||
// updatedDistributors
|
||||
// });
|
||||
// } catch (error) {
|
||||
// console.error("Error processing file:", error);
|
||||
// res.status(500).json({ message: "Error processing file", error: error.message });
|
||||
// }
|
||||
// };
|
||||
export const uploadPrincipaldistributors = async (req, res) => {
|
||||
try {
|
||||
if (!req.files || !req.files.file) {
|
||||
@ -94,6 +366,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
||||
// Map headers from the Excel file to your schema
|
||||
const headerMapping = {
|
||||
"PD ID (From SAP)": "uniqueId",
|
||||
SBU: "SBU",
|
||||
"Principal Distributor Name": "name",
|
||||
Email: "email",
|
||||
"Phone Number": "phone",
|
||||
@ -135,6 +408,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
||||
|
||||
// Validate required fields
|
||||
if (!item.uniqueId) missingFields.add("uniqueId");
|
||||
if (!item.SBU) missingFields.add("SBU");
|
||||
if (!item.name) missingFields.add("name");
|
||||
if (!item.email) missingFields.add("email");
|
||||
if (!item.phone) missingFields.add("phone");
|
||||
@ -198,6 +472,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
||||
if (errorMessage.trim()) {
|
||||
errors.push({
|
||||
uniqueId: item.uniqueId || "N/A",
|
||||
SBU: item.SBU || "N/A",
|
||||
name: item.name || "N/A",
|
||||
email: item.email || "N/A",
|
||||
phone: item.phone || "N/A",
|
||||
@ -238,6 +513,16 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
||||
distributor.name = item.name;
|
||||
userUpdated = true;
|
||||
}
|
||||
if (distributor.email !== item.email) {
|
||||
updatedFields.push("email");
|
||||
distributor.email = item.email;
|
||||
userUpdated = true;
|
||||
}
|
||||
if (distributor.SBU !== item.SBU) {
|
||||
updatedFields.push("SBU");
|
||||
distributor.SBU = item.SBU;
|
||||
userUpdated = true;
|
||||
}
|
||||
if (distributor.phone !== item.phone.toString()) {
|
||||
updatedFields.push("phone");
|
||||
distributor.phone = item.phone;
|
||||
@ -300,6 +585,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
||||
// Create a new user
|
||||
distributor = new User({
|
||||
name: item.name,
|
||||
SBU: item.SBU,
|
||||
email: item.email,
|
||||
phone: item.phone,
|
||||
password,
|
||||
@ -307,126 +593,57 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
||||
uniqueId: item.uniqueId,
|
||||
});
|
||||
await distributor.save();
|
||||
|
||||
// Send email with the new user details
|
||||
await sendEmail({
|
||||
to: item.email,
|
||||
to: distributor.email,
|
||||
from: process.env.SEND_EMAIL_FROM,
|
||||
subject: `Cheminova Account Created`,
|
||||
html: `
|
||||
Your Principal Distributor Account is created successfully.
|
||||
<br/>Name: <strong>${item.name}</strong><br/>
|
||||
<br/>Mobile Number: <strong>${item.phone}</strong><br/>
|
||||
<br/>Name: <strong>${distributor.name}</strong><br/>
|
||||
<br/>Mobile Number: <strong>${distributor.phone}</strong><br/>
|
||||
<br/>Password: <strong>${password}</strong><br/><br/>
|
||||
<a href="${process.env.PD_APP_URL}/login">Click here to login</a><br/><br/>
|
||||
If you have not requested this email, please ignore it.
|
||||
`,
|
||||
});
|
||||
// Now create the address for the new user
|
||||
const addressData = {
|
||||
street: item.street,
|
||||
city: item.city,
|
||||
state: item.state,
|
||||
postalCode: item.postalCode.toString(),
|
||||
country: "India", // Default country
|
||||
panNumber: item.panNumber,
|
||||
tradeName: item.tradeName,
|
||||
gstNumber: item.gstNumber,
|
||||
user: distributor._id, // Use the saved user's ID
|
||||
};
|
||||
const newAddress = await ShippingAddress.create(addressData);
|
||||
|
||||
newlyCreated.push(distributor._doc);
|
||||
// Push both the distributor and the addressData to the newlyCreated array
|
||||
newlyCreated.push({
|
||||
distributor,
|
||||
address: newAddress,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fs.unlinkSync(filePath); // Clean up uploaded file
|
||||
|
||||
res.status(201).json({
|
||||
message:
|
||||
errors.length > 0
|
||||
? "File processed with errors!"
|
||||
: "File processed successfully!",
|
||||
processedUsers: {
|
||||
newlyCreated: newlyCreated.length,
|
||||
updatedDistributors: updatedDistributors.length,
|
||||
},
|
||||
errors,
|
||||
res.status(200).json({
|
||||
message: "File processed successfully",
|
||||
newlyCreated,
|
||||
updatedDistributors,
|
||||
errors,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error processing file:", error);
|
||||
res
|
||||
.status(500)
|
||||
.json({ message: "Error processing file", error: error.message });
|
||||
console.error(error);
|
||||
res.status(500).json({ message: "Internal Server Error" });
|
||||
}
|
||||
};
|
||||
|
||||
// 1.Register a User
|
||||
// export const registerUser = async (req, res) => {
|
||||
// try {
|
||||
// const { name, email, password, phone, accessTo, role } = req.body;
|
||||
// // console.log("this is the password ", password, name, req.body);
|
||||
|
||||
// let findUser = await User.findOne({ email });
|
||||
// if (findUser) {
|
||||
// return res
|
||||
// .status(400)
|
||||
// .json({ success: false, message: "User already exists" });
|
||||
// }
|
||||
// if (req.files) {
|
||||
// const files = req.files.avatar;
|
||||
// const myCloud = await cloudinary.uploader.upload(
|
||||
// files.tempFilePath,
|
||||
// {
|
||||
// folder: "Cheminova/user-image",
|
||||
// },
|
||||
// function (error, result) {
|
||||
// result, error;
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
|
||||
// const user = await User.create({
|
||||
// name,
|
||||
// email,
|
||||
// password,
|
||||
// phone,
|
||||
// role,
|
||||
// accessTo,
|
||||
// // avatar: {
|
||||
// // public_id: myCloud.public_id,
|
||||
// // url: myCloud.secure_url,
|
||||
// // },
|
||||
// });
|
||||
// // const emailData = await RegisterEmail.find();
|
||||
// // let emailSubject = emailData[0]?.subject;
|
||||
// // let emailDescription = emailData[0]?.description;
|
||||
// const config = await Config.find();
|
||||
// let appName = config[0]?.appName;
|
||||
|
||||
// // await sendEmail({
|
||||
// // to: `${email}`, // Change to your recipient
|
||||
|
||||
// // from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender
|
||||
|
||||
// // subject: `Welcome to Cheminova - Let the Shopping Begin!`,
|
||||
// // html: ` <h1 style="color: #333; text-align: left; font-family: Arial, sans-serif;">Welcome to ${appName} - Let the Shopping Begin!</h1>
|
||||
// // <strong style="color: #1b03a3; font-size: 16px"> Hey ${name},</strong>
|
||||
|
||||
// // <p style="color: #555; font-size: 15px;">
|
||||
|
||||
// // Welcome to Cheminova - Let the Shopping Begin!
|
||||
// // </p>
|
||||
// // <br/>
|
||||
// // <p style="color: #555; font-size: 15px;">You can login into :${role === "Employee" || role === "admin"
|
||||
// // ? `https://admin.smellika.com/`
|
||||
// // : `https://smellika.com`
|
||||
// // } </p>
|
||||
// // <br/>
|
||||
// // <p style="color: #555; font-size: 15px;">Below are your login credentials:</p>
|
||||
// // <p style="color: #555; font-size: 15px;">Email: ${email}</p>
|
||||
// // <p style="color: #555; font-size: 15px;">Password: ${password}</p>
|
||||
// // <span style="color: #555; font-size: 13px;">Happy shopping,</span><br/>
|
||||
|
||||
// // <span style="color: #555; font-size: 13px;">Team ${appName}</span>`,
|
||||
// // });
|
||||
// sendToken(user, 201, res);
|
||||
// } catch (e) {
|
||||
// return res.status(400).json({ success: false, message: e.message });
|
||||
// }
|
||||
// };
|
||||
export const registerUser = async (req, res) => {
|
||||
try {
|
||||
const { name, email, phone, accessTo, role, PD_ID } = req.body;
|
||||
const { name, email, phone, accessTo, role, PD_ID, SBU } = req.body;
|
||||
// console.log(req.body);
|
||||
const password = generatePassword(name, email);
|
||||
// console.log(password);
|
||||
@ -440,7 +657,7 @@ export const registerUser = async (req, res) => {
|
||||
user.phone = phone;
|
||||
user.role = role;
|
||||
user.accessTo = accessTo;
|
||||
|
||||
user.SBU = SBU;
|
||||
// Save updates
|
||||
await user.save();
|
||||
// console.log("finduser", user);
|
||||
@ -455,6 +672,7 @@ export const registerUser = async (req, res) => {
|
||||
// Create a new user if not found
|
||||
user = new User({
|
||||
uniqueId: PD_ID,
|
||||
SBU,
|
||||
name,
|
||||
email,
|
||||
password,
|
||||
@ -779,13 +997,132 @@ export const updateProfile = catchAsyncErrors(async (req, res, next) => {
|
||||
// 9.Get all users(admin)
|
||||
export const getAllUser = catchAsyncErrors(async (req, res, next) => {
|
||||
// Assuming your User model is imported as 'User'
|
||||
const users = await User.find({ role: "principal-Distributor" });
|
||||
const { page = 1, show = 10, name, mobileNumber } = req.query;
|
||||
// Create a filter object
|
||||
const filter = { role: "principal-Distributor" };
|
||||
|
||||
if (name) {
|
||||
filter.name = { $regex: name, $options: "i" }; // Case-insensitive regex search
|
||||
}
|
||||
|
||||
if (mobileNumber) {
|
||||
filter.phone = mobileNumber;
|
||||
}
|
||||
// Calculate pagination values
|
||||
const limit = parseInt(show, 10);
|
||||
const skip = (parseInt(page, 10) - 1) * limit;
|
||||
// Count total users matching the filter
|
||||
|
||||
// Find users with pagination and filters
|
||||
const users = await User.find(filter)
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(limit);
|
||||
const totalUsers = await User.countDocuments(filter);
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
users,
|
||||
totalUsers,
|
||||
});
|
||||
});
|
||||
export const getAllPrincipalDistributorbytmId = catchAsyncErrors(
|
||||
async (req, res, next) => {
|
||||
const { page = 1, show = 10, name, mobileNumber } = req.query;
|
||||
const tmId = req.params.id;
|
||||
if (!tmId) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ message: "Please provide Territory Manager ID" });
|
||||
}
|
||||
// Create a filter object
|
||||
const filter = { role: "principal-Distributor" };
|
||||
|
||||
if (name) {
|
||||
filter.name = { $regex: name, $options: "i" }; // Case-insensitive regex search
|
||||
}
|
||||
|
||||
if (mobileNumber) {
|
||||
filter.phone = mobileNumber;
|
||||
}
|
||||
|
||||
// Filter based on the mapped Territory Manager ID if provided
|
||||
if (tmId) {
|
||||
filter.mappedby = tmId;
|
||||
}
|
||||
|
||||
// Calculate pagination values
|
||||
const limit = parseInt(show, 10);
|
||||
const skip = (parseInt(page, 10) - 1) * limit;
|
||||
|
||||
// Find users with pagination and filters
|
||||
const users = await User.find(filter)
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(skip)
|
||||
.limit(limit);
|
||||
|
||||
// Count total users matching the filter
|
||||
const totalUsers = await User.countDocuments(filter);
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
principaldistributor: users,
|
||||
total_data: totalUsers,
|
||||
page: parseInt(page, 10),
|
||||
limit,
|
||||
});
|
||||
}
|
||||
);
|
||||
export const mappedbyTM = catchAsyncErrors(async (req, res, next) => {
|
||||
const { id } = req.params; // SalesCoOrdinator ID from URL parameters
|
||||
const { mappedby } = req.body; // TerritoryManager ID from request body
|
||||
// console.log(id, mappedby);
|
||||
// Validate that the TerritoryManager ID is provided
|
||||
if (!mappedby) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Territory Manager ID (mappedby) is required.",
|
||||
});
|
||||
}
|
||||
const principalDistributor = await User.findById(id);
|
||||
|
||||
if (!principalDistributor) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Principal Distributor not found",
|
||||
});
|
||||
}
|
||||
// Update the mappedby field
|
||||
principalDistributor.mappedby = mappedby;
|
||||
await principalDistributor.save();
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Principal Distributor updated successfully",
|
||||
principalDistributor,
|
||||
});
|
||||
});
|
||||
export const unmappedTMinPrincipalDistributor = catchAsyncErrors(
|
||||
async (req, res, next) => {
|
||||
const { id } = req.params; // Principal Distributor ID from URL parameters
|
||||
// console.log(id);
|
||||
const principalDistributor = await User.findById(id);
|
||||
|
||||
if (!principalDistributor) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Principal Distributor not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Remove the mappedby field
|
||||
principalDistributor.mappedby = null;
|
||||
await principalDistributor.save();
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "Principal Distributor updated successfully",
|
||||
principalDistributor,
|
||||
});
|
||||
}
|
||||
);
|
||||
export const getAllEmployee = catchAsyncErrors(async (req, res, next) => {
|
||||
// Assuming your User model is imported as 'User'
|
||||
const employee = await User.find({ role: "Employee" });
|
||||
|
@ -13,11 +13,14 @@ const userSchema = new mongoose.Schema(
|
||||
unique: true,
|
||||
required: true,
|
||||
},
|
||||
SBU: {
|
||||
type: String,
|
||||
required: [true, "Please Enter Your SBU"],
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: [true, "Please Enter Your Name"],
|
||||
maxLength: [30, "Name cannot exceed 30 characters"],
|
||||
minLength: [4, "Name should have more than 4 characters"],
|
||||
},
|
||||
email: {
|
||||
type: String,
|
||||
@ -55,6 +58,10 @@ const userSchema = new mongoose.Schema(
|
||||
type: String,
|
||||
default: "user",
|
||||
},
|
||||
mappedby: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: "TerritoryManager",
|
||||
},
|
||||
accessTo: {},
|
||||
resetPasswordToken: String,
|
||||
resetPasswordExpire: Date,
|
||||
|
@ -15,6 +15,9 @@ import {
|
||||
deleteEmployeeById,
|
||||
updateEmployeeById,
|
||||
uploadPrincipaldistributors,
|
||||
getAllPrincipalDistributorbytmId,
|
||||
mappedbyTM,
|
||||
unmappedTMinPrincipalDistributor,
|
||||
} from "./userController.js";
|
||||
import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js";
|
||||
|
||||
@ -32,14 +35,34 @@ router.route("/user/logout").get(logout);
|
||||
|
||||
router.route("/user/details").get(isAuthenticatedUser, getUserDetails);
|
||||
router
|
||||
.route('/principaldistributor/upload').post(
|
||||
.route("/principaldistributor/upload")
|
||||
.post(
|
||||
isAuthenticatedUser,
|
||||
authorizeRoles('admin'),
|
||||
authorizeRoles("admin"),
|
||||
uploadPrincipaldistributors
|
||||
);
|
||||
);
|
||||
router
|
||||
.route("/admin/users")
|
||||
.get(isAuthenticatedUser, authorizeRoles("admin", "Employee"), getAllUser);
|
||||
router
|
||||
.route("/getbyTmId/:id")
|
||||
.get(
|
||||
isAuthenticatedUser,
|
||||
authorizeRoles("admin"),
|
||||
getAllPrincipalDistributorbytmId
|
||||
);
|
||||
router.put(
|
||||
"/mappedtm/:id",
|
||||
isAuthenticatedUser,
|
||||
authorizeRoles("admin"),
|
||||
mappedbyTM
|
||||
);
|
||||
router.patch(
|
||||
"/unmap/:id",
|
||||
isAuthenticatedUser,
|
||||
authorizeRoles("admin"),
|
||||
unmappedTMinPrincipalDistributor
|
||||
);
|
||||
router
|
||||
.route("/admin/delete-employee/:id")
|
||||
.delete(
|
||||
|
Loading…
Reference in New Issue
Block a user