diff --git a/docs/2026-08-15-sales-summary-reconciliation.html b/docs/2026-08-15-sales-summary-reconciliation.html index d2f27b2c..e2224ae5 100644 --- a/docs/2026-08-15-sales-summary-reconciliation.html +++ b/docs/2026-08-15-sales-summary-reconciliation.html @@ -105,27 +105,27 @@
Today's calculation, ninety days re-run - $69,560.10 - 1,258 days out of balance · 93.34% clean + $81,023.96 + 1,451 days out of balance · 92.32% clean
The same ninety days, fixes on - $7,790.54 - 279 days out of balance · 98.52% clean + $20,239.00 + 542 days out of balance · 97.13% clean
-
979client-days brought into balance
-
0days knocked out of balance
-
88.8%of the variance removed
+
915client-days brought into balance
+
6days knocked out of balance
+
75.0%of the variance removed
0payments shared between two clients

In one sentence: a day's sales summary should show the money taken and the money earned agreeing to the penny, and on roughly one trading day in eight it did not — because two clients were fighting over the same records, tips that had been refunded were still counted as income, and service charges customers paid were credited to nothing.

-

How the two figures above were produced. Both are the real nightly job, run over the same ninety days against the same restored database, writing real summaries each time — the first pass with the fixes switched off, the second with them on. Comparing a re-run against a re-run rather than against production's stored summaries is the stricter test: production's figures are in places months stale, and crediting the fixes with repairing ordinary staleness would flatter them. On that fairer footing the fixes are worth 979 days and $61,769.56, not the larger number a stale baseline would have shown.

+

How the two figures above were produced. Both are the real nightly job, run over the same ninety days against the same restored database, writing real summaries each time — the first pass with the fixes switched off, the second with them on. Comparing a re-run against a re-run rather than against production's stored summaries is the stricter test: production's figures are in places months stale, and crediting the fixes with repairing ordinary staleness would flatter them. On that fairer footing the fixes are worth a net 909 days and $60,784.96, not the larger number a stale baseline would have shown. Both passes ran with the duplicate client records left active, which is how this will actually be deployed.

Most of what is left is not a balancing fault at all, and the section on the fourth problem explains why deliberately leaving it unbalanced is the right call.

@@ -165,11 +165,11 @@

4. Refunds on records whose sales were never imported not fixed — deliberately

-

This is why the duplicated restaurants looked so much worse than everyone else. Of the days still failing after the first three fixes, 155 of the 158 on duplicate records had no sales orders at all — the summary consisted of nothing but refunds and their fees, with no sales for them to reduce.

-

The obvious reading is that the refund simply settled on a closed day. It is the wrong one. Checking each of those days against the date its client first recorded any order shows 132 of 156 fall before that client had a single order in the system — for seven of the nine records affected, every single one does. These are not quiet days. They are periods where the sales were never imported at all.

+

This is why the duplicated restaurants looked so much worse than everyone else. Of the days still failing after the first three fixes, 155 of the 423 on shared-location records had no sales orders at all — the summary consisted of nothing but refunds and their fees, with no sales for them to reduce.

+

The obvious reading is that the refund simply settled on a closed day. It is the wrong one. Checking each of those days against the date its client first recorded any order shows 140 of 171 fall before that client had a single order in the system — for seven of the nine records affected, every single one does. These are not quiet days. They are periods where the sales were never imported at all.

Where the refunds came from. Reading the database's own ownership history settles it. A $35.35 refund dated 26 February belonged to NGDG that same day, and was taken over by NGDU on 12 August. Others flip between the two records several times a day across 12–15 August. NGDU's first order is 2 August; it holds 94 refunds dated before it existed as a trading record. It never made them — it inherited seven months of the other record's refunds, because the refund key carried no client and whichever import ran last took ownership. That is fault 1, seen from the other end.

-

Across the nine records, 659 refunds worth $15,225.24 sit on a record dated before that record's first order. Nothing is lost and nothing is double-counted — the money is real and the surviving record has its own copy — but it is filed against a set of books that has no sales to put it against.

-

Why it is deliberately left out of balance. The day can be closed in one line: book a return equal to the day's refunds whenever the client recorded no sales. It is safe by construction — no trading day could be touched — and it closes 156 of the 279 remaining days and $4,820.19. It was built, measured, and then removed, because it is the wrong thing to do. An unbalanced day is the only visible signal that a restaurant's sales are not being imported. Making the arithmetic agree would remove the alarm and leave the fire.

