solidus_friendly_promotions 1.0.0.pre → 1.0.0.rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '042911d4cf39c2bddcce9c5826f8c8f790e2ca8396cc32d339c7f5c2dea0a834'
4
- data.tar.gz: cf2a4ee16b1d40a6d89771de93b37b145bfce413aab7114a0660cdf0c5798d27
3
+ metadata.gz: ac98e07030bf9f63775dafdffc94d3a6be02b8050664037469f57a32dc71da43
4
+ data.tar.gz: fdd1d7e3fbbfc92cb347af92c28c5f518d91724a330408a576d47449c6e8f201
5
5
  SHA512:
6
- metadata.gz: 5ffa465ffa65e01a30adae44873edabe2fa25594b7c3c09beda6e0e2946ed4e8fdbec547dafc4d22974bbeddb0a978a96c65c5c83f3b300d4e9b0473dc809f23
7
- data.tar.gz: 601306df6e5de918612d12ed6084d2a6ea2b690d15ef13ff36d826a541dc19dabaf84b62ea74c0065f106baf256089df544e9814fe3ddcc4da5635ffc8e74c60
6
+ metadata.gz: c9eb24c1fabf89ee0b61731b8541e1459dfe9d508ffc2721f8f1384df382c1653b94f407c04438a2047ebf0fc868e4849fc5f88b821ab141eac44b21bde910b9
7
+ data.tar.gz: 1e89490f9daf5ad6480b37bc47581bb7ba4a948bb1af7aa7b9539b3c094c6549db4055132b53155e3fc419f881b71d64c81577ba447d188e704eda176bd5b808
@@ -2,6 +2,20 @@
2
2
 
3
3
  module SolidusFriendlyPromotions
4
4
  module LineItemDecorator
5
+ def self.prepended(base)
6
+ base.belongs_to :managed_by_order_action, class_name: "SolidusFriendlyPromotions::PromotionAction", optional: true
7
+ base.validate :validate_managed_quantity_same, on: :update
8
+ end
9
+
10
+ private
11
+
12
+ def validate_managed_quantity_same
13
+ if managed_by_order_action && quantity_changed?
14
+ errors.add(:quantity, :cannot_be_changed_for_automated_items)
15
+ end
16
+ end
17
+
18
+ Spree::LineItem.prepend self
5
19
  Spree::LineItem.prepend SolidusFriendlyPromotions::DiscountableAmount
6
20
  end
7
21
  end
@@ -29,6 +29,13 @@ module SolidusFriendlyPromotions
29
29
  shipments.each(&:reset_current_discounts)
30
30
  end
31
31
 
32
+ # This helper method excludes line items that are managed by an order action for the benefit
33
+ # of calculators and actions that discount normal line items. Line items that are managed by an
34
+ # order actions handle their discounts themselves.
35
+ def discountable_line_items
36
+ line_items.reject(&:managed_by_order_action)
37
+ end
38
+
32
39
  def apply_shipping_promotions
33
40
  if Spree::Config.promotion_adjuster_class <= SolidusFriendlyPromotions::FriendlyPromotionAdjuster
34
41
  recalculate
@@ -37,6 +44,10 @@ module SolidusFriendlyPromotions
37
44
  end
38
45
  end
39
46
 
47
+ def free_from_order_action?(line_item, _options)
48
+ !line_item.managed_by_order_action
49
+ end
50
+
40
51
  Spree::Order.prepend self
41
52
  end
42
53
  end
@@ -4,9 +4,12 @@ import "solidus_friendly_promotions/jquery/option_value_picker"
4
4
 
5
5
  Turbo.session.drive = false;
6
6
 
