billing 0.0.4d → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/billing/accounts.js +2 -0
  3. data/app/assets/javascripts/billing/application.js +13 -0
  4. data/app/assets/javascripts/billing/charges.js +2 -0
  5. data/app/assets/javascripts/billing/modifiers.js +2 -0
  6. data/app/assets/stylesheets/billing/accounts.css +4 -0
  7. data/app/assets/stylesheets/billing/application.css +15 -0
  8. data/app/assets/stylesheets/billing/charges.css +4 -0
  9. data/app/assets/stylesheets/billing/modifiers.css +4 -0
  10. data/app/assets/stylesheets/billing/payments.css +4 -0
  11. data/app/controllers/billing/accounts_controller.rb +29 -0
  12. data/app/controllers/billing/application_controller.rb +22 -0
  13. data/app/controllers/billing/charges_controller.rb +29 -0
  14. data/app/controllers/billing/modifiers_controller.rb +29 -0
  15. data/app/controllers/billing/payments_controller.rb +29 -0
  16. data/app/helpers/billing/accounts_helper.rb +4 -0
  17. data/app/helpers/billing/application_helper.rb +4 -0
  18. data/app/helpers/billing/charges_helper.rb +4 -0
  19. data/app/helpers/billing/modifiers_helper.rb +4 -0
  20. data/app/helpers/billing/payments_helper.rb +4 -0
  21. data/app/models/billing/{bill.rb → account.rb} +30 -37
  22. data/app/models/billing/charge.rb +11 -25
  23. data/app/models/billing/modifier.rb +5 -5
  24. data/app/models/billing/origin.rb +2 -7
  25. data/app/models/billing/payment.rb +11 -8
  26. data/app/models/billing/payment_with_type.rb +1 -1
  27. data/app/models/billing/report.rb +12 -23
  28. data/app/models/concerns/billing/{bill_item.rb → account_item.rb} +6 -5
  29. data/app/views/billing/accounts/index.html.erb +7 -0
  30. data/app/views/billing/accounts/new.html.erb +8 -0
  31. data/app/views/billing/accounts/show.html.erb +29 -0
  32. data/app/views/billing/application/index.html.erb +3 -0
  33. data/app/views/billing/charges/new.html.erb +12 -0
  34. data/app/views/billing/modifiers/new.html.erb +18 -0
  35. data/app/views/billing/payments/new.html.erb +11 -0
  36. data/app/views/layouts/billing/application.html.erb +14 -0
  37. data/config/routes.rb +6 -1
  38. data/lib/billing.rb +2 -0
  39. data/lib/billing/billable.rb +3 -3
  40. data/lib/billing/engine.rb +0 -3
  41. data/lib/billing/mapping.rb +32 -0
  42. data/lib/billing/routes.rb +16 -0
  43. data/lib/billing/version.rb +1 -1
  44. data/test/controllers/billing/accounts_controller_test.rb +19 -0
  45. data/test/controllers/billing/charges_controller_test.rb +16 -0
  46. data/test/controllers/billing/modifiers_controller_test.rb +16 -0
  47. data/test/controllers/billing/payments_controller_test.rb +16 -0
  48. data/test/dummy/config/routes.rb +3 -1
  49. data/test/dummy/db/development.sqlite3 +0 -0
  50. data/test/dummy/db/schema.rb +17 -23
  51. data/test/dummy/db/test.sqlite3 +0 -0
  52. data/test/dummy/log/development.log +0 -8144
  53. data/test/dummy/log/test.log +0 -64325
  54. data/test/fixtures/billing/{bills.yml → accounts.yml} +0 -0
  55. data/test/fixtures/billing/charges.yml +2 -2
  56. data/test/fixtures/billing/modifiers.yml +2 -2
  57. data/test/fixtures/billing/payments.yml +2 -2
  58. data/test/helpers/billing/accounts_helper_test.rb +6 -0
  59. data/test/helpers/billing/charges_helper_test.rb +6 -0
  60. data/test/helpers/billing/modifiers_helper_test.rb +6 -0
  61. data/test/helpers/billing/payments_helper_test.rb +6 -0
  62. data/test/integration/navigation_test.rb +10 -0
  63. data/test/models/billing/account_test.rb +56 -0
  64. data/test/models/billing/charge_test.rb +6 -13
  65. data/test/models/billing/modifier_test.rb +2 -2
  66. data/test/models/billing/payment_test.rb +12 -12
  67. metadata +58 -20
  68. data/app/models/billing/room_transfer.rb +0 -4
  69. data/app/models/concerns/billing/bill_text_parser.rb +0 -18
  70. data/config/initializers/money.rb +0 -67
  71. data/db/migrate/20140803073707_rename_billing_account_to_bill.rb +0 -8
  72. data/db/migrate/20140804065726_add_qty_to_billing_charge.rb +0 -5
  73. data/db/migrate/20140804070517_add_tax_ratio_to_billing_charge.rb +0 -5
  74. data/db/migrate/20141001185321_add_transfer_device_to_billing_origin.rb +0 -5
  75. data/db/migrate/20141027235427_add_f_amount_to_billing_report.rb +0 -7
  76. data/lib/collection_proxy_wild.rb +0 -9
  77. data/test/models/billing/bill_test.rb +0 -76
