From 59bd4bd4ab5b06d88f50c6b0f1023cc4357ac383 Mon Sep 17 00:00:00 2001 From: martbost Date: Sun, 30 Aug 2026 05:33:55 -0500 Subject: [PATCH] Show recorded figures for closed campaigns, not the wiped counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My own sweep created this. Deactivating an ad ZEROES `remaining` on the network, and for banners — where that column counts UP — zero reads as "served nothing". So every campaign the sweep closed flipped to 0 served / 2,500 left, despite the ledger correctly recording served=2500, completed=true. Closed campaigns now read from what we recorded at the time rather than re-deriving from a counter that no longer holds anything. Live campaigns still compute from the network, per placement type. Also removed the em-dash placeholder that blanked LEFT for stopped rows — there is a real number to show — and made stop()'s error path assume fully served rather than fully unserved, since guessing high would refund impressions that had actually been delivered. Co-Authored-By: Claude Fable 5 --- public/suite-traffic.js | 13 ++++++++++--- suite-traffic.js | 6 +++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/public/suite-traffic.js b/public/suite-traffic.js index f4d2ecb..e3bac73 100644 --- a/public/suite-traffic.js +++ b/public/suite-traffic.js @@ -187,7 +187,14 @@ var bought = Number(c.bought != null ? c.bought : c.impressions) || 0; var rawRem = l.remaining != null ? Number(l.remaining) : null; var servedShown, left; - if (rawRem == null) { servedShown = Number(served) || 0; left = null; } + if (c.completed || c.stopped) { + // Once a campaign is closed, trust what we recorded at the time. + // Deactivating an ad ZEROES `remaining` on the network — and for a + // banner, where that column counts UP, zero reads as "served nothing". + // Re-deriving here would wipe a finished campaign back to 0 served. + servedShown = Number(c.served != null ? c.served : bought) || 0; + left = Math.max(0, bought - servedShown); + } else if (rawRem == null) { servedShown = Number(served) || 0; left = null; } else if (c.kind === 'text') { left = Math.max(0, Math.min(rawRem, bought)); servedShown = bought - left; } else { servedShown = Math.max(0, Math.min(rawRem, bought)); left = bought - servedShown; } // "running" with 0 left reads as broken. It is not: banners have no cap on @@ -211,8 +218,8 @@ tr.innerHTML = '' + label + '' + '' + (c.target.indexOf('/p/') !== -1 ? 'personal page' : 'invite page') + '' + '' + Number(c.bought != null ? c.bought : c.impressions).toLocaleString() + '' + - '' + Number(c.stopped ? (c.served || 0) : servedShown).toLocaleString() + '' + - '' + (c.stopped ? '—' : (left != null ? left.toLocaleString() : '—')) + '' + + '' + Number(servedShown).toLocaleString() + '' + + '' + (left != null ? left.toLocaleString() : '—') + '' + '' + (l.hits != null ? l.hits : '—') + '' + '' + statusCell + ''; var act = document.createElement('td'); diff --git a/suite-traffic.js b/suite-traffic.js index bc23426..8df8ec9 100644 --- a/suite-traffic.js +++ b/suite-traffic.js @@ -238,7 +238,11 @@ async function stop(memberId, adId) { served = r.served; unserved = r.left; } - } catch (e) { /* if stats are unavailable, refund nothing rather than guess */ } + } catch (e) { + // Stats unavailable: refund nothing rather than guess. Guessing high would + // hand back impressions that were actually delivered. + served = boughtGuess; unserved = 0; + } await callNas({ action: 'deactivate', ad_id: Number(adId) });