7
- document.addEventListener("turbo:frame-load", ({ _target }) => {
7
+ const initPickers = ({ _target }) => {
8
8
  Spree.initNumberWithCurrency();
9
9
  $(".product_picker").productAutocomplete();
10
10
  $(".user_picker").userAutocomplete();
11
11
  $(".taxon_picker").taxonAutocomplete();
12
- });
12
+ $(".variant_autocomplete").variantAutocomplete();
13
+ };
14
+ document.addEventListener("turbo:frame-load", initPickers);
15
+ document.addEventListener("DOMContentLoaded", initPickers);
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFriendlyPromotions
4
+ module Actions
5
+ module OrderLevelAction
6
+ def can_discount?(_)
7
+ false
8
+ end
9
+
10
+ def level
11
+ :order
12
+ end
13
+ end
14
+ end
15
+ end
@@ -79,7 +79,7 @@ module SolidusFriendlyPromotions
79
79
  private
80
80
 
81
81
  def actionable_line_items(order)
82
- order.line_items.select do |item|
82
+ order.discountable_line_items.select do |item|
83
83
  promotion.rules.select do |rule|
84
84
  rule.applicable?(item)
85
85
  end.all? { |rule| rule.eligible?(item) }
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFriendlyPromotions
4
+ module Actions
5
+ class CreateDiscountedItem < PromotionAction
6
+ include OrderLevelAction
7
+ preference :variant_id, :integer
8
+ preference :quantity, :integer, default: 1
9
+
10
+ def perform(order)
11
+ line_item = find_item(order) || create_item(order)
12
+ line_item.current_discounts << discount(line_item)
13
+ end
14
+
15
+ def remove_from(order)
16
+ line_item = find_item(order)
17
+ order.line_items.destroy(line_item)
18
+ end
19
+
20
+ private
21
+
22
+ def find_item(order)
23
+ order.line_items.detect { |line_item| line_item.managed_by_order_action == self }
24
+ end
25
+
26
+ def create_item(order)
27
+ order.line_items.create!(quantity: preferred_quantity, variant: variant, managed_by_order_action: self)
28
+ end
29
+
30
+ def variant
31
+ Spree::Variant.find(preferred_variant_id)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -28,7 +28,7 @@ module SolidusFriendlyPromotions
28
28
  private
29
29
 
30
30
  def eligible_line_items(order)
31
- order.line_items.select do |line_item|
31
+ order.discountable_line_items.select do |line_item|
32
32
  calculable.promotion.eligible_by_applicable_rules?(line_item)
33
33
  end
34
34
  end
@@ -16,6 +16,7 @@ module SolidusFriendlyPromotions
16
16
 
17
17
  SolidusFriendlyPromotions::Promotion.ordered_lanes.each do |lane, _index|
18
18
  lane_promotions = eligible_promotions_for_promotable(promotions.select { |promotion| promotion.lane == lane }, order)
19
+ perform_order_actions(lane_promotions, lane) unless dry_run
19
20
  line_item_discounts = adjust_line_items(lane_promotions)
20
21
  shipment_discounts = adjust_shipments(lane_promotions)
21
22
  shipping_rate_discounts = adjust_shipping_rates(lane_promotions)
@@ -29,8 +30,25 @@ module SolidusFriendlyPromotions
29
30
 
30
31
  private
31
32
 
33
+ def perform_order_actions(lane_promotions, lane)
34
+ lane_promotions.each do |promotion|
35
+ promotion.actions.select { |action| action.level == :order }.each { |action| action.perform(order) }
36
+ end
37
+
38
+ automated_line_items = order.line_items.select(&:managed_by_order_action)
39
+ return if automated_line_items.empty?
40
+
41
+ ineligible_line_items = automated_line_items.select do |line_item|
42
+ line_item.managed_by_order_action.promotion.lane == lane && !line_item.managed_by_order_action.in?(lane_promotions.flat_map(&:actions))
43
+ end
44
+
45
+ ineligible_line_items.each do |line_item|
46
+ line_item.managed_by_order_action.remove_from(order)
47
+ end
48
+ end
49
+
32
50
  def adjust_line_items(promotions)
33
- order.line_items.select do |line_item|
51
+ order.discountable_line_items.select do |line_item|
34
52
  line_item.variant.product.promotionable?
35
53
  end.map do |line_item|
36
54
  discounts = generate_discounts(promotions, line_item)
@@ -27,7 +27,6 @@ module SolidusFriendlyPromotions
27
27
  end
