Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

module SolidusPromotions
module AdjustmentDiscounts
private

def discounts_by_lanes(lanes)
adjustments.select do |adjustment|
!adjustment.marked_for_destruction? &&
adjustment.source_type == "SolidusPromotions::Benefit" &&
adjustment.source.promotion.lane.to_sym.in?(lanes.map(&:to_sym))
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

module SolidusPromotions
module DiscountedAmount
def discounted_amount
amount + previous_lane_discounts.sum(&:amount)
end

def current_lane_discounts
raise NotCalculatingPromotions unless PromotionLane.current

discounts_by_lanes([PromotionLane.current])
end

private

def previous_lane_discounts
discounts_by_lanes(PromotionLane.before_current)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module SolidusPromotions
class PromotionLane < ActiveSupport::CurrentAttributes
attribute :current

def ordered
Promotion.lanes.keys.sort_by { |lane| Promotion.lanes[lane] }
end

def before(lane)
ordered.split(lane.to_s).first
end

def before_current
return ordered unless current
before(current)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

module SolidusPromotions
class NotCalculatingPromotions < StandardError; end
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def initialize(order, promotions, dry_run: false)
def call
return order if order.shipped?

SolidusPromotions::Promotion.ordered_lanes.each do |lane|
SolidusPromotions::PromotionLane.ordered.each do |lane|
lane_promotions = promotions.select { |promotion| promotion.lane == lane }
lane_benefits = eligible_benefits_for_promotable(lane_promotions.flat_map(&:benefits), order)
perform_order_benefits(lane_benefits, lane) unless dry_run
Expand Down
6 changes: 1 addition & 5 deletions promotions/app/models/solidus_promotions/promotion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,11 @@ def self.human_enum_name(enum_name, enum_value)
end

def self.lane_options
ordered_lanes.map do |lane|
PromotionLane.ordered.map do |lane|
[human_enum_name(:lane, lane), lane]
end
end

def self.ordered_lanes
lanes.keys.sort_by { |lane| lanes[lane] }
end

def self.order_activatable?(order)
return false if UNACTIVATABLE_ORDER_STATES.include?(order.state)
return false if order.shipped?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def reset_quantity_setter
end

Spree::LineItem.prepend self
Spree::LineItem.prepend SolidusPromotions::AdjustmentDiscounts
Spree::LineItem.prepend SolidusPromotions::DiscountedAmount
Spree::LineItem.prepend SolidusPromotions::DiscountableAmount
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ def reset_current_discounts
end

Spree::Shipment.prepend self
Spree::Shipment.prepend SolidusPromotions::AdjustmentDiscounts
Spree::Shipment.prepend SolidusPromotions::DiscountedAmount
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ def promo_total
discounts.sum(&:amount)
end

private

def discounts_by_lanes(lanes)
discounts.select do |discount|
!discount.marked_for_destruction? &&
discount.benefit.promotion.lane.to_sym.in?(lanes.map(&:to_sym))
end
end

Spree::ShippingRate.prepend SolidusPromotions::DiscountedAmount
Spree::ShippingRate.prepend SolidusPromotions::DiscountableAmount
Spree::ShippingRate.prepend self
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe SolidusPromotions::PromotionLane do
describe ".before_current" do
let(:lane) { :pre }

subject { described_class.before_current }

it { is_expected.to eq(["pre", "default", "post"]) }

context "if lane is given" do
let(:lane) { :pre }

around do |example|
described_class.set(current: lane) do
example.run
end
end

it { is_expected.to be_empty }

context "if lane is default" do
let(:lane) { :default }
it { is_expected.to eq(["pre"]) }
end

context "if lane is post" do
let(:lane) { :post }
it { is_expected.to eq(["pre", "default"]) }
end
end
end

describe ".set(current:)" do
let(:lane) { :pre }

it "runs blocks with current_lane set to lane" do
expect(described_class.current).to be nil
described_class.set(current: lane) do
expect(described_class.current).to eq(:pre)
end
expect(described_class.current).to be nil
end

