docs(sales-summaries): add worked examples and reproduction steps to the report
Every claim in the 30-day report now names the record behind it, so a reader can
check it rather than take it on trust.
R1 square/order/NGLK-SM-OxSX9gpXJV394qqT8mnBGypUwKNZY
tip -60.00, total -60.00, zero charges — the reversal get-tip could not
see, and NGLK 2026-08-04 was off by exactly -60.00
R2 square/order/NTPT-PT-KrMZzcon1cpQEJUyetErkBIpcdEZY
service charge 427.10 inside a 3,198.78 order, credited nowhere, and
NTPT 2026-08-06 was off by exactly +427.10
Also lists the 19 days the data work unbalanced with their before and after
figures — all 19 land back at zero once R1 and R2 apply — and three real rows
behind the balanced-but-changed pattern, where Card Payments falls by exactly
what Fees gains.
Corrects the NGBK 2026-08-06 residual. It was attributed to refunds held by both
twins; tracing the day shows tender of $6,358.99 against order totals of
$6,059.57, a difference of exactly the $299.42 imbalance. The day has no refund
lines, no order carries two charges for one payment id, and the orders missing
line items carry no tender. It is a source-data discrepancy, not a calculation
defect.
Adds the REPL calls that re-derive every figure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -279,7 +279,22 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>The second pattern is worth reading carefully: <code>Card Payments</code> falls by exactly what <code>Fees</code> gains, so the day stays balanced while the split between the two lines changes. That is a data-freshness effect from re-importing, not a change in how anything is calculated.</p>
|
||||
<p>The second pattern is worth reading carefully: <code>Card Payments</code> falls by exactly what <code>Fees</code> gains, so the day stays balanced while the split between the two lines changes. That is a data-freshness effect from re-importing, not a change in how anything is calculated. Three real rows:</p>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<table>
|
||||
<thead><tr><th>Client</th><th>Date</th><th>Line</th><th class="n">Before</th><th class="n">After</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="mono">NGAN</td><td>2026-08-12</td><td>Card Payments</td><td class="n">1,237.23</td><td class="n">1,207.64</td></tr>
|
||||
<tr><td></td><td></td><td>Fees</td><td class="n">0.00</td><td class="n">29.59</td></tr>
|
||||
<tr><td class="mono">NGVC</td><td>2026-08-10</td><td>Card Payments</td><td class="n">2,025.03</td><td class="n">2,074.63</td></tr>
|
||||
<tr><td></td><td></td><td>Fees</td><td class="n">49.60</td><td class="n">0.00</td></tr>
|
||||
<tr><td class="mono">NGGP</td><td>2026-08-13</td><td>every line</td><td class="n">0.00</td><td class="n">real figures</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>NGAN moves $29.59 out of Card Payments and into Fees; NGVC moves $49.60 the other way. Both stay balanced to the cent. NGGP is the first pattern — production held an all-zero summary for a day with $2,478.44 of card payments, $1,148.11 of gyros and pitas, and so on.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -351,6 +366,69 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Worked examples</h2>
|
||||
<div class="measure">
|
||||
<p>Each fix is easiest to check on a single order. In every case below the day's imbalance equals the fix amount exactly, which is what you would expect if the fix is booking something real that was previously booked nowhere.</p>
|
||||
</div>
|
||||
|
||||
<h3>R1 — a tip reversal with no tender to hang it on</h3>
|
||||
<div class="scroll">
|
||||
<table>
|
||||
<thead><tr><th>Field</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Order</td><td class="mono">square/order/NGLK-SM-OxSX9gpXJV394qqT8mnBGypUwKNZY</td></tr>
|
||||
<tr><td><code>:sales-order/tip</code></td><td class="n">−60.00</td></tr>
|
||||
<tr><td><code>:sales-order/total</code></td><td class="n">−60.00</td></tr>
|
||||
<tr><td><code>:sales-order/charges</code></td><td class="n bad">0 — no tender at all</td></tr>
|
||||
<tr class="total"><td>NGLK 2026-08-04 imbalance</td><td class="n">−60.00 → 0.00</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>A guest's tip was handed back. The reversal is recorded on the order, but <code>get-tip</code> reaches tips by joining through <code>:sales-order/charges</code>, and a return-only order has no charge to join through — so the −$60.00 was invisible and the day credited a tip that no longer existed. The other four largest R1 repairs are the same shape: NGFF 08-01 (−$40.00), NGSG 07-29 (−$40.00), NGNV 08-09 (−$35.00), NGJS 07-25 (−$35.00).</p>
|
||||
</div>
|
||||
|
||||
<h3>R2 — a service charge collected but credited nowhere</h3>
|
||||
<div class="scroll">
|
||||
<table>
|
||||
<thead><tr><th>Field</th><th>Value</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Order</td><td class="mono">square/order/NTPT-PT-KrMZzcon1cpQEJUyetErkBIpcdEZY</td></tr>
|
||||
<tr><td><code>:sales-order/service-charge</code></td><td class="n">427.10</td></tr>
|
||||
<tr><td><code>:sales-order/total</code></td><td class="n">3,198.78</td></tr>
|
||||
<tr class="total"><td>NTPT 2026-08-06 imbalance</td><td class="n">+427.10 → 0.00</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>The $427.10 is inside the card tender the customer paid, so it lands on the debit side — but nothing credited it, leaving the day short by exactly that amount. Next largest: NGFO 07-16 ($275.23), NGRN 08-06 ($236.96), N-30008 07-24 ($229.42), N-30003 07-18 ($225.00).</p>
|
||||
</div>
|
||||
|
||||
<h3>The 19 days the data work unbalanced — and all 19 rescued</h3>
|
||||
<div class="scroll">
|
||||
<table>
|
||||
<thead><tr><th>Client</th><th>Date</th><th class="n">After data fix</th><th class="n">After R1 + R2</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="mono">NGSF</td><td>2026-08-13</td><td class="n bad">+137.99</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">NGRY</td><td>2026-08-13</td><td class="n bad">+103.26</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">NGDS</td><td>2026-08-13</td><td class="n bad">+100.52</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">NGDV</td><td>2026-08-13</td><td class="n bad">+100.52</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">N-30007</td><td>2026-08-13</td><td class="n bad">+86.00</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">N-30003</td><td>2026-08-13</td><td class="n bad">+83.53</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">NGSC</td><td>2026-08-13</td><td class="n bad">+75.00</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">NGGH</td><td>2026-08-13</td><td class="n bad">+75.00</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">NGDG</td><td>2026-08-13</td><td class="n bad">+70.00</td><td class="n good">0.00</td></tr>
|
||||
<tr><td class="mono">NGDU</td><td>2026-08-13</td><td class="n bad">+70.00</td><td class="n good">0.00</td></tr>
|
||||
<tr class="total"><td colspan="2">All 19 days</td><td class="n bad">unbalanced</td><td class="n good">balanced</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>These are days production held an empty summary for — trivially balanced at zero. Filling them in with real figures exposes the same two calculation gaps every other day had, and both fixes then close them. Not one of the 19 survives as a regression. Their clustering on 08-13 is expected: it is the most recent day, so it is the one production had least chance to compute.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Days production never had</h2>
|
||||
<div class="measure">
|
||||
@@ -382,7 +460,7 @@
|
||||
<table>
|
||||
<thead><tr><th>Client</th><th>Date</th><th class="n">Variance</th><th>Explanation</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td class="mono">NGBK</td><td>2026-08-06</td><td class="n">+$299.42</td><td>refunds now held by both twins — a re-key consequence</td></tr>
|
||||
<tr><td class="mono">NGBK</td><td>2026-08-06</td><td class="n">+$299.42</td><td>tender exceeds order totals by exactly this much, in the source data</td></tr>
|
||||
<tr><td class="mono">NGDA</td><td>2026-08-01</td><td class="n">−$50.00</td><td>auto-gratuity booked as a service charge</td></tr>
|
||||
<tr><td class="mono">NGEB</td><td>2026-08-10</td><td class="n">−$25.00</td><td>ezCater fee semantics <span class="pill ok">predicted</span></td></tr>
|
||||
<tr><td class="mono">NGEB</td><td>2026-07-29</td><td class="n">−$20.00</td><td>ezCater fee semantics <span class="pill ok">predicted</span></td></tr>
|
||||
@@ -392,6 +470,7 @@
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>The ten-cent threshold separates register rounding from real variance with nothing sitting near the boundary — the largest sub-threshold day is 9.00¢ and the smallest material one is $9.62, two orders of magnitude apart.</p>
|
||||
<p><strong>NGBK 2026-08-06, traced.</strong> Summing that client-day's orders directly: tender totals <span class="num">$6,358.99</span> while the orders' own totals come to <span class="num">$6,059.57</span> — a difference of <span class="num">$299.42</span>, exactly the imbalance. Square recorded more payment than the orders account for. Three explanations were checked and ruled out: the day has no refund lines at all, no order carries two charges for one payment id, and the nine orders lacking line items carry zero tender. This is a source-data discrepancy, not a calculation defect, and not something either fix should paper over.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -412,6 +491,50 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Reproduce it</h2>
|
||||
<div class="measure">
|
||||
<p>Every figure here comes from the restored database and can be re-derived from it. The comparison tool is committed as <code>auto-ap.jobs.compare-sales-summaries</code>.</p>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<pre class="mono" style="margin:0;padding:20px;font-size:13px;line-height:1.7;white-space:pre;"><span class="dim">;; connect to the restored backup (read-only is fine)</span>
|
||||
(def conn (d/connect "datomic:dev://localhost:4337/integreat-prod-restore"))
|
||||
|
||||
<span class="dim">;; the whole comparison: production's own summaries vs now</span>
|
||||
(require '[auto-ap.jobs.compare-sales-summaries :as cs])
|
||||
(def r (cs/compare-against 209608347
|
||||
(clj-time.core/date-time 2026 7 15)
|
||||
(clj-time.core/date-time 2026 8 14)))
|
||||
|
||||
(:tally r) <span class="dim">;; identical / numbers-changed / added</span>
|
||||
(:balance-transitions r) <span class="dim">;; the four-way counts</span>
|
||||
(count (:previously-balanced-and-changed r)) <span class="dim">;; => 237</span>
|
||||
|
||||
<span class="dim">;; what actually moved on one of those days</span>
|
||||
(cs/line-diff (first (:previously-balanced-and-changed r)))
|
||||
|
||||
<span class="dim">;; the single order behind the R1 example</span>
|
||||
(d/pull (d/db conn) '[*]
|
||||
[:sales-order/external-id
|
||||
"square/order/NGLK-SM-OxSX9gpXJV394qqT8mnBGypUwKNZY"])
|
||||
|
||||
<span class="dim">;; the single order behind the R2 example</span>
|
||||
(d/pull (d/db conn) '[*]
|
||||
[:sales-order/external-id
|
||||
"square/order/NTPT-PT-KrMZzcon1cpQEJUyetErkBIpcdEZY"])</pre>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>The unit tests covering both fixes and the re-key run with:</p>
|
||||
</div>
|
||||
<div class="scroll">
|
||||
<pre class="mono" style="margin:0;padding:20px;font-size:13px;line-height:1.7;white-space:pre;">lein test auto-ap.jobs.sales-summaries-test auto-ap.square.core3-test
|
||||
<span class="dim">;; => 15 tests, 26 assertions, 0 failures</span></pre>
|
||||
</div>
|
||||
<div class="measure">
|
||||
<p>Note that the restore currently has Phase 0 applied and the twins deactivated. Re-running the comparison after re-activating them will move the contended clients' figures; everything outside those ten client pairs is unaffected.</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<span>Measured 2026-08-15 on <span class="mono">integreat-prod-restore</span>. Branch <span class="mono">worktree-sales-summary-balance</span>.</span>
|
||||
<span>Service charges map to account 49000 Service Income, chosen to make measurement possible and still pending accounting sign-off. The account affects only whether a day can reach accepted — never whether it balances — so no figure here depends on it.</span>
|
||||
|
||||
Reference in New Issue
Block a user