-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add #discounted_amount by adjustments
#6379
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
Changes from all commits
b453888
9e43a81
0368520
51edebf
0e9ba23
a3dd96e
ff58cdd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module SolidusPromotions | ||
| module AdjustmentDiscounts | ||
| private | ||
|
|
||
| # Returns adjustments from specified promotion lanes. | ||
| # | ||
| # @param lanes [Array<String>] the promotion lanes to filter by | ||
| # @return [Array<Spree::Adjustment>] promotions adjustments from the | ||
| # specified lanes that are not marked for destruction | ||
| def discounts_by_lanes(lanes) | ||
| adjustments.select do |adjustment| | ||
| !adjustment.marked_for_destruction? && | ||
| adjustment.source_type == "SolidusPromotions::Benefit" && | ||
| adjustment.source.promotion.lane.in?(lanes) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module SolidusPromotions | ||
| module DiscountedAmount | ||
| class NotCalculatingPromotions < StandardError | ||
| DEFAULT_MESSAGE = <<~MSG | ||
| You're trying to call `#current_lane_discounts` without a current lane being set on `SolidusPromotions::PromotionLane. | ||
| In order to set a current lane, wrap your call into a `PromotionLane.set` block: | ||
| ``` | ||
| SolidusPromotions::PromotionLane.set(current_lane: "default") do | ||
| # YOUR CODE HERE | ||
| end | ||
| ``` | ||
| MSG | ||
|
|
||
| def initialize | ||
| super(DEFAULT_MESSAGE) | ||
| end | ||
| end | ||
|
|
||
| # Calculates the total discounted amount including adjustments from previous lanes. | ||
| # | ||
| # @return [BigDecimal] the sum of the current amount and all previous lane discount amounts | ||
| def discounted_amount | ||
| amount + previous_lanes_discounts.sum(&:amount) | ||
| end | ||
|
|
||
| # Returns discount objects from the current promotion lane. | ||
| # | ||
| # @return [Array<Spree::Adjustment,SolidusPromotions::ShippingRateDiscount>] Discounts from the current lane | ||
| # @raise [NotCalculatingPromotions] if no promotion lane is currently being calculated | ||
| def current_lane_discounts | ||
| raise NotCalculatingPromotions unless PromotionLane.current_lane | ||
|
|
||
| discounts_by_lanes([PromotionLane.current_lane]) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| # Returns discount objects added by promotion in lanes that come before the current lane. | ||
| # | ||
| # This method retrieves all discounts that were applied by promotion lanes with a priority | ||
| # lower than the current lane, effectively getting discounts from earlier processing stages. | ||
| # | ||
| # @return [Array<Spree::Adjustment,SolidusPromotions::ShippingRateDiscount>] Discounts from previous lanes | ||
| # @see #discounts_by_lanes | ||
| # @see PromotionLane.previous_lanes | ||
| def previous_lanes_discounts | ||
| discounts_by_lanes(PromotionLane.previous_lanes) | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module SolidusPromotions | ||
| # PromotionLane is a thread-safe current attributes class that manages the current promotion lane context. | ||
| # | ||
| # This class extends ActiveSupport::CurrentAttributes to provide thread-local storage for the current | ||
| # promotion lane. It allows setting and retrieving the current lane, as well as getting all lanes | ||
| # that come before the current one. | ||
| # | ||
| # @example Setting and retrieving the current lane | ||
| # PromotionLane.current_lane = :pre | ||
| # PromotionLane.current_lane # => "pre" | ||
| # | ||
| # @example Getting lanes before the current one | ||
| # PromotionLane.current_lane = :post | ||
| # PromotionLane.previous_lanes # => ["pre"] | ||
| # | ||
| # @see ActiveSupport::CurrentAttributes | ||
| class PromotionLane < ActiveSupport::CurrentAttributes | ||
| attribute :current_lane | ||
|
|
||
| def current_lane=(arg) | ||
| if arg.present? | ||
| super(arg.to_s) | ||
| else | ||
| super | ||
| end | ||
| end | ||
|
|
||
| # Retrieves the lanes that occur before the current lane in the promotion flow. | ||
| # | ||
| # Delegates to `before(current_lane)` to compute the preceding lanes. | ||
| # | ||
| # Special considerations: | ||
| # - If `current_lane` is `nil`, all lanes are returned. | ||
| # | ||
| # @return [Array<String>] the set of lanes preceding the current lane; all lanes if no current lane is set | ||
| def previous_lanes | ||
| before(current_lane) | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def before(lane) | ||
| Promotion.ordered_lanes.split(lane).first | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line is critical, and it took me some time to realize why. Having
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even better: When calculating discounts for prices (as we want to do in the future), we can even get the discount for a line item within the lane the price promotion is in. It'll be pretty neat. |
||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require "rails_helper" | ||
|
|
||
| RSpec.describe SolidusPromotions::PromotionLane do | ||
| describe ".before_current" do | ||
| let(:lane) { :pre } | ||
|
|
||
| subject { described_class.previous_lanes } | ||
|
|
||
| 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: 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_lane).to be nil | ||
| described_class.set(current_lane: lane) do | ||
| expect(described_class.current_lane).to eq("pre") | ||
| end | ||
| expect(described_class.current_lane).to be nil | ||
| end | ||
|
|
||
| it "can be nested" do | ||
| expect(described_class.current_lane).to be nil | ||
| described_class.set(current_lane: lane) do | ||
| expect(described_class.current_lane).to eq("pre") | ||
| described_class.set(current_lane: "default") do | ||
| expect(described_class.current_lane).to eq("default") | ||
| end | ||
| expect(described_class.current_lane).to eq("pre") | ||
| end | ||
| expect(described_class.current_lane).to be nil | ||
| end | ||
| end | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.