Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Category charts upgrade to chart allocation ratios instead of absolute values. #1272

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 10 additions & 3 deletions gnucash/report/html-chart.scm
Expand Up @@ -51,6 +51,8 @@
(export gnc:html-chart-set-currency-iso!)
(export gnc:html-chart-currency-symbol)
(export gnc:html-chart-set-currency-symbol!)
(export gnc:html-chart-format-style)
(export gnc:html-chart-set-format-style!)
(export gnc:html-chart-render)
(export gnc:html-chart-set-custom-x-axis-ticks?!)
(export gnc:html-chart-set-title!)
Expand Down Expand Up @@ -144,13 +146,14 @@

(define-record-type <html-chart>
(make-html-chart width height chart-options currency-iso
currency-symbol custom-x-axis-ticks? custom-y-axis-ticks?)
currency-symbol format-style custom-x-axis-ticks? custom-y-axis-ticks?)
html-chart?
(width html-chart-width html-chart-set-width)
(height html-chart-height html-chart-set-height)
(chart-options html-chart-chart-options html-chart-set-chart-options)
(currency-iso html-chart-currency-iso html-chart-set-currency-iso)
(currency-symbol html-chart-currency-symbol html-chart-set-currency-symbol)
(format-style html-chart-format-style html-chart-set-format-style)
(custom-x-axis-ticks? html-chart-custom-x-axis-ticks?
html-chart-set-custom-x-axis-ticks?)
(custom-y-axis-ticks? html-chart-custom-y-axis-ticks?
Expand All @@ -166,6 +169,8 @@
(define gnc:html-chart-set-currency-iso! html-chart-set-currency-iso)
(define gnc:html-chart-currency-symbol html-chart-currency-symbol)
(define gnc:html-chart-set-currency-symbol! html-chart-set-currency-symbol)
(define gnc:html-chart-format-style html-chart-format-style)
(define gnc:html-chart-set-format-style! html-chart-set-format-style)
(define gnc:html-chart-custom-x-axis-ticks? html-chart-custom-x-axis-ticks?)
(define gnc:html-chart-set-custom-x-axis-ticks?! html-chart-set-custom-x-axis-ticks?)
(define gnc:html-chart-custom-y-axis-ticks? html-chart-custom-y-axis-ticks?)
Expand Down Expand Up @@ -254,6 +259,7 @@
(cons 'text ""))))))
"XXX" ;currency-iso
"\u00A4" ;currency-symbol
"currency";format-style
#t ;custom x-axis ticks?
#t ;custom y-axis ticks?
))
Expand Down Expand Up @@ -341,10 +347,10 @@
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
var toLocaleStringSupportsOptions = (typeof Intl == 'object' && Intl && typeof Intl.NumberFormat == 'function');

