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

Allow order rules per lane #20

Merged
merged 2 commits into from
Oct 10, 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
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ def ensure_promotions_eligible
super
end

def discountable_item_total
line_items.sum(&:discountable_amount)
end

def reset_current_discounts
line_items.each(&:reset_current_discounts)
shipments.each(&:reset_current_discounts)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ class FriendlyPromotionDiscounter

def initialize(order)
@order = order
possible_promotions = PromotionLoader.new(order: order).call
@promotions = PromotionEligibility.new(promotable: order, possible_promotions: possible_promotions).call
@promotions = PromotionLoader.new(order: order).call
end

def call
Expand All @@ -16,7 +15,10 @@ def call
order.reset_current_discounts

SolidusFriendlyPromotions::Promotion.ordered_lanes.each do |lane, _index|
lane_promotions = promotions.select { |promotion| promotion.lane == lane }
lane_promotions = PromotionEligibility.new(
promotable: order,
possible_promotions: promotions.select { |promotion| promotion.lane == lane }
).call
item_discounter = ItemDiscounter.new(promotions: lane_promotions)
line_item_discounts = adjust_line_items(item_discounter)
shipment_discounts = adjust_shipments(item_discounter)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

module SolidusFriendlyPromotions
module Rules
# A rule to apply to an order greater than (or greater than or equal to)
# a specific amount after previous promotions have applied
#
# To add extra operators please override `self.operators_map` or any other helper method.
# To customize the error message you can also override `ineligible_message`.
class DiscountedItemTotal < ItemTotal
def to_partial_path
"solidus_friendly_promotions/admin/promotion_rules/rules/item_total"
end

private

def total_for_order(order)
order.discountable_item_total
end
end
end
end
1 change: 1 addition & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ en:
solidus_friendly_promotions/rules/first_order: First Order
solidus_friendly_promotions/rules/first_repeat_purchase_since: First Repeat Purchase Since
solidus_friendly_promotions/rules/item_total: Item Total
solidus_friendly_promotions/rules/discounted_item_total: Item Total after previous lanes
solidus_friendly_promotions/rules/landing_page: Landing Page
solidus_friendly_promotions/rules/nth_order: Nth Order
solidus_friendly_promotions/rules/one_use_per_user: One Use Per User
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"SolidusFriendlyPromotions::Rules::FirstOrder",
"SolidusFriendlyPromotions::Rules::FirstRepeatPurchaseSince",
"SolidusFriendlyPromotions::Rules::ItemTotal",
"SolidusFriendlyPromotions::Rules::DiscountedItemTotal",
"SolidusFriendlyPromotions::Rules::NthOrder",
"SolidusFriendlyPromotions::Rules::OneUsePerUser",
"SolidusFriendlyPromotions::Rules::OptionValue",
Expand Down
20 changes: 20 additions & 0 deletions spec/models/promotion/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,17 @@
context "with two promotions that should stack" do
let(:shirt) { create(:product, name: "Shirt", price: 30) }
let(:pants) { create(:product, name: "Pants", price: 40) }
let(:discounted_item_total_rule_amount) { 60 }
let(:discounted_item_total_rule) do
SolidusFriendlyPromotions::Rules::DiscountedItemTotal.new(preferred_amount: discounted_item_total_rule_amount)
end

let!(:distributed_amount_promo) do
create(:friendly_promotion,
:with_adjustable_action,
preferred_amount: 10.0,
apply_automatically: true,
rules: [discounted_item_total_rule],
lane: :post,
calculator_class: SolidusFriendlyPromotions::Calculators::DistributedAmount)
end
Expand Down Expand Up @@ -92,6 +97,21 @@
expect(order.item_total_before_tax).to eq(54)
expect(order.line_items.flat_map(&:adjustments).length).to eq(3)
end

context "if the post lane promotion is ineligible" do
let(:discounted_item_total_rule_amount) { 68 }

it "does all the right things" do
expect(order.adjustments).to be_empty
# shirt: 30 USD - 20% = 24 USD
# Remaining total: 64 USD
# The 10 off promotion does not apply because now the order total is below 68
expect(order.total).to eq(64.00)
expect(order.item_total).to eq(70.00)
expect(order.item_total_before_tax).to eq(64)
expect(order.line_items.flat_map(&:adjustments).length).to eq(1)
end
end
mamhoff marked this conversation as resolved.
Show resolved Hide resolved
end

context "with a shipment-level rule" do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe SolidusFriendlyPromotions::Rules::DiscountedItemTotal, type: :model do
let(:rule) do
described_class.new(
preferred_amount: preferred_amount,
preferred_operator: preferred_operator
)
end
let(:order) { instance_double("Spree::Order", discountable_item_total: item_total, currency: order_currency) }
let(:preferred_amount) { 50 }
let(:order_currency) { "USD" }

context "preferred operator set to gt" do
let(:preferred_operator) { "gt" }

context "item total is greater than preferred amount" do
let(:item_total) { 51 }

it "is eligible when item total is greater than preferred amount" do
expect(rule).to be_eligible(order)
end

context "when the order is a different currency" do
let(:order_currency) { "CAD" }

it "is not eligible" do
expect(rule).not_to be_eligible(order)
end
end
end

context "when item total is equal to preferred amount" do
let(:item_total) { 50 }

it "is not eligible" do
expect(rule).not_to be_eligible(order)
end

it "set an error message" do
rule.eligible?(order)
expect(rule.eligibility_errors.full_messages.first)
.to eq "This coupon code can't be applied to orders less than or equal to $50.00."
end

it "sets an error code" do
rule.eligible?(order)
expect(rule.eligibility_errors.details[:base].first[:error_code])
.to eq :item_total_less_than_or_equal
end
end

context "when item total is lower than preferred amount" do
let(:item_total) { 49 }

it "is not eligible" do
expect(rule).not_to be_eligible(order)
end

it "set an error message" do
rule.eligible?(order)
expect(rule.eligibility_errors.full_messages.first)
.to eq "This coupon code can't be applied to orders less than or equal to $50.00."
end

it "sets an error code" do
rule.eligible?(order)
expect(rule.eligibility_errors.details[:base].first[:error_code])
.to eq :item_total_less_than_or_equal
end
end
end

context "preferred operator set to gte" do
let(:preferred_operator) { "gte" }

context "total is greater than preferred amount" do
let(:item_total) { 51 }

it "is eligible when item total is greater than preferred amount" do
expect(rule).to be_eligible(order)
end

context "when the order is a different currency" do
let(:order_currency) { "CAD" }

it "is not eligible" do
expect(rule).not_to be_eligible(order)
end
end
end

context "item total is equal to preferred amount" do
let(:item_total) { 50 }

it "is eligible" do
expect(rule).to be_eligible(order)
end

context "when the order is a different currency" do
let(:order_currency) { "CAD" }

it "is not eligible" do
expect(rule).not_to be_eligible(order)
end
end
end

context "when item total is lower than preferred amount" do
let(:item_total) { 49 }

it "is not eligible" do
expect(rule).not_to be_eligible(order)
end

it "set an error message" do
rule.eligible?(order)
expect(rule.eligibility_errors.full_messages.first)
.to eq "This coupon code can't be applied to orders less than $50.00."
end

it "sets an error code" do
rule.eligible?(order)
expect(rule.eligibility_errors.details[:base].first[:error_code])
.to eq :item_total_less_than
end
end
end
end