diff --git a/scratch-sessions/weird_cleanup.clj b/scratch-sessions/weird_cleanup.clj index 315220ca..f2ae5019 100644 --- a/scratch-sessions/weird_cleanup.clj +++ b/scratch-sessions/weird_cleanup.clj @@ -3990,3 +3990,32 @@ o [:db/retract 17592232578099 :transaction/location "A"] [:db/retract 17592232578099 :transaction/vendor 17592232789952] [:db/retractEntity 17592233413525]]) + + +(def z + (with-open [output-stream (java.io.ByteArrayOutputStream.) + writer (io/writer output-stream)] + (csv/write-csv writer (->> (d/query {:query {:find ['(pull ?e [:journal-entry/amount + :journal-entry/date + :journal-entry/external-id + {:journal-entry/client [:client/code] + :journal-entry/line-items [:journal-entry-line/debit + :journal-entry-line/credit + {:journal-entry-line/account [:account/name]}]}])] + :in ['$] + :where ['[?e :journal-entry/line-items ?jel] + '[?e :journal-entry/amount ?jamt] + '[(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit] + '[(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit] + '[(= ?jamt 0.0)]]} + :args [(d/db auto-ap.datomic/conn)]}) + (map first) + (map (fn [x] + [(:journal-entry/external-id x) + (:journal-entry/amount x) + (:journal-entry/date x) + (:client/code (:journal-entry/client x)) + (str/join ", " (map (comp :account/name :journal-entry-line/account) (:journal-entry/line-items x)))])))) + (.flush output-stream) + (.toString output-stream) + )) diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 4bb3641c..9708274b 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -183,7 +183,8 @@ :account {:type :account} :location {:type 'String} :debit {:type 'String} - :credit {:type 'String}}} + :credit {:type 'String} + :running_balance {:type :money}}} :journal_entry {:fields {:id {:type :id} :source {:type 'String} diff --git a/src/clj/auto_ap/graphql/ledger.clj b/src/clj/auto_ap/graphql/ledger.clj index 58b6c8d7..328415a9 100644 --- a/src/clj/auto_ap/graphql/ledger.clj +++ b/src/clj/auto_ap/graphql/ledger.clj @@ -1,5 +1,5 @@ (ns auto-ap.graphql.ledger - (:require [auto-ap.datomic :refer [audit-transact-batch remove-nils uri]] + (:require [auto-ap.datomic :refer [audit-transact-batch remove-nils uri conn]] [auto-ap.datomic.accounts :as a] [auto-ap.datomic.clients :as d-clients] [auto-ap.datomic.ledger :as l] @@ -14,11 +14,31 @@ [clojure.string :as str] [clojure.tools.logging :as log] [datomic.api :as d] - [unilog.context :as lc])) + [unilog.context :as lc] + [yang.scheduler :as scheduler] + [mount.core :as mount])) +(mount/defstate running-balance-cache + :start (atom {})) (defn get-ledger-page [context args value] (let [args (assoc args :id (:id context)) - [journal-entries journal-entries-count] (l/get-graphql (<-graphql args))] + [journal-entries journal-entries-count] (l/get-graphql (<-graphql args)) + journal-entries (mapv + (fn [je] + + (update je :journal-entry/line-items + (fn [jels] + (mapv + (fn [jel] + (log/info (get-in @running-balance-cache [(:db/id (:journal-entry/client je)) + (:db/id jel)])) + (assoc jel :running-balance (get-in @running-balance-cache [(:db/id (:journal-entry/client je)) + (:db/id jel)]))) + + jels) + )) + ) + journal-entries)] (result->page journal-entries journal-entries-count :journal_entries args))) ;; TODO a better way to do this might be to accumulate ALL credits and ALL debits, and then just do for credits: balance = credits - debits. and for debits balance = debits - credits @@ -299,3 +319,79 @@ :errors (map (fn [x] {:external_id (:external_id x) :error (:error x)}) errors)}))) +(defn build-running-balance + ([lookup-account all-ledger-entries] + (->> all-ledger-entries + (reduce + (fn [[rollup cache] [_ jel account location debit credit]] + (let [rollup (-> rollup + (update-in [[location account] :debit] (fnil + 0.0) debit) + (update-in [[location account] :credit] (fnil + 0.0) credit) + (update-in [[location account] :count] (fnil + 0) 1))] + [rollup + (assoc cache jel (assoc (get rollup [location account]) :account-id account))])) + [{} {}]) + (second) + (reduce-kv + (fn [acc jel {:keys [debit credit count account-id]}] + (let [account (lookup-account account-id) + account-type (:account_type account)] + (assoc acc jel + (if account-type (if (#{:account-type/asset + :account-type/dividend + :account-type/expense} account-type) + (- debit credit) + (- credit debit)) + 0.0)))) + + {})))) + +(defn running-balance-for [client-id] + (let [lookup-account (build-account-lookup client-id)] + (->> (d/query + {:query {:find ['?d '?jel '?account '?location '?debit '?credit] + :in ['$ '?client-id] + :where '[[?e :journal-entry/client ?client-id] + [?e :journal-entry/date ?d] + [?e :journal-entry/line-items ?jel] + (or-join [?e] + (and [?e :journal-entry/original-entity ?i] + (or-join [?e ?i] + (and + [?i :transaction/bank-account ?b] + (or [?b :bank-account/include-in-reports true] + (not [?b :bank-account/include-in-reports]))) + (not [?i :transaction/bank-account]))) + (not [?e :journal-entry/original-entity ])) + [(get-else $ ?jel :journal-entry-line/account :account/unknown) ?account] + [(get-else $ ?jel :journal-entry-line/debit 0.0) ?debit ] + [(get-else $ ?jel :journal-entry-line/credit 0.0) ?credit] + [(get-else $ ?jel :journal-entry-line/location "") ?location]] + } + :args [(d/db conn) client-id]}) + (sort-by first) + (build-running-balance lookup-account)))) + +(defn build-running-balance-cache [] + (reduce + (fn [acc client] + (log/info "Computing running balance cache for " (:client/code client)) + (assoc acc (:db/id client) (running-balance-for (:db/id client)))) + {} + (d-clients/get-all))) + + + +(defn refresh-running-balance-cache [] + (lc/with-context {:source "running-balance-cache"} + (try + (log/info "Refreshing running balance cache") + (reset! running-balance-cache (build-running-balance-cache)) + (catch Exception e + (log/error e))))) + + +(mount/defstate running-balance-cache-worker + :start (scheduler/every (* 5 60 1000) refresh-running-balance-cache) + :stop (scheduler/stop running-balance-cache-worker)) + diff --git a/src/cljs/auto_ap/views/pages/ledger.cljs b/src/cljs/auto_ap/views/pages/ledger.cljs index 7e8757c6..8580820b 100644 --- a/src/cljs/auto_ap/views/pages/ledger.cljs +++ b/src/cljs/auto_ap/views/pages/ledger.cljs @@ -54,7 +54,7 @@ [:client [:name :id]] [:line-items - [:id :debit :credit :location + [:id :debit :credit :location :running-balance [:account [:id]]]] :date]] :total diff --git a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs index 5fea57a9..b10a7f62 100644 --- a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs @@ -30,6 +30,17 @@ [[from to] [(t/minus from (t/years 1)) (t/minus to (t/years 1))]]) +(defn in-range? [code] + (reduce + (fn [acc [start end]] + (if (<= start code end) + (reduced true) + acc)) + false + (vals ranges))) + + + ;; SUBS (re-frame/reg-sub ::locations @@ -38,6 +49,7 @@ ::report :periods (mapcat :accounts) + (filter (comp in-range? :numeric-code)) (group-by :location) (filter (fn [[k as]] (not (dollars-0? (reduce + 0 (map :amount as)))))) @@ -242,7 +254,7 @@ [:client [:name :id]] [:line-items - [:id :debit :credit :location + [:id :debit :credit :location :running-balance [:account [:id :name]]]] :date]] :total diff --git a/src/cljs/auto_ap/views/pages/ledger/table.cljs b/src/cljs/auto_ap/views/pages/ledger/table.cljs index 9710543d..fd40ab91 100644 --- a/src/cljs/auto_ap/views/pages/ledger/table.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/table.cljs @@ -22,9 +22,10 @@ [grid/cell {} (date->str date) ] [grid/cell {} ] [grid/cell {:class "has-text-right"} (nf amount )] - [grid/cell {:class "has-text-right"} (nf amount )]] + [grid/cell {:class "has-text-right"} (nf amount )] + #_[grid/cell {}]] [:<> - (for [{:keys [debit credit location account id]} line-items + (for [{:keys [debit credit location account id running-balance]} line-items :let [account (or (accounts-by-id (:id account)) (bank-accounts-by-id (:id account)))]] ^{:key id} @@ -37,7 +38,8 @@ (str location ": " (:name account)) [:i "unknown"])] [grid/cell {:class "has-text-right"} (when debit (nf debit ))] - [grid/cell {:class "has-text-right"} (when credit (nf credit ))]])]]) + [grid/cell {:class "has-text-right"} (when credit (nf credit ))] + #_[grid/cell {:class "has-text-right"} (when running-balance (nf running-balance ))]])]]) (defn table [{:keys [id data-page status vendors on-check-changed expense-event]}] (let [{:keys [data status]} @(re-frame/subscribe [::data-page/page data-page]) @@ -56,7 +58,8 @@ [grid/sortable-header-cell {:sort-key "date" :sort-name "Date" :style {:width "8em"}} "Date"] [grid/header-cell {} "Account"] [grid/sortable-header-cell {:sort-key "amount" :sort-name "Amount" :class "has-text-right" :style {:width "7em"}} "Debit"] - [grid/sortable-header-cell {:sort-key "amount" :sort-name "Amount" :class "has-text-right" :style {:width "7em"}} "Credit"]]] + [grid/sortable-header-cell {:sort-key "amount" :sort-name "Amount" :class "has-text-right" :style {:width "7em"}} "Credit"] + #_[grid/header-cell {:class "has-text-right" :style {:width "10em"}} "Running Balance"]]] [grid/body (for [{:keys [client vendor status date amount id line-items] :as i} (:data data)] ^{:key id}