@@ -1,13 +1,13 @@
1
1
  module Billing
2
2
  class Modifier < ActiveRecord::Base
3
- include BillItem
4
- belongs_to :bill, inverse_of: :modifiers, validate: true
3
+ include AccountItem
4
+ belongs_to :account, inverse_of: :modifiers, validate: true
5
5
  belongs_to :charge, inverse_of: :modifier
6
6
  monetize :fixed_value_cents
7
7
 
8
8
  validate :percent_or_value
9
- validates_uniqueness_of :charge, scope: :bill_id, allow_nil: true
10
- validates_uniqueness_of :bill, scope: :charge_id
9
+ validates_uniqueness_of :charge, scope: :account_id, allow_nil: true
10
+ validates_uniqueness_of :account, scope: :charge_id
11
11
 
12
12
  private
13
13
  def percent_or_value
@@ -16,7 +16,7 @@ module Billing
16
16
  end
17
17
 
18
18
  class << self
19
- def wild_args(*args)
19
+ def args(*args)
20
20
  case when args.blank? || args.first.kind_of?(Hash) then
21
21
  {}.merge(*args)
22
22
  when args.first.kind_of?(String) then
@@ -1,11 +1,10 @@
1
1
  module Billing
2
2
  class Origin < ActiveRecord::Base
3
- has_many :bills, inverse_of: :origin
3
+ has_many :accounts, inverse_of: :origin
4
4
  has_many :charges, inverse_of: :origin
5
- has_many :payments, through: :bills
5
+ has_many :payments, through: :accounts
6
6
  if defined? Extface
7
7
  belongs_to :fiscal_device, ->(o) { where( extfaceable_id: o.master_id ) }, class_name: 'Extface::Device'
8
- belongs_to :transfer_device, ->(o) { where( extfaceable_id: o.master_id ) }, class_name: 'Extface::Device'
9
8
  end
10
9
 
11
10
  validates_presence_of :name
@@ -14,9 +13,5 @@ module Billing
14
13
  def external_payment?
15
14
  payment_model != Payment::PAYMENT_WITH_TYPE
16
15
  end
17
-
18
- def room_transfer?
19
- payment_model == Payment::ROOM_TRANSFER
20
- end
21
16
  end
22
17
  end
@@ -3,31 +3,34 @@ module Billing
3
3
  PAYMENT_WITH_TYPE = 'Billing::PaymentWithType'.freeze
4
4
  PAYMENT_EXTERNAL = 'Billing::PaymentExternal'.freeze
5
5
  PAYPAL_EXPRESS = 'Billing::PaypalExpress'.freeze
6
- ROOM_TRANSFER = 'Billing::RoomTransfer'.freeze
7
- PAYMENT_MODELS = [PAYMENT_WITH_TYPE, PAYMENT_EXTERNAL, PAYPAL_EXPRESS, ROOM_TRANSFER].freeze
6
+ PAYMENT_MODELS = [PAYMENT_WITH_TYPE, PAYMENT_EXTERNAL, PAYPAL_EXPRESS].freeze
8
7
 