+

Across the nine records, 660 refunds worth $15,237.02 sit on a record dated before that record's first order. Nothing is lost and nothing is double-counted — the money is real and the surviving record has its own copy — but it is filed against a set of books that has no sales to put it against.

+

Why it is deliberately left out of balance. The day can be closed in one line: book a return equal to the day's refunds whenever the client recorded no sales. It is safe by construction — no trading day could be touched — and on this data it closes 171 of the 542 remaining days and $5,795.18. It was built, measured, and then removed, because it is the wrong thing to do. An unbalanced day is the only visible signal that a restaurant's sales are not being imported. Making the arithmetic agree would remove the alarm and leave the fire.

technical get-returns sums :sales-order/returns over orders scanned for the date. With no orders the sum is nil and no Returns line is written, while get-refund-items still credits Card Refunds from the sales-refund records. The imbalance is the correct output for the input; the input is what is wrong. A test now pins this behaviour in place so it is not "fixed" by someone reading only the arithmetic.

@@ -226,9 +226,8 @@

The result is pinned as the record's id on the way in, so the write lands on the existing row regardless of which name it currently carries. The proof this worked is a count that did not move. Every one of the 265,965 refunds, payouts and cash-drawer shifts in the database was - re-named, and afterwards there were still exactly 144,688 payouts and 69,291 cash-drawer - shifts — the same figures as at the restore point. Refunds went from 50,986 to 51,986, - and all 1,000 of those came from the live Square import run afterwards, not from the renaming. + re-named, and afterwards there were still exactly 50,986 refunds, 144,688 payouts and + 69,291 cash-drawer shifts — the same three figures as at the restore point. Had the fallback lookup been missing, each of these would have doubled instead.

The fallback also has to refuse. Reading the old name is what stops duplicates; reading anyone's old name is what creates them. Two clients share a Square @@ -259,9 +258,9 @@ link it to the copy instead

-

Run over the whole database that was 9,100,314 renamed and 200,027 copied, +

Run over the whole database that was 16,236,839 renamed and 500,438 copied, and payments owned by two orders went from 11,469 in a 20,000-order sample to zero across - every order of the last year. The record count rose by about 200,027 — the number of copies it + every order of the last year. The record count rose by about 500,438 — the number of copies it reported making, which is the check that it created what it meant to and nothing else.

One subtlety worth recording, because it bit us: the Square id has to be recovered from the record's current owner rather than by trimming a fixed prefix. Client codes contain dashes — @@ -333,7 +332,7 @@ (when-not (zero? amount) amount))))

-

It works. Measured over the same ninety days it closed 156 days and $4,820.19, +

It works. Measured over the same ninety days it closed 171 days and $5,795.18, knocked nothing out of balance, and altered no already-balanced day — the guard makes it incapable of touching a day that traded.

It was removed anyway. Those days are not quiet days; they are days whose sales were never @@ -372,9 +371,9 @@ - - - + + +
StageDays out of balanceCleanTotal variance
Today's calculation, ninety days re-run1,25893.34%$69,560.10
+ refunded tips97194.86%$66,414.39
+ service charges27998.52%$7,790.54
Today's calculation, ninety days re-run1,45192.32%$81,023.96
+ refunded tips1,19993.66%$78,522.85
+ service charges54297.13%$20,239.00

@@ -387,16 +386,18 @@ - - - + + +
ChangeUnchangedInto balanceOut of balanceBalanced days alteredMoney moved
Refunded tips18,59028700$3,712.67
Service charges18,20869200$58,349.85
Both, end to end17,91697900$61,769.56
Refunded tips18,58825266$3,777.67
Service charges18,20466300$58,923.85
Both, end to end17,91691566$60,784.96
-

Neither fix touched a day that was already correct. Across all 18,900 client-days, no balanced day was knocked out of balance, and no balanced day had a single figure altered — 17,916 summaries came out byte-identical, and every one of the 984 that moved was already wrong. Service charges are by far the larger of the two, moving $58,349.85 against the tip fix's $3,712.67.

+

Six days were knocked out of balance, and they are worth understanding rather than hiding. Across all 18,900 client-days, 17,916 summaries came out byte-identical and 978 of the 984 that moved were already wrong. The six exceptions all have one shape: the Tip line drops by a round amount — $10, $15, $20, $30, $30, $50 — and the day breaks by exactly that. They are all on shared-location records (NGDG, NGEZ, NGDA, NGBK).