// format a number e.g. 2.5 into monetary e.g. \"$2.50\"
// format a number e.g. 2.5 into monetary e.g. \"$2.50\" or other style formsty
function numformat(amount) {
if (toLocaleStringSupportsOptions) {
return amount.toLocaleString(undefined, {style:'currency', currency:curriso});
return amount.toLocaleString(undefined, {style:formsty, currency:curriso});
} else {
return currsym + amount.toLocaleString();
}
Expand Down Expand Up @@ -457,6 +463,7 @@ document.getElementById(chartid).onclick = function(evt) {
(push (format #f "<script id='script-~a'>\n" id))
(push (format #f "var curriso = ~s;\n" (gnc:html-chart-currency-iso chart)))
(push (format #f "var currsym = ~s;\n" (gnc:html-chart-currency-symbol chart)))
(push (format #f "var formsty = ~s;\n" (gnc:html-chart-format-style chart)))
(push (format #f "var chartid = ~s;\n" id))
(push (format #f "var jumpid = 'jump-~a';\n" id))
(push (format #f "var loadstring = ~s;\n" (G_ "Load")))
Expand Down
92 changes: 61 additions & 31 deletions gnucash/report/reports/standard/category-barchart.scm
Expand Up @@ -34,6 +34,12 @@
(use-modules (gnucash app-utils))
(use-modules (gnucash report))

;; useful functions
(define (safe-/ x y)
(if (zero? y) 0 (/ x y)))
(define (l1norm numbers)
(fold (lambda (a b) (+ (abs a) b)) 0 numbers))

;; The option names are defined here to 1. save typing and 2. avoid
;; spelling errors. The *reportnames* are defined here (and not only
;; once at the very end) because I need them to define the "other"
Expand Down Expand Up @@ -176,6 +182,15 @@ developing over time"))
"e" (N_ "Display a table of the selected data.")
#f))

;; contributed by https://github.com/exxus
;; https://github.com/Gnucash/gnucash/pull/1272
(add-option
(gnc:make-simple-boolean-option
gnc:pagename-display
(N_ "Replace amounts with percentage ratios.")
"e1" (N_ "Display percentage contribution of each account to the Gand Total instead of amounts.")
#f))

(gnc:options-add-plot-size!
options gnc:pagename-display
optname-plot-width optname-plot-height "f" (cons 'percent 100.0) (cons 'percent 100.0))
Expand Down Expand Up @@ -246,6 +261,7 @@ developing over time"))
(work-to-do 0)
(all-data #f)
(show-table? (get-option gnc:pagename-display (N_ "Show table")))
(ratio-chart? (get-option gnc:pagename-display (N_ "Replace amounts with percentage ratios.")))
(document (gnc:make-html-document))
(chart (gnc:make-html-chart))
(topl-accounts (gnc:filter-accountlist-type
Expand Down Expand Up @@ -328,23 +344,21 @@ developing over time"))
;; created.
(other-anchor ""))

;; Converts a commodity-collector into gnc-monetary in the report's
;; currency using the exchange-fn calculated above. Returns a gnc-monetary
;; Converts a commodity-collector into amount in the report's
;; currency using the exchange-fn calculated above. Returns an amount
;; multiplied by the averaging-multiplier (smaller than one; multiplication
;; instead of division to avoid division-by-zero issues) in case
;; the user wants to see the amounts averaged over some value.
(define (collector->monetary c date)
(gnc:make-gnc-monetary
report-currency
(* averaging-multiplier
(gnc:gnc-monetary-amount
(gnc:sum-collector-commodity
c report-currency
(lambda (a b) (exchange-fn a b date)))))))
(define (collector->report-currency-amount c date)
(* averaging-multiplier
(gnc:gnc-monetary-amount
(gnc:sum-collector-commodity
c report-currency
(lambda (a b) (exchange-fn a b date))))))

(define (all-zeros data)
(cond
((gnc:gnc-monetary? data) (zero? (gnc:gnc-monetary-amount data)))
((number? data) (zero? data))
((pair? data) (every all-zeros data))
(else (error 'huh))))

Expand Down Expand Up @@ -396,11 +410,11 @@ developing over time"))
(loop (cdr list-of-mon-collectors)
(cdr dates-list)
(cons (if do-intervals?
(collector->monetary
(collector->report-currency-amount
(gnc:collector- (cadr list-of-mon-collectors)
(car list-of-mon-collectors))
(cadr dates-list))
(collector->monetary
(collector->report-currency-amount
(car list-of-mon-collectors)
(car dates-list)))
result))))))
Expand Down Expand Up @@ -462,9 +476,7 @@ developing over time"))
;; Sort the account list according to the account code field.
(set! all-data
(sort
(filter (lambda (l)
(not (zero? (gnc:gnc-monetary-amount
(apply gnc:monetary+ (cadr l))))))
(filter (lambda (l) (not (zero? (apply + (cadr l)))))
(traverse-accounts 1 topl-accounts))
(case sort-method
((alphabetical)
Expand All @@ -480,8 +492,8 @@ developing over time"))
(xaccAccountGetCode (car b)))))
((amount)
(lambda (a b)
(> (gnc:gnc-monetary-amount (apply gnc:monetary+ (cadr a)))
(gnc:gnc-monetary-amount (apply gnc:monetary+ (cadr b)))))))))
(> (apply + (cadr a))
(apply + (cadr b))))))))

(cond
((or (null? all-data) (all-zeros (map cadr all-data)))
Expand All @@ -494,7 +506,12 @@ developing over time"))
(let* ((dates-list (if do-intervals?
(list-head dates-list (1- (length dates-list)))
dates-list))
(date-string-list (map qof-print-date dates-list)))
(date-string-list (map qof-print-date dates-list))
(list-of-rows (apply zip (map cadr all-data)))

;; total amount with l1norm, so that positive and
;; negative amounts add up
(row-totals (map l1norm list-of-rows)))

;; Set chart title, subtitle etc.
(gnc:html-chart-set-type!
Expand All @@ -514,7 +531,8 @@ developing over time"))

(gnc:html-chart-set-data-labels! chart date-string-list)
(gnc:html-chart-set-y-axis-label!
chart (gnc-commodity-get-mnemonic report-currency))
chart (if ratio-chart? "Ratio"
(gnc-commodity-get-mnemonic report-currency)))

;; If we have too many categories, we sum them into a new
;; 'other' category and add a link to a new report with just
Expand All @@ -523,7 +541,7 @@ developing over time"))
(let* ((start (take all-data (1- max-slices)))
(finish (drop all-data (1- max-slices)))
(other-sum (map
(lambda (l) (apply gnc:monetary+ l))
(lambda (l) (apply + l))
(apply zip (map cadr finish)))))
(set! all-data
(append start
Expand Down Expand Up @@ -551,7 +569,7 @@ developing over time"))
((string? acct) (car series))
(show-fullname? (gnc-account-get-full-name acct))
(else (xaccAccountGetName acct))))
(amounts (map gnc:gnc-monetary-amount (cadr series)))
(amounts (cadr series))
(stack (if stacked? "default" (number->string stack)))
(fill (eq? chart-type 'barchart))
(urls (cond
Expand All @@ -578,8 +596,8 @@ developing over time"))
(gnc-account-get-full-name acct)
(xaccAccountGetName acct)))))))))
(gnc:html-chart-add-data-series!
chart label amounts color
'stack stack 'fill fill 'urls urls)))
chart label (if ratio-chart? (map safe-/ amounts row-totals) amounts)
color 'stack stack 'fill fill 'urls urls)))
all-data
(gnc:assign-colors (length all-data))
(iota (length all-data)))
Expand All @@ -589,6 +607,8 @@ developing over time"))
chart (gnc-commodity-get-mnemonic report-currency))
(gnc:html-chart-set-currency-symbol!
chart (gnc-commodity-get-nice-symbol report-currency))
(gnc:html-chart-set-format-style!
chart (if ratio-chart? "percent" "currency"))

(gnc:report-percent-done 98)
(gnc:html-document-add-object! document chart)
Expand All @@ -601,18 +621,28 @@ developing over time"))
(define (make-cell contents)
(gnc:make-html-table-cell/markup "number-cell" contents))

(define (make-cell-percent amt grandt)
(gnc:make-html-table-cell/markup "number-cell" (* (safe-/ amt grandt) 100) " %"))

(define (make-monetary-cell amount)
(make-cell (gnc:make-gnc-monetary report-currency amount)))

(for-each
(lambda (date row)
(lambda (date row row-total)
(gnc:html-table-append-row!
table

(append (list (make-cell date))
(map make-cell row)
(map (if ratio-chart?
(cut make-cell-percent <> row-total)
make-monetary-cell)
row)
(if cols>1?
(list
(make-cell (apply gnc:monetary+ row)))
(list (make-monetary-cell (apply + row)))
'()))))
date-string-list
(apply zip (map cadr all-data)))
list-of-rows
row-totals)

(gnc:html-table-set-col-headers!
table
Expand Down Expand Up @@ -655,10 +685,10 @@ developing over time"))
(list date)
row
(if (pair? (cdr all-data))
(list (apply gnc:monetary+ row))
(list (apply + row))
'())))
(map (cut gnc-print-time64 <> iso-date) dates-list)
(apply zip (map cadr all-data)))))))))))))))
list-of-rows)))))))))))))

