Skip to content

Instantly share code, notes, and snippets.

@rhowe
Created August 14, 2018 21:47
Show Gist options
  • Save rhowe/a62c03ba2756dadbf6e442ee343600ab to your computer and use it in GitHub Desktop.
Save rhowe/a62c03ba2756dadbf6e442ee343600ab to your computer and use it in GitHub Desktop.
Convert a gnucash save file into a postgres database
<?xml version="1.0" ?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:cd="http://www.gnucash.org/XML/cd"
xmlns:book="http://www.gnucash.org/XML/book"
xmlns:gnc="http://www.gnucash.org/XML/gnc"
xmlns:cmdty="http://www.gnucash.org/XML/cmdty"
xmlns:trn="http://www.gnucash.org/XML/trn"
xmlns:split="http://www.gnucash.org/XML/split"
xmlns:act="http://www.gnucash.org/XML/act"
xmlns:price="http://www.gnucash.org/XML/price"
xmlns:ts="http://www.gnucash.org/XML/ts"
xmlns:slot="http://www.gnucash.org/XML/slot"
xmlns:cust="http://www.gnucash.org/XML/cust"
xmlns:entry="http://www.gnucash.org/XML/entry"
xmlns:lot="http://www.gnucash.org/XML/lot"
xmlns:invoice="http://www.gnucash.org/XML/invoice"
xmlns:owner="http://www.gnucash.org/XML/owner"
xmlns:job="http://www.gnucash.org/XML/job"
xmlns:billterm="http://www.gnucash.org/XML/billterm"
xmlns:bt-days="http://www.gnucash.org/XML/bt-days"
xmlns:sx="http://www.gnucash.org/XML/sx"
xmlns:fs="http://www.gnucash.org/XML/fs"
xmlns:addr="http://www.gnucash.org/XML/custaddr"
xmlns:str="http://exslt.org/strings"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/gnc-v2">
\set VERBOSITY terse
create table account_id_to_guid (
account int4 references accounts(id),
guid varchar(32) unique not null
);
<xsl:apply-templates select="gnc:book"/>
drop table account_id_to_guid;
</xsl:template>
<xsl:template match="gnc:book">
<xsl:apply-templates select="gnc:account"/>
<xsl:apply-templates select="gnc:transaction"/>
</xsl:template>
<xsl:template match="gnc:account[act:type='ROOT']">
-- Root account
-- name=<xsl:value-of select="act:name" />
-- guid=<xsl:value-of select="act:id" />
insert into account_id_to_guid (account, guid) values
(null, '<xsl:value-of select="act:id"/>');
</xsl:template>
<xsl:template match="gnc:account[act:type != 'ROOT']">
-- name=<xsl:value-of select="act:name" />
-- currency=<xsl:value-of select="act:commodity/cmdty:id" />
-- guid=<xsl:value-of select="act:id" />
-- parentguid=<xsl:value-of select="act:parent" />
-- <xsl:choose>
<xsl:when test='act:slots/slot[slot:key = "placeholder"]/slot:value = "true"'>transactions not allowed</xsl:when>
<xsl:otherwise>transactions allowed</xsl:otherwise>
</xsl:choose>
insert into accounts (name, type, currency, parent, transactions_allowed)
values (<xsl:apply-templates select="act:name"/>,
(select id from accounttypes where name = <xsl:apply-templates select="act:type"/>),
(select id from currencies where symbol = <xsl:apply-templates select="act:commodity"/>),
(select account from account_id_to_guid where guid='<xsl:value-of select="act:parent"/>'),
<xsl:choose>
<xsl:when test='act:slots/slot[slot:key = "placeholder"]/slot:value = "true"'>FALSE</xsl:when>
<xsl:otherwise>TRUE</xsl:otherwise>
</xsl:choose>);
insert into account_id_to_guid (account, guid) values
((SELECT currval('accounts_id_seq')), '<xsl:value-of select="act:id"/>');
</xsl:template>
<xsl:template match="act:type">
<xsl:choose>
<xsl:when test=".='INCOME'">'Income'</xsl:when>
<xsl:when test=".='ASSET'">'Asset'</xsl:when>
<xsl:when test=".='BANK'">'Asset'</xsl:when>
<xsl:when test=".='CASH'">'Asset'</xsl:when>
<xsl:when test=".='STOCK'">'Asset'</xsl:when>
<xsl:when test=".='RECEIVABLE'">'Asset'</xsl:when>
<xsl:when test=".='EXPENSE'">'Expense'</xsl:when>
<xsl:when test=".='CREDIT'">'Liability'</xsl:when>
<xsl:when test=".='LIABILITY'">'Liability'</xsl:when>
<xsl:when test=".='PAYABLE'">'Liability'</xsl:when>
<xsl:when test=".='EQUITY'">'Equity'</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template match="act:commodity">'<xsl:value-of select="cmdty:id" />'</xsl:template>
<xsl:template match="cmdty:id">'<xsl:value-of select="." />'</xsl:template>
<xsl:template match="act:name">'<xsl:value-of select="str:replace(., &quot;&apos;&quot;, &quot;&apos;&apos;&quot;)" />'</xsl:template>
<xsl:template match="act:parent">
<xsl:variable name="parentguid" select="."/><!-- [act:id/text()=$parentguid]/act:name/text() -->
<!--(select id from accounts where name='<xsl:value-of select='//gnc:account[1]/act:name' />'
and type=(select id from accounttypes where name=<xsl:apply-templates select="//gnc:account[1]/act:type" />))
-->'<xsl:value-of select="$parentguid" />'</xsl:template>
<xsl:template match="gnc:transaction">
insert into journals (reference, date, description, currency)
values (
<xsl:choose>
<xsl:when test="trn:num"><xsl:apply-templates select="trn:num" /></xsl:when>
<xsl:otherwise>null</xsl:otherwise>
</xsl:choose>,
cast('<xsl:value-of select="trn:date-posted/ts:date" />' as date),
'<xsl:value-of select="str:replace(trn:description, &quot;&apos;&quot;, &quot;&apos;&apos;&quot;)" />',
(select id from currencies where symbol = '<xsl:value-of select="trn:currency/cmdty:id" />'));
<xsl:apply-templates select="trn:splits/trn:split"/>
</xsl:template>
<xsl:template match="trn:num">'<xsl:value-of select="str:replace(., &quot;&apos;&quot;, &quot;&apos;&apos;&quot;)" />'</xsl:template>
<xsl:template match="trn:split"><xsl:variable name="qty" select="split:quantity" />
<xsl:variable name="quantity" select='substring-before($qty, "/") div substring-after($qty, "/")' />
insert into transactions (journal, description, account, amount, exchange_rate, is_debit)
values (currval('journals_id_seq'),
<xsl:choose>
<xsl:when test="count(split:memo) > 0"><xsl:apply-templates select="split:memo" /></xsl:when>
<xsl:otherwise>null</xsl:otherwise>
</xsl:choose>,
(select account from account_id_to_guid where guid='<xsl:value-of select="split:account"/>'),
abs(<xsl:value-of select="$quantity" />),
<xsl:choose>
<xsl:when test="$quantity = 0">abs(<xsl:value-of select="split:value" />.0),</xsl:when>
<xsl:otherwise>abs((<xsl:value-of select="split:value" />.0)/<xsl:value-of select="$quantity" />),</xsl:otherwise>
</xsl:choose>
case when <xsl:value-of select="$quantity"/> &lt; 0.0 then FALSE else TRUE end);
</xsl:template>
<xsl:template match="split:memo">'<xsl:value-of select="str:replace(., &quot;&apos;&quot;, &quot;&apos;&apos;&quot;)" />'</xsl:template>
</xsl:stylesheet>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment