Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
@@ -0,0 +1,31 @@
# frozen_string_literal: true

module SolidusPromotions
class PromotionEligibilityChecker
attr_reader :order, :promotion
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are these meant to be public?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Well, results definitely is.


def initialize(order:, promotion:)
@order = order
@promotion = promotion
end

def call
SolidusPromotions::PromotionLane.set(current_lane: promotion.lane) do
promotion.benefits.each do |benefit|
benefit.eligible_by_applicable_conditions?(order, dry_run: true)
order.line_items.each do |line_item|
check_item(line_item, benefit)
end
order.shipments.each do |shipment|
check_item(shipment, benefit)
end
Comment on lines +16 to +21
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(order.line_items + order.shipments).each do |item|
  check_item(item, benefit)
end

end
end
end

def check_item(item, benefit)
benefit.can_discount?(item) &&
benefit.eligible_by_applicable_conditions?(item, dry_run: true)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def handle_present_promotion
return promotion_usage_limit_exceeded if promotion.usage_limit_exceeded? || promotion_code.usage_limit_exceeded?
return promotion_applied if promotion_exists_on_order?(order, promotion)

# Try applying this promotion, with no effects
Spree::Config.promotions.order_adjuster_class.new(order, dry_run_promotion: promotion).call
# Check promotion applicability
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

*eligibility

Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion).call

