rock_books 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +200 -0
  8. data/RELEASE_NOTES.md +4 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/rock_books +5 -0
  13. data/lib/rock_books/cmd_line/command_line_interface.rb +391 -0
  14. data/lib/rock_books/cmd_line/main.rb +108 -0
  15. data/lib/rock_books/documents/book_set.rb +113 -0
  16. data/lib/rock_books/documents/chart_of_accounts.rb +113 -0
  17. data/lib/rock_books/documents/journal.rb +161 -0
  18. data/lib/rock_books/documents/journal_entry.rb +73 -0
  19. data/lib/rock_books/documents/journal_entry_builder.rb +148 -0
  20. data/lib/rock_books/errors/account_not_found_error.rb +20 -0
  21. data/lib/rock_books/errors/error.rb +10 -0
  22. data/lib/rock_books/filters/acct_amount_filters.rb +12 -0
  23. data/lib/rock_books/filters/journal_entry_filters.rb +84 -0
  24. data/lib/rock_books/helpers/book_set_loader.rb +62 -0
  25. data/lib/rock_books/helpers/parse_helper.rb +22 -0
  26. data/lib/rock_books/reports/balance_sheet.rb +60 -0
  27. data/lib/rock_books/reports/income_statement.rb +63 -0
  28. data/lib/rock_books/reports/multidoc_transaction_report.rb +66 -0
  29. data/lib/rock_books/reports/receipts_report.rb +57 -0
  30. data/lib/rock_books/reports/report_context.rb +15 -0
  31. data/lib/rock_books/reports/reporter.rb +118 -0
  32. data/lib/rock_books/reports/transaction_report.rb +103 -0
  33. data/lib/rock_books/reports/tx_by_account.rb +82 -0
  34. data/lib/rock_books/reports/tx_one_account.rb +63 -0
  35. data/lib/rock_books/types/account.rb +7 -0
  36. data/lib/rock_books/types/account_type.rb +33 -0
  37. data/lib/rock_books/types/acct_amount.rb +52 -0
  38. data/lib/rock_books/version.rb +3 -0
  39. data/lib/rock_books.rb +7 -0
  40. data/rock_books.gemspec +39 -0
  41. data/sample_data/minimal/rockbooks-inputs/2017-xyz-chart-of-accounts.rbt +62 -0
  42. data/sample_data/minimal/rockbooks-inputs/2017-xyz-checking-journal.rbt +17 -0
  43. data/sample_data/minimal/rockbooks-inputs/2017-xyz-general-journal.rbt +14 -0
  44. data/sample_data/minimal/rockbooks-inputs/2017-xyz-visa-journal.rbt +23 -0
  45. metadata +158 -0
