Makes tasks card

This commit is contained in:
2024-04-28 20:29:21 -07:00
parent f6b413a9f5
commit 0b2ec31160
5 changed files with 125 additions and 66 deletions

1
.gitignore vendored
View File

@@ -44,3 +44,4 @@ data/solr/data/plaid_merchants/data/
data/solr/data/logs
data/solr/logs
.vscode/**
sysco-poller/**/*.csv

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
(:require [amazonica.core :refer [defcredential]]
[auto-ap.client-routes :as client-routes]
[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.routes.auth :as auth]
[auto-ap.routes.exports :as exports]
@@ -304,12 +304,26 @@
{:status 500
: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]}
(defonce app
(-> route-handler
(wrap-hx-current-url-params)
(wrap-guess-route)
(wrap-logging)
(wrap-trim-clients)
(wrap-hydrate-clients)
(wrap-store-client-in-session)
(wrap-gunzip-jwt)

View File

@@ -1,9 +1,10 @@
(ns auto-ap.ssr.dashboard
(:require [auto-ap.datomic :refer [conn]]
[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.routes.dashboard :as d-routes]
[auto-ap.routes.invoice :as i-routes]
[auto-ap.routes.utils :refer [wrap-admin
wrap-client-redirect-unauthenticated]]
[auto-ap.ssr-routes :as ssr-routes]
@@ -15,14 +16,17 @@
[bidi.bidi :as bidi]
[clj-time.coerce :as coerce]
[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]
(com/card {:class "inline-block " }
[:div.p-4
[:h1.text-2xl.font-bold "Bank Accounts"]
[: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
[:bank-account/current-balance
@@ -36,7 +40,7 @@
[:yodlee-account/last-synced :xform clj-time.coerce/from-date]]}
{:bank-account/plaid-account [:plaid-account/balance
[:plaid-account/last-synced :xform clj-time.coerce/from-date]]}]}]
(:db/id c)))
c))
:when (not= :bank-account-type/cash (:bank-account/type b))]
[:div.flex.flex-col.p-4.border-b-2.border-gray-200
[:div.font-bold.text-gray-700 (:client/name c)]
@@ -78,12 +82,7 @@
#_[:div.inline-flex.justify-between.items-baseline]]])]]))
(defn sales-chart-card [request]
(let [
valid-clients (extract-client-ids (:clients request)
(:client-id request)
(when (:client-code request)
[:client/code (:client-code request)]))
totals
(let [ totals
(->> (dc/q '[:find ?sd (sum ?total)
:with ?e
:in $ [?clients ?start-date ?end-date]
@@ -92,7 +91,7 @@ valid-clients (extract-client-ids (:clients request)
[(iol-ion.query/iso-date ?d) ?sd]
[?e :sales-order/total ?total]]
(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 1))))])
(sort-by first))]
@@ -125,11 +124,7 @@ valid-clients (extract-client-ids (:clients request)
});"}]])))
(defn expense-pie-card [request]
(let [valid-clients (extract-client-ids (:clients request)
(:client-id request)
(when (:client-code request)
[:client/code (:client-code request)]))
totals
(let [ totals
(->> (dc/q '[:find ?an (sum ?amt)
:with ?iea
:in $ [?clients ?start-date ?end-date]
@@ -140,7 +135,7 @@ valid-clients (extract-client-ids (:clients request)
[?iea :invoice-expense-account/amount ?amt]
[?ea :account/name ?an]]
(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/plus (time/with-time-at-start-of-day (time/now)) (time/days 1)))])
(sort-by last)
@@ -175,20 +170,15 @@ valid-clients (extract-client-ids (:clients request)
[:h1.text-2xl.font-bold.text-gray-700
"Profit and Loss"
]
(let [all-clients (extract-client-ids (:clients 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
(let [ data (<-graphql (get-profit-and-loss-raw (:valid-trimmed-client-ids request)
[{:start (time/plus (time/now) (time/days -90))
:end (time/now)}]))
data (r/->PNLData {} (:accounts (first (:periods data))) {})
sales (r/aggregate-accounts (r/filter-categories data [ :sales]))
expenses (r/aggregate-accounts (r/filter-categories data [ :cogs :payroll :controllable :fixed-overhead :ownership-controllable ]))]
(list
(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."])
#_(when (not= (count all-clients) (count clients))
)
[:canvas.w-full.h-full.p-8 {:x-data (hx/json {:chart nil
:labels [(format "Income $%,.2f" sales) (format "Expenses $%,.2f" expenses)]
:data [sales expenses]})
@@ -219,13 +209,59 @@ valid-clients (extract-client-ids (:clients request)
[:div
"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]
[:div
[: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 (com/card {:class "w-full h-full p-4"}
[:h1.text-2xl.font-bold.text-gray-700
"Tasks"])]
[:div
(tasks-card request)]
[:div.row-span-2
(bank-accounts-card request)]
@@ -255,6 +291,8 @@ valid-clients (extract-client-ids (:clients request)
(com/breadcrumbs {}
[:a {:href (bidi/path-for ssr-routes/only-routes ::d-routes/page)}
"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))
"Dashboard"))

View File

@@ -1,25 +1,28 @@
(ns auto-ap.views.pages.transactions
(:require [auto-ap.effects.forward :as forward]
[auto-ap.forms :as forms]
[auto-ap.status :as status]
[auto-ap.subs :as subs]
[auto-ap.views.components.modal :as modal]
[auto-ap.views.components.layouts
:refer
[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.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.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.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]
[clojure.string :as str]
[re-frame.core :as re-frame]
[reagent.core :as reagent]
[vimsical.re-frame.fx.track :as track]
[auto-ap.status :as status]
[clojure.string :as str]))
[vimsical.re-frame.fx.track :as track]))
@@ -122,35 +125,38 @@
(re-frame/reg-event-fx
::mounted
(fn [{:keys [db]} _]
{:db (assoc-in db [::data-page/settled-filters ::page :date-range] {:start (date->str (time/plus (time/now) (time/months -1))
standard)})
::track/register {:id ::params
:subscription [::data-page/params ::page]
:event-fn (fn [params]
[::params-change params])}
::forward/register [{:id ::updated
:events #{::edit/edited}
:event-fn (fn [[_ edited-transaction]]
[::data-page/updated-entity ::page edited-transaction])}
{:id ::manual-import
:events #{::manual/import-completed}
:event-fn (fn [[_ result]]
[::status/info ::manual-import
(str "Successfully "
(str/join ", "
[(when-let [imported (:import-batch/imported result)]
(str "imported " imported))
(when-let [extant (:import-batch/extant result)]
(str "extant " extant))
(when-let [suppressed (:import-batch/suppressed result)]
(str "suppressed " suppressed))
(when-let [not-ready (:import-batch/not-ready result)]
(str "too early " not-ready))
(when-let [error (:validation-error result)]
(str "errored " error))])
" transactions."
(when (:sample-error result)
(str " Sample error: " (:info (:sample-error result)))))])}]}))
(let [db (if (:date-range (u/query-params))
db
(assoc-in db [::data-page/settled-filters ::page :date-range] {:start (date->str (time/plus (time/now) (time/months -1))
standard)}))]
{:db db
::track/register {:id ::params
:subscription [::data-page/params ::page]
:event-fn (fn [params]
[::params-change params])}
::forward/register [{:id ::updated
:events #{::edit/edited}
:event-fn (fn [[_ edited-transaction]]
[::data-page/updated-entity ::page edited-transaction])}
{:id ::manual-import
:events #{::manual/import-completed}
:event-fn (fn [[_ result]]
[::status/info ::manual-import
(str "Successfully "
(str/join ", "
[(when-let [imported (:import-batch/imported result)]
(str "imported " imported))
(when-let [extant (:import-batch/extant result)]
(str "extant " extant))
(when-let [suppressed (:import-batch/suppressed result)]
(str "suppressed " suppressed))
(when-let [not-ready (:import-batch/not-ready result)]
(str "too early " not-ready))
(when-let [error (:validation-error result)]
(str "errored " error))])
" transactions."
(when (:sample-error result)
(str " Sample error: " (:info (:sample-error result)))))])}]})))
(defn action-buttons []
(let [is-admin? @(re-frame/subscribe [::subs/is-admin?])