From ab27d3a4da13e3a47f6af2c05dc2f367defc955d Mon Sep 17 00:00:00 2001 From: Bryce Date: Fri, 14 Aug 2026 21:23:01 -0700 Subject: [PATCH] fix(ledger): match bank accounts sharing a code in register account search A client's bank account and the financial account it posts to share a numeric code, and :journal-entry-line/account points at either entity. The register's Account search matched the selected entity id exactly, so picking a financial account missed every line posted to the bank account on that code -- while the Account Code range filter, which already or-joins both namespaces, found them. Resolve the shared bank accounts up front and pass the id set into the existing clause rather than or-joining inside the query: an or-join turns a selective indexed lookup into per-line work and measured 2.5-3.4x slower on every account search, including ones that share no code. When nothing shares the code the emitted query is unchanged, so the common case stays at parity (0.97-1.04x, plus ~0.2ms to resolve). Co-Authored-By: Claude Opus 5 --- src/clj/auto_ap/ssr/ledger/common.clj | 35 ++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/clj/auto_ap/ssr/ledger/common.clj b/src/clj/auto_ap/ssr/ledger/common.clj index 8f0de248..62863789 100644 --- a/src/clj/auto_ap/ssr/ledger/common.clj +++ b/src/clj/auto_ap/ssr/ledger/common.clj @@ -209,6 +209,37 @@ ;; 3. CSVs ;; 4. better date range / advanced mode for dialog +(defn- accounts-sharing-code + "The searched account, plus any of `clients`' bank accounts on the same numeric + code. :journal-entry-line/account points at either entity, so a search for the + financial account has to match lines posted to the bank account too." + [db clients account-id] + (into [account-id] + (when-let [code (:account/numeric-code (dc/entity db account-id))] + (dc/q '[:find [?ba ...] + + :in $ [?client ...] ?code + + :where + [?client :client/bank-accounts ?ba] + [?ba :bank-account/numeric-code ?code]] + db clients code)))) + +(defn- account-filter-query + "Ledger clause for the Account search. Stays a scalar binding when nothing + shares the account's code, so the common case costs what it always did." + [db clients account-id] + (let [ids (accounts-sharing-code db clients account-id)] + (if (second ids) + {:query {:in ['[?a3 ...]] + + :where ['[?li :journal-entry-line/account ?a3]]} + :args [ids]} + {:query {:in ['?a3] + + :where ['[?li :journal-entry-line/account ?a3]]} + :args [account-id]}))) + (defn fetch-ids [db {:keys [query-params route-params] :as request}] (let [valid-clients (extract-client-ids (:clients request) (:client-id request) @@ -288,9 +319,7 @@ '[(<= ?c ?to-numeric-code)]]} :args [(map (juxt :from :to) (:numeric-code args))]}) (seq (:account args)) - (merge-query {:query {:in ['?a3] - :where ['[?li :journal-entry-line/account ?a3]]} - :args [(:db/id (:account args))]}) + (merge-query (account-filter-query db valid-clients (:db/id (:account args)))) (:amount-gte args) (merge-query {:query {:in ['?amount-gte]