rock_books 0.6.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -3
  3. data/RELEASE_NOTES.md +49 -10
  4. data/assets/fonts/JetBrainsMono-Medium.ttf +0 -0
  5. data/lib/rock_books/cmd_line/command_line_interface.rb +6 -6
  6. data/lib/rock_books/cmd_line/main.rb +1 -9
  7. data/lib/rock_books/documents/book_set.rb +1 -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 +115 -98
  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 +47 -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 +13 -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/rock_books.gemspec +1 -0
  47. metadata +43 -10
  48. data/lib/rock_books/helpers/html_helper.rb +0 -29
  49. data/lib/rock_books/reports/index.html.erb +0 -156
  50. data/lib/rock_books/reports/multidoc_transaction_report.rb +0 -66
  51. data/lib/rock_books/reports/receipts.html.erb +0 -54
  52. data/lib/rock_books/reports/reporter.rb +0 -118
  53. data/lib/rock_books/reports/transaction_report.rb +0 -105
  54. data/lib/rock_books/reports/tx_by_account.rb +0 -82
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a7c5d56c718ce58a0fdf060e25be5d3aaf5438ece48a76e02f912e09d203c39
4
- data.tar.gz: b467740538a5e89504fc7e2ac6b72190181519471f898a5c26ae7cfa29a53496
3
+ metadata.gz: ba9e80afcb8f6644e9fa3cc629a4cac6d723843d1609bab54641c77b779fba89
4
+ data.tar.gz: db3b9f6979da2408202715737fedf5f69b55d44657622364706d7597eac827bf
5
5
  SHA512:
6
- metadata.gz: b43529ffa215e13375538d173b939baf659b814dc07a85e7f6c9a695449a86a8dfe49d5134bca22b0c7a479d0707221c4d9bd87efdcef4ac9df95a815b148ca0
7
- data.tar.gz: 15776062f22eeb7ad746c303289512c9f81005f04f87518c1738d3923991d45a9ef32951b930946d2c746b6b7a6a52516892f4a8cbf5811ca2d58da4fadff2d5
6
+ metadata.gz: de4ffe3cbf12789ffa874565e7e60299290e3cc8888a41a2756f0ef7b405152997286f2d4a0b9dd2c00aeb75afc8d2bb10245ae7e747492b14c9da4393948d79
7
+ data.tar.gz: c19a5de4f323c6d50a6a684f87b38e3f12dab1cc55accbeed9ec8560521f02932bf8b6b8788069807496d37e4ff3c6d89056fc16cfcddf87f1c14d1af8e002da
data/README.md CHANGED
@@ -33,10 +33,8 @@ To simplify its implementation, RockBooks assumes some conventions:
33
33
 
34
34
  #### Supported Operating Systems
35
35
 