28
28
  end
29
29
  order.reset_current_discounts
30
- order.promo_total = (order.line_items + order.shipments).sum(&:promo_total)
31
30
  order
32
31
  end
33
32
 
@@ -19,6 +19,13 @@ module SolidusFriendlyPromotions
19
19
  PersistDiscountedOrder.new(discounted_order).call unless dry_run
20
20
 
21
21
  order.reset_current_discounts
22
+
23
+ unless dry_run
24
+ # Since automations might have added a line item, we need to recalculate item total and item count here.
25
+ order.item_total = order.line_items.sum(&:amount)
26
+ order.item_count = order.line_items.sum(&:quantity)
27
+ order.promo_total = (order.line_items + order.shipments).sum(&:promo_total)
28
+ end
22
29
  order
23
30
  end
24
31
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusFriendlyPromotions
4
+ module PromotionHandler
5
+ # We handle shipping promotions just like other promotions, so we don't need a
6
+ # special promotion handler for shipping. However, Solidus wants us to implement one.
7
+ # This is what this class is for.
8
+ class Null
9
+ attr_reader :order
10
+ attr_accessor :error, :success
11
+
12
+ def initialize(order)
13
+ @order = order
14
+ end
15
+
16
+ def activate
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ <%= fields_for param_prefix, promotion_action do |form| %>
2
+ <div class="field">
3
+ <%= form.label :preferred_variant_id %>
4
+ <%= form.text_field :preferred_variant_id, class: "variant_autocomplete fullwidth" %>
5
+ <%= form.label :preferred_quantity %>
6
+ <%= form.number_field :preferred_quantity, class: "fullwidth" %>
7
+ </div>
8
+ <% end %>
9
+
10
+ <%= render(
11
+ "solidus_friendly_promotions/admin/promotion_actions/actions/calculator_fields",
12
+ promotion_action: promotion_action,
13
+ param_prefix: param_prefix,
14
+ form: form
15
+ ) %>
@@ -48,26 +48,28 @@
48
48
  </div>
49
49
 
50
50
  <div class="row">
51
- <% [:line_item, :shipment].each do |level| %>
51
+ <% [:order, :line_item, :shipment].each do |level| %>
52
52
  <% if promotion_actions_by_level(@promotion, level).any? %>
53
- <div class="col-6">
53
+ <div class="col-<%= level == :order ? 12 : 6 %>">
54
54
  <fieldset>
55
55
  <legend align="center"><%= t("#{level}_actions", scope: :solidus_friendly_promotions) %></legend>
56
56
 
57
57
  <%= render partial: 'solidus_friendly_promotions/admin/promotion_actions/promotion_action', collection: promotion_actions_by_level(@promotion, level), locals: {} %>
58
58
  </fieldset>
59
59
  </div>
60
- <div class="col-6">
61
- <fieldset>
62
- <legend align="center"><%= t("#{level}_rules", scope: :solidus_friendly_promotions) %></legend>
60
+ <% if level != :order %>
61
+ <div class="col-6">
62
+ <fieldset>
63
+ <legend align="center"><%= t("#{level}_rules", scope: :solidus_friendly_promotions) %></legend>
63
64
 
64
- <%= render partial: 'solidus_friendly_promotions/admin/promotion_rules/promotion_rule', collection: promotion_rules_by_level(@promotion, level), locals: { level: level } %>
65
+ <%= render partial: 'solidus_friendly_promotions/admin/promotion_rules/promotion_rule', collection: promotion_rules_by_level(@promotion, level), locals: { level: level } %>
65
66
 
66
- <%= turbo_frame_tag @promotion, "new_#{level}_promotion_rule" do %>
67
- <%= link_to t(:add_rule, scope: :solidus_friendly_promotions), solidus_friendly_promotions.new_admin_promotion_promotion_rule_path(@promotion, level: level), class: 'btn btn-secondary' %>
68
- <% end %>
69
- </fieldset>
70
- </div>
67
+ <%= turbo_frame_tag @promotion, "new_#{level}_promotion_rule" do %>
68
+ <%= link_to t(:add_rule, scope: :solidus_friendly_promotions), solidus_friendly_promotions.new_admin_promotion_promotion_rule_path(@promotion, level: level), class: 'btn btn-secondary' %>
69
+ <% end %>
70
+ </fieldset>
71
+ </div>
72
+ <% end %>
71
73
  <% end %>
