|
|
|
@ -3870,7 +3870,10 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => {
|
|
|
|
|
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" &&
|
|
|
|
|
s.support_issue_status !== "inactive"
|
|
|
|
|
);
|
|
|
|
|
if (!masterSensor) return;
|
|
|
|
|
|
|
|
|
@ -3885,16 +3888,17 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
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";
|
|
|
|
|
|
|
|
|
|
// 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" &&
|
|
|
|
|
s.support_issue_status !== "inactive"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const disconnectedSlaves = connectedSlaves
|
|
|
|
@ -3917,10 +3921,8 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => {
|
|
|
|
|
issue.type === "GSM or LoRa Disconnected" &&
|
|
|
|
|
issue.resolved === false
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (unresolvedMasterIssue) return;
|
|
|
|
|
|
|
|
|
|
// Check both unresolved and resolved issues for previously reported slaves
|
|
|
|
|
const allSlaveHardwareIdsAlreadyReported = new Set();
|
|
|
|
|
existingIssues
|
|
|
|
|
.filter(issue => issue.hardwareId?.trim().toLowerCase() === normalizedConnectedTo)
|
|
|
|
@ -3942,7 +3944,6 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Still raise the issue if master is disconnected, even if no new slaves
|
|
|
|
|
if (newSlaveHardwareIds.length === 0 && masterConnectedStatus === "connected") return;
|
|
|
|
|
|
|
|
|
|
const formattedNow = now.format("YYYY-MM-DD HH:mm:ss");
|
|
|
|
@ -3967,11 +3968,21 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => {
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// ✅ Mark all involved sensors as "active"
|
|
|
|
|
await Insensors.updateMany(
|
|
|
|
|
{
|
|
|
|
|
$or: [
|
|
|
|
|
{ hardwareId: normalizedConnectedTo },
|
|
|
|
|
{ tankhardwareId: { $in: newSlaveHardwareIds } }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{ $set: { support_issue_status: "active" } }
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error in raiseATicketLikeLogic:", error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -3987,6 +3998,7 @@ const raiseATicketLikeLogic = async (customerId, connected_to) => {
|
|
|
|
|
cron.schedule("* * * * *", async () => {
|
|
|
|
|
try {
|
|
|
|
|
console.log("🔁 Running auto-disconnect ticket check...");
|
|
|
|
|
|
|
|
|
|
const allMasters = await Insensors.find({ type: "master" }).lean();
|
|
|
|
|
|
|
|
|
|
for (const master of allMasters) {
|
|
|
|
@ -4004,8 +4016,23 @@ cron.schedule("* * * * *", async () => {
|
|
|
|
|
|
|
|
|
|
const masterIsDisconnected = master.connected_status === "disconnected";
|
|
|
|
|
|
|
|
|
|
if (masterIsDisconnected || disconnectedSlaves.length > 0) {
|
|
|
|
|
// ✅ Check if master already has unresolved ticket
|
|
|
|
|
const masterAlreadyActive = master.support_issue_status === "active";
|
|
|
|
|
|
|
|
|
|
// ✅ Check if any disconnected slave already has unresolved ticket
|
|
|
|
|
const anySlaveAlreadyActive = disconnectedSlaves.some(
|
|
|
|
|
slave => slave.support_issue_status === "active"
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// ✅ Only raise a ticket if not already active
|
|
|
|
|
const shouldRaiseTicket =
|
|
|
|
|
(masterIsDisconnected && !masterAlreadyActive) ||
|
|
|
|
|
(disconnectedSlaves.length > 0 && !anySlaveAlreadyActive);
|
|
|
|
|
|
|
|
|
|
if (shouldRaiseTicket) {
|
|
|
|
|
await raiseATicketLikeLogic(customerId, hardwareId);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`Skipping ticket for ${hardwareId} — already active.`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
@ -4013,6 +4040,7 @@ cron.schedule("* * * * *", async () => {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.raiseATicketBuildingDetails = async (req, reply) => {
|
|
|
|
|
try {
|
|
|
|
|
const { customerId, connected_to, installationId } = req.params;
|
|
|
|
@ -5100,6 +5128,7 @@ exports.raiseATicketSlave = async (req, reply) => {
|
|
|
|
|
// }
|
|
|
|
|
// };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
try {
|
|
|
|
|
const { supportId, customerId } = req.params;
|
|
|
|
@ -5116,6 +5145,7 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
const allIssues = supportRecord.issues || [];
|
|
|
|
|
const hardwareSet = new Set();
|
|
|
|
|
|
|
|
|
|
// Collect all hardwareIds and masterHardwareIds from issues
|
|
|
|
|
for (const issue of allIssues) {
|
|
|
|
|
if (issue.hardwareId) hardwareSet.add(issue.hardwareId);
|
|
|
|
|
if (issue.masterHardwareId) hardwareSet.add(issue.masterHardwareId);
|
|
|
|
@ -5123,6 +5153,7 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
|
|
|
|
|
const hardwareIds = [...hardwareSet];
|
|
|
|
|
|
|
|
|
|
// Fetch all relevant sensors (masters and slaves) for customer
|
|
|
|
|
const sensors = await Insensors.find({
|
|
|
|
|
customerId,
|
|
|
|
|
$or: [
|
|
|
|
@ -5131,14 +5162,17 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
]
|
|
|
|
|
}).lean();
|
|
|
|
|
|
|
|
|
|
// Map sensors by their hardwareId and tankhardwareId for quick lookup
|
|
|
|
|
const sensorMap = {};
|
|
|
|
|
for (const sensor of sensors) {
|
|
|
|
|
if (sensor.hardwareId) sensorMap[sensor.hardwareId] = sensor;
|
|
|
|
|
if (sensor.tankhardwareId) sensorMap[sensor.tankhardwareId] = sensor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetch all orders for the customer
|
|
|
|
|
const orders = await Order.find({ customerId }).lean();
|
|
|
|
|
|
|
|
|
|
// Map master order connections by hardwareId for master info
|
|
|
|
|
const orderMap = {};
|
|
|
|
|
for (const order of orders) {
|
|
|
|
|
(order.master_connections || []).forEach(conn => {
|
|
|
|
@ -5151,6 +5185,7 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Map slave order connections by hardwareId for slave info
|
|
|
|
|
const slaveOrderMap = {};
|
|
|
|
|
for (const order of orders) {
|
|
|
|
|
(order.tank_connections || []).forEach(conn => {
|
|
|
|
@ -5170,6 +5205,14 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
const masterSensor = sensorMap[masterId];
|
|
|
|
|
if (!masterSensor || masterSensor.type !== "master") continue;
|
|
|
|
|
|
|
|
|
|
// Only process masters with active support_issue_status
|
|
|
|
|
const masterIssueStatus = masterSensor.support_issue_status || "inactive";
|
|
|
|
|
if (masterIssueStatus !== "active") {
|
|
|
|
|
// Skip this master and its slaves if issue inactive
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get latest IotData for master to check GSM connection
|
|
|
|
|
const latestMasterData = await IotData.findOne({ hardwareId: masterSensor.hardwareId }).sort({ date: -1 }).lean();
|
|
|
|
|
const now = moment.tz("Asia/Kolkata");
|
|
|
|
|
let gsmConnected = false;
|
|
|
|
@ -5198,11 +5241,14 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
team_member_support_gsm_last_check_time: masterSensor.team_member_support_gsm_last_check_time,
|
|
|
|
|
team_member_support_lora_last_check_time: masterSensor.team_member_support_lora_last_check_time,
|
|
|
|
|
connected_slave_count: 0,
|
|
|
|
|
connected_slaves: []
|
|
|
|
|
connected_slaves: [],
|
|
|
|
|
support_issue_status: masterIssueStatus
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const master = masterMap[masterSensor.hardwareId];
|
|
|
|
|
|
|
|
|
|
// Find slaves connected to this master with active issues only
|
|
|
|
|
const connectedSlaves = await Insensors.find({
|
|
|
|
|
connected_to: masterSensor.hardwareId,
|
|
|
|
|
type: "slave",
|
|
|
|
@ -5212,6 +5258,10 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
const slaveSet = new Set(master.connected_slaves.map(s => s.hardwareId));
|
|
|
|
|
|
|
|
|
|
for (const slave of connectedSlaves) {
|
|
|
|
|
// Only add slave if its issue status is active
|
|
|
|
|
const slaveIssueStatus = slave.support_issue_status || "inactive";
|
|
|
|
|
if (slaveIssueStatus !== "active") continue;
|
|
|
|
|
|
|
|
|
|
const slaveHardwareId = slave.tankhardwareId || slave.hardwareId;
|
|
|
|
|
if (slaveSet.has(slaveHardwareId)) continue;
|
|
|
|
|
slaveSet.add(slaveHardwareId);
|
|
|
|
@ -5247,7 +5297,8 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
typeOfWater: slave.typeOfWater || tankInfo?.typeOfWater || slaveOrderInfo.typeOfWater || "",
|
|
|
|
|
tankHeight: slave.tankHeight,
|
|
|
|
|
support_lora_last_check_time: slave.support_lora_last_check_time,
|
|
|
|
|
team_member_support_lora_last_check_time: slave.team_member_support_lora_last_check_time
|
|
|
|
|
team_member_support_lora_last_check_time: slave.team_member_support_lora_last_check_time,
|
|
|
|
|
support_issue_status: slaveIssueStatus
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
master.connected_slaves.push(slaveEnriched);
|
|
|
|
@ -5255,9 +5306,15 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const commentTexts = (supportRecord.comments || []).map(c => c.text);
|
|
|
|
|
// Add comments with text and createdAt to each master device
|
|
|
|
|
const comments = (supportRecord.comments || []).map(c => ({
|
|
|
|
|
text: c.text,
|
|
|
|
|
commentsTime: moment(c.createdAt).tz("Asia/Kolkata").format("DD-MM-YYYY HH:mm")
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const master of Object.values(masterMap)) {
|
|
|
|
|
master.comments = commentTexts;
|
|
|
|
|
master.comments = comments;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return reply.send({
|
|
|
|
@ -5267,6 +5324,7 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
totalMasters: Object.keys(masterMap).length,
|
|
|
|
|
disconnectedIssues: Object.values(masterMap)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error fetching disconnected issues:", error);
|
|
|
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
|
|
@ -5274,6 +5332,7 @@ exports.getDisconnectedIssuesBySupportId = async (req, reply) => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// exports.getRemoveConnectedMastersWithSlaves = async (req, reply) => {
|
|
|
|
|
// try {
|
|
|
|
|
// const { supportId } = req.params;
|
|
|
|
@ -6126,11 +6185,8 @@ exports.moveIssueToCategory = async (req, reply) => {
|
|
|
|
|
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;
|
|
|
|
|
});
|
|
|
|
@ -6141,18 +6197,28 @@ exports.moveIssueToCategory = async (req, reply) => {
|
|
|
|
|
|
|
|
|
|
const issue = support.issues[index];
|
|
|
|
|
|
|
|
|
|
// If the hardwareId matches master hardwareId, move entire issue as is
|
|
|
|
|
if (issue.hardwareId === hardwareId) {
|
|
|
|
|
// Master issue moved
|
|
|
|
|
issue.resolved = true;
|
|
|
|
|
|
|
|
|
|
support.categorizedIssues.push({
|
|
|
|
|
...issue,
|
|
|
|
|
masterHardwareId: issue.masterHardwareId || issue.hardwareId,
|
|
|
|
|
category,
|
|
|
|
|
movedAt: nowTime,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
support.issues.splice(index, 1);
|
|
|
|
|
|
|
|
|
|
// ✅ Mark master as inactive
|
|
|
|
|
await Insensors.updateOne(
|
|
|
|
|
{ hardwareId },
|
|
|
|
|
{ $set: { support_issue_status: "inactive" } }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
issueMoved = true;
|
|
|
|
|
} else {
|
|
|
|
|
// hardwareId matches inside hardwareIds array — move that slave issue individually
|
|
|
|
|
// Slave hardware match
|
|
|
|
|
const slaveIndex = issue.hardwareIds.indexOf(hardwareId);
|
|
|
|
|
if (slaveIndex !== -1) {
|
|
|
|
|
const slaveName = issue.slaveNames?.[slaveIndex] || "Unknown";
|
|
|
|
@ -6166,12 +6232,18 @@ exports.moveIssueToCategory = async (req, reply) => {
|
|
|
|
|
movedAt: nowTime,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Remove slave from issue
|
|
|
|
|
issue.hardwareIds.splice(slaveIndex, 1);
|
|
|
|
|
issue.slaveNames.splice(slaveIndex, 1);
|
|
|
|
|
|
|
|
|
|
// If no more slaves left, remove the issue completely
|
|
|
|
|
// ✅ Mark slave as inactive
|
|
|
|
|
await Insensors.updateOne(
|
|
|
|
|
{ tankhardwareId: hardwareId },
|
|
|
|
|
{ $set: { support_issue_status: "inactive" } }
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// If no more slaves, remove the whole issue
|
|
|
|
|
if (issue.hardwareIds.length === 0) {
|
|
|
|
|
issue.resolved = true;
|
|
|
|
|
support.issues.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -6192,6 +6264,7 @@ exports.moveIssueToCategory = async (req, reply) => {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// exports.particularCategory = async (req, reply) => {
|
|
|
|
|
// const { supportId, category } = req.params;
|
|
|
|
|
|
|
|
|
@ -7006,19 +7079,18 @@ exports.updateComments = async (req, reply) => {
|
|
|
|
|
return reply.code(404).send({ error: "HardwareId not found in this support record's issues" });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Step 4: Append comment object correctly
|
|
|
|
|
// Step 4: Append comment object with formatted createdAt
|
|
|
|
|
const commentObj = {
|
|
|
|
|
text: trimmedComment,
|
|
|
|
|
customerId,
|
|
|
|
|
hardwareId,
|
|
|
|
|
createdAt: new Date()
|
|
|
|
|
createdAt: moment().format("DD-MM-YYYY HH:mm")
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!Array.isArray(supportRecord.comments)) {
|
|
|
|
|
supportRecord.comments = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use push instead of spreading to keep Mongoose subdoc validation intact
|
|
|
|
|
supportRecord.comments.push(commentObj);
|
|
|
|
|
|
|
|
|
|
await supportRecord.save();
|
|
|
|
@ -7030,4 +7102,3 @@ exports.updateComments = async (req, reply) => {
|
|
|
|
|
return reply.code(500).send({ error: "Internal server error" });
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|