9
- include BillItem
8
+ include AccountItem
10
9
 
11
10
  attr_writer :origin
12
11
  attr_accessor :origin_id
13
12
  monetize :value_cents
14
13
 
15
- belongs_to :bill, inverse_of: :payments, validate: true
14
+ belongs_to :account, inverse_of: :payments, validate: true
16
15
 
17
16
  scope :in_period, lambda {|from, to| where(created_at: from..to) }
18
- scope :for_report, -> { joins(:bill).where(billing_bills: { balance_cents: 0 ,report_id: nil }) }
17
+ scope :for_report, -> { joins(:account).where(billing_accounts: { balance_cents: 0 ,report_id: nil }) }
19
18
 
20
19
  if defined? Extface
21
20
  belongs_to :extface_job, class_name: 'Extface::Job'
22
21
  end
23
22
 
24
- delegate :billable, to: :bill
23
+ delegate :billable, to: :account
25
24
 
26
25
  validates_numericality_of :value, greater_than_or_equal_to: 0
27
26
  validates :type, inclusion: { in: PAYMENT_MODELS }
28
27
 
29
28
  after_initialize on: :create do
30
- self.value = -bill.try(:balance) if value.zero?
29
+ self.value = -account.try(:balance) if value.zero?
30
+ end
31
+
32
+ before_validation do
33
+ account.origin = origin unless account.origin and account.payments.many?
31
34
  end
32
35
 
33
36
  def fiscal?; false; end
@@ -40,7 +43,7 @@ module Billing
40
43
 
41
44
  private
42
45
  class << self
43
- def wild_args(*args)
46
+ def args(*args)
44
47
  h = {}
45
48
  case when args.blank? || args.first.kind_of?(Hash) then
46
49
  args.blank? ? h : h.merge(*args)
@@ -22,7 +22,7 @@ module Billing
22
22
  if pt = billable.try(:default_payment_type)
23
23
  pt
24
24
  else
25
- bill.payment_types.try(:first) unless bill.payment_types.many?
25
+ account.payment_types.try(:first) unless account.payment_types.many?
26
26
  end
27
27
  end
28
28
 
@@ -3,31 +3,26 @@ module Billing
3
3
  FISCAL_X_REPORT = 'x_report'.freeze
4
4
  FISCAL_Z_REPORT = 'z_report'.freeze
5
5
  FISCAL_PERIOD_REPORT = 'period_report'.freeze
6
- FISCAL_PAYED_RECVD = 'payed_recvd'.freeze
7
- F_OPERATIONS = [FISCAL_X_REPORT, FISCAL_Z_REPORT, FISCAL_PERIOD_REPORT, FISCAL_PAYED_RECVD].freeze
6
+ F_OPERATIONS = [FISCAL_X_REPORT, FISCAL_Z_REPORT, FISCAL_PERIOD_REPORT].freeze
8
7
 
9
8
  has_paper_trail class_name: 'Billing::Version'
10
9
  belongs_to :origin, inverse_of: :reports
11
- has_many :bills, inverse_of: :report, autosave: true
10
+ has_many :accounts, inverse_of: :report, autosave: true
12
11
  belongs_to :extface_job, class_name: 'Extface::Job'
13
- has_many :payments, through: :bills
12
+ has_many :payments, through: :accounts
14
13
 
15
14
  monetize :payments_sum_cents
16
15
  monetize :payments_cash_cents
17
16
  monetize :payments_fiscal_cents
18
- monetize :f_amount_cents
19
17
 
20
18
  validates_presence_of :origin
21
- validates_absence_of :partially_paid_bills?
19
+ validates_absence_of :partially_paid_accounts?
22
20
  validates :f_operation, inclusion: { in: F_OPERATIONS }, allow_nil: true
23
21
  validates_presence_of :f_period_from, :f_period_to, if: :fiscal_period_report?
24
- validates_presence_of :f_amount, if: :fiscal_payed_recvd?
25
22
 
