Adds the ability to do a column per location

This commit is contained in:
2022-04-03 07:46:15 -07:00
parent 72a97532ee
commit 7739c18bc2
3 changed files with 303 additions and 259 deletions

View File

@@ -90,9 +90,11 @@
(vals ranges))
false))
(defn locations [data]
(->> data
(defn client-locations [pnl-data]
(->> pnl-data
:data
(filter (comp in-range? :numeric-code))
(filter #(not= "A" (:location %)))
(group-by (juxt :client-id :location))
(filter (fn [[_ as]]
(not (dollars-0? (reduce + 0 (map :amount as))))))
@@ -112,6 +114,15 @@
"ZZZZZZ"
(:location x))]))))
(defn locations [pnl-data]
(->> (client-locations pnl-data)
(map second)
set
(sort-by (fn [x]
(if (= x "HQ" )
"ZZZZZZ"
x)))))
(defn aggregate-accounts [pnl-data]
(reduce (fnil + 0.0) 0.0 (map :amount (:data pnl-data))))
@@ -179,34 +190,32 @@
accounts))))
(defn used-accounts [pnl-data]
(->> (:data pnl-data)
(map #(select-keys % [:numeric-code :name]))
(set)
(sort-by :numeric-code)))
(defn used-accounts [pnl-datas]
(->>
pnl-datas
(mapcat :data)
(map #(select-keys % [:numeric-code :name]))
(set)
(sort-by :numeric-code)))
(defn subtotal-row [pnl-data title & [cell-args]]
(into [{:value title
:bold true
:filters (when (:from-numeric-code (:filters pnl-data)) ;; don't allow filtering when you don't at least filter numeric codes
(:filters pnl-data))}]
(defn subtotal-by-column-row [pnl-datas title & [cell-args]]
(into [{:value title
:bold true}]
(map
(fn [p]
(let [data (filter-period pnl-data p)]
(merge
{:format :dollar
:value (aggregate-accounts data)
:filters (when (:from-numeric-code (:filters data)) ;; don't allow filtering when you don't at least filter numeric codes
(:filters data))}
cell-args)))
(-> pnl-data :args :periods))))
(merge
{:format :dollar
:value (aggregate-accounts p)
:filters (when (:from-numeric-code (:filters p)) ;; don't allow filtering when you don't at least filter numeric codes
(:filters p))}
cell-args))
pnl-datas)))
(defn calc-percent-of-sales [table pnl-data]
(let [sales-pnl-data (filter-categories pnl-data [:sales])
sales (map
(defn calc-percent-of-sales [table pnl-datas]
(let [sales (map
(fn [p]
(aggregate-accounts (filter-period sales-pnl-data p)))
(-> pnl-data :args :periods))]
(aggregate-accounts (filter-categories p [:sales])))
pnl-datas)]
(->> table
(map (fn [[_ & values]]
(map
@@ -231,7 +240,7 @@
(:value a))})))))))
(defn combine-tables
([pnl-data table percent-of-sales deltas]
([[pnl-data] table percent-of-sales deltas]
(map (fn [[title & row] percent-of-sales deltas ]
(let [deltas (cons {:value 0.0
:format :dollar
@@ -249,130 +258,156 @@
percent-of-sales
deltas)))
(defn headers [pnl-data header-title]
(let [big-header (into [{:value header-title
(defn headers [[pnl-data :as pnl-datas] header-title]
(let [
big-header (into [{:value header-title
:bold true}]
(map (fn [p]
{:value
(str (date->str (:start p))
" - "
(date->str (:end p)))
:colspan (if (-> pnl-data :args :include-deltas)
:colspan (cond
(-> pnl-data :args :include-deltas)
3
(-> pnl-data :args :column-per-location)
(* 2 (/ (count pnl-datas)
(count (-> pnl-data :args :periods))))
:else
2)
:align :center
:bold true})
(:periods (:args pnl-data))))
sub-header (into [{:value ""}]
(mapcat
(fn [_]
(cond-> [{:value "Amt"
:align :right}
{:value "%"
:align :right}]
(-> pnl-data :args :include-deltas) (conj {:value "+/-"
:align :right})))
(:periods (:args pnl-data))))]
(if (-> pnl-data :args :column-per-location)
(mapcat
(fn [p]
(cond-> [{:value (-> p :filters :location)
:align :right}
{:value "%"
:align :right}]
(-> pnl-data :args :include-deltas) (conj {:value "+/-"
:align :right})))
pnl-datas)
(mapcat
(fn [_]
(cond-> [{:value "Amt"
:align :right}
{:value "%"
:align :right}]
(-> pnl-data :args :include-deltas) (conj {:value "+/-"
:align :right})))
pnl-datas)))]
[big-header
sub-header]))
(defn location-summary-table [pnl-data title]
(let [table [(subtotal-row (filter-categories pnl-data [:sales]) "Sales")
(subtotal-row (filter-categories pnl-data [:cogs ]) "Cogs")
(defn location-summary-table [pnl-datas title]
(let [table [(subtotal-by-column-row (map #(filter-categories % [:sales])
pnl-datas)
"Sales")
(subtotal-by-column-row (map #(filter-categories % [:cogs ]) pnl-datas)
"Cogs")
(subtotal-row (filter-categories pnl-data [:payroll ]) "Payroll")
(subtotal-by-column-row (map #(filter-categories % [:payroll ]) pnl-datas)
"Payroll")
(subtotal-row (-> pnl-data
(filter-categories [:sales :payroll :cogs])
(negate #{:payroll :cogs}))
"Gross Profits")
(subtotal-by-column-row (map #(-> %
(filter-categories [:sales :payroll :cogs])
(negate #{:payroll :cogs}))
pnl-datas)
"Gross Profits")
(subtotal-row (filter-categories pnl-data [:controllable :fixed-overhead :ownership-controllable])
"Overhead")
(subtotal-by-column-row (map #(filter-categories % [:controllable :fixed-overhead :ownership-controllable])
pnl-datas)
"Overhead")
(subtotal-row (-> pnl-data
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
"Net Income")]
percent-of-sales (calc-percent-of-sales table pnl-data)
(subtotal-by-column-row (map #(-> %
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
pnl-datas)
"Net Income")]
percent-of-sales (calc-percent-of-sales table pnl-datas)
deltas (calc-deltas table)]
{:header (headers pnl-data title)
:rows (combine-tables pnl-data table percent-of-sales deltas)}))
{:header (headers pnl-datas title)
:rows (combine-tables pnl-datas table percent-of-sales deltas)}))
(defn detail-rows [pnl-data grouping title]
(let [pnl-data (filter-categories pnl-data [grouping])
(defn detail-rows [pnl-datas grouping title]
(let [pnl-datas (map #(filter-categories % [grouping])
pnl-datas)
individual-accounts
(for [[grouping-name from to] (groupings grouping)
:let [pnl-data (filter-numeric-code pnl-data from to)
account-codes (used-accounts pnl-data)]
:let [pnl-datas (map #(filter-numeric-code % from to)
pnl-datas)
account-codes (used-accounts pnl-datas)]
:when (seq account-codes)
row (-> [[{:value (str "---" grouping-name "---")}]]
(into (for [{:keys [numeric-code name]} account-codes]
(into [{:value name}]
(map
(fn [p]
(let [pnl-data (-> pnl-data
(filter-numeric-code numeric-code numeric-code)
(filter-period p)
)]
(let [pnl-data (-> p (filter-numeric-code numeric-code numeric-code))]
{:format :dollar
:filters (:filters pnl-data)
:value (aggregate-accounts pnl-data)}))
(-> pnl-data :args :periods)))))
(conj (subtotal-row pnl-data "" {:border [:top]})))]
pnl-datas))))
(conj (subtotal-by-column-row pnl-datas "" {:border [:top]})))]
row)]
(-> [[{:value title
:bold true}]]
(into individual-accounts)
(conj (subtotal-row pnl-data title)))))
(conj (subtotal-by-column-row pnl-datas title)))))
(defn location-detail-table [pnl-data client-data title]
(defn location-detail-table [pnl-datas client-data title prefix]
(let [table (-> []
(into (detail-rows pnl-data
(into (detail-rows pnl-datas
:sales
(str (:prefix pnl-data) " Sales")))
(into (detail-rows pnl-data
(str prefix " Sales")))
(into (detail-rows pnl-datas
:cogs
(str (:prefix pnl-data) " COGS")))
(str prefix " COGS")))
(into (detail-rows
pnl-data
pnl-datas
:payroll
(str (:prefix pnl-data) " Payroll")))
(conj (subtotal-row (filter-categories pnl-data [:payroll :cogs])
(str (:prefix pnl-data) " Prime Costs")))
(conj (subtotal-row (-> pnl-data
(filter-categories [:sales :payroll :cogs])
(negate #{:payroll :cogs}))
(str (:prefix pnl-data) " Gross Profits")))
(str prefix " Payroll")))
(conj (subtotal-by-column-row (map #(filter-categories % [:payroll :cogs])
pnl-datas)
(str prefix " Prime Costs")))
(conj (subtotal-by-column-row (map #(-> %
(filter-categories [:sales :payroll :cogs])
(negate #{:payroll :cogs}))
pnl-datas)
(str prefix " Gross Profits")))
(into (detail-rows
pnl-data
pnl-datas
:controllable
(str (:prefix pnl-data) " Controllable Expenses")))
(str prefix " Controllable Expenses")))
(into (detail-rows
pnl-data
pnl-datas
:fixed-overhead
(str (:prefix pnl-data) " Fixed Overhead")))
(str prefix " Fixed Overhead")))
(into (detail-rows
pnl-data
pnl-datas
:ownership-controllable
(str (:prefix pnl-data) " Ownership Controllable")))
(conj (subtotal-row (-> pnl-data
(filter-categories [:controllable :fixed-overhead :ownership-controllable]))
(str (:prefix pnl-data) " Overhead")))
(conj (subtotal-row (-> pnl-data
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
(str (:prefix pnl-data) " Net Income")))
(conj (subtotal-row (-> client-data
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
"All Location Net Income")))
percent-of-sales (calc-percent-of-sales table pnl-data)
(str prefix " Ownership Controllable")))
(conj (subtotal-by-column-row (map #(filter-categories % [:controllable :fixed-overhead :ownership-controllable]) pnl-datas)
(str prefix " Overhead")))
(conj (subtotal-by-column-row (map #(-> %
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
pnl-datas)
(str prefix " Net Income")))
(conj (subtotal-by-column-row (-> client-data
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
"All Location Net Income")))
percent-of-sales (calc-percent-of-sales table pnl-datas)
deltas (into [] (calc-deltas table))]
{:header (headers pnl-data title)
:rows (combine-tables pnl-data table percent-of-sales deltas)}))
{:header (headers pnl-datas title)
:rows (combine-tables pnl-datas table percent-of-sales deltas)}))
(defn warning-message [pnl-data]
(let [errors (->> pnl-data
@@ -389,20 +424,48 @@
errors))))))
(defn summarize-pnl [pnl-data]
{:warning (warning-message pnl-data)
:summaries (for [[client-id location] (locations (:data pnl-data))]
(location-summary-table (-> pnl-data
(filter-client client-id)
(filter-location location))
(str (-> pnl-data :clients-by-id (get client-id)) " (" location ") Summary")))
:details (for [[client-id location] (locations (:data pnl-data))]
(location-detail-table (-> pnl-data
{:warning (warning-message pnl-data)
:summaries
(if (-> pnl-data :args :column-per-location)
(for [client-id (set (map first (client-locations pnl-data)))]
(location-summary-table (for [period (-> pnl-data :args :periods )
location (locations pnl-data)]
(-> pnl-data
(filter-client client-id)
(filter-location location)
(filter-period period)))
(str (-> pnl-data :clients-by-id (get client-id)) " Summary")))
(for [[client-id location] (client-locations pnl-data)]
(location-summary-table (for [period (-> pnl-data :args :periods )]
(-> pnl-data
(filter-client client-id)
(filter-location location)
(filter-period period)))
(str (-> pnl-data :clients-by-id (get client-id)) " (" location ") Summary"))))
:details
(doall (if (-> pnl-data :args :column-per-location)
(for [client-id (set (map first (client-locations pnl-data)))]
(location-detail-table (for [period (-> pnl-data :args :periods )
location (locations pnl-data)]
(-> pnl-data
(filter-client client-id)
(filter-location location)
(assoc :prefix location))
(filter-period period)))
(-> pnl-data
(filter-client client-id))
(str (-> pnl-data :clients-by-id (get client-id)) " Detail")
""))
(for [[client-id location] (client-locations pnl-data)]
(location-detail-table (for [period (-> pnl-data :args :periods )]
(-> pnl-data
(filter-client client-id))
(str (-> pnl-data :clients-by-id (get client-id)) " (" location ") Detail")))})
(filter-client client-id)
(filter-location location)
(filter-period period)))
(-> pnl-data
(filter-client client-id))
(str (-> pnl-data :clients-by-id (get client-id)) " (" location ") Detail")
location))))
})
(defn balance-sheet-headers [pnl-data]
@@ -420,20 +483,27 @@
(:value b))})]))))
(defn summarize-balance-sheet [pnl-data]
(let [table (-> []
(into (detail-rows pnl-data
(let [
pnl-datas (map (fn [p]
(filter-period pnl-data p))
(:periods (:args pnl-data)))
table (-> []
(into (detail-rows pnl-datas
:assets
"Assets"))
(into (detail-rows pnl-data
(into (detail-rows pnl-datas
:liabilities
"Liabilities"))
(into (detail-rows pnl-data
(into (detail-rows pnl-datas
:equities
"Owner's Equity"))
(conj (subtotal-row (-> pnl-data
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
"Retained Earnings")))
(conj (subtotal-by-column-row
(map #(-> %
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
pnl-datas)
"Retained Earnings")))
table (if (:include-comparison (:args pnl-data))
(append-deltas table)
table)]