Skip to content

Commit

Permalink
Merge pull request #1953 from nebulab/mtylty/time-aware-tax-rates
Browse files Browse the repository at this point in the history
Add validity period for Spree::TaxRate
  • Loading branch information
kennyadsl authored Jun 12, 2017
2 parents b6fbb37 + 10d5260 commit aa21865
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 2 deletions.
14 changes: 14 additions & 0 deletions backend/app/views/spree/admin/tax_rates/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@
<%= f.check_box :show_rate_in_label %>
<%= f.label :show_rate_in_label, Spree.t(:show_rate_in_label) %>
</div>

<div class="date-range-filter field">
<%= label_tag :validity_period, Spree.t(:validity_period) %>
<%= admin_hint Spree.t(:validity_period), Spree.t(:validity_period, scope: [:hints, "spree/tax_rate"]) %>
<div class="date-range-fields input-group">
<%= f.text_field :starts_at, class: 'datepicker form-control datepicker-from', value: datepicker_field_value(f.object.starts_at), placeholder: Spree::TaxRate.human_attribute_name(:starts_at) %>

<span class="range-divider input-group-addon">
<i class="fa fa-arrow-right"></i>
</span>

<%= f.text_field :expires_at, class: 'datepicker form-control datepicker-to', value: datepicker_field_value(f.object.expires_at), placeholder: Spree::TaxRate.human_attribute_name(:expires_at) %>
</div>
</div>
</div>
</div>
</fieldset>
Expand Down
5 changes: 4 additions & 1 deletion backend/app/views/spree/admin/tax_rates/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
<col style="width: 15%">
<col style="width: 10%">
<col style="width: 10%">
<col style="width: 10%">
<col style="width: 5%">
<col style="width: 5%">
<col style="width: 10%">
<col style="width: 15%">
<col style="width: 15%">
Expand All @@ -33,6 +34,7 @@
<th><%= Spree::TaxRate.human_attribute_name(:amount) %></th>
<th><%= Spree::TaxRate.human_attribute_name(:included_in_price) %></th>
<th><%= Spree::TaxRate.human_attribute_name(:show_rate_in_label) %></th>
<th><%= Spree::TaxRate.human_attribute_name(:expires_at) %></th>
<th><%= Spree::Calculator.model_name.human %></th>
<th class="actions"></th>
</tr>
Expand All @@ -52,6 +54,7 @@
<td class="align-center"><%=tax_rate.amount %></td>
<td class="align-center"><%=tax_rate.included_in_price? ? Spree.t(:say_yes) : Spree.t(:say_no) %></td>
<td class="align-center"><%=tax_rate.show_rate_in_label? ? Spree.t(:say_yes) : Spree.t(:say_no) %></td>
<td class="align-center"><%=tax_rate.expires_at.to_date.to_s(:short_date) if tax_rate.expires_at %></td>
<td class="align-center"><%=tax_rate.calculator.to_s %></td>
<td class="actions">
<% if can?(:update, tax_rate) %>
Expand Down
2 changes: 2 additions & 0 deletions core/app/models/spree/calculator/default_tax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Calculator::DefaultTax < Calculator
# Orders created before Spree 2.1 had tax adjustments applied to the order, as a whole.
# Orders created with Spree 2.2 and after, have them applied to the line items individually.
def compute_order(order)
return 0 unless rate.active?
matched_line_items = order.line_items.select do |line_item|
rate.tax_categories.include?(line_item.tax_category)
end
Expand All @@ -23,6 +24,7 @@ def compute_order(order)

# When it comes to computing shipments or line items: same same.
def compute_item(item)
return 0 unless rate.active?
if rate.included_in_price
deduced_total_by_rate(item, rate)
else
Expand Down
5 changes: 5 additions & 0 deletions core/app/models/spree/tax_rate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ def compute_amount(item)
calculator.compute(item)
end

def active?
(starts_at.nil? || starts_at < Time.current) &&
(expires_at.nil? || expires_at > Time.current)
end

