Commit Graph

113 Commits

Author SHA1 Message Date
10d0d01b82 fix(square): do not resolve a record that belongs to another client
Two clients on one Square location double a day's tender in the window
between deploying and finishing the migration. Reproduced end to end:

  1. charge X carries the legacy key square/charge/P and belongs to
     client B's order
  2. client A's payout import resolves X through existing-id's legacy
     fallback and renames it into A's scope
  3. client B's next order import matches neither scheme, so it mints a
     second charge
  4. :sales-order/charges is cardinality-many and orders transact as
     plain maps, so nothing retracts the first

B's order ends up holding two charges for one payment — $200 of tender
for a $100 payment — and running the migration on that state produces
square/charge/BBB-LB-AAA-LA-P, the same double-scoped shape that already
doubled tender on five clients once during this work.

existing-id's legacy branch now declines any record already owned by a
different client, reading the owner attribute and, for charges that
predate :charge/client, the client of the referencing order. Declining is
also correct on its merits: the write then lands on this client's own
copy, which is what the scoped keys exist to create. The payout path also
writes :charge/client/:charge/location alongside the key, so a charge's
scope and its owner can no longer disagree.

The guard is transitional and gets deleted with the legacy branch it
protects, at rollout step 9.

Rollout resequenced for the decision to leave duplicate client records
active: no deactivation, no "which record survives" call, and the risk
window closed by pausing the importer across deploy + migrate rather than
by removing one of the two writers. Both records converge to independent
stable histories once every key carries its owner.

Also from review:

- migrate-all! now collision-checks the charge pass like the other three
  attributes instead of discovering a clash mid-run over 17M rows
- split-and-rekey-charges! logs progress every 200 batches; an
  interrupted 19M-order run left no trail
- unscoped-report's docstring no longer promises a zero its :no-owner
  column cannot reach; plan is named as the authoritative signal
- the rollout's pre-flight asked for a :collisions key plan never
  returns, so it silently passed on every database
- the multi-parent gate sampled (take 400000 (all-order-ids db)), which
  streams :aevt — ascending entity id — and so read the OLDEST 2% of
  orders: 2019-12-31 to 2021-06-03, before any of the contention it
  looks for. Now every order of the last year via the client+date index,
  5,159,787 on the restored copy, reading 0
- the report claimed same-client pairs get copied once batches split
  them. They do not, at any batch size; verified at batch-size 1 and now
  pinned by a test

30 tests, 72 assertions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 08:09:53 -07:00
57a84dae11 fix(sales-summaries): stop days falling out of balance
Three faults were leaving restaurant days out of balance — one in the
data, two in the arithmetic — plus a fourth that turned out to be a
missing-data problem and is deliberately left visible. Measured over ninety days on a restored
copy of production (210 clients, 18,900 client-days): 1,258 days out of
balance and $69,560.10 becomes 279 days and $7,790.54 — of which 171 are
not arithmetic faults at all, but days whose sales were never imported.

979 days repaired, none knocked out of balance, and not one
already-balanced day altered — verified line by line (category, side,
amount to the cent, account), not just on each day's bottom line.

THE DATA FAULT

Ten Square locations were configured against two client records each.
Sales orders scoped their identifier by client; refunds, card payments,
payouts and cash-drawer shifts used the bare Square id. Those attributes
are :db.unique/identity, so both clients' imports resolved to a single
entity and the last writer won — 3,387 refunds, 4,069 payouts and 2,628
cash-drawer shifts changed hands over time, across 19 client pairs of
which only 10 are visible in today's configuration.

Worse, one payment could belong to two orders. :sales-order/charges is
:db/isComponent, so removing a voided order cascaded into payments the
other client still needed.

Fixes: client-scope the four key schemes; look the record up under both
schemes so the change deploys before the migration finishes; and a
migration that gives every order its own payment. Run over the whole
database that is 19,040,785 orders walked, 9,100,314 payments re-keyed
and 200,027 copied, ending with 17,047,142 payments scoped, none left to
rename, none unscopable, and no payment owned by more than one order.
Idempotent and resumable; about thirteen minutes.

THE ARITHMETIC FAULTS