26
- before_validation :set_report_to_bills
23
+ before_validation :set_report_to_accounts
27
24
  before_create :update_summary
28
25
 
29
- scope :in_period, lambda {|from, to| where(created_at: from..to) }
30
-
31
26
 
32
27
  def fiscalization
33
28
  if origin.fiscal_device.present?
@@ -42,18 +37,14 @@ module Billing
42
37
  f_operation == FISCAL_PERIOD_REPORT
43
38
  end
44
39
 
45
- def fiscal_payed_recvd?
46
- f_operation == FISCAL_PAYED_RECVD
47
- end
48
-
49
- def set_report_to_bills
50
- self.bills << origin.bills.for_report if zeroing?
40
+ def set_report_to_accounts
41
+ self.accounts << origin.accounts.select(&:paid?) if zeroing?
51
42
  end
52
43
 
53
44
  def update_summary
54
- self.payments_sum = bills.to_a.sum(Money.new(0, 'USD'), &:payments_sum)
55
- self.payments_cash = bills.collect(&:payments).flatten.select{ |p| p.try(:cash?) }.sum(Money.new(0, 'USD'), &:value)
56
- self.payments_fiscal = bills.collect(&:payments).flatten.select{ |p| p.try(:fiscal?) }.sum(Money.new(0, 'USD'), &:value)
45
+ self.payments_sum = accounts.to_a.sum(Money.new(0, 'USD'), &:payments_sum)
46
+ self.payments_cash = payments.select{ |p| p.try(:cash?) }.sum(Money.new(0, 'USD'), &:value)
47
+ self.payments_fiscal = payments.select{ |p| p.try(:fiscal?) }.sum(Money.new(0, 'USD'), &:value)
57
48
  perform_fiscal_job
58
49
  end
59
50
 
@@ -65,13 +56,11 @@ module Billing
65
56
  self.extface_job = origin.fiscal_device.driver.x_report_session
66
57
  when FISCAL_PERIOD_REPORT then
67
58
  p "period"
68
- when FISCAL_PAYED_RECVD then
69
- self.extface_job = origin.fiscal_device.driver.payed_recv_account(f_amount.to_f)
70
59
  end
71
60
  end
72
61
 
73
- def partially_paid_bills?
74
- bills.partially_paid.any?
62
+ def partially_paid_accounts?
63
+ accounts.partially_paid.any?
75
64
  end
76
65
  end
77
66
  end
@@ -1,16 +1,17 @@
1
1
  module Billing
2
- module BillItem
2
+ module AccountItem
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  included do
6
6
  acts_as_paranoid if respond_to?(:acts_as_paranoid)
7
7
  has_paper_trail class_name: 'Billing::Version' if respond_to?(:has_paper_trail)
8
8
 
9
- delegate :save, to: :bill, prefix: :bill
10
- delegate :origins, :payment_types, to: :bill
9
+ delegate :save, to: :account, prefix: :account
10
+ delegate :origins, :payment_types, to: :account
11
11
 
12
- after_save :bill_save
13
- after_destroy :bill_save
12
+ after_save :account_save
13
+
14
+ validates_presence_of :account
14
15
  end
15
16
 
16
17
  end
