progress, working.
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user