mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-18 04:39:14 +00:00
Merge pull request #6758 from Matt-Yorkley/enterprise-fees-refactor
Enterprise fees refactor
This commit is contained in:
@@ -33,7 +33,9 @@ module Admin
|
||||
# and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE
|
||||
order.with_lock do
|
||||
if @line_item.update(line_item_params)
|
||||
order.update_distribution_charge!
|
||||
order.update_line_item_fees! @line_item
|
||||
order.update_order_fees!
|
||||
order.update!
|
||||
render nothing: true, status: :no_content # No Content, does not trigger ng resource auto-update
|
||||
else
|
||||
render json: { errors: @line_item.errors }, status: :precondition_failed
|
||||
|
||||
@@ -13,7 +13,7 @@ class CartController < BaseController
|
||||
|
||||
cart_service.populate(params.slice(:variants, :quantity), true)
|
||||
if cart_service.valid?
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
order.cap_quantity_at_stock!
|
||||
order.update!
|
||||
|
||||
|
||||
@@ -42,7 +42,8 @@ class LineItemsController < BaseController
|
||||
item.destroy
|
||||
order.update_shipping_fees!
|
||||
order.update_payment_fees!
|
||||
order.update_distribution_charge!
|
||||
order.update_order_fees!
|
||||
order.update!
|
||||
order.create_tax_charge!
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,14 +12,6 @@ module Spree
|
||||
|
||||
# Ensure that the distributor is set for an order when
|
||||
before_action :ensure_distribution, only: :new
|
||||
|
||||
# After updating an order, the fees should be updated as well
|
||||
# Currently, adding or deleting line items does not trigger updating the
|
||||
# fees! This is a quick fix for that.
|
||||
# TODO: update fees when adding/removing line items
|
||||
# instead of the update_distribution_charge method.
|
||||
after_action :update_distribution_charge, only: :update
|
||||
|
||||
before_action :require_distributor_abn, only: :invoice
|
||||
|
||||
respond_to :html, :json
|
||||
@@ -43,6 +35,9 @@ module Spree
|
||||
end
|
||||
|
||||
def update
|
||||
@order.recreate_all_fees!
|
||||
@order.update!
|
||||
|
||||
unless order_params.present? && @order.update(order_params) && @order.line_items.present?
|
||||
if @order.line_items.empty?
|
||||
@order.errors.add(:line_items, Spree.t('errors.messages.blank'))
|
||||
@@ -51,7 +46,6 @@ module Spree
|
||||
flash: { error: @order.errors.full_messages.join(', ') })
|
||||
end
|
||||
|
||||
@order.update!
|
||||
if @order.complete?
|
||||
redirect_to spree.edit_admin_order_path(@order)
|
||||
else
|
||||
@@ -103,10 +97,6 @@ module Spree
|
||||
render template: "spree/admin/orders/ticket", layout: false
|
||||
end
|
||||
|
||||
def update_distribution_charge
|
||||
@order.update_distribution_charge!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def order_params
|
||||
|
||||
@@ -75,11 +75,18 @@ module Spree
|
||||
redirect_to(main_app.root_path) && return
|
||||
end
|
||||
|
||||
# This action is called either from the cart page when the order is not yet complete, or from
|
||||
# the edit order page (frontoffice) if the hub allows users to update completed orders.
|
||||
# This line item updating logic should probably be handled through CartService#populate.
|
||||
if @order.update(order_params)
|
||||
discard_empty_line_items
|
||||
with_open_adjustments { update_totals_and_taxes }
|
||||
|
||||
@order.update_distribution_charge!
|
||||
@order.recreate_all_fees! # Enterprise fees on line items and on the order itself
|
||||
|
||||
if @order.complete?
|
||||
@order.update_shipping_fees!
|
||||
@order.update_payment_fees!
|
||||
end
|
||||
|
||||
respond_with(@order) do |format|
|
||||
format.html do
|
||||
@@ -148,30 +155,6 @@ module Spree
|
||||
|
||||
private
|
||||
|
||||
# Updates the various denormalized total attributes of the order and
|
||||
# recalculates the shipment taxes
|
||||
def update_totals_and_taxes
|
||||
@order.updater.update_totals
|
||||
@order.shipment&.ensure_correct_adjustment
|
||||
end
|
||||
|
||||
# Sets the adjustments to open to perform the block's action and restores
|
||||
# their state to whatever the they had. Note that it does not change any new
|
||||
# adjustments that might get created in the yielded block.
|
||||
def with_open_adjustments
|
||||
previous_states = @order.adjustments.each_with_object({}) do |adjustment, hash|
|
||||
hash[adjustment.id] = adjustment.state
|
||||
end
|
||||
@order.adjustments.each { |adjustment| adjustment.fire_events(:open) }
|
||||
|
||||
yield
|
||||
|
||||
@order.adjustments.each do |adjustment|
|
||||
previous_state = previous_states[adjustment.id]
|
||||
adjustment.update_attribute(:state, previous_state) if previous_state
|
||||
end
|
||||
end
|
||||
|
||||
def discard_empty_line_items
|
||||
@order.line_items = @order.line_items.select { |li| li.quantity > 0 }
|
||||
end
|
||||
|
||||
@@ -40,8 +40,8 @@ class EnterpriseFee < ActiveRecord::Base
|
||||
joins(:calculator).where('spree_calculators.type IN (?)', PER_ORDER_CALCULATORS)
|
||||
}
|
||||
|
||||
def self.clear_all_adjustments_on_order(order)
|
||||
order.adjustments.where(originator_type: 'EnterpriseFee').destroy_all
|
||||
def self.clear_all_adjustments(order)
|
||||
order.adjustments.enterprise_fee.destroy_all
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -111,8 +111,8 @@ module Spree
|
||||
# more than on line items at once via accepted_nested_attributes the order
|
||||
# object on the association would be in a old state and therefore the
|
||||
# adjustment calculations would not performed on proper values
|
||||
def update!(calculable = nil)
|
||||
return if immutable?
|
||||
def update!(calculable = nil, force: false)
|
||||
return if immutable? && !force
|
||||
|
||||
# Fix for Spree issue #3381
|
||||
# If we attempt to call 'source' before the reload, then source is currently
|
||||
|
||||
@@ -68,6 +68,9 @@ module Spree
|
||||
accepts_nested_attributes_for :shipments
|
||||
|
||||
delegate :admin_and_handling_total, :payment_fee, :ship_total, to: :adjustments_fetcher
|
||||
delegate :update_totals, to: :updater
|
||||
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
|
||||
@@ -268,8 +271,6 @@ module Spree
|
||||
updater.update
|
||||
end
|
||||
|
||||
delegate :update_totals, to: :updater
|
||||
|
||||
def clone_billing_address
|
||||
if bill_address && ship_address.nil?
|
||||
self.ship_address = bill_address.clone
|
||||
@@ -660,29 +661,6 @@ module Spree
|
||||
end
|
||||
end
|
||||
|
||||
def update_distribution_charge!
|
||||
# `with_lock` acquires an exclusive row lock on order so no other
|
||||
# requests can update it until the transaction is commited.
|
||||
# See https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/locking/pessimistic.rb#L69
|
||||
# and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE
|
||||
with_lock do
|
||||
EnterpriseFee.clear_all_adjustments_on_order self
|
||||
|
||||
loaded_line_items =
|
||||
line_items.includes(variant: :product, order: [:distributor, :order_cycle]).all
|
||||
|
||||
loaded_line_items.each do |line_item|
|
||||
if provided_by_order_cycle? line_item
|
||||
OpenFoodNetwork::EnterpriseFeeCalculator.new.create_line_item_adjustments_for line_item
|
||||
end
|
||||
end
|
||||
|
||||
if order_cycle
|
||||
OpenFoodNetwork::EnterpriseFeeCalculator.new.create_order_adjustments_for self
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_order_cycle!(order_cycle)
|
||||
return if self.order_cycle == order_cycle
|
||||
|
||||
@@ -753,6 +731,10 @@ module Spree
|
||||
|
||||
private
|
||||
|
||||
def fee_handler
|
||||
@fee_handler ||= OrderFeesHandler.new(self)
|
||||
end
|
||||
|
||||
def process_each_payment
|
||||
raise Core::GatewayError, Spree.t(:no_pending_payments) if pending_payments.empty?
|
||||
|
||||
@@ -829,11 +811,6 @@ module Spree
|
||||
subscription.present? && order_cycle.orders_close_at.andand > Time.zone.now
|
||||
end
|
||||
|
||||
def provided_by_order_cycle?(line_item)
|
||||
order_cycle_variants = order_cycle.andand.variants || []
|
||||
order_cycle_variants.include? line_item.variant
|
||||
end
|
||||
|
||||
def require_customer?
|
||||
return true unless new_record? || state == 'cart'
|
||||
end
|
||||
@@ -867,11 +844,8 @@ module Spree
|
||||
def update_adjustment!(adjustment)
|
||||
return if adjustment.finalized?
|
||||
|
||||
state = adjustment.state
|
||||
adjustment.state = 'open'
|
||||
adjustment.update!
|
||||
adjustment.update!(force: true)
|
||||
update!
|
||||
adjustment.state = state
|
||||
end
|
||||
|
||||
# object_params sets the payment amount to the order total, but it does this
|
||||
|
||||
@@ -78,7 +78,7 @@ class OrderFactory
|
||||
end
|
||||
|
||||
def create_payment
|
||||
@order.update_distribution_charge!
|
||||
@order.recreate_all_fees!
|
||||
@order.payments.create(payment_method_id: attrs[:payment_method_id], amount: @order.reload.total)
|
||||
end
|
||||
|
||||
|
||||
61
app/services/order_fees_handler.rb
Normal file
61
app/services/order_fees_handler.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class OrderFeesHandler
|
||||
attr_reader :order, :distributor, :order_cycle
|
||||
|
||||
def initialize(order)
|
||||
@order = order
|
||||
@distributor = order.distributor
|
||||
@order_cycle = order.order_cycle
|
||||
end
|
||||
|
||||
def recreate_all_fees!
|
||||
# `with_lock` acquires an exclusive row lock on order so no other
|
||||
# requests can update it until the transaction is commited.
|
||||
# See https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/locking/pessimistic.rb#L69
|
||||
# and https://www.postgresql.org/docs/current/static/sql-select.html#SQL-FOR-UPDATE-SHARE
|
||||
order.with_lock do
|
||||
EnterpriseFee.clear_all_adjustments order
|
||||
|
||||
create_line_item_fees!
|
||||
create_order_fees!
|
||||
end
|
||||
end
|
||||
|
||||
def create_line_item_fees!
|
||||
order.line_items.includes(variant: :product).each do |line_item|
|
||||
if provided_by_order_cycle? line_item
|
||||
calculator.create_line_item_adjustments_for line_item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_order_fees!
|
||||
return unless order_cycle
|
||||
|
||||
calculator.create_order_adjustments_for order
|
||||
end
|
||||
|
||||
def update_line_item_fees!(line_item)
|
||||
line_item.adjustments.enterprise_fee.each do |fee|
|
||||
fee.update!(line_item, force: true)
|
||||
end
|
||||
end
|
||||
|
||||
def update_order_fees!
|
||||
order.adjustments.enterprise_fee.where(source_type: 'Spree::Order').each do |fee|
|
||||
fee.update!(order, force: true)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def calculator
|
||||
@calculator ||= OpenFoodNetwork::EnterpriseFeeCalculator.new(distributor, order_cycle)
|
||||
end
|
||||
|
||||
def provided_by_order_cycle?(line_item)
|
||||
@order_cycle_variant_ids ||= order_cycle&.variants&.map(&:id) || []
|
||||
@order_cycle_variant_ids.include? line_item.variant_id
|
||||
end
|
||||
end
|
||||
@@ -39,8 +39,6 @@ module OpenFoodNetwork
|
||||
|
||||
def create_line_item_adjustments_for(line_item)
|
||||
variant = line_item.variant
|
||||
@distributor = line_item.order.distributor
|
||||
@order_cycle = line_item.order.order_cycle
|
||||
|
||||
per_item_enterprise_fee_applicators_for(variant).each do |applicator|
|
||||
applicator.create_line_item_adjustment(line_item)
|
||||
@@ -48,9 +46,6 @@ module OpenFoodNetwork
|
||||
end
|
||||
|
||||
def create_order_adjustments_for(order)
|
||||
@distributor = order.distributor
|
||||
@order_cycle = order.order_cycle
|
||||
|
||||
per_order_enterprise_fee_applicators_for(order).each do |applicator|
|
||||
applicator.create_order_adjustment(order)
|
||||
end
|
||||
|
||||
@@ -210,7 +210,9 @@ describe Admin::BulkLineItemsController, type: :controller do
|
||||
.to receive(:find).with(line_item1.id.to_s).and_return(line_item1)
|
||||
|
||||
expect(line_item1.order).to receive(:reload).with(lock: true)
|
||||
expect(line_item1.order).to receive(:update_distribution_charge!)
|
||||
expect(line_item1.order).to receive(:update_line_item_fees!)
|
||||
expect(line_item1.order).to receive(:update_order_fees!)
|
||||
expect(line_item1.order).to receive(:update!).twice
|
||||
|
||||
spree_put :update, params
|
||||
end
|
||||
|
||||
@@ -127,28 +127,31 @@ describe LineItemsController, type: :controller do
|
||||
|
||||
context "on a completed order with enterprise fees" do
|
||||
let(:user) { create(:user) }
|
||||
let(:variant) { create(:variant) }
|
||||
let(:variant1) { create(:variant) }
|
||||
let(:variant2) { create(:variant) }
|
||||
let(:distributor) { create(:distributor_enterprise, allow_order_changes: true) }
|
||||
let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
|
||||
let(:enterprise_fee) { create(:enterprise_fee, calculator: build(:calculator_per_item) ) }
|
||||
let!(:exchange) { create(:exchange, incoming: true, sender: variant.product.supplier, receiver: order_cycle.coordinator, variants: [variant], enterprise_fees: [enterprise_fee]) }
|
||||
let(:calculator) { Calculator::PriceSack.new(preferred_minimal_amount: 15, preferred_normal_amount: 22, preferred_discount_amount: 11) }
|
||||
let(:enterprise_fee) { create(:enterprise_fee, calculator: calculator) }
|
||||
let!(:exchange) { create(:exchange, incoming: true, sender: variant1.product.supplier, receiver: order_cycle.coordinator, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) }
|
||||
let!(:order) do
|
||||
order = create(:completed_order_with_totals, user: user, distributor: distributor, order_cycle: order_cycle, line_items_count: 1)
|
||||
order.reload.line_items.first.update(variant_id: variant.id)
|
||||
order = create(:completed_order_with_totals, user: user, distributor: distributor, order_cycle: order_cycle, line_items_count: 2)
|
||||
order.reload.line_items.first.update(variant_id: variant1.id)
|
||||
order.line_items.last.update(variant_id: variant2.id)
|
||||
while !order.completed? do break unless order.next! end
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
order
|
||||
end
|
||||
let(:params) { { format: :json, id: order.line_items.first } }
|
||||
|
||||
it "updates the fees" do
|
||||
expect(order.reload.adjustment_total).to eq enterprise_fee.calculator.preferred_amount
|
||||
expect(order.reload.adjustment_total).to eq calculator.preferred_discount_amount
|
||||
|
||||
allow(controller).to receive_messages spree_current_user: user
|
||||
delete :destroy, params
|
||||
expect(response.status).to eq 204
|
||||
|
||||
expect(order.reload.adjustment_total).to eq 0
|
||||
expect(order.reload.adjustment_total).to eq calculator.preferred_normal_amount
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,12 +56,48 @@ describe Spree::Admin::OrdersController, type: :controller do
|
||||
end
|
||||
|
||||
it "updates distribution charges and redirects to order details page" do
|
||||
expect_any_instance_of(Spree::Order).to receive(:update_distribution_charge!)
|
||||
expect_any_instance_of(Spree::Order).to receive(:recreate_all_fees!)
|
||||
|
||||
spree_put :update, params
|
||||
|
||||
expect(response).to redirect_to spree.edit_admin_order_path(order)
|
||||
end
|
||||
|
||||
context "recalculating enterprise fees" do
|
||||
let(:user) { create(:admin_user) }
|
||||
let(:variant1) { create(:variant) }
|
||||
let(:variant2) { create(:variant) }
|
||||
let(:distributor) { create(:distributor_enterprise, allow_order_changes: true) }
|
||||
let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
|
||||
let(:enterprise_fee) { create(:enterprise_fee, calculator: build(:calculator_per_item) ) }
|
||||
let!(:exchange) { create(:exchange, incoming: true, sender: variant1.product.supplier, receiver: order_cycle.coordinator, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) }
|
||||
let!(:order) do
|
||||
order = create(:completed_order_with_totals, line_items_count: 2, distributor: distributor, order_cycle: order_cycle)
|
||||
order.reload.line_items.first.update(variant_id: variant1.id)
|
||||
order.line_items.last.update(variant_id: variant2.id)
|
||||
while !order.completed? do break unless order.next! end
|
||||
order.recreate_all_fees!
|
||||
order.update!
|
||||
order
|
||||
end
|
||||
|
||||
before do
|
||||
allow(controller).to receive(:spree_current_user) { user }
|
||||
allow(controller).to receive(:order_to_update) { order }
|
||||
end
|
||||
|
||||
it "recalculates fees if the orders contents have changed" do
|
||||
expect(order.total).to eq order.item_total + (enterprise_fee.calculator.preferred_amount * 2)
|
||||
expect(order.adjustment_total).to eq enterprise_fee.calculator.preferred_amount * 2
|
||||
|
||||
order.contents.add(order.line_items.first.variant, 1)
|
||||
|
||||
spree_put :update, { id: order.number }
|
||||
|
||||
expect(order.reload.total).to eq order.item_total + (enterprise_fee.calculator.preferred_amount * 3)
|
||||
expect(order.adjustment_total).to eq enterprise_fee.calculator.preferred_amount * 3
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "incomplete order" do
|
||||
@@ -86,7 +122,7 @@ describe Spree::Admin::OrdersController, type: :controller do
|
||||
|
||||
context "and no errors" do
|
||||
it "updates distribution charges and redirects to customer details page" do
|
||||
expect_any_instance_of(Spree::Order).to receive(:update_distribution_charge!)
|
||||
expect_any_instance_of(Spree::Order).to receive(:recreate_all_fees!)
|
||||
|
||||
spree_put :update, params
|
||||
|
||||
|
||||
@@ -286,43 +286,34 @@ describe Spree::OrdersController, type: :controller do
|
||||
allow(subject).to receive(:order_to_update) { order }
|
||||
end
|
||||
|
||||
it "updates the fees" do
|
||||
it "updates the shipping and payment fees" do
|
||||
spree_post :update,
|
||||
order: { line_items_attributes: {
|
||||
"0" => { id: line_item1.id, quantity: 1 },
|
||||
"1" => { id: line_item2.id, quantity: 0 }
|
||||
} }
|
||||
|
||||
expect(order.line_items.count).to eq 1
|
||||
expect(order.adjustment_total).to eq((item_num - 1) * (shipping_fee + payment_fee))
|
||||
expect(order.reload.line_items.count).to eq 1
|
||||
expect(order.adjustment_total).to eq(1 * (shipping_fee + payment_fee))
|
||||
expect(order.shipment.adjustment.included_tax).to eq 0.6
|
||||
end
|
||||
|
||||
it "keeps the adjustments' previous state" do
|
||||
spree_post :update,
|
||||
order: { line_items_attributes: {
|
||||
"0" => { id: line_item1.id, quantity: 1 },
|
||||
"1" => { id: line_item2.id, quantity: 0 }
|
||||
} }
|
||||
|
||||
# The second adjustment (shipping adjustment) is open before the update
|
||||
# so, restoring its state leaves it open.
|
||||
expect(order.adjustments.map(&:state)).to eq(['closed', 'open'])
|
||||
end
|
||||
end
|
||||
|
||||
context "with enterprise fees" do
|
||||
let(:user) { create(:user) }
|
||||
let(:variant) { create(:variant) }
|
||||
let(:variant1) { create(:variant) }
|
||||
let(:variant2) { create(:variant) }
|
||||
let(:distributor) { create(:distributor_enterprise, allow_order_changes: true) }
|
||||
let(:order_cycle) { create(:simple_order_cycle, distributors: [distributor]) }
|
||||
let(:enterprise_fee) { create(:enterprise_fee, calculator: build(:calculator_per_item) ) }
|
||||
let!(:exchange) { create(:exchange, incoming: true, sender: variant.product.supplier, receiver: order_cycle.coordinator, variants: [variant], enterprise_fees: [enterprise_fee]) }
|
||||
let!(:exchange) { create(:exchange, incoming: true, sender: variant1.product.supplier, receiver: order_cycle.coordinator, variants: [variant1, variant2], enterprise_fees: [enterprise_fee]) }
|
||||
let!(:order) do
|
||||
order = create(:completed_order_with_totals, line_items_count: 1, user: user, distributor: distributor, order_cycle: order_cycle)
|
||||
order.reload.line_items.first.update(variant_id: variant.id)
|
||||
order = create(:completed_order_with_totals, line_items_count: 2, user: user, distributor: distributor, order_cycle: order_cycle)
|
||||
order.reload.line_items.first.update(variant_id: variant1.id)
|
||||
order.reload.line_items.last.update(variant_id: variant2.id)
|
||||
while !order.completed? do break unless order.next! end
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
order.update!
|
||||
order
|
||||
end
|
||||
let(:params) {
|
||||
@@ -337,12 +328,34 @@ describe Spree::OrdersController, type: :controller do
|
||||
end
|
||||
|
||||
it "updates the fees" do
|
||||
expect(order.reload.adjustment_total).to eq enterprise_fee.calculator.preferred_amount
|
||||
expect(order.total).to eq order.item_total + (enterprise_fee.calculator.preferred_amount * 2)
|
||||
expect(order.adjustment_total).to eq enterprise_fee.calculator.preferred_amount * 2
|
||||
|
||||
allow(controller).to receive_messages spree_current_user: user
|
||||
spree_post :update, params
|
||||
|
||||
expect(order.reload.adjustment_total).to eq enterprise_fee.calculator.preferred_amount * 2
|
||||
expect(order.total).to eq order.item_total + (enterprise_fee.calculator.preferred_amount * 3)
|
||||
expect(order.adjustment_total).to eq enterprise_fee.calculator.preferred_amount * 3
|
||||
end
|
||||
|
||||
context "when a line item is removed" do
|
||||
let(:params) {
|
||||
{ order: { line_items_attributes: {
|
||||
"0" => { id: order.line_items.first.id, quantity: 0 },
|
||||
"1" => { id: order.line_items.last.id, quantity: 1 }
|
||||
} } }
|
||||
}
|
||||
|
||||
it "updates the fees" do
|
||||
expect(order.total).to eq order.item_total + (enterprise_fee.calculator.preferred_amount * 2)
|
||||
expect(order.adjustment_total).to eq enterprise_fee.calculator.preferred_amount * 2
|
||||
|
||||
allow(controller).to receive_messages spree_current_user: user
|
||||
spree_post :update, params
|
||||
|
||||
expect(order.total).to eq order.item_total + (enterprise_fee.calculator.preferred_amount * 1)
|
||||
expect(order.adjustment_total).to eq enterprise_fee.calculator.preferred_amount * 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -90,10 +90,11 @@ FactoryBot.define do
|
||||
after(:create) do |order, evaluator|
|
||||
create(:payment, state: "checkout", order: order, amount: order.total,
|
||||
payment_method: evaluator.payment_method)
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
order.ship_address = evaluator.ship_address
|
||||
while !order.completed? do break unless a = order.next! end
|
||||
order.select_shipping_method(evaluator.shipping_method.id)
|
||||
order.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -185,7 +185,7 @@ feature '
|
||||
|
||||
2.times { order1.next }
|
||||
order1.select_shipping_method shipping_method.id
|
||||
order1.reload.update_distribution_charge!
|
||||
order1.reload.recreate_all_fees!
|
||||
order1.finalize!
|
||||
|
||||
login_as_admin_and_visit spree.admin_reports_path
|
||||
|
||||
@@ -132,7 +132,7 @@ feature "full-page cart", js: true do
|
||||
|
||||
cart_service = CartService.new(order)
|
||||
cart_service.populate(variants: { product_with_fee.variants.first.id => 3, product_with_tax.variants.first.id => 3 })
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
|
||||
visit main_app.cart_path
|
||||
end
|
||||
|
||||
@@ -141,7 +141,7 @@ module OpenFoodNetwork
|
||||
it "creates adjustments for a line item" do
|
||||
exchange.enterprise_fees << enterprise_fee_line_item
|
||||
|
||||
EnterpriseFeeCalculator.new.create_line_item_adjustments_for line_item
|
||||
EnterpriseFeeCalculator.new(distributor, order_cycle).create_line_item_adjustments_for line_item
|
||||
|
||||
a = Spree::Adjustment.last
|
||||
expect(a.metadata.fee_name).to eq(enterprise_fee_line_item.name)
|
||||
@@ -150,7 +150,7 @@ module OpenFoodNetwork
|
||||
it "creates adjustments for an order" do
|
||||
exchange.enterprise_fees << enterprise_fee_order
|
||||
|
||||
EnterpriseFeeCalculator.new.create_order_adjustments_for order
|
||||
EnterpriseFeeCalculator.new(distributor, order_cycle).create_order_adjustments_for order
|
||||
|
||||
a = Spree::Adjustment.last
|
||||
expect(a.metadata.fee_name).to eq(enterprise_fee_order.name)
|
||||
|
||||
@@ -110,7 +110,7 @@ describe EnterpriseFee do
|
||||
order_cycle.exchanges[0].enterprise_fees[0].create_adjustment('foo4', line_item2.order, line_item2, true)
|
||||
|
||||
expect do
|
||||
EnterpriseFee.clear_all_adjustments_on_order order
|
||||
EnterpriseFee.clear_all_adjustments order
|
||||
end.to change(order.adjustments, :count).by(-4)
|
||||
end
|
||||
|
||||
@@ -121,7 +121,7 @@ describe EnterpriseFee do
|
||||
enterprise_fee_aplicator.create_order_adjustment(order)
|
||||
|
||||
expect do
|
||||
EnterpriseFee.clear_all_adjustments_on_order order
|
||||
EnterpriseFee.clear_all_adjustments order
|
||||
end.to change(order.adjustments, :count).by(-1)
|
||||
end
|
||||
|
||||
@@ -135,7 +135,7 @@ describe EnterpriseFee do
|
||||
label: 'hello' })
|
||||
|
||||
expect do
|
||||
EnterpriseFee.clear_all_adjustments_on_order order
|
||||
EnterpriseFee.clear_all_adjustments order
|
||||
end.to change(order.adjustments, :count).by(0)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -45,6 +45,20 @@ module Spree
|
||||
expect(originator).to receive(:update_adjustment)
|
||||
adjustment.update!
|
||||
end
|
||||
|
||||
context "using the :force argument" do
|
||||
it "should update adjustments without changing their state" do
|
||||
expect(originator).to receive(:update_adjustment)
|
||||
adjustment.update!(force: true)
|
||||
expect(adjustment.state).to eq "open"
|
||||
end
|
||||
|
||||
it "should update closed adjustments" do
|
||||
adjustment.close
|
||||
expect(originator).to receive(:update_adjustment)
|
||||
adjustment.update!(force: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "should do nothing when originator is nil" do
|
||||
@@ -307,7 +321,7 @@ module Spree
|
||||
|
||||
context "when enterprise fees have a fixed tax_category" do
|
||||
before do
|
||||
order.reload.update_distribution_charge!
|
||||
order.reload.recreate_all_fees!
|
||||
end
|
||||
|
||||
context "when enterprise fees are taxed per-order" do
|
||||
@@ -326,7 +340,7 @@ module Spree
|
||||
before do
|
||||
fee_tax_rate.update_attribute :included_in_price, false
|
||||
order.reload.create_tax_charge! # Updating line_item or order has the same effect
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "records the tax on TaxRate adjustment on the order" do
|
||||
@@ -339,7 +353,7 @@ module Spree
|
||||
before do
|
||||
enterprise_fee.tax_category = nil
|
||||
enterprise_fee.save!
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "records no tax as charged" do
|
||||
@@ -361,7 +375,7 @@ module Spree
|
||||
before do
|
||||
fee_tax_rate.update_attribute :included_in_price, false
|
||||
order.reload.create_tax_charge! # Updating line_item or order has the same effect
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "records the tax on TaxRate adjustment on the order" do
|
||||
@@ -382,7 +396,7 @@ module Spree
|
||||
variant.product.update_attribute(:tax_category_id, product_tax_category.id)
|
||||
|
||||
order.create_tax_charge! # Updating line_item or order has the same effect
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
context "when enterprise fees are taxed per-order" do
|
||||
@@ -402,7 +416,7 @@ module Spree
|
||||
before do
|
||||
product_tax_rate.update_attribute :included_in_price, false
|
||||
order.reload.create_tax_charge! # Updating line_item or order has the same effect
|
||||
order.reload.update_distribution_charge!
|
||||
order.reload.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "records the no tax on TaxRate adjustment on the order" do
|
||||
@@ -432,7 +446,7 @@ module Spree
|
||||
before do
|
||||
product_tax_rate.update_attribute :included_in_price, false
|
||||
order.reload.create_tax_charge! # Updating line_item or order has the same effect
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "records the tax on TaxRate adjustment on the order" do
|
||||
|
||||
@@ -533,26 +533,36 @@ describe Spree::Order do
|
||||
end
|
||||
end
|
||||
|
||||
describe "updating the distribution charge" do
|
||||
let(:order) { build(:order) }
|
||||
describe "applying enterprise fees" do
|
||||
let(:fee_handler) { ::OrderFeesHandler.new(subject) }
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:fee_handler) { fee_handler }
|
||||
end
|
||||
|
||||
it "clears all enterprise fee adjustments on the order" do
|
||||
expect(EnterpriseFee).to receive(:clear_all_adjustments_on_order).with(subject)
|
||||
subject.update_distribution_charge!
|
||||
expect(EnterpriseFee).to receive(:clear_all_adjustments).with(subject)
|
||||
subject.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "creates line item and order fee adjustments via OrderFeesHandler" do
|
||||
expect(fee_handler).to receive(:create_line_item_fees!)
|
||||
expect(fee_handler).to receive(:create_order_fees!)
|
||||
subject.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "skips order cycle per-order adjustments for orders that don't have an order cycle" do
|
||||
allow(EnterpriseFee).to receive(:clear_all_adjustments_on_order)
|
||||
allow(EnterpriseFee).to receive(:clear_all_adjustments)
|
||||
|
||||
allow(subject).to receive(:order_cycle) { nil }
|
||||
|
||||
subject.update_distribution_charge!
|
||||
subject.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "ensures the correct adjustment(s) are created for order cycles" do
|
||||
allow(EnterpriseFee).to receive(:clear_all_adjustments_on_order)
|
||||
allow(EnterpriseFee).to receive(:clear_all_adjustments)
|
||||
line_item = create(:line_item, order: subject)
|
||||
allow(subject).to receive(:provided_by_order_cycle?) { true }
|
||||
allow(fee_handler).to receive(:provided_by_order_cycle?) { true }
|
||||
|
||||
order_cycle = double(:order_cycle)
|
||||
expect_any_instance_of(OpenFoodNetwork::EnterpriseFeeCalculator).
|
||||
@@ -561,11 +571,11 @@ describe Spree::Order do
|
||||
allow_any_instance_of(OpenFoodNetwork::EnterpriseFeeCalculator).to receive(:create_order_adjustments_for)
|
||||
allow(subject).to receive(:order_cycle) { order_cycle }
|
||||
|
||||
subject.update_distribution_charge!
|
||||
subject.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "ensures the correct per-order adjustment(s) are created for order cycles" do
|
||||
allow(EnterpriseFee).to receive(:clear_all_adjustments_on_order)
|
||||
allow(EnterpriseFee).to receive(:clear_all_adjustments)
|
||||
|
||||
order_cycle = double(:order_cycle)
|
||||
expect_any_instance_of(OpenFoodNetwork::EnterpriseFeeCalculator).
|
||||
@@ -574,35 +584,7 @@ describe Spree::Order do
|
||||
|
||||
allow(subject).to receive(:order_cycle) { order_cycle }
|
||||
|
||||
subject.update_distribution_charge!
|
||||
end
|
||||
end
|
||||
|
||||
describe "looking up whether a line item can be provided by an order cycle" do
|
||||
it "returns true when the variant is provided" do
|
||||
v = double(:variant)
|
||||
line_item = double(:line_item, variant: v)
|
||||
order_cycle = double(:order_cycle, variants: [v])
|
||||
allow(subject).to receive(:order_cycle) { order_cycle }
|
||||
|
||||
expect(subject.send(:provided_by_order_cycle?, line_item)).to be true
|
||||
end
|
||||
|
||||
it "returns false otherwise" do
|
||||
v = double(:variant)
|
||||
line_item = double(:line_item, variant: v)
|
||||
order_cycle = double(:order_cycle, variants: [])
|
||||
allow(subject).to receive(:order_cycle) { order_cycle }
|
||||
|
||||
expect(subject.send(:provided_by_order_cycle?, line_item)).to be false
|
||||
end
|
||||
|
||||
it "returns false when there is no order cycle" do
|
||||
v = double(:variant)
|
||||
line_item = double(:line_item, variant: v)
|
||||
allow(subject).to receive(:order_cycle) { nil }
|
||||
|
||||
expect(subject.send(:provided_by_order_cycle?, line_item)).to be false
|
||||
subject.recreate_all_fees!
|
||||
end
|
||||
end
|
||||
|
||||
@@ -810,7 +792,7 @@ describe Spree::Order do
|
||||
order.add_variant v1
|
||||
order.add_variant v2
|
||||
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
it "removes the variant's line item" do
|
||||
|
||||
@@ -56,7 +56,7 @@ describe TagRule::DiscountOrder, type: :model do
|
||||
let!(:order) { line_item.order }
|
||||
|
||||
before do
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
tag_rule.calculator.update_attribute(:preferred_flat_percent, -10.00)
|
||||
tag_rule.context = { subject: order }
|
||||
end
|
||||
|
||||
61
spec/services/order_fees_handler_spec.rb
Normal file
61
spec/services/order_fees_handler_spec.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe OrderFeesHandler do
|
||||
let(:order_cycle) { create(:order_cycle) }
|
||||
let(:order) { create(:order_with_line_items, line_items_count: 1, order_cycle: order_cycle) }
|
||||
let(:line_item) { order.line_items.first }
|
||||
|
||||
let(:service) { OrderFeesHandler.new(order) }
|
||||
let(:calculator) {
|
||||
double(OpenFoodNetwork::EnterpriseFeeCalculator, create_order_adjustments_for: true)
|
||||
}
|
||||
|
||||
before do
|
||||
allow(service).to receive(:calculator) { calculator }
|
||||
end
|
||||
|
||||
describe "#create_line_item_fees!" do
|
||||
it "creates per-line-item fee adjustments for line items in the order cycle" do
|
||||
allow(service).to receive(:provided_by_order_cycle?) { true }
|
||||
expect(calculator).to receive(:create_line_item_adjustments_for).with(line_item)
|
||||
|
||||
service.create_line_item_fees!
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_order_fees!" do
|
||||
it "creates per-order adjustment for the order cycle" do
|
||||
expect(calculator).to receive(:create_order_adjustments_for).with(order)
|
||||
service.create_order_fees!
|
||||
end
|
||||
|
||||
it "skips per-order fee adjustments for orders that don't have an order cycle" do
|
||||
allow(service).to receive(:order_cycle) { nil }
|
||||
expect(calculator).to_not receive(:create_order_adjustments_for)
|
||||
|
||||
service.create_order_fees!
|
||||
end
|
||||
end
|
||||
|
||||
context "checking if a line item can be provided by the order cycle" do
|
||||
it "returns true when the variant is provided" do
|
||||
allow(order_cycle).to receive(:variants) { [line_item.variant] }
|
||||
|
||||
expect(service.__send__(:provided_by_order_cycle?, line_item)).to be true
|
||||
end
|
||||
|
||||
it "returns false otherwise" do
|
||||
allow(order_cycle).to receive(:variants) { [] }
|
||||
|
||||
expect(service.__send__(:provided_by_order_cycle?, line_item)).to be false
|
||||
end
|
||||
|
||||
it "returns false when there is no order cycle" do
|
||||
allow(order).to receive(:order_cycle) { nil }
|
||||
|
||||
expect(service.__send__(:provided_by_order_cycle?, line_item)).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -81,7 +81,7 @@ describe OrderTaxAdjustmentsFetcher do
|
||||
|
||||
before do
|
||||
order.create_tax_charge!
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
subject { OrderTaxAdjustmentsFetcher.new(order).totals }
|
||||
|
||||
@@ -42,7 +42,7 @@ module ShopWorkflow
|
||||
cart_service.populate(variants: { product.variants.first.id => quantity })
|
||||
|
||||
# Recalculate fee totals
|
||||
order.update_distribution_charge!
|
||||
order.recreate_all_fees!
|
||||
end
|
||||
|
||||
# Add an item to the cart
|
||||
|
||||
Reference in New Issue
Block a user