Merge branch 'cash-flow-idea'
This commit is contained in:
@@ -62,6 +62,16 @@
|
||||
'[(<= ?date ?end-date)]]}
|
||||
:args [(c/to-date (:end (:date-range args)))]})
|
||||
|
||||
(:start (:due-range args)) (merge-query {:query {:in '[?start-due]
|
||||
:where ['[?e :invoice/due ?due]
|
||||
'[(>= ?due ?start-due)]]}
|
||||
:args [(c/to-date (:start (:due-range args)))]})
|
||||
|
||||
(:end (:due-range args)) (merge-query {:query {:in '[?end-due]
|
||||
:where ['[?e :invoice/due ?due]
|
||||
'[(<= ?due ?end-due)]]}
|
||||
:args [(c/to-date (:end (:due-range args)))]})
|
||||
|
||||
(:import-status args)
|
||||
(merge-query {:query {:in ['?import-status]
|
||||
:where ['[?e :invoice/import-status ?import-status]]}
|
||||
|
||||
@@ -394,6 +394,7 @@
|
||||
:invoice_page {:type '(list :invoice_page)
|
||||
:args {:import_status {:type 'String}
|
||||
:date_range {:type :date_range}
|
||||
:due_range {:type :date_range}
|
||||
:status {:type :invoice_status}
|
||||
:client_id {:type :id}
|
||||
:vendor_id {:type :id}
|
||||
@@ -871,11 +872,10 @@
|
||||
true (merge-query {:query {:where ['[?i :invoice/client ?c]
|
||||
'[?i :invoice/outstanding-balance ?outstanding-balance]
|
||||
'[?i :invoice/total ?total]
|
||||
'[?i :invoice/date ?date]
|
||||
'[?i :invoice/due ?date]
|
||||
'[(.toInstant ^java.util.Date ?date) ?d2]
|
||||
'[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2 ) ?d3]
|
||||
'[(+ 30 ?d3) ?d4]
|
||||
'[(auto-ap.graphql/categorize ?d4) ?name]]}})
|
||||
'[(auto-ap.graphql/categorize ?d3) ?name]]}})
|
||||
|
||||
true (d/query ))
|
||||
result (group-by first result)]
|
||||
|
||||
@@ -226,73 +226,7 @@
|
||||
rows)]
|
||||
@(d/transact conn txes)))
|
||||
|
||||
(defn cash-flow-simple []
|
||||
(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]
|
||||
:where ['[?j :journal-entry/line-items ?je]
|
||||
'[?j :journal-entry/date ?date]
|
||||
'[?je :journal-entry-line/account ?a]
|
||||
'[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit]
|
||||
'[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]
|
||||
'[?a :account/type ?account-type]
|
||||
'[?account-type :db/ident ?account-type-ident]]}
|
||||
:args [(d/db (d/connect uri)) "CBC"]})
|
||||
|
||||
(reduce
|
||||
(fn [result [account-type date debit credit]]
|
||||
(let [date (clj-time.coerce/from-date date)]
|
||||
(let [year-month (str (clj-time.core/year date) "-" (clj-time.core/month date))]
|
||||
(-> result
|
||||
(update-in [year-month account-type :debit]
|
||||
(fn [existing-debit]
|
||||
(+ (or existing-debit 0.0)
|
||||
debit)))
|
||||
(update-in [year-month account-type :credit]
|
||||
(fn [existing-credit]
|
||||
(+ (or existing-credit 0.0)
|
||||
credit)))
|
||||
(update-in [year-month account-type :count] #(inc (or % 0)))))))
|
||||
{})))
|
||||
|
||||
(defn attach-signature [client-code filename]
|
||||
@(d/transact (d/connect uri)
|
||||
|
||||
@@ -17,6 +17,22 @@
|
||||
(fn [uri]
|
||||
(pushy/set-token! p/history uri)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:set-uri-params
|
||||
(fn [uri-params]
|
||||
(pushy/set-token! p/history
|
||||
(str (.-protocol (.-location js/window)) "//" (.-host (.-location js/window)) (.-pathname (.-location js/window))
|
||||
"?"
|
||||
(cemerick.url/map->query (into {} (filter (fn [[k v]] v) uri-params)))
|
||||
#_(reduce-kv (fn [result k v]
|
||||
(if v
|
||||
(str (or result "?")
|
||||
(when result "&")
|
||||
(name k) "=" (js/encodeURI v))
|
||||
result))
|
||||
nil
|
||||
uri-params)))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:new-window
|
||||
(fn [url]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.routes :as routes]
|
||||
[auto-ap.effects :as effects]
|
||||
|
||||
[auto-ap.utils :refer [by]]
|
||||
[venia.core :as v]
|
||||
[bidi.bidi :as bidi]
|
||||
@@ -38,9 +39,7 @@
|
||||
{:db (assoc db/default-db
|
||||
:active-page handler
|
||||
:last-client-id (.getItem js/localStorage "last-client-id")
|
||||
:query-params (->> (:query (url (.-location js/window)))
|
||||
(map (fn [[k v]] [(keyword k) v]))
|
||||
(into {}))
|
||||
:query-params (auto-ap.views.utils/query-params)
|
||||
:user token)
|
||||
:graphql {:token token
|
||||
:query-obj {:venia/queries [[:client
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
|
||||
(defn- dispatch-route [matched-route]
|
||||
(println "Matched route" matched-route)
|
||||
(re-frame/dispatch [:auto-ap.events/set-active-page (:handler matched-route) (->> (:query (url (.-location js/window)))
|
||||
(map (fn [[k v]] [(keyword k) v]))
|
||||
(into {}))]))
|
||||
(re-frame/dispatch [:auto-ap.events/set-active-page (:handler matched-route) (auto-ap.views.utils/query-params)]))
|
||||
|
||||
|
||||
(def history (pushy/pushy dispatch-route parse-url))
|
||||
|
||||
@@ -59,10 +59,17 @@
|
||||
:end]]]})
|
||||
|
||||
(re-frame/reg-sub
|
||||
::table-params
|
||||
::specific-table-params
|
||||
(fn [db]
|
||||
(::table-params db)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::table-params
|
||||
:<- [::specific-table-params]
|
||||
:<- [::subs/query-params]
|
||||
(fn [[specific-table-params query-params]]
|
||||
(merge (select-keys query-params #{:start :sort}) specific-table-params )))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::params-changed
|
||||
[(re-frame/path [::table-params])]
|
||||
|
||||
@@ -9,21 +9,41 @@
|
||||
[auto-ap.routes :as routes]
|
||||
[auto-ap.views.components.date-range-filter :refer [date-range-filter]]
|
||||
[auto-ap.views.components.typeahead :refer [typeahead-entity]]
|
||||
[auto-ap.views.utils :refer [active-when dispatch-event bind-field horizontal-field date->str str->date pretty standard]]
|
||||
[auto-ap.views.utils :refer [active-when dispatch-event bind-field horizontal-field date->str str->date pretty standard query-params]]
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.events :as events]))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::specific-filters
|
||||
(fn [db ]
|
||||
(::filters db nil)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::filters
|
||||
(fn [db ]
|
||||
(::filters db {})))
|
||||
:<- [::specific-filters]
|
||||
:<- [::subs/vendors-by-id]
|
||||
:<- [::subs/query-params]
|
||||
(fn [[specific-filters vendors-by-id query-params] ]
|
||||
(let [url-filters (-> query-params
|
||||
(select-keys #{:vendor-id
|
||||
:date-range
|
||||
:due-range
|
||||
:invoice-number-like
|
||||
:status}))
|
||||
url-filters {:vendor (when-let [vendor-id (:vendor-id url-filters)]
|
||||
{:id (str vendor-id)
|
||||
:name (get-in vendors-by-id [(str vendor-id) :name] "Loading...")})
|
||||
:date-range (:date-range url-filters)
|
||||
:due-range (:due-range url-filters)
|
||||
:invoice-number-like {:raw (:invoice-number-like url-filters)
|
||||
:settled (:invoice-number-like url-filters)}}]
|
||||
(merge url-filters specific-filters ))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::filter
|
||||
:<- [::filters]
|
||||
(fn [filters [_ which]]
|
||||
(filters which)))
|
||||
(get filters which)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
::filter-params
|
||||
@@ -32,12 +52,13 @@
|
||||
(fn [[filters ap]]
|
||||
{:vendor-id (:id (:vendor filters))
|
||||
:date-range (:date-range filters)
|
||||
:due-range (:due-range filters)
|
||||
:invoice-number-like (:settled (:invoice-number-like filters))
|
||||
:status (condp = ap
|
||||
:invoices nil
|
||||
:unpaid-invoices :unpaid
|
||||
:paid-invoices :paid
|
||||
:voided-invoices :voided)}))
|
||||
:invoices nil
|
||||
:unpaid-invoices :unpaid
|
||||
:paid-invoices :paid
|
||||
:voided-invoices :voided)}))
|
||||
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
@@ -125,6 +146,13 @@
|
||||
[date-range-filter
|
||||
{:on-change-event [::filter-changed :date-range]
|
||||
:value @(re-frame/subscribe [::filter :date-range])}]]
|
||||
|
||||
[:p.menu-label "Due Range"]
|
||||
[:div
|
||||
[date-range-filter
|
||||
{:on-change-event [::filter-changed :due-range]
|
||||
:value @(re-frame/subscribe [::filter :due-range])}]]
|
||||
|
||||
[:p.menu-label "Invoice #"]
|
||||
[:div
|
||||
[invoice-number-filter]]])]))
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
(ns auto-ap.views.pages.home
|
||||
(:require [auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||
[re-frame.core :as re-frame]
|
||||
[bidi.bidi :as bidi]
|
||||
[auto-ap.routes :as routes]
|
||||
[auto-ap.subs :as subs]
|
||||
[cljs-time.core :as t]
|
||||
[auto-ap.views.utils :refer [local-now date->str]]
|
||||
[reagent.core :as r]))
|
||||
[auto-ap.views.utils :refer [local-now date->str standard]]
|
||||
[reagent.core :as r]
|
||||
[pushy.core :as pushy]))
|
||||
|
||||
|
||||
(def pie-chart (r/adapt-react-class js/Recharts.PieChart))
|
||||
@@ -47,16 +50,21 @@
|
||||
)
|
||||
|
||||
(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]]
|
||||
(let [redirect-fn (fn [x]
|
||||
(pushy/set-token! auto-ap.history/history (str (bidi/path-for routes/routes :unpaid-invoices) "?" (get (js->clj x) "query-params")))
|
||||
)]
|
||||
[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"
|
||||
:on-click redirect-fn}]
|
||||
[bar {:dataKey "outstanding-payments" :fill (get colors 0) :stackId "a" :name "Outstanding Payments"
|
||||
:on-click redirect-fn}]
|
||||
[bar {:dataKey "invoices" :fill (get colors 3) :stackId "a" :name "Invoices"
|
||||
:on-click redirect-fn}]
|
||||
|
||||
[x-axis {:dataKey "name"}]
|
||||
[y-axis]
|
||||
[legend]])
|
||||
)
|
||||
|
||||
(re-frame/reg-event-db
|
||||
@@ -99,8 +107,6 @@
|
||||
(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))))))
|
||||
{}
|
||||
@@ -113,14 +119,18 @@
|
||||
(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)})))
|
||||
(let [today (t/plus start-date (t/days day))]
|
||||
(conj acc
|
||||
{:name (date->str today)
|
||||
:effective-balance (- effective-balance invoices-due-today)
|
||||
:invoices (- invoices-due-today)
|
||||
:query-params (cemerick.url/map->query {:due-range {:start (date->str today standard)
|
||||
:end (date->str today standard)}})}))))
|
||||
(list {:name (date->str start-date)
|
||||
:effective-balance effective-balance
|
||||
:invoices (- (invoices-due-soon (date->str start-date) 0.0))
|
||||
:outstanding-payments (- outstanding-payments)})
|
||||
:outstanding-payments (- outstanding-payments)
|
||||
:query-params (cemerick.url/map->query {:due-range {:end (date->str start-date standard)}})})
|
||||
(range 1 7))))))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
|
||||
@@ -98,14 +98,17 @@
|
||||
::params-change
|
||||
[with-user (re-frame/inject-cofx ::inject/sub [::params])]
|
||||
(fn [{::keys [params] :as cofx} _]
|
||||
(println "params" params)
|
||||
{:db (-> (:db cofx)
|
||||
(assoc-in [:status :loading] true)
|
||||
(assoc-in [::last-params] params))
|
||||
:graphql {:token (-> cofx :db :user)
|
||||
:query-obj (table/query params )
|
||||
:on-success [::received]
|
||||
:on-error [::events/page-failed]}}))
|
||||
:on-error [::events/page-failed]}
|
||||
:set-uri-params (dissoc params
|
||||
:status
|
||||
:client-id
|
||||
:import-status)}))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::unmounted
|
||||
|
||||
@@ -320,3 +320,10 @@
|
||||
:before (fn [context]
|
||||
(-> context
|
||||
(assoc-in [:coeffects :user] (get-in context [:coeffects :db :user]))))))
|
||||
|
||||
(defn query-params []
|
||||
(reduce-kv
|
||||
(fn [result k v]
|
||||
(assoc result (keyword k) (cljs.tools.reader.edn/read-string v)))
|
||||
{}
|
||||
(:query (cemerick.url/url (.-location js/window)))))
|
||||
|
||||
Reference in New Issue
Block a user