diff --git a/src/controllers/installationController.js b/src/controllers/installationController.js index c8c3d167..d8f2dd2e 100644 --- a/src/controllers/installationController.js +++ b/src/controllers/installationController.js @@ -635,7 +635,7 @@ exports.assignTeamMemberToQuotation = async (request, reply) => { // 🔁 Raise ticket if applicable const sensor = await Insensors.findOne({ hardwareId }).lean(); if (sensor?.customerId) { - await raiseATicketLikeLogic(sensor.customerId, hardwareId); + //await raiseATicketLikeLogic(sensor.customerId, hardwareId); } return reply.send({ @@ -716,7 +716,7 @@ exports.getByHardwareIdSupport = async (req, reply) => { // 🔁 Raise ticket if applicable const sensor = await Insensors.findOne({ hardwareId }).lean(); if (sensor?.customerId) { - await raiseATicketLikeLogic(sensor.customerId, hardwareId); + //await raiseATicketLikeLogic(sensor.customerId, hardwareId); } return reply.send({ @@ -3863,71 +3863,61 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => { try { if (!customerId || !connected_to) return; + const normalizedConnectedTo = connected_to.trim().toLowerCase(); + const sensors = await Insensors.find({ customerId }).lean(); - const orders = await Order.find({ customerId }).lean(); if (!sensors.length) return; - const normalizedConnectedTo = connected_to.trim().toLowerCase(); - const masterSensor = sensors.find( - s => s.hardwareId?.trim().toLowerCase() === normalizedConnectedTo && s.type === "master" + (s) => s.hardwareId?.trim().toLowerCase() === normalizedConnectedTo && s.type === "master" ); if (!masterSensor) return; - const orderMap = {}; - orders.forEach(order => { - order.master_connections.forEach(conn => { - orderMap[conn.hardwareId] = { - masterName: conn.master_name || null, - location: conn.location || null - }; - }); - }); - - const now = moment.tz("Asia/Kolkata"); - - const masterConnectedStatus = masterSensor.connected_status || "disconnected"; - const lastDataTime = - masterSensor.connected_gsm_date && masterSensor.connected_gsm_time - ? `${masterSensor.connected_gsm_date} ${masterSensor.connected_gsm_time}` - : "No data"; + const orders = await Order.find({ customerId }).lean(); - // Get all slaves connected to this master const connectedSlaves = sensors.filter( - s => s.connected_to?.trim().toLowerCase() === normalizedConnectedTo && s.type === "slave" + (s) => s.connected_to?.trim().toLowerCase() === normalizedConnectedTo && s.type === "slave" ); const disconnectedSlaves = connectedSlaves - .filter(s => s.connected_status === "disconnected") - .map(s => ({ + .filter((s) => s.connected_status === "disconnected") + .map((s) => ({ slaveHardwareId: s.tankhardwareId?.trim().toLowerCase(), - slaveName: s.tankName || "Unknown Slave" + slaveName: s.tankName || "Unknown Slave", })); - if (disconnectedSlaves.length === 0 && masterConnectedStatus === "connected") return; - const supportRecord = await Support.findOne({ supportId: "AWHYSU64" }); if (!supportRecord) return; - const existingIssues = supportRecord.issues || []; + const now = moment.tz("Asia/Kolkata"); + const formattedNow = now.format("YYYY-MM-DD HH:mm:ss"); - const unresolvedMasterIssue = existingIssues.find( - issue => + const existingMasterIssue = supportRecord.issues.find( + (issue) => issue.hardwareId?.trim().toLowerCase() === normalizedConnectedTo && issue.type === "GSM or LoRa Disconnected" && - issue.resolved === false + !issue.resolved && + !issue.movedToCategory ); - if (unresolvedMasterIssue) return; + if (existingMasterIssue) { + if (masterSensor.connected_status === "connected") { + return; + } + return; + } - // Check both unresolved and resolved issues for previously reported slaves - const allSlaveHardwareIdsAlreadyReported = new Set(); - existingIssues - .filter(issue => issue.hardwareId?.trim().toLowerCase() === normalizedConnectedTo) - .forEach(issue => { - (issue.hardwareIds || []).forEach(id => { + const existingSlaveHardwareIds = new Set(); + supportRecord.issues + .filter( + (issue) => + issue.hardwareId?.trim().toLowerCase() === normalizedConnectedTo && + !issue.movedToCategory + ) + .forEach((issue) => { + (issue.hardwareIds || []).forEach((id) => { if (typeof id === "string") { - allSlaveHardwareIdsAlreadyReported.add(id.trim().toLowerCase()); + existingSlaveHardwareIds.add(id.trim().toLowerCase()); } }); }); @@ -3936,16 +3926,35 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => { const newSlaveNames = []; for (const slave of disconnectedSlaves) { - if (!allSlaveHardwareIdsAlreadyReported.has(slave.slaveHardwareId)) { + const sensorCurrent = sensors.find( + (s) => s.tankhardwareId?.trim().toLowerCase() === slave.slaveHardwareId + ); + + if ( + sensorCurrent && + sensorCurrent.connected_status === "disconnected" && + !existingSlaveHardwareIds.has(slave.slaveHardwareId) + ) { newSlaveHardwareIds.push(slave.slaveHardwareId); newSlaveNames.push(slave.slaveName); } } - // Still raise the issue if master is disconnected, even if no new slaves - if (newSlaveHardwareIds.length === 0 && masterConnectedStatus === "connected") return; + // 👇 Even if no new slaves found, if master is disconnected, still raise a ticket + const masterDisconnected = masterSensor.connected_status !== "connected"; - const formattedNow = now.format("YYYY-MM-DD HH:mm:ss"); + if (!masterDisconnected && newSlaveHardwareIds.length === 0) { + return; + } + + const lastDataTime = + masterSensor.connected_gsm_date && masterSensor.connected_gsm_time + ? `${masterSensor.connected_gsm_date} ${masterSensor.connected_gsm_time}` + : "No data"; + + const message = `Master ${connected_to} is ${masterSensor.connected_status || "disconnected"}${newSlaveHardwareIds.length > 0 + ? ` with ${newSlaveHardwareIds.length} disconnected slave(s)` + : ""}`; const newIssue = { type: "GSM or LoRa Disconnected", @@ -3953,20 +3962,21 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => { hardwareId: normalizedConnectedTo, hardwareIds: newSlaveHardwareIds, slaveNames: newSlaveNames, - message: `Master ${connected_to} is ${masterConnectedStatus} with ${newSlaveHardwareIds.length} disconnected slave(s)`, + message, disconnectedAt: lastDataTime, lastTicketRaisedAt: formattedNow, - resolved: false + resolved: false, + movedToCategory: false, + createdAt: formattedNow, }; await Support.findOneAndUpdate( { supportId: "AWHYSU64" }, { $push: { issues: newIssue }, - $set: { updatedAt: new Date() } + $set: { updatedAt: new Date() }, } ); - } catch (error) { console.error("Error in raiseATicketLikeLogic:", error); } @@ -3976,6 +3986,172 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => { +// exports.raiseATicket = async (req, reply) => { +// try { +// const { customerId, connected_to } = req.params; + +// if (!customerId || !connected_to) { +// return reply.code(400).send({ error: "customerId and connected_to are required" }); +// } + +// const masterDevice = await Insensors.findOne({ +// customerId, +// hardwareId: connected_to, +// type: "master" +// }).lean(); + +// if (!masterDevice) { +// return reply.code(404).send({ message: "Master device not found" }); +// } + +// const allSlaves = await Insensors.find({ +// connected_to, +// customerId, +// type: "slave" +// }).lean(); + +// const disconnectedSlaves = allSlaves.filter( +// slave => slave.connected_status === "disconnected" +// ); + +// const hasDisconnectedSlaves = disconnectedSlaves.length > 0; +// const isMasterDisconnected = masterDevice.connected_status === "disconnected"; + +// // If nothing is disconnected, no ticket should be raised +// if (!isMasterDisconnected && !hasDisconnectedSlaves) { +// return reply.code(200).send({ message: "All devices are connected. No ticket needed." }); +// } + +// const supportId = `${customerId}_${connected_to}`; +// const existingSupport = await Support.findOne({ supportId }); + +// // Helper to check if hardwareId is already in unresolved & not moved +// const isIssueAlreadyTracked = (supportDoc, hardwareId) => { +// return supportDoc.issues?.some(issue => { +// const allHwIds = [ +// issue.hardwareId?.toLowerCase(), +// ...(issue.hardwareIds?.map(id => id?.toLowerCase()) || []) +// ]; +// return ( +// allHwIds.includes(hardwareId.toLowerCase()) && +// !issue.resolved && +// issue.movedToCategory !== true +// ); +// }); +// }; + +// const nowTime = moment().tz("Asia/Kolkata").format("YYYY-MM-DD HH:mm:ss"); + +// if (existingSupport) { +// let updated = false; + +// // Add master if disconnected and not already tracked +// if (isMasterDisconnected && !isIssueAlreadyTracked(existingSupport, masterDevice.hardwareId)) { +// existingSupport.issues.push({ +// type: "master", +// hardwareId: masterDevice.hardwareId, +// masterName: masterDevice.deviceName || "", +// resolved: false, +// movedToCategory: false, +// createdAt: nowTime, +// lastTicketRaisedAt: nowTime +// }); +// updated = true; +// } + +// // Add each disconnected slave if not already tracked +// for (const slave of disconnectedSlaves) { +// if (isIssueAlreadyTracked(existingSupport, slave.hardwareId)) continue; + +// // If issue for this master already exists, append slave +// let masterIssue = existingSupport.issues.find(issue => +// issue.type === "master" && +// issue.hardwareId === masterDevice.hardwareId && +// !issue.resolved +// ); + +// if (!masterIssue) { +// // create new slave-type issue +// existingSupport.issues.push({ +// type: "slave", +// hardwareId: slave.hardwareId, +// masterHardwareId: masterDevice.hardwareId, +// slaveName: slave.deviceName || "", +// resolved: false, +// movedToCategory: false, +// createdAt: nowTime, +// lastTicketRaisedAt: nowTime +// }); +// } else { +// if (!masterIssue.hardwareIds) masterIssue.hardwareIds = []; +// if (!masterIssue.slaveNames) masterIssue.slaveNames = []; +// masterIssue.hardwareIds.push(slave.hardwareId); +// masterIssue.slaveNames.push(slave.deviceName || ""); +// masterIssue.lastTicketRaisedAt = nowTime; +// } + +// updated = true; +// } + +// if (updated) { +// await existingSupport.save(); +// return reply.code(201).send({ message: "Support ticket updated with new disconnected issues." }); +// } else { +// return reply.code(200).send({ message: "No new disconnected issues to add." }); +// } + +// } else { +// // Create new ticket +// const issues = []; + +// if (isMasterDisconnected) { +// issues.push({ +// type: "master", +// hardwareId: masterDevice.hardwareId, +// masterName: masterDevice.deviceName || "", +// resolved: false, +// movedToCategory: false, +// createdAt: nowTime, +// lastTicketRaisedAt: nowTime +// }); +// } + +// for (const slave of disconnectedSlaves) { +// issues.push({ +// type: "slave", +// hardwareId: slave.hardwareId, +// masterHardwareId: masterDevice.hardwareId, +// slaveName: slave.deviceName || "", +// resolved: false, +// movedToCategory: false, +// createdAt: nowTime, +// lastTicketRaisedAt: nowTime +// }); +// } + +// const newSupport = new Support({ +// supportId, +// customerId, +// connected_to, +// createdAt: nowTime, +// updatedAt: nowTime, +// status: "open", +// issues +// }); + +// await newSupport.save(); +// return reply.code(201).send({ message: "Support ticket created successfully." }); +// } + +// } catch (error) { +// console.error("Error in raiseATicket:", error); +// return reply.code(500).send({ error: "Internal server error" }); +// } +// }; + + + + // cron.schedule("* * * * *", async () => { // console.log("Running auto ticket check..."); // const allMasters = await Insensors.find({ }).lean(); @@ -3984,30 +4160,39 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => { // } // }); -cron.schedule("* * * * *", async () => { +cron.schedule("*/1 * * * *", async () => { try { console.log("🔁 Running auto-disconnect ticket check..."); + + // Find all master sensors const allMasters = await Insensors.find({ type: "master" }).lean(); for (const master of allMasters) { const customerId = master.customerId; const hardwareId = master.hardwareId; + if (!customerId || !hardwareId) continue; + + // Find slaves connected to this master const connectedSlaves = await Insensors.find({ connected_to: hardwareId, type: "slave" }).lean(); + // Filter disconnected slaves const disconnectedSlaves = connectedSlaves.filter( - s => s.connected_status === "disconnected" + (s) => s.connected_status === "disconnected" ); const masterIsDisconnected = master.connected_status === "disconnected"; + // Only raise ticket if master is disconnected or has disconnected slaves if (masterIsDisconnected || disconnectedSlaves.length > 0) { await raiseATicketLikeLogic(customerId, hardwareId); } } + + console.log("✅ Auto ticket check completed."); } catch (err) { console.error("Cron error:", err); } @@ -5475,166 +5660,152 @@ exports.getRemoveConnectedMastersWithSlaves = async (req, reply) => { } }; -exports.getDisconnectedCustomerDetails = async (req, reply) => { - try { - const { supportId } = req.params; +// exports.getDisconnectedCustomerDetails = async (req, reply) => { +// try { +// const { supportId } = req.params; - if (!supportId) { - return reply.code(400).send({ error: "supportId is required" }); - } +// if (!supportId) { +// return reply.code(400).send({ error: "supportId is required" }); +// } - // 1. Fetch support record - const supportRecord = await Support.findOne({ supportId }).lean(); - if (!supportRecord) { - return reply.code(404).send({ message: "No support record found for this supportId" }); - } +// const supportRecord = await Support.findOne({ supportId }).lean(); +// if (!supportRecord) { +// return reply.code(404).send({ message: "No support record found for this supportId" }); +// } - // 2. Filter only unresolved issues - const unresolvedIssues = (supportRecord.issues || []).filter(issue => issue.resolved === false); +// const unresolvedIssues = (supportRecord.issues || []).filter( +// issue => issue.resolved === false && issue.movedToCategory !== true +// ); - // 3. Collect all unique hardwareIds (from hardwareId and hardwareIds arrays) from unresolved issues - const allHardwareIds = new Set(); - for (const issue of unresolvedIssues) { - if (issue.hardwareId) allHardwareIds.add(issue.hardwareId.trim().toLowerCase()); - if (Array.isArray(issue.hardwareIds)) { - issue.hardwareIds.forEach(id => { - if (typeof id === "string") allHardwareIds.add(id.trim().toLowerCase()); - }); - } - } +// const allHardwareIds = new Set(); +// for (const issue of unresolvedIssues) { +// if (issue.hardwareId) allHardwareIds.add(issue.hardwareId.trim().toLowerCase()); +// if (Array.isArray(issue.hardwareIds)) { +// issue.hardwareIds.forEach(id => { +// if (typeof id === "string") allHardwareIds.add(id.trim().toLowerCase()); +// }); +// } +// } - if (allHardwareIds.size === 0) { - return reply.code(404).send({ message: "No unresolved hardware IDs found in issues" }); - } +// if (allHardwareIds.size === 0) { +// return reply.code(404).send({ message: "No unresolved hardware IDs found in issues" }); +// } - const hardwareIdsArray = Array.from(allHardwareIds); - console.log("hardwareIdsArray",hardwareIdsArray) - // 4. Find disconnected insensors using connected_to match - const disconnectedSensors = await Insensors.find({ - connected_status: "disconnected", - $or: [ - { connected_to: { $in: hardwareIdsArray } }, // slaves - { hardwareId: { $in: hardwareIdsArray } } // masters - ] - }).lean(); - - console.log("disconnectedSensors",disconnectedSensors) - if (!disconnectedSensors.length) { - return reply.code(404).send({ message: "No disconnected issues found" }); - } +// const hardwareIdsArray = Array.from(allHardwareIds); - // 5. Get unique customerIds from disconnected sensors - const customerIds = [...new Set(disconnectedSensors.map(s => s.customerId))]; - const customers = await User.find({ customerId: { $in: customerIds } }).lean(); +// const disconnectedSensors = await Insensors.find({ +// connected_status: "disconnected", +// $or: [ +// { connected_to: { $in: hardwareIdsArray } }, +// { hardwareId: { $in: hardwareIdsArray } } +// ] +// }).lean(); - // 6. For each customer, calculate total unique hardwareIds involved in their disconnected sensors - const customerHardwareMap = {}; +// if (!disconnectedSensors.length) { +// return reply.code(404).send({ message: "No disconnected issues found" }); +// } - for (const sensor of disconnectedSensors) { - const custId = sensor.customerId; - if (!customerHardwareMap[custId]) { - customerHardwareMap[custId] = new Set(); - } +// const customerIds = [...new Set(disconnectedSensors.map(s => s.customerId))]; +// const customers = await User.find({ customerId: { $in: customerIds } }).lean(); - const sensorHw = sensor.tankhardwareId?.trim().toLowerCase(); - const sensorConnected = sensor.connected_to?.trim().toLowerCase(); +// const customerHardwareMap = {}; +// for (const sensor of disconnectedSensors) { +// const custId = sensor.customerId; +// if (!customerHardwareMap[custId]) { +// customerHardwareMap[custId] = new Set(); +// } - for (const issue of unresolvedIssues) { - const allIssueHardwareIds = [ - ...(issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || []), - issue.hardwareId?.trim().toLowerCase() - ]; +// const sensorHw = sensor.tankhardwareId?.trim().toLowerCase(); +// const sensorConnected = sensor.connected_to?.trim().toLowerCase(); - if ( - allIssueHardwareIds.includes(sensorHw) || - allIssueHardwareIds.includes(sensorConnected) - ) { - customerHardwareMap[custId].add(issue.hardwareId); - } - console.log("allIssueHardwareIds",allIssueHardwareIds) +// for (const issue of unresolvedIssues) { +// const allIssueHardwareIds = [ +// ...(issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || []), +// issue.hardwareId?.trim().toLowerCase() +// ]; - } - } +// if ( +// allIssueHardwareIds.includes(sensorHw) || +// allIssueHardwareIds.includes(sensorConnected) +// ) { +// customerHardwareMap[custId].add(issue.hardwareId); +// } +// } +// } +// const response = []; +// for (const user of customers) { +// const custId = user.customerId; +// const hardwareIdSet = customerHardwareMap[custId] || new Set(); + +// 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?.trim().toLowerCase())); +// }); - // 7. Build final response - const response = []; +// let latestIssueTime = null; +// for (const issue of relatedIssues) { +// if (issue.lastTicketRaisedAt) { +// const issueTime = new Date(issue.lastTicketRaisedAt); +// if (!latestIssueTime || issueTime > latestIssueTime) { +// latestIssueTime = issueTime; +// } +// } +// } - for (const user of customers) { - const custId = user.customerId; - const hardwareIdSet = customerHardwareMap[custId] || new Set(); +// response.push({ +// customer: { +// customerId: custId, +// username: user.username || "", +// firstName: user.profile?.firstName || "", +// lastName: user.profile?.lastName || "", +// phone: user.phone || user.profile?.contactNumber || "", +// email: user.emails?.[0]?.email || "", +// phoneVerified: user.phoneVerified || false, +// address1: user.profile?.address1 || "", +// address2: user.profile?.address2 || "", +// city: user.profile?.city || "", +// state: user.profile?.state || "", +// country: user.profile?.country || "", +// zip: user.profile?.zip || "", +// notes: user.profile?.notes || "", +// latitude: user.latitude, +// longitude: user.longitude, +// 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, +// lastTicketRaisedAt: latestIssueTime ? moment(latestIssueTime).format("YYYY-MM-DD HH:mm:ss") : null, +// totalHardwareIdsCount: hardwareIdSet.size +// } +// }); +// } - // Extract latest unresolved issue-specific lastTicketRaisedAt - const relatedIssues = unresolvedIssues.filter(issue => { - const issueHw = issue.hardwareId?.trim().toLowerCase(); - const hardwareIds = issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || []; - const allIds = [issueHw, ...hardwareIds]; - console.log("allIds",allIds) - console.log("Array.from(hardwareIdSet).some(hw => allIds.includes(hw?.trim().toLowerCase()))",Array.from(hardwareIdSet).some(hw => allIds.includes(hw?.trim().toLowerCase()))) - return Array.from(hardwareIdSet).some(hw => allIds.includes(hw?.trim().toLowerCase())); +// return reply.send({ +// status_code: 200, +// data: response +// }); - }); - //console.log("relatedIssues",relatedIssues) - let latestIssueTime = null; - for (const issue of relatedIssues) { - if (issue.lastTicketRaisedAt) { - const issueTime = new Date(issue.lastTicketRaisedAt); - if (!latestIssueTime || issueTime > latestIssueTime) { - latestIssueTime = issueTime; - } - } - } +// } catch (error) { +// console.error("Error fetching disconnected customer details:", error); +// return reply.code(500).send({ error: "Internal server error" }); +// } +// }; - response.push({ - customer: { - customerId: custId, - username: user.username || "", - firstName: user.profile?.firstName || "", - lastName: user.profile?.lastName || "", - phone: user.phone || user.profile?.contactNumber || "", - email: user.emails?.[0]?.email || "", - phoneVerified: user.phoneVerified || false, - address1: user.profile?.address1 || "", - address2: user.profile?.address2 || "", - city: user.profile?.city || "", - state: user.profile?.state || "", - country: user.profile?.country || "", - zip: user.profile?.zip || "", - notes: user.profile?.notes || "", - latitude: user.latitude, - longitude: user.longitude, - 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, - lastTicketRaisedAt: latestIssueTime ? moment(latestIssueTime).format("YYYY-MM-DD HH:mm:ss") : null, - totalHardwareIdsCount: hardwareIdSet.size - } - }); - } - - return reply.send({ - status_code: 200, - data: response - }); - - } catch (error) { - console.error("Error fetching disconnected customer details:", error); - return reply.code(500).send({ error: "Internal server error" }); - } -}; @@ -5737,6 +5908,180 @@ exports.getDisconnectedCustomerDetails = async (req, reply) => { // }; +exports.getDisconnectedCustomerDetails = 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" }); + } + + // Step 1: Filter unresolved and not moved issues + const unresolvedIssues = (supportRecord.issues || []).filter( + issue => issue.resolved === false && issue.movedToCategory !== true + ); + + // Step 2: Build set of hardwareIds already in categorizedIssues (moved) + const existingCategorizedHardwareIds = new Set(); + (supportRecord.categorizedIssues || []).forEach(issue => { + if (issue.hardwareId) existingCategorizedHardwareIds.add(issue.hardwareId.trim().toLowerCase()); + if (Array.isArray(issue.hardwareIds)) { + issue.hardwareIds.forEach(id => { + if (typeof id === "string") existingCategorizedHardwareIds.add(id.trim().toLowerCase()); + }); + } + }); + + // Step 3: Build list of hardwareIds in unresolved issues (excluding moved) + const allHardwareIds = new Set(); + for (const issue of unresolvedIssues) { + const issueHardwareId = issue.hardwareId?.trim().toLowerCase(); + const issueSlaveIds = issue.hardwareIds?.map(id => id?.trim().toLowerCase()) || []; + + if (issueHardwareId && !existingCategorizedHardwareIds.has(issueHardwareId)) { + allHardwareIds.add(issueHardwareId); + } + + for (const slaveId of issueSlaveIds) { + if (slaveId && !existingCategorizedHardwareIds.has(slaveId)) { + allHardwareIds.add(slaveId); + } + } + } + + // 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); + + // Step 5: Find disconnected sensors + const disconnectedSensors = await Insensors.find({ + connected_status: "disconnected", + $or: [ + { connected_to: { $in: hardwareIdsArray } }, + { hardwareId: { $in: hardwareIdsArray } }, + { tankhardwareId: { $in: hardwareIdsArray } } + ] + }).lean(); + + if (!disconnectedSensors.length) { + return reply.code(404).send({ message: "No disconnected issues found" }); + } + + // 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 + const customerHardwareMap = {}; + for (const sensor of disconnectedSensors) { + const custId = sensor.customerId; + if (!customerHardwareMap[custId]) { + customerHardwareMap[custId] = new Set(); + } + + const sensorHw = sensor.tankhardwareId?.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()); + } + } + } + } + + // 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({ + customer: { + customerId: custId, + username: user.username || "", + firstName: user.profile?.firstName || "", + lastName: user.profile?.lastName || "", + phone: user.phone || user.profile?.contactNumber || "", + email: user.emails?.[0]?.email || "", + phoneVerified: user.phoneVerified || false, + address1: user.profile?.address1 || "", + address2: user.profile?.address2 || "", + city: user.profile?.city || "", + state: user.profile?.state || "", + country: user.profile?.country || "", + zip: user.profile?.zip || "", + notes: user.profile?.notes || "", + latitude: user.latitude, + longitude: user.longitude, + 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, + lastTicketRaisedAt: latestIssueTime ? moment(latestIssueTime).format("YYYY-MM-DD HH:mm:ss") : null, + totalHardwareIdsCount: hardwareIdSet.size + } + }); + } + + return reply.send({ + status_code: 200, + data: response + }); + + } catch (error) { + console.error("Error fetching disconnected customer details:", error); + return reply.code(500).send({ error: "Internal server error" }); + } +}; @@ -6119,93 +6464,93 @@ exports.deleteTeamMemberSupport = async (req, reply)=> { } } -exports.moveIssueToCategory = async (req, reply) => { - try { - const { supportId } = req.params; - const { category, hardwareId } = req.body; +// exports.moveIssueToCategory = async (req, reply) => { +// try { +// const { supportId } = req.params; +// const { category, hardwareId } = req.body; - if (!supportId || !category || !hardwareId) { - return reply.code(400).send({ - message: "supportId (path), category and hardwareId (body) are required", - }); - } +// if (!supportId || !category || !hardwareId) { +// return reply.code(400).send({ +// message: "supportId (path), category and hardwareId (body) are required", +// }); +// } - const support = await Support.findOne({ supportId }); - if (!support) { - return reply.code(404).send({ message: "Support record not found" }); - } +// const support = await Support.findOne({ supportId }); +// if (!support) { +// return reply.code(404).send({ message: "Support record not found" }); +// } - let issueMoved = false; - const nowTime = moment().tz("Asia/Kolkata").format("YYYY-MM-DD HH:mm:ss"); +// let issueMoved = false; +// const nowTime = moment().tz("Asia/Kolkata").format("YYYY-MM-DD HH:mm:ss"); - if (!Array.isArray(support.categorizedIssues)) { - support.categorizedIssues = []; - } +// if (!Array.isArray(support.categorizedIssues)) { +// support.categorizedIssues = []; +// } - // Find the issue where hardwareId matches either hardwareId or inside hardwareIds array - const index = support.issues.findIndex((issue) => { - // Master hardwareId match - if (issue.hardwareId === hardwareId) return true; - // Slave hardwareIds array match - if (Array.isArray(issue.hardwareIds) && issue.hardwareIds.includes(hardwareId)) return true; - return false; - }); +// // Find the issue where hardwareId matches either hardwareId or inside hardwareIds array +// const index = support.issues.findIndex((issue) => { +// // Master hardwareId match +// if (issue.hardwareId === hardwareId) return true; +// // Slave hardwareIds array match +// if (Array.isArray(issue.hardwareIds) && issue.hardwareIds.includes(hardwareId)) return true; +// return false; +// }); - if (index === -1) { - return reply.code(404).send({ message: "No matching issue found to move" }); - } +// if (index === -1) { +// return reply.code(404).send({ message: "No matching issue found to move" }); +// } - const issue = support.issues[index]; +// const issue = support.issues[index]; - // If the hardwareId matches master hardwareId, move entire issue as is - if (issue.hardwareId === hardwareId) { - support.categorizedIssues.push({ - ...issue, - masterHardwareId: issue.masterHardwareId || issue.hardwareId, - category, - movedAt: nowTime, - }); - support.issues.splice(index, 1); - issueMoved = true; - } else { - // hardwareId matches inside hardwareIds array — move that slave issue individually - const slaveIndex = issue.hardwareIds.indexOf(hardwareId); - if (slaveIndex !== -1) { - const slaveName = issue.slaveNames?.[slaveIndex] || "Unknown"; - - support.categorizedIssues.push({ - type: issue.type, - hardwareId, - masterHardwareId: issue.masterHardwareId || issue.hardwareId, - slaveName, - category, - movedAt: nowTime, - }); +// // If the hardwareId matches master hardwareId, move entire issue as is +// if (issue.hardwareId === hardwareId) { +// support.categorizedIssues.push({ +// ...issue, +// masterHardwareId: issue.masterHardwareId || issue.hardwareId, +// category, +// movedAt: nowTime, +// }); +// support.issues.splice(index, 1); +// issueMoved = true; +// } else { +// // hardwareId matches inside hardwareIds array — move that slave issue individually +// const slaveIndex = issue.hardwareIds.indexOf(hardwareId); +// if (slaveIndex !== -1) { +// const slaveName = issue.slaveNames?.[slaveIndex] || "Unknown"; + +// support.categorizedIssues.push({ +// type: issue.type, +// hardwareId, +// masterHardwareId: issue.masterHardwareId || issue.hardwareId, +// slaveName, +// category, +// movedAt: nowTime, +// }); - // Remove slave from issue - issue.hardwareIds.splice(slaveIndex, 1); - issue.slaveNames.splice(slaveIndex, 1); +// // Remove slave from issue +// issue.hardwareIds.splice(slaveIndex, 1); +// issue.slaveNames.splice(slaveIndex, 1); - // If no more slaves left, remove the issue completely - if (issue.hardwareIds.length === 0) { - support.issues.splice(index, 1); - } +// // If no more slaves left, remove the issue completely +// if (issue.hardwareIds.length === 0) { +// support.issues.splice(index, 1); +// } - issueMoved = true; - } - } +// issueMoved = true; +// } +// } - if (issueMoved) { - await support.save(); - return reply.send({ message: "Issue moved to category successfully" }); - } else { - return reply.code(404).send({ message: "No matching issue found to move" }); - } - } catch (err) { - console.error("Error moving issue:", err); - return reply.code(500).send({ error: "Internal Server Error" }); - } -}; +// if (issueMoved) { +// await support.save(); +// return reply.send({ message: "Issue moved to category successfully" }); +// } else { +// return reply.code(404).send({ message: "No matching issue found to move" }); +// } +// } catch (err) { +// console.error("Error moving issue:", err); +// return reply.code(500).send({ error: "Internal Server Error" }); +// } +// }; @@ -6541,6 +6886,88 @@ exports.moveIssueToCategory = async (req, reply) => { // } // }; +exports.moveIssueToCategory = async (req, reply) => { + try { + const { supportId } = req.params; + const { category, hardwareId } = req.body; + + if (!supportId || !category || !hardwareId) { + return reply.code(400).send({ + message: "supportId (path), category and hardwareId (body) are required", + }); + } + + const support = await Support.findOne({ supportId }); + if (!support) { + return reply.code(404).send({ message: "Support record not found" }); + } + + let issueMoved = false; + const nowTime = moment().tz("Asia/Kolkata").format("YYYY-MM-DD HH:mm:ss"); + + if (!Array.isArray(support.categorizedIssues)) { + support.categorizedIssues = []; + } + + const index = support.issues.findIndex((issue) => { + if (issue.hardwareId === hardwareId) return true; + if (Array.isArray(issue.hardwareIds) && issue.hardwareIds.includes(hardwareId)) return true; + return false; + }); + + if (index === -1) { + return reply.code(404).send({ message: "No matching issue found to move" }); + } + + const issue = support.issues[index]; + + if (issue.hardwareId === hardwareId) { + issue.movedToCategory = true; // ✅ Mark moved + support.categorizedIssues.push({ + ...issue, + masterHardwareId: issue.masterHardwareId || issue.hardwareId, + category, + movedAt: nowTime, + }); + support.issues.splice(index, 1); + issueMoved = true; + } else { + const slaveIndex = issue.hardwareIds.indexOf(hardwareId); + if (slaveIndex !== -1) { + const slaveName = issue.slaveNames?.[slaveIndex] || "Unknown"; + + support.categorizedIssues.push({ + type: issue.type, + hardwareId, + masterHardwareId: issue.masterHardwareId || issue.hardwareId, + slaveName, + category, + movedAt: nowTime, + movedToCategory: true, // ✅ Track it here too + }); + + issue.hardwareIds.splice(slaveIndex, 1); + issue.slaveNames.splice(slaveIndex, 1); + + if (issue.hardwareIds.length === 0) { + support.issues.splice(index, 1); + } + + issueMoved = true; + } + } + + if (issueMoved) { + await support.save(); + return reply.send({ message: "Issue moved to category successfully" }); + } else { + return reply.code(404).send({ message: "No matching issue found to move" }); + } + } catch (err) { + console.error("Error moving issue:", err); + return reply.code(500).send({ error: "Internal Server Error" }); + } +}; exports.particularCategory = async (req, reply) => { try { diff --git a/src/models/store.js b/src/models/store.js index ec2797ed..29864b34 100644 --- a/src/models/store.js +++ b/src/models/store.js @@ -4,7 +4,6 @@ const ObjectId = Schema.Types.ObjectId; const { Counter} = require('../models/User') const code = Math.floor(100000 + Math.random() * 900000); - const generateinstallationId = async () => { var result = await Counter.findOneAndUpdate( { _id: 'installation_id' }, @@ -206,6 +205,92 @@ const installationschema = new mongoose.Schema({ } }); + const IssueSchema = new Schema({ + type: { + type: String, + enum: ["master", "slave"], + required: true + }, + hardwareId: { + type: String, + required: true + }, + masterHardwareId: { + type: String + }, + masterName: { + type: String + }, + slaveName: { + type: String + }, + hardwareIds: [String], + slaveNames: [String], + resolved: { + type: Boolean, + default: false + }, + movedToCategory: { + type: Boolean, + default: false + }, + category: { + type: String + }, + createdAt: { + type: String + }, + lastTicketRaisedAt: { + type: String + }, + movedAt: { + type: String + } + }); + + const CategorizedIssueSchema = new Schema({ + type: { + type: String, + enum: ["master", "slave"], + required: true + }, + hardwareId: { + type: String, + required: true + }, + masterHardwareId: { + type: String, + required: true + }, + slaveName: { + type: String + }, + category: { + type: String, + enum: ["Power Outage", "Resolved", "Escalation"], + required: true + }, + movedAt: { + type: String, + required: true + }, + assignedTo: { + name: String, + support_teamMemberId: String, + phone: String, + email: String, + startDate: String, + endDate: String + } + }); + + const CommentSchema = new Schema({ + text: { type: String, required: true }, + customerId: String, + hardwareId: String, + createdAt: { type: Date, default: Date.now } + }); + const supportschema = new mongoose.Schema({ // name: { type: String }, phone: { type: String, unique: true, trim: true }, @@ -231,16 +316,12 @@ const installationschema = new mongoose.Schema({ dateOfLogin: { type: String, default: null }, timeOfLogin: { type: String, default: null }, currentTime: { type: Date, default: Date.now }, - comments: [ - { - text: { type: String, required: true }, - customerId: String, - hardwareId: String, - createdAt: { type: Date, default: Date.now } - } -], + comments: [CommentSchema], + lastTicketRaisedAt: {type : String}, - issues: [{ type: Object }], // existing issues array + issues: [IssueSchema], + + categorizedIssues: [CategorizedIssueSchema], masterDisconnected: [{ // new field for master disconnected details hardwareId: String, masterName: String, @@ -250,44 +331,44 @@ const installationschema = new mongoose.Schema({ slaveHardwareId: String, slaveName: String, }], - categorizedIssues: [ - { - type: { - type: String, - required: true - }, - hardwareId: { - type: String, - required: true - }, - masterHardwareId: { - type: String, - required: true - }, - slaveName: { - type: String, - }, - category: { - type: String, - enum: [ "Power Outage", - "Resolved", - "Escalation",], - required: true - }, - movedAt: { - type: String, // or Date, depending on your preference - required: true - }, - assignedTo: { - name: String, - support_teamMemberId: String, - phone: String, - email: String, - startDate: String, - endDate: String - } - } - ], + // categorizedIssues: [ + // { + // type: { + // type: String, + // required: true + // }, + // hardwareId: { + // type: String, + // required: true + // }, + // masterHardwareId: { + // type: String, + // required: true + // }, + // slaveName: { + // type: String, + // }, + // category: { + // type: String, + // enum: [ "Power Outage", + // "Resolved", + // "Escalation",], + // required: true + // }, + // movedAt: { + // type: String, // or Date, depending on your preference + // required: true + // }, + // assignedTo: { + // name: String, + // support_teamMemberId: String, + // phone: String, + // email: String, + // startDate: String, + // endDate: String + // } + // } + // ], profile: {