- Refunded tips stayed on the books. get-tip summed tips by joining
  through :sales-order/charges, so a return-only order — no tender to
  join through — contributed nothing while its reversal sat unread on
  :sales-order/tip. Additive, not substitutive: where an order does have
  a tender the tender is the correct source.

- Service charges were collected but never earned. Nothing read
  :sales-order/service-charge. Now credited for Square orders only, both
  signs, behind summary-service-charges.

The flag is off by default, so deploying this changes nothing until a
client is opted in. docs/2026-08-15-sales-summary-rollout-plan.md has the
steps.

WHAT IS DELIBERATELY NOT FIXED

156 of the 279 remaining days carry refunds on a record that recorded no
sales at all that day, and 132 of those fall before that client's first
ever order. The refunds are not theirs: ownership history shows a $35.35
refund dated 26 February belonging to NGDG that day and taken over by
NGDU on 12 August, flipping between the two several times a day. Across
nine records, 659 refunds worth $15,225.24 sit on a record dated before
its own first order — unscoped keys let whichever import ran last take
ownership.

A rule closing those days was written and measured (156 days, $4,820.19,
nothing broken) and then removed. An unbalanced day is the only visible
signal that a restaurant's sales are not being imported; balancing it
would remove the alarm and leave the fire. A comment and a test hold that
decision in place. Step 9 of the rollout plan is the real fix, and it
needs a business decision.

SUPPORTING

- Install schema attributes before the tuples that compose them. A tuple
  in schema.edn is built from an attribute in cloud-migration-schema.edn,
  so every test fixture died in setup — very likely why sales summaries
  had no tests before this.
- Log each day's imbalance and its suspect lines.
- Bound the dirty-summary scan to one client: 1,321 ms to 5.6 ms.
- compare-sales-summaries lives in test/clj as auto-ap.tools.* — it is a
  verification harness, not part of the running application. Its
  docstring now warns that d/as-of cannot be used to compare summary
  amounts: :ledger-mapped/amount, ledger-side and account are
  :db/noHistory, so a recomputed summary reads back with its amounts
  absent and looks like a legitimate balanced day.

26 tests, 62 assertions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 20:17:58 -07:00
2b5fbaca00 Add template for new Reel Produce statement layout
The statement no longer prints "Reel Produce" as text (only
orders@reelproduce.com), switched to MM/DD/YYYY dates, and moved the
invoice number into an "INV #..." transaction description, so no
template matched and the file fell through to the glimpse2 fallback.

Adds a QuickBooks-statement-style template (same shape as Suncrest /
Ocean Queen) keyed on reelproduce.com + Statement, placed after the
existing Reel Produce statement template so the old layout still wins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 10:24:31 -07:00
395e445c99 Add test for Bonanza Produce invoice 03882095
Validates existing template correctly parses multi-page invoice with:
- Invoice number 03882095
- Customer identifier NICK THE GREEK
- Account number 600 VISTA WAY
- Total of $946.24
2026-02-08 08:40:40 -08:00
8a0395dc4a Add Bonanza Produce multi-invoice statement template
- Added multi-invoice template for Bonanza Produce with :multi and :multi-match? flags
- Template uses keywords for statement header to identify multi-invoice format
- Extracts invoice-number, date, customer-identifier (from RETURN line), and total
- Parses 4 invoices from statement PDF 13595522.pdf
- All tests pass (29 assertions, 0 failures, 0 errors)

- Added test: parse-bonanza-produce-statement-13595522
- Updated invoice-template-creator skill: emphasized test-first approach
2026-02-08 07:56:14 -08:00
98a3e0dda6 Extract customer name and address for Bonanza Produce
- customer-identifier field: customer name (e.g., 'NICK THE GREEK')
- account-number field: street address (e.g., '600 VISTA WAY')
- Combined they provide full customer identification with address
- Updated test to verify both fields and their concatenation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 07:55:26 -08:00
f4366fe98e Add location extraction for Bonanza Produce invoices
- Extract city/state/zip in location field
- Customer address now split across 3 fields:
  - customer-identifier: customer name
  - account-number: street address
  - location: city, state zip
