Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vouchers part 1 #11002

Merged
merged 16 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 2 additions & 16 deletions app/controllers/split_checkout_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class SplitCheckoutController < ::BaseController
def edit
redirect_to_step_based_on_order unless params[:step]
check_step if params[:step]
recalculate_tax if params[:step] == "summary"

flash_error_when_no_shipping_method_available if available_shipping_methods.none?
end
Expand Down Expand Up @@ -204,6 +203,7 @@ def shipping_method_ship_address_not_required?

def process_voucher
if add_voucher
VoucherAdjustmentsService.calculate(@order)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as taxes don't change after this it should be fine. If I followed correctly taxes are recalculated if address changes, and also after the order move to the "payment" step, which all happen before we can reach this code, is that correct ?

render_voucher_section_or_redirect
elsif @order.errors.present?
render_error
Expand All @@ -226,7 +226,7 @@ def add_voucher

adjustment = voucher.create_adjustment(voucher.code, @order)

if !adjustment.valid?
unless adjustment.valid?
@order.errors.add(:voucher, I18n.t('split_checkout.errors.add_voucher_error'))
adjustment.errors.each { |error| @order.errors.import(error) }
return false
Expand Down Expand Up @@ -308,18 +308,4 @@ def check_step
redirect_to checkout_step_path(:payment) if params[:step] == "summary"
end
end

def recalculate_tax
@order.create_tax_charge!
@order.update_order!

apply_voucher if @order.voucher_adjustments.present?
end

def apply_voucher
VoucherAdjustmentsService.calculate(@order)

# update order to take into account the voucher we applied
@order.update_order!
end
end
10 changes: 0 additions & 10 deletions app/controllers/spree/admin/orders/customer_details_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def update
end

refresh_shipment_rates
recalculate_taxes
OrderWorkflow.new(@order).advance_to_payment

flash[:success] = Spree.t('customer_details_updated')
Expand Down Expand Up @@ -52,15 +51,6 @@ def refresh_shipment_rates
@order.shipments.map(&:refresh_rates)
end

def recalculate_taxes
# If the order's address has been changed, the tax zone could be different,
# which means a different set of tax rates might be applicable.
@order.create_tax_charge!
Spree::TaxRate.adjust(@order, @order.adjustments.admin)

@order.update_totals_and_states
end

