solidus_promotions 4.4.0
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 +7 -0
- data/.eslintrc.json +10 -0
- data/.github/stale.yml +1 -0
- data/MIGRATING.md +184 -0
- data/README.md +122 -0
- data/Rakefile +20 -0
- data/app/assets/config/solidus_promotions/manifest.js +13 -0
- data/app/decorators/models/solidus_promotions/adjustment_decorator.rb +11 -0
- data/app/decorators/models/solidus_promotions/line_item_decorator.rb +27 -0
- data/app/decorators/models/solidus_promotions/order_decorator.rb +42 -0
- data/app/decorators/models/solidus_promotions/order_recalculator_decorator.rb +15 -0
- data/app/decorators/models/solidus_promotions/shipment_decorator.rb +14 -0
- data/app/decorators/models/solidus_promotions/shipping_rate_decorator.rb +29 -0
- data/app/helpers/solidus_promotions/admin/benefits_helper.rb +19 -0
- data/app/helpers/solidus_promotions/admin/conditions_helper.rb +14 -0
- data/app/helpers/solidus_promotions/admin/promotions_helper.rb +15 -0
- data/app/javascript/backend/solidus_promotions/controllers/application.js +9 -0
- data/app/javascript/backend/solidus_promotions/controllers/calculator_tiers_controller.js +37 -0
- data/app/javascript/backend/solidus_promotions/controllers/flash_controller.js +10 -0
- data/app/javascript/backend/solidus_promotions/controllers/index.js +8 -0
- data/app/javascript/backend/solidus_promotions/controllers/product_option_values_controller.js +62 -0
- data/app/javascript/backend/solidus_promotions/web_components/number_with_currency.js +35 -0
- data/app/javascript/backend/solidus_promotions/web_components/option_value_picker.js +52 -0
- data/app/javascript/backend/solidus_promotions/web_components/product_picker.js +7 -0
- data/app/javascript/backend/solidus_promotions/web_components/select_two.js +11 -0
- data/app/javascript/backend/solidus_promotions/web_components/taxon_picker.js +7 -0
- data/app/javascript/backend/solidus_promotions/web_components/user_picker.js +7 -0
- data/app/javascript/backend/solidus_promotions/web_components/variant_picker.js +7 -0
- data/app/javascript/backend/solidus_promotions.js +11 -0
- data/app/jobs/solidus_promotions/promotion_code_batch_job.rb +26 -0
- data/app/mailers/solidus_promotions/promotion_code_batch_mailer.rb +15 -0
- data/app/models/concerns/solidus_promotions/benefits/line_item_benefit.rb +15 -0
- data/app/models/concerns/solidus_promotions/benefits/order_benefit.rb +15 -0
- data/app/models/concerns/solidus_promotions/benefits/shipment_benefit.rb +15 -0
- data/app/models/concerns/solidus_promotions/calculators/promotion_calculator.rb +11 -0
- data/app/models/concerns/solidus_promotions/conditions/line_item_applicable_order_level_condition.rb +23 -0
- data/app/models/concerns/solidus_promotions/conditions/line_item_level_condition.rb +15 -0
- data/app/models/concerns/solidus_promotions/conditions/order_level_condition.rb +15 -0
- data/app/models/concerns/solidus_promotions/conditions/shipment_level_condition.rb +15 -0
- data/app/models/concerns/solidus_promotions/discountable_amount.rb +21 -0
- data/app/models/solidus_promotions/benefit.rb +123 -0
- data/app/models/solidus_promotions/benefits/adjust_line_item.rb +13 -0
- data/app/models/solidus_promotions/benefits/adjust_line_item_quantity_groups.rb +107 -0
- data/app/models/solidus_promotions/benefits/adjust_shipment.rb +13 -0
- data/app/models/solidus_promotions/benefits/create_discounted_item.rb +49 -0
- data/app/models/solidus_promotions/calculators/distributed_amount.rb +31 -0
- data/app/models/solidus_promotions/calculators/flat_rate.rb +23 -0
- data/app/models/solidus_promotions/calculators/flexi_rate.rb +26 -0
- data/app/models/solidus_promotions/calculators/percent.rb +19 -0
- data/app/models/solidus_promotions/calculators/tiered_flat_rate.rb +58 -0
- data/app/models/solidus_promotions/calculators/tiered_percent.rb +68 -0
- data/app/models/solidus_promotions/calculators/tiered_percent_on_eligible_item_quantity.rb +40 -0
- data/app/models/solidus_promotions/condition.rb +61 -0
- data/app/models/solidus_promotions/condition_product.rb +8 -0
- data/app/models/solidus_promotions/condition_store.rb +8 -0
- data/app/models/solidus_promotions/condition_taxon.rb +8 -0
- data/app/models/solidus_promotions/condition_user.rb +8 -0
- data/app/models/solidus_promotions/conditions/discounted_item_total.rb +22 -0
- data/app/models/solidus_promotions/conditions/first_order.rb +31 -0
- data/app/models/solidus_promotions/conditions/first_repeat_purchase_since.rb +31 -0
- data/app/models/solidus_promotions/conditions/item_total.rb +75 -0
- data/app/models/solidus_promotions/conditions/line_item_option_value.rb +37 -0
- data/app/models/solidus_promotions/conditions/line_item_product.rb +56 -0
- data/app/models/solidus_promotions/conditions/line_item_taxon.rb +59 -0
- data/app/models/solidus_promotions/conditions/minimum_quantity.rb +42 -0
- data/app/models/solidus_promotions/conditions/nth_order.rb +40 -0
- data/app/models/solidus_promotions/conditions/one_use_per_user.rb +25 -0
- data/app/models/solidus_promotions/conditions/option_value.rb +28 -0
- data/app/models/solidus_promotions/conditions/product.rb +85 -0
- data/app/models/solidus_promotions/conditions/shipping_method.rb +19 -0
- data/app/models/solidus_promotions/conditions/store.rb +26 -0
- data/app/models/solidus_promotions/conditions/taxon.rb +98 -0
- data/app/models/solidus_promotions/conditions/user.rb +35 -0
- data/app/models/solidus_promotions/conditions/user_logged_in.rb +16 -0
- data/app/models/solidus_promotions/conditions/user_role.rb +42 -0
- data/app/models/solidus_promotions/distributed_amounts_handler.rb +39 -0
- data/app/models/solidus_promotions/eligibility_result.rb +5 -0
- data/app/models/solidus_promotions/eligibility_results.rb +47 -0
- data/app/models/solidus_promotions/item_discount.rb +21 -0
- data/app/models/solidus_promotions/migration_support/order_promotion_syncer.rb +54 -0
- data/app/models/solidus_promotions/order_adjuster/choose_discounts.rb +21 -0
- data/app/models/solidus_promotions/order_adjuster/discount_order.rb +93 -0
- data/app/models/solidus_promotions/order_adjuster/load_promotions.rb +53 -0
- data/app/models/solidus_promotions/order_adjuster/persist_discounted_order.rb +79 -0
- data/app/models/solidus_promotions/order_adjuster.rb +33 -0
- data/app/models/solidus_promotions/order_promotion.rb +23 -0
- data/app/models/solidus_promotions/permission_sets/solidus_promotion_management.rb +15 -0
- data/app/models/solidus_promotions/promotion.rb +163 -0
- data/app/models/solidus_promotions/promotion_advertiser.rb +10 -0
- data/app/models/solidus_promotions/promotion_category.rb +9 -0
- data/app/models/solidus_promotions/promotion_code/batch_builder.rb +73 -0
- data/app/models/solidus_promotions/promotion_code.rb +56 -0
- data/app/models/solidus_promotions/promotion_code_batch.rb +25 -0
- data/app/models/solidus_promotions/promotion_finder.rb +10 -0
- data/app/models/solidus_promotions/promotion_handler/coupon.rb +113 -0
- data/app/models/solidus_promotions/promotion_handler/page.rb +30 -0
- data/app/models/solidus_promotions/shipping_rate_discount.rb +11 -0
- data/app/subscribers/solidus_promotions/order_promotion_subscriber.rb +20 -0
- data/bin/rails +13 -0
- data/config/importmap.rb +14 -0
- data/config/locales/en.yml +376 -0
- data/config/locales/promotion_categories.en.yml +6 -0
- data/config/locales/promotions.en.yml +6 -0
- data/config/routes.rb +38 -0
- data/db/migrate/20230703101637_create_promotions.rb +25 -0
- data/db/migrate/20230703113625_create_promotion_benefits.rb +15 -0
- data/db/migrate/20230703141116_create_promotion_categories.rb +14 -0
- data/db/migrate/20230703143943_create_promotion_conditions.rb +12 -0
- data/db/migrate/20230704083830_add_condition_join_tables.rb +31 -0
- data/db/migrate/20230704102444_create_promotion_codes.rb +11 -0
- data/db/migrate/20230704102656_create_promotion_code_batches.rb +34 -0
- data/db/migrate/20230705171556_create_order_promotions.rb +11 -0
- data/db/migrate/20230725074235_create_shipping_rate_discounts.rb +12 -0
- data/db/migrate/20231011100059_add_db_comments_to_order_promotions.rb +61 -0
- data/db/migrate/20231011120928_add_db_comments_to_condition_taxons.rb +54 -0
- data/db/migrate/20231011131324_add_db_comments_to_conditions.rb +60 -0
- data/db/migrate/20231011142040_add_db_comments_to_condition_users.rb +53 -0
- data/db/migrate/20231011155822_add_db_comments_to_promotions.rb +123 -0
- data/db/migrate/20231011163030_add_db_comments_to_promotion_codes.rb +60 -0
- data/db/migrate/20231011173312_add_db_comments_to_promotion_code_batches.rb +91 -0
- data/db/migrate/20231011184205_add_db_comments_to_condition_stores.rb +53 -0
- data/db/migrate/20231011190222_add_db_comments_to_benefits.rb +61 -0
- data/db/migrate/20231012020928_add_db_comments_to_condition_products.rb +52 -0
- data/db/migrate/20231012120928_add_db_comments_to_promotion_categories.rb +52 -0
- data/db/migrate/20231104135812_add_managed_by_order_benefit_to_line_items.rb +5 -0
- data/lib/components/admin/solidus_admin/orders/show/adjustments/index/source/solidus_promotions_benefit/component.rb +17 -0
- data/lib/components/admin/solidus_promotions/orders/index/component.rb +15 -0
- data/lib/components/admin/solidus_promotions/orders/index/component.yml +3 -0
- data/lib/components/admin/solidus_promotions/promotion_categories/index/component.rb +60 -0
- data/lib/components/admin/solidus_promotions/promotions/index/component.rb +108 -0
- data/lib/components/admin/solidus_promotions/promotions/index/component.yml +10 -0
- data/lib/controllers/admin/solidus_promotions/promotion_categories_controller.rb +29 -0
- data/lib/controllers/admin/solidus_promotions/promotions_controller.rb +46 -0
- data/lib/controllers/backend/solidus_promotions/admin/base_controller.rb +15 -0
- data/lib/controllers/backend/solidus_promotions/admin/benefits_controller.rb +85 -0
- data/lib/controllers/backend/solidus_promotions/admin/conditions_controller.rb +69 -0
- data/lib/controllers/backend/solidus_promotions/admin/promotion_categories_controller.rb +13 -0
- data/lib/controllers/backend/solidus_promotions/admin/promotion_code_batches_controller.rb +42 -0
- data/lib/controllers/backend/solidus_promotions/admin/promotion_codes_controller.rb +58 -0
- data/lib/controllers/backend/solidus_promotions/admin/promotions_controller.rb +65 -0
- data/lib/generators/solidus_promotions/install/install_generator.rb +56 -0
- data/lib/generators/solidus_promotions/install/templates/initializer.rb +6 -0
- data/lib/solidus_promotions/configuration.rb +125 -0
- data/lib/solidus_promotions/engine.rb +115 -0
- data/lib/solidus_promotions/migrate_adjustments.rb +62 -0
- data/lib/solidus_promotions/migrate_order_promotions.rb +73 -0
- data/lib/solidus_promotions/promotion_map.rb +126 -0
- data/lib/solidus_promotions/promotion_migrator.rb +103 -0
- data/lib/solidus_promotions/testing_support/factories/completed_order_with_solidus_promotion_factory.rb +20 -0
- data/lib/solidus_promotions/testing_support/factories/solidus_order_promotion_factory.rb +8 -0
- data/lib/solidus_promotions/testing_support/factories/solidus_promotion_category_factory.rb +7 -0
- data/lib/solidus_promotions/testing_support/factories/solidus_promotion_code_factory.rb +8 -0
- data/lib/solidus_promotions/testing_support/factories/solidus_promotion_factory.rb +65 -0
- data/lib/solidus_promotions/testing_support/factories/solidus_shipping_rate_discount_factory.rb +14 -0
- data/lib/solidus_promotions/testing_support/factory_bot.rb +30 -0
- data/lib/solidus_promotions.rb +37 -0
- data/lib/tasks/solidus_promotions/migrate_adjustments.rake +17 -0
- data/lib/tasks/solidus_promotions/migrate_existing_promotions.rake +12 -0
- data/lib/tasks/solidus_promotions/migrate_order_promotions.rake +17 -0
- data/lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_line_item.html.erb +6 -0
- data/lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_line_item_quantity_groups.html.erb +13 -0
- data/lib/views/backend/solidus_promotions/admin/benefit_fields/_adjust_shipment.html.erb +6 -0
- data/lib/views/backend/solidus_promotions/admin/benefit_fields/_calculator_fields.erb +11 -0
- data/lib/views/backend/solidus_promotions/admin/benefit_fields/_create_discounted_item.html.erb +21 -0
- data/lib/views/backend/solidus_promotions/admin/benefits/_benefit.html.erb +50 -0
- data/lib/views/backend/solidus_promotions/admin/benefits/_calculator_select.html.erb +16 -0
- data/lib/views/backend/solidus_promotions/admin/benefits/_form.html.erb +3 -0
- data/lib/views/backend/solidus_promotions/admin/benefits/_new_benefit.html.erb +33 -0
- data/lib/views/backend/solidus_promotions/admin/benefits/_type_select.html.erb +16 -0
- data/lib/views/backend/solidus_promotions/admin/benefits/edit.html.erb +19 -0
- data/lib/views/backend/solidus_promotions/admin/benefits/new.html.erb +1 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/_default_fields.html.erb +6 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/distributed_amount/_fields.html.erb +18 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/flat_rate/_fields.html.erb +6 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/flexi_rate/_fields.html.erb +24 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/percent/_fields.html.erb +6 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_flat_rate/_fields.html.erb +32 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_flat_rate/_tier_fields.html.erb +31 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent/_fields.html.erb +47 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent/_tier_fields.html.erb +33 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent_on_eligible_item_quantity/_fields.html.erb +36 -0
- data/lib/views/backend/solidus_promotions/admin/calculator_fields/tiered_percent_on_eligible_item_quantity/_tier_fields.html.erb +35 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_first_order.html.erb +3 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_first_repeat_purchase_since.html.erb +7 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_item_total.html.erb +15 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_option_value.html.erb +25 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_product.html.erb +21 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_line_item_taxon.html.erb +17 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_minimum_quantity.html.erb +5 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_nth_order.html.erb +15 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_one_use_per_user.html.erb +3 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_option_value.html.erb +63 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_product.html.erb +27 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_shipping_method.html.erb +10 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_store.html.erb +9 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_taxon.html.erb +27 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_user.html.erb +7 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_user_logged_in.html.erb +3 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/_user_role.html.erb +15 -0
- data/lib/views/backend/solidus_promotions/admin/condition_fields/line_item_option_value/_option_value_fields.html.erb +21 -0
- data/lib/views/backend/solidus_promotions/admin/conditions/_condition.html.erb +26 -0
- data/lib/views/backend/solidus_promotions/admin/conditions/_new_condition.html.erb +26 -0
- data/lib/views/backend/solidus_promotions/admin/conditions/_type_select.html.erb +20 -0
- data/lib/views/backend/solidus_promotions/admin/conditions/new.html.erb +1 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_categories/_form.html.erb +14 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_categories/edit.html.erb +10 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_categories/index.html.erb +47 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_categories/new.html.erb +10 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/_form_fields.html.erb +22 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/download.csv.ruby +8 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/index.html.erb +65 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_code_batches/new.html.erb +8 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_codes/index.csv.ruby +8 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_codes/index.html.erb +32 -0
- data/lib/views/backend/solidus_promotions/admin/promotion_codes/new.html.erb +31 -0
- data/lib/views/backend/solidus_promotions/admin/promotions/_form.html.erb +124 -0
- data/lib/views/backend/solidus_promotions/admin/promotions/_table.html.erb +57 -0
- data/lib/views/backend/solidus_promotions/admin/promotions/_table_filter.html.erb +78 -0
- data/lib/views/backend/solidus_promotions/admin/promotions/edit.html.erb +41 -0
- data/lib/views/backend/solidus_promotions/admin/promotions/index.html.erb +23 -0
- data/lib/views/backend/solidus_promotions/admin/promotions/new.html.erb +9 -0
- data/lib/views/backend/solidus_promotions/admin/shared/_number_with_currency.html.erb +20 -0
- data/lib/views/backend/solidus_promotions/admin/shared/_promotion_sub_menu.html.erb +14 -0
- data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_boolean.html.erb +14 -0
- data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_decimal.html.erb +12 -0
- data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_encrypted_string.html.erb +12 -0
- data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_integer.html.erb +12 -0
- data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_password.html.erb +12 -0
- data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_string.html.erb +12 -0
- data/lib/views/backend/solidus_promotions/admin/shared/preference_fields/_text.html.erb +12 -0
- data/lib/views/backend/solidus_promotions/promotion_code_batch_mailer/promotion_code_batch_errored.text.erb +2 -0
- data/lib/views/backend/solidus_promotions/promotion_code_batch_mailer/promotion_code_batch_finished.text.erb +2 -0
- data/solidus_promotions.gemspec +33 -0
- metadata +366 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class LineItemTaxon < Condition
|
|
6
|
+
include LineItemLevelCondition
|
|
7
|
+
|
|
8
|
+
has_many :condition_taxons, class_name: "SolidusPromotions::ConditionTaxon", foreign_key: :condition_id,
|
|
9
|
+
dependent: :destroy
|
|
10
|
+
has_many :taxons, through: :condition_taxons, class_name: "Spree::Taxon"
|
|
11
|
+
|
|
12
|
+
MATCH_POLICIES = %w[include exclude].freeze
|
|
13
|
+
|
|
14
|
+
validates :preferred_match_policy, inclusion: { in: MATCH_POLICIES }
|
|
15
|
+
|
|
16
|
+
preference :match_policy, :string, default: MATCH_POLICIES.first
|
|
17
|
+
|
|
18
|
+
def preload_relations
|
|
19
|
+
[:taxons]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def eligible?(line_item, _options = {})
|
|
23
|
+
found = Spree::Classification.where(
|
|
24
|
+
product_id: line_item.variant.product_id,
|
|
25
|
+
taxon_id: condition_taxon_ids_with_children
|
|
26
|
+
).exists?
|
|
27
|
+
|
|
28
|
+
case preferred_match_policy
|
|
29
|
+
when "include"
|
|
30
|
+
found
|
|
31
|
+
when "exclude"
|
|
32
|
+
!found
|
|
33
|
+
else
|
|
34
|
+
raise "unexpected match policy: #{preferred_match_policy.inspect}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def taxon_ids_string
|
|
39
|
+
taxons.pluck(:id).join(",")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def taxon_ids_string=(taxon_ids)
|
|
43
|
+
taxon_ids = taxon_ids.to_s.split(",").map(&:strip)
|
|
44
|
+
self.taxons = Spree::Taxon.find(taxon_ids)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def updateable?
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
# ids of taxons conditions and taxons conditions children
|
|
54
|
+
def condition_taxon_ids_with_children
|
|
55
|
+
taxons.flat_map { |taxon| taxon.self_and_descendants.ids }.uniq
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
# Promotion condition for ensuring an order contains a minimum quantity of
|
|
6
|
+
# applicable items.
|
|
7
|
+
#
|
|
8
|
+
# This promotion condition is only compatible with the "all" match policy. It
|
|
9
|
+
# doesn't make a lot of sense to use it without that policy as it reduces
|
|
10
|
+
# it to a simple quantity check across the entire order which would be
|
|
11
|
+
# better served by an item total condition.
|
|
12
|
+
class MinimumQuantity < Condition
|
|
13
|
+
include OrderLevelCondition
|
|
14
|
+
|
|
15
|
+
validates :preferred_minimum_quantity, numericality: { only_integer: true, greater_than: 0 }
|
|
16
|
+
|
|
17
|
+
preference :minimum_quantity, :integer, default: 1
|
|
18
|
+
|
|
19
|
+
# Will look at all of the "applicable" line items in the order and
|
|
20
|
+
# determine if the sum of their quantity is greater than the minimum.
|
|
21
|
+
#
|
|
22
|
+
# "Applicable" items are ones that pass all eligibility checks of applicable conditions.
|
|
23
|
+
#
|
|
24
|
+
# When false is returned, the reason will be included in the
|
|
25
|
+
# `eligibility_errors` object.
|
|
26
|
+
#
|
|
27
|
+
# @param order [Spree::Order] the order we want to check eligibility on
|
|
28
|
+
# @return [Boolean] true if promotion is eligible, false otherwise
|
|
29
|
+
def eligible?(order)
|
|
30
|
+
if benefit.applicable_line_items(order).sum(&:quantity) < preferred_minimum_quantity
|
|
31
|
+
eligibility_errors.add(
|
|
32
|
+
:base,
|
|
33
|
+
eligibility_error_message(:quantity_less_than_minimum, count: preferred_minimum_quantity),
|
|
34
|
+
error_code: :quantity_less_than_minimum
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
eligibility_errors.empty?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class NthOrder < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
|
|
8
|
+
preference :nth_order, :integer, default: 2
|
|
9
|
+
# It does not make sense to have this apply to the first order using preferred_nth_order == 1
|
|
10
|
+
# Instead we could use the first_order condition
|
|
11
|
+
validates :preferred_nth_order, numericality: { only_integer: true, greater_than: 1 }
|
|
12
|
+
|
|
13
|
+
# This is never eligible if the order does not have a user, and that user does not have any previous completed orders.
|
|
14
|
+
#
|
|
15
|
+
# Use the first order condition if you want a promotion to be applied to the first order for a user.
|
|
16
|
+
# @param order [Spree::Order]
|
|
17
|
+
def eligible?(order, _options = {})
|
|
18
|
+
return false unless order.user
|
|
19
|
+
|
|
20
|
+
nth_order?(order)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def completed_order_count(order)
|
|
26
|
+
order
|
|
27
|
+
.user
|
|
28
|
+
.orders
|
|
29
|
+
.complete
|
|
30
|
+
.where(Spree::Order.arel_table[:completed_at].lt(order.completed_at || Time.current))
|
|
31
|
+
.count
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def nth_order?(order)
|
|
35
|
+
count = completed_order_count(order) + 1
|
|
36
|
+
count == preferred_nth_order
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class OneUsePerUser < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
|
|
8
|
+
def eligible?(order, _options = {})
|
|
9
|
+
if order.user.present?
|
|
10
|
+
if promotion.used_by?(order.user, [order])
|
|
11
|
+
eligibility_errors.add(
|
|
12
|
+
:base,
|
|
13
|
+
eligibility_error_message(:limit_once_per_user),
|
|
14
|
+
error_code: :limit_once_per_user
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
else
|
|
18
|
+
eligibility_errors.add(:base, eligibility_error_message(:no_user_specified), error_code: :no_user_specified)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
eligibility_errors.empty?
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class OptionValue < Condition
|
|
6
|
+
include LineItemApplicableOrderLevelCondition
|
|
7
|
+
|
|
8
|
+
preference :eligible_values, :hash
|
|
9
|
+
|
|
10
|
+
def order_eligible?(order)
|
|
11
|
+
order.line_items.any? { |item| line_item_eligible?(item) }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def line_item_eligible?(line_item)
|
|
15
|
+
LineItemOptionValue.new(preferred_eligible_values: preferred_eligible_values).eligible?(line_item)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def preferred_eligible_values
|
|
19
|
+
values = preferences[:eligible_values] || {}
|
|
20
|
+
values.keys.map(&:to_i).zip(
|
|
21
|
+
values.values.map do |value|
|
|
22
|
+
(value.is_a?(Array) ? value : value.split(",")).map(&:to_i)
|
|
23
|
+
end
|
|
24
|
+
).to_h
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
# A condition to limit a promotion based on products in the order. Can
|
|
6
|
+
# require all or any of the products to be present. Valid products
|
|
7
|
+
# either come from assigned product group or are assingned directly to
|
|
8
|
+
# the condition.
|
|
9
|
+
class Product < Condition
|
|
10
|
+
include LineItemApplicableOrderLevelCondition
|
|
11
|
+
|
|
12
|
+
has_many :condition_products,
|
|
13
|
+
dependent: :destroy,
|
|
14
|
+
foreign_key: :condition_id,
|
|
15
|
+
class_name: "SolidusPromotions::ConditionProduct"
|
|
16
|
+
has_many :products, class_name: "Spree::Product", through: :condition_products
|
|
17
|
+
|
|
18
|
+
def preload_relations
|
|
19
|
+
[:products]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
MATCH_POLICIES = %w[any all none only].freeze
|
|
23
|
+
|
|
24
|
+
validates :preferred_match_policy, inclusion: { in: MATCH_POLICIES }
|
|
25
|
+
|
|
26
|
+
preference :match_policy, :string, default: MATCH_POLICIES.first
|
|
27
|
+
|
|
28
|
+
# scope/association that is used to test eligibility
|
|
29
|
+
def eligible_products
|
|
30
|
+
products
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def order_eligible?(order)
|
|
34
|
+
return true if eligible_products.empty?
|
|
35
|
+
|
|
36
|
+
case preferred_match_policy
|
|
37
|
+
when "all"
|
|
38
|
+
unless eligible_products.all? { |product| order_products(order).include?(product) }
|
|
39
|
+
eligibility_errors.add(:base, eligibility_error_message(:missing_product), error_code: :missing_product)
|
|
40
|
+
end
|
|
41
|
+
when "any"
|
|
42
|
+
unless order_products(order).any? { |product| eligible_products.include?(product) }
|
|
43
|
+
eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products),
|
|
44
|
+
error_code: :no_applicable_products)
|
|
45
|
+
end
|
|
46
|
+
when "none"
|
|
47
|
+
unless order_products(order).none? { |product| eligible_products.include?(product) }
|
|
48
|
+
eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
|
|
49
|
+
error_code: :has_excluded_product)
|
|
50
|
+
end
|
|
51
|
+
when "only"
|
|
52
|
+
unless order_products(order).all? { |product| eligible_products.include?(product) }
|
|
53
|
+
eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
|
|
54
|
+
error_code: :has_excluded_product)
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
raise "unexpected match policy: #{preferred_match_policy.inspect}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
eligibility_errors.empty?
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def line_item_eligible?(line_item, _options = {})
|
|
64
|
+
# The order level eligibility check happens first, and if none of the products
|
|
65
|
+
# are in the order, then no line items should be available to check.
|
|
66
|
+
raise "This should not happen" if preferred_match_policy == "none"
|
|
67
|
+
product_ids.include?(line_item.variant.product_id)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def product_ids_string
|
|
71
|
+
product_ids.join(",")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def product_ids_string=(product_ids)
|
|
75
|
+
self.product_ids = product_ids.to_s.split(",").map(&:strip)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def order_products(order)
|
|
81
|
+
order.line_items.map(&:variant).map(&:product)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class ShippingMethod < Condition
|
|
6
|
+
include ShipmentLevelCondition
|
|
7
|
+
|
|
8
|
+
preference :shipping_method_ids, type: :array, default: []
|
|
9
|
+
|
|
10
|
+
def applicable?(promotable)
|
|
11
|
+
promotable.is_a?(Spree::Shipment) || promotable.is_a?(Spree::ShippingRate)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def eligible?(promotable)
|
|
15
|
+
promotable.shipping_method&.id&.in?(preferred_shipping_method_ids.map(&:to_i))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class Store < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
|
|
8
|
+
has_many :condition_stores, class_name: "SolidusPromotions::ConditionStore",
|
|
9
|
+
foreign_key: :condition_id,
|
|
10
|
+
dependent: :destroy
|
|
11
|
+
has_many :stores, through: :condition_stores, class_name: "Spree::Store"
|
|
12
|
+
|
|
13
|
+
def preload_relations
|
|
14
|
+
[:stores]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def eligible?(order, _options = {})
|
|
18
|
+
stores.none? || stores.include?(order.store)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def updateable?
|
|
22
|
+
true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class Taxon < Condition
|
|
6
|
+
include LineItemApplicableOrderLevelCondition
|
|
7
|
+
|
|
8
|
+
has_many :condition_taxons, class_name: "SolidusPromotions::ConditionTaxon", foreign_key: :condition_id,
|
|
9
|
+
dependent: :destroy
|
|
10
|
+
has_many :taxons, through: :condition_taxons, class_name: "Spree::Taxon"
|
|
11
|
+
|
|
12
|
+
def preload_relations
|
|
13
|
+
[:taxons]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
MATCH_POLICIES = %w[any all none].freeze
|
|
17
|
+
|
|
18
|
+
validates :preferred_match_policy, inclusion: { in: MATCH_POLICIES }
|
|
19
|
+
|
|
20
|
+
preference :match_policy, :string, default: MATCH_POLICIES.first
|
|
21
|
+
|
|
22
|
+
def order_eligible?(order)
|
|
23
|
+
order_taxons = taxons_in_order(order)
|
|
24
|
+
|
|
25
|
+
case preferred_match_policy
|
|
26
|
+
when "all"
|
|
27
|
+
matches_all = taxons.all? do |condition_taxon|
|
|
28
|
+
order_taxons.where(id: condition_taxon.self_and_descendants.ids).exists?
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
unless matches_all
|
|
32
|
+
eligibility_errors.add(:base, eligibility_error_message(:missing_taxon), error_code: :missing_taxon)
|
|
33
|
+
end
|
|
34
|
+
when "any"
|
|
35
|
+
unless order_taxons.where(id: condition_taxon_ids_with_children).exists?
|
|
36
|
+
eligibility_errors.add(
|
|
37
|
+
:base,
|
|
38
|
+
eligibility_error_message(:no_matching_taxons),
|
|
39
|
+
error_code: :no_matching_taxons
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
when "none"
|
|
43
|
+
if order_taxons.where(id: condition_taxon_ids_with_children).exists?
|
|
44
|
+
eligibility_errors.add(
|
|
45
|
+
:base,
|
|
46
|
+
eligibility_error_message(:has_excluded_taxon),
|
|
47
|
+
error_code: :has_excluded_taxon
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
else
|
|
51
|
+
raise "unexpected match policy: #{preferred_match_policy.inspect}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
eligibility_errors.empty?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def line_item_eligible?(line_item)
|
|
58
|
+
# The order level eligibility check happens first, and if none of the taxons
|
|
59
|
+
# are in the order, then no line items should be available to check.
|
|
60
|
+
raise "This should not happen" if preferred_match_policy == "none"
|
|
61
|
+
|
|
62
|
+
raise "unexpected match policy: #{preferred_match_policy.inspect}" unless preferred_match_policy.in?(MATCH_POLICIES)
|
|
63
|
+
|
|
64
|
+
Spree::Classification.where(
|
|
65
|
+
product_id: line_item.variant.product_id,
|
|
66
|
+
taxon_id: condition_taxon_ids_with_children
|
|
67
|
+
).exists?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def taxon_ids_string
|
|
71
|
+
taxon_ids.join(",")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def taxon_ids_string=(taxon_ids)
|
|
75
|
+
self.taxon_ids = taxon_ids.to_s.split(",").map(&:strip)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def updateable?
|
|
79
|
+
true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
# All taxons in an order
|
|
85
|
+
def taxons_in_order(order)
|
|
86
|
+
Spree::Taxon
|
|
87
|
+
.joins(products: { variants_including_master: :line_items })
|
|
88
|
+
.where(spree_line_items: { order_id: order.id })
|
|
89
|
+
.distinct
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# ids of taxons conditions and taxons conditions children
|
|
93
|
+
def condition_taxon_ids_with_children
|
|
94
|
+
taxons.flat_map { |taxon| taxon.self_and_descendants.ids }.uniq
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class User < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
|
|
8
|
+
has_many :condition_users,
|
|
9
|
+
class_name: "SolidusPromotions::ConditionUser",
|
|
10
|
+
foreign_key: :condition_id,
|
|
11
|
+
dependent: :destroy
|
|
12
|
+
has_many :users, through: :condition_users, class_name: Spree::UserClassHandle.new
|
|
13
|
+
|
|
14
|
+
def preload_relations
|
|
15
|
+
[:users]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def eligible?(order, _options = {})
|
|
19
|
+
users.include?(order.user)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def user_ids_string
|
|
23
|
+
user_ids.join(",")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def user_ids_string=(user_ids)
|
|
27
|
+
self.user_ids = user_ids.to_s.split(",").map(&:strip)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def updateable?
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class UserLoggedIn < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
|
|
8
|
+
def eligible?(order, _options = {})
|
|
9
|
+
if order.user.blank?
|
|
10
|
+
eligibility_errors.add(:base, eligibility_error_message(:no_user_specified), error_code: :no_user_specified)
|
|
11
|
+
end
|
|
12
|
+
eligibility_errors.empty?
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class UserRole < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
|
|
8
|
+
preference :role_ids, :array, default: []
|
|
9
|
+
|
|
10
|
+
MATCH_POLICIES = %w[any all].freeze
|
|
11
|
+
preference :match_policy, default: MATCH_POLICIES.first
|
|
12
|
+
|
|
13
|
+
def eligible?(order, _options = {})
|
|
14
|
+
return false unless order.user
|
|
15
|
+
|
|
16
|
+
if all_match_policy?
|
|
17
|
+
match_all_roles?(order)
|
|
18
|
+
else
|
|
19
|
+
match_any_roles?(order)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def all_match_policy?
|
|
26
|
+
preferred_match_policy == "all" && preferred_role_ids.present?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def user_roles(order)
|
|
30
|
+
order.user.spree_roles.where(id: preferred_role_ids)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def match_all_roles?(order)
|
|
34
|
+
user_roles(order).count == preferred_role_ids.count
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def match_any_roles?(order)
|
|
38
|
+
user_roles(order).exists?
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
class DistributedAmountsHandler
|
|
5
|
+
attr_reader :line_items, :total_amount
|
|
6
|
+
|
|
7
|
+
def initialize(line_items, total_amount)
|
|
8
|
+
@line_items = line_items
|
|
9
|
+
@total_amount = total_amount
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @param line_item [LineItem] one of the line_items distributed over
|
|
13
|
+
# @return [BigDecimal] the weighted adjustment for this line_item
|
|
14
|
+
def amount(line_item)
|
|
15
|
+
distributed_amounts[line_item.id].to_d
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# @private
|
|
21
|
+
# @return [Hash<Integer, BigDecimal>] a hash of line item IDs and their
|
|
22
|
+
# corresponding weighted adjustments
|
|
23
|
+
def distributed_amounts
|
|
24
|
+
line_item_ids.zip(allocated_amounts).to_h
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def line_item_ids
|
|
28
|
+
line_items.map(&:id)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def elligible_amounts
|
|
32
|
+
line_items.map(&:discountable_amount)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def allocated_amounts
|
|
36
|
+
total_amount.to_money.allocate(elligible_amounts).map(&:to_money)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
class EligibilityResults
|
|
5
|
+
include Enumerable
|
|
6
|
+
attr_reader :results, :promotion
|
|
7
|
+
|
|
8
|
+
def initialize(promotion)
|
|
9
|
+
@promotion = promotion
|
|
10
|
+
@results = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add(item:, condition:, success:, code:, message:)
|
|
14
|
+
results << EligibilityResult.new(
|
|
15
|
+
item: item,
|
|
16
|
+
condition: condition,
|
|
17
|
+
success: success,
|
|
18
|
+
code: code,
|
|
19
|
+
message: message
|
|
20
|
+
)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def success?
|
|
24
|
+
return true if results.empty?
|
|
25
|
+
promotion.benefits.any? do |benefit|
|
|
26
|
+
benefit.conditions.all? do |condition|
|
|
27
|
+
results_for_condition = results.select { |result| result.condition == condition }
|
|
28
|
+
results_for_condition.any?(&:success)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def error_messages
|
|
34
|
+
return [] if results.empty?
|
|
35
|
+
results.group_by(&:condition).map do |_condition, results|
|
|
36
|
+
next if results.any?(&:success)
|
|
37
|
+
results.detect { |r| !r.success }&.message
|
|
38
|
+
end.compact
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def each(&block)
|
|
42
|
+
results.each(&block)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
delegate :last, to: :results
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
# Simple object used to hold discount data for an item.
|
|
5
|
+
#
|
|
6
|
+
# This generic object will hold the amount of discount that should be applied to
|
|
7
|
+
# an item.
|
|
8
|
+
#
|
|
9
|
+
# @attr_reader [Spree::LineItem,Spree::Shipment] the item to be discounted.
|
|
10
|
+
# @attr_reader [String] label information about the discount
|
|
11
|
+
# @attr_reader [ApplicationRecord] source will be used as the source for adjustments
|
|
12
|
+
# @attr_reader [BigDecimal] amount the amount of discount applied to the item
|
|
13
|
+
class ItemDiscount
|
|
14
|
+
include ActiveModel::Model
|
|
15
|
+
attr_accessor :item, :label, :source, :amount
|
|
16
|
+
|
|
17
|
+
def ==(other)
|
|
18
|
+
item == other.item && label == other.label && source == other.source && amount == other.amount
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|