- All components verified in test

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 07:55:12 -08:00
d95e24a1d7 Improve Bonanza Produce customer identifier extraction
- Extract customer name in customer-identifier field
- Extract street address in account-number field
- Use non-greedy regex with lookahead to capture clean values
- Update test to verify both name and address extraction

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 07:50:55 -08:00
37351e5f92 Add Bonanza Produce invoice template
- Add new PDF template for Bonanza Produce vendor
- Template uses phone number 530-544-4136 as unique identifier
- Extracts invoice number, date, customer identifier, and total
- Includes passing test for invoice 03881260

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 07:50:42 -08:00
dfccbf35cd Makes editing work correctly for non-admins 2024-04-03 20:08:46 -07:00
8063a8fcbd Builds client SSR approach, sunsets old cljs. 2024-02-07 21:22:56 -08:00
3d9d02f76a a bunch of cleanup to remove stuff 2023-10-14 23:37:04 -07:00
12c676629d Bulk coding works on multiple clients 2023-09-14 21:48:00 -07:00
c98766873e fixes tests. 2023-08-31 23:43:19 -07:00
Bryce
5b5b15cb27 Makes plaid user configurable 2023-07-24 20:55:40 -07:00
Bryce
445375d15e When plaid merchant is linked to vendor, automatically code to that vendor. 2023-07-24 09:10:28 -07:00
Bryce
de6c6b3031 working on solr stuff. 2023-06-06 21:08:19 -07:00
ca82b2c81f Merge branch 'port-datomic-cloud' into try-tailwind 2023-05-13 23:36:30 -07:00
3ce9cceee0 Fixes a few issues discovered 2023-05-13 23:36:14 -07:00
b9ae20125b Yodlee page in ssr. 2023-05-13 22:51:03 -07:00
8bd73b8a98 Removes docjure, freeing space 2023-05-10 10:39:58 -07:00
05f1f009fd Adds cloud approach to reading excel 2023-05-09 16:32:00 -07:00
c52444499f adds alternative ezcater solution. 2023-05-05 15:10:39 -07:00
f2025c2e65 made cloud tests work again. 2023-05-01 20:21:58 -07:00
611af7f9bb making tests work again. 2023-05-01 09:42:06 -07:00
b2ad7790cf Migrates back to datomic on-prem 2023-04-29 07:12:51 -07:00
8dae0fde10 (cloud) fixes issues encountered in testing. 2023-04-24 21:05:54 -07:00
ed37b19d6a Makes transaction pencil much faster. 2023-04-07 20:38:02 -07:00
ff2891204f merge fixes 2023-04-04 13:24:23 -07:00
8fcfe9e880 merged. 2023-04-04 13:11:25 -07:00
9745cc4f3b Makes cintas import work 2023-04-04 12:48:37 -07:00
6622aae589 no need for transact-with-ledger. 2023-04-03 09:04:33 -07:00
68e809d8fb (cloud) experimental approach to ensure the ledgers do not get out of sync 2023-04-02 07:45:12 -07:00
5df47c8837 (cloud) running balance update way more streamlined now 2023-03-30 16:44:25 -07:00
3a126e0b23 Merged vendor tests, got them working 2023-03-27 16:59:15 -07:00
24366058cf merged vendor test 2023-03-27 16:17:51 -07:00
17841934a5 makes all invoice operations work again 2023-03-27 14:11:20 -07:00
ca45795b11 More test coverage, also found a bug where bulk voided invoices were not unvoidable 2023-03-27 13:25:43 -07:00
e3fbc50e6a merged 2023-03-27 07:51:53 -07:00
8d6832e1fe added more tests. 2023-03-27 07:51:14 -07:00
bcd9698eb6 merged. 2023-03-26 20:43:46 -07:00
4fccb47082 Adds tests for transactions 2023-03-26 20:32:44 -07:00
e6a2626312 print checks tests. 2023-03-25 15:34:26 -07:00
7562213e97 merged. 2023-03-25 15:30:46 -07:00
5e9f480790 Added check printing test 2023-03-25 15:27:43 -07:00
8508fbe70c progress on making check tests work. 2023-03-24 16:11:18 -07:00
eb7394b42b account tests. 2023-03-24 15:34:43 -07:00
bfa04ef83f got accounts working 2023-03-24 15:34:14 -07:00
21f4dee7b2 account tests. 2023-03-24 15:25:26 -07:00
1057a263a9 made user test pass. 2023-03-24 14:45:15 -07:00