|
|
@ -8392,6 +8392,256 @@ exports.getLongTermCustomerDetails = async (req, reply) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.powerOutageCustomerDetails = async (req, reply) => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const { supportId } = req.params;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!supportId) {
|
|
|
|
|
|
|
|
return reply.code(400).send({ error: "supportId is required" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const supportRecord = await Support.findOne({ supportId }).lean();
|
|
|
|
|
|
|
|
if (!supportRecord) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No support record found for this supportId" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const categorizedIssues = supportRecord.categorizedIssues || [];
|
|
|
|
|
|
|
|
const longTermIssues = categorizedIssues.filter(issue => issue.category === "Power Outage");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!longTermIssues.length) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No Long Term Issues to process" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hardwareIds = [];
|
|
|
|
|
|
|
|
for (const issue of longTermIssues) {
|
|
|
|
|
|
|
|
if (issue.hardwareId) hardwareIds.push(issue.hardwareId);
|
|
|
|
|
|
|
|
if (Array.isArray(issue.hardwareIds)) hardwareIds.push(...issue.hardwareIds);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!hardwareIds.length) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No hardware IDs in Long Term Issues" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fetch related sensors to get customerId
|
|
|
|
|
|
|
|
const sensors = await Insensors.find({
|
|
|
|
|
|
|
|
$or: [
|
|
|
|
|
|
|
|
{ hardwareId: { $in: hardwareIds } },
|
|
|
|
|
|
|
|
{ connected_to: { $in: hardwareIds } }
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!sensors.length) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No sensors found for Long Term hardware" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const customerIds = [...new Set(sensors.map(s => s.customerId))];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const customers = await User.find({ customerId: { $in: customerIds } }).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Build map of movedAt per hardwareId
|
|
|
|
|
|
|
|
const movedAtMap = {};
|
|
|
|
|
|
|
|
for (const issue of longTermIssues) {
|
|
|
|
|
|
|
|
const movedAt = issue.movedAt;
|
|
|
|
|
|
|
|
if (issue.hardwareId) movedAtMap[issue.hardwareId] = movedAt;
|
|
|
|
|
|
|
|
if (Array.isArray(issue.hardwareIds)) {
|
|
|
|
|
|
|
|
issue.hardwareIds.forEach(hid => {
|
|
|
|
|
|
|
|
movedAtMap[hid] = movedAt;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uniqueCustomerMap = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const user of customers) {
|
|
|
|
|
|
|
|
const cid = user.customerId;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!uniqueCustomerMap[cid]) {
|
|
|
|
|
|
|
|
const customerSensorHardwareIds = sensors
|
|
|
|
|
|
|
|
.filter(s => s.customerId === cid)
|
|
|
|
|
|
|
|
.map(s => s.hardwareId || s.tankhardwareId || s.connected_to)
|
|
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const movedTimes = customerSensorHardwareIds
|
|
|
|
|
|
|
|
.map(hid => movedAtMap[hid])
|
|
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
|
|
.sort((a, b) => new Date(b) - new Date(a)); // Descending
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uniqueCustomerMap[cid] = {
|
|
|
|
|
|
|
|
customer: {
|
|
|
|
|
|
|
|
customerId: cid,
|
|
|
|
|
|
|
|
username: user.username || "",
|
|
|
|
|
|
|
|
firstName: user.profile?.firstName || user.firstName || "",
|
|
|
|
|
|
|
|
lastName: user.profile?.lastName || user.lastName || "",
|
|
|
|
|
|
|
|
phone: user.phone || user.profile?.contactNumber || user.alternativeNumber || "",
|
|
|
|
|
|
|
|
email: user.emails?.[0]?.email || user.email || "",
|
|
|
|
|
|
|
|
phoneVerified: user.phoneVerified || false,
|
|
|
|
|
|
|
|
address1: user.profile?.address1 || user.address1 || "",
|
|
|
|
|
|
|
|
address2: user.profile?.address2 || user.address2 || "",
|
|
|
|
|
|
|
|
city: user.profile?.city || user.city || "",
|
|
|
|
|
|
|
|
state: user.profile?.state || user.state || "",
|
|
|
|
|
|
|
|
country: user.profile?.country || user.country || "",
|
|
|
|
|
|
|
|
zip: user.profile?.zip || "",
|
|
|
|
|
|
|
|
notes: user.profile?.notes || "",
|
|
|
|
|
|
|
|
latitude: user.latitude || 0,
|
|
|
|
|
|
|
|
longitude: user.longitude || 0,
|
|
|
|
|
|
|
|
fcmIds: (user.fcmIds || []).filter(fcm => typeof fcm === "string" && fcm.startsWith("d")),
|
|
|
|
|
|
|
|
installationId: user.installationId || "",
|
|
|
|
|
|
|
|
notificationPreferences: {
|
|
|
|
|
|
|
|
allowNotifications: user.allowNotifications || false,
|
|
|
|
|
|
|
|
automaticStartAndStopNotify: user.automaticStartAndStopNotify || false,
|
|
|
|
|
|
|
|
manualStartAndStopNotify: user.manualStartAndStopNotify || false,
|
|
|
|
|
|
|
|
criticalLowWaterAlert: user.criticalLowWaterAlert || false,
|
|
|
|
|
|
|
|
lowWaterAlert: user.lowWaterAlert || false,
|
|
|
|
|
|
|
|
notificationPreference: user.notificationPreference || "never"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
surveyStatus: user.survey_status || "pending",
|
|
|
|
|
|
|
|
buildingName: user.buildingName || "",
|
|
|
|
|
|
|
|
stripePaymentStatus: user.stripePaymentStatus || false,
|
|
|
|
|
|
|
|
stripeSubscriptionStatus: user.stripeSubscriptionStatus || false,
|
|
|
|
|
|
|
|
createdAt: user.createdAt,
|
|
|
|
|
|
|
|
updatedAt: user.updatedAt,
|
|
|
|
|
|
|
|
movedAt: movedTimes[0] || null // ⏳ Latest movedAt
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return reply.send({
|
|
|
|
|
|
|
|
status_code: 200,
|
|
|
|
|
|
|
|
data: Object.values(uniqueCustomerMap)
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error("❌ Error in getLongTermCustomerDetails:", error);
|
|
|
|
|
|
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.outDoorEscalationCustomerDetails = async (req, reply) => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const { supportId } = req.params;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!supportId) {
|
|
|
|
|
|
|
|
return reply.code(400).send({ error: "supportId is required" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const supportRecord = await Support.findOne({ supportId }).lean();
|
|
|
|
|
|
|
|
if (!supportRecord) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No support record found for this supportId" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const categorizedIssues = supportRecord.categorizedIssues || [];
|
|
|
|
|
|
|
|
const longTermIssues = categorizedIssues.filter(issue => issue.category === "OutDoor Escalation");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!longTermIssues.length) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No Long Term Issues to process" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hardwareIds = [];
|
|
|
|
|
|
|
|
for (const issue of longTermIssues) {
|
|
|
|
|
|
|
|
if (issue.hardwareId) hardwareIds.push(issue.hardwareId);
|
|
|
|
|
|
|
|
if (Array.isArray(issue.hardwareIds)) hardwareIds.push(...issue.hardwareIds);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!hardwareIds.length) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No hardware IDs in Long Term Issues" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fetch related sensors to get customerId
|
|
|
|
|
|
|
|
const sensors = await Insensors.find({
|
|
|
|
|
|
|
|
$or: [
|
|
|
|
|
|
|
|
{ hardwareId: { $in: hardwareIds } },
|
|
|
|
|
|
|
|
{ connected_to: { $in: hardwareIds } }
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!sensors.length) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No sensors found for Long Term hardware" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const customerIds = [...new Set(sensors.map(s => s.customerId))];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const customers = await User.find({ customerId: { $in: customerIds } }).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Build map of movedAt per hardwareId
|
|
|
|
|
|
|
|
const movedAtMap = {};
|
|
|
|
|
|
|
|
for (const issue of longTermIssues) {
|
|
|
|
|
|
|
|
const movedAt = issue.movedAt;
|
|
|
|
|
|
|
|
if (issue.hardwareId) movedAtMap[issue.hardwareId] = movedAt;
|
|
|
|
|
|
|
|
if (Array.isArray(issue.hardwareIds)) {
|
|
|
|
|
|
|
|
issue.hardwareIds.forEach(hid => {
|
|
|
|
|
|
|
|
movedAtMap[hid] = movedAt;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const uniqueCustomerMap = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const user of customers) {
|
|
|
|
|
|
|
|
const cid = user.customerId;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!uniqueCustomerMap[cid]) {
|
|
|
|
|
|
|
|
const customerSensorHardwareIds = sensors
|
|
|
|
|
|
|
|
.filter(s => s.customerId === cid)
|
|
|
|
|
|
|
|
.map(s => s.hardwareId || s.tankhardwareId || s.connected_to)
|
|
|
|
|
|
|
|
.filter(Boolean);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const movedTimes = customerSensorHardwareIds
|
|
|
|
|
|
|
|
.map(hid => movedAtMap[hid])
|
|
|
|
|
|
|
|
.filter(Boolean)
|
|
|
|
|
|
|
|
.sort((a, b) => new Date(b) - new Date(a)); // Descending
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uniqueCustomerMap[cid] = {
|
|
|
|
|
|
|
|
customer: {
|
|
|
|
|
|
|
|
customerId: cid,
|
|
|
|
|
|
|
|
username: user.username || "",
|
|
|
|
|
|
|
|
firstName: user.profile?.firstName || user.firstName || "",
|
|
|
|
|
|
|
|
lastName: user.profile?.lastName || user.lastName || "",
|
|
|
|
|
|
|
|
phone: user.phone || user.profile?.contactNumber || user.alternativeNumber || "",
|
|
|
|
|
|
|
|
email: user.emails?.[0]?.email || user.email || "",
|
|
|
|
|
|
|
|
phoneVerified: user.phoneVerified || false,
|
|
|
|
|
|
|
|
address1: user.profile?.address1 || user.address1 || "",
|
|
|
|
|
|
|
|
address2: user.profile?.address2 || user.address2 || "",
|
|
|
|
|
|
|
|
city: user.profile?.city || user.city || "",
|
|
|
|
|
|
|
|
state: user.profile?.state || user.state || "",
|
|
|
|
|
|
|
|
country: user.profile?.country || user.country || "",
|
|
|
|
|
|
|
|
zip: user.profile?.zip || "",
|
|
|
|
|
|
|
|
notes: user.profile?.notes || "",
|
|
|
|
|
|
|
|
latitude: user.latitude || 0,
|
|
|
|
|
|
|
|
longitude: user.longitude || 0,
|
|
|
|
|
|
|
|
fcmIds: (user.fcmIds || []).filter(fcm => typeof fcm === "string" && fcm.startsWith("d")),
|
|
|
|
|
|
|
|
installationId: user.installationId || "",
|
|
|
|
|
|
|
|
notificationPreferences: {
|
|
|
|
|
|
|
|
allowNotifications: user.allowNotifications || false,
|
|
|
|
|
|
|
|
automaticStartAndStopNotify: user.automaticStartAndStopNotify || false,
|
|
|
|
|
|
|
|
manualStartAndStopNotify: user.manualStartAndStopNotify || false,
|
|
|
|
|
|
|
|
criticalLowWaterAlert: user.criticalLowWaterAlert || false,
|
|
|
|
|
|
|
|
lowWaterAlert: user.lowWaterAlert || false,
|
|
|
|
|
|
|
|
notificationPreference: user.notificationPreference || "never"
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
surveyStatus: user.survey_status || "pending",
|
|
|
|
|
|
|
|
buildingName: user.buildingName || "",
|
|
|
|
|
|
|
|
stripePaymentStatus: user.stripePaymentStatus || false,
|
|
|
|
|
|
|
|
stripeSubscriptionStatus: user.stripeSubscriptionStatus || false,
|
|
|
|
|
|
|
|
createdAt: user.createdAt,
|
|
|
|
|
|
|
|
updatedAt: user.updatedAt,
|
|
|
|
|
|
|
|
movedAt: movedTimes[0] || null // ⏳ Latest movedAt
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return reply.send({
|
|
|
|
|
|
|
|
status_code: 200,
|
|
|
|
|
|
|
|
data: Object.values(uniqueCustomerMap)
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
|
|
console.error("❌ Error in getLongTermCustomerDetails:", error);
|
|
|
|
|
|
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
exports.getLongTermIssuesByCustomer = async (req, reply) => {
|
|
|
|
exports.getLongTermIssuesByCustomer = async (req, reply) => {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const { supportId, customerId } = req.params;
|
|
|
|
const { supportId, customerId } = req.params;
|
|
|
@ -8540,6 +8790,301 @@ exports.getLongTermIssuesByCustomer = async (req, reply) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.getPowerOutageIssuesByCustomer = async (req, reply) => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const { supportId, customerId } = req.params;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!supportId || !customerId) {
|
|
|
|
|
|
|
|
return reply.code(400).send({ error: "supportId and customerId are required in path params" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const support = await Support.findOne({ supportId }).lean();
|
|
|
|
|
|
|
|
if (!support) return reply.code(404).send({ message: "Support record not found" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const longTermIssues = (support.categorizedIssues || []).filter(i => i.category === "Power Outage");
|
|
|
|
|
|
|
|
if (!longTermIssues.length) return reply.code(404).send({ message: "No Long Term Issues found" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hardwareIds = [...new Set(longTermIssues.map(i => i.hardwareId).filter(Boolean))];
|
|
|
|
|
|
|
|
const sensors = await Insensors.find({
|
|
|
|
|
|
|
|
customerId,
|
|
|
|
|
|
|
|
hardwareId: { $in: hardwareIds }
|
|
|
|
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!sensors.length) return reply.code(404).send({ message: "No sensors found for this customer" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const orders = await Order.find({ customerId }).lean();
|
|
|
|
|
|
|
|
const orderMap = {};
|
|
|
|
|
|
|
|
for (const order of orders) {
|
|
|
|
|
|
|
|
for (const conn of order.master_connections || []) {
|
|
|
|
|
|
|
|
const trimmedId = (conn.hardwareId || "").trim();
|
|
|
|
|
|
|
|
if (trimmedId) {
|
|
|
|
|
|
|
|
orderMap[trimmedId] = {
|
|
|
|
|
|
|
|
masterName: conn.master_name?.trim() || "",
|
|
|
|
|
|
|
|
location: conn.location?.trim() || ""
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const issueMap = {};
|
|
|
|
|
|
|
|
for (const issue of longTermIssues) {
|
|
|
|
|
|
|
|
issueMap[issue.hardwareId] = issue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const disconnectedIssues = [];
|
|
|
|
|
|
|
|
for (const master of sensors.filter(s => s.type === "master")) {
|
|
|
|
|
|
|
|
const slaves = await Insensors.find({ connected_to: master.hardwareId, customerId }).lean();
|
|
|
|
|
|
|
|
const latestIotData = await IotData.findOne({ hardwareId: master.hardwareId }).sort({ date: -1 }).lean();
|
|
|
|
|
|
|
|
const now = moment.tz("Asia/Kolkata");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let gsmConnected = false;
|
|
|
|
|
|
|
|
if (latestIotData?.date) {
|
|
|
|
|
|
|
|
const gsmTime = moment.tz(latestIotData.date, "Asia/Kolkata");
|
|
|
|
|
|
|
|
gsmConnected = now.diff(gsmTime, "minutes") <= 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const slaveDetails = await Promise.all(slaves.map(async (slave) => {
|
|
|
|
|
|
|
|
const slaveHardwareId = slave.tankhardwareId?.trim();
|
|
|
|
|
|
|
|
const matchedTank = latestIotData?.tanks?.find(tank => tank.tankhardwareId === slaveHardwareId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let loraConnected = false;
|
|
|
|
|
|
|
|
if (matchedTank?.date && matchedTank?.tankHeight !== "0") {
|
|
|
|
|
|
|
|
const tankTime = moment.tz(matchedTank.date, "Asia/Kolkata");
|
|
|
|
|
|
|
|
loraConnected = now.diff(tankTime, "minutes") <= 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const tankInfo = await Tank.findOne({
|
|
|
|
|
|
|
|
$or: [
|
|
|
|
|
|
|
|
{ hardwareId: slaveHardwareId },
|
|
|
|
|
|
|
|
{ tankhardwareId: slaveHardwareId }
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const slaveComments = (support.comments || []).filter(
|
|
|
|
|
|
|
|
comment => comment.hardwareId === slave.hardwareId && comment.customerId === customerId
|
|
|
|
|
|
|
|
).map(c => ({
|
|
|
|
|
|
|
|
text: c.text,
|
|
|
|
|
|
|
|
commentsTime: c.createdAt ? moment(c.createdAt).tz("Asia/Kolkata").format("DD-MM-YYYY HH:mm") : null
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
hardwareId: slave.tankhardwareId,
|
|
|
|
|
|
|
|
tankName: slave.tankName || "",
|
|
|
|
|
|
|
|
location: slave.tankLocation || "",
|
|
|
|
|
|
|
|
connected_status: loraConnected ? "connected" : "disconnected",
|
|
|
|
|
|
|
|
connected_to: slave.connected_to || "",
|
|
|
|
|
|
|
|
gsm_last_check_time: slave.gsm_last_check_time || null,
|
|
|
|
|
|
|
|
gsm_last_disconnect_time: slave.gsm_last_disconnect_time || null,
|
|
|
|
|
|
|
|
lora_last_disconnect_time: slave.lora_last_disconnect_time || null,
|
|
|
|
|
|
|
|
connected_gsm_date: slave.connected_gsm_date || "",
|
|
|
|
|
|
|
|
connected_gsm_time: slave.connected_gsm_time || "",
|
|
|
|
|
|
|
|
connected_lora_date: slave.connected_lora_date || "",
|
|
|
|
|
|
|
|
connected_lora_time: slave.connected_lora_time || "",
|
|
|
|
|
|
|
|
support_lora_last_check_time: slave.support_lora_last_check_time || null,
|
|
|
|
|
|
|
|
masterName: orderMap[master.hardwareId?.trim()]?.masterName || "",
|
|
|
|
|
|
|
|
type: "slave",
|
|
|
|
|
|
|
|
typeOfWater: tankInfo?.typeOfWater || "",
|
|
|
|
|
|
|
|
outDoor_status: slave.outDoor_status || "inprogress"
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const masterComments = (support.comments || []).filter(
|
|
|
|
|
|
|
|
comment => comment.hardwareId === master.hardwareId && comment.customerId === customerId
|
|
|
|
|
|
|
|
).map(c => ({
|
|
|
|
|
|
|
|
text: c.text,
|
|
|
|
|
|
|
|
commentsTime: c.createdAt ? moment(c.createdAt).tz("Asia/Kolkata").format("DD-MM-YYYY HH:mm") : null
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const orderDetails = orderMap[master.hardwareId?.trim()] || {};
|
|
|
|
|
|
|
|
const issue = issueMap[master.hardwareId];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
disconnectedIssues.push({
|
|
|
|
|
|
|
|
hardwareId: master.hardwareId,
|
|
|
|
|
|
|
|
masterName: orderDetails.masterName || "",
|
|
|
|
|
|
|
|
location: orderDetails.location || "",
|
|
|
|
|
|
|
|
type: "master",
|
|
|
|
|
|
|
|
connected_status: gsmConnected ? "connected" : "disconnected",
|
|
|
|
|
|
|
|
connected_slave_count: slaveDetails.length,
|
|
|
|
|
|
|
|
gsm_last_check_time: master.gsm_last_check_time || null,
|
|
|
|
|
|
|
|
gsm_last_disconnect_time: master.gsm_last_disconnect_time || null,
|
|
|
|
|
|
|
|
lora_last_disconnect_time: master.lora_last_disconnect_time || null,
|
|
|
|
|
|
|
|
connected_gsm_date: master.connected_gsm_date || "",
|
|
|
|
|
|
|
|
connected_gsm_time: master.connected_gsm_time || "",
|
|
|
|
|
|
|
|
connected_lora_date: master.connected_lora_date || "",
|
|
|
|
|
|
|
|
connected_lora_time: master.connected_lora_time || "",
|
|
|
|
|
|
|
|
support_gm_last_check_time: master.support_gsm_last_check_time || null,
|
|
|
|
|
|
|
|
connected_slaves: slaveDetails,
|
|
|
|
|
|
|
|
comments: masterComments,
|
|
|
|
|
|
|
|
outDoor_status: master.outDoor_status || "inprogress",
|
|
|
|
|
|
|
|
movedAt: issue?.movedAt || null,
|
|
|
|
|
|
|
|
resolvedAt: issue?.resolvedAt || null,
|
|
|
|
|
|
|
|
category: issue?.category || "Uncategorized",
|
|
|
|
|
|
|
|
hardwareList: master.hardwareList || {},
|
|
|
|
|
|
|
|
assignedTo: issue?.assignedTo || null
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return reply.send({
|
|
|
|
|
|
|
|
status_code: 200,
|
|
|
|
|
|
|
|
supportId,
|
|
|
|
|
|
|
|
customerId,
|
|
|
|
|
|
|
|
totalMasters: disconnectedIssues.length,
|
|
|
|
|
|
|
|
disconnectedIssues
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
|
|
console.error("❌ Error in getLongTermIssuesByCustomer:", err);
|
|
|
|
|
|
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.getOutDoorEscalationIssuesByCustomer = async (req, reply) => {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const { supportId, customerId } = req.params;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!supportId || !customerId) {
|
|
|
|
|
|
|
|
return reply.code(400).send({ error: "supportId and customerId are required in path params" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const support = await Support.findOne({ supportId }).lean();
|
|
|
|
|
|
|
|
if (!support) return reply.code(404).send({ message: "Support record not found" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const longTermIssues = (support.categorizedIssues || []).filter(i => i.category === "OutDoor Escalation");
|
|
|
|
|
|
|
|
if (!longTermIssues.length) return reply.code(404).send({ message: "No Long Term Issues found" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const hardwareIds = [...new Set(longTermIssues.map(i => i.hardwareId).filter(Boolean))];
|
|
|
|
|
|
|
|
const sensors = await Insensors.find({
|
|
|
|
|
|
|
|
customerId,
|
|
|
|
|
|
|
|
hardwareId: { $in: hardwareIds }
|
|
|
|
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!sensors.length) return reply.code(404).send({ message: "No sensors found for this customer" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const orders = await Order.find({ customerId }).lean();
|
|
|
|
|
|
|
|
const orderMap = {};
|
|
|
|
|
|
|
|
for (const order of orders) {
|
|
|
|
|
|
|
|
for (const conn of order.master_connections || []) {
|
|
|
|
|
|
|
|
const trimmedId = (conn.hardwareId || "").trim();
|
|
|
|
|
|
|
|
if (trimmedId) {
|
|
|
|
|
|
|
|
orderMap[trimmedId] = {
|
|
|
|
|
|
|
|
masterName: conn.master_name?.trim() || "",
|
|
|
|
|
|
|
|
location: conn.location?.trim() || ""
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const issueMap = {};
|
|
|
|
|
|
|
|
for (const issue of longTermIssues) {
|
|
|
|
|
|
|
|
issueMap[issue.hardwareId] = issue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const disconnectedIssues = [];
|
|
|
|
|
|
|
|
for (const master of sensors.filter(s => s.type === "master")) {
|
|
|
|
|
|
|
|
const slaves = await Insensors.find({ connected_to: master.hardwareId, customerId }).lean();
|
|
|
|
|
|
|
|
const latestIotData = await IotData.findOne({ hardwareId: master.hardwareId }).sort({ date: -1 }).lean();
|
|
|
|
|
|
|
|
const now = moment.tz("Asia/Kolkata");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let gsmConnected = false;
|
|
|
|
|
|
|
|
if (latestIotData?.date) {
|
|
|
|
|
|
|
|
const gsmTime = moment.tz(latestIotData.date, "Asia/Kolkata");
|
|
|
|
|
|
|
|
gsmConnected = now.diff(gsmTime, "minutes") <= 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const slaveDetails = await Promise.all(slaves.map(async (slave) => {
|
|
|
|
|
|
|
|
const slaveHardwareId = slave.tankhardwareId?.trim();
|
|
|
|
|
|
|
|
const matchedTank = latestIotData?.tanks?.find(tank => tank.tankhardwareId === slaveHardwareId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let loraConnected = false;
|
|
|
|
|
|
|
|
if (matchedTank?.date && matchedTank?.tankHeight !== "0") {
|
|
|
|
|
|
|
|
const tankTime = moment.tz(matchedTank.date, "Asia/Kolkata");
|
|
|
|
|
|
|
|
loraConnected = now.diff(tankTime, "minutes") <= 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const tankInfo = await Tank.findOne({
|
|
|
|
|
|
|
|
$or: [
|
|
|
|
|
|
|
|
{ hardwareId: slaveHardwareId },
|
|
|
|
|
|
|
|
{ tankhardwareId: slaveHardwareId }
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const slaveComments = (support.comments || []).filter(
|
|
|
|
|
|
|
|
comment => comment.hardwareId === slave.hardwareId && comment.customerId === customerId
|
|
|
|
|
|
|
|
).map(c => ({
|
|
|
|
|
|
|
|
text: c.text,
|
|
|
|
|
|
|
|
commentsTime: c.createdAt ? moment(c.createdAt).tz("Asia/Kolkata").format("DD-MM-YYYY HH:mm") : null
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
|
|
hardwareId: slave.tankhardwareId,
|
|
|
|
|
|
|
|
tankName: slave.tankName || "",
|
|
|
|
|
|
|
|
location: slave.tankLocation || "",
|
|
|
|
|
|
|
|
connected_status: loraConnected ? "connected" : "disconnected",
|
|
|
|
|
|
|
|
connected_to: slave.connected_to || "",
|
|
|
|
|
|
|
|
gsm_last_check_time: slave.gsm_last_check_time || null,
|
|
|
|
|
|
|
|
gsm_last_disconnect_time: slave.gsm_last_disconnect_time || null,
|
|
|
|
|
|
|
|
lora_last_disconnect_time: slave.lora_last_disconnect_time || null,
|
|
|
|
|
|
|
|
connected_gsm_date: slave.connected_gsm_date || "",
|
|
|
|
|
|
|
|
connected_gsm_time: slave.connected_gsm_time || "",
|
|
|
|
|
|
|
|
connected_lora_date: slave.connected_lora_date || "",
|
|
|
|
|
|
|
|
connected_lora_time: slave.connected_lora_time || "",
|
|
|
|
|
|
|
|
support_lora_last_check_time: slave.support_lora_last_check_time || null,
|
|
|
|
|
|
|
|
masterName: orderMap[master.hardwareId?.trim()]?.masterName || "",
|
|
|
|
|
|
|
|
type: "slave",
|
|
|
|
|
|
|
|
typeOfWater: tankInfo?.typeOfWater || "",
|
|
|
|
|
|
|
|
outDoor_status: slave.outDoor_status || "inprogress"
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const masterComments = (support.comments || []).filter(
|
|
|
|
|
|
|
|
comment => comment.hardwareId === master.hardwareId && comment.customerId === customerId
|
|
|
|
|
|
|
|
).map(c => ({
|
|
|
|
|
|
|
|
text: c.text,
|
|
|
|
|
|
|
|
commentsTime: c.createdAt ? moment(c.createdAt).tz("Asia/Kolkata").format("DD-MM-YYYY HH:mm") : null
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const orderDetails = orderMap[master.hardwareId?.trim()] || {};
|
|
|
|
|
|
|
|
const issue = issueMap[master.hardwareId];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
disconnectedIssues.push({
|
|
|
|
|
|
|
|
hardwareId: master.hardwareId,
|
|
|
|
|
|
|
|
masterName: orderDetails.masterName || "",
|
|
|
|
|
|
|
|
location: orderDetails.location || "",
|
|
|
|
|
|
|
|
type: "master",
|
|
|
|
|
|
|
|
connected_status: gsmConnected ? "connected" : "disconnected",
|
|
|
|
|
|
|
|
connected_slave_count: slaveDetails.length,
|
|
|
|
|
|
|
|
gsm_last_check_time: master.gsm_last_check_time || null,
|
|
|
|
|
|
|
|
gsm_last_disconnect_time: master.gsm_last_disconnect_time || null,
|
|
|
|
|
|
|
|
lora_last_disconnect_time: master.lora_last_disconnect_time || null,
|
|
|
|
|
|
|
|
connected_gsm_date: master.connected_gsm_date || "",
|
|
|
|
|
|
|
|
connected_gsm_time: master.connected_gsm_time || "",
|
|
|
|
|
|
|
|
connected_lora_date: master.connected_lora_date || "",
|
|
|
|
|
|
|
|
connected_lora_time: master.connected_lora_time || "",
|
|
|
|
|
|
|
|
support_gm_last_check_time: master.support_gsm_last_check_time || null,
|
|
|
|
|
|
|
|
connected_slaves: slaveDetails,
|
|
|
|
|
|
|
|
comments: masterComments,
|
|
|
|
|
|
|
|
outDoor_status: master.outDoor_status || "inprogress",
|
|
|
|
|
|
|
|
movedAt: issue?.movedAt || null,
|
|
|
|
|
|
|
|
resolvedAt: issue?.resolvedAt || null,
|
|
|
|
|
|
|
|
category: issue?.category || "Uncategorized",
|
|
|
|
|
|
|
|
hardwareList: master.hardwareList || {},
|
|
|
|
|
|
|
|
assignedTo: issue?.assignedTo || null
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return reply.send({
|
|
|
|
|
|
|
|
status_code: 200,
|
|
|
|
|
|
|
|
supportId,
|
|
|
|
|
|
|
|
customerId,
|
|
|
|
|
|
|
|
totalMasters: disconnectedIssues.length,
|
|
|
|
|
|
|
|
disconnectedIssues
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
|
|
console.error("❌ Error in getLongTermIssuesByCustomer:", err);
|
|
|
|
|
|
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// const bcrypt = require("bcrypt");
|
|
|
|
// const bcrypt = require("bcrypt");
|
|
|
|
|
|
|
|
|
|
|
|