rock_books 0.6.1 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -10
  3. data/RELEASE_NOTES.md +57 -10
  4. data/assets/fonts/JetBrainsMono-Medium.ttf +0 -0
  5. data/lib/rock_books/cmd_line/command_line_interface.rb +15 -24
  6. data/lib/rock_books/cmd_line/main.rb +1 -9
  7. data/lib/rock_books/documents/book_set.rb +5 -2
  8. data/lib/rock_books/documents/chart_of_accounts.rb +29 -12
  9. data/lib/rock_books/documents/journal.rb +3 -8
  10. data/lib/rock_books/documents/journal_entry.rb +7 -2
  11. data/lib/rock_books/documents/journal_entry_builder.rb +4 -0
  12. data/lib/rock_books/helpers/book_set_loader.rb +3 -3
  13. data/lib/rock_books/reports/balance_sheet.rb +9 -43
  14. data/lib/rock_books/reports/book_set_reporter.rb +138 -106
  15. data/lib/rock_books/reports/data/bs_is_data.rb +61 -0
  16. data/lib/rock_books/reports/data/bs_is_section_data.rb +30 -0
  17. data/lib/rock_books/reports/data/journal_data.rb +38 -0
  18. data/lib/rock_books/reports/data/multidoc_txn_by_account_data.rb +40 -0
  19. data/lib/rock_books/reports/data/multidoc_txn_report_data.rb +39 -0
  20. data/lib/rock_books/reports/data/receipts_report_data.rb +47 -0
  21. data/lib/rock_books/reports/data/tx_one_account_data.rb +37 -0
  22. data/lib/rock_books/reports/helpers/erb_helper.rb +21 -0
  23. data/lib/rock_books/reports/helpers/receipts_hyperlink_converter.rb +59 -0
  24. data/lib/rock_books/reports/helpers/text_report_helper.rb +144 -0
  25. data/lib/rock_books/reports/income_statement.rb +9 -47
  26. data/lib/rock_books/reports/index_html_page.rb +27 -0
  27. data/lib/rock_books/reports/journal_report.rb +82 -0
  28. data/lib/rock_books/reports/multidoc_txn_by_account_report.rb +32 -0
  29. data/lib/rock_books/reports/multidoc_txn_report.rb +25 -0
  30. data/lib/rock_books/reports/receipts_report.rb +6 -55
  31. data/lib/rock_books/reports/templates/html/index.html.erb +158 -0
  32. data/lib/rock_books/reports/templates/html/report_page.html.erb +25 -0
  33. data/lib/rock_books/reports/templates/text/_receipt_section.txt.erb +17 -0
  34. data/lib/rock_books/reports/templates/text/_totals.txt.erb +8 -0
  35. data/lib/rock_books/reports/templates/text/balance_sheet.txt.erb +23 -0
  36. data/lib/rock_books/reports/templates/text/income_statement.txt.erb +23 -0
  37. data/lib/rock_books/reports/templates/text/journal.txt.erb +23 -0
  38. data/lib/rock_books/reports/templates/text/multidoc_txn_by_account_report.txt.erb +31 -0
  39. data/lib/rock_books/reports/templates/text/multidoc_txn_report.txt.erb +25 -0
  40. data/lib/rock_books/reports/templates/text/receipts_report.txt.erb +16 -0
  41. data/lib/rock_books/reports/templates/text/tx_one_account.txt.erb +21 -0
  42. data/lib/rock_books/reports/tx_one_account.rb +10 -45
  43. data/lib/rock_books/types/account.rb +13 -1
  44. data/lib/rock_books/types/account_type.rb +18 -7
  45. data/lib/rock_books/version.rb +2 -1
  46. data/manual.md +13 -16
  47. data/rock_books.gemspec +2 -0
  48. metadata +57 -10
  49. data/lib/rock_books/helpers/html_helper.rb +0 -29
  50. data/lib/rock_books/reports/index.html.erb +0 -156
  51. data/lib/rock_books/reports/multidoc_transaction_report.rb +0 -66
  52. data/lib/rock_books/reports/receipts.html.erb +0 -54
  53. data/lib/rock_books/reports/reporter.rb +0 -118
  54. data/lib/rock_books/reports/transaction_report.rb +0 -105
  55. data/lib/rock_books/reports/tx_by_account.rb +0 -82
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <!-- <%= metadata_comment %> -->
4
+ <head>
5
+ <title><%= title %></title>
6
+ <meta name="generator" content="RockBooks Accounting"/>
7
+ <style>
8
+ body {
9
+ padding: 36px;
10
+ background-color: #d5f3fb;
11
+ color: #01182d;
12
+ max-width: 1024px;
13
+ float: none !important;
14
+ margin: 0 auto !important;
15
+ border: 0px;
16
+ font-size: 15px !important;
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <pre>
22
+ <%= report_body %>
23
+ </pre>
24
+ </body>
25
+ </html>
@@ -0,0 +1,17 @@
1
+ <% receipt_info_line = ->(info) { sprintf("%-16.16s %s", info[:journal], info[:receipt]) } -%>
2
+ <% format_heading_line = ->(str1, str2) { sprintf("%-16.16s %s", str1, str2) } -%>
3
+
4
+
5
+
6
+ <%= name %> Receipts:
7
+
8
+ <%= format_heading_line.('Journal', 'Receipt Filespec') %>
9
+ <%= format_heading_line.('-------', '----------------') %>
10
+
11
+ <% if list.empty? -%>
12
+ [None]
13
+ <% else -%>
14
+ <% list.each do |receipt| -%>
15
+ <%= receipt_info_line.(receipt) %>
16
+ <% end -%>
17
+ <% end -%>
@@ -0,0 +1,8 @@
1
+ Totals
2
+ ------
3
+
4
+ <% totals.keys.sort.each do |acct_code| -%>
5
+ <%= sprintf(line_item_format_string, totals[acct_code], acct_code, fn_acct_name.(acct_code)) %>
6
+ <% end -%>
7
+ ------------
8
+ <%= fn_total_with_ok_or_discrepancy.(grand_total) %>
@@ -0,0 +1,23 @@
1
+ <%= banner_line %>
2
+ <%= fn_center.(entity || 'Unspecified Entity') %>
3
+ <%= fn_center.("Balance Sheet for Period Ending #{end_date}") %>
4
+
5
+ <%= fn_center.(generated) %>
6
+ <%= banner_line %>
7
+
8
+ <% %i(asset liability equity).each do |section_type| -%>
9
+ <%= fn_section_heading.(section_type) %>
10
+
11
+ <% section_data = sections[section_type] -%>
12
+ <% section_data[:acct_totals].each do |code, amount| -%>
13
+ <%= line_item_format_string % [amount, code, fn_acct_name.(code)] %>
14
+ <% end -%>
15
+ ------------
16
+ <%= sprintf(line_item_format_string, section_data[:total], '', '') %>
17
+ <% end %>
18
+
19
+
20
+ Assets - (Liabilities + Equity)
21
+
22
+ <%= fn_total_with_ok_or_discrepancy.(grand_total) %>
23
+ ============
@@ -0,0 +1,23 @@
1
+ <%= banner_line %>
2
+ <%= fn_center.(entity || 'Unspecified Entity') %>
3
+ <%= fn_center.("Income Statement -- #{accounting_period}") %>
4
+
5
+ <%= fn_center.(generated) %>
6
+ <%= banner_line %>
7
+
8
+ <% %i(income expense).each do |section_type| -%>
9
+ <%= fn_section_heading.(section_type) %>
10
+
11
+ <% section_data = sections[section_type] -%>
12
+ <% section_data[:acct_totals].each do |code, amount| -%>
13
+ <%= line_item_format_string % [amount, code, fn_acct_name.(code)] %>
14
+ <% end -%>
15
+ ------------
16
+ <%= sprintf(line_item_format_string, section_data[:total], '', '') %>
17
+ <% end %>
18
+
19
+
20
+ Net Income
21
+
22
+ <%= sprintf(line_item_format_string, net_income, '', '') %>
23
+ ============
@@ -0,0 +1,23 @@
1
+ <%= banner_line %>
2
+ <%= fn_center.(entity || 'Unspecified Entity') %>
3
+ <%= fn_center.(title) %>
4
+ <%= fn_center.(accounting_period) %>
5
+
6
+ <% if code && name -%>
7
+ <%= fn_center.("Account: #{name} (#{code})") %>
8
+ <% end -%>
9
+ <% if short_name -%>
10
+ <%= fn_center.("Journal Short Name: #{short_name}") %>
11
+ <%= fn_center.("Amounts shown are #{debit_or_credit}s to specified accounts (negate for #{short_name})") %>
12
+ <% end -%>
13
+
14
+ <%= fn_center.(generated) %>
15
+ <%= banner_line %>
16
+
17
+
18
+ <% entries.each do |entry| -%>
19
+ <%= fn_format_entry.(entry) %>
20
+ <% end %>
21
+
22
+ <%= fn_erb_render_binding.('text/_totals.txt.erb', binding) %>
23
+
@@ -0,0 +1,31 @@
1
+ <%= banner_line %>
2
+ <%= fn_center.(entity || 'Unspecified Entity') %>
3
+ <%= fn_center.('Multi Document Transaction Report by Account') %>
4
+ <%= fn_center.(accounting_period) %>
5
+
6
+ <%= fn_center.(generated) %>
7
+
8
+ <%= fn_center.('Source Documents') %>
9
+
10
+ <% journals.each do |journal| -%>
11
+ <% short_name = sprintf(short_name_format_string, journal.short_name) -%>
12
+ <%= fn_center.("#{short_name} -- #{journal.title}") %>
13
+ <% end -%>
14
+ <%= banner_line %>
15
+
16
+ <% acct_sections.each do |acct| %>
17
+
18
+
19
+ <%= banner_line %>
20
+ <% total_string = "%.2f" % acct[:total] -%>
21
+ <%= fn_center.("Total: #{total_string} -- #{fn_account_code_name_type_string_for_code.(acct[:code])}") %>
22
+ <%= banner_line %>
23
+
24
+ <% acct[:entries].each do |entry| -%>
25
+ <%= fn_format_multidoc_entry.(entry) %>
26
+ <% end %>
27
+ <%= fn_account_total_line.(acct[:code], acct[:total]) %>
28
+ <% end %>
29
+
30
+
31
+ <%= fn_erb_render_binding.('text/_totals.txt.erb', binding) %>
@@ -0,0 +1,25 @@
1
+ <%= banner_line %>
2
+ <%= fn_center.(entity || 'Unspecified Entity') %>
3
+ <%= fn_center.('Multi Document Transaction Report') %>
4
+ <%= fn_center.(accounting_period) %>
5
+
6
+ <%= fn_center.(generated) %>
7
+
8
+ <%= fn_center.('Source Documents') %>
9
+
10
+ <% journals.each do |journal| -%>
11
+ <% short_name = sprintf(short_name_format_string, journal.short_name) -%>
12
+ <%= fn_center.("#{short_name} -- #{journal.title}") %>
13
+ <% end -%>
14
+ <%= banner_line %>
15
+
16
+
17
+ Date Document Amount Account
18
+ ---- -------- ------ -------
19
+
20
+ <% entries.each do |entry| -%>
21
+ <%= fn_format_multidoc_entry.(entry) %>
22
+ <% end %>
23
+
24
+
25
+ <%= fn_erb_render_binding.('text/_totals.txt.erb', binding) %>
@@ -0,0 +1,16 @@
1
+ <% create_section = ->(name, list) do
2
+ fn_erb_render_hashes.('text/_receipt_section.txt.erb', { name: name, list: list }, {})
3
+ end -%>
4
+ <%= banner_line %>
5
+ <%= fn_center.(entity || 'Unspecified Entity') %>
6
+ <%= fn_center.('Receipts Report') %>
7
+ <%= fn_center.(accounting_period) %>
8
+
9
+ <%= fn_center.(generated) %>
10
+ <%= banner_line %>
11
+
12
+ <% # Unlike existing and missing, which arrays of hashes with keys :receipt and :journal, unused is an array of receipt filespecs %>
13
+
14
+ <%= create_section.('Missing', missing) %>
15
+ <%= create_section.('Unused', unused.map { |receipt| { receipt: receipt, journal: nil } }) %>
16
+ <%= create_section.('Existing', existing) %>
@@ -0,0 +1,21 @@
1
+ <%= banner_line %>
2
+ <%= fn_center.(entity || 'Unspecified Entity') %>
3
+ <%= fn_center.("Transactions for Account #{account_string}") %>
4
+ <%= fn_center.(sprintf("Total: %.2f", total)) %>
5
+ <%= fn_center.(accounting_period) %>
6
+
7
+ <%= fn_center.(generated) %>
8
+ <%= banner_line %>
9
+
10
+
11
+ <% if entries.empty? %>
12
+ There were no transactions for this account.
13
+
14
+
15
+ <% else -%>
16
+ <% entries.each do |entry| -%>
17
+ <%= fn_format_multidoc_entry.(entry) %>
18
+ <% end -%>
19
+
20
+ <%= fn_erb_render_binding.('text/_totals.txt.erb', binding) %>
21
+ <% end %>
@@ -1,63 +1,28 @@
1
+ require_relative 'data/tx_one_account_data'
1
2
  require_relative '../documents/chart_of_accounts'
2
3
  require_relative '../documents/journal'
3
- require_relative 'reporter'
4
+ require_relative 'helpers/erb_helper'
5
+ require_relative 'helpers/text_report_helper'
4
6
  require_relative 'report_context'
5
7
 
6
8
  module RockBooks
7
9
 
8
10
  class TxOneAccount
9
11
 
10
- include Reporter
12
+ include TextReportHelper
13
+ include ErbHelper
11
14
 
12
- attr_reader :context, :account_code, :account
15
+ attr_reader :context, :account_code, :data
13
16
 
14
17
 
15
- def initialize(report_context, account_code)
18
+ def initialize(data, report_context)
19
+ @data = data
16
20
  @context = report_context
17
- @account_code = account_code
18
- @account = context.chart_of_accounts.account_for_code(account_code)
19
21
  end
20
22
 
21
23
 
22
- def generate_header(account_total)
23
- lines = [banner_line]
24
- lines << center(context.entity || 'Unspecified Entity')
25
- lines << center("Transactions for Account #{account_code_name_type_string(account)}")
26
- lines << center("Total: %.2f" % account_total)
27
- lines << banner_line
28
- lines << ''
29
- lines << ''
30
- lines << ''
31
- lines.join("\n")
24
+ def generate
25
+ ErbHelper.render_hashes('text/tx_one_account.txt.erb', data, template_presentation_context)
32
26
  end
33
-
34
-
35
- def process_account(entries)
36
- entries.each_with_object('') do |entry, output|
37
- output << format_multidoc_entry(entry) << "\n"
38
- output << "\n" if entry.description && entry.description.length > 0
39
- end
40
- end
41
-
42
-
43
- def generate_report
44
- entries = Journal.entries_in_documents(context.journals, JournalEntryFilters.account_code(account_code))
45
- account_total = JournalEntry.total_for_code(entries, account_code)
46
- output = generate_header(account_total)
47
-
48
- if entries.empty?
49
- output << "There were no transactions for this account.\n\n\n\n"
50
- else
51
- output << process_account(entries)
52
- totals = AcctAmount.aggregate_amounts_by_account(JournalEntry.entries_acct_amounts(entries))
53
- output << generate_and_format_totals('Totals', totals)
54
- end
55
-
56
- output
57
- end
58
-
59
- alias_method :to_s, :generate_report
60
- alias_method :call, :generate_report
61
27
  end
62
-
63
28
  end
@@ -2,6 +2,18 @@ module RockBooks
2
2
 
3
3
  # The ChartOfAccount holds the set of all these accounts for the entity.
4
4
  class Account < Struct.new(:code, :type, :name)
5
- end
6
5
 
6
+ PERMITTED_CODE_CHAR_REGEX = /[\w\-\.]$/ # word chars (letters, numbers, underscores), hyphens, periods
7
+
8
+ def validate_code(code)
9
+ unless PERMITTED_CODE_CHAR_REGEX.match(code)
10
+ raise "Code {#{code}} may only contain letters, numbers, underscores, hyphens, and periods."
11
+ end
12
+ end
13
+
14
+ def initialize(code, type, name)
15
+ validate_code(code)
16
+ super
17
+ end
18
+ end
7
19
  end
@@ -12,7 +12,7 @@ module RockBooks
12
12
 
13
13
  ALL_TYPES = [ASSET, LIABILITY, EQUITY, INCOME, EXPENSE]
14
14
 
15
- TYPE_HASH = {
15
+ LETTER_TO_TYPE = {
16
16
  'A' => ASSET,
17
17
  'L' => LIABILITY,
18
18
  'O' => EQUITY,
@@ -20,14 +20,25 @@ module RockBooks
20
20
  'E' => EXPENSE
21
21
  }
22
22
 
23
- # Converts strings
24
- def self.to_type(string)
25
- type = TYPE_HASH[string[0].upcase]
26
- if type.nil?
23
+ SYMBOL_TO_TYPE = {
24
+ :asset => ASSET,
25
+ :liability => LIABILITY,
26
+ :equity => EQUITY,
27
+ :income => INCOME,
28
+ :expense => EXPENSE
29
+ }
30
+
31
+ # Converts type upper case letter representation to a type object (e.g. 'A' => ASSET)
32
+ def self.letter_to_type(string)
33
+ key = string[0].upcase
34
+ LETTER_TO_TYPE.fetch(key) do
27
35
  raise Error.new("Account type of #{string} not valid. " +
28
- "Must be one of #{TYPE_HASH.keys} (#{ALL_TYPES.map(&:singular_name)})")
36
+ "Must be one of #{LETTER_TO_TYPE.keys} (#{ALL_TYPES.map(&:singular_name)})")
29
37
  end
30
- type
38
+ end
39
+
40
+ def self.symbol_to_type(symbol)
41
+ SYMBOL_TO_TYPE[symbol]
31
42
  end
32
43
  end
33
44
  end
@@ -1,3 +1,4 @@
1
1
  module RockBooks
2
- VERSION = "0.6.1"
2
+ VERSION = '0.10.0'
3
+ PROJECT_URL = 'https://github.com/keithrbennett/rock_books'
3
4
  end
data/manual.md CHANGED
@@ -15,7 +15,7 @@ Install the RockBooks software:
15
15
  It is recommended that you create a directory structure such as this:
16
16
 
17
17
  ```.
18
- ├── 2018-xyz-inc
18
+ ├── 2021-xyz-inc
19
19
  │   ├── invoices
20
20
  │   ├── receipts
21
21
  │   ├── references
@@ -44,9 +44,9 @@ Feel free to organize your files in subdirectories of these directories in whate
44
44
 
45
45
  ### Version Control
46
46
 
47
- Tracking this directory tree with version control software such as `git` in a private repository is _highly_ recommended because it provides:
47
+ Tracking this directory tree with version control software such as `git` is _highly_ recommended because it provides:
48
48
 
49
- * free cloud backup with Github, Gitlab, and/or Bitbucket
49
+ * free cloud backup in a private repository with Github, Gitlab, and/or Bitbucket
50
50
  * an audit trail with human readable diffs
51
51
  * manageable collaboration
52
52
 
@@ -58,7 +58,7 @@ You will need a chart of accounts in the `rockbooks-inputs` directory. A sample
58
58
 
59
59
  ### Journals
60
60
 
61
- You will need journals. Usually there would be one journal per external financial institution accounts, such as checking and credit card accounts. Samples have been provided in the sample_data/minimal/rockbooks-inputs directory of the gem:
61
+ You will need journals. Usually there would be one journal per external financial institution account, such as checking and credit card accounts. Samples have been provided in the sample_data/minimal/rockbooks-inputs directory of the gem:
62
62
 
63
63
  * [Checking](sample_data/minimal/rockbooks-inputs/2018-xyz-checking-journal.txt)
64
64
  * [Credit Card](sample_data/minimal/rockbooks-inputs/2018-xyz-visa-journal.txt)
@@ -92,10 +92,8 @@ Fields are space separated; any number of spaces can be used.
92
92
 
93
93
  #### Comment Lines
94
94
 
95
- Lines beginning with `#` will be ignored when the input data is parsed. Comment lines are useful for:
96
-
97
- * explanations of the input itself
98
- * information that you would like to be available for deeper research or examination but not printed in the reports
95
+ Lines beginning with `#` will be ignored when the input data is parsed. Therefore, comment lines will not be included in the generated reports. Comment lines are useful for explanations that are too verbose for the reports, but that should be available for deeper research. An example might be several lines of explanatory notes about an unusual transaction.
96
+
99
97
 
100
98
  #### Document Properties
101
99
 
@@ -110,19 +108,19 @@ as opposed to input records, will be expressed as lines beginning with `@`:
110
108
 
111
109
  #### Input Records
112
110
 
113
- Input records are multiple records for the type appropriate to the document:
111
+ Input records are records of a type appropriate to the document:
114
112
 
115
113
  * chart of accounts - each account
116
114
  * journals - each transaction
117
115
 
118
- Input records are, in general, entered into the text files after all properties. One exception is that the `@date_prefix` is often specified in multiple places in the journal, usually with a new month (e.g. `@date_prefix: 2018-11`).
116
+ Input records are, in general, entered into the text files after all properties. One exception is that the `@date_prefix` is often specified in multiple places in the journal, usually with a new month (e.g. `@date_prefix: 2021-11`).
119
117
 
120
118
 
121
119
 
122
120
  Data lines will contain fields that an be separated with an arbitrary number of spaces, e.g.:
123
121
 
124
122
  ```
125
- 2018-05-18 123.45 supplies
123
+ 2021-05-18 123.45 supplies
126
124
  ```
127
125
 
128
126
  In journals, all entries will begin with dates, and all dates begin with numerals, so the
@@ -133,11 +131,11 @@ its other data and included in reports.
133
131
 
134
132
  In order to make the entry of dates more convenient, many documents will support
135
133
  a `@date_prefix` property that will be prepended to dates. For example, if this prefix
136
- contains `2018-`, then subsequent dates must exclude that prefix since it will be
134
+ contains `2021-`, then subsequent dates must exclude that prefix since it will be
137
135
  automatically prepended. So, for example, a journal might contain the following lines:
138
136
 
139
137
  ```
140
- @date_prefix: 2018-
138
+ @date_prefix: 2021-
141
139
  # ...more lines...
142
140
  05-29 37.50 ofc.spls
143
141
  05-30 22.20 tr.taxi
@@ -236,9 +234,8 @@ the cash account), you would set the property to `debit`:
236
234
 
237
235
  #### General Journal
238
236
 
239
- The general journal is a special form of journal that does not have a primary account.
240
-
241
- In this journal, debits and credits need to be specified literally as account code/amount
237
+ Since the general journal is a special form of journal that does not have a primary account,
238
+ debits and credits need to be specified literally as account code/amount
242
239
  pairs, where positive numbers will result in debits, and negative numbers will result in credits, e.g.:
243
240
 
244
241
  ```