Difference between revisions of "Custom Reports Using Eguile"

From GnuCash
Jump to: navigation, search
(Available Reports)
(Custom for-loop syntax: fix broken <)
 
(67 intermediate revisions by 10 users not shown)
Line 7: Line 7:
 
The GnuCash version of eguile is eguile-gnc.scm, written in early 2009 by Chris Dennis, and based on Neale Pickett's [http://woozle.org/~neale/src/eguile/ eguile.scm].
 
The GnuCash version of eguile is eguile-gnc.scm, written in early 2009 by Chris Dennis, and based on Neale Pickett's [http://woozle.org/~neale/src/eguile/ eguile.scm].
  
== ...the rest of this page will be here very soon ==
+
For example,
 +
<SyntaxHighlight lang="HTML">
 +
<h3><?scm:d coyname ?></h3>
 +
<h2><?scm:d reportname ?> as at <?scm:d (gnc-print-date opt-date-tp) ?></h2>
 +
</SyntaxHighlight>
 +
would become
 +
<SyntaxHighlight lang="Scheme">
 +
(display "<h3>")(display "Acme Tools Ltd.")(display "</h3>
 +
<h2>)(display "Balance Sheet")(display " as at ")
 +
(display "05/04/2009")(display "<h2">)
 +
</SyntaxHighlight>
 +
which is then evaluated as guile code to create the text of the report.
  
