Revert "Revert "Prep to select more than 1 client""

This reverts commit 574023fea0.
This commit is contained in:
Bryce Covert
2021-10-18 06:34:13 -07:00
parent 52d7eff1bd
commit d81096b8bc
4 changed files with 312 additions and 323 deletions

View File

@@ -554,6 +554,7 @@
:profit_and_loss {:type :profit_and_loss_report
:args {:client_id {:type :id}
:client_ids {:type '(list :id)}
:periods {:type '(list :date_range)}}
:resolve :get-profit-and-loss}

View File

@@ -183,9 +183,14 @@
(defn get-profit-and-loss [context args value]
(let [client-id (:client_id args)
_ (assert-can-see-client (:id context) client-id)
all-ledger-entries (full-ledger-for-client client-id)
lookup-account (build-account-lookup client-id)]
client-ids (or (some-> client-id vector)
(:client_ids args))
_ (when (not (seq client-ids))
(throw (ex-info "Please select a client." {:validation-error "Please select a client."})))
_ (doseq [client-id client-ids]
(assert-can-see-client (:id context) client-id))
all-ledger-entries (full-ledger-for-client (first client-ids))
lookup-account (build-account-lookup (first client-ids))]
(->graphql
{:periods (reduce (fn [acc {:keys [start end]}]
(conj acc

View File

@@ -7,8 +7,9 @@
[auto-ap.utils :refer [dollars-0? by ]]
[auto-ap.forms :as forms]
[auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]]
[auto-ap.views.components.typeahead :refer [typeahead-v3]]
[auto-ap.views.components.modal :as modal]
[auto-ap.views.utils :refer [date->str date-picker bind-field standard pretty dispatch-event local-today ->% ->$ str->date with-user dispatch-value-change query-params]]
[auto-ap.views.utils :refer [date->str date-picker date-picker-friendly bind-field standard pretty dispatch-event local-today ->% ->$ str->date with-user dispatch-value-change query-params]]
[cljs-time.core :as t]
[re-frame.core :as re-frame]
[auto-ap.status :as status]
@@ -44,9 +45,12 @@
;; SUBS
(re-frame/reg-sub
::locations
:<- [::forms/form ::form]
(fn [db]
(->> db
::report
:report
:periods
(mapcat :accounts)
(filter (comp in-range? :numeric-code))
@@ -63,11 +67,6 @@
"ZZZZZZ"
x))))))
(re-frame/reg-sub
::report
(fn [db]
(-> db ::report)))
(re-frame/reg-sub
::error
(fn [db]
@@ -81,9 +80,10 @@
(re-frame/reg-sub
::period-accounts
:<- [::forms/form ::form]
(fn [db [_ which type only-location]]
(->> (get-in db [::report :periods which :accounts])
(->> (get-in db [:report :periods which :accounts])
(map #(update % :amount js/parseFloat))
(filter (fn [{:keys [account-type location numeric-code]}]
(and (or (nil? only-location)
@@ -93,9 +93,10 @@
(re-frame/reg-sub
::uncategorized-accounts
:<- [::forms/form ::form]
(fn [db [_ ]]
(->> (get-in db [::report :periods ])
(->> (get-in db [:report :periods ])
(mapcat :accounts)
(map #(update % :amount js/parseFloat))
(filter (fn [{:keys [account-type location numeric-code]}]
@@ -121,6 +122,7 @@
(re-frame/reg-sub
::all-accounts
:<- [::forms/form ::form]
(fn [db [_ which type only-location]]
(transduce
(comp
@@ -129,12 +131,13 @@
conj
[]
(get-in db [::report :periods]))))
(get-in db [:report :periods]))))
(re-frame/reg-event-db
::received
[(forms/in-form ::form)]
(fn [db [_ data]]
(-> db (assoc ::report (:profit-and-loss data)))))
(-> db (assoc :report (:profit-and-loss data)))))
(re-frame/reg-sub
::params
@@ -159,68 +162,53 @@
(assoc db ::ledger-list-active? false)))
(re-frame/reg-event-fx
::mounted
(fn [{:keys [db]}]
(let [qp (query-params)]
(if (:periods qp)
(let [periods (mapv (fn [[start end title]]
[
(str->date start standard)
(str->date end standard)
title])
(:periods qp))]
{:dispatch [::range-selected periods (:include-deltas qp) nil]})
{:dispatch [::range-selected (and-last-year [(t/minus (local-today) (t/period :years 1)) (local-today)]) true nil]}))))
::params-change
[with-user (forms/in-form ::form)]
(fn [{:keys [db user] :as cofx}]
(let [c @(re-frame/subscribe [::subs/client])]
(cond-> {:graphql {:token user
:owns-state {:single ::page}
:query-obj {:venia/queries [[:profit-and-loss
{:client-ids (or (some-> (:id c) vector)
(some-> (:id (:client (:data db)))
vector))
:periods (mapv (fn [[start end] ] {:start (date->str start standard) :end (date->str end standard)} )
(:periods (:data db)))}
[[:periods [[:accounts [:name :amount :account-type :id :count :numeric-code :location]]]]]]]}
:on-success [::received]}
:set-uri-params {:periods (mapv (fn [[start end title]]
[(date->str start standard)
(date->str end standard)])
(:periods (:data db)))
:client (select-keys (:client (:data db))
[:name :id])}
:db (dissoc db :report)}))))
(re-frame/reg-event-db
::period-inputs-change
(fn [db [_ field value]]
(assoc-in db (into [::period-inputs ] field) value)))
::change
(forms/change-handler ::form
(fn [data field value]
(cond
(= [:periods] field)
[field (mapv (fn [period]
(mapv
(fn [dt]
(str->date dt standard))
period))
value)]
(re-frame/reg-event-fx
::params-change
(fn [cofx [_ params]]
(let [c @(re-frame/subscribe [::subs/client])]
(cond-> {:db (-> (:db cofx)
(assoc-in [::params] params)
(dissoc ::report))
:set-uri-params (update params
:periods
(fn [p]
(mapv (fn [[start end title]]
[(date->str start standard)
(date->str end standard)
title]
) p)))}
c (assoc :graphql (when @(re-frame/subscribe [::subs/client])
{:token (-> cofx :db :user)
:owns-state {:single ::page}
:query-obj {:venia/queries [[:profit-and-loss
{:client-id (:id c)
:periods (mapv (fn [[start end] ] {:start (date->str start standard) :end (date->str end standard)} )
(:periods params))}
[[:periods [[:accounts [:name :amount :account-type :id :count :numeric-code :location]]]]]]]}
:on-success [::received]}))))))
(and (= :periods (first field))
(= 2 (count field)))
[field value] ;;already serialized
(= :periods (first field)) [field (str->date value standard) ]
(re-frame/reg-event-fx
::date-picked
(fn [cofx [_ [_ period which] date]]
{:dispatch [::range-selected (assoc-in @(re-frame/subscribe [::periods]) [period which] (str->date date standard)) nil nil]}))
(re-frame/reg-event-fx
::range-selected
(fn [{:keys [db]} [_ periods include-deltas selected]]
{:dispatch [::params-change (cond-> @(re-frame/subscribe [::params])
true (assoc
:periods periods
:include-deltas include-deltas
:selected selected)
(not (nil? include-deltas)) (assoc :include-deltas include-deltas))]
:db (assoc db ::periods periods)}))
:else nil)
)))
(defn data-params->query-params [params]
@@ -288,6 +276,7 @@
:can-submit [::can-submit-period-form]
:id ::period-form}))
;; 877 w fremont ave suite i4
(defn change-period-body [idx which]
(let [{:keys [data active? error id]} @(re-frame/subscribe [::forms/form ::period-form])
{:keys [form-inline horizontal-field field raw-field error-notification submit-button]} change-period-form]
@@ -319,23 +308,23 @@
}] )])))
(re-frame/reg-event-fx
::period-change-submitted
(fn [{:keys [db]} [_ idx which]]
{ :db (-> db (forms/stop-form ::period-form)
(assoc-in [::periods idx] which))
(fn [{:keys [db]} [_ idx [start end] :as z]]
{ :db (-> db (forms/stop-form ::period-form))
:dispatch-n [[::modal/modal-closed ]
[::range-selected (assoc (::periods db) idx which) nil nil]]}))
[::change [:periods idx ] [start end]]]}))
(re-frame/reg-event-fx
::period-removed
(fn [{:keys [db]} [_ idx which]]
{ :db (-> db (forms/stop-form ::period-form)
(update ::periods (fn [ps]
(->> ps
(map vector (range) )
(filter (fn [[i _]]
(not= i idx)))
(map second)
(into [])))))
(update-in [::forms/forms ::form :data :periods]
(fn [ps]
(->> ps
(map vector (range) )
(filter (fn [[i _]]
(not= i idx)))
(map second)
(into [])))))
:dispatch [::modal/modal-closed ]}))
(defn change-period-foot [idx which]
@@ -609,249 +598,223 @@
]))
(re-frame/reg-sub
::can-submit
(fn [db]
true))
(def pnl-form (forms/vertical-form {:can-submit [::can-submit]
:change-event [::change]
:submit-event [::params-change]
:id ::form}))
(def profit-and-loss-content
(with-meta
(fn []
(let [current-client @(re-frame/subscribe [::subs/client])
user @(re-frame/subscribe [::subs/user])
status @(re-frame/subscribe [::status/single ::page])
params @(re-frame/subscribe [::params])
period-inputs @(re-frame/subscribe [::period-inputs])
periods @(re-frame/subscribe [::periods])
unresolved-accounts @(re-frame/subscribe [::uncategorized-accounts]) ]
(if-not current-client
[:div
[:h1.title "Profit and Loss " ]
[:h2.title.is-4 "Please choose a client first"]]
[:div
[:h1.title "Profit and Loss - " (:name current-client)]
[status/status-notification {:statuses [[::status/single ::page]]}]
[:div.report-controls
[:h2.title.is-4 "Range"]
[:div
[:div.field.is-grouped
[:div.control
[:div.field.has-addons
[:div.control
[bind-field
[date-picker {:class-name "input"
:class "input"
:format-week-number (fn [] "")
:previous-month-button-label ""
:placeholder "End date"
:next-month-button-label ""
:next-month-label ""
:type "date"
:field [:thirteen-periods-end]
:subscription period-inputs
:event [::period-inputs-change]}]]]
[:div.control
[:a.button
{:class (when (= (:selected params) "13 periods") "is-active")
:on-click (dispatch-event
[::range-selected
(let [today (or (some-> (:thirteen-periods-end period-inputs) (str->date standard))
(local-today))]
(into
[[(t/plus (t/minus today (t/weeks (* 13 4)))
(t/days 1))
today
"Total"]]
(for [i (range 13)]
[(t/plus (t/minus today (t/weeks (* (inc i) 4)))
(t/days 1))
(t/minus today (t/weeks (* i 4)))])))
false
"13 periods"])}
"13 periods"]]]]
(defn profit-and-loss-content []
(let [current-client @(re-frame/subscribe [::subs/client])
clients @(re-frame/subscribe [::subs/clients])
user @(re-frame/subscribe [::subs/user])
status @(re-frame/subscribe [::status/single ::page])
params @(re-frame/subscribe [::params])
unresolved-accounts @(re-frame/subscribe [::uncategorized-accounts])
{:keys [data report active? error id]} @(re-frame/subscribe [::forms/form ::form])
{:keys [form-inline field raw-field error-notification submit-button ]} pnl-form
periods (:periods data)]
(when data
(form-inline {}
[:div
[:h1.title "Profit and Loss - " (:name current-client)]
[status/status-notification {:statuses [[::status/single ::page]]}]
[:div.report-controls
(when-not current-client
[:<>
[:h2.title.is-4 "Entities"]
(raw-field
[typeahead-v3 {:entities @(re-frame/subscribe [::subs/clients])
:entity->text :name
:type "typeahead-v3"
:field [:client]}])])
[:h2.title.is-4 "Range"]
[:div
[:div.field.is-grouped
[:div.control
[:div.field.has-addons
[:div.control
(raw-field
[date-picker-friendly {:placeholder "End date"
:type "date"
:field [:thirteen-periods-end]}])]
[:div.control
[:a.button
{:class (when (= (:selected params) "13 periods") "is-active")
:on-click (dispatch-event
[::forms/change ::form
[:periods]
(let [today (or (some-> (:thirteen-periods-end data) (str->date standard))
(local-today))]
(into
[[(t/plus (t/minus today (t/weeks (* 13 4)))
(t/days 1))
today
"Total"]]
(for [i (range 13)]
[(t/plus (t/minus today (t/weeks (* (inc i) 4)))
(t/days 1))
(t/minus today (t/weeks (* i 4)))])))])}
"13 periods"]]]]
[:div.control
[:div.field.has-addons
[:div.control
[bind-field
[date-picker {:class-name "input"
:class "input"
:format-week-number (fn [] "")
:previous-month-button-label ""
:placeholder "End date"
:next-month-button-label ""
:next-month-label ""
:type "date"
:field [:twelve-periods-end]
:subscription period-inputs
:event [::period-inputs-change]}]]]
[:div.control
[:a.button
{:class (when (= (:selected params) "13 periods") "is-active")
:on-click (dispatch-event
[::range-selected
(let [end-date (or (some-> (:twelve-periods-end period-inputs) (str->date standard))
(local-today))
this-month (t/local-date (t/year end-date)
(t/month end-date)
1)
]
(into
[[(t/minus this-month (t/months 11))
(t/minus (t/plus this-month (t/months 1))
(t/days 1))
"Total"]]
(for [i (range 12)]
[(t/minus this-month (t/months (- 11 i)))
(t/minus (t/minus this-month (t/months (- 10 i)))
(t/days 1))])))
false
"12 months"]
)}
"12 months"]]]]
[:div.control
[:div.field.has-addons
[:div.control
(raw-field
[date-picker-friendly {:placeholder "End date"
:type "date"
:field [:twelve-periods-end]}])
]
[:div.control
[:a.button
{:class (when (= (:selected params) "13 periods") "is-active")
:on-click (dispatch-event
[::forms/change ::form
[:periods]
(let [end-date (or (some-> (:twelve-periods-end data) (str->date standard))
(local-today))
this-month (t/local-date (t/year end-date)
(t/month end-date)
1)
]
(into
[[(t/minus this-month (t/months 11))
(t/minus (t/plus this-month (t/months 1))
(t/days 1))
"Total"]]
(for [i (range 12)]
[(t/minus this-month (t/months (- 11 i)))
(t/minus (t/minus this-month (t/months (- 10 i)))
(t/days 1))])))])}
"12 months"]]]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Last week") "is-active")
:on-click (dispatch-event
(let [last-sunday (loop [current (local-today)]
(if (= 7 (t/day-of-week current))
current
(recur (t/minus current (t/period :days 1)))))]
[::range-selected
(and-last-year [(t/minus last-sunday (t/period :days 6)) last-sunday])
true
"Last week"]))}
"Last week"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Week to date") "is-active")
:on-click (dispatch-event [::range-selected
(and-last-year [(loop [current (local-today)]
(if (= 1 (t/day-of-week current))
current
(recur (t/minus current (t/period :days 1)))))
(local-today)])
true
"Week to date"])}
"Week to date"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Last Month") "is-active")
:on-click (dispatch-event [::range-selected
(and-last-year [(t/minus (t/local-date (t/year (local-today))
(t/month (local-today))
1)
(t/period :months 1))
(t/minus (t/local-date (t/year (local-today))
(t/month (local-today))
1)
(t/period :days 1))])
true
"Last Month"])}
"Last Month"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Month to date") "is-active")
:on-click (dispatch-event [::range-selected
(and-last-year [(t/local-date (t/year (local-today))
(t/month (local-today))
1)
(local-today)])
true
"Month to date"])}
"Month to date"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Year to date") "is-active")
:on-click (dispatch-event [::range-selected
(and-last-year [(t/local-date (t/year (local-today)) 1 1)
(local-today)])
true
"Year to date"])}
"Year to date"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Full year") "is-active")
:on-click (dispatch-event [::range-selected
(and-last-year [(t/plus (t/minus (local-today) (t/period :years 1))
(t/period :days 1))
(local-today)])
true
"Full year"])}
"Full year"]]]]
[:div
[:div.field
[:label.checkbox
[bind-field
[:input {:type "checkbox"
:field [:show-advanced?]
:event [::period-inputs-change]
:subscription period-inputs}]]
" Show Advanced"]]]
(when (:show-advanced? period-inputs)
(for [[_ i] (map vector periods (range))]
^{:key i}
[:div.field.is-grouped
[:div.control
[:p.help "From"]
[bind-field
[date-picker {:class-name "input"
:class "input"
:format-week-number (fn [] "")
:previous-month-button-label ""
:placeholder "mm/dd/yyyy"
:next-month-button-label ""
:next-month-label ""
:type "date"
:field [:periods i 0]
:event [::date-picked]
:popper-props (clj->js {:placement "right"})
:subscription params}]]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Last week") "is-active")
:on-click (dispatch-event
[::forms/change ::form
[:periods]
(let [last-sunday (loop [current (local-today)]
(if (= 7 (t/day-of-week current))
current
(recur (t/minus current (t/period :days 1)))))]
(and-last-year [(t/minus last-sunday (t/period :days 6)) last-sunday]))])}
"Last week"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Week to date") "is-active")
:on-click (dispatch-event
[::forms/change ::form
[:periods]
(and-last-year [(loop [current (local-today)]
(if (= 1 (t/day-of-week current))
current
(recur (t/minus current (t/period :days 1)))))
(local-today)])])}
"Week to date"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Last Month") "is-active")
:on-click (dispatch-event
[:div.control
[:p.help "To"]
[bind-field
[date-picker {:class-name "input"
:class "input"
:format-week-number (fn [] "")
:previous-month-button-label ""
:placeholder "mm/dd/yyyy"
:next-month-button-label ""
:next-month-label ""
:type "date"
:field [:periods i 1]
:event [::date-picked]
:popper-props (clj->js {:placement "right"})
:subscription params}]]]]))]
[status/big-loader status]
(when (not= :loading (:state status))
[:div
(when (seq unresolved-accounts)
[:div.notification.is-warning.is-light
"This report does not include " (str/join ", "
(map #(str (:count %) " unresolved ledger entries for " (if (str/blank? (:location %))
" all locations"
(:location %)))
unresolved-accounts))])
[:<>
(for [location @(re-frame/subscribe [::locations])]
^{:key (str location "-summary")}
[location-summary location params]
)]
[:h2.title.is-4 {:style {:margin-bottom "1rem"}} "Detail"]
[:table.table.compact.balance-sheet
[:tbody
[period-header {:include-deltas (:include-deltas params)
:periods periods}]
[:<>
(for [location @(re-frame/subscribe [::locations])]
^{:key location}
[location-rows location]
)]]]])])))
{:component-will-mount #(re-frame/dispatch [::mounted]) }))
[::forms/change ::form
[:periods]
(and-last-year [(t/minus (t/local-date (t/year (local-today))
(t/month (local-today))
1)
(t/period :months 1))
(t/minus (t/local-date (t/year (local-today))
(t/month (local-today))
1)
(t/period :days 1))])])}
"Last Month"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Month to date") "is-active")
:on-click (dispatch-event
[::forms/change ::form
[:periods]
(and-last-year [(t/local-date (t/year (local-today))
(t/month (local-today))
1)
(local-today)])]
)}
"Month to date"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Year to date") "is-active")
:on-click (dispatch-event
[::forms/change ::form
[:periods]
(and-last-year [(t/local-date (t/year (local-today)) 1 1)
(local-today)])])}
"Year to date"]]
[:div.control
[:a.button
{:class (when (= (:selected params) "Full year") "is-active")
:on-click (dispatch-event
[::forms/change ::form
[:periods]
(and-last-year [(t/plus (t/minus (local-today) (t/period :years 1))
(t/period :days 1))
(local-today)])])}
"Full year"]]]]
[:div
[:div.field
[:label.checkbox
(raw-field
[:input {:type "checkbox"
:field [:show-advanced?]}])
" Show Advanced"]]]
(when (:show-advanced? data)
(doall
(for [[_ i] (map vector periods (range))]
^{:key i}
[:div.field.is-grouped
[:div.control
[:p.help "From"]
(raw-field
[date-picker-friendly {:type "date"
:field [:periods i 0]}])]
[:div.control
[:p.help "To"]
(raw-field
[date-picker-friendly {:type "date"
:field [:periods i 1]}])]])))
(submit-button "Run")]
[status/big-loader status]
(when (and (not= :loading (:state status))
report)
[:div
(when (seq unresolved-accounts)
[:div.notification.is-warning.is-light
"This report does not include " (str/join ", "
(map #(str (:count %) " unresolved ledger entries for " (if (str/blank? (:location %))
" all locations"
(:location %)))
unresolved-accounts))])
[:<>
(for [location @(re-frame/subscribe [::locations])]
^{:key (str location "-summary")}
[location-summary location params]
)]
[:h2.title.is-4 {:style {:margin-bottom "1rem"}} "Detail"]
[:table.table.compact.balance-sheet
[:tbody
[period-header {:include-deltas (:include-deltas params)
:periods periods}]
[:<>
(for [location @(re-frame/subscribe [::locations])]
^{:key location}
[location-rows location])]]]])]))))
(re-frame/reg-event-fx
::unmounted-pnl
@@ -862,9 +825,18 @@
(re-frame/reg-event-fx
::mounted-pnl
(fn [{:keys [db]} _]
{::track/register {:id ::ledger-params
:subscription [::data-page/params ::ledger]
:event-fn (fn [params] [::ledger-params-change params])}}))
(let [qp (query-params)]
{:db (forms/start-form db ::form {:periods (->> qp
:periods
(mapv (fn [period]
(mapv
(fn [dt]
(str->date dt standard))
period))))
:client (:client qp)})
::track/register {:id ::ledger-params
:subscription [::data-page/params ::ledger]
:event-fn (fn [params] [::ledger-params-change params])}})))
(defn ledger-list [_ ]
[:div [:a.delete.is-pulled-right {:on-click (dispatch-event [::ledger-list-closing])}]

View File

@@ -435,6 +435,17 @@
(do
(reagent/adapt-react-class (.-default react-datepicker))))
(defn date-picker-friendly [params]
[date-picker (assoc params
:class-name "input"
:class "input"
:format-week-number (fn [] "")
:previous-month-button-label ""
:next-month-button-label ""
:next-month-label ""
:type "date")])
(defn local-now []
(t/to-default-time-zone (t/now)))