def adjustment_label(amount)
Spree.t(
translation_key(amount),
Expand Down
5 changes: 5 additions & 0 deletions core/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ en:
included_in_price: Included In Price
name: Name
show_rate_in_label: Show Rate In Label
starts_at: Start date
expires_at: Expiration date
spree/taxon:
description: Description
icon: Icon
Expand Down Expand Up @@ -1274,6 +1276,8 @@ en:
deleted: "Deleted Variant"
deleted_explanation: "This variant was deleted on %{date}."
deleted_explanation_with_replacement: "This variant was deleted on %{date}. It has since been replaced by another with the same SKU."
spree/tax_rate:
validity_period: "This determines the validity period within which the tax rate is valid and will be applied to eligible items. <br /> If no start date value is specified, the tax rate will be immediately available. <br /> If no expiration date value is specified, the tax rate will never expire"
failed_payment_attempts: Failed Payment Attempts
failure: Failure
filename: Filename
Expand Down Expand Up @@ -2034,6 +2038,7 @@ en:
is_too_large: is too large -- stock on hand cannot cover requested quantity!
must_be_int: must be an integer
must_be_non_negative: must be a non-negative value
validity_period: Validity Period
value: Value
variant: Variant
variant_placeholder: Choose a variant
Expand Down
6 changes: 6 additions & 0 deletions core/db/migrate/20170522143442_add_time_range_to_tax_rate.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddTimeRangeToTaxRate < ActiveRecord::Migration[5.0]
def change
add_column :spree_tax_rates, :starts_at, :datetime
add_column :spree_tax_rates, :expires_at, :datetime
end
end
73 changes: 72 additions & 1 deletion core/spec/models/spree/calculator/default_tax_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,13 @@
let!(:zone) { create(:zone, name: "Country Zone", default_tax: default_tax, countries: [tax_rate_country]) }
let(:tax_rate_country) { address.country }
let(:tax_category) { create(:tax_category) }
let!(:rate) { create(:tax_rate, tax_categories: [tax_category], amount: 0.05, included_in_price: included_in_price, zone: zone) }
let(:starts_at) { nil }
let(:expires_at) { nil }
let!(:rate) do
create(:tax_rate, tax_categories: [tax_category], amount: 0.05,
included_in_price: included_in_price, zone: zone,
starts_at: starts_at, expires_at: expires_at)
end
let(:included_in_price) { false }
let(:default_tax) { false }
subject(:calculator) { Spree::Calculator::DefaultTax.new(calculable: rate ) }
Expand All @@ -32,6 +38,15 @@
it "should be equal to the sum of the item totals * rate" do
expect(calculator.compute(order)).to eq(3)
end

context "when rate is not in its validity period" do
let(:starts_at) { 1.day.from_now }
let(:expires_at) { 2.days.from_now }

it "should be 0" do
expect(calculator.compute(order)).to eq(0)
end
end
end

context "when no line items match the tax category" do
Expand All @@ -52,6 +67,15 @@
expect(calculator.compute(order)).to eq(1.5)
end

context "when rate is not in its validity period" do
let(:starts_at) { 1.day.from_now }
let(:expires_at) { 2.days.from_now }

it "should be 0" do
expect(calculator.compute(order)).to eq(0)
end
end

context "correctly rounds to within two decimal places" do
let(:line_item_one_options) { { price: 10.333, quantity: 1 } }

Expand All @@ -73,6 +97,15 @@
expect(calculator.compute(order).to_f).to eql 2.86
end

context "when rate is not in its validity period" do
let(:starts_at) { 1.day.from_now }
let(:expires_at) { 2.days.from_now }

it "should be 0" do
expect(calculator.compute(order)).to eq(0)
end
end

context "when the order's tax address is outside the default VAT zone" do
let(:default_tax) { true }
let(:default_vat_country) { create(:country, iso: "DE") }
Expand All @@ -86,6 +119,17 @@
expect(subject.compute(order)).to eq(-2.86)
end
end

context "when rate is not in its validity period" do
let(:starts_at) { 1.day.from_now }
let(:expires_at) { 2.days.from_now }

it "should be 0" do
Spree::Deprecation.silence do
expect(calculator.compute(order)).to eq(0)
end
end
end
end
end
end
Expand All @@ -103,6 +147,15 @@
expect(calculator.compute(item)).to eql 1.43
end

context "when rate is not in its validity period" do
let(:starts_at) { 1.day.from_now }
let(:expires_at) { 2.days.from_now }

it "should be 0" do
expect(calculator.compute(item)).to eq(0)
end
end

context "when line item is discounted" do
let(:promo_total) { -1 }

Expand Down Expand Up @@ -135,12 +188,30 @@
it "should be equal to the item's pre-tax total * rate" do
expect(calculator.compute(item)).to eq(1.45)
end

context "when rate is not in its validity period" do
let(:starts_at) { 1.day.from_now }
let(:expires_at) { 2.days.from_now }

it "should be 0" do
expect(calculator.compute(item)).to eq(0)
end
end
end

context "when the variant matches the tax category" do
it "should be equal to the item pre-tax total * rate" do
expect(calculator.compute(item)).to eq(1.50)
end

context "when rate is not in its validity period" do
let(:starts_at) { 1.day.from_now }
let(:expires_at) { 2.days.from_now }

it "should be 0" do
expect(calculator.compute(item)).to eq(0)
end
end
end
end
end
Expand Down
70 changes: 70 additions & 0 deletions core/spec/models/spree/tax_rate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -264,4 +264,74 @@
end
end
end

describe "#active?" do
subject(:rate) { create(:tax_rate, validity).active? }

context "when validity is not set" do
let(:validity) { {} }

it { is_expected.to eq(true) }
end

context "when starts_at is set" do
context "now" do
let(:validity) { { starts_at: DateTime.now } }

it { is_expected.to eq(true) }
end

context "in the past" do
let(:validity) { { starts_at: 1.day.ago } }

it { is_expected.to eq(true) }
end

context "in the future" do
let(:validity) { { starts_at: 1.day.from_now } }

it { is_expected.to eq(false) }
end
end

context "when expires_at is set" do
context "now" do
let(:validity) { { expires_at: DateTime.now } }

it { is_expected.to eq(false) }
end

context "in the past" do
let(:validity) { { expires_at: 1.day.ago } }

it { is_expected.to eq(false) }
end

context "in the future" do
let(:validity) { { expires_at: 1.day.from_now } }

it { is_expected.to eq(true) }
end
end

context "when starts_at and expires_at are set" do
context "so that today is in range" do
let(:validity) { { starts_at: 1.day.ago, expires_at: 1.day.from_now } }

it { is_expected.to eq(true) }
end

context "both in the past" do
let(:validity) { { starts_at: 2.days.ago, expires_at: 1.day.ago } }

it { is_expected.to eq(false) }
end

context "both in the future" do
let(:validity) { { starts_at: 1.day.from_now, expires_at: 2.days.from_now } }

it { is_expected.to eq(false) }
end
end
end
end

0 comments on commit aa21865

Please sign in to comment.