Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# frozen_string_literal: true

require "rails_helper"

RSpec.describe SolidusPromotions::PromotionEligibilityChecker do
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(:checker) { described_class.new(order:, promotion:) }

subject { checker.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" 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