fix(ledger): honor include-in-reports in the register

A journal entry with a line posted to a bank account flagged
:bank-account/include-in-reports false shows on the SSR register but not on
the GraphQL/CLJS ledger page, which has always dropped those entries in
auto-ap.datomic.ledger/graphql-results (and the CSV export does the same in
auto-ap.routes.exports). Same entry, same filters, visible on one page and
missing from the other. The admin client form defaults the flag to false, so
any bank account saved without ticking the box is affected -- 33 of 676 bank
accounts carry an explicit false today.

Resolve the affected entries up front off VAET and drop them before sorting
rather than excluding them inside the query: the equivalent not-join measured
~36ms against ~3ms for the bare scan on a wide date range, while the two
lookups cost ~2ms and are skipped entirely for clients with nothing flagged.

Unlike the GraphQL page, which filters after pagination and so quietly serves
short pages against an unfiltered total, this runs before pagination, so the
row count matches what the register renders.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 18:16:17 -07:00
parent ab27d3a4da
commit 6e7f66a78f
2 changed files with 116 additions and 0 deletions

View File

@@ -203,6 +203,39 @@
result)) result))
results)) results))
(defn- entries-off-reports
"Journal entries with a line posted to one of `clients`' bank accounts flagged
`:bank-account/include-in-reports` false, or nil when there are none.
The GraphQL ledger page has always dropped these (see
auto-ap.datomic.ledger/graphql-results), so the register has to agree --
otherwise the same entry is visible on one ledger page and missing from the
other. Resolved up front off VAET, which is ~150 entries db-wide and costs
~2ms; excluding them inside the query with a not-join instead measured ~10x
the cost of the whole scan on a wide date range."
[db clients]
(when-let [off-reports (seq (dc/q '[:find [?ba ...]
:in $ [?client ...]
:where
[?client :client/bank-accounts ?ba]
[?ba :bank-account/include-in-reports false]]
db clients))]
(set (dc/q '[:find [?e ...]
:in $ [?ba ...]
:where
[?li :journal-entry-line/account ?ba]
[?e :journal-entry/line-items ?li]]
db off-reports))))
(defn- apply-include-in-reports [db clients results]
(if-let [hidden (entries-off-reports db clients)]
(remove (comp hidden last) results)
results))
;; TODO ;; TODO
;; 1. Sorting in investigate dialog ;; 1. Sorting in investigate dialog
;; 2. actual date range filtering in investigate dialog ;; 2. actual date range filtering in investigate dialog
@@ -375,6 +408,7 @@
(merge-query {:query {:find ['?sort-default '?e]}})))] (merge-query {:query {:find ['?sort-default '?e]}})))]
(->> (observable-query query) (->> (observable-query query)
(apply-include-in-reports db valid-clients)
(apply-sort-4 (assoc query-params :default-asc? true)) (apply-sort-4 (assoc query-params :default-asc? true))
(apply-only-unbalanced query-params) (apply-only-unbalanced query-params)
(apply-pagination query-params)))) (apply-pagination query-params))))

View File

@@ -797,3 +797,85 @@
[:journal-entry/external-id (external-id "ext-good-1")])] [:journal-entry/external-id (external-id "ext-good-1")])]
(is (= 100.0 (:journal-entry/amount entry))) (is (= 100.0 (:journal-entry/amount entry)))
(is (= 2 (count (:journal-entry/line-items entry)))))))))))) (is (= 2 (count (:journal-entry/line-items entry))))))))))))
(deftest fetch-ids-include-in-reports-test
(testing "Should leave an entry out of the register when a line posts to a bank account off reports"
(let [{:strs [reports-client shown-entry]}
(setup-test-data [(test-bank-account :db/id "shown-bank"
:bank-account/name "Shown Bank"
:bank-account/numeric-code 11000
:bank-account/include-in-reports true)
(test-bank-account :db/id "hidden-bank"
:bank-account/name "Hidden Bank"
:bank-account/numeric-code 11001
:bank-account/include-in-reports false)
(test-client :db/id "reports-client"
:client/code "REPORTS-TEST"
:client/locations ["HQ"]
:client/bank-accounts ["shown-bank" "hidden-bank"])
{:db/id "reports-expense"
:account/name "Expense"
:account/numeric-code 60000
:account/account-set "default"}
{:db/id "shown-entry"
:journal-entry/client "reports-client"
:journal-entry/date #inst "2024-03-01"
:journal-entry/amount 100.0
:journal-entry/source "transaction"
:journal-entry/line-items
[{:journal-entry-line/account "shown-bank"
:journal-entry-line/credit 100.0}
{:journal-entry-line/account "reports-expense"
:journal-entry-line/debit 100.0}]}
{:db/id "hidden-entry"
:journal-entry/client "reports-client"
:journal-entry/date #inst "2024-03-02"
:journal-entry/amount 50.0
:journal-entry/source "transaction"
:journal-entry/line-items
[{:journal-entry-line/account "hidden-bank"
:journal-entry-line/credit 50.0}
{:journal-entry-line/account "reports-expense"
:journal-entry-line/debit 50.0}]}])
request {:client-id reports-client
:clients [reports-client]
:route-params {}
:query-params {}}
result (common/fetch-ids (dc/db conn) request)]
(is (= [shown-entry] (vec (:all-ids result))))
(is (= [shown-entry] (vec (:ids result))))
(testing "and counts it out of the total, so the page is not silently short"
(is (= 1 (:count result))))))
(testing "Should keep every entry when no bank account is off reports"
(let [{:strs [open-client open-entry]}
(setup-test-data [(test-bank-account :db/id "open-bank"
:bank-account/name "Open Bank"
:bank-account/numeric-code 11002
:bank-account/include-in-reports true)
(test-client :db/id "open-client"
:client/code "REPORTS-OPEN"
:client/locations ["HQ"]
:client/bank-accounts ["open-bank"])
{:db/id "open-expense"
:account/name "Expense"
:account/numeric-code 60001
:account/account-set "default"}
{:db/id "open-entry"
:journal-entry/client "open-client"
:journal-entry/date #inst "2024-03-01"
:journal-entry/amount 100.0
:journal-entry/source "transaction"
:journal-entry/line-items
[{:journal-entry-line/account "open-bank"
:journal-entry-line/credit 100.0}
{:journal-entry-line/account "open-expense"
:journal-entry-line/debit 100.0}]}])
result (common/fetch-ids (dc/db conn) {:client-id open-client
:clients [open-client]
:route-params {}
:query-params {}})]
(is (= [open-entry] (vec (:all-ids result))))
(is (= 1 (:count result))))))