36
- At this time, RockBooks is supported only on Mac OS and Linux. (It's really the external tools that is the issue, not the Ruby code.) It should probably work with Windows Subsystem for Linux (WSL) but it hasn't been tested. If you're unsuccessful trying to use RockBooks with WSL, give me as much information as possible and I'll try to resolve the issue.
36
+ At this time, RockBooks is tested only on Mac OS and Linux. However, it will probably work fine on Windows.
37
37
 
38
- If you get an error message saying that an external command is missing, install that command using your system's package manager (e.g. `sudo apt install txt2html`).
39
-
40
38
  #### Text Files as Input
41
39
 
42
40
  Instead of a web interface, data input is done in plain text files. This isn't as fancy but has the following advantages:
@@ -1,3 +1,42 @@
1
+ ### v0.9.0
2
+
3
+ * Center generated index.html's content.
4
+ * Improve report headings.
5
+ * Fix index.html hyperlinks to resource (receipts, invoices, statements, worksheets) directories.
6
+ * Fix cases where split journal entries were not included in the journal report.
7
+
8
+
9
+ ### v0.8.0
10
+
11
+ * Add metadata to PDF and HTML reports
12
+ * Add report generation timestamp and accounting period to all reports.
13
+ * Refactoring and cleanup.
14
+
15
+
16
+ ### v0.7.1
17
+
18
+ * Refactor report helpers.
19
+ * Improve/reduce text output during reporting.
20
+ * Add title to HTML reports.
21
+ * Minor fixes.
22
+
23
+
24
+ ### v0.7.0
25
+
26
+ * Dependencies on external commands in Linux and Mac OS for generating PDF and HTML files has been eliminated,
27
+ using the prawn gem for PDF and simple ERB templating for HTML.
28
+ * Massive refactoring of reports to separate data generation from presentation.
29
+ * Reports are now computed and then written one at a time. Previously they were all computed, then all written.
30
+ * ERB is now used for generating text reports.
31
+ * Fix receipt hyperlinks in HTML output.
32
+ * Improve some error output.
33
+ * Various minor improvements and bug fixes.
34
+
35
+
36
+ ### v0.6.1
37
+
38
+ * Linux PDF generation fixed by using wkhtmltopdf instead of cupsfilter.
39
+
1
40
  ### v0.6.0
2
41
 
3
42
  * Linux support added!
@@ -7,7 +46,7 @@
7
46
  * Add receipt hyperlinks to HTML output.
8
47
 
9
48
 
10
- ## v0.4.0
49
+ ### v0.4.0
11
50
 
12
51
  * Sort unused receipts alphanumerically.
13
52
  * Add 'x' receipts option for reporting both missing and unused receipts.
@@ -15,7 +54,7 @@
15
54
  * Improve Receipts report.
16
55
 
17
56
 
18
- ## v0.3.0
57
+ ### v0.3.0
19
58
 
20
59
  * Added ability to report unused receipts.
21
60
  * Errors now include more context information.
@@ -23,12 +62,12 @@
23
62
  * Change license from MIT to Apache 2.
24
63
 
25
64
 
26
- ## v0.2.1
65
+ ### v0.2.1
27
66
 
28
67
  * Add help text to readme.
29
68
 
30
69
 
31
- ## v0.2.0
70
+ ### v0.2.0
32
71
 
33
72
  * Add instruction manual, modify readme.
34
73
  * Overhaul generated index.html.
@@ -36,18 +75,18 @@
36
75
  * Add validation of transaction dates to ensure within configured date range.
37
76
  * Make report hash / OpenStruct keys consistently symbols.
38
77
 
39
- ## v0.1.6
78
+ ### v0.1.6
40
79
 
41
80
  * Fixed PDF output; PDF files were corrupt because cupsfilter starting sending
42
81
  output to stderr at some point.
43
82
 
44
83
 
45
- ## v0.1.4, v0.1.5
84
+ ### v0.1.4, v0.1.5
46
85
 
47
86
  * Intermediate unsatisfactory fixes, these versions were published but yanked.
48
87
 
49
88
 
50
- ## v0.1.3
89
+ ### v0.1.3
51
90
 
52
91
  * Report output now goes to separate txt, html, and pdf subdirectories.
53
92
  * Add vendor.yml to exclude generated report files from language reporting.
@@ -62,17 +101,17 @@ output to stderr at some point.
62
101
  * Add 'from_string' methods to Journal and ChartOfAccounts.
63
102
 
64
103
 
65
- ## v0.1.2
104
+ ### v0.1.2
66
105
 
67
106
  * Improve error message when the needed directories do not exist.
68
107
 
69
108
 
70
- ## v0.1.1
109
+ ### v0.1.1
71
110
 
72
111
  * Fix startup error.
73
112
 
74
113
 
75
- ## v0.1.0
114
+ ### v0.1.0
76
115
 
77
116
  * First release.
78
117
 
@@ -4,7 +4,7 @@ require 'ostruct'
4
4
 
5
5
  require_relative '../../rock_books'
6
6
  require_relative '../version'
7
- require_relative '../reports/reporter'
7
+ require_relative '../reports/helpers/text_report_helper'
8
8
  require_relative '../helpers/book_set_loader'
9
9
 
10
10
  module RockBooks
@@ -105,7 +105,7 @@ When in interactive shell mode:
105
105
  end
106
106
  end
107
107
 
108
- validate_receipts_dir = -> do
108
+ validate_receipt_dir = -> do
109
109
  File.directory?(options.receipt_dir) ? nil : \
110
110
  "Receipts directory '#{options.receipt_dir}' does not exist. "
111
111
  end
@@ -114,7 +114,7 @@ When in interactive shell mode:
114
114
  output << validate_input_dir.()
115
115
  output << validate_output_dir.()
116
116
  if run_options.do_receipts
117
- output << validate_receipts_dir.()
117
+ output << validate_receipt_dir.()
118
118
  end
119
119
 
120
120
  output.compact!
@@ -298,9 +298,9 @@ When in interactive shell mode:
298
298
 
299
299
 
300
300
  def cmd_d
301
- book_set.all_reports($filter).each do |short_name, report_text|
301
+ book_set.all_reports($filter).each do |short_name, text_report|
302
302
  puts "#{short_name}:\n\n"
303
- puts report_text
303
+ puts text_report
304
304
  puts "\n\n\n"
305
305
  end
306
306
  nil
@@ -358,7 +358,7 @@ When in interactive shell mode:
358
358
  end
359
359
 
360
360
  def cmd_w
361
- BookSetReporter.new(book_set, run_options.output_dir, $filter).call
361
+ BookSetReporter.new(book_set, run_options.output_dir, $filter).generate
362
362
  nil
363
363
  end
364
364
 
@@ -1,4 +1,4 @@
1
- require 'awesome_print'
1
+ require 'amazing_print'
2
2
  require 'optparse'
3
3
  require 'pry'
4
4
  require 'shellwords'
@@ -67,10 +67,6 @@ class Main
67
67
  options.verbose_mode = v
68
68
  end
69
69
 
70
- parser.on('-y', '--[no-]say', 'Say error messages.') do |v|
71
- options.say = v
72
- end
73
-
74
70
  parser.on('', '--[no-]receipts', 'Include report on existing and missing receipts.') do |v|
75
71
  options.do_receipts = v
76
72
  end
@@ -101,10 +97,6 @@ class Main
101
97
 
102
98
  HEREDOC
103
99
 
104
- if run_options.say
105
- `say #{error}`
106
- end
107
-
108
100
  exit(-1)
109
101
  binding.pry
110
102
  raise error
@@ -1,10 +1,9 @@
1
- require 'awesome_print'
1
+ require 'amazing_print'
2
2
  require 'os'
3
3
 
4
4
  require_relative 'chart_of_accounts'
5
5
  require_relative 'journal'
6
6
  require_relative '../filters/journal_entry_filters' # for shell mode
7
- require_relative '../helpers/html_helper'
8
7
  require_relative '../helpers/parse_helper'
9
8
  require_relative '../reports/book_set_reporter'
10
9
 
@@ -1,3 +1,4 @@
1
+ require 'stringio'
1
2
  require_relative '../types/account'
2
3
  require_relative '../types/account_type'
3
4
  require_relative '../errors/error'
@@ -10,8 +11,8 @@ class ChartOfAccounts
10
11
  REQUIRED_FIELDS.each { |field| attr_reader(field) }
11
12
 
12
13
 
13
- def self.from_file(file)
14
- self.new(File.readlines(file).map(&:chomp))
14
+ def self.from_file(filespec)
15
+ self.new(File.readlines(filespec).map(&:chomp), filespec)
15
16
  end
16
17
 
17
18
 
@@ -20,11 +21,16 @@ class ChartOfAccounts
20
21
  end
21
22
 
22
23
 
23
- def initialize(input_lines)
24
+ def initialize(input_lines, filespec = nil)
25
+ @filespec = filespec
24
26
  @accounts = []
25
- input_lines.each { |line| parse_line(line) }
27
+ parse_lines(input_lines)
26
28
  # TODO: Add validation for required fields.
29
+ check_for_missing_fields
30
+ end
31
+
27
32
 
33
+ def check_for_missing_fields
28
34
  missing_fields = REQUIRED_FIELDS.select do |field|
29
35
  instance_variable_get("@#{field}").nil?
30
36
  end
@@ -35,6 +41,19 @@ class ChartOfAccounts
35
41
  end
36
42
 
37
43
 
44
+ def parse_lines(input_lines)
45
+ input_lines.each_with_index do |line, line_num|
46
+ begin
47
+ parse_line(line)
48
+ rescue => e
49
+ file_message_fragment = (@filespec ? " in file '#{@filespec}'" : '')
50
+ puts "Error parsing chart of accounts#{file_message_fragment}. Bad line is line ##{line_num}, text is:\n#{line}\n\n"
51
+ raise
52
+ end
53
+ end
54
+ end
55
+
56
+
38
57
  def parse_date(date_string)
39
58
  # TODO: Add better handling for this error.
40
59
  # begin
@@ -69,16 +88,14 @@ class ChartOfAccounts
69
88
  rest = matcher[2]
70
89
 
71
90
  matcher = rest.match(/^(\S+)\s+(.*)$/)
91
+
72
92
  account_type_token = matcher[1]
73
- account_type = AccountType.to_type(account_type_token).symbol
93
+ account_type = AccountType.letter_to_type(account_type_token)
74
94
 
75
95
  name = matcher[2]
76
96
 
77
- accounts << Account.new(code, account_type, name)
97
+ accounts << Account.new(code, account_type.symbol, name)
78
98
  end
79
- rescue => e
80
- puts "Error parsing chart of accounts. Line text is:\n#{line}\n\n"
81
- raise
82
99
  end
83
100
 
84
101
  end
@@ -105,7 +122,7 @@ class ChartOfAccounts
105
122
 
106
123
 
107
124
  def report_string
108
- result = ''
125
+ result = StringIO.new
109
126
 
110
127
  if title
111
128
  result << title << "\n\n"
@@ -113,9 +130,9 @@ class ChartOfAccounts
113
130
 
114
131
  code_width = @accounts.inject(0) { |width, a| width = [width, a.code.length].max }
115
132
  format_string = "%-#{code_width}s %-10.10s %s\n"
116
- accounts.each { |a| result << (format_string % [a.code, a.type.to_s, a.name]) }
133
+ accounts.each { |a| result << sprintf(format_string, a.code, a.type.to_s, a.name) }
117
134
 
118
- result
135
+ result.string
119
136
  end
120
137
 
121
138
 
@@ -7,7 +7,7 @@ require_relative '../types/acct_amount'
7
7
  require_relative '../types/journal_entry_context'
8
8
  require_relative 'journal_entry'
9
9
  require_relative 'journal_entry_builder'
10
- require_relative '../reports/reporter'
10
+ require_relative '../reports/helpers/text_report_helper'
11
11
 
12
12
  module RockBooks
13
13
 
@@ -45,9 +45,7 @@ class Journal
45
45
  end
46
46
 
47
47
 
48
-
49
-
50
- def self.acct_amounts_in_documents(documents, entries_filter = nil, acct_amounts_filter = nil)
48
+ def self.acct_amounts_in_documents(documents, entries_filter = nil, acct_amounts_filter = nil)
51
49
  entries = entries_in_documents(documents, entries_filter)
52
50
 
53
51
  acct_amounts = entries.each_with_object([]) do |entry, acct_amounts|
@@ -67,12 +65,10 @@ class Journal
67
65
  attr_reader :short_name, :account_code, :chart_of_accounts, :date_prefix, :debit_or_credit, :doc_type, :title, :entries
68
66
 
69
67
  # short_name is a name that will appear on reports identifying the journal from which a transaction comes
70
- def initialize(chart_of_accounts, input_lines, short_name = nil)
68
+ def initialize(chart_of_accounts, input_lines)
71
69
  @chart_of_accounts = chart_of_accounts
72
- @short_name = short_name
73
70
  @entries = []
74
71
  @date_prefix = ''
75
- @title = ''
76
72
  input_lines.each_with_index do |line, linenum|
77
73
  context = JournalEntryContext.new(self, linenum + 1, line)
78
74
  parse_line(context)
@@ -131,7 +127,6 @@ class Journal
131
127
  end
132
128
 
133
129
 
134
-
135
130
  def acct_amounts
136
131
  entries.each_with_object([]) { |entry, acct_amounts| acct_amounts << entry.acct_amounts }.flatten
137
132
  end
@@ -32,12 +32,17 @@ class JournalEntry < Struct.new(:date, :acct_amounts, :doc_short_name, :descript
32
32
 
33
33
  def self.sort_entries_by_amount_descending!(entries)
34
34
  entries.sort_by! do |entry|
35
- [entry.total_absolute_value, entry.doc_short_name]
35
+ [-entry.total_absolute_value, entry.doc_short_name]
36
36
  end
37
- entries.reverse!
38
37
  end
39
38
 
40
39
 
40
+ def self.sort_entries_by_date!(entries)
41
+ entries.sort_by! { |entry| [entry.date, entry.doc_short_name] }
42
+ end
43
+
44
+
45
+
41
46
  def total_for_code(account_code)
42
47
  acct_amounts_with_code(account_code).map(&:amount).sum
43
48
  end
@@ -14,6 +14,10 @@ class JournalEntryBuilder < Struct.new(:journal_entry_context)
14
14
  def chart_of_accounts; journal_entry_context.chart_of_accounts; end
15
15
 
16
16
 
17
+ # A "token" in this context means an account name and account amount pair, e.g. "pnc.checking 1234.56".
18
+ # @param tokens the account name/amount pairs found in the source text
19
+ # @date transaction date
20
+ # @return array of AcctAmount instances
17
21
  def acct_amounts_from_tokens(tokens, date)
18
22
  acct_amounts = []
19
23
 
@@ -7,6 +7,7 @@ module RockBooks
7
7
 
8
8
  module_function
9
9
 
10
+ # @return a hash whose keys are the filespecs and values are the document types
10
11
  def get_files_with_types(directory)
11
12
  files = Dir[File.join(directory, '*.txt')]
12
13
  files.each_with_object({}) do |filespec, files_with_types|
@@ -42,7 +43,7 @@ module RockBooks
42
43
 
43
44
  # Uses all *.txt files in the specified directory; uses @doc_type to determine which
44
45
  # is the chart of accounts and which are journals.
45
- # To exclude a file, make the extension other than .rdt.
46
+ # To exclude a file, make the extension something other than .txt.
46
47
  def load(run_options)
47
48
 
48
49
  files_with_types = get_files_with_types(run_options.input_dir)
@@ -54,9 +55,8 @@ module RockBooks
54
55
  validate_journal_file_count(journal_files)
55
56
 
56
57
  chart_of_accounts = ChartOfAccounts.from_file(chart_of_account_files.first)
57
- journals = journal_files.map { |fs| Journal.from_file(chart_of_accounts, fs) }
58
+ journals = journal_files.map { |filespec| Journal.from_file(chart_of_accounts, filespec) }
58
59
  BookSet.new(run_options, chart_of_accounts, journals)
59
60
  end
60
-
61
61
  end
62
62
  end
@@ -1,6 +1,5 @@
1
- require_relative '../filters/journal_entry_filters'
2
- require_relative '../documents/journal'
3
- require_relative 'report_context'
1
+ require_relative 'helpers/erb_helper'
2
+ require_relative 'helpers/text_report_helper'
4
3
 
5
4
  module RockBooks
6
5
 
@@ -9,52 +8,19 @@ module RockBooks
9
8
  # in order to calculate the correct balances, so we ignore the global $filter.
10
9
  class BalanceSheet
11
10
 
12
- include Reporter
11
+ include TextReportHelper
12
+ include ErbHelper
13
13
 
14
- attr_accessor :context
14
+ attr_accessor :context, :data
15
15
 
16
- def initialize(report_context)
16
+ def initialize(report_context, data)
17
17
  @context = report_context
18
+ @data = data
18
19
  end
19
20
 
20
21
 
21
- def end_date
22
- context.chart_of_accounts.end_date
22
+ def generate
23
+ ErbHelper.render_hashes('text/balance_sheet.txt.erb', data, template_presentation_context)
23
24
  end
24
-
25
-
26
- def generate_header
27
- lines = [banner_line]
28
- lines << center(context.entity || 'Unspecified Entity')
29
- lines << center("Balance Sheet for Period Ending #{end_date}")
30
- lines << banner_line
31
- lines << ''
32
- lines << ''
33
- lines << ''
34
- lines.join("\n")
35
- end
36
-
37
-
38
- def generate_report
39
- filter = RockBooks::JournalEntryFilters.date_on_or_before(end_date)
40
- acct_amounts = Journal.acct_amounts_in_documents(context.journals, filter)
41
- totals = AcctAmount.aggregate_amounts_by_account(acct_amounts)
42
- output = generate_header
43
-
44
- asset_output, asset_total = generate_account_type_section('Assets', totals, :asset, false)
45
- liab_output, liab_total = generate_account_type_section('Liabilities', totals, :liability, true)
46
- equity_output, equity_total = generate_account_type_section('Equity', totals, :equity, true)
47
-
48
- output << [asset_output, liab_output, equity_output].join("\n\n")
49
-
50
- grand_total = asset_total - (liab_total + equity_total)
51
-
52
- output << "\n#{"%12.2f Assets - (Liabilities + Equity)" % grand_total}\n============\n"
53
- output
54
- end
55
-
56
- alias_method :to_s, :generate_report
57
- alias_method :call, :generate_report
58
-
59
25
  end
60
26
  end