conflict resolved
This commit is contained in:
commit
afbc17cee7
6
app.js
6
app.js
@ -132,6 +132,7 @@ app.use(
|
|||||||
//auth
|
//auth
|
||||||
import user from "./resources/user/userRoute.js";
|
import user from "./resources/user/userRoute.js";
|
||||||
import ProductRouter from "./resources/Products/ProductRoute.js";
|
import ProductRouter from "./resources/Products/ProductRoute.js";
|
||||||
|
import ProductManualRouter from "./resources/ProductMannual/ProductManualRoute.js";
|
||||||
//Businesses
|
//Businesses
|
||||||
// import BusinessRoute from "./resources/Businesses/BusinessRoute.js";
|
// 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 ConfigRouter from "./resources/setting/Configration/Config_routes.js";
|
||||||
|
|
||||||
import TaxRouter from "./resources/Tax/tax_routes.js";
|
|
||||||
//specialties
|
//specialties
|
||||||
import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js";
|
import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js";
|
||||||
import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js";
|
import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js";
|
||||||
@ -200,6 +200,8 @@ app.use("/api/v1", user);
|
|||||||
|
|
||||||
//Product
|
//Product
|
||||||
app.use("/api", ProductRouter);
|
app.use("/api", ProductRouter);
|
||||||
|
//Product Manual
|
||||||
|
app.use("/api/productmanual", ProductManualRouter);
|
||||||
//businesses
|
//businesses
|
||||||
// app.use("/api/businesses", BusinessRoute);
|
// app.use("/api/businesses", BusinessRoute);
|
||||||
// Design
|
// Design
|
||||||
@ -239,8 +241,6 @@ app.use("/api/language", LanguageRoute);
|
|||||||
//Purpose
|
//Purpose
|
||||||
app.use("/api/purpose", PurposeRoute);
|
app.use("/api/purpose", PurposeRoute);
|
||||||
app.use("/api/business", orderRoute);
|
app.use("/api/business", orderRoute);
|
||||||
//Tax
|
|
||||||
app.use("/api/tax", TaxRouter);
|
|
||||||
//Currency Route
|
//Currency Route
|
||||||
app.use("/api/currency", CurrencyRoute);
|
app.use("/api/currency", CurrencyRoute);
|
||||||
//config
|
//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": "^1.4.5-lts.1",
|
||||||
"multer-storage-cloudinary": "^4.0.0",
|
"multer-storage-cloudinary": "^4.0.0",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
"nodemailer": "^6.9.4",
|
"nodemailer": "^6.9.4",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"pm2": "^5.3.1",
|
"pm2": "^5.3.1",
|
||||||
@ -4825,6 +4826,27 @@
|
|||||||
"node": ">= 0.4.0"
|
"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": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"multer-storage-cloudinary": "^4.0.0",
|
"multer-storage-cloudinary": "^4.0.0",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
|
"node-cron": "^3.0.3",
|
||||||
"nodemailer": "^6.9.4",
|
"nodemailer": "^6.9.4",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"pm2": "^5.3.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 { v4 as uuidv4 } from "uuid";
|
||||||
import { CategoryModel } from "../Category/CategoryModel.js";
|
import { CategoryModel } from "../Category/CategoryModel.js";
|
||||||
import { BrandModel } from "../Brands/BrandsModel.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 XLSX from "xlsx";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import mongoose from "mongoose";
|
import mongoose from "mongoose";
|
||||||
|
|
||||||
// Function to handle product upload
|
// 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) => {
|
export const uploadProducts = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
if (!req.files || !req.files.file) {
|
if (!req.files || !req.files.file) {
|
||||||
|
@ -7,7 +7,7 @@ import password from "secure-random-password";
|
|||||||
import catchAsyncErrors from "../../middlewares/catchAsyncErrors.js";
|
import catchAsyncErrors from "../../middlewares/catchAsyncErrors.js";
|
||||||
|
|
||||||
export const register = async (req, res) => {
|
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);
|
// console.log(req.body);
|
||||||
countryCode = countryCode?.trim();
|
countryCode = countryCode?.trim();
|
||||||
mobileNumber = mobileNumber?.trim();
|
mobileNumber = mobileNumber?.trim();
|
||||||
@ -178,6 +178,12 @@ export const getAllSalesCoOrdinator = async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const PAGE_SIZE = parseInt(req.query?.show || "10");
|
const PAGE_SIZE = parseInt(req.query?.show || "10");
|
||||||
const page = parseInt(req.query?.page || "1") - 1;
|
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 = {};
|
let filter = {};
|
||||||
if (req.query?.name) {
|
if (req.query?.name) {
|
||||||
filter.name = {
|
filter.name = {
|
||||||
@ -192,6 +198,90 @@ export const getAllSalesCoOrdinator = async (req, res) => {
|
|||||||
if (req.query?.isVerified) {
|
if (req.query?.isVerified) {
|
||||||
filter.isVerified = req.query.isVerified === "true";
|
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 total = await SalesCoOrdinator.countDocuments(filter);
|
||||||
const salesCoOrinators = await SalesCoOrdinator.find(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) => {
|
export const getOneSalesCoOrdinator = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
if (!req.params.id) {
|
if (!req.params.id) {
|
||||||
|
@ -8,6 +8,11 @@ import crypto from "crypto";
|
|||||||
|
|
||||||
const salescoordinatorSchema = new mongoose.Schema(
|
const salescoordinatorSchema = new mongoose.Schema(
|
||||||
{
|
{
|
||||||
|
designation: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: "Sales Coordinator",
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
@ -54,7 +59,6 @@ const salescoordinatorSchema = new mongoose.Schema(
|
|||||||
mappedby: {
|
mappedby: {
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
ref: "TerritoryManager",
|
ref: "TerritoryManager",
|
||||||
required: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ timestamps: true }
|
{ timestamps: true }
|
||||||
|
@ -16,6 +16,10 @@ import {
|
|||||||
ChangePassword,
|
ChangePassword,
|
||||||
getOneSalesCoOrdinator,
|
getOneSalesCoOrdinator,
|
||||||
logout,
|
logout,
|
||||||
|
getAllSalesCoOrdinatorbytmId,
|
||||||
|
mappedbyTM,
|
||||||
|
unmapSalesCoOrdinator,
|
||||||
|
getAllSalesCoOrdinatorforTM_App,
|
||||||
} from "./SalesCoOrdinatorController.js";
|
} from "./SalesCoOrdinatorController.js";
|
||||||
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
|
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
|
||||||
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
|
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
|
||||||
@ -35,7 +39,25 @@ router.get(
|
|||||||
router.get(
|
router.get(
|
||||||
"/getAll-TM",
|
"/getAll-TM",
|
||||||
isAuthenticatedTerritoryManager,
|
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(
|
router.get(
|
||||||
"/getOne/:id",
|
"/getOne/:id",
|
||||||
@ -75,11 +97,7 @@ router.patch(
|
|||||||
authorizeRoles("admin"),
|
authorizeRoles("admin"),
|
||||||
UpdateProfile
|
UpdateProfile
|
||||||
);
|
);
|
||||||
router.patch(
|
router.patch("/profile/update", isAuthenticatedSalesCoOrdinator, UpdateProfile);
|
||||||
"/profile/update",
|
|
||||||
isAuthenticatedSalesCoOrdinator,
|
|
||||||
UpdateProfile
|
|
||||||
);
|
|
||||||
//change password
|
//change password
|
||||||
router.put(
|
router.put(
|
||||||
"/password/update/:id",
|
"/password/update/:id",
|
||||||
|
@ -1,6 +1,44 @@
|
|||||||
import Task from "./TaskModel.js";
|
import Task from "./TaskModel.js";
|
||||||
import SalesCoOrdinator from "../SalesCoOrdinators/SalesCoOrdinatorModel.js";
|
|
||||||
import crypto from "crypto";
|
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) => {
|
export const assignTask = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
@ -12,23 +50,47 @@ export const assignTask = async (req, res) => {
|
|||||||
taskAssignedTo,
|
taskAssignedTo,
|
||||||
addedFor,
|
addedFor,
|
||||||
addedForId,
|
addedForId,
|
||||||
|
tradename,
|
||||||
} = req.body;
|
} = 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 currentYear = new Date().getFullYear().toString().slice(-2);
|
||||||
const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase();
|
const randomChars = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||||
const uniqueId = `${currentYear}-${randomChars}`;
|
const uniqueId = `${currentYear}-${randomChars}`;
|
||||||
|
|
||||||
// Create a new task
|
// Create a new task
|
||||||
const newTask = await Task.create({
|
const newTask = await Task.create({
|
||||||
taskId: uniqueId,
|
taskId: uniqueId,
|
||||||
task,
|
task,
|
||||||
note,
|
note,
|
||||||
taskStatus: "Pending",
|
taskStatus: "New",
|
||||||
taskPriority,
|
taskPriority,
|
||||||
taskDueDate,
|
taskDueDate: dueDate, // Save the date as a Date object
|
||||||
taskAssignedTo,
|
taskAssignedTo,
|
||||||
taskAssignedBy: req.user._id, // The Territory Manager's ID
|
taskAssignedBy: req.user._id,
|
||||||
addedFor,
|
addedFor,
|
||||||
addedForId,
|
addedForId,
|
||||||
|
tradename,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(201).json({
|
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 {
|
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({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
tasks,
|
tasks,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: error.message,
|
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) => {
|
export const updateTaskStatus = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { taskId } = req.params;
|
const { taskId } = req.params;
|
||||||
|
|
||||||
// Find the task to ensure it belongs to the logged-in Sales Coordinator
|
const task = await Task.findOne({
|
||||||
const task = await Task.findOne({ _id: taskId, taskAssignedTo: req.user._id });
|
_id: taskId,
|
||||||
|
taskAssignedTo: req.user._id,
|
||||||
|
});
|
||||||
|
|
||||||
if (!task) {
|
if (!task) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
|
@ -12,18 +12,19 @@ const TaskSchema = new mongoose.Schema(
|
|||||||
task: {
|
task: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
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: {
|
note: {
|
||||||
type: String,
|
type: String,
|
||||||
required: function () {
|
required: function () {
|
||||||
return this.task === "Collect KYC";
|
return this.task === "Collect KYC" || this.task === "Visit RD/PD";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
taskStatus: {
|
taskStatus: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
enum: ["Pending", "In Progress", "Completed"],
|
enum: ["New", "Pending", "Completed"],
|
||||||
|
default: "New",
|
||||||
},
|
},
|
||||||
taskPriority: {
|
taskPriority: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -31,9 +32,8 @@ const TaskSchema = new mongoose.Schema(
|
|||||||
enum: ["Low", "Medium", "High"],
|
enum: ["Low", "Medium", "High"],
|
||||||
},
|
},
|
||||||
taskDueDate: {
|
taskDueDate: {
|
||||||
type: String,
|
type: Date, // Change to Date
|
||||||
required: true,
|
required: true,
|
||||||
match: /^\d{2}\/\d{2}\/\d{4}$/,
|
|
||||||
},
|
},
|
||||||
taskAssignedTo: {
|
taskAssignedTo: {
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
@ -49,14 +49,20 @@ const TaskSchema = new mongoose.Schema(
|
|||||||
type: String,
|
type: String,
|
||||||
enum: ['PrincipalDistributor', 'RetailDistributor'],
|
enum: ['PrincipalDistributor', 'RetailDistributor'],
|
||||||
required: function () {
|
required: function () {
|
||||||
return this.task === "Update Inventory Data";
|
return this.task === "Update Inventory Data" || this.task === "Visit RD/PD";
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
addedForId: {
|
addedForId: {
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
refPath: 'addedFor',
|
refPath: 'addedFor',
|
||||||
required: function () {
|
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 express from "express";
|
||||||
import {
|
import {
|
||||||
assignTask,
|
assignTask,
|
||||||
getTasksForSalesCoordinator,
|
getTasksByStatus,
|
||||||
updateTaskStatus,
|
updateTaskStatus,
|
||||||
|
getTasksbytask,
|
||||||
|
getAllTasksByStatus,
|
||||||
} from "./TaskController.js";
|
} from "./TaskController.js";
|
||||||
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
|
import { isAuthenticatedSalesCoOrdinator } from "../../middlewares/SalesCoOrdinatorAuth.js";
|
||||||
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
|
import { isAuthenticatedTerritoryManager } from "../../middlewares/TerritoryManagerAuth.js";
|
||||||
@ -16,13 +18,23 @@ router.post(
|
|||||||
assignTask
|
assignTask
|
||||||
);
|
);
|
||||||
|
|
||||||
// Route for Sales Coordinator to view their tasks
|
// Route for Sales Coordinator to view their tasks by status
|
||||||
router.get(
|
router.get(
|
||||||
"/tasks",
|
"/tasks/:status",
|
||||||
isAuthenticatedSalesCoOrdinator,
|
isAuthenticatedSalesCoOrdinator,
|
||||||
getTasksForSalesCoordinator
|
getTasksByStatus
|
||||||
);
|
);
|
||||||
|
router.get(
|
||||||
|
"/alltasks/:status",
|
||||||
|
isAuthenticatedTerritoryManager,
|
||||||
|
getAllTasksByStatus
|
||||||
|
);
|
||||||
|
router.get(
|
||||||
|
"/task/type/:task",
|
||||||
|
isAuthenticatedSalesCoOrdinator,
|
||||||
|
getTasksbytask
|
||||||
|
);
|
||||||
|
// Route to update task status
|
||||||
router.put(
|
router.put(
|
||||||
"/update-task-status/:taskId",
|
"/update-task-status/:taskId",
|
||||||
isAuthenticatedSalesCoOrdinator,
|
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(
|
const territorymanagerSchema = new mongoose.Schema(
|
||||||
{
|
{
|
||||||
|
designation: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
default: "Territory Manager",
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -59,6 +59,278 @@ const generatePassword = (name, email) => {
|
|||||||
return password;
|
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) => {
|
export const uploadPrincipaldistributors = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
if (!req.files || !req.files.file) {
|
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
|
// Map headers from the Excel file to your schema
|
||||||
const headerMapping = {
|
const headerMapping = {
|
||||||
"PD ID (From SAP)": "uniqueId",
|
"PD ID (From SAP)": "uniqueId",
|
||||||
|
SBU: "SBU",
|
||||||
"Principal Distributor Name": "name",
|
"Principal Distributor Name": "name",
|
||||||
Email: "email",
|
Email: "email",
|
||||||
"Phone Number": "phone",
|
"Phone Number": "phone",
|
||||||
@ -135,6 +408,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
|||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
if (!item.uniqueId) missingFields.add("uniqueId");
|
if (!item.uniqueId) missingFields.add("uniqueId");
|
||||||
|
if (!item.SBU) missingFields.add("SBU");
|
||||||
if (!item.name) missingFields.add("name");
|
if (!item.name) missingFields.add("name");
|
||||||
if (!item.email) missingFields.add("email");
|
if (!item.email) missingFields.add("email");
|
||||||
if (!item.phone) missingFields.add("phone");
|
if (!item.phone) missingFields.add("phone");
|
||||||
@ -198,6 +472,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
|||||||
if (errorMessage.trim()) {
|
if (errorMessage.trim()) {
|
||||||
errors.push({
|
errors.push({
|
||||||
uniqueId: item.uniqueId || "N/A",
|
uniqueId: item.uniqueId || "N/A",
|
||||||
|
SBU: item.SBU || "N/A",
|
||||||
name: item.name || "N/A",
|
name: item.name || "N/A",
|
||||||
email: item.email || "N/A",
|
email: item.email || "N/A",
|
||||||
phone: item.phone || "N/A",
|
phone: item.phone || "N/A",
|
||||||
@ -238,6 +513,16 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
|||||||
distributor.name = item.name;
|
distributor.name = item.name;
|
||||||
userUpdated = true;
|
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()) {
|
if (distributor.phone !== item.phone.toString()) {
|
||||||
updatedFields.push("phone");
|
updatedFields.push("phone");
|
||||||
distributor.phone = item.phone;
|
distributor.phone = item.phone;
|
||||||
@ -300,6 +585,7 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
|||||||
// Create a new user
|
// Create a new user
|
||||||
distributor = new User({
|
distributor = new User({
|
||||||
name: item.name,
|
name: item.name,
|
||||||
|
SBU: item.SBU,
|
||||||
email: item.email,
|
email: item.email,
|
||||||
phone: item.phone,
|
phone: item.phone,
|
||||||
password,
|
password,
|
||||||
@ -307,126 +593,57 @@ export const uploadPrincipaldistributors = async (req, res) => {
|
|||||||
uniqueId: item.uniqueId,
|
uniqueId: item.uniqueId,
|
||||||
});
|
});
|
||||||
await distributor.save();
|
await distributor.save();
|
||||||
|
|
||||||
// Send email with the new user details
|
|
||||||
await sendEmail({
|
await sendEmail({
|
||||||
to: item.email,
|
to: distributor.email,
|
||||||
from: process.env.SEND_EMAIL_FROM,
|
from: process.env.SEND_EMAIL_FROM,
|
||||||
subject: `Cheminova Account Created`,
|
subject: `Cheminova Account Created`,
|
||||||
html: `
|
html: `
|
||||||
Your Principal Distributor Account is created successfully.
|
Your Principal Distributor Account is created successfully.
|
||||||
<br/>Name: <strong>${item.name}</strong><br/>
|
<br/>Name: <strong>${distributor.name}</strong><br/>
|
||||||
<br/>Mobile Number: <strong>${item.phone}</strong><br/>
|
<br/>Mobile Number: <strong>${distributor.phone}</strong><br/>
|
||||||
<br/>Password: <strong>${password}</strong><br/><br/>
|
<br/>Password: <strong>${password}</strong><br/><br/>
|
||||||
<a href="${process.env.PD_APP_URL}/login">Click here to login</a><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.
|
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(200).json({
|
||||||
|
message: "File processed successfully",
|
||||||
res.status(201).json({
|
|
||||||
message:
|
|
||||||
errors.length > 0
|
|
||||||
? "File processed with errors!"
|
|
||||||
: "File processed successfully!",
|
|
||||||
processedUsers: {
|
|
||||||
newlyCreated: newlyCreated.length,
|
|
||||||
updatedDistributors: updatedDistributors.length,
|
|
||||||
},
|
|
||||||
errors,
|
|
||||||
newlyCreated,
|
newlyCreated,
|
||||||
updatedDistributors,
|
updatedDistributors,
|
||||||
|
errors,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error processing file:", error);
|
console.error(error);
|
||||||
res
|
res.status(500).json({ message: "Internal Server Error" });
|
||||||
.status(500)
|
|
||||||
.json({ message: "Error processing file", error: error.message });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1.Register a User
|
// 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) => {
|
export const registerUser = async (req, res) => {
|
||||||
try {
|
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);
|
// console.log(req.body);
|
||||||
const password = generatePassword(name, email);
|
const password = generatePassword(name, email);
|
||||||
// console.log(password);
|
// console.log(password);
|
||||||
@ -440,7 +657,7 @@ export const registerUser = async (req, res) => {
|
|||||||
user.phone = phone;
|
user.phone = phone;
|
||||||
user.role = role;
|
user.role = role;
|
||||||
user.accessTo = accessTo;
|
user.accessTo = accessTo;
|
||||||
|
user.SBU = SBU;
|
||||||
// Save updates
|
// Save updates
|
||||||
await user.save();
|
await user.save();
|
||||||
// console.log("finduser", user);
|
// console.log("finduser", user);
|
||||||
@ -455,6 +672,7 @@ export const registerUser = async (req, res) => {
|
|||||||
// Create a new user if not found
|
// Create a new user if not found
|
||||||
user = new User({
|
user = new User({
|
||||||
uniqueId: PD_ID,
|
uniqueId: PD_ID,
|
||||||
|
SBU,
|
||||||
name,
|
name,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
@ -779,13 +997,132 @@ export const updateProfile = catchAsyncErrors(async (req, res, next) => {
|
|||||||
// 9.Get all users(admin)
|
// 9.Get all users(admin)
|
||||||
export const getAllUser = catchAsyncErrors(async (req, res, next) => {
|
export const getAllUser = catchAsyncErrors(async (req, res, next) => {
|
||||||
// Assuming your User model is imported as 'User'
|
// 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({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
users,
|
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) => {
|
export const getAllEmployee = catchAsyncErrors(async (req, res, next) => {
|
||||||
// Assuming your User model is imported as 'User'
|
// Assuming your User model is imported as 'User'
|
||||||
const employee = await User.find({ role: "Employee" });
|
const employee = await User.find({ role: "Employee" });
|
||||||
|
@ -13,11 +13,14 @@ const userSchema = new mongoose.Schema(
|
|||||||
unique: true,
|
unique: true,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
SBU: {
|
||||||
|
type: String,
|
||||||
|
required: [true, "Please Enter Your SBU"],
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
required: [true, "Please Enter Your Name"],
|
required: [true, "Please Enter Your Name"],
|
||||||
maxLength: [30, "Name cannot exceed 30 characters"],
|
maxLength: [30, "Name cannot exceed 30 characters"],
|
||||||
minLength: [4, "Name should have more than 4 characters"],
|
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -55,6 +58,10 @@ const userSchema = new mongoose.Schema(
|
|||||||
type: String,
|
type: String,
|
||||||
default: "user",
|
default: "user",
|
||||||
},
|
},
|
||||||
|
mappedby: {
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: "TerritoryManager",
|
||||||
|
},
|
||||||
accessTo: {},
|
accessTo: {},
|
||||||
resetPasswordToken: String,
|
resetPasswordToken: String,
|
||||||
resetPasswordExpire: Date,
|
resetPasswordExpire: Date,
|
||||||
|
@ -15,6 +15,9 @@ import {
|
|||||||
deleteEmployeeById,
|
deleteEmployeeById,
|
||||||
updateEmployeeById,
|
updateEmployeeById,
|
||||||
uploadPrincipaldistributors,
|
uploadPrincipaldistributors,
|
||||||
|
getAllPrincipalDistributorbytmId,
|
||||||
|
mappedbyTM,
|
||||||
|
unmappedTMinPrincipalDistributor,
|
||||||
} from "./userController.js";
|
} from "./userController.js";
|
||||||
import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.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("/user/details").get(isAuthenticatedUser, getUserDetails);
|
||||||
router
|
router
|
||||||
.route('/principaldistributor/upload').post(
|
.route("/principaldistributor/upload")
|
||||||
|
.post(
|
||||||
isAuthenticatedUser,
|
isAuthenticatedUser,
|
||||||
authorizeRoles('admin'),
|
authorizeRoles("admin"),
|
||||||
uploadPrincipaldistributors
|
uploadPrincipaldistributors
|
||||||
);
|
);
|
||||||
router
|
router
|
||||||
.route("/admin/users")
|
.route("/admin/users")
|
||||||
.get(isAuthenticatedUser, authorizeRoles("admin", "Employee"), getAllUser);
|
.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
|
router
|
||||||
.route("/admin/delete-employee/:id")
|
.route("/admin/delete-employee/:id")
|
||||||
.delete(
|
.delete(
|
||||||
|
Loading…
Reference in New Issue
Block a user