You can now see cash flow details

This commit is contained in:
Bryce Covert
2020-08-23 07:42:12 -07:00
parent 3a2351aa41
commit edbc9f85c4
9 changed files with 144 additions and 35 deletions

View File

@@ -1020,13 +1020,15 @@
'[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit] '[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit]
'[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]} '[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]}
:args [(d/db (d/connect uri)) client_id]})) :args [(d/db (d/connect uri)) client_id]}))
bills-due-soon (d/query {:query {:find '[?due ?outstanding] bills-due-soon (d/query {:query {:find '[?due ?outstanding ?invoice-number ?vendor-id]
:in '[$ ?client ?due-before] :in '[$ ?client ?due-before]
:where ['[?i :invoice/client ?client] :where ['[?i :invoice/client ?client]
'[?i :invoice/status :invoice-status/unpaid] '[?i :invoice/status :invoice-status/unpaid]
'[?i :invoice/due ?due] '[?i :invoice/due ?due]
'[(<= ?due ?due-before)] '[(<= ?due ?due-before)]
'[?i :invoice/outstanding-balance ?outstanding]]} '[?i :invoice/outstanding-balance ?outstanding]
'[?i :invoice/invoice-number ?invoice-number]
'[?i :invoice/vendor ?vendor-id]]}
:args [(d/db (d/connect uri)) client_id (coerce/to-date (t/plus (time/local-now) (t/days 180)))]}) :args [(d/db (d/connect uri)) client_id (coerce/to-date (t/plus (time/local-now) (t/days 180)))]})
outstanding-checks (reduce outstanding-checks (reduce
+ +
@@ -1063,8 +1065,10 @@
{:beginning_balance total-cash {:beginning_balance total-cash
:outstanding_payments outstanding-checks :outstanding_payments outstanding-checks
:invoices_due_soon (mapv (fn [[due outstanding]] :invoices_due_soon (mapv (fn [[due outstanding invoice-number vendor-id]]
{:due (coerce/to-date-time due) {:due (coerce/to-date-time due)
:invoice_number invoice-number
:vendor {:id vendor-id}
:outstanding_balance outstanding}) :outstanding_balance outstanding})
bills-due-soon) bills-due-soon)
:upcoming_credits (into (mapv :upcoming_credits (into (mapv

View File

@@ -1,12 +1,23 @@
(ns auto-ap.views.components.buttons (ns auto-ap.views.components.buttons
(:require [auto-ap.views.utils :refer [dispatch-event]])) (:require [auto-ap.views.utils :refer [dispatch-event]]
[reagent.core :as r]))
(defn fa-icon [{:keys [event icon class]}] (defn fa-icon [{:keys [event icon class]}]
[:a.button {:class class [:a.button {:class class
:on-click (dispatch-event event)} [:span.icon [:i.fa {:class icon}]]]) :on-click (dispatch-event event)} (into
[:<>
[:span.icon [:i.fa {:class icon}]]
]
(r/children (r/current-component)))])
(defn sl-icon [{:keys [event icon class] :as params}] (defn sl-icon [{:keys [event icon class] :as params}]
[:a.button (-> params [:a.button (-> params
(dissoc :event :icon) (dissoc :event :icon)
(assoc :on-click (dispatch-event event))) (assoc :on-click (dispatch-event event)))
[:span.icon [:span {:class icon :style {:font-weight "400"}}]]]) [:span.icon [:span {:class icon :style {:font-weight "400"}}]]])
(defn new-button [{:keys [event name class ]}]
[:a.button.is-outlined {:class class
:on-click (dispatch-event event)}
[:span.icon [:i.fa.fa-plus]]
[:span name]])

View File

@@ -121,12 +121,13 @@
(mapv (fn [c] (mapv (fn [c]
[:div.level-item c]) children))]]))))])) [:div.level-item c]) children))]]))))]))
(defn table [{:keys [fullwidth class]}] (defn table [{:keys [fullwidth class style]}]
(into (into
[:table.table.compact.grid {:class (cond-> [] [:table.table.compact.grid {:class (cond-> []
fullwidth (conj "is-fullwidth") fullwidth (conj "is-fullwidth")
class (into class))}] class (into class))
:style style}]
(r/children (r/current-component)))) (r/children (r/current-component))))
(defn header [] (defn header []

View File

@@ -9,7 +9,7 @@
[auto-ap.views.components.grid :as grid] [auto-ap.views.components.grid :as grid]
[auto-ap.views.pages.invoices.form :as form] [auto-ap.views.pages.invoices.form :as form]
[auto-ap.views.pages.invoices.common :refer [invoice-read]] [auto-ap.views.pages.invoices.common :refer [invoice-read]]
[auto-ap.views.utils :refer [date->str dispatch-event nf]] [auto-ap.views.utils :refer [date->str dispatch-event nf days-until]]
[bidi.bidi :as bidi] [bidi.bidi :as bidi]
[cemerick.url :as url] [cemerick.url :as url]
[cljs-time.core :as t] [cljs-time.core :as t]
@@ -106,11 +106,7 @@
[grid/cell {} (date->str date) ] [grid/cell {} (date->str date) ]
[grid/cell {} [grid/cell {}
(when due (when due
(let [today (t/at-midnight (t/now)) (let [due-in (days-until due)]
due (t/at-midnight due)
due-in (if (t/after? today due)
(- (t/in-days (t/interval (t/minus due (t/days 1)) today)))
(t/in-days (t/interval today due )))]
(if (> due-in 0) (if (> due-in 0)
[:span.has-text-success due-in " days"] [:span.has-text-success due-in " days"]
[:span.has-text-danger due-in " days"]) [:span.has-text-danger due-in " days"])

View File

@@ -12,7 +12,8 @@
[auto-ap.entities.vendors :as vendor] [auto-ap.entities.vendors :as vendor]
[clojure.string :as str] [clojure.string :as str]
[reagent.core :as r] [reagent.core :as r]
[auto-ap.views.components.vendor-dialog :as vendor-dialog])) [auto-ap.views.components.vendor-dialog :as vendor-dialog]
[auto-ap.views.components.buttons :as buttons]))
(defn navbar-drop-down-contents [{:keys [id]} children ] (defn navbar-drop-down-contents [{:keys [id]} children ]
@@ -128,9 +129,9 @@
(when-not is-initial-loading (when-not is-initial-loading
[:div.navbar-end [:div.navbar-end
[:div.navbar-item [:div.navbar-item
[:a.button.is-primary.is-outlined [buttons/new-button {:event [::vendor-dialog/started {}]
{:on-click (dispatch-event [::vendor-dialog/started {}])} :name "Vendor"
[:span.icon [:i.fa.fa-plus] ] [:span "Vendor"]]] :class "is-primary"}]]
(when (> (count @clients) 1) (when (> (count @clients) 1)

View File

@@ -110,10 +110,12 @@
[:div [:div
[:h1.title "Accounts"] [:h1.title "Accounts"]
[:div.is-pulled-right [:div.is-pulled-right
[:a.button.is-success {:on-click (dispatch-event [::account-form/editing [buttons/new-button {:name "Account"
{:type :asset :class "is-primary"
:account-set "default"} :event [::account-form/editing
[::edit-completed]])} "New Account"]] {:type :asset
:account-set "default"}
[::edit-completed]]}]]
[accounts-table {:accounts @(re-frame/subscribe [::account-page])}]]) [accounts-table {:accounts @(re-frame/subscribe [::account-page])}]])
(defn admin-accounts-page [] (defn admin-accounts-page []

View File

@@ -1,15 +1,17 @@
(ns auto-ap.views.pages.home (ns auto-ap.views.pages.home
(:require [auto-ap.views.components.layouts :refer [side-bar-layout]] (:require [auto-ap.routes :as routes]
[re-frame.core :as re-frame]
[bidi.bidi :as bidi]
[auto-ap.routes :as routes]
[auto-ap.subs :as subs] [auto-ap.subs :as subs]
[auto-ap.views.components.grid :as grid]
[auto-ap.views.components.layouts :refer [side-bar-layout]]
[auto-ap.views.utils
:refer
[->$ date->str days-until dispatch-event local-now standard]]
[bidi.bidi :as bidi]
[cljs-time.coerce :as coerce]
[cljs-time.core :as t] [cljs-time.core :as t]
[auto-ap.views.utils :refer [local-now date->str standard dispatch-event]]
[reagent.core :as r]
[pushy.core :as pushy] [pushy.core :as pushy]
)) [re-frame.core :as re-frame]
[reagent.core :as r]))
(def pie-chart (r/adapt-react-class js/Recharts.PieChart)) (def pie-chart (r/adapt-react-class js/Recharts.PieChart))
(def pie (r/adapt-react-class js/Recharts.Pie)) (def pie (r/adapt-react-class js/Recharts.Pie))
@@ -129,6 +131,16 @@
(fn [db] (fn [db]
(::cash-flow db))) (::cash-flow db)))
(re-frame/reg-sub
::cash-flow-table-params
(fn [db]
(::cash-flow-table-params db)))
(re-frame/reg-event-db
::cash-flow-table-params-changed
(fn [db [_ new]]
(assoc db ::cash-flow-table-params new)))
(re-frame/reg-sub (re-frame/reg-sub
::cash-flow ::cash-flow
:<- [::chart-options] :<- [::chart-options]
@@ -150,6 +162,7 @@
(let [today (t/plus start-date (t/days day))] (let [today (t/plus start-date (t/days day))]
(conj acc (conj acc
{:name (date->str today) {:name (date->str today)
:date today
:effective-balance (+ (- effective-balance invoices-due-today ) :effective-balance (+ (- effective-balance invoices-due-today )
debits-due-today debits-due-today
credits-yesterday) credits-yesterday)
@@ -160,6 +173,7 @@
:query-params (cemerick.url/map->query {:due-range {:start (date->str today standard) :query-params (cemerick.url/map->query {:due-range {:start (date->str today standard)
:end (date->str today standard)}})})))) :end (date->str today standard)}})}))))
(list {:name (date->str start-date) (list {:name (date->str start-date)
:date start-date
:effective-balance effective-balance :effective-balance effective-balance
:invoices (- (invoices-due-soon (date->str start-date) 0.0)) :invoices (- (invoices-due-soon (date->str start-date) 0.0))
:credits (upcoming-credits (date->str start-date) 0.0) :credits (upcoming-credits (date->str start-date) 0.0)
@@ -188,6 +202,38 @@
:one-hundred-eighty-days :one-hundred-eighty-days
(range 1 181))))))) (range 1 181)))))))
(re-frame/reg-sub
::cash-flow-page
:<- [::cash-flow-table-params]
:<- [::cash-flow-data]
:<- [::subs/vendors-by-id]
(fn [[params cash-flow-data vendors-by-id]]
(let [ {:keys [outstanding-payments invoices-due-soon upcoming-credits upcoming-debits]} cash-flow-data
rows (concat (map (fn [c]
{:date (:date c)
:days-until (days-until (:date c))
:name (or (:identifier c) "Bi-weekly Average")
:amount (:amount c)
:type "Debit"}) upcoming-debits)
(map (fn [c]
{:date (:date c)
:days-until (days-until (:date c))
:amount (:amount c)
:name (or (:identifier c) "Bi-weekly Average")
:type "Credit"})
upcoming-credits)
(map (fn [c]
{:date (:due c)
:days-until (days-until (:due c))
:amount (:outstanding-balance c)
:name (str (:name (get vendors-by-id (:id (:vendor c)))) " (" (:invoice-number c) ")")
:type "Invoice"})
invoices-due-soon))]
(assoc (grid/virtual-paginate-controls (:start params ) rows)
:data (grid/virtual-paginate (:start params)
(sort-by (comp coerce/to-date :date) rows))))))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::mounted ::mounted
(fn [{:keys [db]} _] (fn [{:keys [db]} _]
@@ -203,9 +249,9 @@
{:client-id (:id @(re-frame/subscribe [::subs/client]))} {:client-id (:id @(re-frame/subscribe [::subs/client]))}
[:beginning-balance [:beginning-balance
:outstanding-payments :outstanding-payments
[:invoices-due-soon [:due :outstanding-balance]] [:invoices-due-soon [:due :outstanding-balance [:vendor [:id]] :invoice-number]]
[:upcoming-credits [:date :amount]] [:upcoming-credits [:date :amount :identifier]]
[:upcoming-debits [:date :amount]]]]]} [:upcoming-debits [:date :amount :identifier]]]]]}
:on-success [::received]}})) :on-success [::received]}}))
(defn cash-flow-range-button [{:keys [name value chart-options]}] (defn cash-flow-range-button [{:keys [name value chart-options]}]
@@ -214,6 +260,40 @@
:on-click (dispatch-event [::select-cash-flow-range value])} :on-click (dispatch-event [::select-cash-flow-range value])}
name]) name])
(defn cash-flow-grid []
(let [page @(re-frame/subscribe [::cash-flow-page])
opc (fn [p]
(re-frame/dispatch [::cash-flow-table-params-changed p]))
params @(re-frame/subscribe [::cash-flow-table-params])]
[grid/grid {:status {:state :complete}
:on-params-change opc
:params params
:column-count 4}
[grid/controls page]
[grid/table {:style {:width "800px"}}
[grid/header
[grid/row {}
[grid/header-cell {} "Date"]
[grid/header-cell {} "Type"]
[grid/header-cell {} "Name"]
[grid/header-cell {:class "has-text-right"} "Amount"]]]
[grid/body
(for [{:keys [date days-until type name amount] } (:data page)]
^{:key date}
[grid/row {}
[grid/cell {}
(if (> days-until 0)
[:span.has-text-success days-until " days"]
[:span.has-text-danger days-until " days"])
[:i.is-size-7 " (" (date->str date) ")"] ]
[grid/cell {} (if (> date 0)
"Upcoming "
"Due ")
type]
[grid/cell {} name]
[grid/cell {:class "has-text-right"} (->$ amount)]
])]]]))
(defn home-content [] (defn home-content []
(let [client-id (-> @(re-frame/subscribe [::subs/client]) :id) (let [client-id (-> @(re-frame/subscribe [::subs/client]) :id)
chart-options @(re-frame/subscribe [::chart-options])] chart-options @(re-frame/subscribe [::chart-options])]
@@ -256,7 +336,9 @@
(make-cash-flow-chart {:width 800 :height 500 (make-cash-flow-chart {:width 800 :height 500
:data (clj->js @(re-frame/subscribe [::cash-flow]))})]}])) :data (clj->js @(re-frame/subscribe [::cash-flow]))})
[cash-flow-grid]]}]))
(defn home-page [] (defn home-page []

View File

@@ -31,7 +31,8 @@
[goog.string :as gstring] [goog.string :as gstring]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[reagent.core :as r] [reagent.core :as r]
[vimsical.re-frame.fx.track :as track])) [vimsical.re-frame.fx.track :as track]
[auto-ap.views.components.buttons :as buttons]))
(re-frame/reg-event-fx (re-frame/reg-event-fx
::params-change ::params-change
@@ -135,7 +136,10 @@
[:div [:div
[:div.is-pulled-right [:div.is-pulled-right
[:div.buttons [:div.buttons
[:button.button.is-outlined.is-primary {:on-click (dispatch-event [::new-invoice-clicked])} "New Invoice"] [buttons/new-button {:event [::new-invoice-clicked]
:name "Invoice"
:class "is-primary"}]
(when current-client (when current-client
[drop-down {:header [:button.button.is-primary {:aria-haspopup true [drop-down {:header [:button.button.is-primary {:aria-haspopup true
:on-click (dispatch-event [::events/toggle-menu ::print-checks ]) :on-click (dispatch-event [::events/toggle-menu ::print-checks ])

View File

@@ -456,3 +456,11 @@
(defn action-cell-width [cnt] (defn action-cell-width [cnt]
(str (inc (* cnt 51)) "px")) (str (inc (* cnt 51)) "px"))
(defn days-until [d]
(let [today (t/at-midnight (t/now))
d (t/at-midnight d)
in (if (t/after? today d)
(- (t/in-days (t/interval (t/minus d (t/days 1)) today)))
(t/in-days (t/interval today d )))]
in))