progress, working.

This commit is contained in:
2022-03-20 08:50:34 -07:00
parent f536d1ac5e
commit dcc7b8f304
2 changed files with 212 additions and 167 deletions

View File

@@ -10,7 +10,8 @@
[clojure.string :as str] [clojure.string :as str]
[clojure.walk :refer [postwalk]] [clojure.walk :refer [postwalk]]
[config.core :refer [env]] [config.core :refer [env]]
[datomic.api :as d]) [datomic.api :as d]
[clojure.tools.logging :as log])
(:import (:import
(java.io ByteArrayOutputStream) (java.io ByteArrayOutputStream)
(java.util UUID))) (java.util UUID)))
@@ -78,8 +79,6 @@
(defn locations [data] (defn locations [data]
(->> data (->> data
:periods
(mapcat :accounts)
(filter (comp in-range? :numeric-code)) (filter (comp in-range? :numeric-code))
(group-by (juxt :client-id :location)) (group-by (juxt :client-id :location))
(filter (fn [[k as]] (filter (fn [[k as]]
@@ -121,101 +120,67 @@
)) ))
data)) data))
(defn map-periods [for-every between periods include-deltas]
(into [:<>]
(for [[_ i] (map vector periods (range))]
[:<> (for-every i)
(if (and include-deltas (not= 0 i))
(between i))])))
(defn period-header [{:keys [include_deltas periods]}] (defn aggregate-accounts [pnl-data]
[ (reduce (fnil + 0.0) 0.0 (map :amount (:data pnl-data))))
[[:cell "Period"]
[:<>
(map-periods
(fn [i]
[:cell {:colspan 2}
(str (date->str (get-in periods [i :start])) " - " (date->str (get-in periods [i :end])))])
(fn [i]
[:cell ""])
periods
include_deltas)]]
[[:cell ""]
[:<> (map-periods
(fn [i]
[:<>
[:cell
"Amount"]
[:cell
"% Sales"]])
(fn [i]
[:cell "𝝙"])
periods
include_deltas)]
]])
(defn all-accounts [data] (defn best-category [a]
(transduce (->> ranges
(comp (filter (fn [[category [start end]]]
(map #(->> (:accounts %) (<= start (:numeric-code a) end)))
(group-by (juxt :numeric-code :client-id :location)) first
(map (fn [[k v]] first))
[k
(reduce (fn [a n]
(-> a
(update :count (fn [z] (+ z (:count n))))
(update :amount (fn [z] (+ z (:amount n))))))
(first v)
(rest v))]))
(into {}))))
conj (defn filter-client [pnl-data client]
[] (update pnl-data :data (fn [data]
(:periods data))) ((group-by :client-id data) client))))
(defn filter-location [pnl-data location]
(update pnl-data :data (fn [data]
((group-by :location data) location))))
(defn filter-categories [pnl-data categories]
(update pnl-data :data (fn [data]
(mapcat identity
((apply juxt categories)
(group-by best-category data))))))
(defn filter-period [pnl-data period]
(update pnl-data :data (fn [data]
((group-by :period data) period))))
(defn filter-numeric-code [pnl-data from to]
(update pnl-data :data (fn [data]
(filter
#(<= from (:numeric-code %) to)
data))))
(defn negate [pnl-data types]
(update pnl-data :data
(fn [accounts]
(map
(fn [account]
(if (types (best-category account))
(update account :amount -)
account))
accounts))))
(defn filter-accounts [accounts period [from to] only-client only-location] (defn used-accounts [pnl-data]
(->> (get accounts period) (->> (:data pnl-data)
vals
(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)))
(defn aggregate-accounts [accounts]
(reduce (fnil + 0.0) 0.0 (map :amount accounts)))
(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])) (map #(select-keys % [:numeric-code :name]))
(set) (set)
(sort-by :numeric-code))) (sort-by :numeric-code)))
(defn subtotal-row [args data types negs title client-id location] (defn subtotal-row [pnl-data sales-pnl-data title]
(let [all-accounts (all-accounts data) (let [raw (map
raw (map-indexed (fn [p]
(fn [i p] (aggregate-accounts (filter-period pnl-data p)))
(aggregate-accounts (mapcat (fn [t] (-> pnl-data :args :periods))
(cond->> (filter-accounts all-accounts i (ranges t) client-id location) sales (map
(negs t) (map #(update % :amount -)))) (fn [p]
types)) (aggregate-accounts (filter-period sales-pnl-data p)))
) (-> pnl-data :args :periods))
(:periods args))
sales (map-indexed
(fn [i _]
(aggregate-accounts (filter-accounts all-accounts i (ranges :sales) client-id location)))
(:periods args))
deltas (->> raw deltas (->> raw
(partition-all 2) (partition-all 2)
(map (fn [[a b]] (map (fn [[a b]]
@@ -234,93 +199,175 @@
) )
deltas))))) deltas)))))
(defn location-summary-table [args data client-id location] (defn location-summary-table [pnl-data]
[(subtotal-row args data [:sales] #{} "Sales" client-id location)
(subtotal-row args data [:cogs ] #{} "Cogs" client-id location) (let [sales-data (filter-categories pnl-data [:sales])]
(subtotal-row args data [:payroll ]#{} "Payroll" client-id location) [(subtotal-row (filter-categories pnl-data [:sales])
(subtotal-row args data [:sales :payroll :cogs] #{:payroll :cogs} "Gross Profits" client-id location) sales-data
(subtotal-row args data [:controllable :fixed-overhead :ownership-controllable] #{} "Overhead" client-id location) "Sales")
(subtotal-row args data [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable] #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable} "Net Income" client-id location)]) (subtotal-row (filter-categories pnl-data [:cogs ])
sales-data
"Cogs")
(subtotal-row (filter-categories pnl-data [:payroll ])
sales-data
"Payroll")
(subtotal-row (-> pnl-data
(filter-categories [:sales :payroll :cogs])
(negate #{:payroll :cogs}))
sales-data
"Gross Profits")
(subtotal-row (filter-categories pnl-data [:controllable :fixed-overhead :ownership-controllable])
sales-data
"Overhead")
(subtotal-row (-> pnl-data
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
sales-data
"Net Income")
]))
(defn detail-sub-rows [args data grouping client-id location] (defn detail-sub-rows [pnl-data sales-data grouping]
(let [all-accounts (all-accounts data)] (for [[grouping-name from to] grouping
(for [[grouping-name from to] grouping :let [pnl-data (filter-numeric-code pnl-data from to)
:let [account-codes (used-accounts all-accounts [from to] client-id location)] account-codes (used-accounts pnl-data)]
:when (seq account-codes)] :when (seq account-codes)]
(-> (->
[[(str "---" grouping-name "---")]] [[(str "---" grouping-name "---")]]
(into (for [{:keys [numeric-code name]} account-codes] (into (for [{:keys [numeric-code name]} account-codes]
(let [raw (map-indexed (let [raw (map
(fn [i p] (fn [p]
(get-in all-accounts [i [numeric-code client-id location] :amount] 0.0)) (-> pnl-data
(filter-numeric-code numeric-code numeric-code)
(filter-period p)
(aggregate-accounts)))
(:periods args)) (-> pnl-data :args :periods))
sales (map-indexed sales (map
(fn [i _] (fn [p]
(aggregate-accounts (filter-accounts all-accounts i (ranges :sales) client-id location))) (-> sales-data
(filter-period p)
(aggregate-accounts)))
(:periods args)) (-> pnl-data :args :periods))
deltas (->> raw deltas (->> raw
(partition-all 2) (partition-all 2)
(map (fn [[a b]] (map (fn [[a b]]
(- b (- b
a))))] a))))]
(into [name] (into [name]
(->> raw (->> raw
(map (fn [s r] (map (fn [s r]
[r (if (dollars-0? s) [r (if (dollars-0? s)
0.0 0.0
(/ r s))]) (/ r s))])
sales) sales)
(partition-all 2) (partition-all 2)
(mapcat (fn [d [[a a-sales] [b b-sales]]] (mapcat (fn [d [[a a-sales] [b b-sales]]]
[a a-sales b b-sales d] [a a-sales b b-sales d]
) )
deltas)))) deltas)))))))))
#_[:tr
[:td name]
#_(map-periods
(fn [i]
(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 client-id location))]]))
(fn [i]
[: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)])))
)))
(defn detail-rows [args data type title client-id location] (defn detail-rows [pnl-data grouping sales-data title]
(-> [[title]] (let [pnl-data (filter-categories pnl-data [grouping])]
(into (detail-sub-rows args data (type groupings) client-id location)) (-> [[title]]
(conj (subtotal-row args data [type] #{} title client-id location)))) (into (detail-sub-rows pnl-data
sales-data
(grouping groupings)))
(conj (subtotal-row pnl-data sales-data title)))))
(defn location-detail-table [args data client-id location] (defn location-detail-table [pnl-data]
(-> [] (let [sales-data (filter-categories pnl-data [:sales])]
(into (detail-rows args data :sales (str location " Sales") client-id location)))) (-> []
(into (detail-rows pnl-data
:sales
sales-data
(str (:prefix pnl-data) " Sales")))
(into (detail-rows pnl-data
:cogs
sales-data
(str (:prefix pnl-data) " COGS")))
(into (detail-rows
pnl-data
:payroll
sales-data
(str (:prefix pnl-data) " Payroll")))
(conj (subtotal-row (filter-categories pnl-data [:payroll :cogs])
sales-data
(str (:prefix pnl-data) " Prime Costs")))
(conj (subtotal-row (-> pnl-data
(filter-categories [:payroll :cogs])
(negate #{:payroll :cogs}))
sales-data
(str (:prefix pnl-data) " Gross Profits")))
(into (detail-rows
pnl-data
:fixed-overhead
sales-data
(str (:prefix pnl-data) " Fixed Overhead")))
(into (detail-rows
pnl-data
:ownership-controllable
sales-data
(str (:prefix pnl-data) " Ownership Controllable")))
(conj (subtotal-row (-> pnl-data
(filter-categories [:controllable :fixed-overhead :ownership-controllable]))
sales-data
(str (:prefix pnl-data) " Overhead")))
(conj (subtotal-row (-> pnl-data
(filter-categories [:controllable :fixed-overhead :ownership-controllable]))
sales-data
(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}))
sales-data
(str (:prefix pnl-data) " Net Income"))))))
(defn summarize-pnl [args data] (defn summarize-pnl [pnl-data]
{:summaries (for [[client-id location] (locations data)] (try
(location-summary-table args data client-id location)) {:summaries (for [[client-id location] (locations (:data pnl-data))]
:details (for [[client-id location] (locations data)] (location-summary-table (-> pnl-data
(location-detail-table args data client-id location))} (filter-client client-id)
(filter-location location))))
:details (for [[client-id location] (locations (:data pnl-data))]
(location-detail-table (-> pnl-data
(filter-client client-id)
(filter-location location)
(assoc :prefix location))))}
(catch Throwable e
(println e))))
(defrecord PNLData [args data]
) )
(defn make-pnl [args data] (defn make-pnl [args data]
(let [data (<-graphql data) (let [data (<-graphql data)
args (<-graphql args) args (<-graphql args)
_ (clojure.pprint/pprint (summarize-pnl (assoc args :deltas true) data))output-stream (ByteArrayOutputStream.) data (->> data
_ (println (:client_ids args)) :periods
clients (d/pull-many (d/db conn) '[:client/name] (:client-ids args))] (mapcat (fn [p1 p2]
(map
(fn [a]
(assoc a :period p1)
)
(:accounts p2))
)
(:periods args)))
report (PNLData. (assoc args :deltas true) data)
_ (clojure.pprint/pprint (summarize-pnl report))
output-stream (ByteArrayOutputStream.)
_ (println (:client_ids args))
clients (d/pull-many (d/db conn) '[:client/name] (:client-ids args))]
(pdf/pdf (pdf/pdf
(expand (expand
[{:left-margin 25 :right-margin 0 :top-margin 0 :bottom-margin 0 :size :letter} [{:left-margin 25 :right-margin 0 :top-margin 0 :bottom-margin 0 :size :letter}

View File

@@ -95,7 +95,6 @@
(fn [db] (fn [db]
(-> db ::ledger-list-active?))) (-> db ::ledger-list-active?)))
(re-frame/reg-sub (re-frame/reg-sub
::period-accounts ::period-accounts
:<- [::forms/form ::form] :<- [::forms/form ::form]
@@ -579,7 +578,6 @@
(let [all-accounts @(re-frame/subscribe [::all-accounts]) (let [all-accounts @(re-frame/subscribe [::all-accounts])
periods @(re-frame/subscribe [::periods]) periods @(re-frame/subscribe [::periods])
include-deltas @(re-frame/subscribe [::include-deltas])] include-deltas @(re-frame/subscribe [::include-deltas])]
(println title client-id)
[:tr [:th.is-size-5 title] [:tr [:th.is-size-5 title]
(map-periods (map-periods