Class: TaxRate

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/tax_rate.rb

Overview

== Schema Information

Table name: tax_rates
Database name: primary

id :integer not null, primary key
country_iso :string not null
description :string(255)
effective_date :date not null
expiration_date :date
goods :decimal(6, 4)
services :decimal(6, 4)
shipping :decimal(6, 4)
short_name :string(255)
state_code :string
tax_type :string(3) not null
use_food :decimal(6, 4)
use_merch :decimal(6, 4)
sales_tax_credit_account_id :integer
sales_tax_payable_account_id :integer
use_tax_account_id :integer

Constant Summary

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Class Method Details

.description_for(tax_type, destination_country: nil) ⇒ Object



121
122
123
124
125
126
127
128
# File 'app/models/tax_rate.rb', line 121

def self.description_for(tax_type, destination_country: nil)
  d = TAXABLE_STATES.detect {|k,v| v[:tax_type] == tax_type}.try(:[], 1).try(:[], :description)
  if d.nil? && tax_type == 'vat'
    d = +"VAT"
    d << " #{destination_country.name}" if destination_country
  end
  d ||= 'Sales Tax'
end

.effective_nowActiveRecord::Relation<TaxRate>

A relation of TaxRates that are effective now. Active Record Scope

Returns:

  • (ActiveRecord::Relation<TaxRate>)

See Also:



60
# File 'app/models/tax_rate.rb', line 60

scope :effective_now, -> { effective_on(Date.current) }

.effective_onActiveRecord::Relation<TaxRate>

A relation of TaxRates that are effective on. Active Record Scope

Returns:

  • (ActiveRecord::Relation<TaxRate>)

See Also:



59
# File 'app/models/tax_rate.rb', line 59

scope :effective_on, ->(date) { where("effective_date <= :date and (expiration_date IS NULL OR expiration_date >= :date)", date: date) }

.ransackable_scopes(_auth_object = nil) ⇒ Object



113
114
115
# File 'app/models/tax_rate.rb', line 113

def self.ransackable_scopes(_auth_object = nil)
  %i[effective_on]
end

.rates_for_voucher_entryObject



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'app/models/tax_rate.rb', line 80

def self.rates_for_voucher_entry
  rates = []
  TaxRate.effective_on(Date.current).each do |tr|
    ["goods", "services", "shipping"].each do |rate_type|
      if tr.send(rate_type).present? and tr.send(rate_type) != 0
        if tr.state_code.present?
          rates << {"label" => "#{tr.tax_type.upcase} #{rate_type} #{tr.state_code}", "value" => "#{tr.tax_type.upcase}_#{rate_type}_#{tr.state_code}", "rate" => tr.send(rate_type)*100, "id" => tr.id, "tax_type" => "V"}
        else
          rates << {"label" => "#{tr.tax_type.upcase} #{rate_type} #{tr.country_iso}", "value" => "#{tr.tax_type.upcase}_#{rate_type}_#{tr.country_iso}", "rate" => tr.send(rate_type)*100, "id" => tr.id, "tax_type" => "V"}
        end
      end
    end
    ["use_food", "use_merch"].each do |rate_type|
      if tr.send(rate_type).present? and tr.send(rate_type) != 0
        if tr.state_code.present?
          rates << {"label" => "#{tr.tax_type.upcase} #{rate_type} #{tr.state_code}", "value" => "#{tr.tax_type.upcase}_#{rate_type}_#{tr.state_code}", "rate" => tr.send(rate_type)*100, "id" => tr.id, "tax_type" => "U"}
        else
          rates << {"label" => "#{tr.tax_type.upcase} #{rate_type} #{tr.country_iso}", "value" => "#{tr.tax_type.upcase}_#{rate_type}_#{tr.country_iso}", "rate" => tr.send(rate_type)*100, "id" => tr.id, "tax_type" => "U"}
        end
      end
    end
  end
  return rates
end

.sales_taxActiveRecord::Relation<TaxRate>

A relation of TaxRates that are sales tax. Active Record Scope

Returns:

  • (ActiveRecord::Relation<TaxRate>)

See Also:



63
# File 'app/models/tax_rate.rb', line 63

scope :sales_tax, -> { where("sales_tax_payable_account_id is not null") }

.sales_tax_creditActiveRecord::Relation<TaxRate>

A relation of TaxRates that are sales tax credit. Active Record Scope

Returns:

  • (ActiveRecord::Relation<TaxRate>)

See Also:



61
# File 'app/models/tax_rate.rb', line 61

scope :sales_tax_credit, -> { where("sales_tax_credit_account_id is not null") }

.tax_types_for_selectObject



117
118
119
# File 'app/models/tax_rate.rb', line 117

def self.tax_types_for_select
  TAXABLE_STATES.collect { |_k, v| v[:tax_type].to_s }.uniq + ['vat']
end

.taxjar_summary_rates(country_code: nil) ⇒ Object

