rock_books 0.6.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -3
- data/RELEASE_NOTES.md +49 -10
- data/assets/fonts/JetBrainsMono-Medium.ttf +0 -0
- data/lib/rock_books/cmd_line/command_line_interface.rb +6 -6
- data/lib/rock_books/cmd_line/main.rb +1 -9
- data/lib/rock_books/documents/book_set.rb +1 -2
- data/lib/rock_books/documents/chart_of_accounts.rb +29 -12
- data/lib/rock_books/documents/journal.rb +3 -8
- data/lib/rock_books/documents/journal_entry.rb +7 -2
- data/lib/rock_books/documents/journal_entry_builder.rb +4 -0
- data/lib/rock_books/helpers/book_set_loader.rb +3 -3
- data/lib/rock_books/reports/balance_sheet.rb +9 -43
- data/lib/rock_books/reports/book_set_reporter.rb +115 -98
- data/lib/rock_books/reports/data/bs_is_data.rb +61 -0
- data/lib/rock_books/reports/data/bs_is_section_data.rb +30 -0
- data/lib/rock_books/reports/data/journal_data.rb +38 -0
- data/lib/rock_books/reports/data/multidoc_txn_by_account_data.rb +40 -0
- data/lib/rock_books/reports/data/multidoc_txn_report_data.rb +39 -0
- data/lib/rock_books/reports/data/receipts_report_data.rb +47 -0
- data/lib/rock_books/reports/data/tx_one_account_data.rb +37 -0
- data/lib/rock_books/reports/helpers/erb_helper.rb +21 -0
- data/lib/rock_books/reports/helpers/receipts_hyperlink_converter.rb +47 -0
- data/lib/rock_books/reports/helpers/text_report_helper.rb +144 -0
- data/lib/rock_books/reports/income_statement.rb +9 -47
- data/lib/rock_books/reports/index_html_page.rb +27 -0
- data/lib/rock_books/reports/journal_report.rb +82 -0
- data/lib/rock_books/reports/multidoc_txn_by_account_report.rb +32 -0
- data/lib/rock_books/reports/multidoc_txn_report.rb +25 -0
- data/lib/rock_books/reports/receipts_report.rb +6 -55
- data/lib/rock_books/reports/templates/html/index.html.erb +158 -0
- data/lib/rock_books/reports/templates/html/report_page.html.erb +13 -0
- data/lib/rock_books/reports/templates/text/_receipt_section.txt.erb +17 -0
- data/lib/rock_books/reports/templates/text/_totals.txt.erb +8 -0
- data/lib/rock_books/reports/templates/text/balance_sheet.txt.erb +23 -0
- data/lib/rock_books/reports/templates/text/income_statement.txt.erb +23 -0
- data/lib/rock_books/reports/templates/text/journal.txt.erb +23 -0
- data/lib/rock_books/reports/templates/text/multidoc_txn_by_account_report.txt.erb +31 -0
- data/lib/rock_books/reports/templates/text/multidoc_txn_report.txt.erb +25 -0
- data/lib/rock_books/reports/templates/text/receipts_report.txt.erb +16 -0
- data/lib/rock_books/reports/templates/text/tx_one_account.txt.erb +21 -0
- data/lib/rock_books/reports/tx_one_account.rb +10 -45
- data/lib/rock_books/types/account.rb +13 -1
- data/lib/rock_books/types/account_type.rb +18 -7
- data/lib/rock_books/version.rb +2 -1
- data/rock_books.gemspec +1 -0
- metadata +43 -10
- data/lib/rock_books/helpers/html_helper.rb +0 -29
- data/lib/rock_books/reports/index.html.erb +0 -156
- data/lib/rock_books/reports/multidoc_transaction_report.rb +0 -66
- data/lib/rock_books/reports/receipts.html.erb +0 -54
- data/lib/rock_books/reports/reporter.rb +0 -118
- data/lib/rock_books/reports/transaction_report.rb +0 -105
- data/lib/rock_books/reports/tx_by_account.rb +0 -82
@@ -1,66 +0,0 @@
|
|
1
|
-
require_relative '../documents/journal'
|
2
|
-
require_relative 'reporter'
|
3
|
-
require_relative 'report_context'
|
4
|
-
|
5
|
-
module RockBooks
|
6
|
-
|
7
|
-
class MultidocTransactionReport
|
8
|
-
|
9
|
-
include Reporter
|
10
|
-
|
11
|
-
attr_accessor :context
|
12
|
-
|
13
|
-
SORT_BY_VALID_OPTIONS = %i(date_and_account amount)
|
14
|
-
|
15
|
-
def initialize(report_context)
|
16
|
-
@context = report_context
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
def generate_header(sort_by)
|
21
|
-
lines = [banner_line]
|
22
|
-
lines << center(context.entity || 'Unspecified Entity')
|
23
|
-
lines << center('Multi Document Transaction Report')
|
24
|
-
lines << center('Sorted by Amount Descending') if sort_by == :amount
|
25
|
-
lines << ''
|
26
|
-
lines << center('Source Documents:')
|
27
|
-
lines << ''
|
28
|
-
context.journals.each do |document|
|
29
|
-
short_name = SHORT_NAME_FORMAT_STRING % document.short_name
|
30
|
-
lines << center("#{short_name} -- #{document.title}")
|
31
|
-
end
|
32
|
-
lines << banner_line
|
33
|
-
lines << ''
|
34
|
-
lines << ' Date Document Amount Account'
|
35
|
-
lines << ' ---- -------- ------ -------'
|
36
|
-
lines.join("\n") << "\n\n"
|
37
|
-
end
|
38
|
-
|
39
|
-
|
40
|
-
def generate_report(filter = nil, sort_by = :date_and_account)
|
41
|
-
unless SORT_BY_VALID_OPTIONS.include?(sort_by)
|
42
|
-
raise Error.new("sort_by option '#{sort_by}' not in valid choices of #{SORT_BY_VALID_OPTIONS}.")
|
43
|
-
end
|
44
|
-
|
45
|
-
entries = Journal.entries_in_documents(context.journals, filter)
|
46
|
-
|
47
|
-
if sort_by == :amount
|
48
|
-
JournalEntry.sort_entries_by_amount_descending!(entries)
|
49
|
-
end
|
50
|
-
|
51
|
-
sio = StringIO.new
|
52
|
-
sio << generate_header(sort_by)
|
53
|
-
entries.each { |entry| sio << format_multidoc_entry(entry) << "\n" }
|
54
|
-
|
55
|
-
totals = AcctAmount.aggregate_amounts_by_account(JournalEntry.entries_acct_amounts(entries))
|
56
|
-
sio << generate_and_format_totals('Totals', totals)
|
57
|
-
|
58
|
-
sio << "\n"
|
59
|
-
sio.string
|
60
|
-
end
|
61
|
-
|
62
|
-
|
63
|
-
alias_method :to_s, :generate_report
|
64
|
-
alias_method :call, :generate_report
|
65
|
-
end
|
66
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<body>
|
4
|
-
|
5
|
-
<h1><%= chart_of_accounts.entity %> -- Receipts</h1>
|
6
|
-
<p>Reports Generated at <%= DateTime.now.strftime('%Y-%m-%d_%H-%M-%S') %> by RockBooks version <%=RockBooks::VERSION %></p>
|
7
|
-
<br />
|
8
|
-
|
9
|
-
<% receipts.each %>
|
10
|
-
<h2>Financial Statements</h2>
|
11
|
-
<ul>
|
12
|
-
<li><a href='balance_sheet.html'>Balance Sheet</a></li>
|
13
|
-
<li><a href='income_statement.html'>Income Statement</a></li>
|
14
|
-
</ul>
|
15
|
-
|
16
|
-
<h2>All Transactions</h2>
|
17
|
-
<ul>
|
18
|
-
<li><a href="all_txns_by_acct.html">By Account</a></li>
|
19
|
-
<li><a href="all_txns_by_amount.html">By Amount</a></li>
|
20
|
-
<li><a href="all_txns_by_date.html">By Date</a></li>
|
21
|
-
</ul>
|
22
|
-
|
23
|
-
<h2>Journals</h2>
|
24
|
-
<ul>
|
25
|
-
<% journals.each do |journal|
|
26
|
-
filespec = journal.short_name + '.html'
|
27
|
-
caption = "#{journal.title} -- #{journal.short_name} -- #{journal.account_code}"
|
28
|
-
%>
|
29
|
-
<li><a href="<%= filespec %>"><%= caption %></a></li>
|
30
|
-
<% end %>
|
31
|
-
</ul>
|
32
|
-
|
33
|
-
<h2>Individual Accounts</h2>
|
34
|
-
<ul>
|
35
|
-
|
36
|
-
<%
|
37
|
-
chart_of_accounts.accounts.each do |account|
|
38
|
-
filespec = File.join('single-account', "acct_#{account.code}.html")
|
39
|
-
caption = "#{account.name} (#{account.code})"
|
40
|
-
%>
|
41
|
-
<li><a href="<%= filespec %>"><%= caption %></a></li>
|
42
|
-
<% end %>
|
43
|
-
</ul>
|
44
|
-
|
45
|
-
|
46
|
-
<h2>Receipts</h2>
|
47
|
-
<ul>
|
48
|
-
<% if run_options.do_receipts %>
|
49
|
-
<li><a href="receipts.html">Missing and Existing Receipts</a></li>
|
50
|
-
<% end %>
|
51
|
-
</ul>
|
52
|
-
|
53
|
-
</body>
|
54
|
-
</html>
|
@@ -1,118 +0,0 @@
|
|
1
|
-
require_relative '../documents/journal_entry'
|
2
|
-
|
3
|
-
module RockBooks
|
4
|
-
module Reporter
|
5
|
-
|
6
|
-
module_function
|
7
|
-
|
8
|
-
SHORT_NAME_MAX_LENGTH = 16
|
9
|
-
|
10
|
-
SHORT_NAME_FORMAT_STRING = "%#{SHORT_NAME_MAX_LENGTH}.#{SHORT_NAME_MAX_LENGTH}s"
|
11
|
-
|
12
|
-
|
13
|
-
def page_width
|
14
|
-
context.page_width || 80
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
def format_account_code(code)
|
19
|
-
"%*.*s" % [max_account_code_length, max_account_code_length, code]
|
20
|
-
end
|
21
|
-
|
22
|
-
|
23
|
-
def account_code_name_type_string(account)
|
24
|
-
"#{account.code} -- #{account.name} (#{account.type.to_s.capitalize})"
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
def format_amount(amount)
|
29
|
-
"%9.2f" % amount
|
30
|
-
end
|
31
|
-
|
32
|
-
|
33
|
-
# e.g. " 117.70 tr.mileage Travel - Mileage Allowance"
|
34
|
-
def format_acct_amount(acct_amount)
|
35
|
-
"%s %s %s" % [
|
36
|
-
format_amount(acct_amount.amount),
|
37
|
-
format_account_code(acct_amount.code),
|
38
|
-
context.chart_of_accounts.name_for_code(acct_amount.code)
|
39
|
-
]
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
def banner_line
|
44
|
-
@banner_line ||= '-' * page_width
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
def center(string)
|
49
|
-
indent = (page_width - string.length) / 2
|
50
|
-
indent = 0 if indent < 0
|
51
|
-
(' ' * indent) + string
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
def max_account_code_length
|
56
|
-
@max_account_code_length ||= context.chart_of_accounts.max_account_code_length
|
57
|
-
end
|
58
|
-
|
59
|
-
|
60
|
-
def generate_and_format_totals(section_caption, totals)
|
61
|
-
output = section_caption
|
62
|
-
output << "\n#{'-' * section_caption.length}\n\n"
|
63
|
-
format_string = "%12.2f %-#{context.chart_of_accounts.max_account_code_length}s %s\n"
|
64
|
-
totals.keys.sort.each do |account_code|
|
65
|
-
account_name = context.chart_of_accounts.name_for_code(account_code)
|
66
|
-
account_total = totals[account_code]
|
67
|
-
output << format_string % [account_total, account_code, account_name]
|
68
|
-
end
|
69
|
-
|
70
|
-
output << "------------\n"
|
71
|
-
output << "%12.2f\n" % totals.values.sum.round(2)
|
72
|
-
output
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
def generate_account_type_section(section_caption, totals, section_type, need_to_reverse_sign)
|
77
|
-
account_codes_this_section = context.chart_of_accounts.account_codes_of_type(section_type)
|
78
|
-
|
79
|
-
totals_this_section = totals.select do |account_code, _amount|
|
80
|
-
account_codes_this_section.include?(account_code)
|
81
|
-
end
|
82
|
-
|
83
|
-
if need_to_reverse_sign
|
84
|
-
totals_this_section.each { |code, amount| totals_this_section[code] = -amount }
|
85
|
-
end
|
86
|
-
|
87
|
-
section_total_amount = totals_this_section.map { |aa| aa.last }.sum
|
88
|
-
|
89
|
-
output = generate_and_format_totals(section_caption, totals_this_section)
|
90
|
-
[ output, section_total_amount ]
|
91
|
-
end
|
92
|
-
|
93
|
-
|
94
|
-
def format_multidoc_entry(entry)
|
95
|
-
acct_amounts = entry.acct_amounts
|
96
|
-
|
97
|
-
# "2017-10-29 hsbc_visa":
|
98
|
-
output = entry.date.to_s << ' ' << (SHORT_NAME_FORMAT_STRING % entry.doc_short_name) << ' '
|
99
|
-
|
100
|
-
indent = ' ' * output.length
|
101
|
-
|
102
|
-
output << format_acct_amount(acct_amounts.first) << "\n"
|
103
|
-
|
104
|
-
acct_amounts[1..-1].each do |acct_amount|
|
105
|
-
output << indent << format_acct_amount(acct_amount) << "\n"
|
106
|
-
end
|
107
|
-
|
108
|
-
if entry.description && entry.description.length > 0
|
109
|
-
output << entry.description
|
110
|
-
end
|
111
|
-
|
112
|
-
output
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
|
@@ -1,105 +0,0 @@
|
|
1
|
-
require_relative 'reporter'
|
2
|
-
require_relative 'report_context'
|
3
|
-
|
4
|
-
module RockBooks
|
5
|
-
|
6
|
-
class TransactionReport
|
7
|
-
|
8
|
-
include Reporter
|
9
|
-
|
10
|
-
attr_accessor :journal, :context
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(journal, report_context)
|
14
|
-
@journal = journal
|
15
|
-
@context = report_context
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
def generate_header
|
20
|
-
|
21
|
-
code = journal.account_code
|
22
|
-
name = journal.chart_of_accounts.name_for_code(code)
|
23
|
-
title = "Transactions for Account ##{code} -- #{name}"
|
24
|
-
|
25
|
-
lines = [banner_line]
|
26
|
-
lines << center(context.entity || 'Unspecified Entity')
|
27
|
-
lines << center(journal.title) if journal.title && journal.title.length > 0
|
28
|
-
lines << center(title)
|
29
|
-
lines << banner_line
|
30
|
-
lines << ''
|
31
|
-
lines << ''
|
32
|
-
lines << ''
|
33
|
-
lines.join("\n")
|
34
|
-
end
|
35
|
-
|
36
|
-
|
37
|
-
def format_entry_first_acct_amount(entry)
|
38
|
-
entry.date.to_s \
|
39
|
-
<< ' ' \
|
40
|
-
<< format_acct_amount(entry.acct_amounts.last) \
|
41
|
-
<< "\n"
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
# Formats an entry like this, with entry description added on additional line(s) if it exists:
|
46
|
-
# 2018-05-21 $120.00 701 Office Supplies
|
47
|
-
def format_entry_no_split(entry)
|
48
|
-
output = format_entry_first_acct_amount(entry)
|
49
|
-
|
50
|
-
if entry.description && entry.description.length > 0
|
51
|
-
output << entry.description
|
52
|
-
end
|
53
|
-
output
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
# Formats an entry like this, with entry description added on additional line(s) if it exists::
|
58
|
-
# 2018-05-21 $120.00 95.00 701 Office Supplies
|
59
|
-
# 25.00 751 Gift to Customer
|
60
|
-
def format_entry_with_split(entry)
|
61
|
-
output = format_entry_first_acct_amount(entry)
|
62
|
-
indent = ' ' * 12
|
63
|
-
|
64
|
-
entry.acct_amounts[1..-1].each do |acct_amount|
|
65
|
-
output << indent << format_acct_amount(acct_amount) << "\n"
|
66
|
-
end
|
67
|
-
|
68
|
-
if entry.description && entry.description.length > 0
|
69
|
-
output << entry.description
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
def format_entry(entry)
|
75
|
-
if entry.acct_amounts.size > 2
|
76
|
-
format_entry_with_split(entry)
|
77
|
-
else
|
78
|
-
format_entry_no_split(entry)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
def generate_report(filter = nil)
|
84
|
-
sio = StringIO.new
|
85
|
-
sio << generate_header
|
86
|
-
|
87
|
-
entries = journal.entries
|
88
|
-
if filter
|
89
|
-
entries = entries.select { |entry| filter.(entry) }
|
90
|
-
end
|
91
|
-
|
92
|
-
entries.each do |entry|
|
93
|
-
sio << format_entry(entry) << "\n"
|
94
|
-
end
|
95
|
-
totals = AcctAmount.aggregate_amounts_by_account(JournalEntry.entries_acct_amounts(entries))
|
96
|
-
sio << generate_and_format_totals('Totals', totals)
|
97
|
-
sio.string
|
98
|
-
end
|
99
|
-
|
100
|
-
|
101
|
-
alias_method :to_s, :generate_report
|
102
|
-
alias_method :call, :generate_report
|
103
|
-
end
|
104
|
-
|
105
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
require_relative '../documents/chart_of_accounts'
|
2
|
-
require_relative '../documents/journal'
|
3
|
-
require_relative 'reporter'
|
4
|
-
require_relative 'report_context'
|
5
|
-
|
6
|
-
module RockBooks
|
7
|
-
|
8
|
-
class TxByAccount
|
9
|
-
|
10
|
-
include Reporter
|
11
|
-
|
12
|
-
attr_accessor :context
|
13
|
-
|
14
|
-
|
15
|
-
def initialize(report_context)
|
16
|
-
@context = report_context
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
def generate_header
|
21
|
-
lines = [banner_line]
|
22
|
-
lines << center(context.entity || 'Unspecified Entity')
|
23
|
-
lines << center("Transactions by Account")
|
24
|
-
lines << banner_line
|
25
|
-
lines << ''
|
26
|
-
lines << ''
|
27
|
-
lines << ''
|
28
|
-
lines.join("\n")
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
def account_header(account, account_total)
|
33
|
-
total_string = "%.2f" % account_total
|
34
|
-
title = "Total: #{total_string} -- #{account_code_name_type_string(account)}"
|
35
|
-
|
36
|
-
<<~HEREDOC
|
37
|
-
#{banner_line}
|
38
|
-
#{center(title)}
|
39
|
-
#{banner_line}
|
40
|
-
|
41
|
-
HEREDOC
|
42
|
-
end
|
43
|
-
|
44
|
-
|
45
|
-
def account_total_line(account_code, account_total)
|
46
|
-
account_name = context.chart_of_accounts.name_for_code(account_code)
|
47
|
-
"%.2f Total for account: %s - %s" % [account_total, account_code, account_name]
|
48
|
-
end
|
49
|
-
|
50
|
-
|
51
|
-
def generate_report
|
52
|
-
output = generate_header
|
53
|
-
|
54
|
-
all_entries = Journal.entries_in_documents(context.journals)
|
55
|
-
|
56
|
-
context.chart_of_accounts.accounts.each do |account|
|
57
|
-
code = account.code
|
58
|
-
account_entries = JournalEntry.entries_containing_account_code(all_entries, code)
|
59
|
-
account_total = JournalEntry.total_for_code(account_entries, code)
|
60
|
-
output << account_header(account, account_total)
|
61
|
-
|
62
|
-
account_entries.each do |entry|
|
63
|
-
output << format_multidoc_entry(entry) << "\n"
|
64
|
-
output << "\n" if entry.description && entry.description.length > 0
|
65
|
-
end
|
66
|
-
output << account_total_line(code, account_total) << "\n"
|
67
|
-
output << "\n\n\n"
|
68
|
-
end
|
69
|
-
|
70
|
-
totals = AcctAmount.aggregate_amounts_by_account(JournalEntry.entries_acct_amounts(all_entries))
|
71
|
-
output << generate_and_format_totals('Totals', totals)
|
72
|
-
|
73
|
-
output
|
74
|
-
end
|
75
|
-
|
76
|
-
alias_method :to_s, :generate_report
|
77
|
-
alias_method :call, :generate_report
|
78
|
-
|
79
|
-
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|