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
@@ -1,23 +1,29 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module LedgerRest
|
2
3
|
class Ledger
|
3
4
|
class Balance
|
4
|
-
|
5
5
|
FORMAT = [
|
6
6
|
'{',
|
7
|
-
'
|
8
|
-
'
|
9
|
-
'
|
7
|
+
'"total":%(quoted(display_total)),',
|
8
|
+
'"name":%(quoted(partial_account)),',
|
9
|
+
'"depth":%(depth), "fullname": %(quoted(account))',
|
10
10
|
'},%/',
|
11
|
-
'{
|
12
|
-
].join
|
11
|
+
'{"total":%(quoted(display_total))}'
|
12
|
+
].join
|
13
13
|
|
14
14
|
class << self
|
15
|
+
def get(query = nil, params = {})
|
16
|
+
data = JSON.parse(json(query, params), symbolize_names: true)
|
17
|
+
|
18
|
+
data[:accounts] = expand_accounts(data[:accounts])
|
19
|
+
unless query =~ /--flat/
|
20
|
+
data[:accounts] = wrap_accounts(data[:accounts])
|
21
|
+
end
|
15
22
|
|
16
|
-
|
17
|
-
JSON.parse(json(query, params), :symbolize_names => true)
|
23
|
+
data
|
18
24
|
end
|
19
25
|
|
20
|
-
def json
|
26
|
+
def json(query = nil, params = {})
|
21
27
|
params = { '--format' => FORMAT }.merge(params)
|
22
28
|
result = Ledger.exec("bal #{query}", params)
|
23
29
|
|
@@ -25,19 +31,46 @@ module LedgerRest
|
|
25
31
|
if result.end_with?(',')
|
26
32
|
result = result[0..-2]
|
27
33
|
else
|
28
|
-
match_total = result.match(
|
34
|
+
match_total = result.match(/,{"total":("[0-9\.A-Za-z ]+")}\z/)
|
29
35
|
if match_total
|
30
36
|
total = match_total[1]
|
31
|
-
result = result[0,match_total.offset(0)[0]]
|
37
|
+
result = result[0, match_total.offset(0)[0]]
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
35
|
-
json_str =
|
36
|
-
json_str << "
|
37
|
-
json_str << "
|
38
|
-
json_str <<
|
41
|
+
json_str = '{'
|
42
|
+
json_str << "\"accounts\":[#{result}]"
|
43
|
+
json_str << ",\"total\":#{total}" if total
|
44
|
+
json_str << '}'
|
45
|
+
end
|
46
|
+
|
47
|
+
def expand_accounts(accounts)
|
48
|
+
accounts.inject([]) do |acc, elem|
|
49
|
+
fullname = elem[:fullname].gsub(/:?#{elem[:name]}/, '')
|
50
|
+
acc + elem[:name].split(':').map do |name|
|
51
|
+
fullname << "#{':' unless fullname.empty?}#{name}"
|
52
|
+
parent = elem.dup
|
53
|
+
parent[:fullname], parent[:name], parent[:depth] =
|
54
|
+
fullname.dup, name.dup, fullname.count(':')+1
|
55
|
+
parent
|
56
|
+
end
|
57
|
+
end
|
39
58
|
end
|
40
59
|
|
60
|
+
def wrap_accounts(accounts)
|
61
|
+
stack = []
|
62
|
+
accounts.inject([]) do |acc, elem|
|
63
|
+
stack.pop while stack.last && stack.last[:depth] >= elem[:depth]
|
64
|
+
if stack.empty?
|
65
|
+
stack << elem
|
66
|
+
acc << elem
|
67
|
+
else
|
68
|
+
(stack.last[:accounts] ||= []) << elem
|
69
|
+
stack << elem
|
70
|
+
end
|
71
|
+
acc
|
72
|
+
end
|
73
|
+
end
|
41
74
|
end
|
42
75
|
end
|
43
76
|
end
|
@@ -1,33 +1,52 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module LedgerRest
|
2
3
|
class Ledger
|
3
4
|
class Budget
|
4
5
|
FORMAT = [
|
5
|
-
'
|
6
|
-
'
|
7
|
-
'
|
8
|
-
'
|
9
|
-
'
|
10
|
-
'
|
11
|
-
'
|
12
|
-
'
|
13
|
-
|
6
|
+
'%(scrub(get_at(display_total, 0)))\n',
|
7
|
+
'%(-scrub(get_at(display_total, 1)))\n',
|
8
|
+
'%(scrub(get_at(display_total, 1) + get_at(display_total, 0)))\n',
|
9
|
+
'%(get_at(display_total, 1) ? (100% * scrub(get_at(display_total, 0))) / -scrub(get_at(display_total, 1)) : 0)\n',
|
10
|
+
'%(account)\n',
|
11
|
+
'---\n',
|
12
|
+
'%/---\n',
|
13
|
+
'%$1\n',
|
14
|
+
'%$2\n',
|
15
|
+
'%$3\n',
|
16
|
+
'%$4\n',
|
17
|
+
'%/'
|
18
|
+
].join
|
14
19
|
|
15
20
|
class << self
|
16
|
-
|
17
21
|
def get(query = nil, params = {})
|
18
|
-
JSON.parse(json(query, params), :
|
22
|
+
JSON.parse(json(query, params), symbolize_names: true)
|
19
23
|
end
|
20
24
|
|
21
25
|
def json(query = nil, params = {})
|
22
26
|
params = { '--format' => FORMAT }.merge(params)
|
23
27
|
result = Ledger.exec("budget #{query}", params)
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
budget = {}
|
29
|
+
accounts, total = result.split("---\n---\n")
|
30
|
+
budget['accounts'] = accounts.split("---\n").map do |str|
|
31
|
+
val = str.split("\n")
|
32
|
+
{
|
33
|
+
'total' => val[0].empty? ? '0' : val[0],
|
34
|
+
'budget' => val[1].empty? ? '0' : val[1],
|
35
|
+
'difference' => val[2].empty? ? '0' : val[2],
|
36
|
+
'percentage' => val[3].empty? ? '0' : val[3],
|
37
|
+
'account' => val[4].empty? ? '0' : val[4]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
if total
|
41
|
+
val = total.split("\n")
|
42
|
+
budget['total'] = val[0].empty? ? '0' : val[0]
|
43
|
+
budget['budget'] = val[1].empty? ? '0' : val[1]
|
44
|
+
budget['difference'] = val[2].empty? ? '0' : val[2]
|
45
|
+
budget['percentage'] = val[3].empty? ? '0' : val[3]
|
46
|
+
end
|
47
|
+
budget.to_json
|
27
48
|
end
|
28
|
-
|
29
49
|
end
|
30
|
-
|
31
50
|
end
|
32
51
|
end
|
33
52
|
end
|
@@ -1,13 +1,11 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module LedgerRest
|
2
3
|
class Ledger
|
3
|
-
|
4
4
|
# Ledger offers a simple command to create a new entry based on
|
5
5
|
# previous entries in your ledger files. This class abstracts
|
6
6
|
# mentioned functionality for easy integration into ledger-rest.
|
7
7
|
class Entry
|
8
|
-
|
9
8
|
class << self
|
10
|
-
|
11
9
|
# Return a new transaction object based on previous transactions.
|
12
10
|
def get(desc, options = {})
|
13
11
|
result = Ledger.exec("entry #{desc}", options)
|
@@ -20,7 +18,6 @@ module LedgerRest
|
|
20
18
|
transaction.append_to(Ledger.append_file)
|
21
19
|
transaction
|
22
20
|
end
|
23
|
-
|
24
21
|
end
|
25
22
|
end
|
26
23
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
module LedgerRest
|
3
3
|
class Ledger
|
4
|
-
|
5
4
|
# A very simple parser for single legder format transactions.
|
6
5
|
# There has to be a better way ... This does not implement the
|
7
6
|
# whole ledger format. Tragically ... someone told me it´s the
|
@@ -11,14 +10,11 @@ module LedgerRest
|
|
11
10
|
#
|
12
11
|
# This works for `ledger entry` with my transactions ...
|
13
12
|
class Parser
|
14
|
-
|
15
13
|
class << self
|
16
|
-
|
17
14
|
def parse(str)
|
18
15
|
parser = Parser.new
|
19
16
|
parser.parse(str)
|
20
17
|
end
|
21
|
-
|
22
18
|
end
|
23
19
|
|
24
20
|
def initialize
|
@@ -30,20 +26,20 @@ module LedgerRest
|
|
30
26
|
@str = str
|
31
27
|
@transaction[:postings] = []
|
32
28
|
|
33
|
-
@transaction[:date],str = parse_date(str)
|
29
|
+
@transaction[:date], str = parse_date(str)
|
34
30
|
|
35
31
|
effective_date, str = parse_effective_date(str)
|
36
32
|
@transaction[:effective_date] = effective_date if effective_date
|
37
33
|
|
38
|
-
@transaction[:cleared],str = parse_cleared(str)
|
39
|
-
@transaction[:pending],str = parse_pending(str)
|
34
|
+
@transaction[:cleared], str = parse_cleared(str)
|
35
|
+
@transaction[:pending], str = parse_pending(str)
|
40
36
|
|
41
37
|
code, str = parse_code(str)
|
42
38
|
@transaction[:code] = code if code
|
43
39
|
|
44
|
-
@transaction[:payee],str = parse_payee(str)
|
40
|
+
@transaction[:payee], str = parse_payee(str)
|
45
41
|
|
46
|
-
comments,str = parse_comments(str)
|
42
|
+
comments, str = parse_comments(str)
|
47
43
|
@transaction[:comments] = comments if comments
|
48
44
|
|
49
45
|
str.split("\n").each do |line|
|
@@ -58,60 +54,60 @@ module LedgerRest
|
|
58
54
|
end
|
59
55
|
|
60
56
|
def parse_date(str)
|
61
|
-
if match = str.match(
|
62
|
-
[
|
57
|
+
if match = str.match(%r{\A(\d{4}/\d{1,2}/\d{1,2})(.*)}m)
|
58
|
+
[match[1], match[2]]
|
63
59
|
else
|
64
|
-
|
60
|
+
fail 'Date was expected.'
|
65
61
|
end
|
66
62
|
end
|
67
63
|
|
68
64
|
def parse_effective_date(str)
|
69
|
-
if match = str.match(
|
70
|
-
[
|
65
|
+
if match = str.match(%r{\A=(\d{4}/\d{1,2}/\d{1,2})(.*)}m)
|
66
|
+
[match[1], match[2]]
|
71
67
|
else
|
72
|
-
[
|
68
|
+
[nil, str]
|
73
69
|
end
|
74
70
|
end
|
75
71
|
|
76
72
|
def parse_pending(str)
|
77
73
|
if match = str.match(/\A ! (.*)/m)
|
78
|
-
[
|
74
|
+
[true, match[1]]
|
79
75
|
else
|
80
|
-
[
|
76
|
+
[false, str]
|
81
77
|
end
|
82
78
|
end
|
83
79
|
|
84
80
|
def parse_cleared(str)
|
85
81
|
if match = str.match(/\A \* (.*)/m)
|
86
|
-
[
|
82
|
+
[true, match[1]]
|
87
83
|
else
|
88
|
-
[
|
84
|
+
[false, str]
|
89
85
|
end
|
90
86
|
end
|
91
87
|
|
92
88
|
def parse_code(str)
|
93
89
|
if match = str.match(/\A *?\(([^\(\)]+)\) (.*)/m)
|
94
|
-
[
|
90
|
+
[match[1], match[2]]
|
95
91
|
else
|
96
|
-
[
|
92
|
+
[nil, str]
|
97
93
|
end
|
98
94
|
end
|
99
95
|
|
100
96
|
def parse_payee(str)
|
101
97
|
if match = str.match(/\A *?([^ ][^\n]+)\n(.*)/m)
|
102
|
-
[
|
98
|
+
[match[1], match[2]]
|
103
99
|
else
|
104
|
-
|
100
|
+
fail 'No payee given.'
|
105
101
|
end
|
106
102
|
end
|
107
103
|
|
108
104
|
def parse_comments(str)
|
109
|
-
comments =
|
105
|
+
comments = ''
|
110
106
|
while str && match = str.match(/\A +;(.*?)(\n|$)(.*)/m)
|
111
107
|
comments << match[1].strip << "\n"
|
112
108
|
str = match[3]
|
113
109
|
end
|
114
|
-
[
|
110
|
+
[comments.empty? ? nil : comments, str]
|
115
111
|
end
|
116
112
|
|
117
113
|
# parses a ledger posting line
|
@@ -124,9 +120,9 @@ module LedgerRest
|
|
124
120
|
posting[:balanced] = balanced if balanced
|
125
121
|
|
126
122
|
amount, posting_cost, per_unit_cost, str = parse_amount(str)
|
127
|
-
posting[:amount] = amount if amount
|
128
|
-
posting[:posting_cost] = posting_cost if posting_cost
|
129
|
-
posting[:per_unit_cost] = per_unit_cost if per_unit_cost
|
123
|
+
posting[:amount], posting[:commodity] = parse_amount_parts(amount) if amount
|
124
|
+
posting[:posting_cost], posting[:posting_cost_commodity] = parse_amount_parts(posting_cost) if posting_cost
|
125
|
+
posting[:per_unit_cost], posting[:per_unit_commodity] = parse_amount_parts(per_unit_cost) if per_unit_cost
|
130
126
|
|
131
127
|
comment, actual_date, effective_date = parse_posting_comment(str)
|
132
128
|
posting[:comment] = comment if comment
|
@@ -137,28 +133,36 @@ module LedgerRest
|
|
137
133
|
end
|
138
134
|
|
139
135
|
def parse_account(str)
|
140
|
-
return [] if str.nil?
|
136
|
+
return [] if str.nil? || str.empty?
|
141
137
|
if match = str.match(/\A +([\w:]+)(\n|$| )(.*)/m)
|
142
|
-
[
|
138
|
+
[match[1], false, false , match[3]]
|
143
139
|
elsif match = str.match(/\A +\(([\w:]+)\)(\n|$| )(.*)/m)
|
144
|
-
[
|
140
|
+
[match[1], true, false , match[3]]
|
145
141
|
elsif match = str.match(/\A +\[([\w:]+)\](\n|$| )(.*)/m)
|
146
|
-
[
|
142
|
+
[match[1], true, true , match[3]]
|
147
143
|
else
|
148
|
-
[
|
144
|
+
[nil, false, false, str]
|
149
145
|
end
|
150
146
|
end
|
151
147
|
|
152
148
|
def parse_amount(str)
|
153
149
|
if match = str.match(/\A(.*?)@@([^;]*?)(;(.*)|\n(.*)|$(.*))/m)
|
154
150
|
amount = match[1].strip
|
155
|
-
[
|
151
|
+
[amount.empty? ? nil : amount, nil, match[2].strip, match[3]]
|
156
152
|
elsif match = str.match(/\A(.*?)@([^;]*)(;(.*)|\n(.*)|$(.*))/m)
|
157
153
|
amount = match[1].strip
|
158
|
-
[
|
154
|
+
[amount.empty? ? nil : amount, match[2].strip, nil, match[3]]
|
159
155
|
elsif match = str.match(/\A([^;]*?)(;(.*)|\n(.*)|$(.*))/m)
|
160
156
|
amount = match[1].strip
|
161
|
-
[
|
157
|
+
[amount.empty? ? nil : amount, nil, nil, match[2]]
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def parse_amount_parts(str)
|
162
|
+
if match = str.match(/^(.*?)(\d+(\.\d+)?)(.*?)$/)
|
163
|
+
[match[2].to_f, match[1].strip + match[4].strip]
|
164
|
+
else
|
165
|
+
[str, nil]
|
162
166
|
end
|
163
167
|
end
|
164
168
|
|
@@ -171,7 +175,7 @@ module LedgerRest
|
|
171
175
|
comment = match[1]
|
172
176
|
end
|
173
177
|
|
174
|
-
[
|
178
|
+
[comment, actual_date, effective_date]
|
175
179
|
end
|
176
180
|
end
|
177
181
|
end
|
@@ -1,38 +1,45 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
1
2
|
module LedgerRest
|
2
3
|
class Ledger
|
3
4
|
class Register
|
4
|
-
|
5
|
-
FORMAT = [
|
6
|
-
"{",
|
7
|
-
' "date": %(quoted(date)),',
|
8
|
-
' "effective_date": %(effective_date ? quoted(effective_date) : "null"),',
|
9
|
-
' "code": %(code ? quoted(code) : "null"),',
|
10
|
-
' "cleared": %(cleared ? "true" : "false"),',
|
11
|
-
' "pending": %(pending ? "true" : "false"),',
|
12
|
-
' "payee": %(quoted(payee)),',
|
13
|
-
' "postings":',
|
14
|
-
' [',
|
15
|
-
' { "account": %(quoted(display_account)), "amount": %(quoted(amount)) },%/',
|
16
|
-
' { "account": %(quoted(display_account)), "amount": %(quoted(amount)) },%/',
|
17
|
-
' ]',
|
18
|
-
'},'
|
19
|
-
].join('\\n')
|
20
|
-
|
21
5
|
class << self
|
6
|
+
def format(query)
|
7
|
+
format = '{'
|
8
|
+
if query =~ /-D|--daily|-W|--weekly|-M|--monthly|--quarterly|-Y|--yearly/
|
9
|
+
format << '"beginning": %(quoted(format_date(date))),'
|
10
|
+
format << '"end": %(quoted(payee)),'
|
11
|
+
elsif query =~ /--by-payee/
|
12
|
+
format << '"payee": %(quoted(payee)),'
|
13
|
+
else
|
14
|
+
format << '"date": %(quoted(format_date(date))),'
|
15
|
+
format << '"effective_date": %(effective_date ? quoted(format_date(effective_date)) : "null"),'
|
16
|
+
format << '"code": %(code ? quoted(code) : "null"),'
|
17
|
+
format << '"cleared": %(cleared ? "true" : "false"),'
|
18
|
+
format << '"pending": %(pending ? "true" : "false"),'
|
19
|
+
format << '"payee": %(quoted(payee)),'
|
20
|
+
end
|
21
|
+
format << '"postings": ['
|
22
|
+
format << '{ "account": %(quoted(display_account)), "amount": %(quoted(quantity(scrub(display_amount)))), "total": %(quoted(quantity(scrub(display_amount)))), "commodity": %(quoted(commodity)) },%/'
|
23
|
+
format << '{ "account": %(quoted(display_account)), "amount": %(quoted(quantity(scrub(display_amount)))), "total": %(quoted(quantity(scrub(display_amount)))), "commodity": %(quoted(commodity)) },%/'
|
24
|
+
format << ']},'
|
25
|
+
format
|
26
|
+
end
|
22
27
|
|
23
28
|
def get(query = nil, params = {})
|
24
|
-
JSON.parse(json(query, params), :
|
29
|
+
JSON.parse(json(query, params), symbolize_names: true)
|
25
30
|
end
|
26
31
|
|
27
32
|
def json(query = nil, params = {})
|
28
|
-
params = {
|
33
|
+
params = {
|
34
|
+
'--format' => format(query),
|
35
|
+
'--date-format' => '%Y/%m/%d'
|
36
|
+
}.merge(params)
|
29
37
|
result = Ledger.exec("reg #{query}", params)
|
30
|
-
result << "
|
31
|
-
result.gsub!
|
32
|
-
|
33
|
-
"
|
38
|
+
result << "]}"
|
39
|
+
result.gsub! '"end": "- ', '"end": "'
|
40
|
+
result.gsub! /\},\n? *?\]/m, "}]"
|
41
|
+
"[#{result}]"
|
34
42
|
end
|
35
|
-
|
36
43
|
end
|
37
44
|
end
|
38
45
|
end
|
@@ -2,14 +2,11 @@
|
|
2
2
|
module LedgerRest
|
3
3
|
class Ledger
|
4
4
|
class Transaction < Hash
|
5
|
-
|
6
5
|
class << self
|
7
|
-
|
8
6
|
# Parse a ledger transaction string into a `Transaction` object.
|
9
7
|
def parse(str)
|
10
8
|
LedgerRest::Ledger::Parser.parse(str)
|
11
9
|
end
|
12
|
-
|
13
10
|
end
|
14
11
|
|
15
12
|
def initialize(params = {})
|
@@ -18,31 +15,39 @@ module LedgerRest
|
|
18
15
|
|
19
16
|
# Return true if the `Transaction#to_ledger` is a valid ledger string.
|
20
17
|
def valid?
|
21
|
-
result = IO.popen("#{
|
22
|
-
f.write
|
18
|
+
result = IO.popen("#{Ledger.bin} -f - stats 2>&1", 'r+') do |f|
|
19
|
+
f.write to_ledger
|
23
20
|
f.close_write
|
24
21
|
f.readlines
|
25
22
|
end
|
26
23
|
|
27
|
-
$?.success?
|
24
|
+
$?.success? && !result.empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
def check
|
28
|
+
IO.popen("#{Ledger.bin} -f - stats 2>&1", 'r+') do |f|
|
29
|
+
f.write to_ledger
|
30
|
+
f.close_write
|
31
|
+
f.readlines
|
32
|
+
end
|
28
33
|
end
|
29
34
|
|
30
35
|
def to_ledger
|
31
|
-
if self[:date].nil?
|
32
|
-
self[:payee].nil? or
|
33
|
-
self[:postings].nil?
|
36
|
+
if self[:date].nil? || self[:payee].nil? || self[:postings].nil?
|
34
37
|
return nil
|
35
38
|
end
|
36
39
|
|
37
|
-
result =
|
40
|
+
result = ''
|
38
41
|
|
39
42
|
result << self[:date]
|
40
|
-
|
43
|
+
if self[:effective_date]
|
44
|
+
result << "=#{self[:effective_date]}"
|
45
|
+
end
|
41
46
|
|
42
47
|
if self[:cleared]
|
43
|
-
result <<
|
48
|
+
result << ' *'
|
44
49
|
elsif self[:pending]
|
45
|
-
result <<
|
50
|
+
result << ' !'
|
46
51
|
end
|
47
52
|
|
48
53
|
result << " (#{self[:code]})" if self[:code]
|
@@ -51,7 +56,7 @@ module LedgerRest
|
|
51
56
|
|
52
57
|
self[:postings].each do |posting|
|
53
58
|
if posting[:comment]
|
54
|
-
result << "
|
59
|
+
result << " ; #{posting[:comment]}\n"
|
55
60
|
next
|
56
61
|
end
|
57
62
|
|
@@ -59,12 +64,12 @@ module LedgerRest
|
|
59
64
|
|
60
65
|
if posting[:virtual]
|
61
66
|
if posting[:balance]
|
62
|
-
result << "
|
67
|
+
result << " [#{posting[:account]}]"
|
63
68
|
else
|
64
|
-
result << "
|
69
|
+
result << " (#{posting[:account]})"
|
65
70
|
end
|
66
71
|
else
|
67
|
-
result << "
|
72
|
+
result << " #{posting[:account]}"
|
68
73
|
end
|
69
74
|
|
70
75
|
if posting[:amount].nil?
|
@@ -72,26 +77,26 @@ module LedgerRest
|
|
72
77
|
next
|
73
78
|
end
|
74
79
|
|
75
|
-
result << " #{posting[:amount]}"
|
80
|
+
result << " #{'%.2f' % posting[:amount]}#{posting[:commodity]}"
|
76
81
|
|
77
|
-
if
|
78
|
-
result << " @@ #{posting[:per_unit_cost]}"
|
79
|
-
elsif
|
80
|
-
result << " @ #{posting[:posting_cost]}"
|
82
|
+
if posting[:per_unit_cost]
|
83
|
+
result << " @@ #{'%.2f' % posting[:per_unit_cost]}#{posting[:per_unit_commodity]}"
|
84
|
+
elsif posting[:posting_cost]
|
85
|
+
result << " @ #{'%.2f' % posting[:posting_cost]}#{posting[:posting_cost_commodity]}"
|
81
86
|
end
|
82
87
|
|
83
|
-
if posting[:actual_date]
|
84
|
-
result <<
|
88
|
+
if posting[:actual_date] || posting[:effective_date]
|
89
|
+
result << ' ; ['
|
85
90
|
result << posting[:actual_date] if posting[:actual_date]
|
86
|
-
|
87
|
-
|
91
|
+
if posting[:effective_date]
|
92
|
+
result << "=#{posting[:effective_date]}"
|
93
|
+
end
|
94
|
+
result << ']'
|
88
95
|
end
|
89
96
|
|
90
97
|
result << "\n"
|
91
98
|
end
|
92
99
|
|
93
|
-
result << "\n"
|
94
|
-
|
95
100
|
result
|
96
101
|
end
|
97
102
|
end
|