gnucash2ledger 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +6 -0
- data/README +30 -0
- data/bin/gnucash2ledger +4 -0
- data/lib/input.rb +115 -0
- metadata +139 -0
data/CHANGELOG
ADDED
data/README
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
= gnucash2ledger
|
2
|
+
|
3
|
+
A command-line script that converts GnuCash v2.2.9 data files to human readable ledger format. Ledger v2 did this natively, but it has been removed from the v3 branch with the intention of replacing it with a script just like this one. This project is bridging the gap until such a script exists.
|
4
|
+
|
5
|
+
== Quickstart
|
6
|
+
|
7
|
+
gem install gnucash2ledger
|
8
|
+
cat your/gnucash/datafile | gunzip | gnucash2ledger > new-ledger.dat
|
9
|
+
ledger -f new-ledger.dat bal
|
10
|
+
|
11
|
+
== Developing
|
12
|
+
|
13
|
+
You'll need git and bundler. You probably already have them eh.
|
14
|
+
|
15
|
+
git clone git://github.com/xaviershay/gnucash2ledger.git
|
16
|
+
cd gnucash2ledger
|
17
|
+
bundle install
|
18
|
+
bundle exec rake
|
19
|
+
|
20
|
+
== Status
|
21
|
+
|
22
|
+
Works for me on my Snow Leopard mac.
|
23
|
+
|
24
|
+
* Normal transactions
|
25
|
+
* Reconciled transactions (if any split in gnucash is marked as reconciled, the entry in ledger is marked as cleared)
|
26
|
+
* Commodities (only the initial price is used, doesn't convert price history yet)
|
27
|
+
|
28
|
+
== References
|
29
|
+
* http://www.gnucash.org/
|
30
|
+
* http://wiki.github.com/jwiegley/ledger/
|
data/bin/gnucash2ledger
ADDED
data/lib/input.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'date'
|
3
|
+
require 'active_support/core_ext'
|
4
|
+
|
5
|
+
module G2L
|
6
|
+
class Input
|
7
|
+
attr_accessor :input
|
8
|
+
|
9
|
+
def initialize(input)
|
10
|
+
self.input = input
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_ledger
|
14
|
+
doc = Nokogiri::XML(input)
|
15
|
+
|
16
|
+
accounts = doc.xpath('//gnc:account').inject({}) do |all, account|
|
17
|
+
id = account.xpath("act:id")[0].text
|
18
|
+
parent = account.xpath("act:parent")[0]
|
19
|
+
|
20
|
+
all.update(id => {
|
21
|
+
:name => account.xpath("act:name")[0].text,
|
22
|
+
:commodity => account.xpath("act:commodity/cmdty:id")[0].try(:text),
|
23
|
+
:parent => parent ? parent.text : nil
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
27
|
+
accounts = resolve_accounts(accounts)
|
28
|
+
|
29
|
+
transactions = doc.xpath('//gnc:transaction').map do |transaction|
|
30
|
+
{
|
31
|
+
:date => Date.parse(transaction.xpath("trn:date-posted/ts:date")[0].text),
|
32
|
+
:description => transaction.xpath("trn:description")[0].text,
|
33
|
+
:splits => transaction.xpath("trn:splits/trn:split").map {|split| {
|
34
|
+
:account => split.xpath("split:account")[0].text,
|
35
|
+
:action => split.xpath("split:action")[0].try(:text),
|
36
|
+
:value => parse_value(split.xpath("split:value")[0]),
|
37
|
+
:quantity => parse_value(split.xpath("split:quantity")[0]),
|
38
|
+
:reconciled => split.xpath("split:reconciled-state")[0].text == 'y'
|
39
|
+
}}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Generate output
|
44
|
+
transactions.sort_by {|x| x[:date] }.map do |tx|
|
45
|
+
(["%s %s%s" % [
|
46
|
+
tx[:date].strftime("%Y/%m/%d"),
|
47
|
+
tx[:splits].any? {|y| y[:reconciled] } ? '* ' : '',
|
48
|
+
tx[:description]
|
49
|
+
]] + tx[:splits].map {|split|
|
50
|
+
account = accounts[split[:account]]
|
51
|
+
|
52
|
+
value = if split[:action]
|
53
|
+
"%i %s" % [split[:quantity].to_i, account[:commodity]]
|
54
|
+
else
|
55
|
+
'$' + split[:quantity].to_s
|
56
|
+
end
|
57
|
+
" %-44s%s" % [account[:name], value]
|
58
|
+
}).join("\n")
|
59
|
+
end.join("\n\n")
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# Shelved
|
65
|
+
def to_xml
|
66
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
67
|
+
xml.ledger('xmlns:tr' => '', 'xmlns:en' => '') {
|
68
|
+
transactions.each do |tx|
|
69
|
+
xml.entry {
|
70
|
+
xml['en'].date(tx[:date].strftime("%Y/%m/%d"))
|
71
|
+
xml['en'].payee(tx[:description])
|
72
|
+
xml['en'].cleared if tx[:splits].any? {|x| x[:reconciled]}
|
73
|
+
xml['en'].transactions {
|
74
|
+
tx[:splits].each do |split|
|
75
|
+
xml.transaction {
|
76
|
+
xml['tr'].account accounts[split[:account]][:name]
|
77
|
+
xml.value(:type => 'amount') {
|
78
|
+
xml.commodity(:flags => 'PT') {
|
79
|
+
xml.symbol '$'
|
80
|
+
}
|
81
|
+
xml.quantity split[:value]
|
82
|
+
}
|
83
|
+
}
|
84
|
+
end
|
85
|
+
}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
}
|
89
|
+
end
|
90
|
+
builder.to_xml
|
91
|
+
end
|
92
|
+
|
93
|
+
def parse_value(value)
|
94
|
+
if value
|
95
|
+
value.text.split("/").map(&:to_f).inject {|a, b| a / b }
|
96
|
+
else
|
97
|
+
0
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def resolve_accounts(accounts)
|
102
|
+
resolve_name = lambda do |id|
|
103
|
+
if accounts[id][:parent]
|
104
|
+
[resolve_name[accounts[id][:parent]], accounts[id][:name]].flatten.join(':')
|
105
|
+
else
|
106
|
+
[]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
accounts.inject({}) do |a, (id, account)|
|
111
|
+
a.update(id => account.merge(:name => resolve_name[id]))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gnucash2ledger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 9
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: "0.1"
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Xavier Shay
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-25 00:00:00 +10:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
22
|
+
none: false
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
hash: 3
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
requirement: *id001
|
31
|
+
name: rake
|
32
|
+
type: :development
|
33
|
+
prerelease: false
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
hash: 3
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
requirement: *id002
|
45
|
+
name: rspec
|
46
|
+
type: :development
|
47
|
+
prerelease: false
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
hash: 3
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
requirement: *id003
|
59
|
+
name: builder
|
60
|
+
type: :runtime
|
61
|
+
prerelease: false
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
requirement: *id004
|
73
|
+
name: activesupport
|
74
|
+
type: :runtime
|
75
|
+
prerelease: false
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
hash: 3
|
83
|
+
segments:
|
84
|
+
- 0
|
85
|
+
version: "0"
|
86
|
+
requirement: *id005
|
87
|
+
name: nokogiri
|
88
|
+
type: :runtime
|
89
|
+
prerelease: false
|
90
|
+
description:
|
91
|
+
email:
|
92
|
+
- contact@rhnh.net
|
93
|
+
executables:
|
94
|
+
- gnucash2ledger
|
95
|
+
extensions: []
|
96
|
+
|
97
|
+
extra_rdoc_files: []
|
98
|
+
|
99
|
+
files:
|
100
|
+
- bin/gnucash2ledger
|
101
|
+
- lib/input.rb
|
102
|
+
- README
|
103
|
+
- CHANGELOG
|
104
|
+
has_rdoc: true
|
105
|
+
homepage: http://github.com/xaviershay/gnucash2ledger
|
106
|
+
licenses: []
|
107
|
+
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
hash: 3
|
119
|
+
segments:
|
120
|
+
- 0
|
121
|
+
version: "0"
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
hash: 3
|
128
|
+
segments:
|
129
|
+
- 0
|
130
|
+
version: "0"
|
131
|
+
requirements: []
|
132
|
+
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 1.3.7
|
135
|
+
signing_key:
|
136
|
+
specification_version: 3
|
137
|
+
summary: Convert GnuCash files to a format supported by the ledger command line application
|
138
|
+
test_files: []
|
139
|
+
|