it "can be nested" do
expect(described_class.current).to be nil
described_class.set(current: lane) do
expect(described_class.current).to eq(:pre)
described_class.set(current: "default") do
expect(described_class.current).to eq("default")
end
expect(described_class.current).to eq(:pre)
end
expect(described_class.current).to be nil
end
end

describe ".ordered" do
subject { described_class.ordered }

it { is_expected.to eq(%w[pre default post]) }
end
end
6 changes: 0 additions & 6 deletions promotions/spec/models/solidus_promotions/promotion_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,6 @@
end
end

describe ".ordered_lanes" do
subject { described_class.ordered_lanes }

it { is_expected.to eq(%w[pre default post]) }
end

describe "validations" do
subject(:promotion) { build(:solidus_promotion) }

Expand Down
57 changes: 57 additions & 0 deletions promotions/spec/models/spree/line_item_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,63 @@
end
end

describe "#current_lane_discounts" do
let(:order) { Spree::Order.new }
let(:tax_rate) { create(:tax_rate) }
let(:pre_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :pre) }
let(:post_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :post) }
let(:line_item) { Spree::LineItem.new(adjustments:, order:) }
let(:adjustments) { [tax_adjustment, pre_lane_adjustment, post_lane_adjustment] }
let(:tax_adjustment) { Spree::Adjustment.new(source: tax_rate, amount: 2) }
let(:pre_lane_adjustment) { Spree::Adjustment.new(source: pre_lane_promotion.benefits.first) }
let(:post_lane_adjustment) { Spree::Adjustment.new(source: post_lane_promotion.benefits.first) }

subject { line_item.current_lane_discounts }

it "raises unless we're doing a promotion calculation" do
expect { subject }.to raise_exception(SolidusPromotions::NotCalculatingPromotions)
end

context "while calculating promotions" do
around do |example|
SolidusPromotions::PromotionLane.set(current: lane) do
example.run
end
end

let(:lane) { "pre" }
it { is_expected.to contain_exactly(pre_lane_adjustment) }

context "if lane is default" do
let(:lane) { "default" }

it { is_expected.to be_empty }
end

context "if lane is post" do
let(:lane) { "post" }

it { is_expected.to contain_exactly(post_lane_adjustment) }
end
end
end

describe "#discounted_amount" do
let(:order) { Spree::Order.new }
let(:tax_rate) { create(:tax_rate) }
let(:pre_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :pre) }
let(:post_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :post) }
let(:line_item) { Spree::LineItem.new(adjustments:, order:, price: 14, quantity: 2) }
let(:adjustments) { [tax_adjustment, pre_lane_adjustment, post_lane_adjustment] }
let(:tax_adjustment) { Spree::Adjustment.new(source: tax_rate, amount: 2) }
let(:pre_lane_adjustment) { Spree::Adjustment.new(source: pre_lane_promotion.benefits.first, amount: -3) }
let(:post_lane_adjustment) { Spree::Adjustment.new(source: post_lane_promotion.benefits.first, amount: -2) }

subject { line_item.discounted_amount }

it { is_expected.to eq(23) }
end

describe "changing quantities" do
context "when line item is managed by an automation" do
let(:order) { create(:order) }
Expand Down
57 changes: 57 additions & 0 deletions promotions/spec/models/spree/shipment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,61 @@
end
end
end

describe "#discounted_amount" do
let(:order) { Spree::Order.new }
let(:tax_rate) { create(:tax_rate) }
let(:pre_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :pre) }
let(:post_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :post) }
let(:shipment) { Spree::Shipment.new(adjustments:, order:, cost: 28) }
let(:adjustments) { [tax_adjustment, pre_lane_adjustment, post_lane_adjustment] }
let(:tax_adjustment) { Spree::Adjustment.new(source: tax_rate, amount: 2) }
let(:pre_lane_adjustment) { Spree::Adjustment.new(source: pre_lane_promotion.benefits.first, amount: -3) }
let(:post_lane_adjustment) { Spree::Adjustment.new(source: post_lane_promotion.benefits.first, amount: -2) }

