diff --git a/backend/routes/vclMultiVertical.js b/backend/routes/vclMultiVertical.js index e7338b9..d52db95 100644 --- a/backend/routes/vclMultiVertical.js +++ b/backend/routes/vclMultiVertical.js @@ -141,15 +141,20 @@ async function persistMultiVerticalUpload({ items, summary, reportDate, filename } // 6. Create/update compliance_snapshots for this vertical - // Use summary data for accurate totals (compliance_items only has non-compliant devices) + // Use summary data for accurate totals (compliance_items only has non-compliant devices). + // IMPORTANT: Only use "ALL:" rollup rows to avoid double-counting. Each Summary sheet + // has sub-team rows AND a rollup row per metric — the rollup already includes sub-teams. const currentMonth = new Date().toISOString().slice(0, 7); let totalDevices = 0, snapshotCompliant = 0, snapshotNonCompliant = 0; if (summary && summary.entries && summary.entries.length > 0) { for (const entry of summary.entries) { - totalDevices += entry.total || 0; - snapshotCompliant += entry.compliant || 0; - snapshotNonCompliant += entry.non_compliant || 0; + // Only count rollup rows (team starts with "ALL:") to avoid double-counting + if (entry.team && entry.team.startsWith('ALL:')) { + totalDevices += entry.total || 0; + snapshotCompliant += entry.compliant || 0; + snapshotNonCompliant += entry.non_compliant || 0; + } } } @@ -460,16 +465,18 @@ function createVCLMultiVerticalRouter(upload) { const latestUploadIds = latestUploads.map(u => u.id); - // Aggregate summary data from the latest upload per vertical - // Each row in vcl_multi_vertical_summary is one metric for one vertical. - // We sum across metrics per vertical to get vertical-level totals. + // Aggregate summary data from the latest upload per vertical. + // IMPORTANT: Only use "ALL:" rollup rows to avoid double-counting. + // Each spreadsheet's Summary sheet contains both sub-team rows (ACCESS-OPS, + // STEAM, etc.) AND a rollup row (ALL: NTS-AEO) per metric. The rollup row + // already includes all sub-team totals, so summing all rows would double-count. const { rows: verticalSummary } = await pool.query(` SELECT vertical, SUM(total)::int AS total_devices, SUM(compliant)::int AS compliant, SUM(non_compliant)::int AS non_compliant FROM vcl_multi_vertical_summary - WHERE upload_id = ANY($1) + WHERE upload_id = ANY($1) AND team LIKE 'ALL:%' GROUP BY vertical ORDER BY vertical `, [latestUploadIds]); @@ -702,9 +709,10 @@ function createVCLMultiVerticalRouter(upload) { [uploadId, vertical] ); - // Aggregate by category + // Aggregate by category — only use "ALL:" rollup rows to avoid double-counting const categoryMap = {}; for (const m of metrics) { + if (!m.team || !m.team.startsWith('ALL:')) continue; const cat = m.category || 'Other'; if (!categoryMap[cat]) categoryMap[cat] = { category: cat, non_compliant: 0, compliant: 0, total: 0 }; categoryMap[cat].non_compliant += m.non_compliant;