72
74
  <% end %>
73
75
  </div>
@@ -23,6 +23,7 @@ en:
23
23
  shipment_rules: Shipment Rules
24
24
  line_item_actions: Line Item Actions
25
25
  shipment_actions: Shipment Actions
26
+ order_actions: Order Actions
26
27
  invalid_promotion_rule_level: Invalid Promotion Rule Level. Must be one of "order", "shipment", or "line_item"
27
28
  invalid_promotion_action: Invalid promotion action.
28
29
  invalid_promotion_rule: Invalid promotion rule.
@@ -161,6 +162,7 @@ en:
161
162
  models:
162
163
  solidus_friendly_promotions/actions/adjust_shipment: Discount matching shipments
163
164
  solidus_friendly_promotions/actions/adjust_line_item: Discount matching line items
165
+ solidus_friendly_promotions/actions/create_discounted_item: Create discounted line item
164
166
  solidus_friendly_promotions/actions/adjust_line_item_quantity_groups: Discount matching line items based on quantity groups
165
167
  solidus_friendly_promotions/calculators/distributed_amount: Distributed Amount
166
168
  solidus_friendly_promotions/calculators/percent: Flat Percent
@@ -199,6 +201,8 @@ en:
199
201
  description: Creates a promotion credit on matching line items
200
202
  solidus_friendly_promotions/actions/adjust_shipment:
201
203
  description: Creates a promotion credit on matching shipments
204
+ solidus_friendly_promotions/actions/create_discounted_item:
205
+ description: Creates a discounted item
202
206
  solidus_friendly_promotions/rules/first_order:
203
207
  description: Must be the customer's first order
204
208
  solidus_friendly_promotions/rules/first_repeat_purchase_since:
@@ -253,3 +257,7 @@ en:
253
257
  attributes:
254
258
  base:
255
259
  disallowed_with_apply_automatically: Could not create promotion code on promotion that apply automatically
260
+ spree/line_item:
261
+ attributes:
262
+ quantity:
263
+ cannot_be_changed_for_automated_items: cannot be changed on a line item managed by a promotion action
@@ -0,0 +1,5 @@
1
+ class AddManagedByOrderActionToLineItems < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_reference :spree_line_items, :managed_by_order_action, foreign_key: {to_table: :friendly_promotion_actions, null: true}
4
+ end
5
+ end
@@ -4,6 +4,10 @@
4
4
  Spree::Config.order_contents_class = "SolidusFriendlyPromotions::SimpleOrderContents"
5
5
  Spree::Config.promotion_adjuster_class = "SolidusFriendlyPromotions::FriendlyPromotionAdjuster"
6
6
 
7
+ Rails.application.config.to_prepare do |config|
8
+ Spree::Order.line_item_comparison_hooks << :free_from_order_action?
9
+ end
10
+
7
11
  # Replace the promotions menu from core with ours
8
12
  Spree::Backend::Config.configure do |config|
9
13
  config.menu_items = config.menu_items.map do |item|
@@ -82,6 +86,10 @@ SolidusFriendlyPromotions.configure do |config|
82
86
  "SolidusFriendlyPromotions::Actions::AdjustLineItemQuantityGroups" => [
83
87
  "SolidusFriendlyPromotions::Calculators::FlatRate",
84
88
  "SolidusFriendlyPromotions::Calculators::Percent"
89
+ ],
90
+ "SolidusFriendlyPromotions::Actions::CreateDiscountedItem" => [
91
+ "SolidusFriendlyPromotions::Calculators::FlatRate",
92
+ "SolidusFriendlyPromotions::Calculators::Percent"
85
93
  ]
86
94
  )
87
95
 
