Makes tasks card
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -44,3 +44,4 @@ data/solr/data/plaid_merchants/data/
|
|||||||
data/solr/data/logs
|
data/solr/data/logs
|
||||||
data/solr/logs
|
data/solr/logs
|
||||||
.vscode/**
|
.vscode/**
|
||||||
|
sysco-poller/**/*.csv
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
|||||||
(:require [amazonica.core :refer [defcredential]]
|
(:require [amazonica.core :refer [defcredential]]
|
||||||
[auto-ap.client-routes :as client-routes]
|
[auto-ap.client-routes :as client-routes]
|
||||||
[auto-ap.datomic :refer [conn pull-many]]
|
[auto-ap.datomic :refer [conn pull-many]]
|
||||||
[auto-ap.graphql.utils :refer [limited-clients]]
|
[auto-ap.graphql.utils :refer [extract-client-ids limited-clients]]
|
||||||
[auto-ap.logging :as alog]
|
[auto-ap.logging :as alog]
|
||||||
[auto-ap.routes.auth :as auth]
|
[auto-ap.routes.auth :as auth]
|
||||||
[auto-ap.routes.exports :as exports]
|
[auto-ap.routes.exports :as exports]
|
||||||
@@ -304,12 +304,26 @@
|
|||||||
{:status 500
|
{:status 500
|
||||||
:body (pr-str e)})))))
|
:body (pr-str e)})))))
|
||||||
|
|
||||||
|
(defn wrap-trim-clients [handler]
|
||||||
|
(fn [request]
|
||||||
|
(let [valid-clients (extract-client-ids (:clients request)
|
||||||
|
(:client request)
|
||||||
|
(:client-id (:parsed-query-params request))
|
||||||
|
(when (:client-code (:parsed-query-params request))
|
||||||
|
[:client/code (:client-code (:parsed-query-params request))]))
|
||||||
|
trimmed-clients (->> valid-clients (take 20) set)]
|
||||||
|
(handler (assoc request :valid-client-ids valid-clients
|
||||||
|
:valid-trimmed-client-ids trimmed-clients
|
||||||
|
:first-client-id (first valid-clients)
|
||||||
|
:clients-trimmed? (not= (count trimmed-clients) (count valid-clients)))))))
|
||||||
|
|
||||||
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
#_{:clj-kondo/ignore [:clojure-lsp/unused-public-var]}
|
||||||
(defonce app
|
(defonce app
|
||||||
(-> route-handler
|
(-> route-handler
|
||||||
(wrap-hx-current-url-params)
|
(wrap-hx-current-url-params)
|
||||||
(wrap-guess-route)
|
(wrap-guess-route)
|
||||||
(wrap-logging)
|
(wrap-logging)
|
||||||
|
(wrap-trim-clients)
|
||||||
(wrap-hydrate-clients)
|
(wrap-hydrate-clients)
|
||||||
(wrap-store-client-in-session)
|
(wrap-store-client-in-session)
|
||||||
(wrap-gunzip-jwt)
|
(wrap-gunzip-jwt)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
(ns auto-ap.ssr.dashboard
|
(ns auto-ap.ssr.dashboard
|
||||||
(:require [auto-ap.datomic :refer [conn]]
|
(:require [auto-ap.datomic :refer [conn]]
|
||||||
[auto-ap.graphql.ledger :refer [get-profit-and-loss-raw]]
|
[auto-ap.graphql.ledger :refer [get-profit-and-loss-raw]]
|
||||||
[auto-ap.graphql.utils :refer [<-graphql extract-client-ids]]
|
[auto-ap.graphql.utils :refer [<-graphql]]
|
||||||
[auto-ap.ledger.reports :as r]
|
[auto-ap.ledger.reports :as r]
|
||||||
[auto-ap.routes.dashboard :as d-routes]
|
[auto-ap.routes.dashboard :as d-routes]
|
||||||
|
[auto-ap.routes.invoice :as i-routes]
|
||||||
[auto-ap.routes.utils :refer [wrap-admin
|
[auto-ap.routes.utils :refer [wrap-admin
|
||||||
wrap-client-redirect-unauthenticated]]
|
wrap-client-redirect-unauthenticated]]
|
||||||
[auto-ap.ssr-routes :as ssr-routes]
|
[auto-ap.ssr-routes :as ssr-routes]
|
||||||
@@ -15,14 +16,17 @@
|
|||||||
[bidi.bidi :as bidi]
|
[bidi.bidi :as bidi]
|
||||||
[clj-time.coerce :as coerce]
|
[clj-time.coerce :as coerce]
|
||||||
[clj-time.core :as time]
|
[clj-time.core :as time]
|
||||||
[datomic.api :as dc]))
|
[datomic.api :as dc]
|
||||||
|
[hiccup.util :as hu]
|
||||||
|
[auto-ap.client-routes :as client-routes]
|
||||||
|
[cemerick.url :as url]))
|
||||||
|
|
||||||
(defn bank-accounts-card [request]
|
(defn bank-accounts-card [request]
|
||||||
(com/card {:class "inline-block " }
|
(com/card {:class "inline-block " }
|
||||||
[:div.p-4
|
[:div.p-4
|
||||||
[:h1.text-2xl.font-bold "Bank Accounts"]
|
[:h1.text-2xl.font-bold "Bank Accounts"]
|
||||||
[:div {:class "max-h-[900px] overflow-scroll"}
|
[:div {:class "max-h-[900px] overflow-scroll"}
|
||||||
(for [c (:clients request)
|
(for [c (:valid-trimmed-client-ids request)
|
||||||
b (:client/bank-accounts (dc/pull (dc/db conn) '[{:client/bank-accounts
|
b (:client/bank-accounts (dc/pull (dc/db conn) '[{:client/bank-accounts
|
||||||
|
|
||||||
[:bank-account/current-balance
|
[:bank-account/current-balance
|
||||||
@@ -36,7 +40,7 @@
|
|||||||
[:yodlee-account/last-synced :xform clj-time.coerce/from-date]]}
|
[:yodlee-account/last-synced :xform clj-time.coerce/from-date]]}
|
||||||
{:bank-account/plaid-account [:plaid-account/balance
|
{:bank-account/plaid-account [:plaid-account/balance
|
||||||
[:plaid-account/last-synced :xform clj-time.coerce/from-date]]}]}]
|
[:plaid-account/last-synced :xform clj-time.coerce/from-date]]}]}]
|
||||||
(:db/id c)))
|
c))
|
||||||
:when (not= :bank-account-type/cash (:bank-account/type b))]
|
:when (not= :bank-account-type/cash (:bank-account/type b))]
|
||||||
[:div.flex.flex-col.p-4.border-b-2.border-gray-200
|
[:div.flex.flex-col.p-4.border-b-2.border-gray-200
|
||||||
[:div.font-bold.text-gray-700 (:client/name c)]
|
[:div.font-bold.text-gray-700 (:client/name c)]
|
||||||
@@ -78,12 +82,7 @@
|
|||||||
#_[:div.inline-flex.justify-between.items-baseline]]])]]))
|
#_[:div.inline-flex.justify-between.items-baseline]]])]]))
|
||||||
|
|
||||||
(defn sales-chart-card [request]
|
(defn sales-chart-card [request]
|
||||||
(let [
|
(let [ totals
|
||||||
valid-clients (extract-client-ids (:clients request)
|
|
||||||
(:client-id request)
|
|
||||||
(when (:client-code request)
|
|
||||||
[:client/code (:client-code request)]))
|
|
||||||
totals
|
|
||||||
(->> (dc/q '[:find ?sd (sum ?total)
|
(->> (dc/q '[:find ?sd (sum ?total)
|
||||||
:with ?e
|
:with ?e
|
||||||
:in $ [?clients ?start-date ?end-date]
|
:in $ [?clients ?start-date ?end-date]
|
||||||
@@ -92,7 +91,7 @@ valid-clients (extract-client-ids (:clients request)
|
|||||||
[(iol-ion.query/iso-date ?d) ?sd]
|
[(iol-ion.query/iso-date ?d) ?sd]
|
||||||
[?e :sales-order/total ?total]]
|
[?e :sales-order/total ?total]]
|
||||||
(dc/db conn)
|
(dc/db conn)
|
||||||
[valid-clients
|
[(:valid-trimmed-client-ids request)
|
||||||
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/days -14))))
|
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/days -14))))
|
||||||
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/days 1))))])
|
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/days 1))))])
|
||||||
(sort-by first))]
|
(sort-by first))]
|
||||||
@@ -125,11 +124,7 @@ valid-clients (extract-client-ids (:clients request)
|
|||||||
});"}]])))
|
});"}]])))
|
||||||
|
|
||||||
(defn expense-pie-card [request]
|
(defn expense-pie-card [request]
|
||||||
(let [valid-clients (extract-client-ids (:clients request)
|
(let [ totals
|
||||||
(:client-id request)
|
|
||||||
(when (:client-code request)
|
|
||||||
[:client/code (:client-code request)]))
|
|
||||||
totals
|
|
||||||
(->> (dc/q '[:find ?an (sum ?amt)
|
(->> (dc/q '[:find ?an (sum ?amt)
|
||||||
:with ?iea
|
:with ?iea
|
||||||
:in $ [?clients ?start-date ?end-date]
|
:in $ [?clients ?start-date ?end-date]
|
||||||
@@ -140,7 +135,7 @@ valid-clients (extract-client-ids (:clients request)
|
|||||||
[?iea :invoice-expense-account/amount ?amt]
|
[?iea :invoice-expense-account/amount ?amt]
|
||||||
[?ea :account/name ?an]]
|
[?ea :account/name ?an]]
|
||||||
(dc/db conn)
|
(dc/db conn)
|
||||||
[valid-clients
|
[(:valid-trimmed-client-ids request)
|
||||||
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/months -1))))
|
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/months -1))))
|
||||||
(coerce/to-date (time/plus (time/with-time-at-start-of-day (time/now)) (time/days 1)))])
|
(coerce/to-date (time/plus (time/with-time-at-start-of-day (time/now)) (time/days 1)))])
|
||||||
(sort-by last)
|
(sort-by last)
|
||||||
@@ -175,20 +170,15 @@ valid-clients (extract-client-ids (:clients request)
|
|||||||
[:h1.text-2xl.font-bold.text-gray-700
|
[:h1.text-2xl.font-bold.text-gray-700
|
||||||
"Profit and Loss"
|
"Profit and Loss"
|
||||||
]
|
]
|
||||||
(let [all-clients (extract-client-ids (:clients request)
|
(let [ data (<-graphql (get-profit-and-loss-raw (:valid-trimmed-client-ids request)
|
||||||
(:client-id request)
|
|
||||||
(when (:client-code request)
|
|
||||||
[:client/code (:client-code request)]))
|
|
||||||
clients (take 10 all-clients)
|
|
||||||
data (<-graphql (get-profit-and-loss-raw clients
|
|
||||||
[{:start (time/plus (time/now) (time/days -90))
|
[{:start (time/plus (time/now) (time/days -90))
|
||||||
:end (time/now)}]))
|
:end (time/now)}]))
|
||||||
data (r/->PNLData {} (:accounts (first (:periods data))) {})
|
data (r/->PNLData {} (:accounts (first (:periods data))) {})
|
||||||
sales (r/aggregate-accounts (r/filter-categories data [ :sales]))
|
sales (r/aggregate-accounts (r/filter-categories data [ :sales]))
|
||||||
expenses (r/aggregate-accounts (r/filter-categories data [ :cogs :payroll :controllable :fixed-overhead :ownership-controllable ]))]
|
expenses (r/aggregate-accounts (r/filter-categories data [ :cogs :payroll :controllable :fixed-overhead :ownership-controllable ]))]
|
||||||
(list
|
(list
|
||||||
(when (not= (count all-clients) (count clients))
|
#_(when (not= (count all-clients) (count clients))
|
||||||
[:div.bg-yellow-100.rounded-lg.p-4.my-2.text-yellow-900 "Warning: Too many clients are selected. This report only shows 10 of the selected clients."])
|
)
|
||||||
[:canvas.w-full.h-full.p-8 {:x-data (hx/json {:chart nil
|
[:canvas.w-full.h-full.p-8 {:x-data (hx/json {:chart nil
|
||||||
:labels [(format "Income $%,.2f" sales) (format "Expenses $%,.2f" expenses)]
|
:labels [(format "Income $%,.2f" sales) (format "Expenses $%,.2f" expenses)]
|
||||||
:data [sales expenses]})
|
:data [sales expenses]})
|
||||||
@@ -219,13 +209,59 @@ valid-clients (extract-client-ids (:clients request)
|
|||||||
[:div
|
[:div
|
||||||
"Expenses: " (format "$%,.2f" expenses)]))))
|
"Expenses: " (format "$%,.2f" expenses)]))))
|
||||||
|
|
||||||
|
(defn tasks-card [request]
|
||||||
|
(com/card {:class "w-full h-full p-4 space-y-2"}
|
||||||
|
[:h1.text-2xl.font-bold.text-gray-700
|
||||||
|
"Tasks"]
|
||||||
|
(let [[unpaid-invoice-count unpaid-invoice-amount]
|
||||||
|
(first (dc/q '[:find (count ?e) (sum ?ab)
|
||||||
|
:in $ [?clients ?start-date ?end-date]
|
||||||
|
:where [(iol-ion.query/scan-invoices $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
||||||
|
[?e :invoice/status :invoice-status/unpaid]
|
||||||
|
[?e :invoice/outstanding-balance ?ab]]
|
||||||
|
(dc/db conn)
|
||||||
|
[(:valid-trimmed-client-ids request)
|
||||||
|
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/years -1))))
|
||||||
|
nil]))
|
||||||
|
|
||||||
|
[uncategorized-transaction-count uncategorized-transaction-amount]
|
||||||
|
(first (dc/q '[:find (count ?e) (sum ?am)
|
||||||
|
:in $ [?clients ?start-date ?end-date]
|
||||||
|
:where [(iol-ion.query/scan-transactions $ ?clients ?start-date ?end-date) [[?e _ ?sort-default] ...]]
|
||||||
|
[?e :transaction/approval-status :transaction-approval-status/requires-feedback]
|
||||||
|
[?e :transaction/amount ?am]]
|
||||||
|
(dc/db conn)
|
||||||
|
[(:valid-trimmed-client-ids request)
|
||||||
|
(coerce/to-date (time/with-time-at-start-of-day (time/plus (time/now) (time/years -1))))
|
||||||
|
nil]))]
|
||||||
|
(list
|
||||||
|
(when (not= 0 (or unpaid-invoice-count 0))
|
||||||
|
[:div.bg-gray-50.rounded.p-4
|
||||||
|
[:span "You have " (str unpaid-invoice-count) " unpaid invoices with an outstanding balance of " (format "$%,.2f" unpaid-invoice-amount) ". " ]
|
||||||
|
|
||||||
|
(com/link {:href (hu/url (bidi.bidi/path-for ssr-routes/only-routes ::i-routes/unpaid-page)
|
||||||
|
{:date-range "year"})
|
||||||
|
:hx-boost "true"}
|
||||||
|
|
||||||
|
"Pay now")
|
||||||
|
])
|
||||||
|
(when (not= 0 (or uncategorized-transaction-count 0))
|
||||||
|
[:div.bg-gray-50.rounded.p-4
|
||||||
|
[:span "You have " (str uncategorized-transaction-count) " transactions needing your feedback. " ]
|
||||||
|
|
||||||
|
(com/link {:href (str (bidi.bidi/path-for client-routes/routes :requires-feedback-transactions)
|
||||||
|
"?date-range="
|
||||||
|
(url/url-encode (pr-str {:start (atime/unparse-local (time/plus (time/now) (time/years -1)) atime/iso-date) :end (atime/unparse-local (time/now) atime/iso-date)}))) }
|
||||||
|
|
||||||
|
"Review now")
|
||||||
|
])))))
|
||||||
|
|
||||||
(defn- page-contents [request]
|
(defn- page-contents [request]
|
||||||
[:div
|
[:div
|
||||||
[:div {:class "grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-4 auto-rows-fr max-h-[970px]"}
|
[:div {:class "grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-4 auto-rows-fr max-h-[970px]"}
|
||||||
[:div (expense-pie-card request)]
|
[:div (expense-pie-card request)]
|
||||||
[:div (com/card {:class "w-full h-full p-4"}
|
[:div
|
||||||
[:h1.text-2xl.font-bold.text-gray-700
|
(tasks-card request)]
|
||||||
"Tasks"])]
|
|
||||||
[:div.row-span-2
|
[:div.row-span-2
|
||||||
(bank-accounts-card request)]
|
(bank-accounts-card request)]
|
||||||
|
|
||||||
@@ -255,6 +291,8 @@ valid-clients (extract-client-ids (:clients request)
|
|||||||
(com/breadcrumbs {}
|
(com/breadcrumbs {}
|
||||||
[:a {:href (bidi/path-for ssr-routes/only-routes ::d-routes/page)}
|
[:a {:href (bidi/path-for ssr-routes/only-routes ::d-routes/page)}
|
||||||
"Dashboard"])
|
"Dashboard"])
|
||||||
|
(when (:clients-trimmed? request)
|
||||||
|
[:div.bg-yellow-100.rounded-lg.p-4.my-2.text-yellow-900.border-1 "Warning: These reports are only for twenty of the selected customers. Please select a specific customer to see more detail."])
|
||||||
(page-contents request))
|
(page-contents request))
|
||||||
"Dashboard"))
|
"Dashboard"))
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
(ns auto-ap.views.pages.transactions
|
(ns auto-ap.views.pages.transactions
|
||||||
(:require [auto-ap.effects.forward :as forward]
|
(:require [auto-ap.effects.forward :as forward]
|
||||||
[auto-ap.forms :as forms]
|
[auto-ap.forms :as forms]
|
||||||
|
[auto-ap.status :as status]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.views.components.modal :as modal]
|
|
||||||
[auto-ap.views.components.layouts
|
[auto-ap.views.components.layouts
|
||||||
:refer
|
:refer
|
||||||
[appearing-side-bar side-bar-layout]]
|
[appearing-side-bar side-bar-layout]]
|
||||||
|
[auto-ap.views.components.modal :as modal]
|
||||||
[auto-ap.views.pages.data-page :as data-page]
|
[auto-ap.views.pages.data-page :as data-page]
|
||||||
[auto-ap.views.pages.transactions.common :refer [transaction-read data-params->query-params]]
|
[auto-ap.views.pages.transactions.bulk-updates :as bulk]
|
||||||
|
[auto-ap.views.pages.transactions.common :refer [data-params->query-params
|
||||||
|
transaction-read]]
|
||||||
[auto-ap.views.pages.transactions.form :as edit]
|
[auto-ap.views.pages.transactions.form :as edit]
|
||||||
[auto-ap.views.pages.transactions.manual :as manual]
|
[auto-ap.views.pages.transactions.manual :as manual]
|
||||||
[auto-ap.views.pages.transactions.bulk-updates :as bulk]
|
|
||||||
[auto-ap.views.pages.transactions.side-bar :as side-bar]
|
[auto-ap.views.pages.transactions.side-bar :as side-bar]
|
||||||
[auto-ap.views.pages.transactions.table :as table]
|
[auto-ap.views.pages.transactions.table :as table]
|
||||||
[auto-ap.views.utils :refer [dispatch-event with-user date->str standard]]
|
[auto-ap.views.utils :refer [date->str dispatch-event standard
|
||||||
|
with-user]]
|
||||||
|
[auto-ap.views.utils :as u]
|
||||||
[cljs-time.core :as time]
|
[cljs-time.core :as time]
|
||||||
|
[clojure.string :as str]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[reagent.core :as reagent]
|
[reagent.core :as reagent]
|
||||||
[vimsical.re-frame.fx.track :as track]
|
[vimsical.re-frame.fx.track :as track]))
|
||||||
[auto-ap.status :as status]
|
|
||||||
[clojure.string :as str]))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -122,35 +125,38 @@
|
|||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::mounted
|
::mounted
|
||||||
(fn [{:keys [db]} _]
|
(fn [{:keys [db]} _]
|
||||||
{:db (assoc-in db [::data-page/settled-filters ::page :date-range] {:start (date->str (time/plus (time/now) (time/months -1))
|
(let [db (if (:date-range (u/query-params))
|
||||||
standard)})
|
db
|
||||||
::track/register {:id ::params
|
(assoc-in db [::data-page/settled-filters ::page :date-range] {:start (date->str (time/plus (time/now) (time/months -1))
|
||||||
:subscription [::data-page/params ::page]
|
standard)}))]
|
||||||
:event-fn (fn [params]
|
{:db db
|
||||||
[::params-change params])}
|
::track/register {:id ::params
|
||||||
::forward/register [{:id ::updated
|
:subscription [::data-page/params ::page]
|
||||||
:events #{::edit/edited}
|
:event-fn (fn [params]
|
||||||
:event-fn (fn [[_ edited-transaction]]
|
[::params-change params])}
|
||||||
[::data-page/updated-entity ::page edited-transaction])}
|
::forward/register [{:id ::updated
|
||||||
{:id ::manual-import
|
:events #{::edit/edited}
|
||||||
:events #{::manual/import-completed}
|
:event-fn (fn [[_ edited-transaction]]
|
||||||
:event-fn (fn [[_ result]]
|
[::data-page/updated-entity ::page edited-transaction])}
|
||||||
[::status/info ::manual-import
|
{:id ::manual-import
|
||||||
(str "Successfully "
|
:events #{::manual/import-completed}
|
||||||
(str/join ", "
|
:event-fn (fn [[_ result]]
|
||||||
[(when-let [imported (:import-batch/imported result)]
|
[::status/info ::manual-import
|
||||||
(str "imported " imported))
|
(str "Successfully "
|
||||||
(when-let [extant (:import-batch/extant result)]
|
(str/join ", "
|
||||||
(str "extant " extant))
|
[(when-let [imported (:import-batch/imported result)]
|
||||||
(when-let [suppressed (:import-batch/suppressed result)]
|
(str "imported " imported))
|
||||||
(str "suppressed " suppressed))
|
(when-let [extant (:import-batch/extant result)]
|
||||||
(when-let [not-ready (:import-batch/not-ready result)]
|
(str "extant " extant))
|
||||||
(str "too early " not-ready))
|
(when-let [suppressed (:import-batch/suppressed result)]
|
||||||
(when-let [error (:validation-error result)]
|
(str "suppressed " suppressed))
|
||||||
(str "errored " error))])
|
(when-let [not-ready (:import-batch/not-ready result)]
|
||||||
" transactions."
|
(str "too early " not-ready))
|
||||||
(when (:sample-error result)
|
(when-let [error (:validation-error result)]
|
||||||
(str " Sample error: " (:info (:sample-error result)))))])}]}))
|
(str "errored " error))])
|
||||||
|
" transactions."
|
||||||
|
(when (:sample-error result)
|
||||||
|
(str " Sample error: " (:info (:sample-error result)))))])}]})))
|
||||||
|
|
||||||
(defn action-buttons []
|
(defn action-buttons []
|
||||||
(let [is-admin? @(re-frame/subscribe [::subs/is-admin?])
|
(let [is-admin? @(re-frame/subscribe [::subs/is-admin?])
|
||||||
|
|||||||
Reference in New Issue
Block a user