import mongoose from "mongoose";
import RetailDistributor from "./RetailDistributorModel.js";
import validator from "validator";
import password from "secure-random-password";
import crypto from "crypto";
import { RdOrder } from "../RD_Ordes/rdOrderModal.js";
import sendEmail, { sendOtp } from "../../Utils/sendEmail.js";
import { KYC } from "../KYC/KycModel.js";
import { generatePassword } from "../../Utils/generatepassword.js";
import XLSX from "xlsx";
import fs from "fs";
import path from "path";
import ShippingAddressRD from "../ShippingAddressesRD/RDShippingAddressModel.js";
import cloudinary from "../../Utils/cloudinary.js";
export const uploadRetaildistributors = async (req, res) => {
try {
if (!mongoose.Types.ObjectId.isValid(req.user._id)) {
return res.status(400).json({ message: "Please login again" });
}
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 = {
"Retail Distributor Name": "name",
Email: "email",
"Phone Number": "mobile_number",
"PAN Number": "pan_number",
"Trade Name": "trade_name",
"GST Number": "gst_number",
"Aadhar Number": "aadhar_number",
State: "state",
City: "city",
District: "district",
Address: "address",
Pincode: "pincode",
};
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];
// Skip the row if it's completely empty
if (row.every((cell) => cell === undefined || cell === "")) {
continue;
}
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.name) missingFields.add("name");
if (!item.email) missingFields.add("email");
if (!item.mobile_number) missingFields.add("mobile_number");
if (!item.pan_number) missingFields.add("pan_number");
if (!item.gst_number) missingFields.add("gst_number");
if (!item.trade_name) missingFields.add("trade_name");
if (!item.aadhar_number) missingFields.add("aadhar_number");
if (!item.state) missingFields.add("state");
if (!item.city) missingFields.add("city");
if (!item.pincode) missingFields.add("pincode");
if (!item.district) missingFields.add("district");
if (!item.address) missingFields.add("address");
// Check email validity
if (item.email && !validator.isEmail(item.email)) {
validationErrors.add("incorrect mail");
}
// Validate mobile number
if (item.mobile_number && !/^\d{10}$/.test(item.mobile_number)) {
validationErrors.add("Invalid Mobile Number (should be 10 digits)");
}
// Check GST, PAN, and postal code validation
item.pan_number = item.pan_number ? item.pan_number.toUpperCase() : "";
item.gst_number = item.gst_number ? item.gst_number.toUpperCase() : "";
// Validate PAN Number
if (
item.pan_number &&
!/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(item.pan_number)
) {
validationErrors.add("Invalid PAN Number");
}
// Validate GST Number
if (
item.gst_number &&
!/^(\d{2}[A-Z]{5}\d{4}[A-Z]{1}\d[Z]{1}[A-Z\d]{1})$/.test(
item.gst_number
)
) {
validationErrors.add("Invalid GST Number");
}
// Validate Aadhar number
if (item.aadhar_number && !/^\d{12}$/.test(item.aadhar_number)) {
validationErrors.add("Invalid Aadhar Number (should be 12 digits)");
}
// Validate Postal Code
if (item.pincode && !/^\d{6}$/.test(item.pincode)) {
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({
name: item.name || "N/A",
email: item.email || "N/A",
TradeName: item.trade_name || "N/A",
phone: item.mobile_number || "N/A",
panNumber: item.pan_number || "N/A",
gstNumber: item.gst_number || "N/A",
AadharNumber: item.aadhar_number || "N/A",
message: errorMessage.trim(),
});
continue;
}
// Generate a password
const password = generatePassword(item.name, item.email);
// Check for existing user by uniqueId
let Kyc = await KYC.findOne({ email: item.email });
let Retaildistributor = await RetailDistributor.findOne({
email: item.email,
});
let existingAddress = await ShippingAddressRD.findOne({
user: Retaildistributor?._id,
isDefault: true,
}).exec();
if (!existingAddress) {
existingAddress = await ShippingAddressRD.findOne({
user: Retaildistributor?._id,
})
.sort({ createdAt: 1 })
.exec();
}
if (Kyc) {
// Track updated fields
const updatedFields = [];
const addressFields = [
"Name",
"phoneNumber",
"state",
"city",
"street",
"tradeName",
"postalCode",
"district",
];
// Check for changes in user details
let kycUpdated = false;
for (let field in item) {
const currentValue = Kyc[field]?.toString();
const newValue = item[field]?.toString();
if (currentValue !== newValue) {
updatedFields.push(field);
Kyc[field] = item[field]; // Update Kyc with the new value
if (Retaildistributor && field !== "email") {
Retaildistributor[field] = item[field];
}
kycUpdated = true;
}
}
// Update Kyc and Retaildistributor if there are any changes
if (kycUpdated) {
await Kyc.save();
await Retaildistributor.save();
// Check for changes in address details
const addressData = {
Name: item.name,
phoneNumber: item.mobile_number.toString(),
street: item.address,
city: item.city,
state: item.state,
postalCode: item.pincode,
district: item.district,
country: "India", // Default country
tradeName: item.trade_name,
user: Retaildistributor._id,
};
// console.log(addressData);
let addressUpdated = false;
if (existingAddress) {
const addressUpdates = [];
addressFields.forEach((field) => {
if (existingAddress[field] !== addressData[field]) {
addressUpdates.push(field);
addressUpdated = true;
}
});
if (addressUpdated) {
await ShippingAddressRD.updateOne(
{ user: Retaildistributor._id },
addressData
);
}
} else {
// Create new address
await ShippingAddressRD.create(addressData);
}
updatedDistributors.push({
...Kyc._doc,
updatedFields: updatedFields.join(", "),
});
}
} else {
// Create a new Kyc
Kyc = new KYC({
...item,
status: "approved",
});
const newkyc = await Kyc.save();
const retailDistributorData = {
name: item.name,
email: item.email,
mobile_number: item.mobile_number,
kyc: newkyc._id,
password,
};
Retaildistributor = new RetailDistributor(retailDistributorData);
const newRd = await Retaildistributor.save();
// Now create the address for the new user
const addressData = {
Name: item.name,
phoneNumber: item.mobile_number.toString(),
street: item.address,
city: item.city,
state: item.state,
postalCode: item.pincode,
district: item.district,
country: "India", // Default country
tradeName: item.trade_name,
user: newRd._id, // Use the saved user's ID
isDefault: true,
};
await ShippingAddressRD.create(addressData);
// Send email with the new password
await sendEmail({
to: `${item.email}`, // Change to your recipient
from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender
subject: `Cheminova Account Created`,
html: `Your Retail Distributor Account is created successfully.
name is: ${item.name}
MobileNumber is: ${item.mobile_number}
Email is: ${item.email}
password is: ${password}
If you have not requested this email, please ignore it.`,
});
newlyCreated.push({ Kyc });
}
}
// Clean up uploaded file if any error occurs
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
res.status(200).json({
message: "File processed successfully",
newlyCreated,
updatedDistributors,
errors,
});
} catch (error) {
console.error(error);
// Clean up uploaded file if any error occurs
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
res.status(500).json({ message: "Internal Server Error" });
}
};
// Helper function to upload images to Cloudinary
const uploadImage = async (image, folder) => {
if (!image) return null;
const result = await cloudinary.v2.uploader.upload(image.tempFilePath, {
folder,
});
return {
public_id: result.public_id,
url: result.secure_url,
};
};
export const updateretaildistributorwithKYC = async (req, res) => {
try {
const { id } = req.params;
const retailDistributorData = req.body;
const files = req.files;
// Update RetailDistributor model
const updatedRetailDistributor = await RetailDistributor.findByIdAndUpdate(
id,
{
name: retailDistributorData.name,
email: retailDistributorData.email,
mobile_number: retailDistributorData.mobile_number,
},
{ new: true }
);
if (!updatedRetailDistributor) {
return res.status(404).json({ message: "Retail Distributor not found" });
}
// Get the associated KYC document
const kycId = updatedRetailDistributor.kyc;
const kycUpdates = {
name: retailDistributorData.name,
email: retailDistributorData.email,
trade_name: retailDistributorData.trade_name,
address: retailDistributorData.address,
state: retailDistributorData.state,
city: retailDistributorData.city,
district: retailDistributorData.district,
pincode: retailDistributorData.pincode,
mobile_number: retailDistributorData.mobile_number,
pan_number: retailDistributorData.pan_number,
aadhar_number: retailDistributorData.aadhar_number,
gst_number: retailDistributorData.gst_number,
};
// Upload images to Cloudinary if they are provided in req.files
if (files) {
if (files.panImg) {
kycUpdates.pan_img = await uploadImage(files.panImg, "KYC/pan");
}
if (files.aadharImg) {
kycUpdates.aadhar_img = await uploadImage(files.aadharImg, "KYC/aadhar");
}
if (files.gstImg) {
kycUpdates.gst_img = await uploadImage(files.gstImg, "KYC/gst");
}
if (files.pesticideLicenseImg) {
kycUpdates.pesticide_license_img = await uploadImage(files.pesticideLicenseImg, "KYC/pesticide_license");
}
if (files.fertilizerLicenseImg) {
kycUpdates.fertilizer_license_img = await uploadImage(files.fertilizerLicenseImg, "KYC/fertilizer_license");
}
if (files.selfieEntranceImg) {
kycUpdates.selfie_entrance_img = await uploadImage(files.selfieEntranceImg, "KYC/selfie_entrance");
}
}
// Update the KYC model
const updatedKYC = await KYC.findByIdAndUpdate(kycId, kycUpdates, { new: true });
if (!updatedKYC) {
return res.status(404).json({ message: "KYC record not found" });
}
// Update ShippingAddressRD model with new data
const shippingAddressUpdate = {
Name: retailDistributorData.name,
phoneNumber: retailDistributorData.mobile_number,
street: retailDistributorData.address,
district: retailDistributorData.district,
city: retailDistributorData.city,
state: retailDistributorData.state,
postalCode: retailDistributorData.pincode,
tradeName: retailDistributorData.trade_name,
};
// Find and update the default or first ShippingAddressRD record
let shippingAddress = await ShippingAddressRD.findOneAndUpdate(
{ user: id, isDefault: true },
shippingAddressUpdate,
{ new: true }
);
// If no default address found, update the first one found
if (!shippingAddress) {
shippingAddress = await ShippingAddressRD.findOneAndUpdate(
{ user: id },
shippingAddressUpdate,
{ new: true }
);
}
res.status(200).json({
message: "Retail Distributor, KYC, and Shipping Address updated successfully",
success: true,
retailDistributor: updatedRetailDistributor,
kyc: updatedKYC,
shippingAddress: shippingAddress,
});
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal Server Error" });
}
};
export const loginRD = async (req, res) => {
const { email, password } = req.body;
try {
if (!email || !password) {
return res.status(400).json({ message: "Please Enter Email & Password" });
}
const retailDistributor = await RetailDistributor.findOne({ email }).select(
"+password"
);
if (!retailDistributor) {
return res.status(400).json({ message: "Invalid Email or Password" });
}
const isPasswordMatched = await retailDistributor.comparePassword(password);
if (!isPasswordMatched) {
return res.status(400).json({ message: "Invalid Email or Password" });
}
const token = retailDistributor.getJWTToken();
return res.status(200).json({
success: true,
token,
message: "Login Successfully",
});
} catch (error) {
return res.status(500).json({
message: error.message ? error.message : "Something went wrong!",
});
}
};
export const ChangePasswordRD = async (req, res) => {
// Retrieve id from req.params
const { oldPassword, newPassword, confirmPassword } = req.body;
const userId = req.user._id; // Use the ID from the URL or from the authenticated user
// console.log(userId);
if (!oldPassword) {
return res.status(400).json({ message: "Please Enter Old password" });
}
if (!newPassword) {
return res.status(400).json({ message: "Please Enter New Password " });
}
if (!confirmPassword) {
return res.status(400).json({ message: "Please Enter Confirm Password" });
}
try {
const retailDistributor = await RetailDistributor.findById(userId).select(
"+password"
);
if (!retailDistributor) {
return res.status(404).json({ message: "Retail Distributer not found" });
}
const isPasswordMatched = await retailDistributor.comparePassword(
oldPassword
);
if (!isPasswordMatched) {
return res.status(400).json({ message: "Old password is incorrect" });
}
if (newPassword !== confirmPassword) {
return res
.status(400)
.json({ message: "New password and confirm Password do not match" });
}
retailDistributor.password = newPassword;
await retailDistributor.save();
return res
.status(200)
.json({ success: true, message: "Password updated successfully" });
} catch (error) {
console.error("Error updating password:", error);
return res.status(500).json({
message: error.message ? error.message : "Server error!",
});
}
};
export const forgotPasswordRD = async (req, res) => {
try {
// Check if email is provided
const { email } = req.body;
if (!email) {
return res.status(400).json({ message: "Please Enter Email!" });
}
// Find the Retail Distributor by email
const retailDistributor = await RetailDistributor.findOne({ email });
if (!retailDistributor) {
return res.status(404).json({ message: "Retail Distributor not found" });
}
// Generate a random password
const newPassword = password.randomPassword({
length: 12,
characters: [
{ characters: password.upper, exactly: 1 }, // At least 1 uppercase letter
{ characters: password.symbols, exactly: 1 }, // At least 1 symbol
password.lower, // Lowercase letters
password.digits, // Digits
],
});
// Update the retail distributor's password
retailDistributor.password = newPassword;
await retailDistributor.save(); // The pre-save hook in your schema will handle password hashing
// Send an email to the retail distributor with the new password
await sendEmail({
to: retailDistributor.email,
from: process.env.SEND_EMAIL_FROM,
subject: `Cheminova Password Recovery`,
html: `Your new password is: ${newPassword}
If you did not request this, please ignore this email.`,
});
// Respond with success message
return res.status(200).json({
success: true,
message: `Email sent to ${retailDistributor.email} successfully`,
});
} catch (error) {
console.error("Error during password reset:", error);
return res.status(500).json({
message: error.message || "Something went wrong!",
});
}
};
export const ResetPassword = async (req, res) => {
const id = req.params.id;
// console.log(id);
if (!id) {
return res
.status(400)
.json({ message: "Invalid request. ID is required." });
}
try {
const Retailers = await RetailDistributor.findById(id);
if (!Retailers) {
return res.status(404).json({ message: "Retailers not found" });
}
// Generate a new random password
const newPassword = password.randomPassword({
length: 12,
characters: [
{ characters: password.upper, exactly: 1 },
{ characters: password.symbols, exactly: 1 },
password.lower,
password.digits,
],
});
// console.log(newPassword);
// Update the Retailer's password
Retailers.password = newPassword;
await Retailers.save();
// Send email with the new credentials
await sendEmail({
to: `${Retailers.email}`, // Recipient email
from: `${process.env.SEND_EMAIL_FROM}`, // Sender email
subject: "Cheminova Account Credentials",
html: `
Dear ${Retailers.name},
Your account credentials have been updated. Please find your new login details below:
Email: ${Retailers.email}
Password: ${newPassword}
Please use these credentials to log in to your account. For security reasons, it's recommended to change your password after logging in.
If you did not request this change, please contact our support team immediately.
Best regards,
Cheminova Support Team
`, }); // console.log(Retailers); res.status(200).json({ success: true, message: `Account credentials sent to ${Retailers.email} successfully.`, }); } catch (error) { console.error("Error resetting password:", error); res.status(500).json({ success: false, message: error.message || "Something went wrong while resetting the password.", }); } }; export const UpdateProfileRD = async (req, res) => { const { name, mobile_number } = req.body; // Only expecting name from the request body const userId = req.user._id; // User ID from authenticated user try { // Find the RetailDistributor by user ID const retailDistributor = await RetailDistributor.findById(userId); if (!retailDistributor) { return res.status(404).json({ message: "Retail Distributor not found" }); } // Assuming you have an 'isVerified' field in your RetailDistributor schema // Update name if provided if (name) { retailDistributor.name = name; retailDistributor.mobile_number = mobile_number ? mobile_number : retailDistributor.mobile_number; } else { return res.status(400).json({ message: "Name is required" }); } // Save the updated RetailDistributor await retailDistributor.save(); return res.status(200).json({ retailDistributor, message: "Profile updated successfully", }); } catch (error) { return res.status(500).json({ message: error.message || "Server error!", }); } }; export const getmyProfileRD = async (req, res) => { try { // Fetch the profile data using the authenticated user's ID const myData = await RetailDistributor.findById(req.user?._id); if (myData) { return res.status(200).json({ success: true, message: "Profile fetched successfully!", myData, }); } else { return res.status(404).json({ success: false, message: "Retail Distributor not found", }); } } catch (error) { return res.status(500).json({ success: false, message: error.message || "Something went wrong!", }); } }; //reatil distributor mapping export const getAllRetailDistributorApproved = async (req, res) => { try { // Extract query parameters const { page = 1, show = 10, tradename, name, mobile_number, principaldistributor, } = req.query; const skip = (page - 1) * show; // Build the aggregation pipeline let pipeline = [ { $lookup: { from: "kycs", // Assuming your KYC collection is named "kycs" localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, // Unwind kycDetails and allow null/empty arrays { $lookup: { from: "users", // Assuming your User collection is named "users" localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, // Unwind principalDetails and allow null/empty arrays // Lookup for mappedTM (Territory Manager) { $lookup: { from: "territorymanagers", // Assuming your Territory Manager collection localField: "mappedTM", foreignField: "_id", as: "mappedTMDetails", }, }, { $unwind: { path: "$mappedTMDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedTMDetails and allow null/empty arrays // Lookup for mappedSC (Sales Coordinator) { $lookup: { from: "salescoordinators", // Assuming your Sales Coordinator collection localField: "mappedSC", foreignField: "_id", as: "mappedSCDetails", }, }, { $unwind: { path: "$mappedSCDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedSCDetails and allow null/empty arrays // Filter to ensure data exists in kyc or principalDetails { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, // Ensure KYC exists { "principalDetails.name": { $exists: true } }, // Ensure Principal Distributor exists ], }, }, ]; // Add filters based on query parameters // Filter by KYC trade_name (case-insensitive) if (tradename) { pipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by principal_distributer name (case-insensitive) if (principaldistributor) { pipeline.push({ $match: { "principalDetails.name": new RegExp(principaldistributor, "i"), }, }); } // Filter by name (RetailDistributor model's name) if (name) { pipeline.push({ $match: { name: new RegExp(name, "i") }, // Case-insensitive search for name }); } // Filter by mobile_number (RetailDistributor model's mobile number) if (mobile_number) { pipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, // Case-insensitive search for mobile_number }); } // Project only the required fields pipeline.push({ $project: { _id: 1, // RetailDistributor ID uniqueId: 1, // RetailDistributor uniqueId name: 1, // RetailDistributor name mobile_number: 1, // RetailDistributor mobile_number email: 1, // RetailDistributor email "kycDetails.trade_name": 1, // Only trade_name from kyc "principalDetails.name": 1, // Only name from principal_distributer "mappedTMDetails.name": 1, // Only name from mappedTM (Territory Manager) "mappedSCDetails.name": 1, // Only name from mappedSC (Sales Coordinator) createdAt: 1, // For sorting }, }); // Pagination and sorting pipeline.push({ $sort: { createdAt: -1 } }); pipeline.push({ $skip: skip }); pipeline.push({ $limit: parseInt(show) }); // Execute the aggregation pipeline const Retaildistributor = await RetailDistributor.aggregate(pipeline); // Get total count of documents matching the query const countPipeline = [ { $lookup: { from: "kycs", localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "users", localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, { "principalDetails.name": { $exists: true } }, ], }, }, ]; // Apply search filters to count query // Filter by KYC trade_name if (tradename) { countPipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by principal_distributer name if (principaldistributor) { countPipeline.push({ $match: { "principalDetails.name": new RegExp(principaldistributor, "i"), }, }); } // Filter by name if (name) { countPipeline.push({ $match: { name: new RegExp(name, "i") }, }); } // Filter by mobile_number if (mobile_number) { countPipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, }); } // Get the total count of filtered documents const total_data = await RetailDistributor.aggregate([ ...countPipeline, { $count: "total" }, ]); const totalCount = total_data[0]?.total || 0; // Ensure count is zero if no data found // Send the response with pagination data res.status(200).json({ success: true, total_data: totalCount, total_pages: Math.ceil(totalCount / show), Retaildistributor, }); } catch (error) { console.error(error); res.status(500).json({ message: "Server Error", error }); } }; export const getAllRetailDistributorwithTotalorder = async (req, res) => { try { const { page = 1, show = 10, tradename, name, mobile_number, principaldistributor, } = req.query; const skip = (page - 1) * show; // Build the aggregation pipeline let pipeline = [ { $lookup: { from: "kycs", // KYC collection localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "users", // Principal Distributor collection localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, { $lookup: { from: "territorymanagers", // Territory Manager collection localField: "mappedTM", foreignField: "_id", as: "mappedTMDetails", }, }, { $unwind: { path: "$mappedTMDetails", preserveNullAndEmptyArrays: true }, }, { $lookup: { from: "salescoordinators", // Sales Coordinator collection localField: "mappedSC", foreignField: "_id", as: "mappedSCDetails", }, }, { $unwind: { path: "$mappedSCDetails", preserveNullAndEmptyArrays: true }, }, ]; // Add filters based on query parameters const matchConditions = {}; if (tradename) matchConditions["kycDetails.trade_name"] = new RegExp(tradename, "i"); if (name) matchConditions["name"] = new RegExp(name, "i"); if (mobile_number) matchConditions["mobile_number"] = new RegExp(mobile_number, "i"); if (principaldistributor) matchConditions["principalDetails.name"] = new RegExp( principaldistributor, "i" ); if (Object.keys(matchConditions).length) { pipeline.push({ $match: matchConditions }); } // Project required fields early pipeline.push({ $project: { _id: 1, uniqueId: 1, name: 1, mobile_number: 1, email: 1, "kycDetails.trade_name": 1, "principalDetails.name": 1, "mappedTMDetails.name": 1, "mappedSCDetails.name": 1, createdAt: 1, }, }); // Pagination and sorting pipeline.push({ $sort: { createdAt: -1 } }); pipeline.push({ $skip: skip }); pipeline.push({ $limit: parseInt(show) }); // Execute the main aggregation pipeline const Retaildistributor = await RetailDistributor.aggregate(pipeline); // Aggregate orders data for each distributor const orderStats = await RdOrder.aggregate([ { $match: { addedBy: { $in: Retaildistributor.map((user) => user._id) } }, }, { $group: { _id: "$addedBy", // Group by distributor ID totalOrders: { $sum: 1 }, // Count total orders lastOrderDate: { $max: "$createdAt" }, // Get last order date }, }, ]); // Combine order stats with Retaildistributor data const usersWithOrderStats = Retaildistributor.map((user) => { const orderData = orderStats.find( (order) => order._id.toString() === user._id.toString() ); return { ...user, totalOrders: orderData ? orderData.totalOrders : 0, lastOrderDate: orderData ? orderData.lastOrderDate : null, }; }); // Get total count of documents matching the query const countPipeline = [{ $match: matchConditions }, { $count: "total" }]; const total_data = await RetailDistributor.aggregate(countPipeline); const totalCount = total_data[0]?.total || 0; // Send the response res.status(200).json({ success: true, total_data: totalCount, total_pages: Math.ceil(totalCount / show), Retaildistributor: usersWithOrderStats, }); } catch (error) { console.error(error); res.status(500).json({ message: "Server Error", error }); } }; //get RD by Id export const getRDId = async (req, res) => { try { const { id } = req.params; // console.log(id); // Fetch the KYC document from the database by ID const RD = await RetailDistributor.findById(id) .populate("principal_distributer", "name email phone") .populate("addedBy") .populate("kyc") .populate("mappedTM") .populate("mappedSC"); // Check if the KYC document exists if (!RD) { return res.status(404).json({ message: "No RetailDistributor found" }); } // console.log(RD); // Send the fetched KYC document as a response res.status(200).json(RD); } catch (error) { // Handle any errors that occur during the fetch operation res.status(500).json({ message: "Server Error", error }); } }; //mapping export const getAllRDbytmid = async (req, res) => { try { // Extract query parameters const { page = 1, show = 10, tradename, name, mobile_number, principaldistributor, } = req.query; const { mappedTMId } = req.params; // Extract mappedTM ID from request params // Convert mappedTMId to ObjectId if it's a valid ObjectId string let mappedTMObjectId; try { mappedTMObjectId = mongoose.Types.ObjectId(mappedTMId); } catch (error) { return res.status(400).json({ message: "Invalid mappedTM ID format" }); } const skip = (page - 1) * show; // Build the aggregation pipeline let pipeline = [ { $match: { mappedTM: mappedTMObjectId, // Filter by mappedTM ObjectId }, }, { $lookup: { from: "kycs", // Assuming your KYC collection is named "kycs" localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, // Unwind kycDetails and allow null/empty arrays { $lookup: { from: "users", // Assuming your User collection is named "users" localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, // Unwind principalDetails and allow null/empty arrays // Lookup for mappedTM (Territory Manager) { $lookup: { from: "territorymanagers", // Assuming your Territory Manager collection localField: "mappedTM", foreignField: "_id", as: "mappedTMDetails", }, }, { $unwind: { path: "$mappedTMDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedTMDetails and allow null/empty arrays // Lookup for mappedSC (Sales Coordinator) { $lookup: { from: "salescoordinators", // Assuming your Sales Coordinator collection localField: "mappedSC", foreignField: "_id", as: "mappedSCDetails", }, }, { $unwind: { path: "$mappedSCDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedSCDetails and allow null/empty arrays // Filter to ensure data exists in kyc or principalDetails { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, // Ensure KYC exists { "principalDetails.name": { $exists: true } }, // Ensure Principal Distributor exists ], }, }, ]; // Add filters based on query parameters // Filter by KYC trade_name (case-insensitive) if (tradename) { pipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by principal_distributer name (case-insensitive) if (principaldistributor) { pipeline.push({ $match: { "principalDetails.name": new RegExp(principaldistributor, "i"), }, }); } // Filter by name (RetailDistributor model's name) if (name) { pipeline.push({ $match: { name: new RegExp(name, "i") }, // Case-insensitive search for name }); } // Filter by mobile_number (RetailDistributor model's mobile number) if (mobile_number) { pipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, // Case-insensitive search for mobile_number }); } // Project only the required fields pipeline.push({ $project: { _id: 1, // RetailDistributor ID uniqueId: 1, // RetailDistributor uniqueId name: 1, // RetailDistributor name mobile_number: 1, // RetailDistributor mobile_number email: 1, // RetailDistributor email "kycDetails.trade_name": 1, // Only trade_name from kyc "principalDetails.name": 1, // Only name from principal_distributer "mappedTMDetails.name": 1, // Only name from mappedTM (Territory Manager) "mappedSCDetails.name": 1, // Only name from mappedSC (Sales Coordinator) createdAt: 1, // For sorting }, }); // Pagination and sorting pipeline.push({ $sort: { createdAt: -1 } }); pipeline.push({ $skip: skip }); pipeline.push({ $limit: parseInt(show) }); // Execute the aggregation pipeline const Retaildistributor = await RetailDistributor.aggregate(pipeline); // Get total count of documents matching the query const countPipeline = [ { $match: { mappedTM: mappedTMObjectId, // Filter by mappedTM ObjectId }, }, { $lookup: { from: "kycs", localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "users", localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, { "principalDetails.name": { $exists: true } }, ], }, }, ]; // Apply search filters to count query // Filter by KYC trade_name if (tradename) { countPipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by principal_distributer name if (principaldistributor) { countPipeline.push({ $match: { "principalDetails.name": new RegExp(principaldistributor, "i"), }, }); } // Filter by name if (name) { countPipeline.push({ $match: { name: new RegExp(name, "i") }, }); } // Filter by mobile_number if (mobile_number) { countPipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, }); } // Get the total count of filtered documents const total_data = await RetailDistributor.aggregate([ ...countPipeline, { $count: "total" }, ]); const totalCount = total_data[0]?.total || 0; // Ensure count is zero if no data found // Send the response with pagination data res.status(200).json({ success: true, total_data: totalCount, total_pages: Math.ceil(totalCount / show), Retaildistributor, }); } catch (error) { console.error(error); res.status(500).json({ message: "Server Error", error }); } }; export const getAllRDbyscid = async (req, res) => { try { // Extract query parameters const { page = 1, show = 10, tradename, name, mobile_number, principaldistributor, } = req.query; const { mappedSCId } = req.params; // Extract mappedTM ID from request params // Convert mappedSCId to ObjectId if it's a valid ObjectId string let mappedSCObjectId; try { mappedSCObjectId = mongoose.Types.ObjectId(mappedSCId); } catch (error) { return res.status(400).json({ message: "Invalid mappedSC ID format" }); } const skip = (page - 1) * show; // Build the aggregation pipeline let pipeline = [ { $match: { mappedSC: mappedSCObjectId, // Filter by mappedSC ObjectId }, }, { $lookup: { from: "kycs", // Assuming your KYC collection is named "kycs" localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, // Unwind kycDetails and allow null/empty arrays { $lookup: { from: "users", // Assuming your User collection is named "users" localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, // Unwind principalDetails and allow null/empty arrays // Lookup for mappedTM (Territory Manager) { $lookup: { from: "territorymanagers", // Assuming your Territory Manager collection localField: "mappedTM", foreignField: "_id", as: "mappedTMDetails", }, }, { $unwind: { path: "$mappedTMDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedTMDetails and allow null/empty arrays // Lookup for mappedSC (Sales Coordinator) { $lookup: { from: "salescoordinators", // Assuming your Sales Coordinator collection localField: "mappedSC", foreignField: "_id", as: "mappedSCDetails", }, }, { $unwind: { path: "$mappedSCDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedSCDetails and allow null/empty arrays // Filter to ensure data exists in kyc or principalDetails { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, // Ensure KYC exists { "principalDetails.name": { $exists: true } }, // Ensure Principal Distributor exists ], }, }, ]; // Add filters based on query parameters // Filter by KYC trade_name (case-insensitive) if (tradename) { pipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by principal_distributer name (case-insensitive) if (principaldistributor) { pipeline.push({ $match: { "principalDetails.name": new RegExp(principaldistributor, "i"), }, }); } // Filter by name (RetailDistributor model's name) if (name) { pipeline.push({ $match: { name: new RegExp(name, "i") }, // Case-insensitive search for name }); } // Filter by mobile_number (RetailDistributor model's mobile number) if (mobile_number) { pipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, // Case-insensitive search for mobile_number }); } // Project only the required fields pipeline.push({ $project: { _id: 1, // RetailDistributor ID uniqueId: 1, // RetailDistributor uniqueId name: 1, // RetailDistributor name mobile_number: 1, // RetailDistributor mobile_number email: 1, // RetailDistributor email "kycDetails.trade_name": 1, // Only trade_name from kyc "principalDetails.name": 1, // Only name from principal_distributer "mappedTMDetails.name": 1, // Only name from mappedTM (Territory Manager) "mappedSCDetails.name": 1, // Only name from mappedSC (Sales Coordinator) createdAt: 1, // For sorting }, }); // Pagination and sorting pipeline.push({ $sort: { createdAt: -1 } }); pipeline.push({ $skip: skip }); pipeline.push({ $limit: parseInt(show) }); // Execute the aggregation pipeline const Retaildistributor = await RetailDistributor.aggregate(pipeline); // Get total count of documents matching the query const countPipeline = [ { $match: { mappedSC: mappedSCObjectId, // Filter by mappedTM ObjectId }, }, { $lookup: { from: "kycs", localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "users", localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, { "principalDetails.name": { $exists: true } }, ], }, }, ]; // Apply search filters to count query // Filter by KYC trade_name if (tradename) { countPipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by principal_distributer name if (principaldistributor) { countPipeline.push({ $match: { "principalDetails.name": new RegExp(principaldistributor, "i"), }, }); } // Filter by name if (name) { countPipeline.push({ $match: { name: new RegExp(name, "i") }, }); } // Filter by mobile_number if (mobile_number) { countPipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, }); } // Get the total count of filtered documents const total_data = await RetailDistributor.aggregate([ ...countPipeline, { $count: "total" }, ]); const totalCount = total_data[0]?.total || 0; // Ensure count is zero if no data found // Send the response with pagination data res.status(200).json({ success: true, total_data: totalCount, total_pages: Math.ceil(totalCount / show), Retaildistributor, }); } catch (error) { console.error(error); res.status(500).json({ message: "Server Error", error }); } }; export const getAllRDbypdid = async (req, res) => { try { // Extract query parameters const { page = 1, show = 10, tradename, name, mobile_number } = req.query; const { mappedPDId } = req.params; // Extract mappedTM ID from request params // Convert mappedPDId to ObjectId if it's a valid ObjectId string let mappedPDObjectId; try { mappedPDObjectId = mongoose.Types.ObjectId(mappedPDId); } catch (error) { return res.status(400).json({ message: "Invalid mappedPD ID format" }); } const skip = (page - 1) * show; // Build the aggregation pipeline let pipeline = [ { $match: { principal_distributer: mappedPDObjectId, // Filter by principal_distributer ObjectId }, }, { $lookup: { from: "kycs", // Assuming your KYC collection is named "kycs" localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, // Unwind kycDetails and allow null/empty arrays { $lookup: { from: "users", // Assuming your User collection is named "users" localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, // Unwind principalDetails and allow null/empty arrays // Lookup for mappedTM (Territory Manager) { $lookup: { from: "territorymanagers", // Assuming your Territory Manager collection localField: "mappedTM", foreignField: "_id", as: "mappedTMDetails", }, }, { $unwind: { path: "$mappedTMDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedTMDetails and allow null/empty arrays // Lookup for mappedSC (Sales Coordinator) { $lookup: { from: "salescoordinators", // Assuming your Sales Coordinator collection localField: "mappedSC", foreignField: "_id", as: "mappedSCDetails", }, }, { $unwind: { path: "$mappedSCDetails", preserveNullAndEmptyArrays: true }, }, // Unwind mappedSCDetails and allow null/empty arrays // Filter to ensure data exists in kyc or principalDetails { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, // Ensure KYC exists { "principalDetails.name": { $exists: true } }, // Ensure Principal Distributor exists ], }, }, ]; // Add filters based on query parameters // Filter by KYC trade_name (case-insensitive) if (tradename) { pipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by name (RetailDistributor model's name) if (name) { pipeline.push({ $match: { name: new RegExp(name, "i") }, // Case-insensitive search for name }); } // Filter by mobile_number (RetailDistributor model's mobile number) if (mobile_number) { pipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, // Case-insensitive search for mobile_number }); } // Project only the required fields pipeline.push({ $project: { _id: 1, // RetailDistributor ID uniqueId: 1, // RetailDistributor uniqueId name: 1, // RetailDistributor name mobile_number: 1, // RetailDistributor mobile_number email: 1, // RetailDistributor email "kycDetails.trade_name": 1, // Only trade_name from kyc "mappedTMDetails.name": 1, // Only name from mappedTM (Territory Manager) "mappedSCDetails.name": 1, // Only name from mappedSC (Sales Coordinator) "principalDetails.name": 1, // Only name from principal_distributer createdAt: 1, // For sorting }, }); // Pagination and sorting pipeline.push({ $sort: { createdAt: -1 } }); pipeline.push({ $skip: skip }); pipeline.push({ $limit: parseInt(show) }); // Execute the aggregation pipeline const Retaildistributor = await RetailDistributor.aggregate(pipeline); // Get total count of documents matching the query const countPipeline = [ { $match: { principal_distributer: mappedPDObjectId, // Filter by mappedTM ObjectId }, }, { $lookup: { from: "kycs", localField: "kyc", foreignField: "_id", as: "kycDetails", }, }, { $unwind: { path: "$kycDetails", preserveNullAndEmptyArrays: true } }, { $lookup: { from: "users", localField: "principal_distributer", foreignField: "_id", as: "principalDetails", }, }, { $unwind: { path: "$principalDetails", preserveNullAndEmptyArrays: true, }, }, { $match: { $or: [ { "kycDetails.trade_name": { $exists: true } }, { "principalDetails.name": { $exists: true } }, ], }, }, ]; // Apply search filters to count query // Filter by KYC trade_name if (tradename) { countPipeline.push({ $match: { "kycDetails.trade_name": new RegExp(tradename, "i") }, }); } // Filter by name if (name) { countPipeline.push({ $match: { name: new RegExp(name, "i") }, }); } // Filter by mobile_number if (mobile_number) { countPipeline.push({ $match: { mobile_number: new RegExp(mobile_number, "i") }, }); } // Get the total count of filtered documents const total_data = await RetailDistributor.aggregate([ ...countPipeline, { $count: "total" }, ]); const totalCount = total_data[0]?.total || 0; // Ensure count is zero if no data found // Send the response with pagination data res.status(200).json({ success: true, total_data: totalCount, total_pages: Math.ceil(totalCount / show), Retaildistributor, }); } catch (error) { console.error(error); res.status(500).json({ message: "Server Error", error }); } }; export const updateRDMapped = async (req, res) => { try { const { id } = req.params; const { principal_distributor, mappedTM, mappedSC } = req.body; // console.log(req.body); // console.log(req.params); // Find the RetailDistributor document by ID const RD = await RetailDistributor.findById(id); if (!RD) { return res.status(404).json({ message: "RetailDistributor not found" }); } // Update the fields if provided in the request body if (principal_distributor) { RD.principal_distributer = principal_distributor; } if (mappedTM) { RD.mappedTM = mappedTM; } if (mappedSC) { RD.mappedSC = mappedSC; } // Save the updated document await RD.save(); res.status(200).json({ message: "RetailDistributor record updated successfully", data: RD, }); } catch (error) { // Handle any errors during the update res.status(500).json({ message: "Error updating RD record", error: error.message, }); } }; export const updateunmapRD = async (req, res) => { try { const { id } = req.params; const { principal_distributor, mappedTM, mappedSC } = req.body; // Find the RetailDistributor document by ID const RD = await RetailDistributor.findById(id); if (!RD) { return res.status(404).json({ message: "RetailDistributor not found" }); } // Update the fields if provided in the request body if (principal_distributor) { RD.principal_distributer = null; } if (mappedTM) { RD.mappedTM = null; } if (mappedSC) { RD.mappedSC = null; } // Save the updated document await RD.save(); res.status(200).json({ message: "RetailDistributor record updated successfully", data: RD, }); } catch (error) { // Handle any errors during the update res.status(500).json({ message: "Error updating RD record", error: error.message, }); } }; export const saveFCMTokenForRD = async (req, res) => { const { fcmToken } = req.body; const userId = req.user._id; try { // Fetch the current FCM token for the user const user = await RetailDistributor.findById(userId); if (!user) { return res.status(404).send("User not found"); } // Check if the new FCM token is different from the current one if (user.fcm_token && user.fcm_token === fcmToken) { return res.status(200).send("FCM Token is already up to date"); } // Update the FCM token user.fcm_token = fcmToken; await user.save(); res.status(200).send("FCM Token saved successfully"); } catch (error) { console.error("Error saving FCM Token:", error); res.status(500).send("Internal Server Error"); } }; export const generateRetailerReport = async (req, res) => { try { // Fetch retailer data and populate required fields const retailers = await RetailDistributor.find() .populate("kyc", "trade_name pan_number aadhar_number gst_number state city district address pincode") .populate("principal_distributer", "name") .populate("mappedTM", "name") .populate("mappedSC", "name") .select("uniqueId name email mobile_number"); if (!retailers.length) { return res.status(404).json({ message: "No retailers found." }); } // Prepare data for the sheet const retailerData = retailers.map((retailer) => ({ "Retailer ID": retailer.uniqueId, "Retailer Name": retailer.name, Email: retailer.email, "Mobile Number": retailer.mobile_number, "Trade Name": retailer.kyc?.trade_name || "", "PAN Number": retailer.kyc?.pan_number || "", "Aadhaar Number": retailer.kyc?.aadhar_number || "", "GST Number": retailer.kyc?.gst_number || "", State: retailer.kyc?.state || "", City: retailer.kyc?.city || "", District: retailer.kyc?.district || "", Address: retailer.kyc?.address || "", Pincode: retailer.kyc?.pincode || "", PrincipalDistributor: retailer.principal_distributer?.name || "N/A", TerritoryManager: retailer.mappedTM?.name || "N/A", SalesCoordinator: retailer.mappedSC?.name || "N/A", })); // Define headers for the worksheet const headers = [ [ "Retailer ID", "Retailer Name", "Email", "Mobile Number", "Trade Name", "PAN Number", "Aadhaar Number", "GST Number", "State", "City", "District", "Address", "Pincode", "Principal Distributor", "Territory Manager", "Sales Coordinator", ], ]; // Create worksheet with headers const worksheet = XLSX.utils.aoa_to_sheet(headers); // Append retailer data below headers XLSX.utils.sheet_add_json(worksheet, retailerData, { skipHeader: true, origin: "A2", }); // Set column widths based on content length const columnWidths = headers[0].map((header, index) => { let maxLength = header.length; for (const row of retailerData) { const cellContent = row[header]?.toString() || ""; maxLength = Math.max(maxLength, cellContent.length); } return { wch: maxLength + 2 }; }); worksheet["!cols"] = columnWidths; // Create workbook and append worksheet const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, "RetailerReport"); // Write workbook to a buffer const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "buffer", }); // Send Excel file as response res.setHeader( "Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ); res.setHeader( "Content-Disposition", "attachment; filename=RetailerReport.xlsx" ); res.send(excelBuffer); } catch (error) { console.error("Error generating retailer report:", error); res.status(500).json({ message: "Failed to generate report" }); } };