+

That is the tip fix working, not failing. Each is a tip that was handed back: the reversal sits on this record's order, but the refund that should offset it went to the record's twin. Before the fix the day balanced by accident, because the reversal was ignored. After it, the day correctly shows that half the transaction is filed elsewhere. The honest description is that the fix converts a hidden mis-attribution into a visible one — which is the same trade the fourth problem below makes deliberately.

+

Service charges are by far the larger of the two fixes, moving $58,923.85 against the tip fix's $3,777.67.

That claim is stronger than a balance check, and it is the one worth insisting on: a day can stay balanced while its individual lines move, which would still be a change to the books. Every line of every summary was compared — category, debit or credit side, amount to the cent, and account — not just the day's bottom line.

-

The two fixes account for the repair exactly. Adding up the untendered-tip and service-charge amounts for the 979 repaired days leaves a residue of 0.0000000013 against $61,769.56 of imbalance removed. Nothing else moved those days; there is no unexplained remainder hiding a third effect.

+

The two fixes account for every day they moved, exactly. Adding up the untendered-tip and service-charge amounts across all 984 changed days leaves a residue of 0.0000000002. Nothing else moved those days; there is no unexplained remainder hiding a third effect, and the six that broke are accounted for by the same arithmetic as the 915 that healed.

@@ -465,9 +466,9 @@ - - - + + + @@ -475,13 +476,13 @@
StepResult
Walk every order in the database19,040,785 orders
Give every order its own payment record9,100,314 re-keyed · 200,027 copied
Payments owned by two orders0 across every order of the last year — 5,159,787
Walk every order in the database, newest month first19,040,785 orders · 38 minutes
Give every order its own payment record16,236,839 re-keyed · 500,438 copied
Payments owned by two orders0 across every order of the last year — 5,158,470
Client-scope refunds, payouts and cash-drawer shiftscounts unchanged · 0 collisions
Live Square import afterwards0 orders with duplicated payment · 0 shared payments
Ownership changes after the change0 refunds · 0 payouts · 0 shifts
-

The count checks are the ones that matter. If re-keying had gone wrong it would have created a second copy of every record rather than updating the existing one, and the totals would have doubled. They did not move. The payment-copy step is the exception and is meant to add records — it added exactly 200,027, close to the number of copies it reported making. (Close, not exact: the counter increments while the transaction is being assembled, so two copies that resolve onto one entity are counted twice. It is a good check, not a proof.)

+

The count checks are the ones that matter. If re-keying had gone wrong it would have created a second copy of every record rather than updating the existing one, and the totals would have doubled. They did not move. The payment-copy step is the exception and is meant to add records — it added 500,438, matching the number of copies it reported making. (Close, not exact: the counter increments while the transaction is being assembled, so two copies that resolve onto one entity are counted twice. It is a good check, not a proof.)

The measurement above was taken with the duplicate client records deactivated, and that is not how this will be deployed. Leaving both records live is the intended configuration — the re-key is what separates them — but it means the deactivation that made this measurement clean will not be there. The gap that opens is narrow and specific: while any record still carries a legacy key, a second client can resolve onto it. That is why the deployment runs the migration with imports paused, and why existing-id now refuses to resolve a record belonging to another client.

The whole analysis was run again from nothing, and landed in the same place -

Everything above was rebuilt from a fresh restore of the production backup: restore, deactivate, re-key and duplicate across all nineteen million orders, re-import from Square, then two full ninety-day recomputes. At the point the two runs are comparable — before the refund-only fix below, which the earlier run did not have — the end state matched to the cent: 279 days out of balance and $7,790.54, with not one client-day differing by so much as half a penny. The recompute was then run a third time, after the database-wide re-key had finished, and produced the identical figure again. Numbers that survive being derived twice from separate restores are not artefacts of how the measurement was set up.

+

Everything above was rebuilt from a fresh restore of the production backup, three times over, each time from the backup point itself rather than from a database an earlier run had touched: restore, re-key and split across all nineteen million orders, then two full ninety-day recomputes. The runs used different preparation — one deactivated the duplicate records and ran a live Square import, this one leaves the configuration exactly as production has it — so their headline figures differ, and that difference is itself the most useful measurement in this report. What did not move is the part that should not: for the 190 clients that do not share a Square location, the residue is 119 days and $1,730.61 in both runs, with the same five restaurants accounting for it. The arithmetic fixes behave identically no matter what is done to the duplicates.