(unless (gnc:html-document-export-string document)
(gnc:html-document-set-export-error document (G_ "No exportable data")))
Expand Down
40 changes: 24 additions & 16 deletions gnucash/report/reports/standard/net-charts.scm
Expand Up @@ -227,9 +227,16 @@
(chart (gnc:make-html-chart)))

;; This exchanges the commodity-collector 'c' to one single
;; 'report-currency' according to the exchange-fn. Returns a gnc:monetary
(define (collector->monetary c date)
(gnc:sum-collector-commodity c report-currency (cut exchange-fn <> <> date)))
;; 'report-currency' according to the exchange-fn. Returns an
;; amount.
(define (collector->report-currency-amount c date)
(fold
(lambda (mon acc)
(+ acc (gnc-numeric-convert
(gnc:gnc-monetary-amount (exchange-fn mon report-currency date))
(gnc-commodity-get-fraction report-currency)
GNC-RND-ROUND)))
0 (c 'format gnc:make-gnc-monetary #f)))

;; gets an account alist balances
;; output: (list acc bal0 bal1 bal2 ...)
Expand All @@ -244,8 +251,8 @@
#:nosplit->elt (gnc:make-gnc-monetary comm 0)))))

;; This calculates the balances for all the 'account-balances' for
;; each element of the list 'dates'. Uses the collector->monetary
;; conversion function above. Returns a list of gnc-monetary.
;; each element of the list 'dates'. Uses the collector->report-currency-amount
;; conversion function above. Returns a list of amounts.
(define (process-datelist account-balances dates left-col?)

(define accountlist
Expand Down Expand Up @@ -283,7 +290,7 @@
(loop (cdr dates)
(cdr acct-balances)
(cons
(collector->monetary
(collector->report-currency-amount
(if inc-exp?
(gnc:collector- (car acct-balances) (cadr acct-balances))
(car acct-balances))
Expand Down Expand Up @@ -312,7 +319,7 @@
(subtrahend-balances (process-datelist account-balancelist dates-list #f))
(dummy (gnc:report-percent-done 80))

(difference-balances (map gnc:monetary+ minuend-balances subtrahend-balances))
(difference-balances (map + minuend-balances subtrahend-balances))

(dates-list (if inc-exp?
(list-head dates-list (1- (length dates-list)))
Expand Down Expand Up @@ -344,7 +351,7 @@
(gnc:html-chart-add-data-series!
chart
(if inc-exp? (G_ "Income") (G_ "Assets"))
(map gnc:gnc-monetary-amount minuend-balances)
minuend-balances
"#0074D9"
'fill (not linechart?)
'pointRadius markers
Expand All @@ -365,7 +372,7 @@
(gnc:html-chart-add-data-series!
chart
(if inc-exp? (G_ "Expense") (G_ "Liabilities"))
(map - (map gnc:gnc-monetary-amount subtrahend-balances))
(map - subtrahend-balances)
"#FF4136"
'fill (not linechart?)
'pointRadius markers
Expand All @@ -387,17 +394,16 @@
(gnc:html-chart-add-data-series!
chart
(if inc-exp? (G_ "Net Profit") (G_ "Net Worth"))
(map gnc:gnc-monetary-amount difference-balances)
difference-balances
"#2ECC40"
'fill (not linechart?)
'pointRadius markers
'borderWidth line-width))

;; Test for all-zero data here.
(if (gnc:not-all-zeros (map gnc:gnc-monetary-amount
(append minuend-balances
subtrahend-balances
difference-balances)))
(if (gnc:not-all-zeros (append minuend-balances
subtrahend-balances
difference-balances))
(begin
(gnc:html-document-add-object! document chart)
(if show-table?
Expand Down Expand Up @@ -429,8 +435,10 @@
(cons date
(map
(cut gnc:make-html-table-cell/markup "number-cell" <>)
(append (if show-sep? (list minuend subtrahend) '())
(if show-net? (list difference) '()))))))
(map
(cut gnc:make-gnc-monetary report-currency <>)
(append (if show-sep? (list minuend subtrahend) '())
(if show-net? (list difference) '())))))))
date-string-list
minuend-balances
subtrahend-balances
Expand Down
Expand Up @@ -243,6 +243,16 @@
(test-equal "auto"
'("Auto")
(sxml->table-row-col sxml 1 0 2))

;; test ratios
(set-option expense-options gnc:pagename-display "Replace amounts with percentage ratios." #t)
(let ((sxml (gnc:options->sxml expense-report-uuid expense-options
"test-standard-category-report"
"ratios"
#:strip-tag "script")))
(test-equal "multi-acct ratios"
'("02/02/22" "57.14 %" "14.29 %" "14.29 %" "14.29 %" "$14.00")
(sxml->table-row-col sxml 1 2 #f)))
(test-end "multi-acct-test"))))


Expand Down