@@ -0,0 +1,7 @@
1
+ <h1>Accounts#index</h1>
2
+ <p>Find me in app/views/billing/accounts/index.html.erb</p>
3
+ <%= link_to :new, new_account_path %>
4
+ <hr />
5
+ <% @accounts.each do |account| %>
6
+ <%= link_to account.inspect, account %>
7
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <h1>
2
+ New account
3
+ <small><%= billable %></small>
4
+ </h1>
5
+ <%= form_for @account do |f| %>
6
+ <%= @account.inspect %>
7
+ <%= f.submit %>
8
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <%= @account.inspect %>
2
+ <h2>
3
+ total: <%= @account.total %>
4
+ <br />
5
+ balance: <%= @account.balance %>
6
+ </h2>
7
+ <hr />
8
+ Charges:<br />
9
+ <% @account.charges.each do |charge| %>
10
+ <%= charge.inspect %>
11
+ <% end %>
12
+ <%= link_to :charge, [:new, @account, :charge] %>
13
+ <hr />
14
+ Modifiers:<br />
15
+ <% @account.modifiers.each do |modifier| %>
16
+ <%= modifier.inspect %>
17
+ <% end %>
18
+ <%= link_to :modifier, [:new, @account, :modifier] %>
19
+ <div>
20
+ <% @account.modifier_items.each do |mi| %>
21
+ <%= mi.inspect %><br />
22
+ <% end %>
23
+ </div>
24
+ <hr />
25
+ Payments:<br />
26
+ <% @account.payments.each do |payment| %>
27
+ <%= payment.inspect %>
28
+ <% end %>
29
+ <%= link_to :pay, [:new, @account, :payment] %>
@@ -0,0 +1,3 @@
1
+ <%= link_to :accounts, accounts_path %>
2
+ <br />
3
+ <%= link_to :new_account, new_account_path %>
@@ -0,0 +1,12 @@
1
+ <h1>
2
+ New charge
3
+ <small><%= @account %></small>
4
+ </h1>
5
+ <%= form_for [@account, @charge] do |f| %>
6
+ <%= @charge.inspect %>
7
+ <br />
8
+ <%= f.number_field :price, step: 0.01 %>
9
+ <br />
10
+ <%= f.submit %>
11
+ <% end %>
12
+
@@ -0,0 +1,18 @@
1
+ <h1>
2
+ New Modifier
3
+ <small><%= @account %></small>
4
+ </h1>
5
+ <div>
6
+ <%= @modifier.errors.full_messages.join(', ') %>
7
+ </div>
8
+ <%= form_for [@account, @modifier] do |f| %>
9
+ <%= @modifier.inspect %>
10
+ <br />
11
+ percent ratio <%= f.number_field :percent_ratio, step: 0.001 %>
12
+ <br />
13
+ fixed value <%= f.number_field :fixed_value, step: 0.01 %>
14
+ <br />
15
+ <%= f.select :charge_id, @account.charges.collect(&:id), { include_blank: true } %>
16
+ <br />
17
+ <%= f.submit %>
18
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <h1>
2
+ New payment
3
+ <small><%= @account %></small>
4
+ </h1>
5
+ <%= form_for [@account, @payment] do |f| %>
6
+ <%= @payment.inspect %>
7
+ <br />
8
+ <%= f.number_field :value, step: 0.01 %>
9
+ <br />
10
+ <%= f.submit %>
11
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Billing</title>
5
+ <%= stylesheet_link_tag "billing/application", media: "all" %>
6
+ <%= javascript_include_tag "billing/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
data/config/routes.rb CHANGED
@@ -1,3 +1,8 @@
1
1
  Billing::Engine.routes.draw do
2
-
2
+ resources :accounts do
3
+ resources :charges
4
+ resources :modifiers
5
+ resources :payments
6
+ end
7
+ root "application#index"
3
8
  end
data/lib/billing.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  require "billing/engine"
2
2
  require "billing/billable"
3
+ require "billing/mapping"
4
+ require "billing/routes"
3
5
 
4
6
  module Billing
5
7
  mattr_reader :mappings
@@ -9,10 +9,10 @@ module Billing
9
9
  module ClassMethods
10
10
  def has_billing(options={})
11
11
  payment_types_scope = options.delete(:payment_types)
12
- has_many :billing_bills, options.merge(as: :billable).reverse_merge(class_name: 'Billing::Bill')
13
- provide_billing_items(:billing_bills)
12
+ has_many :billing_accounts, options.merge(as: :billable).reverse_merge(class_name: 'Billing::Account')
13
+ provide_billing_items(:billing_accounts)
14
14
  if options[:as]
15
- has_many options[:as], options.merge(as: :billable).reverse_merge(class_name: 'Billing::Bill')
15
+ has_many options[:as], options.merge(as: :billable).reverse_merge(class_name: 'Billing::Account')
16
16
  provide_billing_items(options[:as])
17
17
  end
18
18
  if payment_types_scope.present?
@@ -1,8 +1,5 @@
1
1
  require 'money-rails'