Returns an array of tax rate for a specific country, this is designed for single tax rate
regardless of origin/destination, such as in canada
{
:country_code => "CA",
:country => "Canada",
:region_code => "NB",
:region => "New Brunswick",
:minimum_rate => {
:label => "GST",
:rate => 0.05
},
:average_rate => {
:label => "HST",
:rate => 0.15
}
}



147
148
149
150
151
152
153
# File 'app/models/tax_rate.rb', line 147

def self.taxjar_summary_rates(country_code: nil)
  rates = Api::Taxjar.client.summary_rates
  if country_code
    rates = rates.select{|h| h[:country_code] == country_code}
  end
  rates
end

.use_internal_ratesActiveRecord::Relation<TaxRate>

A relation of TaxRates that are use internal rates. Active Record Scope

Returns:

  • (ActiveRecord::Relation<TaxRate>)

See Also:



64
# File 'app/models/tax_rate.rb', line 64

scope :use_internal_rates, -> (states) { where(state_code: states) }

.use_taxActiveRecord::Relation<TaxRate>

A relation of TaxRates that are use tax. Active Record Scope

Returns:

  • (ActiveRecord::Relation<TaxRate>)

See Also:



62
# File 'app/models/tax_rate.rb', line 62

scope :use_tax, -> { where("use_tax_account_id is not null") }

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


76
77
78
# File 'app/models/tax_rate.rb', line 76

def active?
  effective_date <= Date.current and expiration_date > Date.current
end

#countryCountry

Returns:

See Also:



54
# File 'app/models/tax_rate.rb', line 54

belongs_to :country, foreign_key: 'country_iso', primary_key: 'iso', optional: true

#expiration_date_after_effective_dateObject (protected)



161
162
163
# File 'app/models/tax_rate.rb', line 161

def expiration_date_after_effective_date
  errors.add(:expiration_date, "cannot be before effective date") if expiration_date and effective_date and expiration_date < effective_date
end

#has_overlapping_tax_ratesObject (protected)



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'app/models/tax_rate.rb', line 165

def has_overlapping_tax_rates
  return unless effective_date && tax_type

  # Overlap requires the same country + state + tax_type. Without the
  # country_iso filter, different-country rates that share an empty
  # state_code (e.g. EU VAT) were wrongly flagged as conflicts.
  scope = TaxRate.where(country_iso: country_iso, state_code: state_code, tax_type: tax_type)
  scope = scope.where.not(id: id) if persisted?

  # Two date ranges overlap when each starts on or before the other ends.
  # A NULL expiration_date means "open-ended" / no upper bound, so both the
  # stored rate and the record being validated may be unbounded on the right.
  #   overlap ⇔ r.effective_date <= new.expiration AND new.effective_date <= r.expiration_date
  scope = scope.where('tax_rates.effective_date <= ?', expiration_date) if expiration_date
  scope = scope.where('tax_rates.expiration_date IS NULL OR tax_rates.expiration_date >= ?', effective_date)

  conflicting_ids = scope.pluck(:id)
  return if conflicting_ids.empty?

  errors.add(:base, "Existing tax rates with id: #{conflicting_ids.join(',')} in place for these values and with the same date coverage. Cannot save an overlapping tax rate.")
end

#resource_tax_ratesActiveRecord::Relation<ResourceTaxRate>

Returns:

See Also:



57
# File 'app/models/tax_rate.rb', line 57

has_many :resource_tax_rates

#sales_tax_credit_accountLedgerDetailAccount



52
# File 'app/models/tax_rate.rb', line 52

belongs_to :sales_tax_credit_account, class_name: "LedgerDetailAccount", optional: true

#sales_tax_payable_accountLedgerDetailAccount



53
# File 'app/models/tax_rate.rb', line 53

belongs_to :sales_tax_payable_account, class_name: "LedgerDetailAccount", optional: true

#stateState

Returns:

See Also:



50
# File 'app/models/tax_rate.rb', line 50

belongs_to :state, foreign_key: 'state_code', primary_key: 'code', optional: true

#tax_explanationObject



105
106
107
108
109
110
111
# File 'app/models/tax_rate.rb', line 105

def tax_explanation
  if  != nil
    "U"
  else
    "V"
  end
end

#tax_type_available_in_stateObject (protected)



157
158
159
# File 'app/models/tax_rate.rb', line 157

def tax_type_available_in_state
  errors.add(:tax_type, "Tax Type is not available in this state") if state_code != 'IL' and tax_type == 'il'
end

#use_tax_accountLedgerDetailAccount



51
# File 'app/models/tax_rate.rb', line 51

belongs_to :use_tax_account, class_name: "LedgerDetailAccount", optional: true

#voucher_itemsActiveRecord::Relation<VoucherItem>

Returns:

See Also:



56
# File 'app/models/tax_rate.rb', line 56

has_many :voucher_items