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/location "A"]
|
||||||
[:db/retract 17592232578099 :transaction/vendor 17592232789952]
|
[:db/retract 17592232578099 :transaction/vendor 17592232789952]
|
||||||
[:db/retractEntity 17592233413525]])
|
[: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}
|
:account {:type :account}
|
||||||
:location {:type 'String}
|
:location {:type 'String}
|
||||||
:debit {:type 'String}
|
:debit {:type 'String}
|
||||||
:credit {:type 'String}}}
|
:credit {:type 'String}
|
||||||
|
:running_balance {:type :money}}}
|
||||||
:journal_entry
|
:journal_entry
|
||||||
{:fields {:id {:type :id}
|
{:fields {:id {:type :id}
|
||||||
:source {:type 'String}
|
:source {:type 'String}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
(ns auto-ap.graphql.ledger
|
(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.accounts :as a]
|
||||||
[auto-ap.datomic.clients :as d-clients]
|
[auto-ap.datomic.clients :as d-clients]
|
||||||
[auto-ap.datomic.ledger :as l]
|
[auto-ap.datomic.ledger :as l]
|
||||||
@@ -14,11 +14,31 @@
|
|||||||
[clojure.string :as str]
|
[clojure.string :as str]
|
||||||
[clojure.tools.logging :as log]
|
[clojure.tools.logging :as log]
|
||||||
[datomic.api :as d]
|
[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]
|
(defn get-ledger-page [context args value]
|
||||||
(let [args (assoc args :id (:id context))
|
(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)))
|
(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
|
;; 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)
|
:errors (map (fn [x] {:external_id (:external_id x)
|
||||||
:error (:error x)}) errors)})))
|
: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
|
[:client
|
||||||
[:name :id]]
|
[:name :id]]
|
||||||
[:line-items
|
[:line-items
|
||||||
[:id :debit :credit :location
|
[:id :debit :credit :location :running-balance
|
||||||
[:account [:id]]]]
|
[:account [:id]]]]
|
||||||
:date]]
|
:date]]
|
||||||
:total
|
:total
|
||||||
|
|||||||
@@ -30,6 +30,17 @@
|
|||||||
[[from to]
|
[[from to]
|
||||||
[(t/minus from (t/years 1)) (t/minus to (t/years 1))]])
|
[(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
|
;; SUBS
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::locations
|
::locations
|
||||||
@@ -38,6 +49,7 @@
|
|||||||
::report
|
::report
|
||||||
:periods
|
:periods
|
||||||
(mapcat :accounts)
|
(mapcat :accounts)
|
||||||
|
(filter (comp in-range? :numeric-code))
|
||||||
(group-by :location)
|
(group-by :location)
|
||||||
(filter (fn [[k as]]
|
(filter (fn [[k as]]
|
||||||
(not (dollars-0? (reduce + 0 (map :amount as))))))
|
(not (dollars-0? (reduce + 0 (map :amount as))))))
|
||||||
@@ -242,7 +254,7 @@
|
|||||||
[:client
|
[:client
|
||||||
[:name :id]]
|
[:name :id]]
|
||||||
[:line-items
|
[:line-items
|
||||||
[:id :debit :credit :location
|
[:id :debit :credit :location :running-balance
|
||||||
[:account [:id :name]]]]
|
[:account [:id :name]]]]
|
||||||
:date]]
|
:date]]
|
||||||
:total
|
:total
|
||||||
|
|||||||
@@ -22,9 +22,10 @@
|
|||||||
[grid/cell {} (date->str date) ]
|
[grid/cell {} (date->str date) ]
|
||||||
[grid/cell {} ]
|
[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 {: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))
|
:let [account (or (accounts-by-id (:id account))
|
||||||
(bank-accounts-by-id (:id account)))]]
|
(bank-accounts-by-id (:id account)))]]
|
||||||
^{:key id}
|
^{:key id}
|
||||||
@@ -37,7 +38,8 @@
|
|||||||
(str location ": " (:name account))
|
(str location ": " (:name account))
|
||||||
[:i "unknown"])]
|
[:i "unknown"])]
|
||||||
[grid/cell {:class "has-text-right"} (when debit (nf debit ))]
|
[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]}]
|
(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])
|
(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/sortable-header-cell {:sort-key "date" :sort-name "Date" :style {:width "8em"}} "Date"]
|
||||||
[grid/header-cell {} "Account"]
|
[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"}} "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
|
[grid/body
|
||||||
(for [{:keys [client vendor status date amount id line-items] :as i} (:data data)]
|
(for [{:keys [client vendor status date amount id line-items] :as i} (:data data)]
|
||||||
^{:key id}
|
^{:key id}
|
||||||
|
|||||||
Reference in New Issue
Block a user