Report Development


Table of Contents
Report Development

Report Development

GnuCash reports are generated in a manner that is quite reminiscent of web CGI ; it involves:

This presentation will assume that the reader is already generally familiar with Scheme, particularly with the following concepts:

If the meanings of some of these are not too clear, see the references in the section on Scheme Documentation, and try running code such as the above using Guile.

Report Overview

A GnuCash report is added to the system using the function gnc:define-report, to which one passes the following parameters:

A trivial example might look like the following:

Note that reports are typically defined inside a let environment; the "work functions" will thereby all be invisible to code outside the let environment, which means you won't need to worry about coming up with unique function names. Only the report name forcibly needs to be unique.

So long as each report is similarly wrapped in a let environment, you could call all of the "rendering" functions rendition without causing any conflicts.

The options-generator function

The options generator introduces a number of additional functions that are used to set up option dialogs.

 
(define (options-generator)
  (let ((option-set (gnc:new-options)))  (1)
 (gnc:options-add-report-date!  (2)
  option-set pagename-general "End Date" "a")
 (gnc:options-add-date-interval!  (3)
  option-set pagename-general "From" "To" "a")
 (gnc:options-add-interval-choice!  (4)
  option-set pagename-general 
  "Step Size" "b" 'YearDelta)
 (gnc:options-add-account-levels! (5)
  option-set pagename-general
  "Show Accounts down to Level" "c" 2)
 (gnc:options-add-account-selection! (6)
  option-set pagename-general "Account Display Depth"
  "Always show subaccounts" "Accounts" "a" 3 
  *LIST-OF-INCOME-AND-EXPENSE-ACCOUNTS* 
 options))       
(1)
gnc:new-options creates a new, empty set of options. This has to be run first; the later functions need to have an option set to refer to.
(2)
gnc:options-add-report-date! adds a selection option that indicates a single date, generally used as the end date.
(3)
gnc:options-add-date-interval! adds in a selection option that allows specifying a range of dates.
(4)
gnc:options-add-interval-choice! adds a selection option that allows choosing between various time intervals, including days, weeks, two-week periods, months, and years.
(5)
gnc:options-add-account-levels! adds an option indicating how deep a set of account levels should be shown.
(6)
gnc:options-add-account-selection! allows selecting a set of accounts.

Note that the last argument is a list of accounts from which to select, which means filtering a list of relevant accounts, at some point.

There are also additional option functions:

  • gnc:options-add-currency! to select a currency;

  • gnc:options-add-plot-size! to control how a graphical plot should be sized.

Underlying these are the following base "option generator" functions defined in options-utilities.scm that may be used to create new kinds of options:

  • gnc:register-option

  • gnc:make-date-option

  • gnc:make-multichoice-option

  • gnc:make-simple-boolean-option

  • gnc:make-account-list-option

  • gnc:make-currency-option

  • gnc:make-number-range-option

Accessing GnuCash Data

There are several forms of data that you may wish to access:

Accessing Option Data

Functions gnc:lookup-option, gnc:report-options, and gnc:option-value are the crucial functions you are likely to use from src/scm/options.scm

Exerpted from src/scm/report/hello-world.scm is the following:

;; First, build 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)))
    
;; The next thing we do is make local variables for all the specific
;; options in the set of options given to the function. 
(let 
    ((bool-val     (op-value "Hello, World!" "Boolean Option"))
     (mult-val     (op-value "Hello, World!" "Multi Choice Option"))
     (string-val   (op-value "Hello, World!" "String Option"))
     (date-val     (gnc:date-option-absolute-time
		    (op-value "Hello, World!" "Just a Date Option")))
     (date2-val    (gnc:date-option-absolute-time
		    (op-value "Hello, World!" "Time and Date Option")))
     (rel-date-val (gnc:date-option-absolute-time
		    (op-value "Hello, World!" "Relative Date Option")))
     (combo-date-val (gnc:date-option-absolute-time
		      (op-value "Hello, World!" "Combo Date Option")))
     (num-val      (op-value "Hello, World!" "Number Option"))
     (bg-color-op  (get-op   "Hello, World!" "Background Color"))
     (txt-color-op (get-op   "Hello, World!" "Text Color"))
     (accounts     (op-value "Hello Again"   "An account list option"))
     (list-val     (op-value "Hello Again"   "A list option"))
     (crash-val    (op-value "Testing"       "Crash the report")))
  (now-do-stuff-with-options))

Stock Prices

Warning

The stock price database is under construction, so it is a bit early to get specific about this...

HTML Generation functions

Reports are generated as a tree of Guile records, rooted by an <html-document> record, which consists of style information, a title, and a list of <html-object> records that consist of a rendition function and a further list of objects.

We might generate a simple report thus:

(define (build-simple-document)
  (let* ((document (gnc:make-html-document))
	 ;;; Here are a couple of helper functions
	 (addfpara (lambda (obj)
		    (gnc:html-document-add-object!
		     document
		     (gnc:make-html-text         
		      (gnc:html-markup-p obj)))))
	 (addpara (lambda (text)
		    (addfpara 
		     (gnc:html-markup/format text)))))
       ;;; Set the title
    (gnc:html-document-set-title! document (_ "Simple List of Values"))
    ;;; Add in a paragraph of text
    (addpara 
     (_ "This is a simple report, starting with a paragraph of text"))
    (addpara
     (_ "Next, we calculate random values, adding them to a balance."))
    (let loop
	((balance 0))
      (if (< balance 500)
	  (let ((newamt (- (random 500 200))))  ;;; Random number
	    (addfpara 
	       (gnc:html-markup/format 
		(_ "Another random adjustment of %s yields %s")
		(gnc:html-markup-tt (number->string newamt))
		(gnc:html-markup-b (number->string balance))))
	      (loop (+ balance newamt)))))
    document))  ;;; Finally, return the document