ledger-rest 2.0.3 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +2 -0
- data/Gemfile +1 -1
- data/Guardfile +4 -22
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +62 -11
- data/config.ru +1 -1
- data/ledger-rest.gemspec +17 -17
- data/ledger-rest.yml.example +3 -0
- data/lib/ledger-rest/app.rb +21 -34
- data/lib/ledger-rest/core_ext.rb +4 -3
- data/lib/ledger-rest/git.rb +8 -7
- data/lib/ledger-rest/ledger.rb +29 -24
- data/lib/ledger-rest/ledger/balance.rb +48 -15
- data/lib/ledger-rest/ledger/budget.rb +35 -16
- data/lib/ledger-rest/ledger/entry.rb +1 -4
- data/lib/ledger-rest/ledger/parser.rb +41 -37
- data/lib/ledger-rest/ledger/register.rb +31 -24
- data/lib/ledger-rest/ledger/transaction.rb +33 -28
- data/lib/ledger-rest/transactions.py +47 -0
- data/lib/ledger-rest/version.rb +2 -1
- data/spec/files/append.ledger +13 -0
- data/spec/files/budget.ledger +4 -0
- data/spec/files/main.ledger +4 -0
- data/spec/files/opening_balance.ledger +4 -0
- data/spec/files/transactions.ledger +11 -0
- data/spec/ledger-rest.yml +3 -0
- data/spec/ledger-rest/api/accounts_spec.rb +19 -0
- data/spec/ledger-rest/api/balance_spec.rb +117 -0
- data/spec/ledger-rest/api/budget_spec.rb +110 -0
- data/spec/ledger-rest/api/payees_spec.rb +19 -0
- data/spec/ledger-rest/api/register_spec.rb +233 -0
- data/spec/ledger-rest/api/transactions_spec.rb +201 -0
- data/spec/ledger-rest/api/version_spec.rb +13 -0
- data/spec/ledger-rest/ledger/balance_spec.rb +124 -0
- data/spec/ledger-rest/ledger/parser_spec.rb +184 -117
- data/spec/ledger-rest/ledger/transaction_spec.rb +38 -31
- data/spec/spec_helper.rb +22 -0
- data/spec/support/deep_eq_matcher.rb +149 -0
- metadata +52 -31
- data/ledger-rest.org +0 -22
@@ -0,0 +1,47 @@
|
|
1
|
+
import ledger
|
2
|
+
import json
|
3
|
+
import sys
|
4
|
+
|
5
|
+
filename = sys.argv[1]
|
6
|
+
query = sys.argv[2]
|
7
|
+
|
8
|
+
transactions = []
|
9
|
+
last_xact = None
|
10
|
+
for match in ledger.read_journal(filename).query(query):
|
11
|
+
if match.xact != last_xact:
|
12
|
+
transaction = {}
|
13
|
+
transaction['payee'] = match.xact.payee
|
14
|
+
transaction['date'] = match.xact.date.strftime('%Y/%m/%d')
|
15
|
+
|
16
|
+
if match.xact.code != None:
|
17
|
+
transaction['code'] = match.xact.code
|
18
|
+
|
19
|
+
if match.xact.note != None:
|
20
|
+
transaction['note'] = match.xact.note.strip()
|
21
|
+
|
22
|
+
if match.xact.aux_date != None:
|
23
|
+
transaction['effective_date'] = match.xact.aux_date.strftime('%Y/%m/%d')
|
24
|
+
|
25
|
+
if str(match.xact.state) == 'Cleared':
|
26
|
+
transaction['cleared'] = True
|
27
|
+
transaction['pending'] = False
|
28
|
+
elif str(match.xact.state) == 'Pending':
|
29
|
+
transaction['cleared'] = False
|
30
|
+
transaction['pending'] = True
|
31
|
+
else:
|
32
|
+
transaction['cleared'] = False
|
33
|
+
transaction['pending'] = False
|
34
|
+
|
35
|
+
transaction['posts'] = []
|
36
|
+
for post in match.xact.posts():
|
37
|
+
new_post = {}
|
38
|
+
new_post['account'] = str(post.account)
|
39
|
+
new_post['amount'] = float(post.amount)
|
40
|
+
new_post['commodity'] = str(post.amount.commodity)
|
41
|
+
transaction['posts'].append(new_post)
|
42
|
+
|
43
|
+
transactions.append(transaction)
|
44
|
+
|
45
|
+
last_xact = match.xact
|
46
|
+
|
47
|
+
print json.dumps(transactions)
|
data/lib/ledger-rest/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
2013/12/03 NaveenaPath
|
2
|
+
Expenses:Restaurants 9.00EUR
|
3
|
+
Assets:Cash
|
4
|
+
|
5
|
+
2013/12/05 Shikgoo
|
6
|
+
Expenses:Restaurants 12.00EUR
|
7
|
+
Liabilities:Max Mustermann
|
8
|
+
; meta_info: Some interesting meta information
|
9
|
+
|
10
|
+
2013/12/10 Bioladen Tegeler Straße
|
11
|
+
; Sonnenblumenkernbrot
|
12
|
+
Assets:Reimbursements:Hans Maulwurf 3.10EUR
|
13
|
+
Assets:Cash
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe '/accounts' do
|
5
|
+
it 'returns accounts' do
|
6
|
+
get '/accounts'
|
7
|
+
|
8
|
+
JSON.parse(last_response.body).should =~
|
9
|
+
[
|
10
|
+
'Assets:Cash',
|
11
|
+
'Assets:Giro',
|
12
|
+
'Assets:Reimbursements:Hans Maulwurf',
|
13
|
+
'Equity:Opening Balances',
|
14
|
+
'Expenses:Restaurants',
|
15
|
+
'Income:Invoice',
|
16
|
+
'Liabilities:Max Mustermann'
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe '/balance' do
|
5
|
+
context 'normally' do
|
6
|
+
let(:valid_json) do
|
7
|
+
{
|
8
|
+
'accounts' =>
|
9
|
+
[
|
10
|
+
{
|
11
|
+
'total' => '3177.80EUR',
|
12
|
+
'name' => 'Assets',
|
13
|
+
'depth' => 1,
|
14
|
+
'fullname' => 'Assets',
|
15
|
+
'accounts' =>
|
16
|
+
[
|
17
|
+
{
|
18
|
+
'total' => '5.70EUR',
|
19
|
+
'name' => 'Cash',
|
20
|
+
'depth' => 2,
|
21
|
+
'fullname' => 'Assets:Cash'
|
22
|
+
},
|
23
|
+
{
|
24
|
+
'total' => '3150.00EUR',
|
25
|
+
'name' => 'Giro',
|
26
|
+
'depth' => 2,
|
27
|
+
'fullname' => 'Assets:Giro'
|
28
|
+
},
|
29
|
+
{
|
30
|
+
'total' => '22.10EUR',
|
31
|
+
'name' => 'Reimbursements',
|
32
|
+
'depth' => 2,
|
33
|
+
'fullname' => 'Assets:Reimbursements',
|
34
|
+
'accounts' =>
|
35
|
+
[
|
36
|
+
{
|
37
|
+
'total' => '22.10EUR',
|
38
|
+
'name' => 'Hans Maulwurf',
|
39
|
+
'depth' => 3,
|
40
|
+
'fullname' => 'Assets:Reimbursements:Hans Maulwurf'
|
41
|
+
}
|
42
|
+
]
|
43
|
+
}
|
44
|
+
]
|
45
|
+
},
|
46
|
+
{
|
47
|
+
'total' => '-12.00EUR',
|
48
|
+
'name' => 'Liabilities',
|
49
|
+
'depth' => 1,
|
50
|
+
'fullname' => 'Liabilities',
|
51
|
+
'accounts' =>
|
52
|
+
[
|
53
|
+
{
|
54
|
+
'total' => '-12.00EUR',
|
55
|
+
'name' => 'Max Mustermann',
|
56
|
+
'depth' => 2,
|
57
|
+
'fullname' => 'Liabilities:Max Mustermann'
|
58
|
+
}
|
59
|
+
]
|
60
|
+
}
|
61
|
+
],
|
62
|
+
'total' => '3165.80EUR'
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'expands and wraps accounts' do
|
67
|
+
get '/balance', query: 'Assets Liabilities'
|
68
|
+
JSON.parse(last_response.body).should deep_eq valid_json
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'with --flat query' do
|
73
|
+
let(:valid_json) do
|
74
|
+
{
|
75
|
+
'accounts' =>
|
76
|
+
[
|
77
|
+
{
|
78
|
+
'total' => '5.70EUR',
|
79
|
+
'name' => 'Cash',
|
80
|
+
'depth' => 2,
|
81
|
+
'fullname' => 'Assets:Cash'
|
82
|
+
}, {
|
83
|
+
'total' => '3150.00EUR',
|
84
|
+
'name' => 'Giro',
|
85
|
+
'depth' => 2,
|
86
|
+
'fullname' => 'Assets:Giro'
|
87
|
+
}, {
|
88
|
+
'total' => '22.10EUR',
|
89
|
+
'name' => 'Reimbursements',
|
90
|
+
'depth' => 2,
|
91
|
+
'fullname' => 'Assets:Reimbursements'
|
92
|
+
}, {
|
93
|
+
'total' => '22.10EUR',
|
94
|
+
'name' => 'Hans Maulwurf',
|
95
|
+
'depth' => 3,
|
96
|
+
'fullname' => 'Assets:Reimbursements:Hans Maulwurf'
|
97
|
+
}, {
|
98
|
+
'total' => '-12.00EUR',
|
99
|
+
'name' => 'Liabilities',
|
100
|
+
'depth' => 1,
|
101
|
+
'fullname' => 'Liabilities'
|
102
|
+
}, {
|
103
|
+
'total' => '-12.00EUR',
|
104
|
+
'name' => 'Max Mustermann',
|
105
|
+
'depth' => 2,
|
106
|
+
'fullname' => 'Liabilities:Max Mustermann' }
|
107
|
+
],
|
108
|
+
'total' => '3165.80EUR'
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'does not wrap accounts' do
|
113
|
+
get '/balance', query: '--flat Assets Liabilities'
|
114
|
+
JSON.parse(last_response.body).should deep_eq valid_json
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe '/budget' do
|
4
|
+
context 'with query' do
|
5
|
+
let(:valid_json) do
|
6
|
+
{
|
7
|
+
'accounts' =>
|
8
|
+
[
|
9
|
+
{
|
10
|
+
'total' => '3177.80EUR',
|
11
|
+
'budget' => '-6600.00EUR',
|
12
|
+
'difference' => '9777.80EUR',
|
13
|
+
'percentage' => '-48%',
|
14
|
+
'account' => 'Assets'
|
15
|
+
}, {
|
16
|
+
'total' => '53.20EUR',
|
17
|
+
'budget' => '6600.00EUR',
|
18
|
+
'difference' => '-6546.80EUR',
|
19
|
+
'percentage' => '1%',
|
20
|
+
'account' => 'Expenses'
|
21
|
+
}, {
|
22
|
+
'total' => '0',
|
23
|
+
'budget' => '5400.00EUR',
|
24
|
+
'difference' => '-5400.00EUR',
|
25
|
+
'percentage' => '0%',
|
26
|
+
'account' => 'Expenses:Flat'
|
27
|
+
}, {
|
28
|
+
'total' => '53.20EUR',
|
29
|
+
'budget' => '1200.00EUR',
|
30
|
+
'difference' => '-1146.80EUR',
|
31
|
+
'percentage' => '4%',
|
32
|
+
'account' => 'Expenses:Restaurants'
|
33
|
+
}
|
34
|
+
],
|
35
|
+
'total' => '3231.00EUR',
|
36
|
+
'budget' => '0',
|
37
|
+
'difference' => '3231.00EUR',
|
38
|
+
'percentage' => '0'
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'responds with the right budget' do
|
43
|
+
get '/budget'
|
44
|
+
|
45
|
+
JSON.parse(last_response.body).should deep_eq valid_json
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'querying single account' do
|
50
|
+
let(:valid_json) do
|
51
|
+
{
|
52
|
+
'accounts' =>
|
53
|
+
[
|
54
|
+
{
|
55
|
+
'total' => '53.20EUR',
|
56
|
+
'budget' => '100.00EUR',
|
57
|
+
'difference' => '-46.80EUR',
|
58
|
+
'percentage' => '53%',
|
59
|
+
'account' => 'Expenses:Restaurants'
|
60
|
+
}
|
61
|
+
]
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'responds with the right budget' do
|
66
|
+
get '/budget', query: 'Restaurants'
|
67
|
+
|
68
|
+
JSON.parse(last_response.body).should deep_eq valid_json
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'querying single account' do
|
73
|
+
let(:valid_json) do
|
74
|
+
{
|
75
|
+
'accounts' =>
|
76
|
+
[
|
77
|
+
{
|
78
|
+
'total' => '53.20EUR',
|
79
|
+
'budget' => '550.00EUR',
|
80
|
+
'difference' => '-496.80EUR',
|
81
|
+
'percentage' => '10%',
|
82
|
+
'account' => 'Expenses'
|
83
|
+
}, {
|
84
|
+
'total' => '0',
|
85
|
+
'budget' => '450.00EUR',
|
86
|
+
'difference' => '-450.00EUR',
|
87
|
+
'percentage' => '0%',
|
88
|
+
'account' => 'Expenses:Flat'
|
89
|
+
}, {
|
90
|
+
'total' => '53.20EUR',
|
91
|
+
'budget' => '100.00EUR',
|
92
|
+
'difference' => '-46.80EUR',
|
93
|
+
'percentage' => '53%',
|
94
|
+
'account' => 'Expenses:Restaurants'
|
95
|
+
}
|
96
|
+
],
|
97
|
+
'total' => '53.20EUR',
|
98
|
+
'budget' => '550.00EUR',
|
99
|
+
'difference' => '-496.80EUR',
|
100
|
+
'percentage' => '10%',
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'responds with the right budget' do
|
105
|
+
get '/budget', query: 'Expenses'
|
106
|
+
|
107
|
+
JSON.parse(last_response.body).should deep_eq valid_json
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe '/payees' do
|
5
|
+
it 'returns payees' do
|
6
|
+
get '/payees'
|
7
|
+
|
8
|
+
JSON.parse(last_response.body).should =~
|
9
|
+
[
|
10
|
+
'Bioladen Tegeler Straße',
|
11
|
+
'Customer X',
|
12
|
+
'NaveenaPath',
|
13
|
+
'Opening Balance',
|
14
|
+
'Shikgoo',
|
15
|
+
'Sparkasse',
|
16
|
+
'Trattoria La Vialla'
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe '/register' do
|
5
|
+
context 'normal' do
|
6
|
+
let(:valid_response) do
|
7
|
+
[
|
8
|
+
{
|
9
|
+
'date' => '2013/12/01',
|
10
|
+
'effective_date' => '2013/12/02',
|
11
|
+
'code' => nil,
|
12
|
+
'cleared' => true,
|
13
|
+
'pending' => false,
|
14
|
+
'payee' => 'Sparkasse',
|
15
|
+
'postings' =>
|
16
|
+
[
|
17
|
+
{ 'account' => 'Assets:Cash',
|
18
|
+
'amount' => '50',
|
19
|
+
'total' => '50',
|
20
|
+
'commodity' => 'EUR'
|
21
|
+
},
|
22
|
+
{
|
23
|
+
'account' => 'Assets:Giro',
|
24
|
+
'amount' => '-50',
|
25
|
+
'total' => '-50',
|
26
|
+
'commodity' => 'EUR'
|
27
|
+
}
|
28
|
+
]
|
29
|
+
}
|
30
|
+
]
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'shows all fields' do
|
34
|
+
get '/register', query: '-p "2013-12-01"'
|
35
|
+
JSON.parse(last_response.body).should deep_eq valid_response
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'daily report' do
|
40
|
+
let(:valid_response) do
|
41
|
+
[
|
42
|
+
{
|
43
|
+
'beginning' => '2013/12/03',
|
44
|
+
'end' => '2013/12/03',
|
45
|
+
'postings' =>
|
46
|
+
[
|
47
|
+
{
|
48
|
+
'account' => 'Expenses:Restaurants',
|
49
|
+
'amount' => '9',
|
50
|
+
'total' => '9',
|
51
|
+
'commodity' => 'EUR'
|
52
|
+
}
|
53
|
+
]
|
54
|
+
}, {
|
55
|
+
'beginning' => '2013/12/04',
|
56
|
+
'end' => '2013/12/04',
|
57
|
+
'postings' =>
|
58
|
+
[
|
59
|
+
{
|
60
|
+
'account' => 'Expenses:Restaurants',
|
61
|
+
'amount' => '32.2',
|
62
|
+
'total' => '32.2',
|
63
|
+
'commodity' => 'EUR'
|
64
|
+
}
|
65
|
+
]
|
66
|
+
}, {
|
67
|
+
'beginning' => '2013/12/05',
|
68
|
+
'end' => '2013/12/05',
|
69
|
+
'postings' =>
|
70
|
+
[
|
71
|
+
{
|
72
|
+
'account' => 'Expenses:Restaurants',
|
73
|
+
'amount' => '12',
|
74
|
+
'total' => '12',
|
75
|
+
'commodity' => 'EUR'
|
76
|
+
}
|
77
|
+
]
|
78
|
+
}
|
79
|
+
]
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'shows beginning, end and postings with account, amount and total' do
|
83
|
+
get '/register', query: '--daily ^Expenses'
|
84
|
+
JSON.parse(last_response.body).should deep_eq valid_response
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'weekly report' do
|
89
|
+
let(:valid_response) do
|
90
|
+
[
|
91
|
+
{
|
92
|
+
'beginning' => '2013/12/01',
|
93
|
+
'end' => '2013/12/07',
|
94
|
+
'postings' =>
|
95
|
+
[
|
96
|
+
{
|
97
|
+
'account' => 'Expenses:Restaurants',
|
98
|
+
'amount' => '53.2',
|
99
|
+
'total' => '53.2',
|
100
|
+
'commodity' => 'EUR'
|
101
|
+
}
|
102
|
+
]
|
103
|
+
}
|
104
|
+
]
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'shows beginning, end and postings with account, amount and total' do
|
108
|
+
get '/register', query: '-W ^Expenses'
|
109
|
+
JSON.parse(last_response.body).should deep_eq valid_response
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'monthly report' do
|
114
|
+
let(:valid_response) do
|
115
|
+
[
|
116
|
+
{
|
117
|
+
'beginning' => '2013/12/01',
|
118
|
+
'end' => '2013/12/31',
|
119
|
+
'postings' =>
|
120
|
+
[
|
121
|
+
{
|
122
|
+
'account' => 'Expenses:Restaurants',
|
123
|
+
'amount' => '53.2',
|
124
|
+
'total' => '53.2',
|
125
|
+
'commodity' => 'EUR'
|
126
|
+
}
|
127
|
+
]
|
128
|
+
}
|
129
|
+
]
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'shows beginning, end and postings with account, amount and total' do
|
133
|
+
get '/register', query: '-M ^Expenses'
|
134
|
+
JSON.parse(last_response.body).should deep_eq valid_response
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'quarterly report' do
|
139
|
+
let(:valid_response) do
|
140
|
+
[
|
141
|
+
{
|
142
|
+
'beginning' => '2013/10/01',
|
143
|
+
'end' => '2013/12/31',
|
144
|
+
'postings' =>
|
145
|
+
[
|
146
|
+
{
|
147
|
+
'account' => 'Expenses:Restaurants',
|
148
|
+
'amount' => '53.2',
|
149
|
+
'total' => '53.2',
|
150
|
+
'commodity' => 'EUR'
|
151
|
+
}
|
152
|
+
]
|
153
|
+
}
|
154
|
+
]
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'shows beginning, end and postings with account, amount and total' do
|
158
|
+
get '/register', query: '--quarterly ^Expenses'
|
159
|
+
JSON.parse(last_response.body).should deep_eq valid_response
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'yearly report' do
|
164
|
+
let(:valid_response) do
|
165
|
+
[
|
166
|
+
{
|
167
|
+
'beginning' => '2013/01/01',
|
168
|
+
'end' => '2013/12/31',
|
169
|
+
'postings' =>
|
170
|
+
[
|
171
|
+
{
|
172
|
+
'account' => 'Expenses:Restaurants',
|
173
|
+
'amount' => '53.2',
|
174
|
+
'total' => '53.2',
|
175
|
+
'commodity' => 'EUR'
|
176
|
+
}
|
177
|
+
]
|
178
|
+
}
|
179
|
+
]
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'shows beginning, end and postings with account, amount and total' do
|
183
|
+
get '/register', query: '-Y ^Expenses'
|
184
|
+
JSON.parse(last_response.body).should deep_eq valid_response
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'report by payee' do
|
189
|
+
let(:valid_response) do
|
190
|
+
[
|
191
|
+
{
|
192
|
+
'payee' => 'NaveenaPath',
|
193
|
+
'postings' =>
|
194
|
+
[
|
195
|
+
{
|
196
|
+
'account' => 'Expenses:Restaurants',
|
197
|
+
'amount' => '9',
|
198
|
+
'total' => '9',
|
199
|
+
'commodity' => 'EUR'
|
200
|
+
}
|
201
|
+
]
|
202
|
+
}, {
|
203
|
+
'payee' => 'Shikgoo',
|
204
|
+
'postings' =>
|
205
|
+
[
|
206
|
+
{
|
207
|
+
'account' => 'Expenses:Restaurants',
|
208
|
+
'amount' => '12',
|
209
|
+
'total' => '12',
|
210
|
+
'commodity' => 'EUR'
|
211
|
+
}
|
212
|
+
]
|
213
|
+
}, {
|
214
|
+
'payee' => 'Trattoria La Vialla',
|
215
|
+
'postings' =>
|
216
|
+
[
|
217
|
+
{
|
218
|
+
'account' => 'Expenses:Restaurants',
|
219
|
+
'amount' => '32.2',
|
220
|
+
'total' => '32.2',
|
221
|
+
'commodity' => 'EUR'
|
222
|
+
}
|
223
|
+
]
|
224
|
+
}
|
225
|
+
]
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'shows beginning, end and postings with account, amount and total' do
|
229
|
+
get '/register', query: '--by-payee ^Expenses'
|
230
|
+
JSON.parse(last_response.body).should deep_eq valid_response
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|