|
|
@ -6232,12 +6232,11 @@ exports.getDisconnectedCustomerDetails = async (req, reply) => {
|
|
|
|
return reply.code(404).send({ message: "No support record found for this supportId" });
|
|
|
|
return reply.code(404).send({ message: "No support record found for this supportId" });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Step 1: Filter unresolved and not moved issues
|
|
|
|
const unresolvedIssues = supportRecord.issues?.filter(
|
|
|
|
const unresolvedIssues = (supportRecord.issues || []).filter(
|
|
|
|
(issue) => issue.resolved === false && issue.movedToCategory === false
|
|
|
|
issue => issue.resolved === false && issue.movedToCategory !== true
|
|
|
|
) || [];
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Step 2: Build set of hardwareIds already in categorizedIssues (moved)
|
|
|
|
// Create set of categorized hardwareIds (masters and slaves)
|
|
|
|
const existingCategorizedHardwareIds = new Set();
|
|
|
|
const existingCategorizedHardwareIds = new Set();
|
|
|
|
(supportRecord.categorizedIssues || []).forEach(issue => {
|
|
|
|
(supportRecord.categorizedIssues || []).forEach(issue => {
|
|
|
|
if (issue.hardwareId) existingCategorizedHardwareIds.add(issue.hardwareId.trim().toLowerCase());
|
|
|
|
if (issue.hardwareId) existingCategorizedHardwareIds.add(issue.hardwareId.trim().toLowerCase());
|
|
|
@ -6248,55 +6247,39 @@ exports.getDisconnectedCustomerDetails = async (req, reply) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// Step 3: Build list of hardwareIds in unresolved issues (excluding moved)
|
|
|
|
// Extract all unresolved hardwareIds
|
|
|
|
const allHardwareIds = new Set();
|
|
|
|
const hardwareIdsArray = new Set();
|
|
|
|
for (const issue of unresolvedIssues) {
|
|
|
|
unresolvedIssues.forEach((issue) => {
|
|
|
|
const issueHardwareId = issue.hardwareId?.trim().toLowerCase();
|
|
|
|
if (issue.hardwareId) hardwareIdsArray.add(issue.hardwareId.trim());
|
|
|
|
const issueSlaveIds = issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || [];
|
|
|
|
if (Array.isArray(issue.hardwareIds)) {
|
|
|
|
|
|
|
|
issue.hardwareIds.forEach((id) => {
|
|
|
|
if (issueHardwareId && !existingCategorizedHardwareIds.has(issueHardwareId)) {
|
|
|
|
if (typeof id === "string") hardwareIdsArray.add(id.trim());
|
|
|
|
allHardwareIds.add(issueHardwareId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const slaveId of issueSlaveIds) {
|
|
|
|
|
|
|
|
if (slaveId && !existingCategorizedHardwareIds.has(slaveId)) {
|
|
|
|
|
|
|
|
allHardwareIds.add(slaveId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Debug: Log issue hardware IDs being considered
|
|
|
|
|
|
|
|
console.log("✅ All issue hardwareIds:", Array.from(allHardwareIds));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Step 4: If no valid hardware IDs left, stop
|
|
|
|
|
|
|
|
if (allHardwareIds.size === 0) {
|
|
|
|
|
|
|
|
return reply.code(404).send({ message: "No unresolved hardware IDs found in issues" });
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const hardwareIdsArray = Array.from(allHardwareIds);
|
|
|
|
const allHardwareIds = [...hardwareIdsArray];
|
|
|
|
|
|
|
|
|
|
|
|
// Step 5: Find disconnected sensors
|
|
|
|
// Fetch disconnected sensors, excluding those already categorized
|
|
|
|
const disconnectedSensors = await Insensors.find({
|
|
|
|
const disconnectedSensorsRaw = await Insensors.find({
|
|
|
|
connected_status: "disconnected",
|
|
|
|
connected_status: "disconnected",
|
|
|
|
$or: [
|
|
|
|
$or: [
|
|
|
|
{ connected_to: { $in: hardwareIdsArray } },
|
|
|
|
{ connected_to: { $in: allHardwareIds } },
|
|
|
|
{ hardwareId: { $in: hardwareIdsArray } },
|
|
|
|
{ hardwareId: { $in: allHardwareIds } },
|
|
|
|
{ tankhardwareId: { $in: hardwareIdsArray } }
|
|
|
|
{ tankhardwareId: { $in: allHardwareIds } }
|
|
|
|
]
|
|
|
|
]
|
|
|
|
}).lean();
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
|
|
// Debug: Log disconnected sensors found
|
|
|
|
const disconnectedSensors = disconnectedSensorsRaw.filter(sensor => {
|
|
|
|
console.log("⚙️ Disconnected sensors matched:", disconnectedSensors.map(s => s.hardwareId));
|
|
|
|
const ids = [
|
|
|
|
|
|
|
|
sensor.hardwareId?.trim().toLowerCase(),
|
|
|
|
if (!disconnectedSensors.length) {
|
|
|
|
sensor.connected_to?.trim().toLowerCase(),
|
|
|
|
return reply.code(404).send({ message: "No disconnected issues found" });
|
|
|
|
sensor.tankhardwareId?.trim().toLowerCase()
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
return !ids.some(id => existingCategorizedHardwareIds.has(id));
|
|
|
|
// Step 6: Get relevant customers
|
|
|
|
});
|
|
|
|
const customerIds = [...new Set(disconnectedSensors.map(s => s.customerId))];
|
|
|
|
|
|
|
|
const customers = await User.find({ customerId: { $in: customerIds } }).lean();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Step 7: Group by customer
|
|
|
|
// Map customerId -> Set of affected hardwareIds
|
|
|
|
const customerHardwareMap = {};
|
|
|
|
const customerHardwareMap = {};
|
|
|
|
for (const sensor of disconnectedSensors) {
|
|
|
|
for (const sensor of disconnectedSensors) {
|
|
|
|
const custId = sensor.customerId;
|
|
|
|
const custId = sensor.customerId;
|
|
|
@ -6306,30 +6289,19 @@ exports.getDisconnectedCustomerDetails = async (req, reply) => {
|
|
|
|
|
|
|
|
|
|
|
|
const sensorHw = sensor.tankhardwareId?.trim().toLowerCase();
|
|
|
|
const sensorHw = sensor.tankhardwareId?.trim().toLowerCase();
|
|
|
|
const sensorConnected = sensor.connected_to?.trim().toLowerCase();
|
|
|
|
const sensorConnected = sensor.connected_to?.trim().toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
// for (const issue of unresolvedIssues) {
|
|
|
|
|
|
|
|
// const allIssueHardwareIds = [
|
|
|
|
|
|
|
|
// ...(issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || []),
|
|
|
|
|
|
|
|
// issue.hardwareId?.trim().toLowerCase()
|
|
|
|
|
|
|
|
// ];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if (
|
|
|
|
|
|
|
|
// (sensorHw && allIssueHardwareIds.includes(sensorHw)) ||
|
|
|
|
|
|
|
|
// (sensorConnected && allIssueHardwareIds.includes(sensorConnected))
|
|
|
|
|
|
|
|
// ) {
|
|
|
|
|
|
|
|
// if (issue.hardwareId && !existingCategorizedHardwareIds.has(issue.hardwareId.trim().toLowerCase())) {
|
|
|
|
|
|
|
|
// customerHardwareMap[custId].add(issue.hardwareId.trim().toLowerCase());
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const sensorHardwareId = sensor.hardwareId?.trim().toLowerCase();
|
|
|
|
const sensorHardwareId = sensor.hardwareId?.trim().toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
for (const issue of unresolvedIssues) {
|
|
|
|
for (const issue of unresolvedIssues) {
|
|
|
|
const allIssueHardwareIds = [
|
|
|
|
const allIssueHardwareIds = [
|
|
|
|
...(issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || []),
|
|
|
|
...(issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || []),
|
|
|
|
issue.hardwareId?.trim().toLowerCase()
|
|
|
|
issue.hardwareId?.trim().toLowerCase()
|
|
|
|
];
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const isCategorizedMatch = [sensorHw, sensorConnected, sensorHardwareId].some(id =>
|
|
|
|
|
|
|
|
id && existingCategorizedHardwareIds.has(id)
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
if (isCategorizedMatch) continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
if (
|
|
|
|
(sensorHw && allIssueHardwareIds.includes(sensorHw)) ||
|
|
|
|
(sensorHw && allIssueHardwareIds.includes(sensorHw)) ||
|
|
|
|
(sensorConnected && allIssueHardwareIds.includes(sensorConnected)) ||
|
|
|
|
(sensorConnected && allIssueHardwareIds.includes(sensorConnected)) ||
|
|
|
@ -6342,94 +6314,53 @@ exports.getDisconnectedCustomerDetails = async (req, reply) => {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Debug: Log map of matched customer hardware
|
|
|
|
const customerDetails = await User.find({
|
|
|
|
console.log("📌 Customer hardware map:", Object.fromEntries(
|
|
|
|
customerId: { $in: Object.keys(customerHardwareMap) }
|
|
|
|
Object.entries(customerHardwareMap).map(([k, v]) => [k, Array.from(v)])
|
|
|
|
}).lean();
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Step 8: Build final response
|
|
|
|
|
|
|
|
const response = [];
|
|
|
|
|
|
|
|
for (const user of customers) {
|
|
|
|
|
|
|
|
const custId = user.customerId;
|
|
|
|
|
|
|
|
const hardwareIdSet = customerHardwareMap[custId] || new Set();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (hardwareIdSet.size === 0) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const relatedIssues = unresolvedIssues.filter(issue => {
|
|
|
|
|
|
|
|
const issueHw = issue.hardwareId?.trim().toLowerCase();
|
|
|
|
|
|
|
|
const hardwareIds = issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || [];
|
|
|
|
|
|
|
|
const allIds = [issueHw, ...hardwareIds];
|
|
|
|
|
|
|
|
return Array.from(hardwareIdSet).some(hw => allIds.includes(hw));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let latestIssueTime = null;
|
|
|
|
|
|
|
|
for (const issue of relatedIssues) {
|
|
|
|
|
|
|
|
if (issue.lastTicketRaisedAt) {
|
|
|
|
|
|
|
|
const issueTime = new Date(issue.lastTicketRaisedAt);
|
|
|
|
|
|
|
|
if (!latestIssueTime || issueTime > latestIssueTime) {
|
|
|
|
|
|
|
|
latestIssueTime = issueTime;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
response.push({
|
|
|
|
const customerResults = customerDetails.map((customer) => {
|
|
|
|
customer: {
|
|
|
|
const affectedHardwareSet = customerHardwareMap[customer.customerId] || new Set();
|
|
|
|
customerId: custId,
|
|
|
|
return {
|
|
|
|
username: user.username || "",
|
|
|
|
customerId: customer.customerId,
|
|
|
|
firstName: user.profile?.firstName || "",
|
|
|
|
buildingName: customer.buildingName || "",
|
|
|
|
lastName: user.profile?.lastName || "",
|
|
|
|
location: customer.location || "",
|
|
|
|
phone: user.phone || user.profile?.contactNumber || "",
|
|
|
|
username: customer.username || "",
|
|
|
|
email: user.emails?.[0]?.email || "",
|
|
|
|
firstName: customer.profile?.firstName || "",
|
|
|
|
phoneVerified: user.phoneVerified || false,
|
|
|
|
lastName: customer.profile?.lastName || "",
|
|
|
|
address1: user.profile?.address1 || "",
|
|
|
|
phone: customer.phone || user.profile?.contactNumber || "",
|
|
|
|
address2: user.profile?.address2 || "",
|
|
|
|
email: customer.emails?.[0]?.email || "",
|
|
|
|
city: user.profile?.city || "",
|
|
|
|
phoneVerified: customer.phoneVerified || false,
|
|
|
|
state: user.profile?.state || "",
|
|
|
|
address1: customer.profile?.address1 || "",
|
|
|
|
country: user.profile?.country || "",
|
|
|
|
address2: customer.profile?.address2 || "",
|
|
|
|
zip: user.profile?.zip || "",
|
|
|
|
city: customer.profile?.city || "",
|
|
|
|
notes: user.profile?.notes || "",
|
|
|
|
latitude: customer.latitude,
|
|
|
|
latitude: user.latitude,
|
|
|
|
longitude: customer.longitude,
|
|
|
|
longitude: user.longitude,
|
|
|
|
totalHardwareIdsCount: affectedHardwareSet.size,
|
|
|
|
fcmIds: (user.fcmIds || []).filter(fcm => typeof fcm === "string" && fcm.startsWith("d")),
|
|
|
|
hardwareIds: [...affectedHardwareSet]
|
|
|
|
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,
|
|
|
|
|
|
|
|
lastTicketRaisedAt: latestIssueTime ? moment(latestIssueTime).format("YYYY-MM-DD HH:mm:ss") : null,
|
|
|
|
|
|
|
|
totalHardwareIdsCount: hardwareIdSet.size
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return reply.send({
|
|
|
|
return reply.code(200).send({
|
|
|
|
status_code: 200,
|
|
|
|
success: true,
|
|
|
|
data: response
|
|
|
|
totalCustomers: customerResults.length,
|
|
|
|
|
|
|
|
customers: customerResults
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
} catch (error) {
|
|
|
|
console.error("❌ Error fetching disconnected customer details:", error);
|
|
|
|
console.error("Error in getDisconnectedCustomerDetails:", error);
|
|
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
|
|
return reply.code(500).send({
|
|
|
|
|
|
|
|
success: false,
|
|
|
|
|
|
|
|
message: "Internal Server Error"
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.getDisconnectedCustomerDetailsByTeamMemberId = async (req, reply) => {
|
|
|
|
exports.getDisconnectedCustomerDetailsByTeamMemberId = async (req, reply) => {
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
const { support_teamMemberId } = req.params;
|
|
|
|
const { support_teamMemberId } = req.params;
|
|
|
|