solidus_avatax 0.2.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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +15 -0
  6. data/LICENSE +26 -0
  7. data/README.md +71 -0
  8. data/Rakefile +21 -0
  9. data/app/assets/javascripts/spree/frontend/solidus_avatax.js +1 -0
  10. data/app/assets/stylesheets/spree/frontend/solidus_avatax.css +1 -0
  11. data/app/models/spree/adjustment_decorator.rb +21 -0
  12. data/app/models/spree/order_contents_decorator.rb +26 -0
  13. data/app/models/spree/order_decorator.rb +43 -0
  14. data/app/models/spree/promotion_handler/coupon_decorator.rb +11 -0
  15. data/app/models/spree/reimbursement_decorator.rb +1 -0
  16. data/app/models/spree/tax_rate_decorator.rb +45 -0
  17. data/app/models/spree_avatax/calculator.rb +26 -0
  18. data/app/models/spree_avatax/return_invoice.rb +197 -0
  19. data/app/models/spree_avatax/sales_invoice.rb +157 -0
  20. data/app/models/spree_avatax/sales_shared.rb +211 -0
  21. data/app/models/spree_avatax/shared.rb +53 -0
  22. data/app/models/spree_avatax/short_ship_return_invoice.rb +133 -0
  23. data/app/models/spree_avatax/short_ship_return_invoice_inventory_unit.rb +11 -0
  24. data/bin/rails +7 -0
  25. data/circle.yml +6 -0
  26. data/config/locales/en.yml +5 -0
  27. data/db/migrate/20140122165618_add_avatax_response_at_to_orders.rb +5 -0
  28. data/db/migrate/20140214153139_add_avatax_invoice_at_to_orders.rb +5 -0
  29. data/db/migrate/20140617222244_close_all_tax_adjustments.rb +5 -0
  30. data/db/migrate/20140701144237_update_avatax_calculator_type.rb +17 -0
  31. data/db/migrate/20140801132302_create_spree_avatax_return_invoices.rb +19 -0
  32. data/db/migrate/20140903135132_create_spree_avatax_sales_orders.rb +15 -0
  33. data/db/migrate/20140903135357_create_spree_avatax_sales_invoices.rb +19 -0
  34. data/db/migrate/20140904171341_generate_uncommitted_sales_invoices.rb +31 -0
  35. data/db/migrate/20140911214414_add_transaction_id_to_sales_orders_and_sales_invoices.rb +6 -0
  36. data/db/migrate/20140911215422_add_canceled_at_and_cancel_transaction_id_to_sales_invoices.rb +6 -0
  37. data/db/migrate/20150427154942_create_spree_avatax_short_ship_return_invoices.rb +44 -0
  38. data/db/migrate/20150518172627_fix_avatax_short_ship_index.rb +30 -0
  39. data/lib/generators/solidus_avatax/install/install_generator.rb +31 -0
  40. data/lib/generators/solidus_avatax/install/templates/config/initializers/avatax.rb +18 -0
  41. data/lib/solidus_avatax.rb +4 -0
  42. data/lib/spree_avatax/config.rb +16 -0
  43. data/lib/spree_avatax/engine.rb +29 -0
  44. data/lib/spree_avatax/factories.rb +25 -0
  45. data/lib/tasks/commit_backfill.rake +119 -0
  46. data/lib/tasks/sales_invoice_backfill.rake +43 -0
  47. data/solidus_avatax.gemspec +34 -0
  48. data/spec/features/store_credits_spec.rb +92 -0
  49. data/spec/features/tax_calculation_spec.rb +75 -0
  50. data/spec/fixtures/vcr_cassettes/sales_invoice_gettax_with_discounts.yml +136 -0
  51. data/spec/fixtures/vcr_cassettes/sales_invoice_gettax_without_discounts.yml +136 -0
  52. data/spec/fixtures/vcr_cassettes/taxes_with_store_credits.yml +113 -0
  53. data/spec/models/spree/adjustment_spec.rb +23 -0
  54. data/spec/models/spree/order_contents_spec.rb +35 -0
  55. data/spec/models/spree/order_spec.rb +111 -0
  56. data/spec/models/spree/shipping_rate_spec.rb +23 -0
  57. data/spec/models/spree/tax_rate_spec.rb +40 -0
  58. data/spec/models/spree_avatax/calculator.rb +20 -0
  59. data/spec/models/spree_avatax/return_invoice_spec.rb +193 -0
  60. data/spec/models/spree_avatax/sales_invoice_spec.rb +491 -0
  61. data/spec/models/spree_avatax/sales_shared_spec.rb +47 -0
  62. data/spec/models/spree_avatax/shared_spec.rb +89 -0
  63. data/spec/models/spree_avatax/short_ship_return_invoice_spec.rb +181 -0
  64. data/spec/spec_helper.rb +85 -0
  65. data/spec/support/return_invoice_soap_responses.rb +117 -0
  66. data/spec/support/sales_invoice_soap_responses.rb +259 -0
  67. data/spec/support/short_ship_return_invoice_soap_responses.rb +178 -0
  68. data/spec/support/zone_support.rb +6 -0
  69. metadata +320 -0
