diff --git a/src/controllers/installationController.js b/src/controllers/installationController.js index dd5a401c..b8f79769 100644 --- a/src/controllers/installationController.js +++ b/src/controllers/installationController.js @@ -3095,6 +3095,203 @@ exports.getWaitingMasterSlaveSummary = async (req, reply) => { } }; +exports.getCompleteMasterSlaveSummary = async (req, reply) => { + try { + const { customerId } = req.params; + + if (!customerId) { + return reply.status(400).send({ error: "customerId is required" }); + } + + // Fetch all master devices from Insensors + const masters = await Insensors.find({ customerId, type: "master" }).lean(); + + // Fetch orders to build orderMap: hardwareId → { masterName, location, work_status } + const orders = await Order.find({ customerId }).lean(); + const orderMap = {}; // key: hardwareId + + orders.forEach(order => { + if (Array.isArray(order.master_connections)) { + order.master_connections.forEach(connection => { + if (connection.hardwareId) { + orderMap[connection.hardwareId] = { + masterName: connection.master_name || null, + location: connection.location || null, + work_status: connection.work_status || null + }; + } + }); + } + }); + + const result = []; + + for (const master of masters) { + const orderInfo = orderMap[master.hardwareId] || {}; + + // ✅ Only keep masters where work_status === 'active' + if (orderInfo.work_status !== 'complete') { + continue; // skip this master + } + + // Prefer Insensors name/location, fallback to order info + const masterName = master.masterName || orderInfo.masterName || null; + const location = master.location || orderInfo.location || null; + + // Fetch latest GSM data + const latestGsmData = await IotData.findOne({ hardwareId: master.hardwareId }) + .sort({ date: -1, time: -1 }) + .lean(); + + let connectedGsmDate = null; + let connectedGsmTime = null; + let gsmStatus = "unknown"; + let gsmLastDisconnect = master.gsm_last_disconnect_time || null; + let gsmLastCheckTime = master.gsm_last_check_time || null; + + if (latestGsmData?.date && latestGsmData?.time) { + const indiaTime = moment.tz( + `${moment(latestGsmData.date).format("YYYY-MM-DD")} ${latestGsmData.time}`, + "YYYY-MM-DD HH:mm:ss", + "Asia/Kolkata" + ); + connectedGsmDate = indiaTime.format("DD-MM-YYYY"); + connectedGsmTime = indiaTime.format("HH:mm:ss"); + + const now = moment.tz("Asia/Kolkata"); + const diffInMinutes = now.diff(indiaTime, "minutes"); + + gsmStatus = diffInMinutes <= 1 ? "connected" : "disconnected"; + + if (gsmStatus === "disconnected") { + gsmLastDisconnect = `${connectedGsmDate} ${connectedGsmTime}`; + } + + // Update master Insensors record + await Insensors.updateOne( + { hardwareId: master.hardwareId }, + { + $set: { + connected_status: gsmStatus, + connected_gsm_date: connectedGsmDate, + connected_gsm_time: connectedGsmTime, + gsm_last_check_time: gsmLastCheckTime, + gsm_last_disconnect_time: gsmLastDisconnect + } + } + ); + } + + // Process connected slaves + const connectedSlaves = []; + const slaves = await Insensors.find({ connected_to: master.hardwareId, type: "slave" }).lean(); + + for (const slave of slaves) { + const now = moment.tz("Asia/Kolkata"); + let connectedLoraDate = null; + let connectedLoraTime = null; + let loraStatus = "disconnected"; + let loraLastDisconnect = slave.lora_last_disconnect_time || null; + let loraLastCheckTime = slave.lora_last_check_time || null; + let typeOfWater = null; + + // Fetch latest slave IotData (using master hardwareId because slave data comes there) + const slaveIot = await IotData.findOne({ hardwareId: slave.connected_to }) + .sort({ date: -1, time: -1 }) + .lean(); + + if (slaveIot?.tanks?.length && slave.tankhardwareId) { + const matchedTank = slaveIot.tanks.find(t => t.tankhardwareId === slave.tankhardwareId); + + if (matchedTank) { + const indiaTime = moment.tz( + `${moment(matchedTank.date).format("YYYY-MM-DD")} ${matchedTank.time}`, + "YYYY-MM-DD HH:mm:ss", + "Asia/Kolkata" + ); + + connectedLoraDate = indiaTime.format("DD-MM-YYYY"); + connectedLoraTime = indiaTime.format("HH:mm:ss"); + + const diffMinutes = now.diff(indiaTime, "minutes"); + const tankHeight = parseFloat(matchedTank.tankHeight) || 0; + + loraStatus = (tankHeight > 0 && diffMinutes <= 1) ? "connected" : "disconnected"; + + if (loraStatus === "disconnected") { + loraLastDisconnect = `${connectedLoraDate} ${connectedLoraTime}`; + } + } + } + + // Enrich with tank typeOfWater if exists + const matchedTankDetails = await Tank.findOne({ + customerId, + tankhardwareId: slave.tankhardwareId + }).lean(); + + if (matchedTankDetails?.typeOfWater) { + typeOfWater = matchedTankDetails.typeOfWater; + } + + // Update slave Insensors record + await Insensors.updateOne( + { hardwareId: slave.hardwareId }, + { + $set: { + connected_status: loraStatus, + connected_lora_date: connectedLoraDate, + connected_lora_time: connectedLoraTime, + lora_last_check_time: loraLastCheckTime, + lora_last_disconnect_time: loraLastDisconnect + } + } + ); + + connectedSlaves.push({ + hardwareId: slave.hardwareId, + tankhardwareId: slave.tankhardwareId || null, + tankName: slave.tankName || null, + location: slave.tankLocation || null, + connected_status: loraStatus, + connected_lora_date: connectedLoraDate, + connected_lora_time: connectedLoraTime, + lora_last_check_time: loraLastCheckTime, + lora_last_disconnect_time: loraLastDisconnect, + type: slave.type || "slave", + typeOfWater, + connected_to: slave.connected_to || null + }); + } + + result.push({ + hardwareId: master.hardwareId, + masterName, + location, + type: master.type || "master", + connected_status: gsmStatus, + connected_slave_count: connectedSlaves.length, + connected_slaves: connectedSlaves, + connected_gsm_date: connectedGsmDate, + connected_gsm_time: connectedGsmTime, + gsm_last_check_time: gsmLastCheckTime, + gsm_last_disconnect_time: gsmLastDisconnect, + connected_lora_date: master.connected_lora_date || null, + connected_lora_time: master.connected_lora_time || null + }); + } + + return reply.send({ + status_code: 200, + message: "Master-slave summary retrieved successfully", + data: result + }); + + } catch (error) { + console.error("Error in getMasterSlaveSummary:", error); + return reply.status(500).send({ error: "Internal Server Error" }); + } +}; exports.getMasterWithSlaves = async (req, reply) => { try { const { installationId, customerId, hardwareId } = req.params; diff --git a/src/controllers/storeController.js b/src/controllers/storeController.js index 77d41eab..3fea8be0 100644 --- a/src/controllers/storeController.js +++ b/src/controllers/storeController.js @@ -2780,6 +2780,91 @@ exports.getPendingOrdersByInstallationAndTeamMember = async (req, reply) => { } }; +exports.getCompleteOrdersByInstallationAndTeamMember = async (req, reply) => { + try { + const { installationId, teamMemberId } = req.params; + + if (!installationId) { + return reply.status(400).send({ error: "installationId is required" }); + } + if (!teamMemberId) { + return reply.status(400).send({ error: "teamMemberId is required" }); + } + + // Fetch orders matching installationId and assignedTeamMembers + const orders = await Order.find({ + installationId, + assignedTeamMembers: teamMemberId + }); + + if (!orders.length) { + return reply.send({ + status_code: 200, + message: "No orders found for this installation and team member", + data: [], + }); + } + + const uniqueCustomersMap = new Map(); + + // Build unique customerId-based map + for (const order of orders) { + if (!uniqueCustomersMap.has(order.customerId)) { + uniqueCustomersMap.set(order.customerId, order); + } + } + + let uniqueOrders = Array.from(uniqueCustomersMap.values()); + + // ✅ Filter orders that have at least one pending master_connection + uniqueOrders = uniqueOrders.filter(order => + Array.isArray(order.master_connections) && + order.master_connections.some(mc => mc.work_status === 'complete') + ); + + if (!uniqueOrders.length) { + return reply.send({ + status_code: 200, + message: "No pending orders found for this installation and team member", + data: [], + }); + } + + // Enrich and also filter master_connections inside each order + const ordersWithDetails = await Promise.all( + uniqueOrders.map(async (order) => { + const customer = await User.findOne({ customerId: order.customerId }).lean(); + + const allocatedSensors = await Insensors.find({ + storeId: order.storeId, + customerId: order.customerId, + status: "blocked", + }).lean(); + + // Keep only master_connections with work_status === 'pending' + const pendingMasters = order.master_connections.filter(mc => mc.work_status === 'complete'); + + return { + ...order.toObject(), + master_connections: pendingMasters, + customer: customer || null, + allocated_sensors: allocatedSensors, + }; + }) + ); + + return reply.send({ + status_code: 200, + message: "Pending orders fetched successfully", + data: ordersWithDetails, + }); + + } catch (err) { + console.error("Error fetching pending orders:", err); + return reply.status(500).send({ error: "Internal server error" }); + } +}; + exports.getManagerPendingOrdersByInstallationId = async (req, reply) => { try { const { installationId } = req.params; diff --git a/src/routes/installationRoute.js b/src/routes/installationRoute.js index 3f2b5c73..9f9cb6c4 100644 --- a/src/routes/installationRoute.js +++ b/src/routes/installationRoute.js @@ -621,6 +621,23 @@ fastify.post( handler: installationController.getMasterSlaveSummary, }); + fastify.get("/api/getcompletemasterlistwithslaves/:customerId", { + schema: { + description: "Get Complete masrter connected slave data with full info", + tags: ["Installation"], + summary: "Get Complete masrter connected slave data with full info", + params: { + type: "object", + properties: { + customerId: { type: "string" }, + + }, + required: [ "customerId"], + }, + }, + handler: installationController.getCompleteMasterSlaveSummary, + }); + fastify.get('/api/master-with-slaves/:installationId/:customerId/:hardwareId', { schema: { tags: ['Installation'], diff --git a/src/routes/storeRoute.js b/src/routes/storeRoute.js index a2ea6fab..b5c2bfd5 100644 --- a/src/routes/storeRoute.js +++ b/src/routes/storeRoute.js @@ -1996,7 +1996,7 @@ fastify.get("/api/Pendingordersofinstall/:installationId/:teamMemberId", { schema: { tags: ["Installation"], description: "Fetches orders based on installationId", - summary: "Get Pending orders by installationId", + summary: "Get Pending orders team member by installationId", params: { type: "object", properties: { @@ -2015,6 +2015,29 @@ fastify.get("/api/Pendingordersofinstall/:installationId/:teamMemberId", { handler: storeController.getPendingOrdersByInstallationAndTeamMember, }); +fastify.get("/api/Completeordersofinstall/:installationId/:teamMemberId", { + schema: { + tags: ["Installation"], + description: "Fetches orders based on installationId", + summary: "Get Complete orders Team Member by installationId", + params: { + type: "object", + properties: { + installationId: { type: "string" }, + teamMemberId: { type: "string"}, + //customerId: { type: "string"}, + }, + // required: ["installationId"], + }, + security: [ + { + basicAuth: [], + }, + ], + }, + handler: storeController.getCompleteOrdersByInstallationAndTeamMember, +}); + fastify.get("/api/ManagerPendingordersofinstall/:installationId", { schema: { tags: ["Installation"],