GnuCash User Manual | ||
---|---|---|
<<< Previous | Next >>> |
GnuCash reports are generated in a manner that is quite reminiscent of web CGI ; it involves:
Writing programs that generate "option dialogs."
Unlike the CGI approach, GnuCash options wind up directly generating GNOME widgets, and do not use HTML FORM tagging. The similarities are nonetheless quite conspicuous as the options systems both involve:
Building a property list that describes options and default values;
Submitting that property list for user input;
Returning another property list containing the user's input.
Writing programs that pull data from the application, based on the option property list, and then generate HTML output.
Many web applications produce HTML of questionable integrity since the developer generates HTML in the slap-happy manner of just printing out strings that might contain any sort of "pseudo-tagged" data. In contrast, GnuCash uses an HTML-oriented record structure which makes it harder to build bad HTML than it is to build valid HTML.
This presentation will assume that the reader is already generally familiar with Scheme, particularly with the following concepts:
Binding values and functions using define
As with:
(define *value-x* 25) (define *value-y* 30) (define *string-a* "Here's a string!") (define *string-b* "A string with embedded quote \" ") (define (add-1 x) (+ x 1)) |
Defining local bindings using let and let
> (define let-to-hide-stuff (lambda (x y z) (let ((x 25);; Local binding of x inside the let (w 11)) (display "Inside let, x is:") (display x) (newline) (display "And w only exists inside the let as ") (display w) (newline) (display "Add x, y, z, w inside let:") (display (+ x y z w)) (newline)) (display "Now, outside the let:") (newline) (display "x is:") (display x) (newline) (display "y is:") (display y) (newline) (display "z is:") (display z) (newline) (display "w is:") (display w) (newline))) > (let-to-hide-stuff 10 20 30) > (let-to-hide-stuff 10 20 30) Inside let, x is:25 And w only exists inside the let as 11 Add x, y, z, w inside let:86 Now, outside the let: x is:10 y is:20 z is:30 reference to undefined identifier: w w is:> |
Defining lists using list
As with:
> (list 1 2 3 "four" 'five) (1 2 3 "four" five) > '(1 2 3 "four" five) (1 2 3 "four" five) |
Representing anonymous functions using lambda
The add-1 function shown earlier is actually defined thus:
(define add-1 (lambda (x) (+ x 1))) |
The if control structure
Using map to apply a function to all the members of a list, producing another list, as with:
> (map (lambda (x) (+ x 1)) '(1 2 3 4)) (2 3 4 5) |
> (map (lambda (s n) (string-append s ":" (number->string n))) '("one" "two" "three" "four") '(1 2 3 4)) ("one:1" "two:2" "three:3" "four:4") |
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.
A GnuCash report is added to the system using the function gnc:define-report, to which one passes the following parameters:
version
This is a version number; not presently used to do anything particularly clever.
name
This is a not-translated name for the report that is notably used to provide the name that will be added to the GnuCash reporting menu.
This argument is a function which takes no parameters, and generates a set of options that will be used to display a dialog that the user can use to select values for the report's parameters.
This will typically include things like date ranges and lists of accounts, to determine what data should be selected for the report, as well as other controls that determine how the data will be rendered.
The "renderer" does the work of generating the report. It accepts a "database" of options, as generated by the options-generator , pulls the corresponding data from the GnuCash engine, and generates, as output, an object of type html-document , which is what the GnuCash report engine knows how to display.
A trivial example might look like the following:
(let () (define (options-generator) (let ((options (gnc:new-options))) ;;; Create new option list options)) ;;; Return it (define (renderer options) (let ((document (gnc:make-html-document))) document)) ;;; Generates an empty document (gnc:define-report 'version 1 'name (N_ "Trivial Example") 'options-generator options-generator 'renderer renderer)) |
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 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)) |
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
There are several forms of data that you may wish to access:
The functions used to access the various forms of accounting data may be found in the file src/g-wrap/gnc.html.
Need some examples here... |
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)) |
The stock price database is under construction, so it is a bit early to get specific about this... |
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 |
The rendition function is where the functions from the preceding sections all come together.
It takes, as input, a "database" of options generated once the user adds their input to the dialog produced by the options-generator.
It uses those options to control how it accesses GnuCash data
Finally, from that data, it generates a "database" of HTML output.
The rendition function provides, as its return value, the "database of HTML," which GnuCash then displays in an HTML viewer.
If you need more information, or have developed a new report that may be of use to others, please contact the GnuCash development mailing list at < gnucash-devel@gnucash.org >.
<<< Previous | Home | Next >>> |
Stock Price Report | What's New? |