solidus_avatax 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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