const { getStoreLocation, getStoreRegisters, getStorePrinters, getStorePaymentTerminals } = require('../services/siw'); const { findMerakiNetwork, getMerakiDeviceAvailabilities, getMerakiClients } = require('../services/meraki'); const { formatStoreReport } = require('../utils/formatter'); function formatLastSeen(lastSeen) { if (!lastSeen) return 'N/A'; const now = new Date(); const seen = new Date(lastSeen); const diffMs = now - seen; const diffMin = Math.floor(diffMs / 60000); if (diffMin < 1) return 'Just now'; if (diffMin < 60) return `${diffMin} min ago`; const hours = Math.floor(diffMin / 60); return `${hours} hr ago`; } async function getStoreDetail(storeNumber) { console.log(`šŸ” Starting full analysis for store ${storeNumber}`); let locationData, merakiNetwork, registers, printers, paymentTerminals, merakiClients = [], mdmDevices = []; try { // Phase 1: Fast lookups [locationData, merakiNetwork] = await Promise.all([ getStoreLocation(storeNumber), findMerakiNetwork(storeNumber) ]); // Phase 2: Heavy parallel lookups const parallelPromises = [ getStoreRegisters(storeNumber), getStorePrinters(storeNumber), getStorePaymentTerminals(storeNumber) ]; if (merakiNetwork) { parallelPromises.push( getMerakiClients(merakiNetwork.id), require('../services/mdm').getMDMDevices(storeNumber) ); } const results = await Promise.all(parallelPromises); registers = results[0]; printers = results[1]; paymentTerminals = results[2]; if (merakiNetwork) { console.log(`Meraki Network:`) console.log(JSON.stringify(merakiNetwork, null, 2)) merakiClients = results[3] || []; mdmDevices = results[4] || []; } console.log(`āœ… Fetched: ${registers?.length || 0} registers, ${printers?.length || 0} printers, ${paymentTerminals?.length || 0} payment terminals`); if (merakiNetwork) { console.log(`āœ… Fetched ${merakiClients.length} Meraki clients`); console.log(`āœ… Fetched ${mdmDevices.length} MDM devices`); } } catch (err) { console.error('āŒ Error fetching data:', err.message); } let report = formatStoreReport(locationData, storeNumber); // === Meraki Network Hardware === if (merakiNetwork) { report += `**🌐 [${merakiNetwork.name}](${merakiNetwork.url})**\n\n`; let activeDevices = []; try { const availabilities = await getMerakiDeviceAvailabilities(merakiNetwork.id); activeDevices = availabilities.filter(d => d.status !== 'dormant'); } catch (e) { console.error('Device availabilities failed:', e.message); } // Sort: Rear Switch → Front Switch → APs activeDevices.sort((a, b) => { const nameA = (a.name || '').trim().toUpperCase(); const nameB = (b.name || '').trim().toUpperCase(); if (nameA.includes('SWR') || /SW.*R\d?$/.test(nameA)) return -1; if (nameB.includes('SWR') || /SW.*R\d?$/.test(nameB)) return 1; if (nameA.includes('SWF') || /SW.*F\d/.test(nameA)) return -1; if (nameB.includes('SWF') || /SW.*F\d/.test(nameB)) return 1; return nameA.localeCompare(nameB); }); report += `**šŸ› ļø Active Network Devices (${activeDevices.length})**\n`; activeDevices.forEach(dev => { const statusEmoji = dev.status === 'online' ? 'āœ…' : 'āš ļø'; const deviceName = (dev.name || dev.serial || 'Unknown').trim(); report += `**${deviceName}** — ${statusEmoji} ${dev.status}`; if (dev.serial && merakiNetwork.url) { // Extract short network code from the URL (e.g. JCz-ucnd) const urlParts = merakiNetwork.url.split('/n/'); const networkCode = urlParts[1] ? urlParts[1].split('/')[0] : merakiNetwork.id; let link = `${urlParts[0]}/n/${networkCode}`; if (dev.productType === 'switch') { link += `/manage/switches/${dev.serial}/summary`; } else if (dev.productType === 'wireless') { link += `/manage/access_points/${dev.serial}/summary`; } else { link += `/manage/${dev.serial}/summary`; } report += ` → [Dashboard](${link})`; } report += `\n`; }); } else { report += `\nāš ļø No matching Meraki network found.\n`; } // === Store Registers === if (registers && registers.length > 0) { report += `\n**šŸ–„ļø Store Registers (${registers.length})**\n`; registers.forEach(reg => { const regNum = reg.register_number ? `Register ${reg.register_number}` : 'Register'; const brand = reg.brand_display_name || 'Unknown'; const type = reg.register_type_name || 'Unknown'; const name = (reg.register_display_name || reg.printer_name || 'Unknown').trim(); const match = merakiClients.find(c => c?.description && (c.description.trim().toLowerCase() === name.toLowerCase() || c.description.includes(name)) ); const status = match ? (match.status === 'Online' ? 'āœ… Online' : 'āŒ Offline') : 'ā“ Unknown'; const lastSeen = match ? formatLastSeen(match.lastSeen) : ''; const deviceName = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : ''; const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : ''; let link = ''; if (match && merakiNetwork?.url) { const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id; link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`; } report += `**${regNum}** — ${brand} — ${type} — ${status} — ${lastSeen}${deviceName}${connection}${link}\n`; }); } // === Store Printers (USB filtered) === const filteredPrinters = (printers || []).filter(p => (p.connection_type_name || '').toLowerCase() !== 'usb' ); if (filteredPrinters.length > 0) { report += `\n**šŸ–Øļø Store Printers (${filteredPrinters.length})**\n`; filteredPrinters.forEach(printer => { const name = (printer.printer_name || 'Unknown').trim(); const model = printer.printer_model_name || 'N/A'; const match = merakiClients.find(c => c?.description && (c.description.trim().toLowerCase() === name.toLowerCase() || c.description.includes(name)) ); const status = match ? (match.status === 'Online' ? 'āœ… Online' : 'āŒ Offline') : 'ā“ Unknown'; const lastSeen = match ? formatLastSeen(match.lastSeen) : ''; const deviceName = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : ''; const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : ''; let link = ''; if (match && merakiNetwork?.url) { const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id; link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`; } report += `**${name}** — ${model} — ${status} — ${lastSeen}${deviceName}${connection}${link}\n`; }); } if (paymentTerminals && paymentTerminals.length > 0) { report += `\n**šŸ’³ Payment Terminals (${paymentTerminals.length})**\n`; paymentTerminals.forEach(term => { const deviceName = term.device_name || 'Unknown Terminal'; const adyenName = term.adyen_device_name || 'N/A'; const model = term.device_model_name || 'N/A'; const type = term.device_type_name || 'N/A'; const match = merakiClients.find(c => c?.description && (c.description.includes(deviceName) || c.description.includes(adyenName) || (term.ip_address && c.description.includes(term.ip_address.split('.')[0]))) ); const status = match ? (match.status === 'Online' ? 'āœ… Online' : 'āŒ Offline') : 'ā“ Unknown'; const lastSeen = match ? formatLastSeen(match.lastSeen) : ''; const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : ''; const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : ''; let link = ''; if (match && merakiNetwork?.url) { const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id; link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`; } report += `**${deviceName}** — ${adyenName} — ${model} — ${type} — ${status} — ${lastSeen}${recentDevice}${connection}${link}\n`; }); } if (mdmDevices && mdmDevices.length > 0) { const servers = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('SRV')); const mobileRegs = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('MR')); const custDisplays = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('CD')); const iphones = mdmDevices.filter(d => (d.UserName || d.DeviceFriendlyName || '').includes('IPH')); // === Store Servers === if (servers.length > 0) { report += `\n**šŸ–„ļø Store Servers (${servers.length})**\n`; servers.forEach(dev => { const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown Server'; const match = merakiClients.find(c => c?.description && (c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName))) ); const status = match ? (match.status === 'Online' ? 'āœ… Online' : 'āŒ Offline') : 'ā“ Unknown'; const lastSeen = match ? formatLastSeen(match.lastSeen) : ''; const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : ''; const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : ''; let link = ''; if (match && merakiNetwork?.url) { const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id; link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`; } report += `**${name}** — ${status} — ${lastSeen}${recentDevice}${connection}${link}\n`; }); } // === Mobile Registers === if (mobileRegs.length > 0) { report += `\n**šŸ“± Mobile Registers (${mobileRegs.length})**\n`; mobileRegs.forEach(dev => { const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown Mobile Register'; const match = merakiClients.find(c => c?.description && (c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName))) ); const status = match ? (match.status === 'Online' ? 'āœ… Online' : 'āŒ Offline') : 'ā“ Unknown'; const lastSeen = match ? formatLastSeen(match.lastSeen) : ''; const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : ''; const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : ''; let link = ''; if (match && merakiNetwork?.url) { const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id; link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`; } report += `**${name}** — ${status} — ${lastSeen}${recentDevice}${connection}${link}\n`; }); } // === Customer Displays === if (custDisplays.length > 0) { report += `\n**šŸ–„ļø Customer Displays (${custDisplays.length})**\n`; custDisplays.forEach(dev => { const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown Display'; const match = merakiClients.find(c => c?.description && (c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName))) ); const status = match ? (match.status === 'Online' ? 'āœ… Online' : 'āŒ Offline') : 'ā“ Unknown'; const lastSeen = match ? formatLastSeen(match.lastSeen) : ''; const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : ''; const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : ''; let link = ''; if (match && merakiNetwork?.url) { const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id; link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`; } report += `**${name}** — ${status} — ${lastSeen}${recentDevice}${connection}${link}\n`; }); } // === Store iPhones === if (iphones.length > 0) { report += `\n**šŸ“± Store iPhones (${iphones.length})**\n`; iphones.forEach(dev => { const name = dev.UserName || dev.DeviceFriendlyName || 'Unknown iPhone'; const match = merakiClients.find(c => c?.description && (c.description.includes(name) || (dev.UserName && c.description.includes(dev.UserName))) ); const status = match ? (match.status === 'Online' ? 'āœ… Online' : 'āŒ Offline') : 'ā“ Unknown'; const lastSeen = match ? formatLastSeen(match.lastSeen) : ''; const recentDevice = match?.recentDeviceName ? ` via ${match.recentDeviceName}` : ''; const connection = match?.recentDeviceConnection ? ` (${match.recentDeviceConnection})` : ''; let link = ''; if (match && merakiNetwork?.url) { const networkCode = merakiNetwork.url.split('/n/')[1]?.split('/')[0] || merakiNetwork.id; link = ` → [Meraki Client](https://n976.dashboard.meraki.com/${merakiNetwork.name.replace(/ /g, '-')}/n/${networkCode}/manage/clients/${match.id}/overview)`; } report += `**${name}** — ${status} — ${lastSeen}${recentDevice}${connection}${link}\n`; }); } } return report; } module.exports = { getStoreDetail };