@@ -113,6 +121,7 @@ SolidusFriendlyPromotions.configure do |config|
113
121
  config.actions = [
114
122
  "SolidusFriendlyPromotions::Actions::AdjustLineItem",
115
123
  "SolidusFriendlyPromotions::Actions::AdjustLineItemQuantityGroups",
116
- "SolidusFriendlyPromotions::Actions::AdjustShipment"
124
+ "SolidusFriendlyPromotions::Actions::AdjustShipment",
125
+ "SolidusFriendlyPromotions::Actions::CreateDiscountedItem"
117
126
  ]
118
127
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidusFriendlyPromotions
4
- VERSION = "1.0.0.pre"
4
+ VERSION = "1.0.0.rc.1"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solidus_friendly_promotions
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre
4
+ version: 1.0.0.rc.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Meyerhoff
@@ -188,6 +188,7 @@ files:
188
188
  - app/javascript/solidus_friendly_promotions/jquery/option_value_picker.js
189
189
  - app/jobs/solidus_friendly_promotions/promotion_code_batch_job.rb
190
190
  - app/mailers/solidus_friendly_promotions/promotion_code_batch_mailer.rb
191
+ - app/models/concerns/solidus_friendly_promotions/actions/order_level_action.rb
191
192
  - app/models/concerns/solidus_friendly_promotions/discountable_amount.rb
192
193
  - app/models/concerns/solidus_friendly_promotions/rules/line_item_applicable_order_rule.rb
193
194
  - app/models/concerns/solidus_friendly_promotions/rules/line_item_level_rule.rb
@@ -197,6 +198,7 @@ files:
197
198
  - app/models/solidus_friendly_promotions/actions/adjust_line_item.rb
198
199
  - app/models/solidus_friendly_promotions/actions/adjust_line_item_quantity_groups.rb
199
200
  - app/models/solidus_friendly_promotions/actions/adjust_shipment.rb
201
+ - app/models/solidus_friendly_promotions/actions/create_discounted_item.rb
200
202
  - app/models/solidus_friendly_promotions/calculators/distributed_amount.rb
201
203
  - app/models/solidus_friendly_promotions/calculators/flat_rate.rb
202
204
  - app/models/solidus_friendly_promotions/calculators/flexi_rate.rb
@@ -223,6 +225,7 @@ files:
223
225
  - app/models/solidus_friendly_promotions/promotion_code/batch_builder.rb
224
226
  - app/models/solidus_friendly_promotions/promotion_code_batch.rb
225
227
  - app/models/solidus_friendly_promotions/promotion_handler/coupon.rb
228
+ - app/models/solidus_friendly_promotions/promotion_handler/null.rb
226
229
  - app/models/solidus_friendly_promotions/promotion_handler/page.rb
227
230
  - app/models/solidus_friendly_promotions/promotion_rule.rb
228
231
  - app/models/solidus_friendly_promotions/promotion_rules_store.rb
@@ -256,6 +259,7 @@ files:
256
259
  - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_adjust_line_item_quantity_groups.html.erb
257
260
  - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_adjust_shipment.html.erb
258
261
  - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_calculator_fields.erb
262
+ - app/views/solidus_friendly_promotions/admin/promotion_actions/actions/_create_discounted_item.html.erb
259
263
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/_default_fields.html.erb
260
264
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/distributed_amount/_fields.html.erb
261
265
  - app/views/solidus_friendly_promotions/admin/promotion_actions/calculators/flat_rate/_fields.html.erb
@@ -338,6 +342,7 @@ files:
338
342
  - db/migrate/20231012020928_add_db_comments_to_friendly_products_promotion_rules.rb
339
343
  - db/migrate/20231012120928_add_db_comments_to_friendly_promotion_categories.rb
340
344
  - db/migrate/20231013181921_add_original_promotion_ids.rb
345
+ - db/migrate/20231104135812_add_managed_by_order_action_to_line_items.rb
341
346
  - lib/generators/solidus_friendly_promotions/install/install_generator.rb
342
347
  - lib/generators/solidus_friendly_promotions/install/templates/initializer.rb
343
348
  - lib/solidus_friendly_promotions.rb