subject { shipment.discounted_amount }

it { is_expected.to eq(23) }
end

describe "#current_lane_discounts" do
let(:order) { Spree::Order.new }
let(:tax_rate) { create(:tax_rate) }
let(:pre_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :pre) }
let(:post_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :post) }
let(:shipment) { Spree::Shipment.new(adjustments:, order:) }
let(:adjustments) { [tax_adjustment, pre_lane_adjustment, post_lane_adjustment] }
let(:tax_adjustment) { Spree::Adjustment.new(source: tax_rate, amount: 2) }
let(:pre_lane_adjustment) { Spree::Adjustment.new(source: pre_lane_promotion.benefits.first) }
let(:post_lane_adjustment) { Spree::Adjustment.new(source: post_lane_promotion.benefits.first) }

subject { shipment.current_lane_discounts }

it "raises unless we're doing a promotion calculation" do
expect { subject }.to raise_exception(SolidusPromotions::NotCalculatingPromotions)
end

context "while calculating promotions" do
around do |example|
SolidusPromotions::PromotionLane.set(current: lane) do
example.run
end
end

let(:lane) { "pre" }
it { is_expected.to contain_exactly(pre_lane_adjustment) }

context "if lane is default" do
let(:lane) { "default" }

it { is_expected.to be_empty }
end

context "if lane is post" do
let(:lane) { "post" }

it { is_expected.to contain_exactly(post_lane_adjustment) }
end
end
end
end
57 changes: 57 additions & 0 deletions promotions/spec/models/spree/shipping_rate_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,61 @@
expect { subject }.to change { shipping_rate.current_discounts.length }.from(1).to(0)
end
end

describe "#current_lane_discounts" do
let(:order) { Spree::Order.new }
let(:shipment) { Spree::Shipment.new(order:) }
let(:tax_rate) { create(:tax_rate) }
let(:pre_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :pre) }
let(:post_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :post) }
let(:shipping_rate) { Spree::ShippingRate.new(discounts:, shipment:) }
let(:discounts) { [pre_lane_discount, post_lane_discount] }
let(:pre_lane_discount) { SolidusPromotions::ShippingRateDiscount.new(benefit: pre_lane_promotion.benefits.first) }
let(:post_lane_discount) { SolidusPromotions::ShippingRateDiscount.new(benefit: post_lane_promotion.benefits.first) }

subject { shipping_rate.current_lane_discounts }

it "raises unless we're doing a promotion calculation" do
expect { subject }.to raise_exception(SolidusPromotions::NotCalculatingPromotions)
end

context "while calculating promotions" do
around do |example|
SolidusPromotions::PromotionLane.set(current: lane) do
example.run
end
end

let(:lane) { "pre" }
it { is_expected.to contain_exactly(pre_lane_discount) }

context "if lane is default" do
let(:lane) { "default" }

it { is_expected.to be_empty }
end

context "if lane is post" do
let(:lane) { "post" }

it { is_expected.to contain_exactly(post_lane_discount) }
end
end
end

describe "#discounted_amount" do
let(:order) { Spree::Order.new }
let(:shipment) { Spree::Shipment.new(order:) }
let(:tax_rate) { create(:tax_rate) }
let(:pre_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :pre) }
let(:post_lane_promotion) { create(:solidus_promotion, :with_adjustable_benefit, lane: :post) }
let(:shipping_rate) { Spree::ShippingRate.new(discounts:, shipment:, amount: 28) }
let(:discounts) { [pre_lane_discount, post_lane_discount] }
let(:pre_lane_discount) { SolidusPromotions::ShippingRateDiscount.new(benefit: pre_lane_promotion.benefits.first, amount: -2) }
let(:post_lane_discount) { SolidusPromotions::ShippingRateDiscount.new(benefit: post_lane_promotion.benefits.first, amount: -3) }

subject { shipping_rate.discounted_amount }

it { is_expected.to eq(23) }
end
end
Loading