def order_params
params.require(:order).permit(
:email,
Expand Down
2 changes: 2 additions & 0 deletions app/models/spree/adjustment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ class Adjustment < ApplicationRecord
scope :charge, -> { where('amount >= 0') }
scope :credit, -> { where('amount < 0') }
scope :return_authorization, -> { where(originator_type: "Spree::ReturnAuthorization") }
scope :voucher, -> { where(originator_type: "Voucher") }
scope :non_voucher, -> { where.not(originator_type: "Voucher") }
scope :inclusive, -> { where(included: true) }
scope :additional, -> { where(included: false) }
scope :legacy_tax, -> { additional.tax.where(adjustable_type: "Spree::Order") }
Expand Down
42 changes: 30 additions & 12 deletions app/models/spree/order.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,6 @@ def states
delegate :create_line_item_fees!, :create_order_fees!, :update_order_fees!,
:update_line_item_fees!, :recreate_all_fees!, to: :fee_handler

# Needs to happen before save_permalink is called
before_validation :set_currency
before_validation :generate_order_number, if: :new_record?
before_validation :clone_billing_address, if: :use_billing?
before_validation :ensure_customer

before_create :link_by_email
after_create :create_tax_charge!

validates :customer, presence: true, if: :require_customer?
validate :products_available_from_new_distribution, if: lambda {
distributor_id_changed? || order_cycle_id_changed?
Expand All @@ -107,16 +98,25 @@ def states
validates :order_cycle, presence: true, on: :require_distribution
validates :distributor, presence: true, on: :require_distribution

make_permalink field: :number
before_validation :set_currency
before_validation :generate_order_number, if: :new_record?
before_validation :clone_billing_address, if: :use_billing?
before_validation :ensure_customer
dacook marked this conversation as resolved.
Show resolved Hide resolved

before_create :link_by_email
before_save :update_shipping_fees!, if: :complete?
before_save :update_payment_fees!, if: :complete?

after_create :create_tax_charge!
after_save :reapply_tax_on_changed_address

after_save_commit DefaultAddressUpdater

make_permalink field: :number

attribute :send_cancellation_email, type: :boolean, default: true
attribute :restock_items, type: :boolean, default: true
# -- Scopes

scope :not_empty, -> {
left_outer_joins(:line_items).where.not(spree_line_items: { id: nil })
}
Expand Down Expand Up @@ -171,6 +171,11 @@ def amount
line_items.inject(0.0) { |sum, li| sum + li.amount }
end

# Order total without any applied discounts from vouchers
def pre_discount_total
dacook marked this conversation as resolved.
Show resolved Hide resolved
item_total + all_adjustments.additional.eligible.non_voucher.sum(:amount)
end

def currency
self[:currency] || Spree::Config[:currency]
end
Expand Down Expand Up @@ -317,12 +322,13 @@ def ship_total
# Creates new tax charges if there are any applicable rates. If prices already
# include taxes then price adjustments are created instead.
def create_tax_charge!
return if state.in?(["cart", "address", "delivery"])
return if before_payment_state?

clear_legacy_taxes!

Spree::TaxRate.adjust(self, line_items)
Spree::TaxRate.adjust(self, shipments) if shipments.any?
Spree::TaxRate.adjust(self, adjustments.admin) if adjustments.admin.any?
fee_handler.tax_enterprise_fees!
end

Expand Down Expand Up @@ -574,8 +580,20 @@ def sorted_line_items
end
end

def before_payment_state?
dacook marked this conversation as resolved.
Show resolved Hide resolved
state.in?(["cart", "address", "delivery"])
end

private

def reapply_tax_on_changed_address
return if before_payment_state?
return unless tax_address&.saved_changes?

create_tax_charge!
update_totals_and_states
end

def deliver_order_confirmation_email
return if subscription.present?

Expand Down
7 changes: 5 additions & 2 deletions app/models/spree/order/checkout.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ def self.define_state_machine!
before_transition to: :delivery, do: :ensure_available_shipping_rates
before_transition to: :confirmation, do: :validate_payment_method!

after_transition to: :payment, do: :create_tax_charge!
after_transition to: :payment do |order|
order.create_tax_charge!
order.update_totals_and_states
end
after_transition to: :complete, do: :finalize!
after_transition to: :resumed, do: :after_resume
after_transition to: :canceled, do: :after_cancel
Expand Down Expand Up @@ -144,7 +147,7 @@ def state_changed(name)
private

def after_cancel
shipments.each(&:cancel!)
shipments.reject(&:canceled?).each(&:cancel!)
payments.checkout.each(&:void!)

OrderMailer.cancel_email(id).deliver_later if send_cancellation_email
Expand Down
2 changes: 1 addition & 1 deletion app/models/voucher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ def create_adjustment(label, order)
# We limit adjustment to the maximum amount needed to cover the order, ie if the voucher
# covers more than the order.total we only need to create an adjustment covering the order.total
def compute_amount(order)
-amount.clamp(0, order.total)
-amount.clamp(0, order.pre_discount_total)
end
end
2 changes: 1 addition & 1 deletion app/services/order_fees_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def recreate_all_fees!
create_order_fees!
end

tax_enterprise_fees!
tax_enterprise_fees! unless order.before_payment_state?
order.update_order!
end

Expand Down
4 changes: 2 additions & 2 deletions app/services/order_workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ def next(options = {})
end

def advance_to_payment
return unless order.state.in? ["cart", "address", "delivery"]
return unless order.before_payment_state?

advance_to_state("payment", advance_order_options)
end

def advance_checkout(options = {})
advance_to = order.state.in?(["cart", "address", "delivery"]) ? "payment" : "confirmation"
advance_to = order.before_payment_state? ? "payment" : "confirmation"

advance_to_state(advance_to, advance_order_options.merge(options))
end
Expand Down
1 change: 0 additions & 1 deletion app/views/layouts/darkswarm.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
= yield

#footer
%loading
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice.


= yield :scripts
= inject_current_hub
Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/spree/admin/orders_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

it "updates fees and taxes and redirects to order details page" do
expect(order).to receive(:recreate_all_fees!)
expect(order).to receive(:create_tax_charge!)
expect(order).to receive(:create_tax_charge!).at_least :once
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So now it's called multiple times? I would have thought that the transition happens only once.

Copy link
Contributor Author

@Matt-Yorkley Matt-Yorkley Jun 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I puzzled over this a bit, but it looks like this happens in relation to what the test setup is doing as opposed to what happens in the test itself. Basically before the test even starts (or when the order object is called and lazy-instantiated) the process of created a completed order (:completed_order_with_totals) is running through various bits of logic in a sequence like adding line items and creating shipments and setting the shipping address, etc etc, and some of that is doing things that are triggering tax-related callbacks because the order total is changing or the tax address is changing?


spree_put :update, params

Expand Down
1 change: 1 addition & 0 deletions spec/models/proxy_order_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@
}
break unless order.next! while !order.completed?
order.cancel
order.reload
end

it "returns true, clears canceled_at and resumes the order" do
Expand Down
15 changes: 15 additions & 0 deletions spec/models/spree/adjustment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ module Spree
context "when the shipment has an inclusive tax rate" do
it "calculates the shipment tax from the tax rate" do
order.shipments = [shipment]
order.state = "payment"
order.create_tax_charge!
order.update_totals

Expand All @@ -274,6 +275,7 @@ module Spree

it "records the tax on the shipment's adjustments" do
order.shipments = [shipment]
order.state = "payment"
order.create_tax_charge!
order.update_totals

Expand Down Expand Up @@ -355,6 +357,7 @@ module Spree

context "when enterprise fees have a fixed tax_category" do
before do
order.update(state: "payment")
order.recreate_all_fees!
end

Expand Down Expand Up @@ -440,6 +443,11 @@ module Spree
calculator: ::Calculator::FlatRate.new(preferred_amount: 50.0))
}

