Implementing running balance, fixing it so A accounts won't show up unless miscoded
This commit is contained in:
@@ -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)
|
||||
))
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
[:client
|
||||
[:name :id]]
|
||||
[:line-items
|
||||
[:id :debit :credit :location
|
||||
[:id :debit :credit :location :running-balance
|
||||
[:account [:id]]]]
|
||||
:date]]
|
||||
:total
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user