see [http://bugzilla.gnome.org/show_bug.cgi?id=574582] for more information.
+
== How to install an eguile report ==
 +
 
 +
To try out eguile-based reports, you first need to download [http://www.fb-cs.co.uk/files/gnucash/eguile-gnc.scm eguile-gnc.scm] and put it into the report folder. On my system -- Ubuntu -- that's
 +
<tt>/usr/share/gnucash/guile-modules/gnucash/report/eguile-gnc.scm</tt>.  In general, it should go into the same folder as files such as fancy-invoice.scm.
 +
 
 +
;Note: GnuCash >=2.3 already includes eguile-gnc.scm.
 +
 
 +
Install the report and its associated files in the usual way (see [[Custom Reports#Loading the Report]] for a full explanation).
 +
 
 +
Your report should start with a define-module statement like <SyntaxHighlight lang="Scheme" inline>
 +
(define-module (gnucash report my-report-name))
 +
</SyntaxHighlight> and have a unique name (GnuCash 2.2.x and before)/a unique id (GnuCash 2.3.x and later).
 +
 
 +
The easiest way is to put all the files for a report into your <tt>.gnucash</tt> folder, and then add a line to <tt>.gnucash/config.user</tt> like this:<SyntaxHighlight lang="Scheme">
 +
(load "/path/to/my/.gnucash/report.scm") 
 +
</SyntaxHighlight>
 +
If <tt>.gnucash/config.user</tt> does not already exist, just create it and add the new line.
  
== How to install an eguile report ==
+
For example, on a Linux system, for a user called 'chris', the Tax Invoice report can be installed by:
 +
# Downloading taxinvoice.scm and taxinvoice.eguile.scm, and putting them into the <tt>/home/chris/.gnucash/</tt> folder.
 +
# Adding this line to <tt>/home/chris/.gnucash/config.user</tt>:<SyntaxHighlight lang="Scheme">
 +
(load "/home/chris/.gnucash/taxinvoice.scm")
 +
</SyntaxHighlight>
 +
 
 +
Restart GnuCash, and the report should then show up somewhere in the reports menu, depending on the menu-path option in the report.  The Tax Invoice and Balance Sheet reports (see below) put themselves into the Reports/Business sub-menu.
 +
 
 +
== How to create an eguile report ==
  
 
...
 
...
  
== How to create an eguile report ==
+
=== eguile syntax ===
 +
 
 +
Within what is otherwise an HTML source file, Guile/Scheme code is wrapped in  '<?scm ... ?>'
 +
(whitespace is required after '<?scm' and before '?>')
 +
 
 +
'<?scm ... ?>' pairs can NOT be nested.
 +
 
 +
The optional :d modifier (i.e. '<?scm:d' ) is just a shortcut for '(display ... )',
 +
so '<?scm:d x ?>' is the same as '<?scm (display x) ?>'
 +
 
 +
Note that s-expressions can be spread across more than one '<?scm ... ?>',
 +
for example:<SyntaxHighlight lang="HTML">
 +
<?scm (if (> x 3) (begin ?>Bigger<?scm ) (begin ?>Smaller<?scm )) ?>
 +
</SyntaxHighlight>
 +
 
 +
Each chunk of text outside a '<?scm ... ?>' pair ends up wrapped
 +
in a (display ... ), after having had double quotes etc. escaped.
 +
 
 +
The processing happens in two passes.  Initially the input file is converted
 +
to a Guile script, and then that script is evaluated to produce the final
 +
result (as a string which is passed back to the report-displaying part of GnuCash).
 +
 
 +
For example, if the input file contained these lines:<SyntaxHighlight lang="HTML">
 +
<h1 align="center">Invoice <?scm:d invoiceid ?></h1>
 +
<?scm (for entry in entries do ?>
 +
  <p>Date: <?scm:d (entry date) ?>, description: <?scm:d (entry desc) ?>
 +
<?scm ) ?>
 +
</SyntaxHighlight>
  
 +
the resulting script would look like:<SyntaxHighlight lang="Scheme">
 +
(display "<h1 align=\"center\">Invoice ")(display invoiceid)(display "</h1>")
 +
(for entry in entries do
 +
  (display "<p>Date: ")(display (entry date))
 +
  (display ", description: ")(display (entry desc))
 +
)
 +
</SyntaxHighlight>
 +
and the final result might be this:<SyntaxHighlight lang="Scheme">
 +
<h1 align="center">Invoice 002345</h1>
 +
  <p>Date: 04/03/2009, description: Widgets
 +
  <p>Date: 05/03/2009, description: Modified widgets
 +
</SyntaxHighlight>
 
...
 
...
 +
 +
=== Custom for-loop syntax ===
 +
To simply looping over a list or map (which by default is a bit cumbersome in scheme), GnuCash defines a custom for-loop syntax, which works similar to for loops in some imperative languages. There are three forms of this syntax: For a single list, for multiple lists and for hashes. This for loop syntax should be available in all eguile-based reports (both in the report itself as well as in the eguile templates). This syntax is defined in eguile-utilities.scm, so it might be available in other parts of scheme as well.
 +
 +
==== Single list ====
 +
<SyntaxHighlight lang="Scheme">
 +
(for <var> in <list> do <expr> ...)
 +
</SyntaxHighlight>
 +
 +
This syntax loops over <tt><list></tt>, assigning each value to <tt>&lt;var&gt;</tt> in turn and evaluating all <expr>'s. In other words, it takes the first element of <list>, assigns it to <var>, evaluates each <expr>, takes the second element of <list>, etc.
 +
 +
Consider the following example:<SyntaxHighlight lang="Scheme">
 +
(for entry in (gncInvoiceGetEntries invoice) do
 +
  (displayEntry entry))
 +
</SyntaxHighlight>
 +
 +
This example would evaluate the "<tt>displayEntry</tt>" function for each entry in a given invoice.
 +
 +
==== Multiple lists ====
 +
<SyntaxHighlight lang="Scheme">
 +
(for (<var> ...) in (<list> ...) do <expr> ...)
 +
</SyntaxHighlight>
 +
 +
This syntax loops over all <tt><list></tt>'s in parallel, assigning each value to the corresponding <var> and evaluates all <expr>'s. In other words, it takes the first element of the first <list>, assigns it to the first <var>, takes the first element of the second <list>, assigns it to the second <var>, up to the last <list> and <var>. Then it evaluates each <expr> and starts over with the second element of each list, etc.
 +
 +
Consider the following example:<SyntaxHighlight lang="Scheme">
 +
(let ((descriptions (list "Company name" "Address" "Website"))
 +
      (values (list "Example Corp" "Example street 1" "http://example.org")))
 +
  (for (desc val) in (descriptions values) do
 +
    (display desc)
 +
    (display ": ")
 +
    (display val)
 +
    (display "<br/>\n")))
 +
</SyntaxHighlight>
 +
 +
This example would output:<SyntaxHighlight lang="HTML">
 +
Website: http://example.org<br/>
 +
Company name: Example Corp<br/>
 +
Address: Example street 1<br/>
 +
</SyntaxHighlight>
 +
 +
==== Hashes (removed in Gnucash 3.7) ====
 +
<SyntaxHighlight lang="Scheme">
 +
(for <key> => <value> in <hash> do <expr> ...)
 +
</SyntaxHighlight>
 +
 +
This syntax loops over all keys and values in a hash, assiging each key to <key> and each value to <value> in turn and evaluating all <expr>'s. Note that as always with hashes, the keys from the hash are looped in arbitrary order.
 +
 +
Consider the following example:<SyntaxHighlight lang="Scheme">
 +
(let ((company (make-hash-table 16)))
 +
  (hash-set! company "Company name" "Example Corp")
 +
  (hash-set! company "Address" "Example street 1")
 +
  (hash-set! company "Website" "http://example.org")
 +
  (for desc => val in company do
 +
    (display desc)
 +
    (display ": ")
 +
    (display val)
 +
    (display "<br/>\n")))
 +
</SyntaxHighlight>
 +
 +
This example would output exactly the same as the previous example.
 +
 +
This syntax was removed in Gnucash 3.7 (in this [https://github.com/Gnucash/gnucash/commit/e506b7c3325f09e84c1e5d9519e551cc49943535#diff-0e5f3da1af5a9af1929e52328ef4cf8c commit]). Instead, you can use the scheme built-in [https://www.gnu.org/software/guile/manual/html_node/Hash-Table-Reference.html hash-for-each], e.g.:<SyntaxHighlight lang="Scheme">
 +
  (hash-for-each (lambda (desc val) (
 +
    (display desc)
 +
    (display ": ")
 +
    (display val)
 +
    (display "<br/>\n")
 +
  )) company)
 +
</SyntaxHighlight>
 +
 +
=== ...the rest of this page will be here very soon ===
 +
 +
 +
=== Internationalisation ===
 +
Always use _ rather than N_ in the template file.  N_ can be used for report option names, help text, and default values.
 +
 +
More precise N_ means NOP, no operation. It can be used to mark strings for translation e.g. in array declarations, but doesn't translate them. Then later the translation is called with _. More Details in [[Translation#Tips for Developers]].
 +
  
 
== Available Reports ==
 
== Available Reports ==
Line 24: Line 177:
  
 
==== Tax Invoice ====
 
==== Tax Invoice ====
; Report file: taxinvoice.scm
+
{|
; Template file: taxinvoice-eguile.scm
+
| Report file: || [https://raw.githubusercontent.com/Gnucash/gnucash/maint/gnucash/report/business-reports/taxinvoice.scm taxinvoice.scm]
; CSS file: none
+
|-
; Author: Chris Dennis
+
| Template file: || [https://raw.githubusercontent.com/Gnucash/gnucash/maint/gnucash/report/business-reports/taxinvoice.eguile.scm taxinvoice.eguile.scm]
; Version: 0.01
+
|-
; Last update: June 2009
+
| CSS file: || [https://raw.githubusercontent.com/Gnucash/gnucash/maint/gnucash/report/business-reports/taxinvoice.css taxinvoice.css]
 +
|-
 +
| Author: || [[User:ChrisDennis|ChrisDennis]]
 +
|-
 +
| Version: || 0.03
 +
|-
 +
| Last update: || June 2018
 +
|}
 +
===== Features =====
 +
* Included in the GnuCash source tree
 +
* Includes full company and customer details, including a company logo if required.
 +
* Automatically includes only relevant columns.  For example, if none of the items on the invoice have discounts applied, then the discount columns will be omitted.
 +
 
 +
===== Issues =====
 +
* No known issues
  
 
==== Balance Sheet ====
 
==== Balance Sheet ====
; Report file: balsheet-eg.scm
+
{|
; Template file: balsheet-eg.eguile.scm
+
| Report file: || [https://raw.githubusercontent.com/Gnucash/gnucash/maint/gnucash/report/business-reports/balsheet-eg.scm balsheet-eg.scm]
; CSS file: balsheet-eg.css
+
|-
; Author: Chris Dennis
+
| Template file: || [https://raw.githubusercontent.com/Gnucash/gnucash/maint/gnucash/report/business-reports/balsheet-eg.eguile.scm balsheet-eg.eguile.scm]
; Version: 0.01
+
|-
; Last update: June 2009
+
| CSS file: || [https://raw.githubusercontent.com/Gnucash/gnucash/maint/gnucash/report/business-reports/balsheet-eg.css balsheet-eg.css]
 +
|-
 +
| Author: || [[User:ChrisDennis|ChrisDennis]]
 +
|-
 +
| Version: || 1.54
 +
|-
 +
| Last update: || June 2018
 +
|}
 +
===== Features =====
 +
* This report has been included in GnuCash.
 +
* Displays the balance sheet in a 'text book' style with rules under columns etc.
 +
* Uses the 'Assets = Equity + Liabilities' model.  If there is demand for an 'Assets - Liabilities = Equity' version, then that could be done as a separate report, or by adding an option and some extra code to this one.
 +
* One- or two-column layout.
 +
* Negative values can be shown as, for example, '-£100.00' or '(£100.00)'.
 +
* Deliberately has fewer options than the standard balance sheet.  It makes 'intelligent' decisions about what to display.  For example, parent accounts that are marked as placeholders and have a zero balance do not have their balance shown at all.
 +
* Handles commodities/currencies in a basic way -- exchange rates are calculated as at the date of the balance sheet.
 +
* A balancing entry (labelled 'Retained Earnings' by default) is calculated so that the sheet always balances.
 +
* Only displays Imbalance and Orphan accounts if non-zero.
 +
* Does not have a account selection option -- I don't see why you would want a balance sheet with some accounts missing.
 +
* Does not deal with unrealised gains/losses.
 +
 
 +
===== To Do =====
 +
* Option to put the currency symbol at the top of each column, rather than on every value.
 +
* Option to put "Less: " in front of negative accounts, e.g.
 +
  Less: Drawings        (£1000.00)
 +
===== Issues =====
 +
* No known issues.
 +
 
 +
==Proposed Reports==
 +
===Cash Based Income Statement===
 +
Because of the importance and impacts this discussion moved to [[Cash Based Accounting]].

Latest revision as of 22:52, 4 February 2020

What is eguile?

eguile is a way of processing a template file to create a guile script.

More specifically, within GnuCash, guile is used to combine HTML and guile code to create a report, such as an invoice or balance sheet.

The GnuCash version of eguile is eguile-gnc.scm, written in early 2009 by Chris Dennis, and based on Neale Pickett's eguile.scm.

For example,

<h3><?scm:d coyname ?></h3>
<h2><?scm:d reportname ?> as at <?scm:d (gnc-print-date opt-date-tp) ?></h2>

would become

(display "<h3>")(display "Acme Tools Ltd.")(display "</h3>
<h2>)(display "Balance Sheet")(display " as at ")
(display "05/04/2009")(display "<h2">)

which is then evaluated as guile code to create the text of the report.

How to install an eguile report

To try out eguile-based reports, you first need to download eguile-gnc.scm and put it into the report folder. On my system -- Ubuntu -- that's /usr/share/gnucash/guile-modules/gnucash/report/eguile-gnc.scm. In general, it should go into the same folder as files such as fancy-invoice.scm.

Note
GnuCash >=2.3 already includes eguile-gnc.scm.

Install the report and its associated files in the usual way (see Custom Reports#Loading the Report for a full explanation).

Your report should start with a define-module statement like (define-module (gnucash report my-report-name)) and have a unique name (GnuCash 2.2.x and before)/a unique id (GnuCash 2.3.x and later).

The easiest way is to put all the files for a report into your .gnucash folder, and then add a line to .gnucash/config.user like this:
(load "/path/to/my/.gnucash/report.scm")

If .gnucash/config.user does not already exist, just create it and add the new line.

For example, on a Linux system, for a user called 'chris', the Tax Invoice report can be installed by:

  1. Downloading taxinvoice.scm and taxinvoice.eguile.scm, and putting them into the /home/chris/.gnucash/ folder.
  2. Adding this line to /home/chris/.gnucash/config.user:
    (load "/home/chris/.gnucash/taxinvoice.scm")
    

Restart GnuCash, and the report should then show up somewhere in the reports menu, depending on the menu-path option in the report. The Tax Invoice and Balance Sheet reports (see below) put themselves into the Reports/Business sub-menu.

How to create an eguile report

...

eguile syntax

Within what is otherwise an HTML source file, Guile/Scheme code is wrapped in '<?scm ... ?>' (whitespace is required after '<?scm' and before '?>')

'<?scm ... ?>' pairs can NOT be nested.

The optional :d modifier (i.e. '<?scm:d' ) is just a shortcut for '(display ... )', so '<?scm:d x ?>' is the same as '<?scm (display x) ?>'

Note that s-expressions can be spread across more than one '<?scm ... ?>',

for example:
<?scm (if (> x 3) (begin ?>Bigger<?scm ) (begin ?>Smaller<?scm )) ?>

Each chunk of text outside a '<?scm ... ?>' pair ends up wrapped in a (display ... ), after having had double quotes etc. escaped.

The processing happens in two passes. Initially the input file is converted to a Guile script, and then that script is evaluated to produce the final result (as a string which is passed back to the report-displaying part of GnuCash).

For example, if the input file contained these lines:
 <h1 align="center">Invoice <?scm:d invoiceid ?></h1>
 <?scm (for entry in entries do ?>
   <p>Date: <?scm:d (entry date) ?>, description: <?scm:d (entry desc) ?>
 <?scm ) ?>
the resulting script would look like:
 (display "<h1 align=\"center\">Invoice ")(display invoiceid)(display "</h1>")
 (for entry in entries do
   (display "<p>Date: ")(display (entry date))
   (display ", description: ")(display (entry desc))
 )
and the final result might be this:
 <h1 align="center">Invoice 002345</h1>
   <p>Date: 04/03/2009, description: Widgets
   <p>Date: 05/03/2009, description: Modified widgets

...

Custom for-loop syntax

To simply looping over a list or map (which by default is a bit cumbersome in scheme), GnuCash defines a custom for-loop syntax, which works similar to for loops in some imperative languages. There are three forms of this syntax: For a single list, for multiple lists and for hashes. This for loop syntax should be available in all eguile-based reports (both in the report itself as well as in the eguile templates). This syntax is defined in eguile-utilities.scm, so it might be available in other parts of scheme as well.

Single list

 (for <var> in <list> do <expr> ...)

This syntax loops over <list>, assigning each value to <var> in turn and evaluating all <expr>'s. In other words, it takes the first element of <list>, assigns it to , evaluates each <expr>, takes the second element of <list>, etc.

Consider the following example:
 (for entry in (gncInvoiceGetEntries invoice) do
   (displayEntry entry))

This example would evaluate the "displayEntry" function for each entry in a given invoice.

Multiple lists

 (for (<var> ...) in (<list> ...) do <expr> ...)

This syntax loops over all <list>'s in parallel, assigning each value to the corresponding and evaluates all <expr>'s. In other words, it takes the first element of the first <list>, assigns it to the first , takes the first element of the second <list>, assigns it to the second , up to the last <list> and . Then it evaluates each <expr> and starts over with the second element of each list, etc.

Consider the following example:
 (let ((descriptions (list "Company name" "Address" "Website"))
       (values (list "Example Corp" "Example street 1" "http://example.org")))
   (for (desc val) in (descriptions values) do
     (display desc)
     (display ": ")
     (display val)
     (display "<br/>\n")))
This example would output:
Website: http://example.org<br/>
Company name: Example Corp<br/>
Address: Example street 1<br/>

Hashes (removed in Gnucash 3.7)

 (for <key> => <value> in <hash> do <expr> ...)

This syntax loops over all keys and values in a hash, assiging each key to <key> and each value to <value> in turn and evaluating all <expr>'s. Note that as always with hashes, the keys from the hash are looped in arbitrary order.

Consider the following example:
 (let ((company (make-hash-table 16)))
   (hash-set! company "Company name" "Example Corp")
   (hash-set! company "Address" "Example street 1")
   (hash-set! company "Website" "http://example.org")
   (for desc => val in company do
     (display desc)
     (display ": ")
     (display val)
     (display "<br/>\n")))

This example would output exactly the same as the previous example.

This syntax was removed in Gnucash 3.7 (in this commit). Instead, you can use the scheme built-in hash-for-each, e.g.:
   (hash-for-each (lambda (desc val) (
     (display desc)
     (display ": ")
     (display val)
     (display "<br/>\n")
   )) company)

...the rest of this page will be here very soon

Internationalisation

Always use _ rather than N_ in the template file. N_ can be used for report option names, help text, and default values.

More precise N_ means NOP, no operation. It can be used to mark strings for translation e.g. in array declarations, but doesn't translate them. Then later the translation is called with _. More Details in Translation#Tips for Developers.


Available Reports

The following eguile-based reports are available. More will be contributed soon (hopefully).

Tax Invoice

Report file: taxinvoice.scm
Template file: taxinvoice.eguile.scm
CSS file: taxinvoice.css
Author: ChrisDennis
Version: 0.03
Last update: June 2018
Features
  • Included in the GnuCash source tree
  • Includes full company and customer details, including a company logo if required.
  • Automatically includes only relevant columns. For example, if none of the items on the invoice have discounts applied, then the discount columns will be omitted.
Issues
  • No known issues

Balance Sheet

Report file: balsheet-eg.scm
Template file: balsheet-eg.eguile.scm
CSS file: balsheet-eg.css
Author: ChrisDennis
Version: 1.54
Last update: June 2018
Features
  • This report has been included in GnuCash.
  • Displays the balance sheet in a 'text book' style with rules under columns etc.
  • Uses the 'Assets = Equity + Liabilities' model. If there is demand for an 'Assets - Liabilities = Equity' version, then that could be done as a separate report, or by adding an option and some extra code to this one.
  • One- or two-column layout.
  • Negative values can be shown as, for example, '-£100.00' or '(£100.00)'.
  • Deliberately has fewer options than the standard balance sheet. It makes 'intelligent' decisions about what to display. For example, parent accounts that are marked as placeholders and have a zero balance do not have their balance shown at all.
  • Handles commodities/currencies in a basic way -- exchange rates are calculated as at the date of the balance sheet.
  • A balancing entry (labelled 'Retained Earnings' by default) is calculated so that the sheet always balances.
  • Only displays Imbalance and Orphan accounts if non-zero.
  • Does not have a account selection option -- I don't see why you would want a balance sheet with some accounts missing.
  • Does not deal with unrealised gains/losses.
To Do
  • Option to put the currency symbol at the top of each column, rather than on every value.
  • Option to put "Less: " in front of negative accounts, e.g.
 Less: Drawings        (£1000.00)
Issues
  • No known issues.

Proposed Reports

Cash Based Income Statement

Because of the importance and impacts this discussion moved to Cash Based Accounting.