Dear Ben,
thanks for the interesting update. I was trying to run your report with the
most recent 2.4.0, but had to change a few lines until it was loaded correctly
at start-up. The changed file is attached.
However, when trying to run the report I still run into plenty of Scheme
errors (below). Hence, the report in the current form probably cannot be used
with gnucash-2.4.0. If you feel inclined to fix those bugs, I would happily
include it into SVN so that it can go into the next release, but currently
this doesn't quite work.
Also, I suggest to submit your contributions as "enhancement request" in
bugzilla http://wiki.gnucash.org/wiki/Bugzilla with the file attached, because
that way, your contribution doesn't get lost just as your 2008 email to
gnucash-devel did...
Best Regards,
Christian
Am Mittwoch, 12. Januar 2011 schrieb Benjamin Johnsen:
> Here is an update to the budget report that fixes some issues and adds some
> new features.
>
> See attached file.
>
> Thanks,
> Ben
>
;; -*-scheme-*-
(define-module (gnucash report standard-reports advanced-budget))
(use-modules (ice-9 slib))
(use-modules (gnucash gnc-module))
(gnc:module-load "gnucash/report/report-system" 0)
(gnc:module-load "gnucash/gnome-utils" 0) ;;for gnc-build-url
(define reportname (N_ "Advanced Budget"))
;; This function will generate a set of options that GnuCash
;; will use to display a dialog where the user can select
;; values for your report's parameters.
(define (options-generator)
(let* ((options (gnc:new-options))
;; This is just a helper function for making options.
;; See gnucash/src/scm/options.scm for details.
(add-option
(lambda (new-option)
(gnc:register-option options new-option))))
(add-option
(gnc:make-budget-option
(N_ "General")
(N_ "Budget")
"a"
(N_ "Budget for report")))
(add-option
(gnc:make-date-option
(N_ "General")
(N_ "Start Date")
"ba"
(N_ "Select which date to start generating the report from.")
(lambda () (cons 'relative 'start-cal-year))
#f
'both
'(
today
start-this-month
start-prev-month
start-current-quarter
start-prev-quarter
start-cal-year
start-prev-year
start-accounting-period)))
(add-option
(gnc:make-number-range-option
(N_ "General") (N_ "Number of Report Periods")
"bb" (N_ "Selects the number of periods to report from the start date.")
12 ;; default
0 ;; lower bound
100000 ;; upper bound
0 ;; number of decimals
1 ;; step size
))
(add-option
(gnc:make-simple-boolean-option
(N_ "General") (N_ "Generate Year to Date (YTD) column.")
"c"
(N_ "Selecting this adds a column at the end of the report that is a YTD.")
#t))
(add-option
(gnc:make-simple-boolean-option
(N_ "General") (N_ "Generate End of Year (EOY) column.")
"da"
(N_ "Selecting this adds a column at the end of the report that uses the date below to create a column of year totals.")
#f))
(add-option
(gnc:make-date-option
(N_ "General")
(N_ "End of Year (EOY) Date")
"db"
(N_ "Select which date to generating the EOY column for. ONLY THE YEAR IS USED.")
(lambda () (cons 'relative 'start-cal-year))
#f
'both
'(
today
start-this-month
start-prev-month
start-current-quarter
start-prev-quarter
start-cal-year
start-prev-year
start-accounting-period)))
;; This is a color option, defined by rgba values. A color value
;; is a list where the elements are the red, green, blue, and
;; alpha channel values respectively. The penultimate argument
;; (255) is the allowed range of rgba values. The final argument
;; (#f) indicates the alpha value should be ignored. You can get
;; a color string from a color option with gnc:color-option->html,
;; which will scale the values appropriately according the range.
(add-option
(gnc:make-color-option
(N_ "Color") (N_ "Background Color")
"a" (N_ "This is a color option")
(list #xff #xff #xff 0)
255
#f))
(add-option
(gnc:make-color-option
(N_ "Color") (N_ "Text Color")
"b" (N_ "This is a color option")
(list #x00 #x00 #x00 0)
255
#f))
(add-option
(gnc:make-color-option
(N_ "Color") (N_ "Column Color 1")
"c" (N_ "Select the color for one of the columns")
(list #xFF #xFF #xFF 0)
255
#f))
(add-option
(gnc:make-color-option
(N_ "Color") (N_ "Column Color 2")
"d" (N_ "Select the color for one of the columns")
(list #xDD #xDD #xDD 0)
255
#f))
(add-option
(gnc:make-color-option
(N_ "Color") (N_ "Row Color 1")
"e" (N_ "Select the color for one of the rows")
(list #xFF #xFF #xFF 0)
255
#f))
(add-option
(gnc:make-color-option
(N_ "Color") (N_ "Row Color 2")
"f" (N_ "Select the color for one of the rows")
(list #xB3 #xDB #xFF 0)
255
#f))
;; Selection for accounts we want to report on
(add-option
(gnc:make-account-list-option
;;Which tab it goes under
(N_ "Accounts")
;;Name on the box in the tab
(N_ "Select Accounts")
;;What order it has with other items in this tab
"a"
;;What appears when hovered over by the mouse
(N_ "Select accounts on which you want to report")
;;Default selection algorithm
(lambda () '())
;;Which accounts to select
#f
;;Are multiple selections allowed
#t))
(gnc:options-set-default-section options "General")
options))
;; This is the rendering function. It accepts a database of options
;; and generates an object of type <html-document>. See the file
;; report-html.txt for documentation;; the file report-html.scm
;; includes all the relevant Scheme code. The option database passed
;; to the function is one created by the options-generator function
;; defined above.
(define (dev-detailed-budget-renderer report-obj)
;; These are some helper functions for looking up option values.
(define (get-op section name)
(gnc:lookup-option (gnc:report-options report-obj) section name))
(define (op-value section name)
(gnc:option-value (get-op section name)))
;; Find the start period in the budget that is the same month and year as the start date
(define (get-start-period bdgt start-date)
(let ((bdgt-start-date (car (gnc-budget-get-period-start-date bdgt 0)))
(period 0))
(cond
((< start-date bdgt-start-date) (inexact->exact period))
(else (inexact->exact (+ period (- (gnc:date-to-month-fraction start-date) (gnc:date-to-month-fraction bdgt-start-date))))))))
;; Find the first period for the End of Year date given
(define (get-eoy-start-period bdgt eoy-date period)
(let ((bdgt-start-date (car (gnc-budget-get-period-start-date bdgt period))))
(cond
((= (gnc:date-get-year (localtime eoy-date)) (gnc:date-get-year (localtime bdgt-start-date))) (inexact->exact period))
(else (get-eoy-start-period bdgt eoy-date (+ period 1))))))
;; Find the number of periods for the End of Year date given
(define (get-eoy-end-period bdgt eoy-date period)
(let ((bdgt-start-date (car (gnc-budget-get-period-start-date bdgt period))))
(cond
((= (gnc:date-get-year (localtime eoy-date)) (gnc:date-get-year (localtime bdgt-start-date)))
(get-eoy-end-period bdgt eoy-date (+ period 1)))
(else (inexact->exact (- period 1))))))
;; Returns a list of dates for the budget
(define (budget-date-list bdgt period num-periods)
(cond
((= num-periods 0) (let ((eoy-string (string-append "End Of Year (" (gnc:date-get-year-string (localtime (car (gnc:date-option-absolute-time (op-value "General" "End of Year (EOY) Date"))))) ")")))
(if (op-value "General" "Generate Year to Date (YTD) column.")
(if (op-value "General" "Generate End of Year (EOY) column.")
(list "Year To Date" eoy-string)
'("Year To Date"))
(if (op-value "General" "Generate End of Year (EOY) column.")
(list eoy-string)
'()))))
(else (cons (gnc:date-get-month-year-string (gnc:timepair->date (gnc-timespec2timepair (gnc-budget-get-period-start-date bdgt (inexact->exact period)))))
(budget-date-list bdgt (+ period 1) (- num-periods 1))))))
;; Creates a row out of a title and a list of values
(define (build-html-row header bold span-list table values color-list align-list row-base-color)
(gnc:html-table-append-row! table (build-html-cell-list header bold span-list values color-list align-list row-base-color)))
;; Creates a list of cells
(define (build-html-cell-list header bold span-list values color-list align-list row-base-color)
(let ((cell (gnc:make-html-table-cell)))
;;Keep going until there are no values left to generate
(cond
((null? values) '())
(else (cons (build-html-cell header bold (car span-list) cell (car values) (car color-list) (car align-list) row-base-color)
(build-html-cell-list header bold (cdr span-list) (cdr values) (cdr color-list) (cdr align-list) row-base-color))))))
;; Turns a number into a hex-string
(define (number->hex-string number)
(number->string number 16))
;; Turns a hex-string into a number
(define (hex-string->number hex-string)
(string->number hex-string 16))
;; Returns a hex string of 2 characters if it is over 255
;; then it returns ff otherwise it returns the value of the number in a hex-string
(define (single-color-string num base-num)
(let ((sum-string (number->hex-string (- base-num (- 255 num)))))
(cond
((= (string-length sum-string) 2) sum-string)
((< (string-length sum-string) 2) (string-append "0" sum-string))
(else (number->hex-string 0)))))
;; Returns a hex string with a # and the beginning
;; Calculate each color independently so overflow from one doesn't mix into the next
(define (get-cell-color-string color base-color)
(string-append "#"
(single-color-string (hex-string->number (substring color 0 2)) (hex-string->number (substring base-color 0 2)))
(single-color-string (hex-string->number (substring color 2 4)) (hex-string->number (substring base-color 2 4)))
(single-color-string (hex-string->number (substring color 4 6)) (hex-string->number (substring base-color 4 6)))))
;; Creates a cell with the value passed in
(define (build-html-cell header bold span cell value color align row-base-color)
;; Start out by making the cell the first thing in the list
(let ((attribute-list (list cell))
(cell-color (get-cell-color-string color row-base-color))
(pre-value "")
(post-value ""))
;;Set the attribute list for a cell
(set! attribute-list (append attribute-list (list "td")))
;;If it is a header row increase the font by 1
(if (eqv? header #t)
(set! attribute-list (append attribute-list (list 'font-size "+1"))))
;;If the values should be bolded that set the bold tags
(if (eqv? bold #t) (let ()
(set! pre-value "<b>")
(set! post-value "</b>")))
;;List all the attributes that are desired
(set! attribute-list (append attribute-list (list 'attribute (list "align" align))))
(set! attribute-list (append attribute-list (list 'attribute (list "nowrap" "nowrap"))))
;;Set the color to the color passed in
(set! attribute-list (append attribute-list (list 'attribute (list "bgcolor" cell-color))))
;;If the number is negative set the text color to red
(if (gnc:gnc-monetary? value)
(if (gnc-numeric-negative-p (gnc:gnc-monetary-amount value))
(set! attribute-list (append attribute-list (list 'font-color "#FF0000")))))
;;Set the column span
(gnc:html-table-cell-set-colspan! cell span)
;;Apply all the attributes to the cell and then add the value to the cell
(apply gnc:html-table-cell-set-style! attribute-list)
(gnc:html-table-cell-append-objects! cell pre-value value post-value))
;;Return the cell
cell)
;; Returns the Year to Date budget account and difference value
(define (ytd-budget-act-diff bdgt acnt start-period curr-period bdgt-cc act-cc diff-cc comm)
(cond
((> start-period curr-period) (list (bdgt-cc 'getmonetary comm #f) (act-cc 'getmonetary comm #f) (diff-cc 'getmonetary comm #f)))
(else (let ()
(budget-act-diff-cc bdgt acnt start-period bdgt-cc act-cc diff-cc comm)
(ytd-budget-act-diff bdgt acnt (+ start-period 1) curr-period bdgt-cc act-cc diff-cc comm)))))
;; Returns the End of Year budget account and difference value
(define (eoy-budget-act-diff bdgt acnt start-period end-period bdgt-cc act-cc diff-cc comm)
(cond
((> start-period end-period) (list (bdgt-cc 'getmonetary comm #f) (act-cc 'getmonetary comm #f) (diff-cc 'getmonetary comm #f)))
(else (let ()
(budget-act-diff-cc bdgt acnt start-period bdgt-cc act-cc diff-cc comm)
(eoy-budget-act-diff bdgt acnt (+ start-period 1) end-period bdgt-cc act-cc diff-cc comm)))))
;; Returns the a commodity collect for the budget actual and difference amount
(define (budget-act-diff-cc bdgt acnt period bdgt-cc act-cc diff-cc comm)
(let* ((bdgt-val (gnc-budget-get-account-period-value bdgt acnt period))
(act-val (gnc-budget-get-account-period-actual-value bdgt acnt period))
(bdgt-denom (gnc:gnc-numeric-denom bdgt-val))
(act-denom (gnc:gnc-numeric-denom act-val)))
;; If the denom is not 100 make it 100
(if (< bdgt-denom 100)
(set! bdgt-val (gnc:make-gnc-numeric (* (gnc:gnc-numeric-num bdgt-val) (/ 100 bdgt-denom))
100)))
(if (< act-denom 100)
(set! act-val (gnc:make-gnc-numeric (* (gnc:gnc-numeric-num act-val) (/ 100 act-denom))
100)))
;; If it is an INCOME account negate the number because of the way it is stored
(if (= (xaccAccountGetType acnt) ACCT-TYPE-INCOME)
(set! act-val (gnc-numeric-neg act-val)))
;;Create commodity collectors to be able to be able subtract the two values from each other for a diff
(bdgt-cc 'add comm bdgt-val)
(act-cc 'add comm act-val)
;;Clear the diff-cc back to zero
(diff-cc 'minusmerge diff-cc #f)
;; If it is an INCOME account subtract the budget from the actual
;; Else it is an Expense account so subtract the actual from the budget
(cond
((= (xaccAccountGetType acnt) ACCT-TYPE-INCOME)
(let ()
(diff-cc 'merge act-cc #f)
(diff-cc 'minusmerge bdgt-cc #f)))
(else
(let ()
(diff-cc 'merge bdgt-cc #f)
(diff-cc 'minusmerge act-cc #f))))))
;; Returns a list that has a budget values, account values and the difference for all the periods
(define (account-budget-act-diff-row bdgt acnt start-period num-periods)
(let ((comm (xaccAccountGetCommodity acnt))
(bdgt-cc (gnc:make-commodity-collector))
(act-cc (gnc:make-commodity-collector))
(diff-cc (gnc:make-commodity-collector)))
;;If the number of periods is 0 stop processing
;; When returning the commodity values just return a value with the commodity symbol in from
(cond
((= num-periods 0) (let* ((ytd-start-period (get-start-period bdgt (car (gnc:get-start-cal-year))))
(ytd-curr-period (get-start-period bdgt (car (gnc:get-start-this-month))))
(eoy-start-period
(get-eoy-start-period bdgt (car (gnc:date-option-absolute-time (op-value "General" "End of Year (EOY) Date"))) 0))
(eoy-end-period
(get-eoy-end-period bdgt (car (gnc:date-option-absolute-time (op-value "General" "End of Year (EOY) Date"))) eoy-start-period)))
(if (op-value "General" "Generate Year to Date (YTD) column.")
(if(op-value "General" "Generate End of Year (EOY) column.")
(append (ytd-budget-act-diff bdgt
acnt
ytd-start-period
ytd-curr-period
bdgt-cc
act-cc
diff-cc
comm)
(eoy-budget-act-diff bdgt
acnt
eoy-start-period
eoy-end-period
bdgt-cc
act-cc
diff-cc
comm))
(ytd-budget-act-diff bdgt
acnt
ytd-start-period
ytd-curr-period
bdgt-cc
act-cc
diff-cc
comm))
(if(op-value "General" "Generate End of Year (EOY) column.")
(eoy-budget-act-diff bdgt
acnt
eoy-start-period
eoy-end-period
bdgt-cc
act-cc
diff-cc
comm)
'()))))
(else (let ((bdgt-val (gnc-budget-get-account-period-value bdgt acnt start-period))
(act-val (gnc-budget-get-account-period-actual-value bdgt acnt start-period)))
(budget-act-diff-cc bdgt acnt start-period bdgt-cc act-cc diff-cc comm)
;;Return the budget actual and differential amount
(append (list (bdgt-cc 'getmonetary comm #f)
(act-cc 'getmonetary comm #f)
(diff-cc 'getmonetary comm #f))
(account-budget-act-diff-row bdgt acnt (+ start-period 1) (- num-periods 1))))))))
;; Returns a list of accounts for all the periods that have budget items
(define (budget-account-list bdgt acnt-list start-period num-periods)
;;If the account list is empty we are done
(if (null? acnt-list)
;; We are done return a blank list
'()
;; Else check to see if the next account is in the budget
;; If it is cons the account to with the rest of the accounts
;; Else move on to the next account
(let ((acnt (car acnt-list)))
(if (account-in-budget bdgt acnt start-period num-periods)
(cons acnt (budget-account-list bdgt (cdr acnt-list) start-period num-periods))
(budget-account-list bdgt (cdr acnt-list) start-period num-periods)))))
;; Returns a true if account found in budget during the periods
(define (account-in-budget bdgt acnt start-period num-periods)
(cond
((= num-periods 0) #f)
((gnc-budget-is-account-period-value-set bdgt acnt start-period) #t)
(else (account-in-budget bdgt acnt (+ start-period 1) (- num-periods 1)))))
;; Adds values in the account list of values to the totals list
(define (add-acnt-vals-to-totals acnt-values totals-list)
(cond
((null? acnt-values) '())
(else (let* ((acnt-val (gnc:gnc-monetary-amount (car acnt-values)))
(total-val (gnc:gnc-monetary-amount (car totals-list)))
(total-cc (gnc:make-commodity-collector))
(comm (gnc:gnc-monetary-commodity (car acnt-values))))
(total-cc 'add comm total-val)
(total-cc 'add comm acnt-val)
(cons (total-cc 'getmonetary comm #f) (add-acnt-vals-to-totals (cdr acnt-values) (cdr totals-list)))))))
;; Adds a row for each account in the account list
;; with a budget value, real account value and difference for every period
(define (build-value-html-table table acnt-list bdgt start-period num-periods color-list align-list totals-list row-color-1 row-color-2)
(let ((row-color (if (odd? (length acnt-list))
row-color-1
row-color-2)))
;;When the account list is empty stop
;;Otherwise add another row starting with account name and call the function again
(cond
((null? acnt-list) (let ()
;;Build the Totals Row
(build-html-row #f #t (gen-list (+ (length totals-list) 1) 1) table (cons "Totals" totals-list) color-list align-list row-color)
totals-list))
(else (let* ((acnt (car acnt-list))
(acnt-values (account-budget-act-diff-row bdgt acnt start-period num-periods)) ;Generates a list of values of the account
(temp-totals-list (add-acnt-vals-to-totals acnt-values totals-list)) ;Adds the values to the total values
(acnt-name-values (cons (gnc:html-account-anchor acnt) acnt-values))) ;Adds the account name to the beginning of the account value list
(build-html-row #f #f (gen-list (length acnt-name-values) 1) table acnt-name-values color-list align-list row-color)
(build-value-html-table table (cdr acnt-list) bdgt start-period num-periods color-list align-list temp-totals-list row-color-1 row-color-2))))))
;; Span List for Date header
;; The first cell is 1 and all the rest are 3
;; The first one is for account name the rest or for periods
(define (date-header-span-list num-periods)
(cons '1 (gen-list num-periods 3)))
;; Generate a list of the lenght of num-times and of the value passed in
(define (gen-list num-times value)
(cond
((= num-times 0) '())
(else (cons value (gen-list (- num-times 1) value)))))
;; Budget Actual Difference header builder
;; This builds a list with Budget Actual and Difference
;; repeated for the number of periods passed in
(define (budget-actual-diff-header num-periods)
(cond
((= num-periods 0) '())
(else (append (list 'Budget 'Actual 'Diff) (budget-actual-diff-header (- num-periods 1))))))
;; Build a color list for the date table
(define (date-header-color-list num-periods)
(cond
((= num-periods 0) '())
((odd? (inexact->exact num-periods)) (cons (gnc:color-option->hex-string (get-op "Color" "Column Color 2")) (date-header-color-list (- num-periods 1))))
((even? (inexact->exact num-periods)) (cons (gnc:color-option->hex-string (get-op "Color" "Column Color 1")) (date-header-color-list (- num-periods 1))))))
;; Build a list with 3 one color and 3 another color alternating every 3
(define (three-on-three-off-color num-periods)
(let ((color_1 (gnc:color-option->hex-string (get-op "Color" "Column Color 2")))
(color_2 (gnc:color-option->hex-string (get-op "Color" "Column Color 1"))))
(cond
((= num-periods 0) '())
((odd? (inexact->exact num-periods)) (append (three-on-three-off-color (- num-periods 1))
(list color_1 color_1 color_1)))
((even? (inexact->exact num-periods)) (append (three-on-three-off-color (- num-periods 1))
(list color_2 color_2 color_2))))))
;; Build a header alignment list
(define (header-align-list num-periods)
(cond
((= num-periods 0) '())
(else (cons 'center (header-align-list (- num-periods 1))))))
;; Build an account values alignment list
(define (account-values-align-list num-periods)
(cond
((= num-periods 0) '())
(else (cons 'right (account-values-align-list (- num-periods 1))))))
;; Generates a list of gnc-monetary for collecting totals
(define (gen-list-monetary num-comm acnt)
(cond
((= num-comm 0))
(else (let* ((comm (xaccAccountGetCommodity acnt))
(temp-cc (gnc:make-commodity-collector)));
(temp-cc 'add comm (gnc:make-gnc-numeric 0 100))
(cons (temp-cc 'getmonetary comm #f) (gen-list-monetary (- num-comm 1) acnt))))))
;; Generates a row of cells that creates line
(define (gen-horiz-line num-cells table)
(gnc:html-table-append-row! table (gen-horiz-line-cell-list num-cells)))
;; Generates a list of cells that create a horizonal line
(define (gen-horiz-line-cell-list num-cells)
(cond
((= num-cells 0) '())
(else (cons (gen-horiz-line-cell num-cells) (gen-horiz-line-cell-list (- num-cells 1))))))
;; Generate a list of cells
(define (gen-horiz-line-cell num-cells)
(let* ((cell (gnc:make-html-table-cell))
(attribute-list (list cell))
(pre-html-value "<b><sub>")
(post-html-value "</sub></b>")
(html-value ""))
(set! attribute-list (append attribute-list (list "td")))
;;Set the color to BLACK
(set! attribute-list (append attribute-list (list 'attribute (list "bgcolor" "#FFFFFF"))))
;;Decrease the font size to make the row thinner
(set! attribute-list (append attribute-list (list 'font-size "1")))
;;Apply all the attributes to the cell and then add the value to the cell
(apply gnc:html-table-cell-set-style! attribute-list)
;;Set the column span
; (gnc:html-table-cell-set-colspan! cell num-cells)
;;Add an horizonal line to the cell
;; This is a hack. GnuCash doesn't let you put nothing in a cell.
;; If left empty GnCash puts in a <BR> tag which causes the cell to
;; be the height of the font. If it could be left empty then the
; height= could be used in the HTML to allw the cell to be made
;; thin but since the cell can't be left blank we put in a "." and
;; superscript it as small as it will go.
(gnc:html-table-cell-append-objects! cell pre-html-value html-value post-html-value)
;;Return the cell
cell))
;; Return and list that is a diff of the income and expense lists passed in
;; Pass in the col-num because every 3 column is a diff column and has to be treated differently
(define (diff-income-expense-list income-list expense-list col-num)
(cond
((null? income-list) '())
(else (let* ((income-val (cond ;;Every third column is a diff row and the income diff has had its value inverted so we want to undo that invert on the diff columns
((= col-num 3)
(set! col-num 0)
(gnc:gnc-monetary-amount (gnc:monetary-neg (car income-list))))
(else
(gnc:gnc-monetary-amount (car income-list)))))
(expense-val (gnc:gnc-monetary-amount (gnc:monetary-neg (car expense-list)))) ;;Make the expense negative so when it adds it actually subtracts
(diff-cc (gnc:make-commodity-collector))
(comm (gnc:gnc-monetary-commodity (car income-list))))
(diff-cc 'add comm income-val)
(diff-cc 'add comm expense-val) ;;Expense was made negative early so this is actually a subtract
(cons (diff-cc 'getmonetary comm #f) (diff-income-expense-list (cdr income-list) (cdr expense-list) (+ col-num 1)))))))
;; This adds the Date, Budget Actual and Diff header to all the columns
(define (date-bdgt-act-diff-header-table table budget num-col start-period num-periods)
;;Build the Date Header Row
(build-html-row #t
#t
(date-header-span-list (+ num-col 1))
table
(cons "Accounts" (budget-date-list budget start-period num-periods))
(cons (gnc:color-option->hex-string (get-op "Color" "Column Color 1"))
(reverse (date-header-color-list num-col)))
(header-align-list (+ num-col 1))
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))
;;Build the Budget Actual Diff Header Row
(build-html-row #f
#f
(gen-list (+ (* num-col 3) 1) 1)
table
(cons " " (budget-actual-diff-header num-col))
(cons (gnc:color-option->hex-string (get-op "Color" "Column Color 1"))
(three-on-three-off-color num-col))
(header-align-list (+ (* num-col 3) 1))
(gnc:color-option->hex-string (get-op "Color" "Row Color 1"))))
;; Generates part of the HTML table for a specific Account Catagory (ie Expense, Income)
(define (build-catagory-table table acnt-list catagory-name budget start-period num-periods num-col)
;;Build Catagory Header
(build-html-row #t
#t
(gen-list (+ (* num-col 3) 1) 1)
table
(cons catagory-name (gen-list (* num-col 3) " "))
(gen-list (+ (* num-col 3) 1) (gnc:color-option->hex-string (get-op "Color" "Column Color 1")))
(gen-list (+ (* num-col 3) 1) 'left)
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))
;;Put in the header for all the coloums
(date-bdgt-act-diff-header-table table budget num-col start-period num-periods)
;;Build the rest of the table (This returns the totals array so make sure it is last or sets a variable that is returned)
(build-value-html-table table
acnt-list
budget
start-period
num-periods
(cons (gnc:color-option->hex-string (get-op "Color" "Column Color 1"))
(three-on-three-off-color num-col))
(cons 'left (account-values-align-list (* num-col 3)))
(gen-list-monetary (* num-col 3) (car acnt-list))
(if (odd? (length acnt-list))
(gnc:color-option->hex-string (get-op "Color" "Row Color 2")) ;;This ensures that the first row always starts with the same color
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))
(if (odd? (length acnt-list))
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")) ;;This ensures that the first row always starts with the same color
(gnc:color-option->hex-string (get-op "Color" "Row Color 2")))))
;; Add the totals at to the html table
(define (build-totals-table table budget num-col start-period num-periods income-totals-list expense-totals-list diff-totals-list)
;;Put in the Totals header
(build-html-row #t
#t
(gen-list (+ (* num-col 3) 1) 1)
table
(cons "Totals" (gen-list (* num-col 3) " "))
(gen-list (+ (* num-col 3) 1) (gnc:color-option->hex-string (get-op "Color" "Column Color 1")))
(gen-list (+ (* num-col 3) 1) 'left)
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))
;;Put in the header for all the columns
(date-bdgt-act-diff-header-table table budget num-col start-period num-periods)
;;Put in the income row
(build-html-row #f
#f
(gen-list (length income-totals-list) 1)
table
income-totals-list
(cons (gnc:color-option->hex-string (get-op "Color" "Column Color 1"))
(three-on-three-off-color num-col))
(cons 'left (account-values-align-list (* num-col 3)))
(gnc:color-option->hex-string (get-op "Color" "Row Color 2")))
;;Put in the expense row
(build-html-row #f
#f
(gen-list (length expense-totals-list) 1)
table
expense-totals-list
(cons (gnc:color-option->hex-string (get-op "Color" "Column Color 1"))
(three-on-three-off-color num-col))
(cons 'left (account-values-align-list (* num-col 3)))
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))
;;Insert a horizontal line before the totals row
; (gen-horiz-line (+ (* num-col 3) 1) table)
;;Put in the difference row
(build-html-row #f
#t
(gen-list (length diff-totals-list) 1)
table
diff-totals-list
(cons (gnc:color-option->hex-string (get-op "Color" "Column Color 1"))
(three-on-three-off-color num-col))
(cons 'left (account-values-align-list (* num-col 3)))
(gnc:color-option->hex-string (get-op "Color" "Row Color 2"))))
;; This is the main routine that puts it all together
(let* ((document (gnc:make-html-document))
(table (gnc:make-html-table))
(income-totals '())
(expense-totals '())
(budget (op-value "General" "Budget"))
(num-periods (op-value "General" "Number of Report Periods"))
(start-period (get-start-period budget (car (gnc:date-option-absolute-time (op-value "General" "Start Date")))))
(acnt-list (budget-account-list budget
(gnc-account-get-descendants-sorted (gnc-get-current-root-account))
start-period
num-periods))
(num-col (if (op-value "General" "Generate Year to Date (YTD) column.")
(if (op-value "General" "Generate End of Year (EOY) column.")
(+ num-periods 2)
(+ num-periods 1))
(if (op-value "General" "Generate End of Year (EOY) column.")
(+ num-periods 1)
num-periods))))
;;Setup the Title and font color and back ground color of the document
(gnc:html-document-set-title! document reportname)
(gnc:html-document-set-style! document "body"
'attribute (list "bgcolor" (gnc:color-option->html (get-op "Color" "Background Color")))
'font-color (gnc:color-option->html (get-op "Color" "Text Color")))
(gnc:html-table-set-style! table "table"
'attribute (list "border" 0)
'attribute (list "cellspacing" 0)
'attribute (list "cellpadding" 2))
;;Check to see if the account list is empty. If it is generate a table with an error message in it
(cond
((<= (length acnt-list) 1) (let* ((date-str (strftime "%x" (localtime (car (gnc:date-option-absolute-time (op-value "General" "Start Date"))))))
(print-msg (string-append "No Accounts have a Budget Value on or after " date-str ". Either change the \"Start Date\" in the options or add values to the budget that correspond to the \"Start Date\".")))
;;Put a blank row inbetween catagories
(build-html-row #f
#f
'(1)
table
(list print-msg)
(list (gnc:color-option->hex-string (get-op "Color" "Column Color 1")))
'('left)
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))))
(else (
;; Build the Income Table
(set! income-totals (build-catagory-table table
(assoc-ref (gnc:decompose-accountlist acnt-list) ACCT-TYPE-INCOME)
"Income"
budget
start-period
num-periods
num-col))
;;Put a blank row inbetween catagories
(build-html-row #f
#f
(gen-list (+ (* num-col 3) 1) 1)
table
(gen-list (+ (* num-col 3) 1) " ")
(gen-list (+ (* num-col 3) 1) (gnc:color-option->hex-string (get-op "Color" "Column Color 1")))
(gen-list (+ (* num-col 3) 1) 'left)
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))
;;Build the Expense Table
(set! expense-totals (build-catagory-table table
(assoc-ref (gnc:decompose-accountlist acnt-list) ACCT-TYPE-EXPENSE)
"Expense"
budget
start-period
num-periods
num-col))
;;Put a blank row inbetween catagories
(build-html-row #f
#f
(gen-list (+ (* num-col 3) 1) 1)
table
(gen-list (+ (* num-col 3) 1) " ")
(gen-list (+ (* num-col 3) 1) (gnc:color-option->hex-string (get-op "Color" "Column Color 1")))
(gen-list (+ (* num-col 3) 1) 'left)
(gnc:color-option->hex-string (get-op "Color" "Row Color 1")))
;;Build and put in the totals table at the end
(build-totals-table table
budget
num-col
start-period
num-periods
(cons "Income Totals" income-totals)
(cons "Expense Totals" expense-totals)
(cons "Difference Totals" (diff-income-expense-list income-totals expense-totals 1))))))
;;Add the generated table to the document
(gnc:html-document-add-object! document table)
;;Return the document
document)
)
;; Here we define the actual report with gnc:define-report
(gnc:define-report
'version 1
'name reportname
'report-guid "39e5c50bac902d38308432ba10cae0df"
'menu-tip (N_ "A detailed budget report displaying budget totals and balance.")
'menu-path (list gnc:menuname-budget)
'options-generator options-generator
'renderer dev-detailed-budget-renderer)
_______________________________________________
gnucash-devel mailing list
[email protected]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel