Splits out multiple tables when too wide
This commit is contained in:
@@ -188,73 +188,65 @@
|
|||||||
|
|
||||||
(defn combine-tables [pnl-data table percent-of-sales deltas]
|
(defn combine-tables [pnl-data table percent-of-sales deltas]
|
||||||
(map (fn [[title & row] percent-of-sales deltas ]
|
(map (fn [[title & row] percent-of-sales deltas ]
|
||||||
(let [deltas (cons nil deltas)]
|
(let [deltas (cons "-" deltas)]
|
||||||
(into [title]
|
(into [title]
|
||||||
(filter identity
|
(mapcat
|
||||||
(mapcat
|
(fn [v p d]
|
||||||
(fn [v p d]
|
[v p d])
|
||||||
[v p d])
|
row
|
||||||
row
|
percent-of-sales
|
||||||
percent-of-sales
|
deltas))
|
||||||
deltas)))
|
|
||||||
))
|
))
|
||||||
table
|
table
|
||||||
percent-of-sales
|
percent-of-sales
|
||||||
deltas))
|
deltas))
|
||||||
|
|
||||||
(defn headers [pnl-data header-title]
|
(defn headers [pnl-data header-title]
|
||||||
(let [deltas (cons nil (repeat "Change"))
|
(let [big-header (into [{:value header-title
|
||||||
big-header (into [{:value header-title
|
|
||||||
:bold true}]
|
:bold true}]
|
||||||
(map-indexed (fn [i p]
|
(map-indexed (fn [i p]
|
||||||
{:value
|
{:value
|
||||||
(str (date->str (:start p))
|
(str (date->str (:start p))
|
||||||
" - "
|
" - "
|
||||||
(date->str (:end p)))
|
(date->str (:end p)))
|
||||||
:colspan (if (= 0 i)
|
:colspan 3
|
||||||
2
|
|
||||||
3)
|
|
||||||
:align :center
|
:align :center
|
||||||
:bold true})
|
:bold true})
|
||||||
(:periods (:args pnl-data))))
|
(:periods (:args pnl-data))))
|
||||||
sub-header (into [{:value ""}]
|
sub-header (into [{:value ""}]
|
||||||
(filter identity
|
(mapcat
|
||||||
(mapcat
|
(fn [_]
|
||||||
(fn [v p d]
|
[{:value "Amount"
|
||||||
[{:value "Amount"
|
:align :right
|
||||||
:align :right
|
:bold true}
|
||||||
:bold true}
|
{:value "% Sales"
|
||||||
{:value "% Sales"
|
:align :right
|
||||||
:align :right
|
:bold true}
|
||||||
:bold true}
|
{:value "Change"
|
||||||
(when d
|
:align :right
|
||||||
{:value d
|
:bold true}])
|
||||||
:align :right
|
(:periods (:args pnl-data))))]
|
||||||
:bold true})])
|
|
||||||
(:periods (:args pnl-data))
|
|
||||||
(:periods (:args pnl-data))
|
|
||||||
deltas)))]
|
|
||||||
[big-header
|
[big-header
|
||||||
sub-header]))
|
sub-header]))
|
||||||
|
|
||||||
(defn location-summary-table [pnl-data title]
|
(defn location-summary-table [pnl-data title]
|
||||||
(let [table [(subtotal-row (filter-categories pnl-data [:sales]) "Sales")
|
(let [table [(subtotal-row (filter-categories pnl-data [:sales]) "Sales")
|
||||||
(subtotal-row (filter-categories pnl-data [:cogs ]) "Cogs")
|
(subtotal-row (filter-categories pnl-data [:cogs ]) "Cogs")
|
||||||
|
|
||||||
(subtotal-row (filter-categories pnl-data [:payroll ]) "Payroll")
|
(subtotal-row (filter-categories pnl-data [:payroll ]) "Payroll")
|
||||||
|
|
||||||
(subtotal-row (-> pnl-data
|
(subtotal-row (-> pnl-data
|
||||||
(filter-categories [:sales :payroll :cogs])
|
(filter-categories [:sales :payroll :cogs])
|
||||||
(negate #{:payroll :cogs}))
|
(negate #{:payroll :cogs}))
|
||||||
"Gross Profits")
|
"Gross Profits")
|
||||||
|
|
||||||
(subtotal-row (filter-categories pnl-data [:controllable :fixed-overhead :ownership-controllable])
|
(subtotal-row (filter-categories pnl-data [:controllable :fixed-overhead :ownership-controllable])
|
||||||
"Overhead")
|
"Overhead")
|
||||||
|
|
||||||
(subtotal-row (-> pnl-data
|
(subtotal-row (-> pnl-data
|
||||||
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
|
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
|
||||||
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
|
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
|
||||||
"Net Income")]
|
"Net Income")]
|
||||||
percent-of-sales (calc-percent-of-sales table pnl-data)
|
percent-of-sales (calc-percent-of-sales table pnl-data)
|
||||||
deltas (calc-deltas table)]
|
deltas (calc-deltas table)]
|
||||||
{:header (headers pnl-data title)
|
{:header (headers pnl-data title)
|
||||||
@@ -317,9 +309,6 @@
|
|||||||
(conj (subtotal-row (-> pnl-data
|
(conj (subtotal-row (-> pnl-data
|
||||||
(filter-categories [:controllable :fixed-overhead :ownership-controllable]))
|
(filter-categories [:controllable :fixed-overhead :ownership-controllable]))
|
||||||
(str (:prefix pnl-data) " Overhead")))
|
(str (:prefix pnl-data) " Overhead")))
|
||||||
(conj (subtotal-row (-> pnl-data
|
|
||||||
(filter-categories [:controllable :fixed-overhead :ownership-controllable]))
|
|
||||||
(str (:prefix pnl-data) " Overhead")))
|
|
||||||
(conj (subtotal-row (-> pnl-data
|
(conj (subtotal-row (-> pnl-data
|
||||||
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
|
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
|
||||||
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
|
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
|
||||||
@@ -357,7 +346,7 @@
|
|||||||
(:colspan cell) (assoc :colspan (:colspan cell)))
|
(:colspan cell) (assoc :colspan (:colspan cell)))
|
||||||
|
|
||||||
(cond (= :dollar (:format cell))
|
(cond (= :dollar (:format cell))
|
||||||
(.format (DecimalFormat. "$###,###.00") (:value cell))
|
(.format (DecimalFormat. "$###,##0.00") (:value cell))
|
||||||
|
|
||||||
(= :percent (:format cell))
|
(= :percent (:format cell))
|
||||||
(.format (DecimalFormat. "0%") (:value cell))
|
(.format (DecimalFormat. "0%") (:value cell))
|
||||||
@@ -365,14 +354,34 @@
|
|||||||
:else
|
:else
|
||||||
(str (:value cell)))])
|
(str (:value cell)))])
|
||||||
|
|
||||||
|
(defn cell-count [table]
|
||||||
|
(let [counts (map count (:rows table))]
|
||||||
|
(if (seq counts)
|
||||||
|
(apply max counts)
|
||||||
|
0)))
|
||||||
|
|
||||||
(defn table->pdf [table]
|
(defn table->pdf [table]
|
||||||
(let [cell-count (apply max (map count (:rows table)))]
|
(let [cell-count (cell-count table)]
|
||||||
(-> [:pdf-table {:header (mapv
|
(-> [:pdf-table {:header (mapv
|
||||||
(fn [header]
|
(fn [header]
|
||||||
(map cell->pdf header))
|
(map cell->pdf header))
|
||||||
(:header table))
|
(:header table))
|
||||||
:cell-border false}
|
:cell-border false
|
||||||
(into [70] (take (dec cell-count) (repeat 20)))]
|
:width-percent (cond (= 5 cell-count)
|
||||||
|
50
|
||||||
|
|
||||||
|
(= 6 cell-count)
|
||||||
|
60
|
||||||
|
|
||||||
|
(= 7 cell-count)
|
||||||
|
70
|
||||||
|
|
||||||
|
(= 8 cell-count)
|
||||||
|
80
|
||||||
|
|
||||||
|
:else
|
||||||
|
100)}
|
||||||
|
(into [20] (take (dec cell-count) (repeat 10)))]
|
||||||
|
|
||||||
(into
|
(into
|
||||||
(for [row (:rows table)]
|
(for [row (:rows table)]
|
||||||
@@ -381,6 +390,63 @@
|
|||||||
(cell->pdf cell)
|
(cell->pdf cell)
|
||||||
))))
|
))))
|
||||||
(conj (take cell-count (repeat (cell->pdf {:value " "})))))))
|
(conj (take cell-count (repeat (cell->pdf {:value " "})))))))
|
||||||
|
|
||||||
|
(defn split-table [table n]
|
||||||
|
(let [cell-count (cell-count table)]
|
||||||
|
(if (<= cell-count n)
|
||||||
|
[table]
|
||||||
|
(let [new-table (-> table
|
||||||
|
(update :rows (fn [rows]
|
||||||
|
(map
|
||||||
|
(fn [[header & rest]]
|
||||||
|
(into [header]
|
||||||
|
(take (dec n) rest)))
|
||||||
|
rows)))
|
||||||
|
(update :header (fn [headers]
|
||||||
|
(map
|
||||||
|
(fn [[title & header]]
|
||||||
|
(first
|
||||||
|
(reduce
|
||||||
|
(fn [[so-far a] next]
|
||||||
|
(let [new-a (+ a (or (:colspan next)
|
||||||
|
1))]
|
||||||
|
(if (<= new-a n)
|
||||||
|
[(conj so-far next) new-a]
|
||||||
|
[so-far new-a])))
|
||||||
|
|
||||||
|
[[title] 1]
|
||||||
|
header)))
|
||||||
|
headers))))
|
||||||
|
remaining (-> table
|
||||||
|
(update :rows (fn [rows]
|
||||||
|
(map
|
||||||
|
(fn [[header & rest]]
|
||||||
|
(into [header]
|
||||||
|
(drop (dec n) rest)))
|
||||||
|
rows)))
|
||||||
|
(update :header (fn [headers]
|
||||||
|
(map
|
||||||
|
(fn [[title & header]]
|
||||||
|
(first
|
||||||
|
(reduce
|
||||||
|
(fn [[so-far a] next]
|
||||||
|
(let [new-a (+ a (or (:colspan next)
|
||||||
|
1))]
|
||||||
|
(if (> new-a n)
|
||||||
|
[(conj so-far next) new-a]
|
||||||
|
[so-far new-a])))
|
||||||
|
|
||||||
|
[[title] 1]
|
||||||
|
header)))
|
||||||
|
headers))))]
|
||||||
|
(into [new-table]
|
||||||
|
(split-table remaining n))))))
|
||||||
|
|
||||||
|
(defn break-apart-tables [tables]
|
||||||
|
(for [table tables
|
||||||
|
table (split-table table 10)]
|
||||||
|
table))
|
||||||
|
|
||||||
(defn make-pnl [args data]
|
(defn make-pnl [args data]
|
||||||
|
|
||||||
(let [data (<-graphql data)
|
(let [data (<-graphql data)
|
||||||
@@ -399,13 +465,14 @@
|
|||||||
report (summarize-pnl (PNLData. (assoc args :deltas true) data (by :db/id clients)))
|
report (summarize-pnl (PNLData. (assoc args :deltas true) data (by :db/id clients)))
|
||||||
output-stream (ByteArrayOutputStream.)]
|
output-stream (ByteArrayOutputStream.)]
|
||||||
(pdf/pdf
|
(pdf/pdf
|
||||||
[{:left-margin 15 :right-margin 15 :top-margin 15 :bottom-margin 15 :size :letter
|
(into
|
||||||
:font {:size 8}}
|
[{:left-margin 10 :right-margin 10 :top-margin 15 :bottom-margin 15 :size :letter
|
||||||
[:heading (str "Profit and Loss - " (str/join ", " (map :client/name clients)))]
|
:orientation :landscape
|
||||||
(into [:paragraph]
|
:font {:size 8}}
|
||||||
(map table->pdf (:summaries report)))
|
[:heading (str "Profit and Loss - " (str/join ", " (map :client/name clients)))]]
|
||||||
(into [:paragraph]
|
(for [table (break-apart-tables (concat (:summaries report)
|
||||||
(map table->pdf (:details report)))]
|
(:details report)))]
|
||||||
|
(table->pdf table)))
|
||||||
output-stream)
|
output-stream)
|
||||||
(.toByteArray output-stream)))
|
(.toByteArray output-stream)))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user