☁️ Google Cloud Web App

Dashboard

TOTAL CUSTOMERS
0
All registered accounts
PAID CUSTOMERS
0
Current month fully paid
PENDING CUSTOMERS
0
Have outstanding balance
CLOSED / INACTIVE
0
Not included in current billing
CURRENT MONTH TOTAL
0
Expected this month
CURRENT MONTH PAID
0
Collected this month
CURRENT MONTH PENDING
0
Only current month outstanding
PREVIOUS MONTH / OLD DUE
0
Only previous/old outstanding
ROUTERS INSTALLED
0
Customer routers in service
ONU / ONT INSTALLED
0
Active optical units
FIBER ROUTERS
0
Active fiber routers
NETWORK SWITCHES
0
Installed switches
MIKROTIK USERS
0
Users on MikroTik
PANEL USERS
0
Users on Panel

Collection Performance

0%
Current Total0
Collected
Outstanding0

Service / Router Due

Service Charges0
Service Paid0
Service Pending0

Customers With Pending Balance

CustomerMobileCurrent FeePrevious DueTotal OutstandingStatus
`; downloadBlob(new Blob([doc],{type:'application/vnd.ms-excel;charset=utf-8'}),`SkyNet Pending Fees ${m}.xls`); toast('Pending fee Excel downloaded'); }; window.downloadPendingImage=function(){ const m=document.getElementById('ledgerMonth')?.value||currentMonth(); const rows=pendingRowsForMonth(m); if(!rows.length){toast('No pending customers for this month');return;} const width=1500, rowH=42, headerH=170, footerH=55, height=Math.min(6000,headerH+rows.length*rowH+footerH); const canvas=document.createElement('canvas'); canvas.width=width; canvas.height=height; const ctx=canvas.getContext('2d'); ctx.fillStyle='#f4f7fb';ctx.fillRect(0,0,width,height); ctx.fillStyle='#102a43';ctx.fillRect(0,0,width,120); ctx.fillStyle='#fff';ctx.font='bold 30px Arial';ctx.fillText('SkyNet Internet Fee Pending Report',42,48); ctx.font='16px Arial';ctx.fillText(monthLabel(m)+' • Current Pending + Previous Due',42,82); ctx.font='bold 14px Arial';ctx.fillText('Generated: '+new Date().toLocaleString(),42,106); const cols=[ ['Serial',45],['Customer',150],['Area',330],['Mobile',500],['Month',660], ['Fee',790],['Paid',900],['Current Due',1010],['Previous Due',1150],['Total Due',1320] ]; let y=145; ctx.fillStyle='#eaf3fb';ctx.fillRect(30,y-28,width-60,38); ctx.fillStyle='#38526b';ctx.font='bold 13px Arial'; cols.forEach(([t,x])=>ctx.fillText(t,x,y)); y+=28; ctx.font='13px Arial'; rows.forEach((x,i)=>{ if(i%2===0){ctx.fillStyle='#fff';ctx.fillRect(30,y-22,width-60,rowH);} ctx.fillStyle='#1d2b3a'; const vals=[x.serial,x.name,x.area,x.mobile,monthLabel(x.month),fmt(x.fee),fmt(x.paid),fmt(x.current),fmt(x.previous),fmt(x.total)]; cols.forEach(([t,xp],j)=>{ let v=String(vals[j]??''); if(v.length>20)v=v.slice(0,19)+'…'; ctx.fillText(v,xp,y); }); y+=rowH; }); ctx.fillStyle='#6b7b8f';ctx.font='12px Arial'; ctx.fillText(`Total pending customers: ${rows.length} | Total outstanding: Rs. ${fmt(rows.reduce((s,x)=>s+x.total,0))}`,42,y+15); canvas.toBlob(blob=>downloadBlob(blob,`SkyNet Pending Fees ${m}.png`),'image/png'); toast('Pending fee image downloaded'); }; // Make the sidebar feel like a compact professional app navigation. const side=document.querySelector('.side'); if(side){ side.setAttribute('aria-label','SkyNet navigation'); const nav=document.querySelector('.nav'); if(nav){ const buttons=[...nav.querySelectorAll('button')]; buttons.forEach((b,i)=>{ b.setAttribute('title',b.textContent.trim()); b.dataset.navIndex=i+1; }); } } // Prevent accidental horizontal page overflow on desktop; tables remain scrollable. const style=document.createElement('style'); style.id='skynet-final-qa-style'; style.textContent=` .nav{padding-right:8px} .nav button{border-radius:12px;min-height:44px} .nav button span{font-weight:700;letter-spacing:.05px} .nav button.active:after{content:"";position:absolute;right:7px;width:4px;height:22px;border-radius:99px;background:#fff;opacity:.9} .ledger-tools{gap:7px} .ledger-tools select{min-width:120px} .ledger-tools button{white-space:nowrap} #customers .panelhead .toolbar{display:flex;flex-wrap:wrap;gap:7px} @media(max-width:950px){.nav button.active:after{display:none}} @media(max-width:650px){.ledger-tools{display:grid!important;grid-template-columns:1fr 1fr}.ledger-tools button{width:100%}} `; document.head.appendChild(style); })(); /* Old v11 automatic billing migration disabled: existing billing data is preserved unchanged. */ /* Apps Script Web App: expose the actual functions from this IIFE scope. */ window.seedProvidedCustomersV10 = seedProvidedCustomersV10; window.nextCustomerSerial = nextCustomerSerial; window.ensureCustomerSerials = ensureCustomerSerials; window.currentMonth = currentMonth; window.currentDue = currentDue; window.totalDue = totalDue; window.active = active; window.save = save; window.toast = toast; window.page = page; window.closeM = closeM; window.openM = openM; window.monthLabel = monthLabel; window.monthsList = monthsList; window.recordUserStatusEvent = recordUserStatusEvent; window.recordPackageChange = recordPackageChange; window.normalizeCustomerFields = normalizeCustomerFields; window.isPaidForCurrentMonth = isPaidForCurrentMonth; window.pendingCustomerList = pendingCustomerList; window.openProfile = openProfile; window.renderProfileLedger = renderProfileLedger; window.renderPackageHistory = renderPackageHistory; window.renderProfilePayments = renderProfilePayments; window.openManualDueForProfile = openManualDueForProfile; window.editManualDue = editManualDue; window.deleteManualDue = deleteManualDue; window.openService = openService; window.openPayment = openPayment; window.editPayment = editPayment; window.deletePayment = deletePayment; window.updatePaymentDue = updatePaymentDue; window.openPaymentForProfile = openPaymentForProfile; window.feeShareText = feeShareText; window.openFeeShareForCustomer = openFeeShareForCustomer; window.openFeeShare = openFeeShare; window.copyFeeShare = copyFeeShare; window.shareFeeWhatsApp = shareFeeWhatsApp; window.renderLedger = renderLedger; window.receiveLedgerPayment = receiveLedgerPayment; window.payAllLedger = payAllLedger; window.initLedger = initLedger; window.renderPendingCustomers = renderPendingCustomers; window.renderExpenses = renderExpenses; window.openExpense = openExpense; window.renderStock = renderStock; window.openStock = openStock; window.updateAdvancedSections = updateAdvancedSections; window.resetAll = resetAll; window.lockApp = lockApp; window.normalizePhone = normalizePhone; window.reminderDueDay = reminderDueDay; window.ensureBillingCycle = ensureBillingCycle; window.manualDueFor = manualDueFor; window.manualDueEntries = manualDueEntries; window.historicalFee = historicalFee; window.monthCharge = monthCharge; window.billingStartMonth = billingStartMonth; window.dueMonthsFor = dueMonthsFor; window.paymentAllocations = paymentAllocations; window.monthPaid = monthPaid; window.monthPending = monthPending; window.customerCurrentDue = customerCurrentDue; window.customerOldDue = customerOldDue; window.totalCustomerDue = totalCustomerDue; window.whatsappMessage = whatsappMessage; window.renderWhatsApp = renderWhatsApp; window.sendWhatsApp = sendWhatsApp; window.openAllDueWhatsApp = openAllDueWhatsApp; window.markReminderRun = markReminderRun; window.automaticReminderCheck = automaticReminderCheck; window.areaOptions = areaOptions; window.waPhone1 = waPhone1; window.waPhone2 = waPhone2; window.waContacts = waContacts; window.waAreaOptions = waAreaOptions; window.renderWABroadcast = renderWABroadcast; window.specialWhatsApp = specialWhatsApp; window.copyWABroadcast = copyWABroadcast; window.renderAreaReport = renderAreaReport; window.renderUserReport = renderUserReport; window.initUserReport = initUserReport; window.getGoogleClientId = getGoogleClientId; window.loadGoogleClientId = loadGoogleClientId; window.googleDriveToken = googleDriveToken; window.uploadBackupToDrive = uploadBackupToDrive; window.backupToGoogleDrive = backupToGoogleDrive; window.backupCounts = backupCounts; window.isAppsScriptWebApp = isAppsScriptWebApp; window.getCloudSyncUrl = getCloudSyncUrl; window.loadCloudSyncUrl = loadCloudSyncUrl; window.saveCloudSyncUrl = saveCloudSyncUrl; window.updateCloudSyncBadge = updateCloudSyncBadge; window.setCloudSyncStatus = setCloudSyncStatus; window.scheduleCloudPush = scheduleCloudPush; window.cloudRunSave = cloudRunSave; window.pushToCloud = pushToCloud; window.jsonpRequest = jsonpRequest; window.cleanup = cleanup; window.cloudRunLoad = cloudRunLoad; window.pullFromCloud = pullFromCloud; window.backupHash = backupHash; window.backupPayload = backupPayload; window.verifyBackupPayload = verifyBackupPayload; window.backupSnapshot = backupSnapshot; window.updateBackupUI = updateBackupUI; window.runDailyBackup = runDailyBackup; window.downloadLatestBackup = downloadLatestBackup; window.renderAll = renderAll; window.renderAllNoLoop = renderAllNoLoop; window.dashboard = dashboard; window.renderCustomers = renderCustomers; window.dashboardOpen = dashboardOpen; window.renderPayments = renderPayments; window.renderServices = renderServices; window.renderAssets = renderAssets; window.openAsset = openAsset; window.editAsset = editAsset; window.reports = reports; window.fillSelects = fillSelects; window.openCustomer = openCustomer; window.editCustomer = editCustomer; window.deleteCustomer = deleteCustomer; window.exportJSON = exportJSON; window.importJSON = importJSON; window.exportCSV = exportCSV; window.pendingRowsForMonth = pendingRowsForMonth; window.downloadBlob = downloadBlob; window.applyBillingCorrectionV11 = applyBillingCorrectionV11; renderAll();