@@ -0,0 +1,118 @@
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
+
@@ -0,0 +1,103 @@
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.first) \
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 { |entry| sio << format_entry(entry) << "\n" }
93
+ totals = AcctAmount.aggregate_amounts_by_account(JournalEntry.entries_acct_amounts(entries))
94
+ sio << generate_and_format_totals('Totals', totals)
95
+ sio.string
96
+ end
97
+
98
+
99
+ alias_method :to_s, :generate_report
100
+ alias_method :call, :generate_report
101
+ end
102
+
103
+ end
@@ -0,0 +1,82 @@
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
@@ -0,0 +1,63 @@
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 TxOneAccount
9
+
10
+ include Reporter
11
+
12
+ attr_reader :context, :account_code, :account
13
+
14
+
15
+ def initialize(report_context, account_code)
16
+ @context = report_context
17
+ @account_code = account_code
18
+ @account = context.chart_of_accounts.account_for_code(account_code)
19
+ end
20
+
21
+
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")
32
+ 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_filter(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
+ end
62
+
63
+ end
@@ -0,0 +1,7 @@
1
+ module RockBooks
2
+
3
+ # The ChartOfAccount holds the set of all these accounts for the entity.
4
+ class Account < Struct.new(:code, :type, :name)
5
+ end
6
+
7
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../errors/error'
2
+
3
+ module RockBooks
4
+
5
+ class AccountType < Struct.new(:symbol, :singular_name, :plural_name)
6
+
7
+ ASSET = self.new(:asset, 'Asset', 'Assets')
8
+ LIABILITY = self.new(:liability, 'Liability', 'Liabilities')
9
+ EQUITY = self.new(:equity, 'Equity', 'Equity')
10
+ INCOME = self.new(:income, 'Income', 'Income')
11
+ EXPENSE = self.new(:expense, 'Expense', 'Expenses')
12
+
13
+ ALL_TYPES = [ASSET, LIABILITY, EQUITY, INCOME, EXPENSE]
14
+
15
+ TYPE_HASH = {
16
+ 'A' => ASSET,
17
+ 'L' => LIABILITY,
18
+ 'O' => EQUITY,
19
+ 'I' => INCOME,
20
+ 'E' => EXPENSE
21
+ }
22
+
23
+ # Converts strings
24
+ def self.to_type(string)
25
+ type = TYPE_HASH[string[0].upcase]
26
+ if type.nil?
27
+ raise Error.new("Account type of #{string} not valid. " +
28
+ "Must be one of #{TYPE_HASH.keys} (#{ALL_TYPES.map(&:singular_name)})")
29
+ end
30
+ type
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,52 @@
1
+ module RockBooks
2
+
3
+ # This class represents an account code and an amount.
4
+ # Journal entries will have multiple instances of these.
5
+ class AcctAmount < Struct.new(:date, :code, :amount)
6
+
7
+
8
+ # Same as constructor except it raises an error if the account code is not in the chart of accounts.
9
+ def self.create_with_chart_validation(date, code, amount, chart_of_accounts)
10
+ unless chart_of_accounts.include?(code)
11
+ raise AccountNotFoundError.new(code)
12
+ end
13
+ self.new(date, code, amount)
14
+ end
15
+
16
+
17
+ def self.total_amount(acct_amounts)
18
+ acct_amounts.inject(0) { |sum, acct_amount| sum += acct_amount.amount }
19
+ end
20
+
21
+
22
+ # Returns a hash whose keys are account codes and values are the totals for those codes.
23
+ # The 'aggregate' in the method name is intended to be a noun, not a verb.
24
+ def self.aggregate_amounts_by_account(acct_amounts)
25
+ totals = acct_amounts.each_with_object(Hash.new(0)) do |acct_amount, by_account|
26
+ by_account[acct_amount.code] += acct_amount.amount
27
+ end
28
+ totals.each do |code, amount |
29
+ totals[code] = amount.round(2)
30
+ end
31
+ end
32
+
33
+
34
+ # Returns the subset of the passed array of acct_amount's that contain the specified account code
35
+ def self.containing_code(acct_amounts, account_code)
36
+ acct_amounts.select { |acct_amount| acct_amount.code == account_code }
37
+ end
38
+
39
+
40
+ # For the passed array of AcctAmount's, calculate the total for a single account.
41
+ def self.total_amount_for_code(acct_amounts, account_code)
42
+ containing_code(acct_amounts, account_code) \
43
+ .map(&:amount) \
44
+ .sum
45
+ end
46
+
47
+
48
+ def self.filter(acct_amounts, filter)
49
+ acct_amounts.select { |acct_amount| filter.(acct_amount)}
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module RockBooks
2
+ VERSION = "0.1.0"
3
+ end
data/lib/rock_books.rb ADDED
@@ -0,0 +1,7 @@
1
+ module RockBooks
2
+
3
+ DEFAULT_INPUT_DIR = './rockbooks-inputs'
4
+ DEFAULT_OUTPUT_DIR = './rockbooks-reports'
5
+ DEFAULT_RECEIPT_DIR = './receipts'
6
+ SINGLE_ACCT_SUBDIR = 'single-account'
7
+ end
@@ -0,0 +1,39 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "rock_books/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rock_books"
8
+ spec.version = RockBooks::VERSION
9
+ spec.authors = ["Keith Bennett"]
10
+ spec.email = ["keithrbennett@gmail.com"]
11
+
12
+ spec.summary = %q{Very basic accounting package.}
13
+ spec.description = %q{Extremely primitive accounting software.}
14
+ spec.homepage = "http://example.com"
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ # if spec.respond_to?(:metadata)
20
+ # spec.metadata["allowed_push_host"] = ": Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against " \
23
+ # "public gem pushes."
24
+ # end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = "exe"
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_dependency 'awesome_print', '> 0'
34
+ spec.add_dependency 'pry', '> 0.0.0'
35
+
36
+ spec.add_development_dependency "bundler", "~> 1.16"
37
+ spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rspec", "~> 3.0"
39
+ end
@@ -0,0 +1,62 @@
1
+ @doc_type: chart_of_accounts
2
+ @title: Chart of Accounts - 2017
3
+ @entity: XYZ Consulting, Inc.
4
+
5
+
6
+ # Assets
7
+
8
+ ck.hsbc A HSBC Checking
9
+ paypal A Paypal
10
+ accts.rec A Accounts Receivable
11
+
12
+
13
+ # Liabilities
14
+
15
+ cc.hsbc.visa L Visa Credit Card
16
+ loan.to.sh L Loan Payable to Shareholder
17
+
18
+
19
+ # Equity
20
+
21
+ own.equity O Owner's Equity
22
+ ret.earn O Retained Earnings
23
+
24
+
25
+ # Income
26
+
27
+ sls.cons I Sales - Consulting
28
+
29
+
30
+ # Expenses
31
+
32
+ bank.fees E Bank Charges
33
+ books.refs E Books, Screencasts, References
34
+ conf.fees E Conference Fees
35
+ cowork.fees E Coworking Fees
36
+ govt.fees E Government Fees
37
+ inet.fees E Internet Service, Domain, and Hosting Fees
38
+ insurance E Insurance
39
+ int.exp E Interest Expense
40
+ mktng.exp E Marketing Expenses
41
+ meals.ent E Meals & Entertainment
42
+ misc.exp E Miscellaneous Expenses
43
+ prof.fees E Professional Fees
44
+ repair.maint E Repair & Maintenance
45
+ ship.exp E Shipping and Mailing Expenses
46
+ sw.exp E Software Expense
47
+ supplies E Supplies
48
+ cc.proc E Credit Card Processing Fees
49
+ tr.airfare E Travel - Air Fares
50
+ tr.autorent E Travel - Auto Rental
51
+ tr.gas.etc E Travel - Gas, Oil, Tolls, etc.
52
+ tr.govt E Travel - Government Fees
53
+ tr.lodging E Travel - Lodging
54
+ tr.m.i E Travel - Meals & Incidentals
55
+ tr.mileage E Travel - Mileage Allowance
56
+ tr.misc E Travel - Miscellaneous
57
+ tr.parking E Travel - Parking
58
+ tr.perdiem.mi E Travel - Per Diem (Meals and Incidentals)
59
+ tr.taxi E Travel - Taxi
60
+ tr.trainfare E Travel - Train Fare
61
+ tr.m.e E Meals and Entertainment
62
+ tr.unclass E Expenses Not Yet Classified
@@ -0,0 +1,17 @@
1
+ @doc_type: journal
2
+ @title: HSBC Checking Disbursements Journal - 2017
3
+ @account_code: ck.hsbc
4
+ @debit_or_credit: debit
5
+ @short_name: ck.hsbc
6
+ @date_prefix: 2017-
7
+
8
+ 01-01 -5000.00 own.equity
9
+ Initial Deposit from Shareholder
10
+
11
+ 01-05 2000.00 cc.hsbc.visa
12
+
13
+ 01-07 -10000.00 sls.cons
14
+ Invoice #437, Dec. 2016 work, ABC, Inc.
15
+
16
+
17
+
@@ -0,0 +1,14 @@
1
+ @doc_type: general_journal
2
+ @title: General Journal - 2017
3
+ @date_prefix: 2017-
4
+ @short_name: general
5
+
6
+ 01-08 tr.airfare 300.00 loan.to.sh -300.00
7
+ Phoenix conference air ticket paid on personal credit card
8
+
9
+ 01-20 tr.perdiem.mi 495.00 loan.to.sh -495.00
10
+ Per diem allowance for Phoenix conference (see worksheet)
11
+
12
+ 01-31 tr.mileage 117.70 loan.to.sh -117.70
13
+ Mileage reimbursement for business travel of January 2017 (see worksheet)
14
+
@@ -0,0 +1,23 @@
1
+ @doc_type: journal
2
+ @title: HSBC Visa Journal - 2017
3
+ @account_code: cc.hsbc.visa
4
+ @short_name: hsbc_visa
5
+ @debit_or_credit: debit
6
+ @date_prefix: 2017-
7
+
8
+ 01-02 750 insurance
9
+ Professional insurance for the year 2017, Hartford Insurance
10
+ Receipt: 01/2017-01-02-hartford-insurance.pdf
11
+
12
+ 01-02 100 cowork.fees
13
+ New Work City coworking fee for the month of January
14
+ Receipt: 01/2017-01-02-nwc.pdf
15
+
16
+ 01-06 500 conf.fees
17
+ Phoenix conference registration fee
18
+ Receipt: 01/2017-01-06-phoenix-conference-registration.pdf
19
+
20
+ 01-20 400 tr.lodging
21
+ Hampton Inn Phoenix (conference)
22
+ Receipt: 01/2017-01-20-phoenix-hampton.pdf
23
+