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