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 424bc850..e05be1e8 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 @@ -57,18 +57,24 @@ :periods (mapcat :accounts) (filter (comp in-range? :numeric-code)) - (group-by :location) + (group-by (juxt :client-id :location)) (filter (fn [[k as]] (not (dollars-0? (reduce + 0 (map :amount as)))))) (mapcat second) - (map :location) - (map not-empty) + (map (fn [a] + (if (or (not (:client-id a)) + (empty? (:location a))) + nil + [(:client-id a) + (:location a)]))) (filter identity) (set) - (sort-by (fn [x] (if (= x "HQ" ) - "ZZZZZZ" - x)))))) + (sort-by (fn [x] + [(:client-id x) + (if (= (:location x) "HQ" ) + "ZZZZZZ" + (:location x))]))))) (re-frame/reg-sub ::multi-client? @@ -119,13 +125,15 @@ (map #(update % :amount js/parseFloat) a)))) -(defn filter-accounts [accounts period [from to] only-location] +(defn filter-accounts [accounts period [from to] only-client only-location] (->> (get accounts period) vals - (filter (fn [{:keys [location numeric-code]}] + (filter (fn [{:keys [location client-id numeric-code]}] (and (or (nil? only-location) (= only-location location)) + (or (nil? only-client) + (= only-client client-id)) (<= from numeric-code to)))) (sort-by :numeric-code))) @@ -140,7 +148,7 @@ (comp (map parse-amounts) (map #(->> (:accounts %) - (group-by (juxt :numeric-code :location)) + (group-by (juxt :numeric-code :client-id :location)) (map (fn [[k v]] [k (reduce (fn [a n] @@ -420,17 +428,18 @@ ["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))] +(defn percent-of-sales [amount accounts which client-id location] + (let [sales (aggregate-accounts (filter-accounts accounts which (get ranges :sales) client-id location))] (if (not (dollars-0? sales)) (/ amount sales) 0.0))) -(defn used-accounts [accounts [from to] location] +(defn used-accounts [accounts [from to] client-id location] (->> accounts (mapcat vals) (filter #(<= from (:numeric-code %) to)) + (filter #(= client-id (:client-id %))) (filter #(= location (:location %))) (map #(select-keys % [:numeric-code :name])) (set) @@ -448,17 +457,15 @@ {:key (str "between-" i)}))])) -;; TODO Allow choosing more than one client ;; TODO allow "sandwhiching" clients data -;; TODO do we need squashing locations? -(defn grouping [{:keys [header type groupings location periods all-accounts]}] +(defn grouping [{:keys [header type groupings client-id location periods all-accounts]}] (let [include-deltas @(re-frame/subscribe [::include-deltas]) multi-client? @(re-frame/subscribe [::multi-client?])] [:<> (doall (for [[grouping-name from to] groupings - :let [account-codes (used-accounts all-accounts [from to] location)] + :let [account-codes (used-accounts all-accounts [from to] client-id location)] :when (seq account-codes)] ^{:key grouping-name} [:<> @@ -479,17 +486,17 @@ [:td name] (map-periods (fn [i] - (let [amount (get-in all-accounts [i [numeric-code location] :amount] 0.0)] + (let [amount (get-in all-accounts [i [numeric-code client-id location] :amount] 0.0)] [:<> [:td.has-text-right (if multi-client? [:span (->$ amount)] [:a {:on-click (dispatch-event [::investigate-clicked location numeric-code numeric-code i :current]) :disabled (boolean multi-client?)} (->$ amount)])] - [:td.has-text-right (->% (percent-of-sales amount all-accounts i location))]])) + [:td.has-text-right (->% (percent-of-sales amount all-accounts i client-id location))]])) (fn [i] - [:td.has-text-right (->$ (- (get-in all-accounts [i [numeric-code location] :amount] 0.0) - (get-in all-accounts [(dec i) [numeric-code location] :amount] 0.0)))]) + [:td.has-text-right (->$ (- (get-in all-accounts [i [numeric-code client-id location] :amount] 0.0) + (get-in all-accounts [(dec i) [numeric-code client-id location] :amount] 0.0)))]) periods include-deltas)])] @@ -497,22 +504,22 @@ [:th] (map-periods (fn [i] - (let [amount (aggregate-accounts (filter-accounts all-accounts i [from to] location))] + (let [amount (aggregate-accounts (filter-accounts all-accounts i [from to] client-id location))] [:<> [:th.has-text-right.total (if multi-client? [:span (->$ amount)] [: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))]])) + [:th.has-text-right.total (->% (percent-of-sales amount all-accounts i client-id location))]])) (fn [i] - [:th.has-text-right.total (->$ (- (aggregate-accounts (filter-accounts all-accounts i [from to] location)) - (aggregate-accounts (filter-accounts all-accounts (dec i) [from to] location))))]) + [:th.has-text-right.total (->$ (- (aggregate-accounts (filter-accounts all-accounts i [from to] client-id location)) + (aggregate-accounts (filter-accounts all-accounts (dec i) [from to] client-id location))))]) periods include-deltas)]]))])) -(defn overall-grouping [type title location] +(defn overall-grouping [type title client-id location] (let [all-accounts @(re-frame/subscribe [::all-accounts]) periods @(re-frame/subscribe [::periods]) [min-numeric-code max-numeric-code] (ranges type) @@ -522,6 +529,7 @@ [:tr [:th.is-size-5 title]] [grouping {:location location + :client-id client-id :groupings (type groupings) :periods periods :all-accounts all-accounts}] @@ -529,43 +537,44 @@ [:tr [:th.is-size-5 title] (map-periods (fn [i] - (let [amount (aggregate-accounts (filter-accounts all-accounts i [min-numeric-code max-numeric-code] location))] + (let [amount (aggregate-accounts (filter-accounts all-accounts i [min-numeric-code max-numeric-code] client-id location))] [:<> [:th.has-text-right (if multi-client? [:span (->$ amount)] [: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))] + [:th.has-text-right (->% (percent-of-sales amount all-accounts i client-id location))] ])) (fn [i] - [:th.has-text-right (->$ (- (aggregate-accounts (filter-accounts all-accounts i [min-numeric-code max-numeric-code] location)) - (aggregate-accounts (filter-accounts all-accounts (dec i) [min-numeric-code max-numeric-code] location))))]) + [:th.has-text-right (->$ (- (aggregate-accounts (filter-accounts all-accounts i [min-numeric-code max-numeric-code] client-id location)) + (aggregate-accounts (filter-accounts all-accounts (dec i) [min-numeric-code max-numeric-code] client-id location))))]) periods include-deltas)]])) -(defn subtotal [types negs title location] +(defn subtotal [types negs title client-id location] (let [all-accounts @(re-frame/subscribe [::all-accounts]) periods @(re-frame/subscribe [::periods]) include-deltas @(re-frame/subscribe [::include-deltas])] + (println title client-id) [:tr [:th.is-size-5 title] (map-periods (fn [i] (let [amount (aggregate-accounts (mapcat (fn [t] - (cond->> (filter-accounts all-accounts i (ranges t) location) + (cond->> (filter-accounts all-accounts i (ranges t) client-id 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))]])) + [:td.has-text-right (->% (percent-of-sales amount all-accounts i client-id location))]])) (fn [i] [:td.has-text-right (->$ (- (aggregate-accounts (mapcat (fn [t] - (cond->> (filter-accounts all-accounts i (ranges t) location) + (cond->> (filter-accounts all-accounts i (ranges t) client-id location) (negs t) (map #(update % :amount -)))) types)) (aggregate-accounts (mapcat (fn [t] - (cond->> (filter-accounts all-accounts (dec i) (ranges t) location) + (cond->> (filter-accounts all-accounts (dec i) (ranges t) client-id location) (negs t) (map #(update % :amount -)))) types))))]) periods @@ -600,22 +609,22 @@ periods include-deltas)]]) -(defn location-rows [location] +(defn location-rows [client-id location] [:<> - [overall-grouping :sales (str location " Sales") location] - [overall-grouping :cogs (str location " COGS") location] - [overall-grouping :payroll (str location " Payroll") location] - [subtotal [:payroll :cogs] #{} (str location " Prime Costs") location] - [subtotal [:sales :payroll :cogs] #{:payroll :cogs} (str location " Gross Profits") location] - [overall-grouping :controllable (str location " Controllable Expenses") location] - [overall-grouping :fixed-overhead (str location " Fixed Overhead") location] - [overall-grouping :ownership-controllable (str location " Ownership Controllable") location] - [subtotal [:controllable :fixed-overhead :ownership-controllable] #{} (str location " Overhead") location] - [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} (str location " Net Income") location] - [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} "Net Income" nil]]) + [overall-grouping :sales (str location " Sales") client-id location] + [overall-grouping :cogs (str location " COGS") client-id location] + [overall-grouping :payroll (str location " Payroll") client-id location] + [subtotal [:payroll :cogs] #{} (str location " Prime Costs") client-id location] + [subtotal [:sales :payroll :cogs] #{:payroll :cogs} (str location " Gross Profits") client-id location] + [overall-grouping :controllable (str location " Controllable Expenses") client-id location] + [overall-grouping :fixed-overhead (str location " Fixed Overhead") client-id location] + [overall-grouping :ownership-controllable (str location " Ownership Controllable") client-id location] + [subtotal [:controllable :fixed-overhead :ownership-controllable] #{} (str location " Overhead") client-id location] + [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} (str location " Net Income") client-id location] + [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} "Net Income" client-id nil]]) -(defn location-summary [location include-deltas] +(defn location-summary [client-id location include-deltas] (let [periods @(re-frame/subscribe [::periods])] [:div [:h2.title.is-4.mb-4 location " Summary"] @@ -625,12 +634,12 @@ :periods periods}] - [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]]]])) + [subtotal [:sales ] #{} "Sales" client-id location] + [subtotal [:cogs ] #{} "Cogs" client-id location] + [subtotal [:payroll ]#{} "Payroll" client-id location] + [subtotal [:sales :payroll :cogs] #{:payroll :cogs} "Gross Profits" client-id location] + [subtotal [:controllable :fixed-overhead :ownership-controllable] #{} "Overhead" client-id location] + [subtotal [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} "Net Income" client-id location]]]])) (re-frame/reg-sub ::can-submit @@ -876,6 +885,7 @@ (defn profit-and-loss-content [] (let [status @(re-frame/subscribe [::status/single ::page]) unresolved-accounts @(re-frame/subscribe [::uncategorized-accounts]) + clients-by-id @(re-frame/subscribe [::subs/clients-by-id]) {: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)] @@ -899,9 +909,9 @@ (:location %))) unresolved-accounts))]) [:<> - (for [location @(re-frame/subscribe [::locations])] - ^{:key (str location "-summary")} - [location-summary location (:include-deltas data)] + (for [[client-id location] @(re-frame/subscribe [::locations])] + ^{:key (str client-id "-" location "-summary")} + [location-summary client-id location (:include-deltas data)] )] [:h2.title.is-4 {:style {:margin-bottom "1rem"}} "Detail"] [:table.table.compact.balance-sheet @@ -910,9 +920,11 @@ :periods periods}] [:<> - (for [location @(re-frame/subscribe [::locations])] - ^{:key location} - [location-rows location])]]]])]))) + (for [[client-id location] @(re-frame/subscribe [::locations])] + ^{:key (str client-id "-" location)} + [:<> + [:tr [:th.is-size-3 (:name (clients-by-id client-id))]] + [location-rows client-id location]])]]]])]))) (re-frame/reg-event-fx ::unmounted-pnl