Commit Graph

116 Commits

Author SHA1 Message Date
784c221f99 fix(square): split shared charges instead of re-keying them to one client
Re-keying alone could not undo an existing shared charge. It handed the single
entity to whichever client was looked at first and left the other order pointing
at a charge it does not own. Because :sales-order/charges is a component
attribute, that is not untidy but dangerous: retracting either order deletes a
charge the other still needs. It also produced double tender when the second
client re-imported and created its own.

split-and-rekey-charges! now gives every order its own charge. The first order
to claim a shared charge keeps it, re-keyed to that order's client and location;
every other order gets a copy carrying the same amounts, scoped to itself, with
its reference repointed. Afterwards no charge has more than one parent order and
the component relationship means what it says.

charges-with-multiple-parents is the §3.3 gate, which must read zero before any
historical cleanup or voided-order retraction is safe.

Four tests cover it: that the shared condition exists to begin with, that the
split produces two distinct entities with amounts copied and one parent each,
that an unshared charge is only re-keyed, and that re-running changes nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 13:17:59 -07:00
87f7fd564b fix(square): client-scope payout and cash-drawer-shift keys too
An audit of every unique-identity entity the importers create found two more
that carry no client scoping:

  :expected-deposit/external-id   "square/payout/" id
  :cash-drawer-shift/external-id  "square/cash-drawer-shift/" id

Both endpoints are location-filtered, so two clients configured on one Square
location import the same payout or shift and collide on a single entity, exactly
as refunds and charges did.

This is not hypothetical. Reading ownership out of the history of the restored
backup, the client has actually changed on:

  4,069 of 144,688 expected deposits   (2.8%)
  2,628 of  69,291 cash drawer shifts  (3.8%)
  3,387 of  51,990 refunds             (6.5%, before the earlier fix)

across 19 distinct client pairs — and only 10 of those pairs share a location in
today's configuration. Nine, including NGMJ/NGSC with 1,546 affected entities
and NGAK/NGMH with 952, are invisible to any point-in-time check because the
configuration has since changed. Deactivating what looks shared today is
therefore a snapshot fix; scoping the keys is what makes contention structurally
impossible.

Verified on the restore: 144,652 deposits and 69,291 shifts re-keyed with all
four entity counts unchanged, zero collisions, zero legacy keys remaining. 36
deposits carry neither client nor location and were left alone.

ezCater orders and sales orders were already scoped and need no change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 12:01:43 -07:00
4882c1c9cf perf(sales-summaries): stop the dirty-summary scan at the client boundary
dirty-sales-summaries index-pulled from [client-id true] and then filtered by
client. index-pull returns a lazy seq running to the END of the index, and a
lazy filter does not stop it, so for every client the job walked every summary
belonging to every client sorting after it — pulling their items along the way.
Quadratic in the number of summaries.

take-while stops at the client boundary instead, which is safe because
:sales-summary/client+dirty sorts by client first, so a client's dirty
summaries are contiguous from that start point.

Measured on a 14,458-summary database: 1,321ms -> 5.6ms per client, a 237x
improvement, with identical results. A full refresh had been degrading from
~180 client-days a minute to ~3 as summaries accumulated.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 08:44:52 -07:00
42a8207be9 feat(square): client-scope refund and charge external ids
Sales order keys already carry client and location; refund and charge keys do
not. That is why two clients configured on the same Square location share one
entity: the refund's owner flips every time either client imports, and a single
charge ends up referenced by both clients' orders. Scoping the keys the same way
makes contention structurally impossible — each client gets its own entity.

The hazard is the cutover. These ids are :db.unique/identity and the import
relies on upsert-by-identity, so changing the key format alone would match
nothing and Datomic would create a SECOND entity for every refund and charge,
orphaning the originals under their legacy keys. square.core3/existing-id
resolves the entity explicitly, scoped key first and legacy key second, and pins
the result as :db/id so the write lands on the existing entity whichever scheme
it currently carries.

All three construction sites are covered: order tenders, refunds, and the payout
path, which mints bare charge stubs from an external id alone.

The migration job re-keys whatever the importer has not yet touched. It recovers
scope from the referencing sales order or expected deposit for the ~12.8% of
charges that carry neither :charge/client nor :charge/location, detects
already-scoped entities by comparing against the key they should have rather
than pattern-matching ids that may themselves contain dashes, and is therefore
idempotent and re-runnable over a partially migrated database.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 07:49:31 -07:00
9e4cb851ff fix(sales-summaries): count reversed tips, credit Square service charges
Two calculation defects that leave a day out of balance, plus the first tests to
cover sales summaries.

Tips: get-tip joins through :sales-order/charges, so it only sees tips that
settled on a tender. A return-only order has no tender — it carries the reversal
on :sales-order/tip, which nothing read — so the day credited a tip that had
been handed back. Now additive: tendered tips plus the tips on orders that have
no tender at all. Deliberately not a swap, because where an order does have a
tender the tender is the correct source; real orders exist whose tender carries
a tip their :sales-order/tip does not (auto-gratuity booked as a service charge,
wallet tips missing from the net amounts), and reading the order would drop them.

Service charges: nothing reads :sales-order/service-charge. The charge is
collected inside the card tender but no line credits it, so every order carrying
one leaves the day short by exactly that amount. Both signs count — a returned
catering fee arrives as a negative service charge and is subtracted back out of
:sales-order/returns, so dropping negatives would lose the reversal.

The vendor gate is load-bearing: ezCater service charges are commission deducted
from the restaurant rather than collected from the diner, and crediting those
makes the day worse. It matches on :sales-order/vendor where that is set and
falls back to the external id prefix where it is not — whole eras of Square
orders carry no vendor attribute at all, and a gate on vendor alone would have
silently credited nothing.

Service charges stay behind the per-client "summary-service-charges" flag, and
the account they map to (49000 Service Income) needs accounting sign-off before
that flag is enabled anywhere.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 23:35:29 -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