if promotion.eligibility_results.success?
order.solidus_order_promotions.create!(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def initialize(order, path)

def activate
if promotion
Spree::Config.promotions.order_adjuster_class.new(order, dry_run_promotion: promotion).call
Spree::Config.promotions.eligibility_checker_class.new(order: order, promotion: promotion).call
if promotion.eligibility_results.success?
order.solidus_promotions << promotion
order.recalculate
Expand Down
5 changes: 5 additions & 0 deletions promotions/lib/solidus_promotions/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ class Configuration < Spree::Preferences::Configuration

class_name_attribute :order_adjuster_class, default: "SolidusPromotions::OrderAdjuster"

# Service object that checks whether a promotion is eligible for an order.
# @!attribute [rw] eligibility_checker_class
# @return [String] Defaults to "SolidusPromotions::PromotionEligibilityChecker".
class_name_attribute :eligibility_checker_class, default: "SolidusPromotions::PromotionEligibilityChecker"

class_name_attribute :coupon_code_handler_class, default: "SolidusPromotions::PromotionHandler::Coupon"

# The class used to normalize coupon codes before saving or lookup.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
end
end

describe ".eligibility_checker_class" do
it "is the standard eligibility checker" do
expect(subject.eligibility_checker_class).to eq(SolidusPromotions::PromotionEligibilityChecker)
end
end

describe ".advertiser_class" do
it "is the standard advertiser" do
expect(subject.advertiser_class).to eq(SolidusPromotions::PromotionAdvertiser)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,178 +32,4 @@
expect { subject }.not_to raise_exception
end
end

describe "collecting eligibility results in a dry run" do
let(:shirt) { create(:product, name: "Shirt") }
let(:order) { create(:order_with_line_items, line_items_attributes: [{variant: shirt.master, quantity: 1}]) }
let(:conditions) { [product_condition] }
let!(:promotion) { create(:solidus_promotion, :with_adjustable_benefit, conditions: conditions, name: "20% off Shirts", apply_automatically: true) }
let(:product_condition) { SolidusPromotions::Conditions::OrderProduct.new(products: [shirt]) }
let(:promotions) { [promotion] }
let(:discounter) { described_class.new(order, promotions, dry_run: true) }

subject { discounter.call }

it "will collect eligibility results" do
subject

expect(promotion.eligibility_results.first.success).to be true
expect(promotion.eligibility_results.first.code).to be nil
expect(promotion.eligibility_results.first.condition).to eq(product_condition)
expect(promotion.eligibility_results.first.message).to be nil
expect(promotion.eligibility_results.first.item).to eq(order)
end

it "can tell us about success" do
subject
expect(promotion.eligibility_results.success?).to be true
end

context "with two conditions" do
let(:conditions) { [product_condition, item_total_condition] }
let(:item_total_condition) { SolidusPromotions::Conditions::ItemTotal.new(preferred_amount: 2000) }

it "will collect eligibility results" do
subject

expect(promotion.eligibility_results.first.success).to be true
expect(promotion.eligibility_results.first.code).to be nil
expect(promotion.eligibility_results.first.condition).to eq(product_condition)
expect(promotion.eligibility_results.first.message).to be nil
expect(promotion.eligibility_results.first.item).to eq(order)
expect(promotion.eligibility_results.last.success).to be false
expect(promotion.eligibility_results.last.condition).to eq(item_total_condition)
expect(promotion.eligibility_results.last.code).to eq :item_total_less_than_or_equal
expect(promotion.eligibility_results.last.message).to eq "This coupon code can't be applied to orders less than or equal to $2,000.00."
expect(promotion.eligibility_results.last.item).to eq(order)
end

it "can tell us about success" do
subject
expect(promotion.eligibility_results.success?).to be false
end

it "has errors for this promo" do
subject
expect(promotion.eligibility_results.error_messages).to eq([
"This coupon code can't be applied to orders less than or equal to $2,000.00."
])
end
end

context "with an order with multiple line items and an item-level condition" do
let(:pants) { create(:product, name: "Pants") }
let(:order) do
create(
:order_with_line_items,
line_items_attributes: [{variant: shirt.master, quantity: 1}, {variant: pants.master, quantity: 1}]
)
end

let(:shirt_product_condition) { SolidusPromotions::Conditions::LineItemProduct.new(products: [shirt]) }
let(:conditions) { [shirt_product_condition] }

it "can tell us about success" do
subject
# This is successful, because one of the line item conditions matches
expect(promotion.eligibility_results.success?).to be true
end

it "has no errors for this promo" do
subject
expect(promotion.eligibility_results.error_messages).to be_empty
end

context "with a second line item level condition" do
let(:hats) { create(:taxon, name: "Hats", products: [hat]) }
let(:hat) { create(:product) }
let(:hat_product_condition) { SolidusPromotions::Conditions::LineItemTaxon.new(taxons: [hats]) }
let(:conditions) { [shirt_product_condition, hat_product_condition] }

it "can tell us about success" do
subject
expect(promotion.eligibility_results.success?).to be false
end

it "has errors for this promo" do
subject
expect(promotion.eligibility_results.error_messages).to eq([
"This coupon code could not be applied to the cart at this time."
])
end
end
end

context "when the order must not contain a shirt" do
let(:no_shirt_condition) { SolidusPromotions::Conditions::OrderProduct.new(products: [shirt], preferred_match_policy: "none") }
let(:conditions) { [no_shirt_condition] }

it "can tell us about success" do
subject
# This is successful, because the order has a shirt
expect(promotion.eligibility_results.success?).to be false
end
end

context "where one benefit succeeds and another errors" do
let(:usps) { create(:shipping_method) }
let(:ups_ground) { create(:shipping_method) }
let(:order) { create(:order_with_line_items, line_items_attributes: [{variant: shirt.master, quantity: 1}], shipping_method: ups_ground) }
let(:product_condition) { SolidusPromotions::Conditions::OrderProduct.new(products: [shirt]) }
let(:shipping_method_condition) { SolidusPromotions::Conditions::ShippingMethod.new(preferred_shipping_method_ids: [usps.id]) }
let(:ten_off_items) { SolidusPromotions::Calculators::Percent.create!(preferred_percent: 10) }
let(:ten_off_shipping) { SolidusPromotions::Calculators::Percent.create!(preferred_percent: 10) }
let(:shipping_benefit) { SolidusPromotions::Benefits::AdjustShipment.new(calculator: ten_off_shipping) }
let(:line_item_benefit) { SolidusPromotions::Benefits::AdjustLineItem.new(calculator: ten_off_items) }
let(:benefits) { [shipping_benefit, line_item_benefit] }
let(:conditions) { [product_condition, shipping_method_condition] }
let!(:promotion) { create(:solidus_promotion, benefits: benefits, name: "10% off Shirts and USPS Shipping", apply_automatically: true) }

before do
shipping_benefit.conditions << shipping_method_condition
line_item_benefit.conditions << product_condition
end

it "can tell us about success" do
subject
expect(promotion.eligibility_results.success?).to be true
end

it "can tell us about errors" do
subject
expect(promotion.eligibility_results.error_messages).to eq(["This coupon code could not be applied to the cart at this time."])
end
end

context "with no conditions" do
let(:conditions) { [] }

it "has no errors for this promo" do
subject
expect(promotion.eligibility_results.error_messages).to be_empty
end
end

context "with an ineligible order-level condition" do
let(:mug) { create(:product) }
let(:order_condition) { SolidusPromotions::Conditions::NthOrder.new(preferred_nth_order: 2) }
let(:line_item_condition) { SolidusPromotions::Conditions::LineItemProduct.new(products: [mug]) }
let(:conditions) { [order_condition, line_item_condition] }

it "can tell us about success" do
subject
expect(promotion.eligibility_results.success?).to be false
end

it "can tell us about all the errors", :pending do
subject
expect(promotion.eligibility_results.error_messages).to eq(
[
"This coupon code could not be applied to the cart at this time.",
"You need to add an applicable product before applying this coupon code."
]
)
end
end
end
end
Loading