@@ -493,37 +494,48 @@

What is still out of balance

-

279 client-days out of 18,900, totalling $7,790.54. Most of it is not a balancing fault, and the split is the point:

+

542 client-days out of 18,900, totalling $20,239.00. Almost all of it sits + on one group of restaurants, and that is the finding:

- + - - - + +
What the remainder actually isDaysVariance
Where the remainder sitsDaysVarianceShare
Sales never imported — refunds on a record with no orders at all that day156$4,988.53
Processing fee on a day with no trading — the same shape, from the payout side15$974.99
Real trading days with genuine discrepancies108$1,995.36
The twenty records that share a Square location423$18,508.3991%
Every other client — 190 of the 210119$1,730.619%
-

Two thirds of what is left is a data-completeness problem, not an arithmetic one, and it is being left visible on purpose. Of those 156 days, 132 fall before their client had recorded a single order — they are periods with no sales in the system at all. The nine records involved hold 659 refunds worth $15,225.24 dated before their own first order.

-

The 15 fee-only days are the same story from the payout side: a processing fee landing on a day with no trading. Closing them needs the payout modelled, not another rule in the summary.

+

For every restaurant that is not one of the ten duplicated pairs, this reproduces + to the penny. 119 days and $1,730.61, with the same five clients accounting for + almost all of it — NG4S at $1,066.61, NGMV at $259.38, NGEB at $199.09, NGPS at $172.82, + N-30012 at $30.31, and 91 further days totalling $2.40 of till rounding. Those are the + numbers an earlier run produced on a differently prepared database, which is a stronger + check on the two arithmetic fixes than any single measurement: they behave identically + whatever is done to the duplicates.

+

The $18,508.39 on the twenty shared-location records is the cost of leaving both + records live. Each restaurant now keeps two sets of books, and the history behind + them was never split: refunds claimed by whichever record imported them first, orders that + went to the other, tips reversed on one side and refunded on the other. Re-keying makes that + attribution stable — it stops moving — but it does not make it right.

+

Of that remainder, 155 days and $4,567.53 are days those records had no sales imported at + all, which is the fourth problem above and is deliberately left visible. The other 268 days + are real trading days on which the two records disagree about who owns what.

-
- - - - - - - - - - -
Of the 108 real trading daysDaysVarianceWhat it is
NGBR1$299.42Square recorded $6,358.99 of tender against $6,059.57 of order totals on 2026-08-06 — the gap itself, not a summary fault
NGBK1$299.42
NGDL1$810.24an order on one record whose payment is on the twin — the mirror image of the refund case
NGMV5$259.38late May, undiagnosed
NGEB4$199.09ezCater fee treatment — an open question
everyone else96$127.81mostly till rounding — pennies a day
-
-
-

The clusters on NGMV and NGEB are unexplained and worth a look. They are visible only because the window is ninety days; a thirty-day view does not reach them.

+
+ What retiring the duplicate records would be worth +

An earlier measurement of the same window, taken with one record of each pair deactivated + and a live Square import run afterwards, left 279 days and $7,790.54 instead + of 542 and $20,239.00. Most of that difference is the twenty shared records: with the + duplicates retired their share fell from $18,508.39 to $6,059.93.

+

That is not an argument that the configuration is wrong — two live records is a deliberate + choice, and the re-key is what makes it safe. It is a number to weigh: leaving both active + costs roughly $12,000 of unexplained variance across 260 client-days per + ninety days, carried on ten restaurants, until the historical attribution behind them is + redistributed. The two figures are not perfectly isolated — that earlier run also included a + live import, which backfilled data this one does not have — so treat it as the right order of + magnitude rather than an exact price.

@@ -539,8 +551,8 @@ - - + + @@ -601,11 +613,12 @@
Re-running is safe, and that was proved at full scale

After the complete pass, asking the migration what it would change next returns - nothing — 17,047,142 payments examined, none to rename, none unscopable. A + nothing — 17,045,933 payments examined, none to rename, none unscopable. A record that already carries the right name is left untouched, so the migration can be stopped, resumed, or repeated without consequence.