before do
order.update(state: "payment")
order.create_tax_charge!
end

describe "when the tax rate includes the tax in the price" do
it "records no tax on the enterprise fee adjustments" do
# EnterpriseFee tax category is nil and inheritance only applies to per item fees
Expand Down Expand Up @@ -471,6 +479,11 @@ module Spree
calculator: ::Calculator::PerItem.new(preferred_amount: 50.0))
}

before do
order.update(state: "payment")
order.create_tax_charge!
end

describe "when the tax rate includes the tax in the price" do
it "records the correct amount in a tax adjustment" do
# Applying product tax rate of 0.2 to enterprise fee of $50
Expand Down Expand Up @@ -522,6 +535,8 @@ module Spree
tax_category.tax_rates << tax_rate
allow(order).to receive(:tax_zone) { zone }
order.line_items << create(:line_item, variant: variant, quantity: 5)
order.update(state: "payment")
order.create_tax_charge!
end

context "with included taxes" do
Expand Down
1 change: 1 addition & 0 deletions spec/models/spree/order_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,7 @@

before do
order.cancel!
order.reload
order.resume!
end

Expand Down
19 changes: 10 additions & 9 deletions spec/models/voucher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,21 @@
end

describe '#compute_amount' do
subject { create(:voucher, code: 'new_code', enterprise: enterprise, amount: 10) }

let(:order) { create(:order_with_totals) }

it 'returns -10' do
expect(subject.compute_amount(order).to_f).to eq(-10)
context 'when order total is more than the voucher' do
subject { create(:voucher, code: 'new_code', enterprise: enterprise, amount: 5) }

it 'uses the voucher total' do
expect(subject.compute_amount(order).to_f).to eq(-5)
end
end

context 'when order total is smaller than 10' do
it 'returns minus the order total' do
order.total = 6
order.save!
context 'when order total is less than the voucher' do
subject { create(:voucher, code: 'new_code', enterprise: enterprise, amount: 20) }

expect(subject.compute_amount(order).to_f).to eq(-6)
it 'matches the order total' do
expect(subject.compute_amount(order).to_f).to eq(-10)
end
end
end
Expand Down
4 changes: 1 addition & 3 deletions spec/services/voucher_adjustments_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@

it 'updates the adjustment amount to -order.total' do
voucher.create_adjustment(voucher.code, order)

order.total = 6
order.save!
order.update_columns(item_total: 6)

VoucherAdjustmentsService.calculate(order)

Expand Down
3 changes: 3 additions & 0 deletions spec/system/admin/bulk_order_management_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,9 @@
let!(:li2) { create(:line_item_with_shipment, order: o2 ) }

before :each do
o1.update_totals_and_states
o2.update_totals_and_states

visit_bulk_order_management
end

Expand Down
5 changes: 1 addition & 4 deletions spec/system/consumer/split_checkout_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1114,11 +1114,8 @@
let(:voucher) { create(:voucher, code: 'some_code', enterprise: distributor, amount: 6) }

before do
# Add voucher to the order
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments might seem superfluous to some, but as a first-time reader of the code, I find them a helpful narration :)

voucher.create_adjustment(voucher.code, order)

# Update order so voucher adjustment is properly taken into account
order.update_order!
order.update_totals
VoucherAdjustmentsService.calculate(order)

visit checkout_step_path(:summary)
Expand Down
Loading