diff --git a/hours.txt b/hours.txt new file mode 100644 index 00000000..b014e5c7 --- /dev/null +++ b/hours.txt @@ -0,0 +1 @@ +7/16/2020 4 hours diff --git a/src/clj/auto_ap/datomic/ledger.clj b/src/clj/auto_ap/datomic/ledger.clj index 43c9c68d..00c9a5f3 100644 --- a/src/clj/auto_ap/datomic/ledger.clj +++ b/src/clj/auto_ap/datomic/ledger.clj @@ -82,7 +82,7 @@ '[(<= ?c ?to-numeric-code)]]} :args [(:to-numeric-code args)]}) - (:location args) + (not-empty (:location args)) (merge-query {:query {:in ['?location] :where ['[?e :journal-entry/line-items ?li] '[?li :journal-entry-line/location ?location]]} diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 1dba394f..5e0ab040 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -115,6 +115,12 @@ {:fields {:balance_sheet_accounts {:type '(list :balance_sheet_account)} :comparable_balance_sheet_accounts {:type '(list :balance_sheet_account)}}} + :profit_and_loss_report_period + {:fields {:accounts {:type '(list :balance_sheet_account)}}} + + :profit_and_loss_report + {:fields {:periods {:type '(list :profit_and_loss_report_period)}}} + :address {:fields {:street1 {:type 'String} :street2 {:type 'String} @@ -404,9 +410,9 @@ :date {:type :iso_date}} :resolve :get-balance-sheet} - :profit_and_loss {:type :balance_sheet + :profit_and_loss {:type :profit_and_loss_report :args {:client_id {:type :id} - :date_range {:type :date_range}} + :periods {:type '(list :date_range)}} :resolve :get-profit-and-loss} diff --git a/src/clj/auto_ap/graphql/ledger.clj b/src/clj/auto_ap/graphql/ledger.clj index ab14371d..fcd5b409 100644 --- a/src/clj/auto_ap/graphql/ledger.clj +++ b/src/clj/auto_ap/graphql/ledger.clj @@ -106,12 +106,15 @@ vals (mapcat (fn [a] (map (fn [o] - [[[(:db/id a) (:db/id (:account-client-override/client o))]] + #_(println "override" (:db/id a) (:db/id (:account-client-override/client o))) + #_(print [[(:db/id a) (:db/id (:account-client-override/client o))]]) + [[(:db/id a) (:db/id (:account-client-override/client o))] (:account-client-override/name o)]) (:account/client-overrides a)) ) ) (into {} ))] (fn [a] + #_(println a client-id (keys overrides-by-client)) {:name (or (:bank-account/name (bank-accounts a)) (overrides-by-client [a client-id]) (:account/name (accounts a))) @@ -155,16 +158,16 @@ (defn get-profit-and-loss [context args value] (let [client-id (:client_id args) _ (assert-can-see-client (:id context) client-id) - start-date (coerce/to-date (:start (:date_range args))) - end-date (coerce/to-date (:end (:date_range args))) - comparable-start-date (coerce/to-date (time/minus (:start (:date_range args)) (time/years 1))) - comparable-end-date (coerce/to-date (time/minus (:end (:date_range args)) (time/years 1))) + _ (println args) all-ledger-entries (full-ledger-for-client client-id) lookup-account (build-account-lookup client-id)] (->graphql - {:balance-sheet-accounts (roll-up-until lookup-account all-ledger-entries end-date start-date ) - :comparable-balance-sheet-accounts (roll-up-until lookup-account all-ledger-entries comparable-end-date comparable-start-date )}))) + {:periods (reduce (fn [acc {:keys [start end]}] + (conj acc + {:accounts (roll-up-until lookup-account all-ledger-entries (coerce/to-date end) (coerce/to-date start) )})) + [] + (:periods args))}))) (defn assoc-error [f] diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index c9769cff..7e8c459b 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -77,10 +77,11 @@ (re-frame/reg-sub ::accounts-by-id - :<- [::accounts] - :<- [::client] - (fn [[accounts client] [_ client-override]] - (accounts-by-id accounts (or client-override client)))) + (fn [[_ client-override]] + [(re-frame/subscribe [::accounts client-override]) + (re-frame/subscribe [::client])]) + (fn [[accounts client] ] + (accounts-by-id accounts client))) (re-frame/reg-sub ::bank-accounts diff --git a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs index 189efb51..e2b95314 100644 --- a/src/cljs/auto_ap/views/components/expense_accounts_field.cljs +++ b/src/cljs/auto_ap/views/components/expense_accounts_field.cljs @@ -199,25 +199,25 @@ [:option "%"]]]]] [:p.control (if (= "$" amount-mode) - [bind-field - [:input.input {:type "number" - :field [index :amount] - :style {:text-align "right" :width "7em"} - :event [::expense-account-changed event expense-accounts max-value] - :disabled disabled - :subscription expense-accounts - :precision 2 - :value (get-in expense-account [:amount]) - :max max-value - :step "0.01"}]] - [bind-field - [:input.input {:type "number" - :field [index :amount-percentage] - :style {:text-align "right" :width "7em"} - :disabled disabled - :event [::expense-account-changed event expense-accounts max-value] - :precision 2 - :subscription expense-accounts - :value (get-in expense-account [:amount-percentage]) - :max "100" - :step "0.01"}]])]]]]])])) + [bind-field + [:input.input {:type "number" + :field [index :amount] + :style {:text-align "right" :width "7em"} + :event [::expense-account-changed event expense-accounts max-value] + :disabled disabled + :subscription expense-accounts + :precision 2 + :value (get-in expense-account [:amount]) + :max max-value + :step "0.01"}]] + [bind-field + [:input.input {:type "number" + :field [index :amount-percentage] + :style {:text-align "right" :width "7em"} + :disabled disabled + :event [::expense-account-changed event expense-accounts max-value] + :precision 2 + :subscription expense-accounts + :value (get-in expense-account [:amount-percentage]) + :max "100" + :step "0.01"}]])]]]]])])) diff --git a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs index 960b8c46..fbb2931e 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs @@ -137,7 +137,7 @@ (re-frame/reg-event-db ::editing (fn [db [_ which]] - (let [accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id (:client which)])] + (let [accounts-by-id @(re-frame/subscribe [::subs/all-accounts-by-id (:client which)])] (-> db (forms/start-form ::form (-> which (select-keys [:description :id @@ -155,7 +155,8 @@ (update :accounts (fn [xs] (mapv #(-> % (assoc :amount-percentage (* (:percentage %) 100.0)) - (update :account (fn [a] (accounts-by-id (:id a))))) + (update :account (fn [a] + (accounts-by-id (:id a))))) xs))))))))) diff --git a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs index 9ff010cf..4e80dec6 100644 --- a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs @@ -10,7 +10,7 @@ [cljs-time.core :as t] [re-frame.core :as re-frame])) (def ranges - {:sales [40000 48999] + {:sales [40000 49999] :cogs [50000 59999] :payroll [60000 69999] :controllable [70000 79999] @@ -23,12 +23,10 @@ (fn [db] (->> db ::report - :balance-sheet-accounts + :periods + (mapcat :accounts) (map :location) - (concat (->> db - ::report - :comparable-balance-sheet-accounts - (map :location))) + (map not-empty) (filter #(not= "A" %)) (filter identity) (set) @@ -93,11 +91,10 @@ params))) (re-frame/reg-sub - ::accounts - (fn [db [_ type only-location]] - (->> db - ::report - :balance-sheet-accounts + ::period-accounts + (fn [db [_ which type only-location]] + + (->> (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) @@ -105,68 +102,35 @@ (<= (get-in ranges [type 0]) numeric-code (get-in ranges [type 1]))))) (sort-by :numeric-code)))) -(re-frame/reg-sub - ::amount - (fn [[_ type only-location]] - [(re-frame/subscribe [::accounts type only-location])]) - (fn [[accounts] _] - (reduce + 0 (map :amount accounts)))) +(defn parse-amounts [period] + (update period :accounts (fn [a] + (map #(update % :amount js/parseFloat) a)))) + + +(defn filter-accounts [accounts period [from to] only-location] + (->> (get accounts period) + vals + + (filter (fn [{:keys [location numeric-code]}] + (and (or (nil? only-location) + (= only-location location)) + (<= from numeric-code to)))) + (sort-by :numeric-code))) + +(defn aggregate-accounts [accounts] + (reduce (fnil + 0.0) 0.0 (map :amount accounts))) (re-frame/reg-sub - ::comparable-amount - (fn [[_ type only-location]] - [(re-frame/subscribe [::comparable-accounts-by-id type only-location])]) - (fn [[accounts] _] - (reduce + 0 (map :amount (vals accounts))))) + ::all-accounts + (fn [db [_ which type only-location]] + (transduce + (comp + (map parse-amounts) + (map #(by (juxt :numeric-code :location) (:accounts %)))) -(re-frame/reg-sub - ::percent-of-sales - (fn [[_ type only-location]] - [(re-frame/subscribe [::amount :sales only-location]) - (re-frame/subscribe [::amount type only-location])]) - (fn [[sales accounts] _] - (if (> (or sales 0) 0 ) - (/ accounts sales) - 0.0))) - -(re-frame/reg-sub - ::comparable-percent-of-sales - (fn [[_ type only-location]] - [(re-frame/subscribe [::comparable-amount :sales only-location]) - (re-frame/subscribe [::comparable-amount type only-location])]) - (fn [[sales accounts] _] - (if (> (or sales 0) 0 ) - (/ accounts sales) - 0.0))) - - - - -(re-frame/reg-sub - ::accounts-by-id - (fn [db [_ type only-location]] - (->> db - ::report - :balance-sheet-accounts - (map #(update % :amount js/parseFloat)) - (filter (fn [{:keys [account-type location numeric-code]}] - (and (or (nil? only-location) - (= only-location location)) - (<= (get-in ranges [type 0]) numeric-code (get-in ranges [type 1]))))) - (by :id)))) - -(re-frame/reg-sub - ::comparable-accounts-by-id - (fn [db [_ type only-location]] - (->> db - ::report - :comparable-balance-sheet-accounts - (map #(update % :amount js/parseFloat)) - (filter (fn [{:keys [account-type location numeric-code]}] - (and (or (nil? only-location) - (= only-location location)) - (<= (get-in ranges [type 0]) numeric-code (get-in ranges [type 1]))))) - (by :id)))) + conj + [] + (get-in db [::report :periods])))) (re-frame/reg-event-db ::received @@ -180,6 +144,11 @@ (fn [db] (-> db ::params))) +(re-frame/reg-sub + ::periods + (fn [db] + (::periods db))) + ;; EVENTS (re-frame/reg-event-db @@ -203,6 +172,7 @@ (re-frame/reg-event-fx ::params-change (fn [cofx [_ params]] + (println params) (let [c @(re-frame/subscribe [::subs/client])] (cond-> {:db (-> (:db cofx) (assoc-in [::error] nil) @@ -212,10 +182,9 @@ {:token (-> cofx :db :user) :query-obj {:venia/queries [[:profit-and-loss {:client-id (:id c) - :date-range {:start (:from-date params) - :end (:to-date params)}} - [[:balance-sheet-accounts [:name :amount :account-type :id :numeric-code :location]] - [:comparable-balance-sheet-accounts [:name :amount :account-type :id :numeric-code :location]]]]]} + :periods (mapv (fn [[start end] ] {:start (date->str start standard) :end (date->str end standard)} ) + (:periods params))} + [[:periods [[:accounts [:name :amount :account-type :id :numeric-code :location]]]]]]]} :on-success [::received] :on-error [::error]})))))) @@ -225,18 +194,16 @@ (re-frame/reg-event-fx ::date-picked - (fn [cofx [_ f date]] - {:dispatch [::params-change (assoc-in @(re-frame/subscribe [::params]) - f - date)]})) + (fn [cofx [_ [_ period which] date]] + {:dispatch [::range-selected (assoc-in @(re-frame/subscribe [::periods]) [period which] (str->date date standard)) nil]})) (re-frame/reg-event-fx ::range-selected - (fn [cofx [_ from to selected]] + (fn [{:keys [db]} [_ periods selected]] {:dispatch [::params-change (assoc @(re-frame/subscribe [::params]) - :from-date from - :to-date to - :selected selected)]})) + :periods periods + :selected selected)] + :db (assoc db ::periods periods)})) (re-frame/reg-event-fx @@ -244,47 +211,43 @@ [with-user (re-frame/inject-cofx ::inject/sub [::ledger-params])] (fn [{:keys [user ::ledger-params db]} [_ ]] - {:db (assoc db - ::ledger-list-loading true - ::last-ledger-params ledger-params) - :graphql {:token user - :query-obj {:venia/queries [[:ledger-page - ledger-params - [[:journal-entries [:id - :source - :amount - [:vendor - [:name :id]] - [:client - [:name :id]] - [:line-items - [:id :debit :credit :location - [:account [:id :name]]]] - :date]] - :total - :start - :end]]]} - :on-success [::ledger-list-received]}})) + (if (seq ledger-params) + {:db (assoc db + ::ledger-list-loading true + ::last-ledger-params ledger-params) + :graphql {:token user + :query-obj {:venia/queries [[:ledger-page + ledger-params + [[:journal-entries [:id + :source + :amount + [:vendor + [:name :id]] + [:client + [:name :id]] + [:line-items + [:id :debit :credit :location + [:account [:id :name]]]] + :date]] + :total + :start + :end]]]} + :on-success [::ledger-list-received]}}))) (re-frame/reg-event-db ::investigate-clicked (fn [db [_ location from-numeric-code to-numeric-code which]] - (-> db - (assoc - ::ledger-list-active? true - ::ledger-list-loading true - ::investigate-ledger-params {:client-id (:id @(re-frame/subscribe [::subs/client])) - :from-numeric-code from-numeric-code - :to-numeric-code to-numeric-code - :location location - :date-range {:start (if (= :current which) - (:from-date (::params db)) - (date->str (t/minus (str->date (:from-date (::params db)) standard) (t/years 1)) - standard)) - :end (if (= :current which) - (:to-date (::params db)) - (date->str (t/minus (str->date (:to-date (::params db)) standard) (t/years 1)) - standard))}})))) + (let [[from to] (get @(re-frame/subscribe [::periods]) which)] + (-> db + (assoc + ::ledger-list-active? true + ::ledger-list-loading true + ::investigate-ledger-params {:client-id (:id @(re-frame/subscribe [::subs/client])) + :from-numeric-code from-numeric-code + :to-numeric-code to-numeric-code + :location location + :date-range {:start (date->str from standard) + :end (date->str to standard)}}))))) (def groupings {:sales [["40000-43999 Food Sales " 40000 43999] @@ -323,133 +286,115 @@ ["97000 Taxes" 97000 97999] ["98000 Other Expenses" 98000 98999]]}) +(defn percent-of-sales [amount accounts which location] + (let [sales (aggregate-accounts (filter-accounts accounts which (get ranges :sales) location))] + (if (not (dollars-0? sales)) + (/ amount + sales) + 0.0))) + +(defn used-accounts [accounts [from to] location] + (->> accounts + (mapcat vals) + (filter #(<= from (:numeric-code %) to)) + (filter #(= location (:location %))) + (map #(select-keys % [:numeric-code :name])) + (set) + (sort-by :numeric-code))) -(defn grouping [{:keys [header accounts comparable-accounts groupings location sales comparable-sales]}] - [:<> - (for [[grouping-name from to] groupings - :let [matching-accounts (filter - #(<= from (:numeric-code %) to) - accounts) - total (reduce + 0 (map :amount matching-accounts)) - comparable-total (reduce + 0 (map #(:amount (get comparable-accounts (:id %)) 0) matching-accounts))] - :when (seq matching-accounts) - ] - ^{:key grouping-name} - [:<> - [:tr [:td "---" grouping-name "---"] - [:td] - [:td] - [:td] - [:td] - [:td] - ] - [:<> - (for [account matching-accounts] - ^{:key (:name account)} - [:tr [:td (:name account)] - [:td.has-text-right [:a {:on-click (dispatch-event [::investigate-clicked location (:numeric-code account) (:numeric-code account) :current])} - (->$ (:amount account))]] - [:td.has-text-right (->% (if (> sales 0) - (/ (:amount account) sales) - 0.0))] - [:td.has-text-right [:a {:on-click (dispatch-event [::investigate-clicked location (:numeric-code account) (:numeric-code account) :comparable])} - (->$ (:amount (get comparable-accounts (:id account)) 0))]] - [:td.has-text-right (->% (if (> comparable-sales 0) - (/ (:amount (get comparable-accounts (:id account)) 0) sales) - 0.0))] - [:td.has-text-right (->$ (- (:amount account ) (:amount (get comparable-accounts (:id account)) 0)))]])] +(defn grouping [{:keys [header type groupings location]}] + (let [periods @(re-frame/subscribe [::periods]) + all-accounts @(re-frame/subscribe [::all-accounts])] + [:<> + (doall + (for [[grouping-name from to] groupings + :let [account-codes (used-accounts all-accounts [from to] location)] + :when (seq account-codes)] + ^{:key grouping-name} + [:<> + [:tr [:td "---" grouping-name "---"] + (for [[_ i] (map vector periods (range))] + [:<> + [:td] + [:td] + (when (not= 0 i) + [:td])])] + [:<> + (for [{:keys [numeric-code name]} account-codes] + ^{:key numeric-code} + [:tr [:td name] + (for [[p i] (map vector periods (range)) + :let [amount (get-in all-accounts [i [numeric-code location] :amount] 0.0)]] + [:<> + [:td.has-text-right [:a {:on-click (dispatch-event [::investigate-clicked location numeric-code numeric-code i :current])} + (->$ amount)]] + [:td.has-text-right (->% (percent-of-sales amount all-accounts i location))] + (when (not= 0 i) + [:td.has-text-right (->$ (- amount + (get-in all-accounts [(dec i) [numeric-code location] :amount] 0.0)))])])])] + + [:tr [:th ] + (for [[p i] (map vector periods (range)) + :let [amount (aggregate-accounts (filter-accounts all-accounts i [from to] location))]] + [:<> + [:th.has-text-right.total [:a {:on-click (dispatch-event [::investigate-clicked location from to i])} + (->$ amount)]] + [:th.has-text-right.total (->% (percent-of-sales amount all-accounts i location))] + (when (not= 0 i) + [:th.has-text-right.total (->$ (- amount + (aggregate-accounts (filter-accounts all-accounts (dec i) [from to] location))))])])]]))])) + - [:tr [:th ] - [:th.has-text-right.total [:a - {:on-click (dispatch-event [::investigate-clicked location from to :current])} - (->$ total)] ] - [:th.has-text-right.total (->% (if (> sales 0) - (/ total sales) - 0.0))] - [:th.has-text-right.total [:a - {:on-click (dispatch-event [::investigate-clicked location from to :comparable])} - (->$ comparable-total)]] - [:th.has-text-right.total (->% (if (> comparable-sales 0) - (/ comparable-total sales) - 0.0))] - [:th.has-text-right.total (->$ (- total comparable-total))] - [:td] - ] - [:tr [:td]]])]) (defn overall-grouping [type title location] - (let [accounts @(re-frame/subscribe [::accounts type location]) - min-numeric-code (or (first (map :numeric-code accounts)) 0) - max-numeric-code (or (last (map :numeric-code accounts)) 0)] + (let [all-accounts @(re-frame/subscribe [::all-accounts]) + periods @(re-frame/subscribe [::periods]) + [min-numeric-code max-numeric-code] (ranges type)] + [:<> - [:tr [:th.is-size-5 title] - [:td] - [:td] - [:td]] + [:tr [:th.is-size-5 title]] - [grouping {:accounts accounts - :location location - :groupings (type groupings) - :comparable-accounts @(re-frame/subscribe [::comparable-accounts-by-id type location]) - :sales @(re-frame/subscribe [::amount :sales location]) - :comparable-sales @(re-frame/subscribe [::comparable-amount :sales location])}] + [grouping {:location location + :groupings (type groupings)}] [:tr [:th.is-size-5 title] - [:th.has-text-right [:a - {:on-click (dispatch-event [::investigate-clicked location min-numeric-code max-numeric-code :current])} - (->$ @(re-frame/subscribe [::amount type location]))]] - [:th.has-text-right (->% @(re-frame/subscribe [::percent-of-sales type location]))] - [:th.has-text-right [:a - {:on-click (dispatch-event [::investigate-clicked location min-numeric-code max-numeric-code :comparable])} - (->$ @(re-frame/subscribe [::comparable-amount type location]))]] - [:th.has-text-right (->% @(re-frame/subscribe [::comparable-percent-of-sales type location]))] - [:th.has-text-right (->$ (- @(re-frame/subscribe [::amount type location]) - @(re-frame/subscribe [::comparable-amount type location])))]]])) + (for [[i p] (map vector (range) periods) + :let [amount (aggregate-accounts (filter-accounts all-accounts i [min-numeric-code max-numeric-code] location))]] + + [:<> + [:th.has-text-right [:a + {:on-click (dispatch-event [::investigate-clicked location min-numeric-code max-numeric-code i])} + (->$ amount)]] + [:th.has-text-right (->% (percent-of-sales amount all-accounts i location))] + (when (not= 0 i) + [:th.has-text-right (->$ (- amount + (aggregate-accounts (filter-accounts all-accounts (dec i) [min-numeric-code max-numeric-code] location))))])])]])) (defn subtotal [types negs title location] - (let [accounts (transduce (comp - (map #(map (fn [a] - (if (negs %) - (update a :amount -) - a)) - (deref (re-frame/subscribe [::accounts % location]))))) - into - [] - types) - comparable (transduce - (comp - (map #(map (fn [a] - (if (negs %) - (update a :amount -) - a)) - (vals (deref (re-frame/subscribe [::comparable-accounts-by-id % location])))))) - into - [] - types) - min-numeric-code (or (first (map :numeric-code accounts)) 0) - max-numeric-code (or (last (map :numeric-code accounts)) 0) - sales @(re-frame/subscribe [::amount :sales location]) - comparable-sales @(re-frame/subscribe [::comparable-amount :sales location])] + (let [all-accounts @(re-frame/subscribe [::all-accounts]) + periods @(re-frame/subscribe [::periods])] [:tr [:th.is-size-5 title] - [:td.has-text-right [:a - {:on-click (dispatch-event [::investigate-clicked location min-numeric-code max-numeric-code :current])} - (->$ (reduce + 0 (map :amount accounts)))]] - [:td.has-text-right (->% (if (> sales 0) - (/ (reduce + 0 (map :amount accounts)) - sales) - 0))] - [:td.has-text-right [:a - {:on-click (dispatch-event [::investigate-clicked location min-numeric-code max-numeric-code :comparable])} - (->$ (reduce + 0 (map :amount comparable)))]] - [:td.has-text-right (->% (if (> comparable-sales 0) - (/ (reduce + 0 (map :amount comparable)) - comparable-sales) - 0))] - [:td.has-text-right (->$ (- (reduce + 0 (map :amount accounts)) - (reduce + 0 (map :amount comparable))))]])) + + (for [[p i] (map vector periods (range)) + :let [amount (aggregate-accounts (mapcat (fn [t] + (cond->> (filter-accounts all-accounts i (ranges t) location) + (negs t) (map #(update % :amount -)))) + types))]] + [:<> + [:td.has-text-right [:span (->$ amount)]] + [:td.has-text-right (->% (percent-of-sales amount all-accounts i location))] + (when (not= 0 i) + [:td.has-text-right (->$ (- amount + (aggregate-accounts (mapcat (fn [t] + (cond->> (filter-accounts all-accounts (dec i) (ranges t) location) + (negs t) (map #(update % :amount -)))) + types))))] + ) + ])])) (defn location-rows [location] + [:<> [overall-grouping :sales (str location " Sales") location] [overall-grouping :cogs (str location " COGS") location] @@ -464,26 +409,32 @@ [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} "Net Income" nil]]) (defn location-summary [location params] - [:div - [:h2.title.is-4 {:style {:margin-bottom "1rem"}} location " Summary"] - [:table.table.compact.balance-sheet {:style {:margin-bottom "2.5rem"}} - [:tbody - [:tr - [:td.has-text-right "Period ending"] - [:td.has-text-right (date->str (str->date (:to-date params) standard))] - [:td] - [:td.has-text-right (when (:to-date params) - (date->str (t/minus (str->date (:to-date params) standard) (t/years 1))))] - [:td] - [:td]] - [subtotal [:sales ] #{} "Sales" location] - [subtotal [:cogs ] #{} "Cogs" location] - [subtotal [:payroll ]#{} "Payroll" location] - [subtotal [:sales :payroll :cogs] #{:payroll :cogs} "Gross Profits" location] - [subtotal [:controllable :fixed-overhead :ownership-controllable] #{} "Overhead" location] - [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} "Net Income" location]]] - - ]) + (let [periods @(re-frame/subscribe [::periods])] + [:div + [:h2.title.is-4 {:style {:margin-bottom "1rem"}} location " Summary"] + [:table.table.compact.balance-sheet {:style {:margin-bottom "2.5rem"}} + [:tbody + [:tr + [:td.has-text-right "Period ending"] + (for [[[_ date] i] (map vector periods (range))] + [:<> + [:td.has-text-right (when date + (date->str date))] + [:td] + (when (not= 0 i) + [:td])])] + [subtotal [:sales ] #{} "Sales" location] + [subtotal [:cogs ] #{} "Cogs" location] + [subtotal [:payroll ]#{} "Payroll" location] + [subtotal [:sales :payroll :cogs] #{:payroll :cogs} "Gross Profits" location] + [subtotal [:controllable :fixed-overhead :ownership-controllable] #{} "Overhead" location] + [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} "Net Income" location]]] + + ])) + +(defn and-last-year [[from to]] + [[from to] + [(t/minus from (t/years 1)) (t/minus to (t/years 1))]]) (def profit-and-loss-content (with-meta @@ -491,7 +442,8 @@ (let [current-client @(re-frame/subscribe [::subs/client]) user @(re-frame/subscribe [::subs/user]) error @(re-frame/subscribe [::error]) - params @(re-frame/subscribe [::params])] + params @(re-frame/subscribe [::params]) + periods @(re-frame/subscribe [::periods])] (if-not current-client [:div @@ -504,6 +456,23 @@ [:h2.title.is-4 "Range"] [:div [:div.field.is-grouped + [:p.control + [:a.button + {:class (when (= (:selected params) "13 periods") "is-active") + :on-click (dispatch-event + [::range-selected + (let [this-month (t/local-date (t/year (local-now)) + (t/month (local-now)) + 1)] + (into + [[this-month (t/minus (t/plus this-month (t/months 1)) (t/days 1))]] + (for [i (range 12)] + [(t/minus this-month (t/months (- 12 i))) + (t/minus (t/minus this-month (t/months (- 11 i))) + (t/days 1))]))) + + "13 periods"])} + "13 periods"]] [:p.control [:a.button {:class (when (= (:selected params) "Last week") "is-active") @@ -513,101 +482,94 @@ current (recur (t/minus current (t/period :days 1)))))] [::range-selected - (date->str (t/minus last-sunday (t/period :days 6)) standard) - (date->str last-sunday standard) + (and-last-year [(t/minus last-sunday (t/period :days 6)) last-sunday]) "Last week"]))} "Last week"]] [:p.control [:a.button {:class (when (= (:selected params) "Week to date") "is-active") :on-click (dispatch-event [::range-selected - (date->str (loop [current (local-now)] - (if (= 1 (t/day-of-week current)) - current - (recur (t/minus current (t/period :days 1))))) - standard) - (date->str (local-now) standard) + (and-last-year [(loop [current (local-now)] + (if (= 1 (t/day-of-week current)) + current + (recur (t/minus current (t/period :days 1))))) + (local-now)]) "Week to date"])} "Week to date"]] [:p.control [:a.button {:class (when (= (:selected params) "Last Month") "is-active") :on-click (dispatch-event [::range-selected - (date->str (t/minus (t/local-date (t/year (local-now)) - (t/month (local-now)) - 1) - (t/period :months 1)) - standard) - (date->str (t/minus (t/local-date (t/year (local-now)) - (t/month (local-now)) - 1) - (t/period :days 1)) standard) + (and-last-year [(t/minus (t/local-date (t/year (local-now)) + (t/month (local-now)) + 1) + (t/period :months 1)) + (t/minus (t/local-date (t/year (local-now)) + (t/month (local-now)) + 1) + (t/period :days 1))]) "Last Month"])} "Last Month"]] [:p.control [:a.button {:class (when (= (:selected params) "Month to date") "is-active") :on-click (dispatch-event [::range-selected - (date->str (t/local-date (t/year (local-now)) - (t/month (local-now)) - 1) - standard) - (date->str (local-now) standard) + (and-last-year [(t/local-date (t/year (local-now)) + (t/month (local-now)) + 1) + (local-now)]) "Month to date"])} "Month to date"]] [:p.control [:a.button {:class (when (= (:selected params) "Year to date") "is-active") :on-click (dispatch-event [::range-selected - (date->str (t/local-date (t/year (local-now)) - 1 - 1) - standard) - (date->str (local-now) standard) + (and-last-year [(t/local-date (t/year (local-now)) 1 1) + (local-now)]) "Year to date"])} "Year to date"]] [:p.control [:a.button {:class (when (= (:selected params) "Full year") "is-active") :on-click (dispatch-event [::range-selected - (date->str (t/plus (t/minus (local-now) (t/period :years 1)) - (t/period :days 1)) - standard) - (date->str (local-now) standard) + (and-last-year [(t/plus (t/minus (local-now) (t/period :years 1)) + (t/period :days 1)) + (local-now)]) "Full year"])} "Full year"]]]] - [:div.field.is-grouped - [:p.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 [:from-date] - :event [::date-picked] - :popper-props (clj->js {:placement "right"}) - :subscription params}]]] + (for [[_ i] (map vector periods (range))] + [:div.field.is-grouped + [:p.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}]]] - [:p.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 [:to-date] - :event [::date-picked] - :popper-props (clj->js {:placement "right"}) - :subscription params}]]]]] + [:p.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}]]]])] (cond error [:div.notification.is-warning error] @@ -627,19 +589,21 @@ [:tbody [:tr [:td.has-text-right "Period Ending"] - [:td.has-text-right (date->str (str->date (:to-date params) standard))] + [:td.has-text-right (date->str (last (first periods)))] [:td] - [:td.has-text-right (when (:to-date params) - (date->str (t/minus (str->date (:to-date params) standard) (t/years 1))))] - [:td] - [:td.has-text-right "𝝙"]] + [:<> + (for [[_ end] (rest periods)] + [:<> + [:td.has-text-right (date->str end)] + [:td] + [:td.has-text-right "𝝙"]] + )]] [:<> (for [location @(re-frame/subscribe [::locations])] ^{:key location} [location-rows location] )]]]])]))) - {:component-will-mount #(re-frame/dispatch-sync [::params-change {:from-date (date->str (t/minus (local-now) (t/period :years 1)) standard) - :to-date (date->str (local-now) standard)}]) })) + {:component-will-mount #(do (re-frame/dispatch-sync [::range-selected (and-last-year [(t/minus (local-now) (t/period :years 1)) (local-now)]) nil])) })) diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index 353e1906..9ab9637a 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -178,9 +178,17 @@ (let [field (if (keyword? field) [field] field) event (if (keyword? event) [event] event) selected (get-in subscription field) - x (str->date selected standard) - selected (if (string? selected) - (c/to-date (time/to-default-time-zone (time/from-default-time-zone x))) + selected (cond (string? selected) + (c/to-date (time/to-default-time-zone (time/from-default-time-zone (str->date selected standard)))) + + + (instance? goog.date.DateTime selected) + (c/to-date selected) + + (instance? goog.date.Date selected) + (c/to-date selected) + + :else selected ) keys (assoc keys :on-change (dispatch-date-change (conj event field))