start of cash flow
This commit is contained in:
BIN
resources/ngzo.jpg
Normal file
BIN
resources/ngzo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
resources/ngzo.png
Normal file
BIN
resources/ngzo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 KiB |
@@ -11,6 +11,7 @@
|
|||||||
[auto-ap.datomic :refer [uri merge-query]]
|
[auto-ap.datomic :refer [uri merge-query]]
|
||||||
[datomic.api :as d]
|
[datomic.api :as d]
|
||||||
[clj-time.coerce :as coerce]
|
[clj-time.coerce :as coerce]
|
||||||
|
[clj-time.core :as t]
|
||||||
[auto-ap.datomic.clients :as d-clients]
|
[auto-ap.datomic.clients :as d-clients]
|
||||||
[auto-ap.datomic.checks :as d-checks]
|
[auto-ap.datomic.checks :as d-checks]
|
||||||
[auto-ap.datomic.users :as d-users]
|
[auto-ap.datomic.users :as d-users]
|
||||||
@@ -343,6 +344,10 @@
|
|||||||
:existing {:type '(list :import_ledger_entry_result)}
|
:existing {:type '(list :import_ledger_entry_result)}
|
||||||
:errors {:type '(list :import_ledger_entry_result)}
|
:errors {:type '(list :import_ledger_entry_result)}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
:cash_flow_result {:fields {:beginning_balance {:type :money}
|
||||||
|
:invoices_due_soon {:type '(list :invoice)}
|
||||||
|
:outstanding_payments {:type :money}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -364,6 +369,9 @@
|
|||||||
:args {:client_id {:type :id}}
|
:args {:client_id {:type :id}}
|
||||||
:resolve :get-invoice-stats}
|
:resolve :get-invoice-stats}
|
||||||
|
|
||||||
|
:cash_flow {:type :cash_flow_result
|
||||||
|
:args {:client_id {:type :id}}
|
||||||
|
:resolve :get-cash-flow}
|
||||||
:potential_payment_matches {:type '(list :payment)
|
:potential_payment_matches {:type '(list :payment)
|
||||||
:args {:transaction_id {:type :id}}
|
:args {:transaction_id {:type :id}}
|
||||||
:resolve :get-potential-payments}
|
:resolve :get-potential-payments}
|
||||||
@@ -880,7 +888,47 @@
|
|||||||
0
|
0
|
||||||
(- total outstanding-balance))})))
|
(- total outstanding-balance))})))
|
||||||
|
|
||||||
|
(defn get-cash-flow [context {:keys [client_id]} value]
|
||||||
|
(let [total-cash (reduce
|
||||||
|
(fn [total [credit debit]]
|
||||||
|
(- (+ total credit)
|
||||||
|
debit))
|
||||||
|
0.0
|
||||||
|
(d/query {:query {:find '[?debit ?credit]
|
||||||
|
:in '[$ ?client]
|
||||||
|
:where ['[?j :journal-entry/client ?client]
|
||||||
|
'[?j :journal-entry/line-items ?je]
|
||||||
|
'[?je :journal-entry-line/account ?ba]
|
||||||
|
'[?ba :bank-account/type :bank-account-type/check]
|
||||||
|
'[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit]
|
||||||
|
'[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]}
|
||||||
|
:args [(d/db (d/connect uri)) client_id]}))
|
||||||
|
bills-due-soon (d/query {:query {:find '[?due ?outstanding]
|
||||||
|
:in '[$ ?client ?due-before]
|
||||||
|
:where ['[?i :invoice/client ?client]
|
||||||
|
'[?i :invoice/status :invoice-status/unpaid]
|
||||||
|
'[?i :invoice/due ?due]
|
||||||
|
'[(<= ?due ?due-before)]
|
||||||
|
'[?i :invoice/outstanding-balance ?outstanding]]}
|
||||||
|
:args [(d/db (d/connect uri)) client_id (coerce/to-date (t/plus (time/local-now) (t/days 7)))]})
|
||||||
|
outstanding-checks (reduce
|
||||||
|
+
|
||||||
|
0.0
|
||||||
|
(map first (d/query {:query {:find '[?amount]
|
||||||
|
:in '[$ ?client ?due-before]
|
||||||
|
:where ['[?p :payment/client ?client]
|
||||||
|
'[?p :payment/status :payment-status/pending]
|
||||||
|
'[?p :payment/amount ?amount]
|
||||||
|
'(or
|
||||||
|
[?p :payment/type :payment-type/debit]
|
||||||
|
[?p :payment/type :payment-type/check])]}
|
||||||
|
:args [(d/db (d/connect uri)) client_id (coerce/to-date (t/plus (auto-ap.time/local-now) (t/days 7)))]})))]
|
||||||
|
{:beginning_balance total-cash
|
||||||
|
:outstanding_payments outstanding-checks
|
||||||
|
:invoices_due_soon (mapv (fn [[due outstanding]]
|
||||||
|
{:due (coerce/to-date-time due)
|
||||||
|
:outstanding_balance outstanding})
|
||||||
|
bills-due-soon)}))
|
||||||
|
|
||||||
(def schema
|
(def schema
|
||||||
(-> integreat-schema
|
(-> integreat-schema
|
||||||
@@ -898,6 +946,7 @@
|
|||||||
:get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches
|
:get-transaction-rule-matches gq-transaction-rules/get-transaction-rule-matches
|
||||||
:get-expense-account-stats get-expense-account-stats
|
:get-expense-account-stats get-expense-account-stats
|
||||||
:get-invoice-stats get-invoice-stats
|
:get-invoice-stats get-invoice-stats
|
||||||
|
:get-cash-flow get-cash-flow
|
||||||
:get-yodlee-merchants ym/get-yodlee-merchants
|
:get-yodlee-merchants ym/get-yodlee-merchants
|
||||||
:get-client gq-clients/get-client
|
:get-client gq-clients/get-client
|
||||||
:get-user get-user
|
:get-user get-user
|
||||||
|
|||||||
@@ -227,7 +227,47 @@
|
|||||||
@(d/transact conn txes)))
|
@(d/transact conn txes)))
|
||||||
|
|
||||||
(defn cash-flow-simple []
|
(defn cash-flow-simple []
|
||||||
(->> (d/query {:query {:find '[?account-type-ident ?date ?debit ?credit]
|
(let [total-cash (reduce
|
||||||
|
(fn [total [credit debit]]
|
||||||
|
(- (+ total credit)
|
||||||
|
debit))
|
||||||
|
0.0
|
||||||
|
(d/query {:query {:find '[?debit ?credit]
|
||||||
|
:in '[$ ?client]
|
||||||
|
:where ['[?j :journal-entry/client ?c]
|
||||||
|
'[?c :client/code ?client]
|
||||||
|
'[?j :journal-entry/line-items ?je]
|
||||||
|
'[?je :journal-entry-line/account ?ba]
|
||||||
|
'[?ba :bank-account/type :bank-account-type/check]
|
||||||
|
'[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit]
|
||||||
|
'[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]}
|
||||||
|
:args [(d/db (d/connect uri)) "CBC"]}))
|
||||||
|
bills-due-soon (d/query {:query {:find '[?due ?outstanding]
|
||||||
|
:in '[$ ?client ?due-before]
|
||||||
|
:where ['[?i :invoice/client ?c]
|
||||||
|
'[?c :client/code ?client]
|
||||||
|
'[?i :invoice/status :invoice-status/unpaid]
|
||||||
|
'[?i :invoice/due ?due]
|
||||||
|
'[(<= ?due ?due-before)]
|
||||||
|
'[?i :invoice/outstanding-balance ?outstanding]]}
|
||||||
|
:args [(d/db (d/connect uri)) "CBC" (c/to-date (t/plus (auto-ap.time/local-now) (t/days 7)))]})
|
||||||
|
outstanding-checks (reduce
|
||||||
|
+
|
||||||
|
0.0
|
||||||
|
(map first (d/query {:query {:find '[?amount]
|
||||||
|
:in '[$ ?client ?due-before]
|
||||||
|
:where ['[?p :payment/client ?c]
|
||||||
|
'[?c :client/code ?client]
|
||||||
|
'[?p :payment/status :payment-status/pending]
|
||||||
|
'[?p :payment/amount ?amount]
|
||||||
|
'(or
|
||||||
|
[?p :payment/type :payment-type/debit]
|
||||||
|
[?p :payment/type :payment-type/check])]}
|
||||||
|
:args [(d/db (d/connect uri)) "CBC" (c/to-date (t/plus (auto-ap.time/local-now) (t/days 7)))]})))]
|
||||||
|
[total-cash bills-due-soon outstanding-checks])
|
||||||
|
|
||||||
|
|
||||||
|
#_(->> (d/query {:query {:find '[?account-type-ident ?date ?debit ?credit]
|
||||||
:in '[$ ?client]
|
:in '[$ ?client]
|
||||||
:where ['[?j :journal-entry/line-items ?je]
|
:where ['[?j :journal-entry/line-items ?je]
|
||||||
'[?j :journal-entry/date ?date]
|
'[?j :journal-entry/date ?date]
|
||||||
@@ -253,3 +293,9 @@
|
|||||||
credit)))
|
credit)))
|
||||||
(update-in [year-month account-type :count] #(inc (or % 0)))))))
|
(update-in [year-month account-type :count] #(inc (or % 0)))))))
|
||||||
{})))
|
{})))
|
||||||
|
|
||||||
|
(defn attach-signature [client-code filename]
|
||||||
|
@(d/transact (d/connect uri)
|
||||||
|
[{:db/id [:client/code client-code]
|
||||||
|
:client/signature-file (str "https://s3.amazonaws.com/integreat-signature-images/" filename)}]))
|
||||||
|
|
||||||
|
|||||||
@@ -165,8 +165,9 @@
|
|||||||
visible-expense-accounts @(re-frame/subscribe [::visible-expense-accounts])
|
visible-expense-accounts @(re-frame/subscribe [::visible-expense-accounts])
|
||||||
selected-client @(re-frame/subscribe [::subs/client])
|
selected-client @(re-frame/subscribe [::subs/client])
|
||||||
percentage-size (if selected-client "%50%" "33%")
|
percentage-size (if selected-client "%50%" "33%")
|
||||||
is-sorted-by-vendor? (seq (filter #(= "vendor" (:sort-key %)) sort))
|
is-loading? (:loading @status)
|
||||||
_ (println is-sorted-by-vendor? sort)
|
is-sorted-by-vendor? (and (= "vendor" (:sort-key (first sort)))
|
||||||
|
(not is-loading?))
|
||||||
[invoice-groups] (if is-sorted-by-vendor?
|
[invoice-groups] (if is-sorted-by-vendor?
|
||||||
(reduce
|
(reduce
|
||||||
(fn [[acc last-vendor] invoice]
|
(fn [[acc last-vendor] invoice]
|
||||||
@@ -208,7 +209,9 @@
|
|||||||
:sort-name "Vendor"
|
:sort-name "Vendor"
|
||||||
:sort-key "vendor"
|
:sort-key "vendor"
|
||||||
:sort sort}
|
:sort sort}
|
||||||
"Vendor"]
|
(if is-sorted-by-vendor?
|
||||||
|
(:name (:vendor (first invoices)))
|
||||||
|
"Vendor")]
|
||||||
[sorted-column {:on-sort opc
|
[sorted-column {:on-sort opc
|
||||||
:style {:width percentage-size :cursor "pointer"}
|
:style {:width percentage-size :cursor "pointer"}
|
||||||
:sort-name "Invoice Number"
|
:sort-name "Invoice Number"
|
||||||
@@ -248,20 +251,13 @@
|
|||||||
:class "has-text-right"
|
:class "has-text-right"
|
||||||
:sort sort}
|
:sort sort}
|
||||||
"Outstanding"]
|
"Outstanding"]
|
||||||
|
[:th {:style {:width "20rem" :cursor "pointer"}}
|
||||||
[:th {
|
|
||||||
:style {:width "20rem" :cursor "pointer"}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
""]]]
|
""]]]
|
||||||
[:tbody
|
[:tbody
|
||||||
|
(if is-loading?
|
||||||
(if (:loading @status)
|
|
||||||
[:tr
|
[:tr
|
||||||
[:td {:col-span 5}
|
[:td {:col-span 5}
|
||||||
[:i.fa.fa-spin.fa-spinner]]]
|
[:i.fa.fa-spin.fa-spinner]]]
|
||||||
|
|
||||||
(for [{:keys [client payments expense-accounts invoice-number date due total outstanding-balance id vendor] :as i} invoices]
|
(for [{:keys [client payments expense-accounts invoice-number date due total outstanding-balance id vendor] :as i} invoices]
|
||||||
^{:key id}
|
^{:key id}
|
||||||
[row {:invoice i
|
[row {:invoice i
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
(:require [auto-ap.views.components.layouts :refer [side-bar-layout]]
|
(:require [auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
|
[cljs-time.core :as t]
|
||||||
|
[auto-ap.views.utils :refer [local-now date->str]]
|
||||||
[reagent.core :as r]))
|
[reagent.core :as r]))
|
||||||
|
|
||||||
|
|
||||||
@@ -44,9 +46,22 @@
|
|||||||
[legend]]
|
[legend]]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
(defn make-cash-flow-chart [{:keys [width height data] }]
|
||||||
|
(println data)
|
||||||
|
[bar-chart {:width width :height height :data data :fill "#FFFFFF" :stackOffset "sign"}
|
||||||
|
[tool-tip]
|
||||||
|
[bar {:dataKey "effective-balance" :fill (get colors 1) :stackId "a" :name "Effective Balance"}]
|
||||||
|
[bar {:dataKey "outstanding-payments" :fill (get colors 0) :stackId "a" :name "Outstanding Payments"}]
|
||||||
|
[bar {:dataKey "invoices" :fill (get colors 3) :stackId "a" :name "Invoices"}]
|
||||||
|
|
||||||
|
[x-axis {:dataKey "name"}]
|
||||||
|
[y-axis]
|
||||||
|
[legend]]
|
||||||
|
)
|
||||||
|
|
||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
::received
|
::received
|
||||||
(fn [db [_ {:keys [expense-account-stats invoice-stats]}]]
|
(fn [db [_ {:keys [expense-account-stats invoice-stats cash-flow]}]]
|
||||||
(let [expense-account-stats (->> expense-account-stats
|
(let [expense-account-stats (->> expense-account-stats
|
||||||
(map #(update % :total (fn [t] (js/parseFloat t))))
|
(map #(update % :total (fn [t] (js/parseFloat t))))
|
||||||
(sort-by :total)
|
(sort-by :total)
|
||||||
@@ -60,12 +75,14 @@
|
|||||||
(assoc ::top-expense-categories (conj top-5 other))
|
(assoc ::top-expense-categories (conj top-5 other))
|
||||||
|
|
||||||
(seq invoice-stats)
|
(seq invoice-stats)
|
||||||
(assoc ::invoice-stats invoice-stats)))))
|
(assoc ::invoice-stats invoice-stats)
|
||||||
|
|
||||||
|
(seq cash-flow)
|
||||||
|
(assoc ::cash-flow cash-flow)))))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::invoice-stats
|
::invoice-stats
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(println (::invoice-stats db))
|
|
||||||
(::invoice-stats db)))
|
(::invoice-stats db)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
@@ -73,6 +90,39 @@
|
|||||||
(fn [db]
|
(fn [db]
|
||||||
(::top-expense-categories db)))
|
(::top-expense-categories db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
::cash-flow
|
||||||
|
(fn [db]
|
||||||
|
(let [{:keys [outstanding-payments beginning-balance invoices-due-soon]} (::cash-flow db)
|
||||||
|
invoices-due-soon (reduce
|
||||||
|
(fn [result invoice]
|
||||||
|
(let [due (if (t/before? (:due invoice) (local-now))
|
||||||
|
(local-now)
|
||||||
|
(:due invoice))]
|
||||||
|
(println due)
|
||||||
|
|
||||||
|
(update result (date->str due)
|
||||||
|
(fn [r] (+ (or r 0.0) (:outstanding-balance invoice))))))
|
||||||
|
{}
|
||||||
|
invoices-due-soon)
|
||||||
|
start-date (local-now)
|
||||||
|
effective-balance (- beginning-balance outstanding-payments (invoices-due-soon (date->str start-date) 0.0))
|
||||||
|
]
|
||||||
|
|
||||||
|
(reverse
|
||||||
|
(reduce
|
||||||
|
(fn [[{:keys [effective-balance] } :as acc] day]
|
||||||
|
(let [invoices-due-today (invoices-due-soon (date->str (t/plus start-date (t/days day))) 0.0)]
|
||||||
|
(conj acc
|
||||||
|
{:name (date->str (t/plus start-date (t/days day)))
|
||||||
|
:effective-balance (- effective-balance invoices-due-today)
|
||||||
|
:invoices (- invoices-due-today)})))
|
||||||
|
(list {:name (date->str start-date)
|
||||||
|
:effective-balance effective-balance
|
||||||
|
:invoices (- (invoices-due-soon (date->str start-date) 0.0))
|
||||||
|
:outstanding-payments (- outstanding-payments)})
|
||||||
|
(range 1 7))))))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::mounted
|
::mounted
|
||||||
(fn [{:keys [db]} _]
|
(fn [{:keys [db]} _]
|
||||||
@@ -83,7 +133,10 @@
|
|||||||
[[:account [:id :name]] :total]]
|
[[:account [:id :name]] :total]]
|
||||||
[:invoice_stats
|
[:invoice_stats
|
||||||
{:client-id (:id @(re-frame/subscribe [::subs/client]))}
|
{:client-id (:id @(re-frame/subscribe [::subs/client]))}
|
||||||
[:name :paid :unpaid]]]}
|
[:name :paid :unpaid]]
|
||||||
|
[:cash-flow
|
||||||
|
{:client-id (:id @(re-frame/subscribe [::subs/client]))}
|
||||||
|
[:beginning-balance :outstanding-payments [:invoices-due-soon [:due :outstanding-balance]]]]]}
|
||||||
:on-success [::received]}}))
|
:on-success [::received]}}))
|
||||||
|
|
||||||
(defn home-content []
|
(defn home-content []
|
||||||
@@ -97,7 +150,11 @@
|
|||||||
(map (fn [x] {:name (:name (:account x)) :value (:total x)}) expense-categories))}))
|
(map (fn [x] {:name (:name (:account x)) :value (:total x)}) expense-categories))}))
|
||||||
[:h1.title.is-4 "Upcoming Bills"]
|
[:h1.title.is-4 "Upcoming Bills"]
|
||||||
(make-bar-chart {:width 800 :height 500 :data (clj->js
|
(make-bar-chart {:width 800 :height 500 :data (clj->js
|
||||||
@(re-frame/subscribe [::invoice-stats]))})]}]))
|
@(re-frame/subscribe [::invoice-stats]))})
|
||||||
|
|
||||||
|
[:h1.title.is-4 "Cash Flow"]
|
||||||
|
(make-cash-flow-chart {:width 800 :height 500
|
||||||
|
:data (clj->js @(re-frame/subscribe [::cash-flow]))})]}]))
|
||||||
|
|
||||||
|
|
||||||
(defn home-page []
|
(defn home-page []
|
||||||
|
|||||||
Reference in New Issue
Block a user