Difference between revisions of "Custom Reports"
(Added location of gnucash/reports on Debian with flatpak.) |
m (→Get to know Gettext: _ to G_ from 2020-July onwards) |
||
Line 49: | Line 49: | ||
=== Get to know ''Gettext''=== | === Get to know ''Gettext''=== | ||
− | That is the tool which makes the strings translatable. <code>( | + | That is the tool which makes the strings translatable. <code>(G_ "Some Text")</code>, <code>(N_ "Some Text")</code> and similar mark the text for translation. See [[Translation#How to make strings in code translatable]] for details. |
=== Where to find existing reports === | === Where to find existing reports === |
Revision as of 15:34, 14 July 2020
Contents
- 1 Custom Reports in GnuCash
- 2 Introduction
- 3 Getting Started
- 4 Development Environment
- 5 Designing new Reports
- 6 The GnuCash API
- 7 Other Resources
Custom Reports in GnuCash
The term Custom Reports has several contexts in GnuCash:
- Customizing settings for standard reports (Using GnuCash#Saved Report Configurations)
- Combining selected standard reports into one view (Using GnuCash#Custom Multicolumn Report)
- Customized output format for reports (Custom Reports Using Eguile)
- Reports written in the Scheme programming language which are not delivered as part of the standard reports (this page)
If you are not up for coding, you should check the
- report options
- stylesheets and
- stylesheet options and
- file properties.
Possibly there are ways to get what you desire without coding.
If you are ready for coding (and even already firm in Scheme and Guile) then this is the place to contribute.
Introduction
Unfortunately, at the present time writing a custom report for GnuCash requires a little bit of hacking. This wiki page should contain some information to help you get started. However, there is no official documentation.
In the GnuCash project the reports are coded in Scheme, not in C. Learning Scheme is one of the prerequisites to create new reports. The obvious drawback is that one must learn yet another programming language (if not already familiar with it) but saves the effort of having to alter and build GnuCash's source code.
Guile is used as interface to get the Scheme code executed during runtime. Again, if not already familiar with it, this is the second piece of work that one has to swallow.
Last, but not least, the API for that sort of thing is not formally specified.
Still reading? Not yet discouraged? Good for you! Thankfully, lots of help is available.
First of all, there is good educational material available on the web to help you learn Scheme and Guile. Secondly, there is a simple hello-world.scm
example report specifically written to show you the base layout of a GnuCash report. And finally, this Wiki page will give you some guidance when starting this venture.
Getting Started
Get to know Scheme
GnuCash reports are written in the Scheme programming language.
- Mastering Scheme is much easier if you've read The Scheme Progamming Language by R. Kent Dybvig.
If you are new to Scheme it is not necessary to read and understand all of it right now. But you should, at least, have gone through the Getting Started section.
Get to know Guile
Guile is used internally by GnuCash to interpret the Scheme reports.
This comes with a marvelous Tutorial. You don't need this for writing the reports themselves, but it gives a clue of how reports are created in GnuCash. This should be good motivation to go and tackle the learning curve for Scheme.
Get to know Gettext
That is the tool which makes the strings translatable. (G_ "Some Text")
, (N_ "Some Text")
and similar mark the text for translation. See Translation#How to make strings in code translatable for details.
Where to find existing reports
The location of existing reports varies with by operating systems:
Operating System | GnuCash v4.x | GnuCash v2.6.x & v3.x | GnuCash v2.4.x |
---|---|---|---|
BSD | /usr/local/share/guile/site/<guile-version*>/gnucash/reports | /usr/local/share/gnucash/scm/gnucash/report | /usr/local/share/gnucash/guile-modules/gnucash/report |
Debian including derivatives such as Ubuntu | /usr/share/guile/site/<guile-version*>/gnucash/reports | /usr/share/gnucash/scm/gnucash/report | /usr/share/gnucash/guile-modules/gnucash/report |
Debian with flatpak including derivatives such as Ubuntu | /var/lib/flatpak/app/org.gnucash.GnuCash/current/active/files/share/guile/site/2.2/gnucash/reports | ||
Gentoo and Foresight | /usr/share/guile/site/<guile-version*>/gnucash/reports | /usr/share/gnucash/scm/gnucash/report | /usr/share/gnucash/guile-modules/gnucash/report |
OpenSuse 10.x and newer | /usr/share/guile/site/<guile-version*>/gnucash/reports | /usr/share/gnucash/scm/gnucash/report | /usr/share/gnucash/guile-modules/gnucash/report |
OpenSuse up to 10.x | /opt/gnome/share/gnucash/guile-modules/gnucash/report | ||
OSX bundle (Gnucash.app) | Gnucash.app/Contents/Resources/share/guile/site/2.2/gnucash/reports | Gnucash.app/Contents/Resources/share/gnucash/scm/gnucash/report | Gnucash.app/Contents/Resources/share/gnucash/guile-modules/gnucash/report |
Windows | <gnucash install dir**>\share\guile\site\2.2\gnucash\reports | <gnucash install dir**>\share\gnucash\scm\gnucash\report | <gnucash install dir**>\share\gnucash\guile-modules\gnucash\report |
- (*) <guile-version> depends on what version of guile is actually available on your platform. At the time of this writing it's typically either 2.0 or 2.2.
- (**) Note that on Windows, the installation directory is commonly "C:\Program Files\gnucash" on a 32-bit operating system (OS), and "C:\Program Files (x86)\gnucash" on a 64-bit OS.
Study some example reports
hello-world report
hello-world.scm was created to demonstrate the basic structure of a report. Don't get too confused by the huge number of options which are shown in this report - they were added to show off the variety of options available in GnuCash.
Have a look under #Where to find existing reports to find the source code for this report. Alternatively, you can browse the current source code for this report on github.
By looking at the result of this report while you study the source code, you will easily get clues to how it functions. You can run this report in GnuCash via the menu:
- (GnuCash 3.x and older) Reports -> Sample & Custom -> Sample Report with Examples
- (GnuCash 4.x and more recent) Reports -> Examples -> Sample Report with Examples
account-piecharts report
If you want to walk through a much more complicated report, study account-piecharts.scm.
There are two long functions in this report.
-
option-generator
defines the options for this report. -
piechart-renderer
makes all the calculations and renders the pie chart.
The list of values to be shown in the pie chart can be calculated in two ways - depth-based or via a method that traverses all accounts. The decision for this is made when calling the piechart-renderer
function. Both methods will work from a list of selected accounts stored in the topl-accounts
variable.
traverse-accounts
is a recursive function that calls itself to traverse through all child accounts in the account tree. To get the balance for a single account, it makes a call to profit-fn
. profit-fn
obtains the account balance for either one interval or at a given date. It calls out to gnc:account-get-comm-balance-at-date
in gnucash/report/report-system/report-utilities.scm
where, finally, lower level calls are made, which create a query for the account balance at the given date.
Well, long story short, this report is rather involved. Sorry.
Setup a prototype report
Even if you haven't ever written a report before, and you don't have a shiny track record of great Scheme experience, setting up a prototype at this moment will have the benefits getting productive after some time of pure theory.
The beginners amongst us might not have a final picture of what report to write. No worries, take the hello-world.scm report source file, store it in your preferred working directory, and rename it to a unique name, say my-world.scm. As, for sure, you already have read through this report, you know that this report is very wordy, giving a lot of text printouts. These can be used to easily see your own first changes on the screen once this prototype is loaded into GnuCash, and even better, it will let you differentiate between the my-world.scm that you have loaded and the hello-world.scm that is available in GnuCash by default (see previous section). So feel free to implement some fancy printout changes.
Prerequisites
GnuCash will not be able to load your report until you make changes in two areas of your report source file:
Give report a unique symbol name
To avoid symbol definition conflicts with other reports, make sure your report code begins with a define-module statement with a report name not used by any other report, for example:
(define-module (gnucash report my-world-report))
Define report with unique name and id
To enable GnuCash to register your report into the Report menu, scroll to the end of the code and update the gnc:define-report statement.
Set the name of your report to be shown in the Report menu. For GnuCash 2.2.x and before this name must be unique across all reports.
'name (N_ "My World")
Further down below, if it is present, comment out the menu-name line:
;;'menu-name (N_ "Sample Report with Examples")
Update the tool tip text to something appropriate for your report:
'menu-tip (N_ "Unstable. Used for Testing.")
For GnuCash 2.3.x and later, your report must have a globally unique identifier (guid). GnuCash will fail to start if multiple reports have the same unique id. For this reason, if you wish to create a backup of a report while you work on it, place the backup outside the GnuCash directories. If you need some newly generated unique id, run the program:
uuidgen | sed -e 's/-//g'
or use an online uuid generator like this one (any other one will do as well). Be sure to untick "Hyphens" to generate gnucash compatible guids. If you forget or the site you use doesn't offer that option, simply remove the hyphens yourself. Copy the newly generated ID and paste it into the report-guid value:
'report-guid "paste generated report unique id here"
Loading Your Report
There are two ways to load a report at startup of GnuCash:
- from a user account or
- from the installation tree.
You will likely want to use the user account approach - a least until your work is finished.
Load the report from a user account
This method is used if you edit your report source file in a working directory.
- For GnuCash v2.x edit DOT_GNUCASH_DIR/config.user to add a line of the form:
(load (gnc-build-dotgnucash-path "my-report.scm"))
- For GnuCash v3.x edit GNC_CONFIG_HOME/config-user.scm to add a line of the form:
(load (gnc-build-userdata-path "my-report.scm"))
- Notes
-
- (gnc-build-dotgnucash-path ...) or (gnc-build-userdata-path ...) will complement your report name with the full path of your DOT_GNUCASH_DIR or GNC_DATA_HOME directory respectively. If you are comfortable with file locations you could equally well use the form
(load "<absolute path to>/my-report.scm")
- to load your report from any arbitrary location on your file system.
- The GnuCash 3.x and above instructions refer to 2 separate directories: GNC_CONFIG_HOME and GNC_DATA_HOME. Be careful to use the proper one in the different locations as indicated.
- If the config.user or config-user.scm file is not yet found in your settings directory, simply create it.
Move, copy, or link* your report file (my-report.scm) from your working directory to the proper DOT_GNUCASH_DIR or GNC_DATA_HOME directory. The report should be visible in the Reports menu after the next restart.
* On Unix-like OSs you can use the ln command; on Windows (7 and higher) you can use the mklink command.
To reload your report, you must restart GnuCash. You will have to do this every time you change a report.
Load the report from the installed report directory
While not recommended you can also copy the report to the installed report directory. Make sure you have included a module definition as shown above. All *.scm files from the directory gnucash/scm/gnucash/report/standard-reports/ will be loaded automatically. This applies only to the standard-reports/ sub-directory, not to any of the other directories with reports. Hence, the easiest way to have your report available to gnucash is to copy your *.scm file into that directory.
- Note
- In case of an eguile based report only the foo.scm file should be stored in the standard-reports directory. The accompanying foo.eguile.scm and foo.css should go in to gnucash/scm/gnucash/report instead.
Then restart GnuCash. You will have to do this every time you change the report definition file.
Debugging your report
When guile cannot make sense of your report, or your report throws an error, GnuCash will present the following error message in place of your report:
Report error
An error occurred while running the report. |
This lets you know there was a problem, but now you need to pinpoint where the problem lies. Here are some tips that might help.
Tip | Details | ||||||||
---|---|---|---|---|---|---|---|---|---|
Enable debug messages | Start GnuCash with debug command line parameters: gnucash --debug --log gnc.scm=debug
On macOS, assuming GnuCash is in your /Applications/Gnucash.app/Contents/MacOS/Gnucash --debug --log gnc.scm=debug
There are other debug related options, run GnuCash with the | ||||||||
Study the trace logs | The GnuCash trace logs can sometimes give you that satisfying "oh, I see". By default you'll find the trace log:
You can change the trace log file location via GnuCash command line parameter | ||||||||
Add your own debug messages | You can use the gnc:debug function to add your own messages. For example: (gnc:debug "here I am")
| ||||||||
Add other messages | There are also gnc:msg , gnc:warn and gnc:error functions; see [1] for more details.
| ||||||||
Use a Scheme friendly editor | For the uninitiated, matching parentheses properly in a Scheme program can make one's eyes cross. A Lisp/Scheme friendly editor can really help here. DrRacket or a properly configured Emacs really helps. | ||||||||
Run often | Test your report often after making very small changes. This will make it easier to understand when and where problems are introduced. |
Although guile does have debug support, this wiki page updater has not found a way, at this time, to hook GnuCash up to the guile debugger.
Development Environment
Programming tools
Any reasonable source/text editor, including emacs and vi, should provide basic syntax support for source-code editing. One of the most basic and important for editings scheme/lisp sources is parenthesis matching. Other scheme-like environments (such as DrRacket) might also be useful for editing the sources, but note that they might allow constructs that are not valid in guile. Beware Racket is a scheme dialect not 100% compatible with Guile, but provides a nice GUI for understanding scheme functions.
Simple Proposal for Linux
This is a simple proposal for a development environment in Linux. Assume
- there is a working directory
~/GnuCash/CustomReports
in your home directory. - the new report is stored in the
test00.scm
file
Here is the proposal how to setup up your environment
cd ~/GnuCash/CustomReports
ln -s test00.scm prototype.scm
cat > config.user << "EOF"
(load "<fullPathTo>/GnuCash/CustomReports/prototype.scm")
EOF
cd ~/.gnucash
ln -s ~/GnuCash/CustomReports/config.user
uuidgen | sed -e s/-//g
Prepare test00.scm
to include
- the define-module statement
(define-module (gnucash report prototype-report))
- following updates in the
gnc:define-report
statement
'name (N_ "Prototype")
;;'menu-name (N_ "Sample Report with Examples")
'menu-tip (N_ "Unstable. Used for Testing.")
'report-guid "the-new-generated-unique-ID"
See also the minimum report defintion in the #Designing new Reports chapter.
By this you can
- use
test00.scm
as a template test report and use the lines as described above without further changes - collect all our test reports as e.g. testXX.scm files in this directory
- switch between them by making the prototype.scm point to the desired report
- restart GnuCash
- always find the report under Reports->Sample & Custom Report->Prototype
Finally, you should also extract a GnuCash source tar ball in ~/GnuCash/CustomReport/
to have access to the Scheme files referenced in this page.
Technique to reload reports without restarting GnuCash
Reports can be modified to allow the majority of the report to be reloaded without requiring GnuCash to be restarted for each change which is very convenient when developing reports. This requires splitting the report into two files:
- a minimal loader which which forces the reload of the meat of your report each time options are generated or report is rendered.
- the meat of your report. This contains actual report code for the options generator and renderer.
Both files must parse and execute correctly before the reload will function.
Note that, at least in GnuCash 2.6.17, for changes to report options to take effect, the report must closed and then relaunched from the Report menu. Changes to the render portion seem to be handled just fine by the reload button.
Minimal Example
The following is a skeleton outline of what is required for a report to be reloaded on the fly. Tested with GnuCash 2.6.17.
Minimal Loader
Update name
, report-guid
and menu-tip
values appropriately for your report.
custom_import.scm:
(define-module (custom_import))
(use-modules (gnucash gnc-module))
(use-modules (gnucash gettext))
(gnc:module-load "gnucash/report/report-system" 0)
; load the renderer module once at the top level to get the symbols
(use-modules (custom_import_renderer))
; get and reload the module
(define (reload-report-module)
(reload-module (resolve-module '(custom_import_renderer))))
; every time options are generated, reload the meat of the report
(define (options_loader)
(reload-report-module)
(custom_import_options))
; every time report is rendered, reload the meat of the report
(define (renderer_loader report-obj)
(reload-report-module)
(custom_import_renderer report-obj))
(gnc:define-report
'version 1
'name (N_ "<type your report name here>")
'report-guid "<paste report unique id here>"
'menu-tip (N_"<type your report menu tip here>")
'menu-path (list gnc:menuname-utility)
'options-generator options_loader
'renderer renderer_loader)
The Meat of Your Report
custom_import_renderer.scm:
(define-module (custom_import_renderer))
; options for the report (called by loader after it reloads this module)
(define (custom_import_options)
(let* ((options (gnc:new-options)))
; add your options here
options))
; report renderer (called by loader after it reloads this module)
(define (custom_import_renderer report-obj)
(let ((document (gnc:make-html-document)))
; add your renderer here
document))
(export custom_import_options)
(export custom_import_renderer)
Configuration
You'll also need to edit config.user so that GnuCash can find the files above.
Update add-to-load-path
value appropriately for your setup.
config.user:
(add-to-load-path "<type the full path to above report files here>")
(use-modules (custom_import))
Designing new Reports
In general, reports are divided into three sections
- the Options-Generator (define the options for the report)
- the Report-Renderer (create the HTML report)
- the
gnc:define-report
statement (makes the report available in GnuCash)
This is the minimum definition needed for a new GnuCash report. Update name
, report-guid
and menu-tip
values appropriately.
;; -*-scheme-*-
;; This is a minimum report definition in GnuCash.
;; It illustrates the the minimum definitions needed to create
;; a new GnuCash report.
;; It will create an empty page with heading 'Prototype'.
;; To be used as template.
;; ------------------------------------------------------------------
;; Top-level definitions
;; ------------------------------------------------------------------
(define-module (gnucash report prototype))
(use-modules (gnucash gnc-module))
(use-modules (gnucash gettext))
(gnc:module-load "gnucash/report/report-system" 0)
;; ------------------------------------------------------------------
;; Define the Options for this report
;; ------------------------------------------------------------------
(define (options-generator)
(let* ((options (gnc:new-options)))
options))
;; ------------------------------------------------------------------
;; Render the HTML document
;; ------------------------------------------------------------------
(define (document-renderer report-obj)
(let ((document (gnc:make-html-document)))
document))
;; ------------------------------------------------------------------
;; Define the actual report
;; ------------------------------------------------------------------
(gnc:define-report
'version 1
'name (N_ "Prototype")
'report-guid "<paste generated unique report id here>"
'menu-tip (N_ "Unstable. Used for Testing.")
'menu-path (list gnc:menuname-utility)
'options-generator options-generator
'renderer document-renderer)
The Options-Generator
Each report takes responsibility to build up an own database that holds the options for the report.
Usually it is a good habit to copy the relavant code from the hello-world.scm
.
In there you will find the database initializing
- call to
gnc:new-options
which will
- create the container called
options
which will later on be filled by
- calling the help function
add-option
which is a nice wrapper to eventually
- call
gnc:register-option
The gnc:...
-procedures to create this database and to fill it with values are contained in gnucash/src/app-utils/options.scm
.
gnucash/src/report/utility-reports/hello-world.scm
presents a nice selection of example option definitions of following types:
gnc:make-simple-boolean-option
gnc:make-multichoice-option
gnc:make-string-option
gnc:make-date-option
gnc:make-number-range-option
gnc:make-color-option
gnc:make-account-list-option
gnc:make-list-option
On top of these, you find
gnc:make-text-option
gnc:make-font-option
gnc:make-currency-option
gnc:make-budget-option
gnc:make-commodity-option
gnc:make-complex-boolean-option
gnc:make-pixmap-option
gnc:make-account-list-limited-option
gnc:make-account-sel-option
gnc:make-account-sel-limited-option
gnc:make-multichoice-callback-option
gnc:make-radiobutton-option
gnc:make-radiobutton-callback-option
gnc:make-internal-option
gnc:make-query-option
gnc:make-dateformat-option
in gnucash/src/app-utils/options.scm
.
Also, gnucash/src/business/business-utils/business-options.scm
provides some options
gnc:make-invoice-option
gnc:make-customer-option
gnc:make-vendor-option
gnc:make-employee-option
gnc:make-owner-option
gnc:make-taxtable-option
gnc:make-counter-option
gnc:make-counter-format-option
In general, the syntax for creating an option is:
(define (gnc:make-<type>-option
section ;; the tab-string in the option dialog presented to the user
name ;; the text for this option in the option dialog presented to the user
sort-tag ;; used to define the order in which the options are listed
documentation-string ;; help string shown when the mouse moved over the option
default-value
<optional type specific parameters>)
Note that section
is free to be any text string. If section-string has not been used before, a new tab with this name will be created automatically. Nevertheless, note that gnucash/src/report/report-system/report.scm
provides three default names. Re-use these before creating new names:
(define gnc:pagename-general (N_ "General"))
(define gnc:pagename-accounts (N_ "Accounts"))
(define gnc:pagename-display (N_ "Display"))
For a start, simply read through the define (options-generator)
statement in hello-world.scm
and copy what you need.
Nice to know:
- The General option group does not need to be defined, it is part of the report definition per default. If you run the above minimum report definition, the General option group will automatically include a text field for the
Report Name
, a selection box for theStylesheet
, and a button toReset defaults
. - To find the option creation procedures in the source file tree you can use
grep gnc:make-.*-option `find . -name "*.scm"` | grep define
- The source file tree is found in
- Debian and Ubuntu Linux:
/usr/share/gnucash/scm
- Debian and Ubuntu Linux:
The Report-Renderer
Define how to apply the options (the actual report contents).
The overall goal of the Report-Renderer is to create an HTML-document and fill it in with the reports results to display it out to the user.
Similar as with the Options-Generator, hello-world.scm
gives you a great introduction how this works in general.
The gnc:...
-procedures are found in the src/report/report-system/html-*.scm
source files.
After runnning the report you can press the Export
button to save the generated HTML-Code into a file for viewing. Doing this for the above minimum prototype report gives the following result:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<style type="text/css">
h3 { font-family: Ubuntu; font-size: 15pt; font-weight: bold; }
a { font-family: Ubuntu; font-size: 10pt; font-style: italic; }
body, p, table, tr, td { text-align: left; font-family: Ubuntu; font-size: 10pt; }
tr.alternate-row { background: #ffffff }
th.column-heading-left { text-align: left; font-family: Ubuntu; font-size: 10pt; }
th.column-heading-center { text-align: center; font-family: Ubuntu; font-size: 10pt; }
th.column-heading-right { text-align: right; font-family: Ubuntu; font-size: 10pt; }
td.neg { color: red; }
td.number-cell, td.total-number-cell { text-align: right; white-space: nowrap; }
td.date-cell { white-space: nowrap; }
td.anchor-cell { white-space: nowrap; font-family: Ubuntu; font-size: 10pt; }
td.number-cell { font-family: Ubuntu; font-size: 10pt; }
td.number-header { text-align: right; font-family: Ubuntu; font-size: 10pt; }
td.text-cell { font-family: Ubuntu; font-size: 10pt; }
td.total-number-cell { font-family: Ubuntu; font-size: 12pt; font-weight: bold; }
td.total-label-cell { font-family: Ubuntu; font-size: 12pt; font-weight: bold; }
td.centered-label-cell { text-align: center; font-family: Ubuntu; font-size: 12pt; font-weight: bold; }
</style>
<title></title>
</head>
<body bgcolor="#ffffff">
<h3></h3>
</body>
</html>
The style section is added automatically without specific definitions inside the prototoype source. Also note that the register name in GnuCash is not part of the HTML document.
The procedures to fill this HTML-document with life and further formatting are given in source tree below
- Linux Ubuntu:
/usr/share/gnucash/scm
in the following files:
html-acct-table.scm html-barchart.scm html-document.scm html-fonts.scm html-linechart.scm html-piechart.scm html-scatter.scm html-style-info.scm html-style-sheet.scm html-table.scm html-text.scm html-utilities.scm
Start making the prototype report a little nicer by adding the following line to the document-renderer
from html-document.scm
(gnc:html-document-set-title! document (_ "GnuCash Report Prototype"))
will result in an update of the title
and h3
lines:
[...]
<title>GnuCash Report Prototype</title>
[...]
<h3>GnuCash Report Prototype</h3>
[...]
Next it is possible to add attributes to the body line.
Note:
You should read the following section for informational purposes only: Style sheets are now the preferred way to specify a document's presentation, the presentational attributes of BODY have been deprecated. (quoted from W3.org)
(Currently in GnuCash the bgcolor
-tag is default part of the body
-tag.)
The body
-tag can be enhanced with attributes via the gnc:html-document-set-style!
funtion. The general sytax:
(gnc:html-document-set-style!
document ;; the reference to the current html-document
"body" ;; specify the tag to add attributes to
'attribute (list "blabla" "balabala") ;; first additional attribute
'attribute (list "nonstop" "nonsens") ;; second additional attribute
'attribute (list "bgcolor" "#f6ffdb") ;; third additional attribute
[...] ;; further attributes
)
The example above lists some imaginary attributes to indicate that the attribute definition will be added to the body
-tag without further validity check. Invalid attributes will appear in the HTML-code but will have no visible effect on the document representation.
The rest of the body is filled with HTML-text by defintion of text objects, which gives the following structure
<body bgcolor="#ffffff">
<h3>[title]</h3>
[text object 1]
[text object 2]
. . .
. . .
. . .
[text object n]
</body>
whereas it does not matter if you define one or more text objects, you can put everything into one object or split it over several objects.
In Scheme these objects are defined like
(gnc:html-document-add-object!
document ;; the overall HTML document defined above
(gnc:make-html-text ;; a text object
[text 1] ;; part 1 of the text
[text 2] ;; part 2 of the text
. .
. .
. .
[text m] ;; part m of the text
)
)
Note the above structure is the definition of 1 text object, which consists of m (sub-)text elements.
The text elements can be simple strings, or markup-formatted. Markup-commands are found in
- Linux Ubuntu:
/usr/share/gnucash/scm/html-text.scm
:
gnc:html-markup-p ;; markup for "paragraph"
gnc:html-markup-tt ;; markup for fixed font (type writer style)
gnc:html-markup-em ;; markup for "emphasis"
gnc:html-markup-b ;; markup for "bold"
gnc:html-markup-i ;; markup for "italic"
gnc:html-markup-h1 ;; markup for headers on level 1
gnc:html-markup-h2 ;; markup for headers on level 2
gnc:html-markup-h3 ;; markup for headers on level 3
gnc:html-markup-br ;; markup for line "break" (no text)
gnc:html-markup-hr ;; markup for "horizontal line" (no text)
gnc:html-markup-ul ;; markup for "unsorted list" (bullet list)
gnc:html-markup-anchor ;; markup for hyperlinks
gnc:html-markup-img ;; markup for images
To give you a basic idea, an example of simple text objects:
(gnc:html-document-add-object!
document
(gnc:make-html-text
"some text"
"some other text"
(gnc:html-markup-br)
(gnc:html-markup-tt "tt text")
(gnc:html-markup-h1 "header1 text")
(gnc:html-markup "bla" "tirili flöt")
(gnc:html-markup "i" "italic text")
)
)
results in following HTML-code
<body bgcolor="#f6ffdb">
<h3>GnuCash Report Prototype</h3>
some text some other text
<br />
<tt>tt text</tt>
<h1>header1 text</h1>
<bla>tirili flöt</bla>
<i>italic text</i>
</body>
Note that it is possible to define new tags (here: "bla") if needed by using gnc:html-markup
.
Again: Take a good look at hello-world.scm
and explore many more examples how to format HTML-text.
Examine:
html-scatter.scm html-acct-table.scm html-piechart.scm html-table.scm html-linechart.scm
to find out more about
gnc:make-html-scatter
gnc:make-html-acct-table
gnc:make-html-barchart
gnc:make-html-table
gnc:make-html-linechart
The gnc:define-report
Statement
If you want to follow the #Simple Proposal for Linux to setup a simple development environment then you need updates to this section only
- once that you set up the development environment
- once that your report is ready, fully tested, and shall become part of your standard reports
And again, hello-world.scm
already proposes how you should proceed in the latter case
[...] to contribute your brand new, totally cool report, consult the mailing list "mailto:gnucash-devel@gnucash.org" [...]
Barcharts
The minimum definition is of a bar chart report is shown in the following example. Before running, update name
, report-guid
and menu-tip
values appropriately.
;; -*-scheme-*-
;; This is a minimum barchart report definition in GnuCash.
;; It illustrates the the minimum definitions needed to create
;; a new GnuCash barchart report.
;; It will create a page with heading 'Prototype'.
;; To be used as template.
;; ------------------------------------------------------------------
;; Top-level definitions
;; ------------------------------------------------------------------
(define-module (gnucash report prototype))
(use-modules (gnucash gnc-module))
(gnc:module-load "gnucash/report/report-system" 0)
;; ------------------------------------------------------------------
;; Define the Options for this report
;; ------------------------------------------------------------------
(define (options-generator)
(let* ((options (gnc:new-options)))
options))
;; ------------------------------------------------------------------
;; Render the HTML document
;; ------------------------------------------------------------------
(define (document-renderer report-obj)
(let (
(document (gnc:make-html-document))
(chart (gnc:make-html-barchart))
)
(gnc:html-barchart-set-title! chart "Barchart Title") ;; optional
(gnc:html-barchart-set-subtitle! chart "Barchart Sub-Title") ;; optional
(gnc:html-barchart-set-width! chart 400)
(gnc:html-barchart-set-height! chart 400)
(gnc:html-barchart-set-row-labels! chart '("row1" "row2"))
(gnc:html-barchart-set-y-axis-label! chart "y-Axis-Label") ;; optional
(gnc:html-barchart-set-x-axis-label! chart "x-Axis-Label") ;; optional
(gnc:html-barchart-append-column! chart '(100 200))
(gnc:html-barchart-append-column! chart '(110 210))
(gnc:html-barchart-set-col-labels! chart '("col1" "col2"))
(gnc:html-barchart-set-col-colors! chart '("green" "blue"))
(gnc:html-document-add-object! document chart)
document ;; RETURN value
)
)
;; ------------------------------------------------------------------
;; Define the actual report
;; ------------------------------------------------------------------
(gnc:define-report
'version 1
'name (N_ "Prototype")
'report-guid "<paste generated unique report id here>"
'menu-tip (N_ "Unstable. Used for Testing.")
'menu-path (list gnc:menuname-utility)
'options-generator options-generator
'renderer document-renderer)
If you look at the HMTL source for this report after it is generated, you will see a line with the <img>
tag. The image for the report is encoded inline in a very long ASCII code string.
<img src="data:image/png;base64,<very_long_ASCII_coded_string>" alt="Cannot display barchart"/>
html-chart.scm
A simple tutorial to generating charts using html-chart.scm (for GnuCash 4.x series) is available.
The GnuCash API
The GnuCash application programming interface (maint or master branch) is quite intimidating, unfortunately. Luckily, for the reports, most of the stuff needed can be found in the scheme sources. Here some random examples of handy procedures (paths shown refer to the location in a source tarball; might be copied into the location as above in your installation):
- create a monetary object via
gnc:make-gnc-monetary
libgnucash/engine/gnc-numeric.scm - get the amount of a monetary object via
gnc:gnc-monetary-amount
libgnucash/engine/gnc-numeric.scm - get an account name via
xaccAccountGetName
libgnucash/engine/Account.cpp - create a commodity collector via
gnc:make-commodity-collector
gnucash/report/report-system/report-utilities.scm
The mightiness of the API is huge. As of GnuCash 2.4.10, there were over 1000 GnuCash api calls that could be made from reports.
It is by far too much to be documented in this Wiki-page.
However, having a table at hand which includes the most common function calls is convenient especially at the start of digging into it.
That is why following table is included in this chapter. The table does not purport to be exhaustive, nor even aim to be, but Scheme-Report developers are invited to add to this table whenever they come across additions of general interest.
Name | Source | Example | Comments |
---|---|---|---|
xaccAccountGetBalanceAsOfDate | Account.cpp | Returns the account balance as a number.
Example: (xaccAccountGetBalanceAsOfDate acct date)
| |
gnc:html-table-append-row! | html-table.scm | Appends a row to an HTML table.
Note that row can be a list. If so, then each list element represents a column entry. Example: (gnc:html-table-append-row! table (list e1 e2 e3))
will result in: <tr>
<td>e1<td>
<td>e2<td>
<td>e3<td>
</tr>
Note that only simple lists are supported for. If you use nested lists as input, then the whole list contents of the nested lists will be displayed. | |
gnc:gnc-numeric-denom | gnc-numeric.scm | Returns the denominator part of a gnc-numeric record. | |
gnc:gnc-numeric-num | gnc-numeric.scm | Returns the numerator part of a gnc-numeric record. | |
gnc-account-get-full-name | gnc_account_get_full_name() in Account.cpp | hello-world.scm | Returns the account name in the format
ParentLevel1:ParentLevel2:...:AccountName To only get the AccountName use xaccAccountGetName |
xaccTransGetDate | xaccTransGetDate() in Transaction.c | Returns the posting date of a transaction. Transactions are return e.g. by xaccSplitGetParent, see below. Note that this function will return the date as numerical value. If you need to have it human readable, you need to convert this numerical value into a date string, like so:
(qof-print-date (xaccTransGetDate (xaccSplitGetParent split)))
| |
gnc-window-show-progress | gnc_window_show_progress() in gnc-window.c | report-utilities.scm | Used to forward processing progress information to the user. In report-utilities.scm this is wrapped in the routines:
|
xaccAccountGetDescription | Account.cpp | Returns the account description. To be used with the account as input parameter:
(xaccAccountGetDescription acct)
| |
xaccAccountGetName | Account.cpp | hello-world.scm | Returns the account name. To get the full account name including parent names use gnc-account-get-full-name |
xaccAccountGetNotes | Account.cpp | Returns the account notes. This is the text field that is found in the edit dialog of the accounts. To be used with the account as input parameter:
(xaccAccountGetNotes acct)
| |
xaccAccountGetSplitList | Account.cpp | report-utilities.scm | Returns the the list of transactions in a given account. Note that in GnuCash each transaction is a split even if it is not a split transaction. A non-split transaction is a transaction consisting of a single split.
To get the numerical value for the amount of splits in a list of accounts use gnc:accounts-count-splits defined in report-utilities.scm. The standard report cash-flow.scm makes use of this api. |
xaccAccountGetType | Account.cpp | Returns the account type, which is the text field that is found in the edit dialog of the accounts. To be used with the account as input parameter (xaccAccountGetNotes acct)
The following account types are available:
| |
xaccSplitGetAccount | Split.c | Returns the account object that the split belongs to. To get the name from the returned account, use:
(xaccAccountGetName (xaccSplitGetAccount split))
| |
xaccSplitGetCorrAccountName | Split.c | Returns the account name at the other end of the split as a string. | |
xaccSplitGetParent | Split.c | Returns the parent transaction the split belongs to. See xaccTransGetDate for an example how to use it. |
Command line access to the API
(Written Jan 2018) It is convenient to access the full API from a Guile Command-line for newbie report developers. This requires the developer is familiar with Linux, and can set up CMake, unit testing environments.
The full GUI-generated reporting construct described above is important to test a custom report, however, is very inconvenient to explore the API.
The preparation work involves:
1. Create a special unit test program in scheme, perhaps naming it test-server.scm - this shall reside in a convenient folder with other unit test files e.g. gnucash/report/standard-reports/test.
(use-modules (gnucash gnc-module))
(gnc:module-begin-syntax (gnc:module-load "gnucash/app-utils" 0))
(use-modules (gnucash engine test test-extras))
;; The following needed for Guile REPL
(use-modules (system repl server))
(define (run-test)
(run-server))
;; The following needed for reports:
(use-modules (gnucash report stylesheets))
(use-modules (gnucash report report-system))
(define constructor (record-constructor <report>))
2. Adding test-server.scm onto the CMakeFiles.txt in the same folder to enable compilation
3. Running make or ninja to generate the build directory
4. Running test-server.scm from the build directory by
cd build/gnucash/report/standard-reports/test
/usr/bin/ctest -R test-server
This will run the test server.
5. Switch to another terminal, run
nc localhost 37146 (the default guile port, upside down 37146 looks like guile...)
This will now present a full Guile REPL prompt, with full access to the test-server.scm bindings as well as the Gnucash API. People familiar with emacs and the geiser package may also connect to the server by
M-x connect-to-guile
. Unfortunately so far, only the environment is loaded. I do not think the file-load mechanism is accessible to scheme, which is an inconvenience for testing reports.
The developer can try:
> ,apropos xacc [RET]
(sw_engine): xaccTransEqual #<procedure xaccTransEqual (_ _ _ _ _ _)>
(sw_engine): xaccSplitGetCorrAccountCode #<procedure xaccSplitGetCorrAccountCode (_)>
(sw_engine): xaccAccountGetReconciledBalance #<procedure xaccAccountGetReconciledBalance (_)>
(sw_engine): xaccClearMarkDown #<procedure xaccClearMarkDown (_ _)>
(sw_engine): xaccSplitSetBaseValue #<procedure xaccSplitSetBaseValue (_ _ _)>
(sw_engine): xaccTransGetFirstAPARAcctSplit #<procedure xaccTransGetFirstAP
etc
> ,apropos gnc- [RET]
(sw_engine): gnc-monetary-list-delete-zeros #<procedure gnc-monetary-list-delete-zeros (_)>
(sw_engine): gnc-gdate-set-prev-fiscal-year-start #<procedure gnc-gdate-set-prev-fiscal-year-start (_ _)>
(sw_engine): gnc-quote-source-get-internal-name #<procedure gnc-quote-source-get-internal-name (_)>
(sw_engine): gnc-numeric-check #<procedure gnc-numeric-check (_)>
(sw_engine): gnc-timegm #<procedure gnc-timegm (_)>
(sw_engine): gnc-account-join-children #<procedure gnc-account-join-c
etc
> ,bindings
constructor #<variable 55cae1534b70 value: #<procedure 55cae11ff080 at ice-9/boot-9.scm:1340:17 (a b c d e f g h)>>
%module-public-interface #<variable 55cae0652da0 value: #<interface (guile-user) 55cae0638b40>>
run-test #<variable 55cae1534cc0 value: #<procedure run-test ()>>
> (current-time)
1515297631
> (qof-print-date (current-time))
"07/01/18"
> (qof-date-format-set QOF-DATE-FORMAT-ISO)
> (qof-print-date (current-time))
"2018-01-07"
Other Resources
One other way to create custom reports outside GnuCash, but directly from GnuCash data, is to use Ledger-CLI.
Old GnuCash Report Information
(may be very out of date)
- Version 1.6 report documentation
- FAQ#Q:_I.27d_like_to_write_my_own_custom_reports._Where_should_I_start.3F
- From the mailing list
Learning Scheme
- MIT Scheme Index
- Teach Yourself Scheme in Fixnum Days
- Structure and Interpretation of Computer Programs
- How to Design Programs and HtDP, 2nd Edition
- The Little Schemer
- Mastering Scheme is highly eased after reading The Scheme Progamming Language by R. Kent Dybvig
- Guile documentation
- Some guile code uses pattern matching. An Introduction to Lispy Pattern Matching