@@ -0,0 +1,11 @@
1
+ class SpreeAvatax::ShortShipReturnInvoiceInventoryUnit < ActiveRecord::Base
2
+ belongs_to(
3
+ :short_ship_return_invoice,
4
+ class_name: 'SpreeAvatax::ShortShipReturnInvoice',
5
+ inverse_of: :short_ship_return_invoice_inventory_units,
6
+ )
7
+ belongs_to :inventory_unit, class_name: 'Spree::InventoryUnit'
8
+
9
+ validates :short_ship_return_invoice, presence: true
10
+ validates :inventory_unit, presence: true
11
+ end
data/bin/rails ADDED
@@ -0,0 +1,7 @@
1
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
2
+
3
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
4
+ ENGINE_PATH = File.expand_path('../../lib/spree_avatax/engine', __FILE__)
5
+
6
+ require 'rails/all'
7
+ require 'rails/engine/commands'
data/circle.yml ADDED
@@ -0,0 +1,6 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.1.5
4
+ test:
5
+ pre:
6
+ - bundle exec rake test_app
@@ -0,0 +1,5 @@
1
+ ---
2
+ en:
3
+ spree:
4
+ avatax_description: No Op Avatax Calculator
5
+ avatax_label: Tax
@@ -0,0 +1,5 @@
1
+ class AddAvataxResponseAtToOrders < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_orders, :avatax_response_at, :datetime
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddAvataxInvoiceAtToOrders < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_orders, :avatax_invoice_at, :datetime
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class CloseAllTaxAdjustments < ActiveRecord::Migration
2
+ def up
3
+ Spree::Adjustment.tax.update_all(finalized: true)
4
+ end
5
+ end
@@ -0,0 +1,17 @@
1
+ class UpdateAvataxCalculatorType < ActiveRecord::Migration
2
+ def up
3
+ Spree::Calculator.where(
4
+ type: "SpreeAvatax::Calculator"
5
+ ).update_all(
6
+ type: "Spree::Calculator::Avatax"
7
+ )
8
+ end
9
+
10
+ def down
11
+ Spree::Calculator.where(
12
+ type: "Spree::Calculator::Avatax"
13
+ ).update_all(
14
+ type: "SpreeAvatax::Calculator"
15
+ )
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ class CreateSpreeAvataxReturnInvoices < ActiveRecord::Migration
2
+ def change
3
+ create_table :spree_avatax_return_invoices do |t|
4
+ t.integer :reimbursement_id
5
+ t.boolean :committed
6
+ t.string :doc_id
7
+ t.string :doc_code
8
+ t.date :doc_date
9
+ t.decimal :pre_tax_total, precision: 10, scale: 2
10
+ t.decimal :additional_tax_total, precision: 10, scale: 2
11
+
12
+ t.timestamps null: true
13
+ end
14
+
15
+ add_index :spree_avatax_return_invoices, :reimbursement_id, unique: true
16
+ add_index :spree_avatax_return_invoices, :doc_id, unique: true
17
+ add_index :spree_avatax_return_invoices, :doc_code, unique: true
18
+ end
19
+ end
@@ -0,0 +1,15 @@
1
+ class CreateSpreeAvataxSalesOrders < ActiveRecord::Migration
2
+ def change
3
+ create_table :spree_avatax_sales_orders do |t|
4
+ t.integer :order_id, null: false
5
+ t.string :doc_code, null: false
6
+ t.date :doc_date, null: false
7
+ t.decimal :pre_tax_total, precision: 10, scale: 2
8
+ t.decimal :additional_tax_total, precision: 10, scale: 2
9
+
10
+ t.timestamps null: true
11
+ end
12
+
13
+ add_index :spree_avatax_sales_orders, :order_id
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ class CreateSpreeAvataxSalesInvoices < ActiveRecord::Migration
2
+ def change
3
+ create_table :spree_avatax_sales_invoices do |t|
4
+ t.integer :order_id, null: false
5
+ t.datetime :committed_at
6
+ t.string :doc_id, null: false
7
+ t.string :doc_code, null: false
8
+ t.date :doc_date, null: false
9
+ t.decimal :pre_tax_total, precision: 10, scale: 2
10
+ t.decimal :additional_tax_total, precision: 10, scale: 2
11
+
12
+ t.timestamps null: true
13
+ end
14
+
15
+ add_index :spree_avatax_sales_invoices, :order_id, unique: true
16
+ add_index :spree_avatax_sales_invoices, :doc_id, unique: true
17
+ add_index :spree_avatax_sales_invoices, :doc_code, unique: true
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ class GenerateUncommittedSalesInvoices < ActiveRecord::Migration
2
+ def up
3
+ scope = Spree::Order.
4
+ where(state: 'confirm').
5
+ joins('left join spree_avatax_sales_invoices on spree_orders.id = spree_avatax_sales_invoices.order_id').
6
+ where(:spree_avatax_sales_invoices => {id: nil}).
7
+ readonly(false)
8
+
9
+ say "Orders to migrate: #{scope.count}"
10
+
11
+ # batch size of 1 to avoid grabbing stale rows
12
+ scope.find_each(batch_size: 1) do |order|
13
+ say "generating an uncommitted sales invoice for order id=#{order.id} number=#{order.number} created_at=#{order.created_at.to_s(:db).inspect}"
14
+
15
+ begin
16
+ SpreeAvatax::SalesInvoice.generate(order)
17
+ rescue => e
18
+ say ""
19
+ say "************************ ERROR on Order #{order.number} ***************************"
20
+ say "ERROR: #{e.class} #{e.message}"
21
+ say e.backtrace.join("\n")
22
+ say "***********************************************************************************"
23
+ say ""
24
+ end
25
+ end
26
+ end
27
+
28
+ def down
29
+ # not reversible
30
+ end
31
+ end
@@ -0,0 +1,6 @@
1
+ class AddTransactionIdToSalesOrdersAndSalesInvoices < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_avatax_sales_orders, :transaction_id, :string
4
+ add_column :spree_avatax_sales_invoices, :transaction_id, :string
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class AddCanceledAtAndCancelTransactionIdToSalesInvoices < ActiveRecord::Migration
2
+ def change
3
+ add_column :spree_avatax_sales_invoices, :canceled_at, :datetime
4
+ add_column :spree_avatax_sales_invoices, :cancel_transaction_id, :string
5
+ end
6
+ end
@@ -0,0 +1,44 @@
1
+ class CreateSpreeAvataxShortShipReturnInvoices < ActiveRecord::Migration
2
+ def change
3
+ create_table :spree_avatax_short_ship_return_invoices do |t|
4
+ t.boolean :committed, null: false
5
+ t.string :doc_id, null: false
6
+ t.string :doc_code, null: false
7
+ t.date :doc_date, null: false
8
+
9
+ t.timestamps null: true
10
+ end
11
+
12
+ create_table :spree_avatax_short_ship_return_invoice_inventory_units do |t|
13
+ t.references :short_ship_return_invoice, null: false
14
+ t.references :inventory_unit, null: false
15
+ end
16
+
17
+ # The default index names for these are too long for sqlite/mysql/postgres
18
+
19
+ add_index(
20
+ :spree_avatax_short_ship_return_invoice_inventory_units,
21
+ :short_ship_return_invoice_id,
22
+ unique: true,
23
+ name: 'index_spree_avatax_short_ships_on_invoice_id',
24
+ )
25
+ add_index(
26
+ :spree_avatax_short_ship_return_invoice_inventory_units,
27
+ :inventory_unit_id,
28
+ unique: true,
29
+ name: 'index_spree_avatax_short_ships_on_unit_id',
30
+ )
31
+ add_index(
32
+ :spree_avatax_short_ship_return_invoices,
33
+ :doc_id,
34
+ unique: true,
35
+ name: 'index_spree_avatax_short_invoices_on_doc_id',
36
+ )
37
+ add_index(
38
+ :spree_avatax_short_ship_return_invoices,
39
+ :doc_code,
40
+ unique: true,
41
+ name: 'index_spree_avatax_short_invoices_on_doc_code',
42
+ )
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ class FixAvataxShortShipIndex < ActiveRecord::Migration
2
+ def up
3
+ remove_index(
4
+ :spree_avatax_short_ship_return_invoice_inventory_units,
5
+ name: 'index_spree_avatax_short_ships_on_invoice_id',
6
+ )
7
+
8
+ # Was previously set as unique, which was not correct -- A short ship return
9
+ # invoice may have multiple units associated with it
10
+ add_index(
11
+ :spree_avatax_short_ship_return_invoice_inventory_units,
12
+ :short_ship_return_invoice_id,
13
+ name: 'index_spree_avatax_short_ships_on_invoice_id',
14
+ )
15
+ end
16
+
17
+ def down
18
+ remove_index(
19
+ :spree_avatax_short_ship_return_invoice_inventory_units,
20
+ name: 'index_spree_avatax_short_ships_on_invoice_id',
21
+ )
22
+
23
+ add_index(
24
+ :spree_avatax_short_ship_return_invoice_inventory_units,
25
+ :short_ship_return_invoice_id,
26
+ unique: true,
27
+ name: 'index_spree_avatax_short_ships_on_invoice_id',
28
+ )
29
+ end
30
+ end
@@ -0,0 +1,31 @@
1
+ module SolidusAvatax
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+
5
+ class_option :auto_run_migrations, type: :boolean, default: false
6
+
7
+ def self.source_paths
8
+ paths = self.superclass.source_paths
9
+ paths << File.expand_path('../templates', __FILE__)
10
+ paths.flatten
11
+ end
12
+
13
+ def add_initializer
14
+ template 'config/initializers/avatax.rb', 'config/initializers/avatax.rb'
15
+ end
16
+
17
+ def add_migrations
18
+ run 'bundle exec rake railties:install:migrations FROM=solidus_avatax'
19
+ end
20
+
21
+ def run_migrations
22
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask 'Would you like to run the migrations now? [Y/n]')
23
+ if run_migrations
24
+ run 'bundle exec rake db:migrate'
25
+ else
26
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,18 @@
1
+ # Avatax Setup
2
+ SpreeAvatax::Config.username = ENV["AVATAX_USERNAME"]
3
+ SpreeAvatax::Config.password = ENV["AVATAX_PASSWORD"]
4
+ SpreeAvatax::Config.company_code = ENV["AVATAX_COMPANY_CODE"]
5
+ if Rails.env.production?
6
+ SpreeAvatax::Config.service_url = "https://avatax.avalara.net"
7
+ else
8
+ SpreeAvatax::Config.service_url = "https://development.avalara.net"
9
+ end
10
+
11
+ # Use Avatax to compute return invoice tax amounts
12
+ Spree::Reimbursement.reimbursement_tax_calculator = lambda do |reimbursement|
13
+ SpreeAvatax::ReturnInvoice.generate(reimbursement)
14
+ end
15
+ # Finalize the avatax return invoice after a reimubursement completes successfully
16
+ Spree::Reimbursement.reimbursement_success_hooks << lambda do |reimbursement|
17
+ SpreeAvatax::ReturnInvoice.finalize(reimbursement)
18
+ end
@@ -0,0 +1,4 @@
1
+ require 'spree_core'
2
+ require "spree_avatax/config"
3
+ require 'spree_avatax/engine'
4
+ require 'avatax_taxservice'
@@ -0,0 +1,16 @@
1
+ module SpreeAvatax
2
+ class Config
3
+ class << self
4
+ attr_accessor :username
5
+ attr_accessor :password
6
+ attr_accessor :company_code
7
+ attr_accessor :service_url
8
+ # These error handlers should be objects that respond to "call" and accept an order and an
9
+ # exception as arguments. This allows you to ignore certain errors or handle them in
10
+ # specific ways.
11
+ attr_accessor :sales_invoice_generate_error_handler
12
+ attr_accessor :sales_invoice_commit_error_handler
13
+ attr_accessor :sales_invoice_cancel_error_handler
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ module SpreeAvatax
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ isolate_namespace SpreeAvatax
5
+ engine_name 'spree_avatax'
6
+
7
+ initializer 'spree_avatax.register.calculators', after: 'spree.register.calculators' do |app|
8
+ app.config.spree.calculators.tax_rates << SpreeAvatax::Calculator
9
+ app.config.spree.calculators.shipping_methods << SpreeAvatax::Calculator
10
+ end
11
+
12
+ initializer 'spree_avatax.promo.register.promotion.calculators', after: 'spree.promo.register.promotion.calculators' do |app|
13
+ app.config.spree.calculators.promotion_actions_create_adjustments << SpreeAvatax::Calculator
14
+ end
15
+
16
+ # use rspec for tests
17
+ config.generators do |g|
18
+ g.test_framework :rspec
19
+ end
20
+
21
+ def self.activate
22
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/**/*_decorator*.rb')) do |c|
23
+ Rails.configuration.cache_classes ? require(c) : load(c)
24
+ end
25
+ end
26
+
27
+ config.to_prepare &method(:activate).to_proc
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ FactoryGirl.define do
2
+ sequence(:doc_id) { |n| n.to_s.rjust(16, '0') }
3
+
4
+ factory :avatax_tax_calculator, class: SpreeAvatax::Calculator do
5
+ end
6
+
7
+ factory :avatax_sales_invoice, class: SpreeAvatax::SalesInvoice do
8
+ association :order, factory: :shipped_order
9
+ doc_id { generate(:doc_id) }
10
+ doc_code { order.number }
11
+ doc_date { order.completed_at.try(:to_date) || 1.day.ago }
12
+ pre_tax_total { order.line_items.sum(:pre_tax_amount) }
13
+ additional_tax_total { order.line_items.sum(:additional_tax_total) }
14
+ end
15
+
16
+ factory :return_invoice, class: SpreeAvatax::ReturnInvoice do
17
+ association :reimbursement
18
+ committed false
19
+ doc_id { generate(:doc_id) }
20
+ doc_code { reimbursement.number }
21
+ doc_date { reimbursement.order.avatax_invoice_at.try(:to_date) || reimbursement.order.completed_at.to_date }
22
+ pre_tax_total { reimbursement.return_items.sum(:pre_tax_amount) }
23
+ additional_tax_total { reimbursement.return_items.sum(:additional_tax_total) }
24
+ end
25
+ end
@@ -0,0 +1,119 @@
1
+ require 'pp'
2
+
3
+ namespace :spree_avatax do
4
+
5
+ desc <<-DESC
6
+ Backfill all existing SalesInvoice records with Commit=true.
7
+ The "start_id" and "end_id" parameters are order ids and are optional.
8
+ Use this if you used spree_avatax prior to Commit=true being passed to Avatax.
9
+ i.e. before this: https://github.com/bonobos/spree_avatax/pull/25
10
+ DESC
11
+ task(:commit_sales_invoices, [:start_id, :end_id] => :environment) do |t, args|
12
+
13
+ puts 'Committing SalesInvoices for orders with an "avatax_invoice_at" value'
14
+
15
+ # CONFIGURE OPTIONS
16
+
17
+ scope = Spree::Order.where.not(avatax_invoice_at: nil)
18
+ batch_options = {}
19
+
20
+ if args[:start_id]
21
+ batch_options[:start] = args[:start_id]
22
+ puts "Beginning at order id #{args[:start_id]}"
23
+ end
24
+
25
+ if args[:end_id]
26
+ scope = scope.where('spree_orders.id <= ?', args[:end_id])
27
+ puts "Will stop at order id #{args[:end_id]}"
28
+ end
29
+
30
+ # PREP TO ITERATE OVER ORDERS
31
+
32
+ tax_svc = AvaTax::TaxService.new({
33
+ username: SpreeAvatax::Config.username,
34
+ password: SpreeAvatax::Config.password,
35
+ service_url: SpreeAvatax::Config.service_url,
36
+ clientname: 'Spree::Avatax',
37
+ })
38
+
39
+ handle_result_errors = ->(result, order, request_method) do
40
+ puts
41
+ puts "** Error on order id=#{order.id} number=#{order.number} **"
42
+ puts "Avatax #{request_method} result code: #{result[:result_code]}"
43
+ puts 'messages:'
44
+ pp result[:messages]
45
+ puts
46
+ end
47
+
48
+ # ITERATE OVER ORDERS
49
+
50
+ error_count = 0
51
+ update_count = 0
52
+ skip_count = 0
53
+
54
+ scope.find_each(batch_options) do |order|
55
+ puts "processing order id=#{order.id} number=#{order.number}"
56
+
57
+ # CHECK CURRENT AVATAX STATUS
58
+
59
+ history_request = {
60
+ companycode: SpreeAvatax::Config.company_code,
61
+ doctype: 'SalesInvoice',
62
+ doccode: order.number,
63
+ detaillevel: 'Tax',
64
+ }
65
+
66
+ history_result = tax_svc.gettaxhistory(history_request)
67
+
68
+ if history_result[:result_code] != 'Success'
69
+ error_count += 1
70
+ handle_result_errors.call(history_result, order, :gettaxhistory)
71
+ next
72
+ end
73
+
74
+ if history_result[:get_tax_result][:doc_status] == 'Committed'
75
+ skip_count += 1
76
+ next # already committed. skip it.
77
+ end
78
+
79
+ # POST AND COMMIT
80
+
81
+ post_request = {
82
+ companycode: SpreeAvatax::Config.company_code,
83
+ doctype: 'SalesInvoice',
84
+ doccode: order.number,
85
+
86
+ commit: true,
87
+
88
+ docdate: order.avatax_invoice_at.to_date,
89
+ # sanity checks for avatax
90
+ totalamount: history_result[:get_tax_result][:total_amount],
91
+ totaltax: history_result[:get_tax_result][:total_tax],
92
+ }
93
+
94
+ post_result = tax_svc.posttax(post_request)
95
+
96
+ if post_result[:result_code] != 'Success'
97
+ error_count += 1
98
+ handle_result_errors.call(post_result, order, :posttax)
99
+ next
100
+ end
101
+
102
+ update_count += 1
103
+ end
104
+
105
+ # PRINT RESULTS
106
+
107
+ puts
108
+ puts "Update complete."
109
+ puts "Update count: #{update_count}. Skip count: #{skip_count}. Error count: #{error_count}."
110
+
111
+ if error_count > 0
112
+ puts
113
+ puts '***********************************************'
114
+ puts "* WARNING: #{error_count} errors encountered"
115
+ puts '***********************************************'
116
+ end
117
+ end
118
+
119
+ end