Its speed is worth a note for whoever schedules it: the whole nineteen million orders were - walked in about thirteen minutes. An earlier attempt appeared to be + walked in about thirty-eight minutes, month by month from the current month + backwards so that stopping early leaves the recent end done. An earlier attempt appeared to be transactor-bound and was projected at two days, which is why a previous run narrowed it to the analysis window. That diagnosis was wrong. The bottleneck was garbage collection in the process driving the migration — freeing held memory took an unrelated recompute from 17 client-days a @@ -641,7 +654,7 @@ Two operational findings, unrelated to the summaries

The production backup had not written a restore point since 2025-03-10 — roughly seventeen months — even though data files were still uploading daily. A backup you cannot restore from is not a backup. A fresh one was taken on 2026-08-14 and is what this work used.

The database server was sized for a toy dataset: a 2 GB cache against 27 GB of data. Worth checking what production is set to.

-

Slowness here was misdiagnosed twice, in the same direction. Both a recompute crawling at 17 client-days a minute and a migration projected to take two days turned out to be garbage collection in the client process, not the database or the transactor. Freeing held memory took the recompute to 4,515 client-days a minute — a factor of 265 — and the migration finished in thirteen minutes. The lesson generalises: before concluding the transactor is the bottleneck, look at the heap of whatever is driving it.

+

Slowness here was misdiagnosed twice, in the same direction. Both a recompute crawling at 17 client-days a minute and a migration projected to take two days turned out to be garbage collection in the client process, not the database or the transactor. Freeing held memory took the recompute to 4,515 client-days a minute — a factor of 265 — and the migration finished in well under an hour. The lesson generalises: before concluding the transactor is the bottleneck, look at the heap of whatever is driving it.