2
2
  require 'paper_trail'
3
-
4
- require 'collection_proxy_wild'
5
-
6
3
  module Billing
7
4
  class Engine < ::Rails::Engine
8
5
  isolate_namespace Billing
@@ -0,0 +1,32 @@
1
+ module Billing
2
+ class Mapping
3
+ attr_reader :name, :i_klass, :i_param, :i_find_key, :i_extra_module
4
+ def initialize(resource, options)
5
+ @name = (options[:as] || resource).to_s
6
+
7
+ @i_klass = (options[:billable_type] || name.to_s.classify).to_s.constantize
8
+
9
+ @i_param = options[:billable_param] || "#{name.singularize}_id" #default #{resource}_id
10
+
11
+ # key to find interfaceable in controller, when
12
+ # :uuid then find_by! :uuid => params[:uuid]
13
+ # :shop_uuid then find_by! :uuid => params[:shop_uuid]
14
+ # :shop_id then find_by! :id => params[:shop_id]
15
+ @i_find_key = @i_param[/^(#{resource}_|)(\w+)/,2]
16
+ # FIXME not before schema load
17
+ #raise "#{@i_klass.name} has no method #{@i_find_key}" unless @i_klass.new.respond_to? @i_find_key
18
+ #raise "Did you forget to add 'has_extface_devices' in #{@i_klass.name} ?" unless @i_klass.new.respond_to? :extface_devices
19
+ @i_extra_module = options[:controller_include].to_s.constantize if options[:controller_include].present?
20
+ end
21
+
22
+ def mount_point
23
+ "#{name}_billing"
24
+ end
25
+
26
+ class << self
27
+ def find(fullpath)
28
+ Billing.mappings[fullpath[%r{/(\w+)_billing\/}, 1]]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ def billing_for(resource, options = {})
4
+ mapping = Billing.add_mapping(resource, options)
5
+ mount Billing::Engine, at: mapping.mount_point, as: [options[:as],:billing].compact.join('_')
6
+ get "#{mapping.mount_point}/accounts", to: "billing/accounts#index", as: options[:as] ? options[:as].to_s.pluralize.intern : :billing_accounts
7
+ get "#{mapping.mount_point}/accounts/:account_id", to: "billing/accounts#show", as: options[:as] ? options[:as].to_s.singularize.intern : :billing_account
8
+ get "#{mapping.mount_point}/accounts/:account_id/charges", to: "billing/charges#index", as: options[:as] ? "#{options[:as].to_s.singularize.intern}_charges" : :billing_account_charges
9
+ get "#{mapping.mount_point}/accounts/:account_id/charges/new", to: "billing/charges#new", as: options[:as] ? "#{options[:as].to_s.singularize.intern}_new_charge" : :billing_account_new_charge
10
+ get "#{mapping.mount_point}/accounts/:account_id/charges/:charge_id", to: "billing/charges#show", as: options[:as] ? "#{options[:as].to_s.singularize.intern}_charge" : :billing_account_charge
11
+ get "#{mapping.mount_point}/accounts/:account_id/payments", to: "billing/payments#index", as: options[:as] ? "#{options[:as].to_s.singularize.intern}_payments" : :billing_account_payments
12
+ get "#{mapping.mount_point}/accounts/:account_id/payments/new", to: "billing/payments#new", as: options[:as] ? "#{options[:as].to_s.singularize.intern}_new_payment" : :billing_account_new_payment
13
+ get "#{mapping.mount_point}/accounts/:account_id/payments/:payment_id", to: "billing/payments#show", as: options[:as] ? "#{options[:as].to_s.singularize.intern}_payment" : :billing_account_payment
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module Billing
2
- VERSION = "0.0.4d"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ module Billing
4
+ class AccountsControllerTest < ActionController::TestCase
5
+ setup do
6
+ @account = billing_accounts(:one)
7
+ end
8
+
9
+ test "should get index" do
10
+ get :index
11
+ assert_response :success
12
+ end
13
+
14
+ test "new" do
15
+ get :new
16
+ assert_response :success
17
+ end
18
+ end
19
+ end