Files
integreat/docs/2026-08-16-sales-summary-business-impact.html
Bryce 62e96556e5 docs(reports): add a business-facing summary of the balancing work
The existing reconciliation report is written for someone who will run
the rollout and wants the evidence behind every figure. This one is for
someone deciding whether it ships and what it is worth, and assumes no
prior context.

Structured around four business requirements rather than four defects,
and states the operational cost in work rather than only in dollars:
1,191 restaurant-days needing manual investigation over ninety days
becomes 122, of which only 32 are worth more than ten cents.

Same measured figures throughout, three worked days, the duplicated-
restaurant choice and why replaying beat retiring a record, and the three
decisions still outstanding. About a third the length of the detailed
report.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 09:44:25 -07:00

424 lines
23 KiB
HTML

<title>Restaurant Books That Balance</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Newsreader:opsz,wght@6..72,400;6..72,600&family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:wght@400;500;600&display=swap">
<style>
:root {
--paper:#F7F8FA; --card:#FFFFFF; --sunk:#EEF1F6;
--ink:#16171E; --ink-soft:#4C5566; --ink-faint:#7B8497;
--rule:#DDE2EA; --accent:#2F4A7A; --accent-soft:#E7ECF5;
--brass:#8A6A2F; --brass-soft:#F6EFDF;
--good:#1E6B4E; --bad:#A33A2E;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--paper:#12151C; --card:#1A1E28; --sunk:#161A23;
--ink:#E9ECF3; --ink-soft:#A7B0C2; --ink-faint:#79839A;
--rule:#2A303D; --accent:#8FAEE0; --accent-soft:#1D2634;
--brass:#D3AE68; --brass-soft:#262014;
--good:#5EC08F; --bad:#E38878;
}
}
:root[data-theme="dark"] {
--paper:#12151C; --card:#1A1E28; --sunk:#161A23;
--ink:#E9ECF3; --ink-soft:#A7B0C2; --ink-faint:#79839A;
--rule:#2A303D; --accent:#8FAEE0; --accent-soft:#1D2634;
--brass:#D3AE68; --brass-soft:#262014;
--good:#5EC08F; --bad:#E38878;
}
*{box-sizing:border-box}
body{background:var(--paper);color:var(--ink);margin:0;padding:0 20px 100px;
font-family:"IBM Plex Sans",system-ui,-apple-system,sans-serif;font-size:16.5px;line-height:1.62}
.wrap{max-width:900px;margin:0 auto}
.measure{max-width:64ch}
.n{font-family:"IBM Plex Mono",ui-monospace,monospace;font-variant-numeric:tabular-nums}
.mono{font-family:"IBM Plex Mono",ui-monospace,monospace;font-size:.88em}
header{padding:76px 0 34px;border-bottom:1px solid var(--ink)}
.kicker{font-size:11.5px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);font-weight:600}
h1{font-family:Newsreader,Georgia,serif;font-weight:600;font-size:clamp(36px,6vw,58px);
line-height:1.04;letter-spacing:-.02em;margin:16px 0 0;text-wrap:balance}
.lede{font-size:19px;color:var(--ink-soft);max-width:60ch;margin:16px 0 0}
.facts{display:flex;flex-wrap:wrap;gap:8px 26px;margin-top:22px;font-size:13px;color:var(--ink-faint)}
.facts b{color:var(--ink-soft);font-weight:600}
section{padding-top:60px;display:flex;flex-direction:column;gap:20px}
h2{font-family:Newsreader,Georgia,serif;font-weight:600;font-size:30px;letter-spacing:-.015em;
margin:0;text-wrap:balance}
h3{font-size:12.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint);
font-weight:600;margin:0}
p{margin:0}
.measure p+p{margin-top:15px}
.strip{display:grid;grid-template-columns:1fr auto 1fr;background:var(--card);
border:1px solid var(--rule);border-radius:3px;overflow:hidden}
.strip>div{padding:28px 30px;display:flex;flex-direction:column;gap:5px}
.strip .mid{background:var(--accent-soft);border-left:1px solid var(--rule);
border-right:1px solid var(--rule);align-items:center;justify-content:center;color:var(--ink-faint);font-size:20px}
.lbl{font-size:11.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-faint);font-weight:600}
.big{font-family:"IBM Plex Mono",monospace;font-size:clamp(27px,4.2vw,38px);font-weight:600;
letter-spacing:-.02em;line-height:1.05;font-variant-numeric:tabular-nums}
.big.after{color:var(--good)}
.sub{font-size:13.5px;color:var(--ink-soft)}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));gap:12px}
.card{background:var(--card);border:1px solid var(--rule);border-radius:3px;padding:18px 20px;
display:flex;flex-direction:column;gap:3px}
.card .v{font-family:"IBM Plex Mono",monospace;font-size:28px;font-weight:600;line-height:1;
letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.card .k{font-size:12.5px;color:var(--ink-soft);line-height:1.4}
.card.ok .v{color:var(--good)}
.req{display:grid;grid-template-columns:auto 1fr;gap:14px 18px;align-items:baseline}
.req dt{font-family:"IBM Plex Mono",monospace;font-size:12.5px;font-weight:600;color:var(--accent);
letter-spacing:.04em;white-space:nowrap}
.req dd{margin:0;color:var(--ink-soft)}
.req dd b{color:var(--ink);font-weight:600}
.scroll{overflow-x:auto;border:1px solid var(--rule);border-radius:3px;background:var(--card)}
table{border-collapse:collapse;width:100%;font-size:14.5px}
th,td{padding:11px 16px;text-align:left;border-bottom:1px solid var(--rule);white-space:nowrap}
thead th{font-size:11px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-faint);
font-weight:600;background:var(--sunk)}
tbody tr:last-child td{border-bottom:none}
td.r,th.r{text-align:right;font-family:"IBM Plex Mono",monospace;font-variant-numeric:tabular-nums}
tr.sum td{font-weight:600;background:var(--accent-soft)}
tr.hl td{background:var(--brass-soft)}
.good{color:var(--good);font-weight:600}
.bad{color:var(--bad);font-weight:600}
.faint{color:var(--ink-faint)}
.note{background:var(--card);border:1px solid var(--rule);border-left:3px solid var(--accent);
border-radius:3px;padding:19px 22px;display:flex;flex-direction:column;gap:9px}
.note.warn{border-left-color:var(--brass)}
.note .h{font-weight:600}
.case{border-left:2px solid var(--rule);padding-left:22px;display:flex;flex-direction:column;gap:12px}
.case h4{margin:0;font-size:16.5px;font-weight:600}
footer{margin-top:80px;padding-top:22px;border-top:1px solid var(--rule);font-size:13px;
color:var(--ink-faint);display:flex;flex-direction:column;gap:7px;max-width:70ch}
a{color:var(--accent)}
a:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
</style>
<div class="wrap">
<header>
<div class="kicker">Sales summaries &middot; measured on a copy of live data</div>
<h1>Restaurant Books That Balance</h1>
<p class="lede">Every trading day, each restaurant's books should show the money taken and the
money earned agreeing to the penny. On roughly one day in sixteen they did not. This is what was
wrong, what it was costing, and what the fix is worth &mdash; measured by running the real job over
ninety days of real trading.</p>
<div class="facts">
<span><b>Period</b> 10 May &ndash; 7 Aug 2026</span>
<span><b>Restaurants</b> <span class="n">210</span></span>
<span><b>Restaurant-days</b> <span class="n">18,900</span></span>
<span><b>Nothing live was changed</b></span>
</div>
</header>
<section>
<div class="strip">
<div>
<span class="lbl">Books as they stand</span>
<span class="big n">$70,276.50</span>
<span class="sub"><span class="n">1,191</span> days that don't balance</span>
</div>
<div class="mid" aria-hidden="true">&rarr;</div>
<div>
<span class="lbl">Same period, after the fix</span>
<span class="big after n">$2,379.45</span>
<span class="sub"><span class="n">122</span> days that don't balance</span>
</div>
</div>
<div class="cards">
<div class="card ok"><span class="v">1,069</span><span class="k">days brought into balance</span></div>
<div class="card ok"><span class="v">0</span><span class="k">days broken by the change</span></div>
<div class="card ok"><span class="v">0</span><span class="k">correct days altered</span></div>
<div class="card"><span class="v">96.6%</span><span class="k">of the discrepancy removed</span></div>
</div>
<div class="measure">
<p>Put plainly: <b>on nine days out of ten that were wrong, the books now close.</b> Of the 122
days still open, only 32 are worth more than ten cents &mdash; the rest are till rounding.</p>
</div>
</section>
<section>
<h2>What an unbalanced day costs</h2>
<div class="measure">
<p>A day that does not balance cannot be signed off. Someone has to open it, compare it against
the till, and work out which figure is missing &mdash; and until they do, that restaurant's
period is not closed.</p>
<p>Across these ninety days that was <b>1,191 restaurant-days needing investigation &mdash; about
thirteen every single day.</b> After the fix it is 122 across the whole period, and only 32 of
those are worth more than ten cents: roughly <b>one day every third day</b> that genuinely needs
a human.</p>
<p>The second cost is subtler and worse. Where the two records of a duplicated restaurant traded
figures between them, the same day could reconcile differently depending on when you looked. That
is not a number anyone can defend in a review, and it is the reason R2 is a requirement rather
than a nicety.</p>
</div>
<div class="cards">
<div class="card"><span class="v">~13</span><span class="k">restaurant-days a day needing investigation, before</span></div>
<div class="card ok"><span class="v">~1.4</span><span class="k">restaurant-days a day, after</span></div>
<div class="card ok"><span class="v">90%</span><span class="k">less manual reconciliation</span></div>
</div>
</section>
<section>
<h2>What the business needs from this</h2>
<div class="measure">
<p>Four requirements. Everything below is judged against them.</p>
</div>
<dl class="req">
<dt>R1</dt>
<dd><b>A day's books must balance.</b> Money taken must equal money earned, to the penny. A day
that doesn't balance can't be signed off, and someone has to work out why by hand.</dd>
<dt>R2</dt>
<dd><b>Each restaurant's records must stay its own.</b> A payment or refund must belong to one
restaurant and stop moving. Figures that change owner between reports can't be reconciled.</dd>
<dt>R3</dt>
<dd><b>Cancelling an order must not delete another restaurant's money.</b> Removing a voided
order should affect that order only.</dd>
<dt>R4</dt>
<dd><b>When the numbers can't be trusted, that must show.</b> A silently plausible figure is
worse than a visibly wrong one.</dd>
</dl>
</section>
<section>
<h2>What was going wrong</h2>
<div class="case">
<h4>Two records for one restaurant, fighting over the same money <span class="faint">&mdash; R2, R3</span></h4>
<p class="measure">Ten restaurants were set up twice in the system, as two separate customer
records, and both were importing from the same till. Because the two records competed for the
same payments and refunds, a refund could belong to one record in the morning and the other by
the afternoon. Over time this had moved <b>3,387 refunds, 4,069 payouts and 2,628 cash-drawer
shifts</b> between records. Nineteen pairs have been affected historically; ten still share a
till today.</p>
<p class="measure">It also meant a single card payment could be attached to both records' copies
of an order. That is the R3 hazard: the nightly job removes orders the till reports as voided,
and removing an order removes its payments &mdash; so cancelling one restaurant's order could
quietly delete the other's payment. In a sample of 20,000 orders, <b>11,469 payments had two
owners.</b></p>
</div>
<div class="case">
<h4>Refunded tips still counted as income <span class="faint">&mdash; R1</span></h4>
<p class="measure">When a guest was refunded, the tip came back too. The summary kept crediting
the original tip, so the day showed income the restaurant no longer had.</p>
</div>
<div class="case">
<h4>Service charges collected but never recorded as earned <span class="faint">&mdash; R1</span></h4>
<p class="measure">A catering fee or auto-gratuity is charged inside the customer's card payment,
so it arrived as money taken &mdash; but nothing recorded it as money earned. Every order
carrying one left the day short by exactly that amount. This was the single largest cause.</p>
</div>
<div class="case">
<h4>Refunds arriving for restaurants whose sales were never imported <span class="faint">&mdash; R4</span></h4>
<p class="measure">A handful of records receive refunds while none of their sales reach the
system. Those days cannot balance, because half the transaction is not there. This one is
<b>deliberately left visible</b> rather than fixed &mdash; see below.</p>
</div>
</section>
<section>
<h2>What the fix is worth</h2>
<div class="measure">
<p>The real nightly job was run over the same ninety days twice &mdash; once as it behaves today,
once with the fixes on &mdash; against the same copy of live data. These are measured outcomes,
not estimates.</p>
</div>
<div class="scroll">
<table>
<thead><tr><th>Stage</th><th class="r">Days not balancing</th><th class="r">Clean</th><th class="r">Discrepancy</th></tr></thead>
<tbody>
<tr><td>Books as they stand</td><td class="r">1,191</td><td class="r">93.70%</td><td class="r">$70,276.50</td></tr>
<tr><td>Counting refunded tips correctly</td><td class="r">890</td><td class="r">95.29%</td><td class="r">$67,032.09</td></tr>
<tr class="sum"><td>Recording service charges as earned</td><td class="r good">122</td><td class="r good">99.35%</td><td class="r good">$2,379.45</td></tr>
</tbody>
</table>
</div>
<div class="scroll">
<table>
<thead><tr><th>Change</th><th class="r">Days fixed</th><th class="r">Days broken</th><th class="r">Correct days altered</th><th class="r">Money involved</th></tr></thead>
<tbody>
<tr><td>Refunded tips</td><td class="r good">301</td><td class="r good">0</td><td class="r good">0</td><td class="r">$4,027.21</td></tr>
<tr><td>Service charges</td><td class="r good">768</td><td class="r good">0</td><td class="r good">0</td><td class="r">$64,752.64</td></tr>
<tr class="sum"><td>Both together</td><td class="r good">1,069</td><td class="r good">0</td><td class="r good">0</td><td class="r">$67,897.05</td></tr>
</tbody>
</table>
</div>
<div class="note">
<span class="h">Nothing that was already right was touched</span>
<p>Of 18,900 restaurant-days, <b>17,827 came out identical</b> &mdash; not just still balancing,
but line for line the same figures. Every day that changed was already wrong. This was checked at
the level of individual lines (category, amount to the cent, and which account it posts to), not
just each day's bottom line, because a day can keep balancing while the figures inside it move.</p>
</div>
</section>
<section>
<h2>What it looks like on a real day</h2>
<div class="measure">
<p>Three days from the period. In each case the correction equals the discrepancy exactly &mdash;
which is what you would expect if the fix is recording something real that was recorded nowhere.</p>
</div>
<div class="scroll">
<table>
<thead><tr><th>A catering fee &mdash; NGPA, 4 June</th><th class="r">Before</th><th class="r">After</th></tr></thead>
<tbody>
<tr class="hl"><td><b>Service charges</b></td><td class="r bad">not recorded</td><td class="r">1,344.86</td></tr>
<tr><td class="faint">Card payments</td><td class="r faint">16,914.26</td><td class="r faint">16,914.26</td></tr>
<tr class="sum"><td>Day out by</td><td class="r bad">1,344.86</td><td class="r good">0.00</td></tr>
</tbody>
</table>
</div>
<p class="measure">The guests paid the catering fee inside their card payments. Nothing in the
books said the restaurant had earned it, so the day was short by exactly the fee.</p>
<div class="scroll">
<table>
<thead><tr><th>A refunded tip &mdash; NGFL, 19 May</th><th class="r">Before</th><th class="r">After</th></tr></thead>
<tbody>
<tr class="hl"><td><b>Tips</b></td><td class="r">238.46</td><td class="r">70.42</td></tr>
<tr><td class="faint">Card refunds</td><td class="r faint">168.04</td><td class="r faint">168.04</td></tr>
<tr class="sum"><td>Day out by</td><td class="r bad">&minus;168.04</td><td class="r good">0.00</td></tr>
</tbody>
</table>
</div>
<p class="measure">Guests were refunded $168.04, tips included. The books kept crediting the full
original tip. The corrected figure matches the refund to the penny.</p>
<div class="scroll">
<table>
<thead><tr><th>One restaurant, two records &mdash; 11 May</th><th class="r">Record A</th><th class="r">Record B</th></tr></thead>
<tbody>
<tr><td>Orders that day</td><td class="r">221</td><td class="r">221</td></tr>
<tr class="hl"><td><b>Refunds that day</b></td><td class="r bad">none</td><td class="r">2 &mdash; $2,232.29</td></tr>
<tr class="sum"><td>Day out by</td><td class="r bad">2,232.29</td><td class="r good">0.00</td></tr>
</tbody>
</table>
</div>
<p class="measure">The same restaurant, twice in the system. Both records held the same 221 orders,
but the refunds existed only once. The record without them showed the sales being returned and no
refunds against them, and was out by exactly what its twin was holding. Across its whole history
that record held <b>158,535 orders and five refunds.</b></p>
</section>
<section>
<h2>Making the duplicated restaurants whole</h2>
<div class="measure">
<p>The duplicated restaurants needed more than the arithmetic fixes, and this is the part with a
real choice in it.</p>
<p>Orders were always recorded per customer record, so both copies of a restaurant built their
own order history. Refunds and payouts were not &mdash; only one record ever held each of them.
So one copy shows returns with no refunds against them, and the other looks fine.</p>
<p><b>Rather than invent duplicate records, the fix asks the till again.</b> Once each record
carries its own identity, replaying ninety days of history means each one imports its own copy
and the two sets of books converge on their own &mdash; no code inventing figures that someone
then has to trust.</p>
</div>
<div class="cards">
<div class="card"><span class="v">423</span><span class="k">days out of balance on these records, before</span></div>
<div class="card ok"><span class="v">3</span><span class="k">days out of balance, after</span></div>
<div class="card"><span class="v">$18,508</span><span class="k">discrepancy before</span></div>
<div class="card ok"><span class="v">$649</span><span class="k">discrepancy after</span></div>
</div>
<div class="measure">
<p>The alternative was to retire one record of each pair. That was measured too, and it is worse:
it leaves <b>279 days and $7,790.54</b> open, and it forces a decision about whose trading history
to abandon. Replaying instead leaves 122 days and $2,379.45, and no record is lost.</p>
</div>
<div class="note warn">
<span class="h">A capped import, found while doing this</span>
<p>The refund import asked the till for a restaurant's refunds and read only the first page of
the answer. The till sends a hundred at a time, so any location with more than a hundred refunds
silently returned a hundred &mdash; and the response looked complete. <b>This was quietly
truncating the nightly refund import for every busy location, not only the duplicated ones.</b>
Now fixed.</p>
</div>
</section>
<section>
<h2>What is left, and what it is</h2>
<div class="scroll">
<table>
<thead><tr><th>Remaining discrepancy</th><th class="r">Days</th><th class="r">Amount</th></tr></thead>
<tbody>
<tr><td>Real trading days &mdash; small, mostly rounding</td><td class="r">106</td><td class="r">$1,151.80</td></tr>
<tr class="sum"><td>Refunds for restaurants whose sales never arrive</td><td class="r">16</td><td class="r">$1,227.65</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>Of the 106 trading days, only <b>three</b> are on the duplicated restaurants, and all three are
understood: two are a day where the till recorded $6,358.99 of card payments against $6,059.57 of
orders &mdash; a genuine gap in the till's own figures, not a fault in the books &mdash; and one
is an auto-gratuity booked as a service charge. The remaining 103 days come to <b>$502.96 spread
across 190 restaurants</b>, mostly a few cents each.</p>
<p><b>That $502.96 has been identical every time this was measured</b>, across several rebuilds
with deliberately different preparation. It is the floor this work reaches.</p>
<p>The 16 remaining days belong to two restaurants &mdash; <span class="mono">NG4S</span> and
<span class="mono">NGPS</span> &mdash; that receive refunds while none of their sales are
imported. Their books cannot close until the sales arrive. <b>These are deliberately left visibly
out of balance (R4).</b> The arithmetic could be made to agree in one line, and that change was
written, measured and then removed: an unbalanced day is the only signal anyone gets that a
restaurant's sales are missing. Closing it would remove the alarm and leave the fire.</p>
</div>
</section>
<section>
<h2>What rollout costs, and what it risks</h2>
<div class="scroll">
<table>
<thead><tr><th>Step</th><th class="r">Cost</th><th>Risk</th></tr></thead>
<tbody>
<tr><td>Deploy the change</td><td class="r">minutes</td><td>None on its own &mdash; the two accounting changes are off per restaurant by default</td></tr>
<tr><td>Re-label existing records</td><td class="r">~38 min</td><td>Runs with imports paused; newest month first, so stopping early is safe</td></tr>
<tr><td>Replay 90 days for the 10 duplicated restaurants</td><td class="r">~5.9 hrs</td><td>Overnight job; reads from the till only</td></tr>
<tr><td>Recompute the books, then switch on per restaurant</td><td class="r">~25 min</td><td>Reversible &mdash; switching a restaurant back off restores today's behaviour exactly</td></tr>
</tbody>
</table>
</div>
<div class="measure">
<p>The whole sequence is one overnight maintenance window. No customer record is retired and no
trading history is discarded. The accounting changes go on a few restaurants at a time, and
switching one back off is the rollback.</p>
</div>
</section>
<section>
<h2>Three decisions needed</h2>
<dl class="req">
<dt>Accounting</dt>
<dd><b>Which revenue account service charges post to.</b> Currently <span class="mono">49000
Service Income</span>, chosen so the work could be measured. It affects how revenue is reported,
never whether a day balances. Needs sign-off before switching any restaurant on.</dd>
<dt>Operations</dt>
<dd><b>Why <span class="mono">NG4S</span> and <span class="mono">NGPS</span> receive refunds but
no sales.</b> 160 refunds worth $4,347.68 predate any order for these restaurants. Replaying
their history from the till is the cheap first thing to try &mdash; it is what resolved seven
similar cases.</dd>
<dt>Reporting</dt>
<dd><b>Ten restaurants remain in the system twice, by design.</b> Each copy now keeps complete,
correct books. If any report adds figures across customer records, one restaurant's takings would
be counted twice at that layer. Worth confirming before this ships.</dd>
</dl>
</section>
<footer>
<span>Measured against a restored copy of live data as at 14 Aug 2026, over 10 May &ndash; 7 Aug
2026: 210 restaurants, 18,900 restaurant-days. Nothing in the live system was read or written.</span>
<span>A day counts as out of balance when money taken minus money earned differs by half a penny or
more. Figures were captured directly after each run rather than reconstructed afterwards.</span>
</footer>
</div>