@@ -691,7 +704,7 @@ diff --git a/docs/2026-08-15-sales-summary-rollout-plan.md b/docs/2026-08-15-sales-summary-rollout-plan.md index 62421cc2..0fd020f7 100644 --- a/docs/2026-08-15-sales-summary-rollout-plan.md +++ b/docs/2026-08-15-sales-summary-rollout-plan.md @@ -5,12 +5,17 @@ begins. The one behaviour change that alters a client's books is behind a per-cl that is **off by default**, so merging and deploying this branch changes nothing on its own. Measured on a restored copy of production (backup point `209608347`), 210 clients over -2026-05-10 → 2026-08-07: **1,258 client-days out of balance / $69,560.10 → 279 days / $7,790.54**, -with zero days knocked out of balance and zero already-balanced days altered. +2026-05-10 → 2026-08-07, with the duplicate client records left active exactly as they will be in +production: **1,451 client-days out of balance / $81,023.96 → 542 days / $20,239.00**. 915 days +came into balance and 6 broke — all six being tips reversed on one record whose refund sits on its +twin, which the fix makes visible rather than causes. -Of the 279 days left, **171 are not balancing faults** — they are days where a client's sales were -never imported while its refunds were. Step 10 is about those, and it is the most important -thing in this document. +**Where the remaining $20,239.00 sits is the thing to read.** $18,508.39 of it — 91% — is on the +twenty records that share a Square location. For the other 190 clients the residue is 119 days and +$1,730.61, which reproduces an earlier run on a differently prepared database to the penny. + +Of the total, 171 days are not balancing faults at all — they are days where a client's sales were +never imported while its refunds were. Step 10 is about those. --- @@ -20,7 +25,7 @@ thing in this document. |---|---| | Flag introduced | `summary-service-charges` — off by default | | Migration to run once | `auto-ap.jobs.rekey-square-external-ids/migrate-all!` | -| Expected migration runtime | ~13 minutes for 19M orders on a warm cache | +| Expected migration runtime | ~38 minutes for 19M orders on a warm cache | | Nothing here touches | invoices, payments, the ledger, or any client without the flag set | **Client configuration is left exactly as it is.** Ten Square locations are configured against two @@ -64,7 +69,8 @@ that is the one code path that can reach across client records. Running the migr paused means no client is resolving keys while the keys are being rewritten, so the window closes entirely rather than merely narrowing. -The migration itself takes about **13 minutes** for all 19M orders, so the pause is short. +The migration itself takes about **38 minutes** for all 19M orders, so the pause is short — and +if you need it shorter, see step 5: you can resume imports before it finishes. --- @@ -104,9 +110,9 @@ Run it immediately after the deploy, while imports are still paused. `migrate-all!` runs this same check itself, on every attribute including charges, and throws rather than transacting if it finds one. Running it by hand first just means finding out before -the 13-minute walk rather than partway through it. +the 38-minute walk rather than partway through it. -Runs in about thirteen minutes over 19M orders. It is **idempotent and resumable** — a record that +Runs in about thirty-eight minutes over 19M orders. It is **idempotent and resumable** — a record that already carries the right name is skipped, so it can be stopped and re-run without consequence. **It is also ordered so that stopping early is survivable.** Refunds, payouts and cash-drawer @@ -140,7 +146,7 @@ attributes: ```clojure (dissoc (rk/plan (d/db conn) :charge/external-id rk/charge-prefix) :new-keys) -;; => {:total 17047142 :to-migrate 0 :already-scoped 17047142 :unscopable 0} +;; => {:total 17045933 :to-migrate 0 :already-scoped 17045933 :unscopable 0} ``` Read `:to-migrate 0` **and** `:unscopable 0`. This is the authoritative signal, and it covers all @@ -163,7 +169,7 @@ order in the last year, with no sampling: ;; => 0 ``` -On the restored copy that is 5,159,787 orders — 27% of the table — via the +On the restored copy that is 5,158,470 orders — 27% of the table — via the `:sales-order/client+date` index. A year is chosen deliberately: `remove-voided-orders` only ever deletes orders Square reports as voided, which are recent, so that is where the destructive risk lives. Completeness across all of history is check (a)'s job, not this one. @@ -280,16 +286,16 @@ people doing the right thing. That is the reason not to let this drift. **The most important item in this document, and the only one that is not just execution.** -156 of the 279 remaining days are a record carrying refunds on a day it recorded no sales at all. -132 of those fall *before that client's first ever order*. Nine records are affected, holding -**659 refunds worth $15,225.24 dated before their own first order**: +171 of the 542 remaining days are a record carrying refunds on a day it recorded no sales at all. +140 of those fall *before that client's first ever order*. Nine records are affected, holding +**660 refunds worth $15,237.02 dated before their own first order**: | Client | First order | Refunds before it | Value | |---|---|---:|---:| | NGDL | 2026-07-29 | 96 | $2,751.50 | | NGWN | 2026-07-30 | 95 | $2,218.61 | | NGDU | 2026-08-02 | 94 | $2,188.44 | -| NG4S | 2026-05-29 | 78 | $2,168.30 | +| NG4S | 2026-05-29 | 79 | $2,180.08 | | NGPS | 2026-05-26 | 81 | $2,167.60 | | NGCT | 2026-08-01 | 98 | $1,789.49 | | NGVC | 2026-08-02 | 95 | $1,594.17 | @@ -329,7 +335,7 @@ all**, so a blanket delete would destroy the only copy of some records. ``` **Until this is resolved those days stay out of balance, on purpose.** A summary change to close -them was written and measured — it works, closes 156 days and $4,820.19, and breaks nothing — and it +them was written and measured — it works, closes 171 days and $5,795.18, and breaks nothing — and it was removed, because an unbalanced day is the only visible signal that a restaurant's sales are not being imported. A test asserts the day stays unbalanced so nobody closes it without reading this. @@ -337,11 +343,13 @@ being imported. A test asserts the day stays unbalanced so nobody closes it with ## What this will not fix -The 108 real trading days still out of balance, $1,995.36 — the NGBR/NGBK tender gap ($6,358.99 of +The 371 real trading days still out of balance, $14,443.82 — of which $13,940.86 is on the twenty +shared-location records, where the two books disagree about who owns what. For every other client +it is 103 days and $502.96. Named causes: the NGBR/NGBK tender gap ($6,358.99 of tender against $6,059.57 of order totals on one day), the ezCater fee question, and unexplained clusters on NGMV and NGEB. -Plus 15 days / $974.99 where a processing fee lands on a day with no trading — the same shape as +Plus 16 days / $1,227.65 where a processing fee lands on a day with no trading — the same shape as step 10 but from the payout side, so it needs the payout modelled rather than a rule in the summary. ---
Record typeTotalClient-scopedStill to renameCannot be scoped
Card payments17,047,14217,047,14200
Refunds51,98651,98600
Card payments17,045,93317,045,93300
Refunds50,98650,98600
Payouts144,688144,652036
Cash-drawer shifts69,29169,29100