line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package GnuCash::SQLite; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
659588
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
30
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
25
|
|
5
|
1
|
|
|
1
|
|
10
|
use 5.10.0; |
|
1
|
|
|
|
|
3
|
|
6
|
1
|
|
|
1
|
|
647
|
use UUID::Tiny ':std'; |
|
1
|
|
|
|
|
16086
|
|
|
1
|
|
|
|
|
204
|
|
7
|
1
|
|
|
1
|
|
1741
|
use DBI; |
|
1
|
|
|
|
|
18731
|
|
|
1
|
|
|
|
|
64
|
|
8
|
1
|
|
|
1
|
|
8
|
use DateTime; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
23
|
|
9
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
55
|
|
10
|
1
|
|
|
1
|
|
957
|
use Path::Tiny; |
|
1
|
|
|
|
|
13809
|
|
|
1
|
|
|
|
|
1427
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
GnuCash::SQLite - A module to access GnuCash SQLite files |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 VERSION |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
version 0.08 |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=cut |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '0.08'; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub new { |
25
|
4
|
|
|
4
|
0
|
1219
|
my $class = shift; |
26
|
4
|
|
|
|
|
13
|
my %attr = @_; |
27
|
4
|
|
|
|
|
8
|
my $self = {}; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
croak 'No GnuCash file defined.' |
30
|
4
|
100
|
|
|
|
222
|
unless defined($attr{db}); |
31
|
|
|
|
|
|
|
croak "File: $attr{db} does not exist." |
32
|
3
|
100
|
|
|
|
12
|
unless path($attr{db})->is_file; |
33
|
|
|
|
|
|
|
|
34
|
2
|
|
|
|
|
212
|
$self->{db} = $attr{db}; |
35
|
2
|
|
|
|
|
20
|
$self->{dbh} = DBI->connect("dbi:SQLite:dbname=$self->{db}","",""); |
36
|
|
|
|
|
|
|
|
37
|
2
|
|
|
|
|
12841
|
bless $self, $class; |
38
|
2
|
|
|
|
|
7
|
return $self; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# Create a 32-character UUID |
42
|
|
|
|
|
|
|
sub create_guid { |
43
|
7
|
|
|
7
|
0
|
907
|
my $uuid = create_uuid_as_string(UUID_V1); |
44
|
7
|
|
|
|
|
1083
|
$uuid =~ s/-//g; |
45
|
7
|
|
|
|
|
23
|
return $uuid; |
46
|
|
|
|
|
|
|
} |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# Given an account name, return the GUID of the currency (aka commodity) |
49
|
|
|
|
|
|
|
# associated with that account |
50
|
|
|
|
|
|
|
sub commodity_guid { |
51
|
3
|
|
|
3
|
0
|
678
|
my $self = shift; |
52
|
3
|
|
|
|
|
14
|
my $account_name = shift; |
53
|
|
|
|
|
|
|
|
54
|
3
|
|
|
|
|
8
|
my $sql = "SELECT commodity_guid FROM accounts " |
55
|
|
|
|
|
|
|
. "WHERE guid = (".$self->account_guid_sql($account_name).")"; |
56
|
|
|
|
|
|
|
|
57
|
3
|
|
|
|
|
8
|
return $self->_runsql($sql)->[0][0]; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Given a date in YYYYMMDD format, |
61
|
|
|
|
|
|
|
# This is always in the local timezone |
62
|
|
|
|
|
|
|
# And GnuCash stores all dates in UTC timezone |
63
|
|
|
|
|
|
|
# This function needs to: |
64
|
|
|
|
|
|
|
# 1. Create a date time with the local timezone |
65
|
|
|
|
|
|
|
# 2. Switch to the UTC timezone |
66
|
|
|
|
|
|
|
# 3. Store that timestamp |
67
|
|
|
|
|
|
|
# For example, the 'Asia/Bangkok' timezone is UTC +7:00 |
68
|
|
|
|
|
|
|
# given txn date of 20140101 (in the local timezone) |
69
|
|
|
|
|
|
|
# return 20131231170000 (which gets stored in the db) |
70
|
|
|
|
|
|
|
sub UTC_post_date { |
71
|
4
|
|
|
4
|
0
|
140
|
my $self = shift; |
72
|
4
|
|
|
|
|
23
|
my ($YYYY, $MM, $DD) = (shift =~ /(....)(..)(..)/); |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Create a new |
75
|
4
|
|
|
|
|
25
|
my $dt = DateTime->new( |
76
|
|
|
|
|
|
|
year => $YYYY, |
77
|
|
|
|
|
|
|
month => $MM, |
78
|
|
|
|
|
|
|
day => $DD, |
79
|
|
|
|
|
|
|
time_zone => 'local' ); |
80
|
4
|
|
|
|
|
8240
|
$dt->set_time_zone('UTC'); |
81
|
4
|
|
|
|
|
51
|
return $dt->ymd('') . $dt->hms(''); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# Returns the system date in YYYYMMDDhhmmss format |
85
|
|
|
|
|
|
|
# Timezone is UTC (GMT 00:00) |
86
|
|
|
|
|
|
|
sub UTC_enter_date { |
87
|
3
|
|
|
3
|
0
|
1072
|
my $dt = DateTime->now(); |
88
|
3
|
|
|
|
|
787
|
return $dt->ymd('').$dt->hms(''); |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Given an account name, return the GUID of the account |
92
|
|
|
|
|
|
|
sub account_guid { |
93
|
15
|
|
|
15
|
0
|
712
|
my $self = shift; |
94
|
15
|
|
|
|
|
26
|
my $account_name = shift; |
95
|
|
|
|
|
|
|
|
96
|
15
|
|
|
|
|
34
|
my $sql = $self->account_guid_sql($account_name); |
97
|
15
|
|
|
|
|
35
|
return $self->_runsql($sql)->[0][0]; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# Given an account name, return the SQL that reads its GUID |
101
|
|
|
|
|
|
|
# Generate a recursive SQL given the full account name e.g. Assets:Cash |
102
|
|
|
|
|
|
|
# A naive implementation may just extract the tail account |
103
|
|
|
|
|
|
|
# i.e. SELECT guid FROM accounts WHERE name = 'Cash'; |
104
|
|
|
|
|
|
|
# That fails when accounts of the same name have different parents |
105
|
|
|
|
|
|
|
# e.g. Assets:Husband:Cash and Assets:Wife:Cash |
106
|
|
|
|
|
|
|
sub account_guid_sql { |
107
|
19
|
|
|
19
|
0
|
28
|
my $self = shift; |
108
|
19
|
|
|
|
|
38
|
my ($acct_name) = @_; |
109
|
19
|
|
|
|
|
26
|
my $sub_sql = 'SELECT guid FROM accounts WHERE name = "Root Account"'; |
110
|
19
|
|
|
|
|
74
|
foreach my $acct (split ":", $acct_name) { |
111
|
35
|
|
|
|
|
113
|
$sub_sql = 'SELECT guid FROM accounts ' |
112
|
|
|
|
|
|
|
. 'WHERE name = "'.$acct.'" ' |
113
|
|
|
|
|
|
|
. 'AND parent_guid = ('.$sub_sql.')'; |
114
|
|
|
|
|
|
|
} |
115
|
19
|
|
|
|
|
50
|
return $sub_sql; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# Given a guid, return a list of child guids or if none, an empty arrayref |
119
|
|
|
|
|
|
|
sub child_guid { |
120
|
15
|
|
|
15
|
0
|
28
|
my $self = shift; |
121
|
15
|
|
|
|
|
24
|
my $parent_guid = shift; |
122
|
|
|
|
|
|
|
|
123
|
15
|
|
|
|
|
46
|
my $sql = qq/SELECT guid FROM accounts WHERE parent_guid = "$parent_guid"/; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# The map belows converts [[x],[y],[z]] into [x,y,z] |
126
|
15
|
|
|
|
|
25
|
my @res = map { $$_[0] } @{ $self->_runsql($sql) }; |
|
8
|
|
|
|
|
61
|
|
|
15
|
|
|
|
|
32
|
|
127
|
15
|
|
|
|
|
148
|
return \@res; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# Given an account guid, |
131
|
|
|
|
|
|
|
# Return the balance at that guid, ignoring child accounts if any. |
132
|
|
|
|
|
|
|
sub _node_bal { |
133
|
14
|
|
|
14
|
|
34
|
my $self = shift; |
134
|
14
|
|
|
|
|
21
|
my $guid = shift; |
135
|
|
|
|
|
|
|
|
136
|
14
|
|
|
|
|
33
|
my $sql = "SELECT printf('%.2f',SUM(value_num/(value_denom*1.0))) FROM splits " |
137
|
|
|
|
|
|
|
. "WHERE account_guid = ?"; |
138
|
14
|
|
50
|
|
|
36
|
return $self->_runsql($sql,$guid)->[0][0] || 0; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# Recursive accumulator |
142
|
|
|
|
|
|
|
sub _guid_bal { |
143
|
13
|
|
|
13
|
|
33
|
my $self = shift; |
144
|
13
|
|
|
|
|
25
|
my $guid = shift; |
145
|
13
|
|
50
|
|
|
63
|
my $bal = shift || 0; |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Accumulate balances in child accounts |
148
|
13
|
|
|
|
|
24
|
foreach my $g (@{$self->child_guid($guid)}) { |
|
13
|
|
|
|
|
40
|
|
149
|
6
|
|
|
|
|
28
|
$bal += $self->_guid_bal($g); |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Add balance in node and return |
153
|
13
|
|
|
|
|
54
|
return $bal + $self->_node_bal($guid); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# Given an account name, |
157
|
|
|
|
|
|
|
# Return the balance in that account, include child accounts, if any |
158
|
|
|
|
|
|
|
sub account_balance { |
159
|
6
|
|
|
6
|
1
|
31
|
my $self = shift; |
160
|
6
|
|
|
|
|
14
|
my $acct_name = shift; |
161
|
|
|
|
|
|
|
|
162
|
6
|
|
|
|
|
24
|
my $guid = $self->account_guid($acct_name); |
163
|
6
|
100
|
|
|
|
35
|
return undef unless defined ($guid); |
164
|
5
|
|
|
|
|
35
|
return $self->_guid_bal($guid); |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# Add a transaction to the GnuCash. |
168
|
|
|
|
|
|
|
# Transaction is a hashref e.g.: |
169
|
|
|
|
|
|
|
# |
170
|
|
|
|
|
|
|
# my $txn = { |
171
|
|
|
|
|
|
|
# date => '20140102', |
172
|
|
|
|
|
|
|
# description => 'Deposit monthly savings', |
173
|
|
|
|
|
|
|
# from_account => 'Assets:Cash', |
174
|
|
|
|
|
|
|
# to_account => 'Assets:aBank', |
175
|
|
|
|
|
|
|
# amount => 2540.15, |
176
|
|
|
|
|
|
|
# number => '' |
177
|
|
|
|
|
|
|
# }; |
178
|
|
|
|
|
|
|
# |
179
|
|
|
|
|
|
|
# To effect the transaction, do the following: |
180
|
|
|
|
|
|
|
# 1. Add 1 row to transactions table |
181
|
|
|
|
|
|
|
# 2. Add 2 rows to splits table |
182
|
|
|
|
|
|
|
# 3. Add 1 row to slots table |
183
|
|
|
|
|
|
|
# See |
184
|
|
|
|
|
|
|
# http://wideopenstudy.blogspot.com/2014/11/how-to-add-transaction-programmatically.html |
185
|
|
|
|
|
|
|
sub add_transaction { |
186
|
1
|
|
|
1
|
1
|
842
|
my $self = shift; |
187
|
1
|
|
|
|
|
2
|
my $txn = shift; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# augment the transaction with needed data |
190
|
1
|
|
|
|
|
3
|
$txn = $self->_augment($txn); |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
# List the SQLs |
193
|
1
|
|
|
|
|
2
|
my $txn_sql = 'INSERT INTO transactions VALUES (?,?,?,?,?,?)'; |
194
|
1
|
|
|
|
|
3
|
my $splt_sql = 'INSERT INTO splits VALUES ' |
195
|
|
|
|
|
|
|
. ' (?,?,?,"","","n","",?,100,?,100,null)'; |
196
|
1
|
|
|
|
|
3
|
my $slot_sql = 'INSERT INTO slots (obj_guid,name,slot_type,int64_val,' |
197
|
|
|
|
|
|
|
. ' string_val,double_val,timespec_val,' |
198
|
|
|
|
|
|
|
. ' guid_val,numeric_val_num,' |
199
|
|
|
|
|
|
|
. ' numeric_val_denom,gdate_val) ' |
200
|
|
|
|
|
|
|
. 'VALUES (?,"date-posted",10,0,"",0.0,"","",0,1,?)'; |
201
|
|
|
|
|
|
|
# This SQL form because slots has auto-increment field |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# Run the SQLs |
204
|
1
|
|
|
|
|
1
|
$self->_runsql($txn_sql, map { $txn->{$_} } |
|
6
|
|
|
|
|
12
|
|
205
|
|
|
|
|
|
|
qw/tx_guid tx_ccy_guid number tx_post_date tx_enter_date |
206
|
|
|
|
|
|
|
description /); |
207
|
1
|
|
|
|
|
7
|
$self->_runsql($splt_sql, map { $txn->{$_} } |
|
5
|
|
|
|
|
19
|
|
208
|
|
|
|
|
|
|
qw/splt_guid_1 tx_guid tx_from_guid tx_from_numer tx_from_numer/); |
209
|
1
|
|
|
|
|
5
|
$self->_runsql($splt_sql, map { $txn->{$_} } |
|
5
|
|
|
|
|
20
|
|
210
|
|
|
|
|
|
|
qw/splt_guid_2 tx_guid tx_to_guid tx_to_numer tx_to_numer/); |
211
|
1
|
|
|
|
|
7
|
$self->_runsql($slot_sql, map { $txn->{$_} } |
|
2
|
|
|
|
|
11
|
|
212
|
|
|
|
|
|
|
qw/tx_guid date/); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# Augment the transaction with data required to generate data rows |
216
|
|
|
|
|
|
|
sub _augment { |
217
|
2
|
|
|
2
|
|
90
|
my $self = shift; |
218
|
2
|
|
|
|
|
3
|
my $txn_orig = shift; |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# Make a copy of the original transaction so as not to clobber it |
221
|
|
|
|
|
|
|
# Copy only the fields needed |
222
|
2
|
|
|
|
|
3
|
my $txn = {}; |
223
|
2
|
|
|
|
|
5
|
map { $txn->{$_} = $txn_orig->{$_} } ( |
|
12
|
|
|
|
|
23
|
|
224
|
|
|
|
|
|
|
qw/date description from_account to_account amount number/); |
225
|
|
|
|
|
|
|
|
226
|
2
|
|
|
|
|
6
|
$txn->{tx_guid} = $self->create_guid(); |
227
|
2
|
|
|
|
|
7
|
$txn->{tx_ccy_guid} = $self->commodity_guid($txn->{from_account}); |
228
|
2
|
|
|
|
|
7
|
$txn->{tx_post_date} = $self->UTC_post_date($txn->{date}); |
229
|
2
|
|
|
|
|
48
|
$txn->{tx_enter_date} = $self->UTC_enter_date(); |
230
|
2
|
|
|
|
|
63
|
$txn->{tx_from_guid} = $self->account_guid($txn->{from_account}); |
231
|
2
|
|
|
|
|
8
|
$txn->{tx_to_guid} = $self->account_guid($txn->{to_account}); |
232
|
2
|
|
|
|
|
7
|
$txn->{tx_from_numer} = $txn->{amount} * -100; |
233
|
2
|
|
|
|
|
6
|
$txn->{tx_to_numer} = $txn->{amount} * 100; |
234
|
2
|
|
|
|
|
5
|
$txn->{splt_guid_1} = $self->create_guid(); |
235
|
2
|
|
|
|
|
5
|
$txn->{splt_guid_2} = $self->create_guid(); |
236
|
|
|
|
|
|
|
|
237
|
2
|
|
|
|
|
5
|
return $txn; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# Return 1 if Gnucash database is locked, |
241
|
|
|
|
|
|
|
# Return 0 if no other application has locked the database. |
242
|
|
|
|
|
|
|
sub is_locked { |
243
|
2
|
|
|
2
|
0
|
9
|
my $self = shift; |
244
|
2
|
|
|
|
|
7
|
my $sql = "SELECT count(*) FROM gnclock"; |
245
|
2
|
100
|
|
|
|
10
|
return $self->_runsql($sql)->[0][0] == 0 ? 0 : 1; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# Given an SQL statement and optionally a list of arguments |
249
|
|
|
|
|
|
|
# execute the SQL with those arguments |
250
|
|
|
|
|
|
|
sub _runsql { |
251
|
55
|
|
|
55
|
|
81
|
my $self = shift; |
252
|
55
|
|
|
|
|
134
|
my ($sql,@args) = @_; |
253
|
|
|
|
|
|
|
|
254
|
55
|
|
|
|
|
350
|
my $sth = $self->{dbh}->prepare($sql); |
255
|
55
|
|
|
|
|
100988
|
$sth->execute(@args); |
256
|
55
|
|
|
|
|
1281
|
my $data = $sth->fetchall_arrayref(); |
257
|
55
|
|
|
|
|
270
|
$sth->finish; |
258
|
|
|
|
|
|
|
|
259
|
55
|
|
|
|
|
1931
|
return $data; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
1; |
263
|
|
|
|
|
|
|
__END__ |
264
|
|
|
|
|
|
|
# Below is stub documentation for your module. You'd better edit it! |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=head1 SYNOPSIS |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
use GnuCash::SQLite; |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
# create the book |
271
|
|
|
|
|
|
|
$book = GnuCash::SQLite->new(db => 'my_accounts.gnucash'); |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# get account balances |
274
|
|
|
|
|
|
|
$on_hand = $book->account_balance('Assets:Cash'); |
275
|
|
|
|
|
|
|
$total = $book->account_balance('Assets'); |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
# check if book is locked by another application |
278
|
|
|
|
|
|
|
die "Book is currently used by another application." |
279
|
|
|
|
|
|
|
if $book->is_locked; |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# add a transaction |
282
|
|
|
|
|
|
|
$book->add_transaction({ |
283
|
|
|
|
|
|
|
date => '20140102', |
284
|
|
|
|
|
|
|
description => 'Deposit monthly savings', |
285
|
|
|
|
|
|
|
from_account => 'Assets:Cash', |
286
|
|
|
|
|
|
|
to_account => 'Assets:aBank', |
287
|
|
|
|
|
|
|
amount => 2540.15, |
288
|
|
|
|
|
|
|
number => '' |
289
|
|
|
|
|
|
|
}); |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
# access internal GUIDs |
292
|
|
|
|
|
|
|
$book->account_guid('Assets:Cash'); # GUID of account |
293
|
|
|
|
|
|
|
$book->commodity_guid('Assets:Cash'); # GUID of currency |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=head1 DESCRIPTION |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
GnuCash::SQLite provides an API to read account balances and write |
298
|
|
|
|
|
|
|
transactions against a GnuCash set of accounts (only SQLite3 backend |
299
|
|
|
|
|
|
|
supported). |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
When using the module, always provide account names in full e.g. "Assets:Cash" |
302
|
|
|
|
|
|
|
rather than just "Cash". This lets the module distinguish between accounts |
303
|
|
|
|
|
|
|
with the same name but different parents e.g. Assets:Misc and |
304
|
|
|
|
|
|
|
Expenses:Misc |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=head1 METHODS |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=head2 Constructor |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
$book = GnuCash::SQLite->new(db => 'my_account.gnucash'); |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Returns a new C<GnuCash::SQLite> object that accesses a GnuCash with and |
313
|
|
|
|
|
|
|
SQLite backend. The module assumes you have already created a GnuCash file |
314
|
|
|
|
|
|
|
with an SQLite backend and that is the file that should be passed as the |
315
|
|
|
|
|
|
|
parameter. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
If no file parameter is passed, or if the file is missing, the program will |
318
|
|
|
|
|
|
|
terminate. |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=head2 account_balance |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
$book->account_balance('Assets:Cash'); # always provide account names in full |
323
|
|
|
|
|
|
|
$book->account_balance('Assets'); # includes child accounts e.g. Assets:Cash |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
Given an account name, return the balance in the account. Account names must |
326
|
|
|
|
|
|
|
be provided in full to distinguish between accounts with the same name but |
327
|
|
|
|
|
|
|
different parents e.g. Assets:Alice:Cash and Assets:Bob:Cash |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
If a parent account name is provided, the total balance, which includes all |
330
|
|
|
|
|
|
|
children accounts, will be returned. |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=head2 add_transaction |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
$deposit = { |
335
|
|
|
|
|
|
|
date => '20140102', |
336
|
|
|
|
|
|
|
description => 'Deposit monthly savings', |
337
|
|
|
|
|
|
|
from_account => 'Assets:Cash', |
338
|
|
|
|
|
|
|
to_account => 'Assets:aBank', |
339
|
|
|
|
|
|
|
amount => 2540.15, |
340
|
|
|
|
|
|
|
number => '' |
341
|
|
|
|
|
|
|
}; |
342
|
|
|
|
|
|
|
$book->add_transaction($deposit); |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
A transaction is defined to have the fields as listed in the example above. |
345
|
|
|
|
|
|
|
All fields are mandatory and hopefully self-explanatory. Constraints on some |
346
|
|
|
|
|
|
|
of the fields are listed below: |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
date Date of the transaction. Formatted as YYYYMMDD. |
349
|
|
|
|
|
|
|
from_account Full account name required. |
350
|
|
|
|
|
|
|
to_account Full account name required. |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=head1 CAVEATS/LIMITATIONS |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Some things to be aware of: |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
1. You should have created a GnuCash file with an SQLite backend already |
358
|
|
|
|
|
|
|
2. Module accesses the GnuCash SQLite3 db directly; i.e. use at your own risk. |
359
|
|
|
|
|
|
|
3. Only transactions between Asset accounts have been tested. |
360
|
|
|
|
|
|
|
4. Only two (2) splits for each transaction will be created |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
This module works with GnuCash v2.4.13 on Linux. |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=head1 SEE ALSO |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
GnuCash wiki pages includes a section on C API and a section on Python |
367
|
|
|
|
|
|
|
bindings which may be of interest. |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
C API : http://wiki.gnucash.org/wiki/C_API |
370
|
|
|
|
|
|
|
Python bindings: http://wiki.gnucash.org/wiki/Python_Bindings |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
This module does not rely on the C API (maybe it should). Instead it relies on |
373
|
|
|
|
|
|
|
some reverse engineering work to understand the changes a transaction makes |
374
|
|
|
|
|
|
|
to the sqlite database. See |
375
|
|
|
|
|
|
|
http://wideopenstudy.blogspot.com/search/label/GnuCash for details. |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=head1 SUPPORT |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 Bugs / Feature Requests |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
Please report any bugs or feature requests through the issue tracker at |
382
|
|
|
|
|
|
|
L<https://github.com/hoekit/GnuCash-SQLite/issues>. You will be notified |
383
|
|
|
|
|
|
|
automatically of any progress on your issue. |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=head2 Source Code |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
This is open source software. The code repository is available for public |
388
|
|
|
|
|
|
|
review and contribution under the terms of the license. |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
<https://github.com/hoekit/GnuCash-SQLite> |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
git clone git@github.com:hoekit/GnuCash-SQLite.git |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=head1 CREDITS |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
Credit goes to L<Sawyer X|https://metacpan.org/author/XSAWYERX> for fixing long-standing floating-point bug. |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
=head1 AUTHOR |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
Hoe Kit CHEW, E<lt>hoekit at gmail.comE<gt> |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Copyright (C) 2014 by Chew Hoe Kit |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
407
|
|
|
|
|
|
|
it under the same terms as Perl itself, either Perl version 5.10.0 or, |
408
|
|
|
|
|
|
|
at your option, any later version of Perl 5 you may have available. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=cut |