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,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Benefits
|
|
5
|
+
class CreateDiscountedItem < Benefit
|
|
6
|
+
include OrderBenefit
|
|
7
|
+
preference :variant_id, :integer
|
|
8
|
+
preference :quantity, :integer, default: 1
|
|
9
|
+
preference :necessary_quantity, :integer, default: 1
|
|
10
|
+
|
|
11
|
+
def perform(order)
|
|
12
|
+
line_item = find_item(order) || create_item(order)
|
|
13
|
+
set_quantity(line_item, determine_item_quantity(order))
|
|
14
|
+
line_item.current_discounts << discount(line_item)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def remove_from(order)
|
|
18
|
+
line_item = find_item(order)
|
|
19
|
+
order.line_items.destroy(line_item)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def find_item(order)
|
|
25
|
+
order.line_items.detect { |line_item| line_item.managed_by_order_benefit == self }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def create_item(order)
|
|
29
|
+
order.line_items.create!(quantity: determine_item_quantity(order), variant: variant, managed_by_order_benefit: self)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def determine_item_quantity(order)
|
|
33
|
+
# Integer division will floor automatically, which is what we want here:
|
|
34
|
+
# 1 Item, 2 needed: 1 * 1 / 2 => 0
|
|
35
|
+
# 5 items, 2 preferred, 2 needed: 5 / 2 * 2 => 4
|
|
36
|
+
applicable_line_items(order).sum(&:quantity) / preferred_necessary_quantity * preferred_quantity
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def set_quantity(line_item, quantity)
|
|
40
|
+
line_item.quantity_setter = self
|
|
41
|
+
line_item.quantity = quantity
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def variant
|
|
45
|
+
Spree::Variant.find(preferred_variant_id)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "spree/calculator"
|
|
4
|
+
|
|
5
|
+
# This is a calculator for line item adjustment benefits. It accepts a line item
|
|
6
|
+
# and calculates its weighted adjustment amount based on the value of the
|
|
7
|
+
# preferred amount and the price of the other line items. More expensive line
|
|
8
|
+
# items will receive a greater share of the preferred amount.
|
|
9
|
+
module SolidusPromotions
|
|
10
|
+
module Calculators
|
|
11
|
+
class DistributedAmount < Spree::Calculator
|
|
12
|
+
include PromotionCalculator
|
|
13
|
+
|
|
14
|
+
preference :amount, :decimal, default: 0
|
|
15
|
+
preference :currency, :string, default: -> { Spree::Config[:currency] }
|
|
16
|
+
|
|
17
|
+
def compute_line_item(line_item)
|
|
18
|
+
return 0 unless line_item
|
|
19
|
+
return 0 unless preferred_currency.casecmp(line_item.currency).zero?
|
|
20
|
+
|
|
21
|
+
distributable_line_items = calculable.applicable_line_items(line_item.order)
|
|
22
|
+
return 0 unless line_item.in?(distributable_line_items)
|
|
23
|
+
|
|
24
|
+
DistributedAmountsHandler.new(
|
|
25
|
+
distributable_line_items,
|
|
26
|
+
preferred_amount
|
|
27
|
+
).amount(line_item)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "spree/calculator"
|
|
4
|
+
|
|
5
|
+
module SolidusPromotions
|
|
6
|
+
module Calculators
|
|
7
|
+
class FlatRate < Spree::Calculator
|
|
8
|
+
include PromotionCalculator
|
|
9
|
+
|
|
10
|
+
preference :amount, :decimal, default: 0
|
|
11
|
+
preference :currency, :string, default: -> { Spree::Config[:currency] }
|
|
12
|
+
|
|
13
|
+
def compute(object = nil)
|
|
14
|
+
currency = object.order.currency
|
|
15
|
+
if object && preferred_currency.casecmp(currency).zero?
|
|
16
|
+
preferred_amount
|
|
17
|
+
else
|
|
18
|
+
0
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "spree/calculator"
|
|
4
|
+
|
|
5
|
+
module SolidusPromotions
|
|
6
|
+
module Calculators
|
|
7
|
+
class FlexiRate < Spree::Calculator
|
|
8
|
+
include PromotionCalculator
|
|
9
|
+
|
|
10
|
+
preference :first_item, :decimal, default: 0
|
|
11
|
+
preference :additional_item, :decimal, default: 0
|
|
12
|
+
preference :max_items, :integer, default: 0
|
|
13
|
+
preference :currency, :string, default: -> { Spree::Config[:currency] }
|
|
14
|
+
|
|
15
|
+
def compute(object)
|
|
16
|
+
items_count = object.quantity
|
|
17
|
+
items_count = [items_count, preferred_max_items].min unless preferred_max_items.zero?
|
|
18
|
+
|
|
19
|
+
return BigDecimal("0") if items_count == 0
|
|
20
|
+
|
|
21
|
+
additional_items_count = items_count - 1
|
|
22
|
+
preferred_first_item + preferred_additional_item * additional_items_count
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "spree/calculator"
|
|
4
|
+
|
|
5
|
+
module SolidusPromotions
|
|
6
|
+
module Calculators
|
|
7
|
+
class Percent < Spree::Calculator
|
|
8
|
+
include PromotionCalculator
|
|
9
|
+
|
|
10
|
+
preference :percent, :decimal, default: 0
|
|
11
|
+
|
|
12
|
+
def compute(object)
|
|
13
|
+
preferred_currency = object.order.currency
|
|
14
|
+
currency_exponent = ::Money::Currency.find(preferred_currency).exponent
|
|
15
|
+
(object.discountable_amount * preferred_percent / 100).round(currency_exponent)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "spree/calculator"
|
|
4
|
+
|
|
5
|
+
module SolidusPromotions
|
|
6
|
+
module Calculators
|
|
7
|
+
class TieredFlatRate < Spree::Calculator
|
|
8
|
+
include PromotionCalculator
|
|
9
|
+
|
|
10
|
+
preference :base_amount, :decimal, default: 0
|
|
11
|
+
preference :tiers, :hash, default: { 10 => 10 }
|
|
12
|
+
preference :currency, :string, default: -> { Spree::Config[:currency] }
|
|
13
|
+
|
|
14
|
+
before_validation do
|
|
15
|
+
# Convert tier values to decimals. Strings don't do us much good.
|
|
16
|
+
if preferred_tiers.is_a?(Hash)
|
|
17
|
+
self.preferred_tiers = preferred_tiers.map do |key, value|
|
|
18
|
+
[cast_to_d(key.to_s), cast_to_d(value.to_s)]
|
|
19
|
+
end.to_h
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
validate :preferred_tiers_content
|
|
24
|
+
|
|
25
|
+
def compute_item(object)
|
|
26
|
+
_base, amount = preferred_tiers.sort.reverse.detect do |value, _|
|
|
27
|
+
object.discountable_amount >= value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if preferred_currency.casecmp(object.currency).zero?
|
|
31
|
+
amount || preferred_base_amount
|
|
32
|
+
else
|
|
33
|
+
0
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
alias_method :compute_shipment, :compute_item
|
|
37
|
+
alias_method :compute_line_item, :compute_item
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def cast_to_d(value)
|
|
42
|
+
value.to_s.to_d
|
|
43
|
+
rescue ArgumentError
|
|
44
|
+
BigDecimal("0")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def preferred_tiers_content
|
|
48
|
+
if preferred_tiers.is_a? Hash
|
|
49
|
+
unless preferred_tiers.keys.all? { |key| key.is_a?(Numeric) && key > 0 }
|
|
50
|
+
errors.add(:base, :keys_should_be_positive_number)
|
|
51
|
+
end
|
|
52
|
+
else
|
|
53
|
+
errors.add(:preferred_tiers, :should_be_hash)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "spree/calculator"
|
|
4
|
+
|
|
5
|
+
module SolidusPromotions
|
|
6
|
+
module Calculators
|
|
7
|
+
class TieredPercent < Spree::Calculator
|
|
8
|
+
include PromotionCalculator
|
|
9
|
+
|
|
10
|
+
preference :base_percent, :decimal, default: 0
|
|
11
|
+
preference :tiers, :hash, default: { 50 => 5 }
|
|
12
|
+
preference :currency, :string, default: -> { Spree::Config[:currency] }
|
|
13
|
+
|
|
14
|
+
before_validation do
|
|
15
|
+
# Convert tier values to decimals. Strings don't do us much good.
|
|
16
|
+
if preferred_tiers.is_a?(Hash)
|
|
17
|
+
self.preferred_tiers = preferred_tiers.map do |key, value|
|
|
18
|
+
[cast_to_d(key.to_s), cast_to_d(value.to_s)]
|
|
19
|
+
end.to_h
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
validates :preferred_base_percent, numericality: {
|
|
24
|
+
greater_than_or_equal_to: 0,
|
|
25
|
+
less_than_or_equal_to: 100
|
|
26
|
+
}
|
|
27
|
+
validate :preferred_tiers_content
|
|
28
|
+
|
|
29
|
+
def compute_item(object)
|
|
30
|
+
order = object.order
|
|
31
|
+
|
|
32
|
+
_base, percent = preferred_tiers.sort.reverse.detect do |value, _|
|
|
33
|
+
order.item_total >= value
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
if preferred_currency.casecmp(order.currency).zero?
|
|
37
|
+
currency_exponent = ::Money::Currency.find(preferred_currency).exponent
|
|
38
|
+
(object.amount * (percent || preferred_base_percent) / 100).round(currency_exponent)
|
|
39
|
+
else
|
|
40
|
+
0
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
alias_method :compute_shipment, :compute_item
|
|
44
|
+
alias_method :compute_line_item, :compute_item
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def cast_to_d(value)
|
|
49
|
+
value.to_s.to_d
|
|
50
|
+
rescue ArgumentError
|
|
51
|
+
BigDecimal("0")
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def preferred_tiers_content
|
|
55
|
+
if preferred_tiers.is_a? Hash
|
|
56
|
+
unless preferred_tiers.keys.all? { |key| key.is_a?(Numeric) && key > 0 }
|
|
57
|
+
errors.add(:base, :keys_should_be_positive_number)
|
|
58
|
+
end
|
|
59
|
+
unless preferred_tiers.values.all? { |key| key.is_a?(Numeric) && key >= 0 && key <= 100 }
|
|
60
|
+
errors.add(:base, :values_should_be_percent)
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
errors.add(:preferred_tiers, :should_be_hash)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_dependency "spree/calculator"
|
|
4
|
+
|
|
5
|
+
module SolidusPromotions
|
|
6
|
+
module Calculators
|
|
7
|
+
class TieredPercentOnEligibleItemQuantity < SolidusPromotions::Calculators::TieredPercent
|
|
8
|
+
preference :tiers, :hash, default: { 10 => 5 }
|
|
9
|
+
|
|
10
|
+
before_validation do
|
|
11
|
+
# Convert tier values to decimals. Strings don't do us much good.
|
|
12
|
+
if preferred_tiers.is_a?(Hash)
|
|
13
|
+
self.preferred_tiers = preferred_tiers.map do |key, value|
|
|
14
|
+
[key.to_i, cast_to_d(value.to_s)]
|
|
15
|
+
end.to_h
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def compute_line_item(line_item)
|
|
20
|
+
order = line_item.order
|
|
21
|
+
|
|
22
|
+
_base, percent = preferred_tiers.sort.reverse.detect do |value, _|
|
|
23
|
+
eligible_line_items_quantity_total(order) >= value
|
|
24
|
+
end
|
|
25
|
+
if preferred_currency.casecmp(order.currency).zero?
|
|
26
|
+
currency_exponent = ::Money::Currency.find(preferred_currency).exponent
|
|
27
|
+
(line_item.discountable_amount * (percent || preferred_base_percent) / 100).round(currency_exponent)
|
|
28
|
+
else
|
|
29
|
+
0
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def eligible_line_items_quantity_total(order)
|
|
36
|
+
calculable.applicable_line_items(order).sum(&:quantity)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spree/preferences/persistable"
|
|
4
|
+
|
|
5
|
+
module SolidusPromotions
|
|
6
|
+
class Condition < Spree::Base
|
|
7
|
+
include Spree::Preferences::Persistable
|
|
8
|
+
|
|
9
|
+
belongs_to :benefit, class_name: "SolidusPromotions::Benefit", inverse_of: :conditions, optional: true
|
|
10
|
+
has_one :promotion, through: :benefit
|
|
11
|
+
|
|
12
|
+
scope :of_type, ->(type) { where(type: type) }
|
|
13
|
+
|
|
14
|
+
validate :unique_per_benefit, on: :create
|
|
15
|
+
validate :possible_condition_for_benefit, if: -> { benefit.present? }
|
|
16
|
+
|
|
17
|
+
def preload_relations
|
|
18
|
+
[]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def applicable?(_promotable)
|
|
22
|
+
raise NotImplementedError, "applicable? should be implemented in a sub-class of SolidusPromotions::Rule"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def eligible?(_promotable, _options = {})
|
|
26
|
+
raise NotImplementedError, "eligible? should be implemented in a sub-class of SolidusPromotions::Rule"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def level
|
|
30
|
+
raise NotImplementedError, "level should be implemented in a sub-class of SolidusPromotions::Rule"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def eligibility_errors
|
|
34
|
+
@eligibility_errors ||= ActiveModel::Errors.new(self)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def to_partial_path
|
|
38
|
+
"solidus_promotions/admin/condition_fields/#{model_name.element}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def updateable?
|
|
42
|
+
preferences.any?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def unique_per_benefit
|
|
48
|
+
return unless self.class.exists?(benefit_id: benefit_id, type: self.class.name)
|
|
49
|
+
|
|
50
|
+
errors.add(:benefit, :already_contains_condition_type)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def possible_condition_for_benefit
|
|
54
|
+
benefit.possible_conditions.include?(self.class) || errors.add(:type, :invalid_condition_type)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def eligibility_error_message(key, options = {})
|
|
58
|
+
I18n.t(key, scope: [:solidus_promotions, :eligibility_errors, self.class.name.underscore], **options)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
# A condition to apply to an order greater than (or greater than or equal to)
|
|
6
|
+
# a specific amount after previous promotions have applied
|
|
7
|
+
#
|
|
8
|
+
# To add extra operators please override `self.operators_map` or any other helper method.
|
|
9
|
+
# To customize the error message you can also override `ineligible_message`.
|
|
10
|
+
class DiscountedItemTotal < ItemTotal
|
|
11
|
+
def to_partial_path
|
|
12
|
+
"solidus_promotions/admin/condition_fields/item_total"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def total_for_order(order)
|
|
18
|
+
order.discountable_item_total
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class FirstOrder < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
attr_reader :user, :email
|
|
8
|
+
|
|
9
|
+
def eligible?(order, options = {})
|
|
10
|
+
@user = order.try(:user) || options[:user]
|
|
11
|
+
@email = order.email
|
|
12
|
+
|
|
13
|
+
if (user || email) && (completed_orders.present? && completed_orders.first != order)
|
|
14
|
+
eligibility_errors.add(:base, eligibility_error_message(:not_first_order), error_code: :not_first_order)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
eligibility_errors.empty?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def completed_orders
|
|
23
|
+
user ? user.orders.complete : orders_by_email
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def orders_by_email
|
|
27
|
+
Spree::Order.where(email: email).complete
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class FirstRepeatPurchaseSince < Condition
|
|
6
|
+
include OrderLevelCondition
|
|
7
|
+
|
|
8
|
+
preference :days_ago, :integer, default: 365
|
|
9
|
+
validates :preferred_days_ago, numericality: { only_integer: true, greater_than: 0 }
|
|
10
|
+
|
|
11
|
+
# This is never eligible if the order does not have a user, and that user does not have any previous completed orders.
|
|
12
|
+
#
|
|
13
|
+
# This is eligible if the user's most recently completed order is more than the preferred days ago
|
|
14
|
+
# @param order [Spree::Order]
|
|
15
|
+
def eligible?(order, _options = {})
|
|
16
|
+
return false unless order.user
|
|
17
|
+
|
|
18
|
+
last_order = last_completed_order(order.user)
|
|
19
|
+
return false unless last_order
|
|
20
|
+
|
|
21
|
+
last_order.completed_at < preferred_days_ago.days.ago
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def last_completed_order(user)
|
|
27
|
+
user.orders.complete.order(:completed_at).last
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
# A condition to apply to an order greater than (or greater than or equal to)
|
|
6
|
+
# a specific amount
|
|
7
|
+
#
|
|
8
|
+
# To add extra operators please override `self.operators_map` or any other helper method.
|
|
9
|
+
# To customize the error message you can also override `ineligible_message`.
|
|
10
|
+
class ItemTotal < Condition
|
|
11
|
+
include OrderLevelCondition
|
|
12
|
+
|
|
13
|
+
preference :amount, :decimal, default: 100.00
|
|
14
|
+
preference :currency, :string, default: -> { Spree::Config[:currency] }
|
|
15
|
+
preference :operator, :string, default: "gt"
|
|
16
|
+
|
|
17
|
+
# The list of allowed operators names mapped to their symbols.
|
|
18
|
+
def self.operators_map
|
|
19
|
+
{
|
|
20
|
+
gte: :>=,
|
|
21
|
+
gt: :>
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.operator_options
|
|
26
|
+
operators_map.map do |name, _method|
|
|
27
|
+
[I18n.t(name, scope: "solidus_promotions.item_total_condition.operators"), name]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def eligible?(order, _options = {})
|
|
32
|
+
return false unless order.currency == preferred_currency
|
|
33
|
+
|
|
34
|
+
unless total_for_order(order).send(operator, threshold)
|
|
35
|
+
eligibility_errors.add(:base, ineligible_message, error_code: ineligible_error_code)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
eligibility_errors.empty?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def operator
|
|
44
|
+
self.class.operators_map.fetch(
|
|
45
|
+
preferred_operator.to_sym,
|
|
46
|
+
preferred_operator_default
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def total_for_order(order)
|
|
51
|
+
order.item_total
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def threshold
|
|
55
|
+
BigDecimal(preferred_amount.to_s)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def formatted_amount
|
|
59
|
+
Spree::Money.new(preferred_amount, currency: preferred_currency).to_s
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def ineligible_message
|
|
63
|
+
eligibility_error_message(ineligible_error_code, amount: formatted_amount)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def ineligible_error_code
|
|
67
|
+
if preferred_operator == "gte"
|
|
68
|
+
:item_total_less_than
|
|
69
|
+
else
|
|
70
|
+
:item_total_less_than_or_equal
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
class LineItemOptionValue < Condition
|
|
6
|
+
include LineItemLevelCondition
|
|
7
|
+
|
|
8
|
+
preference :eligible_values, :hash
|
|
9
|
+
|
|
10
|
+
def eligible?(line_item, _options = {})
|
|
11
|
+
pid = line_item.product.id
|
|
12
|
+
ovids = line_item.variant.option_values.pluck(:id)
|
|
13
|
+
|
|
14
|
+
product_ids.include?(pid) && (value_ids(pid) & ovids).present?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def preferred_eligible_values
|
|
18
|
+
values = preferences[:eligible_values] || {}
|
|
19
|
+
values.keys.map(&:to_i).zip(
|
|
20
|
+
values.values.map do |value|
|
|
21
|
+
(value.is_a?(Array) ? value : value.split(",")).map(&:to_i)
|
|
22
|
+
end
|
|
23
|
+
).to_h
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def product_ids
|
|
29
|
+
preferred_eligible_values.keys
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def value_ids(product_id)
|
|
33
|
+
preferred_eligible_values[product_id]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SolidusPromotions
|
|
4
|
+
module Conditions
|
|
5
|
+
# A condition to apply a promotion only to line items with or without a chosen product
|
|
6
|
+
class LineItemProduct < Condition
|
|
7
|
+
include LineItemLevelCondition
|
|
8
|
+
|
|
9
|
+
MATCH_POLICIES = %w[include exclude].freeze
|
|
10
|
+
|
|
11
|
+
has_many :condition_products,
|
|
12
|
+
dependent: :destroy,
|
|
13
|
+
foreign_key: :condition_id,
|
|
14
|
+
class_name: "SolidusPromotions::ConditionProduct"
|
|
15
|
+
has_many :products,
|
|
16
|
+
class_name: "Spree::Product",
|
|
17
|
+
through: :condition_products
|
|
18
|
+
|
|
19
|
+
preference :match_policy, :string, default: MATCH_POLICIES.first
|
|
20
|
+
|
|
21
|
+
def preload_relations
|
|
22
|
+
[:products]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def eligible?(line_item, _options = {})
|
|
26
|
+
order_includes_product = product_ids.include?(line_item.variant.product_id)
|
|
27
|
+
success = inverse? ? !order_includes_product : order_includes_product
|
|
28
|
+
|
|
29
|
+
unless success
|
|
30
|
+
message_code = inverse? ? :has_excluded_product : :no_applicable_products
|
|
31
|
+
eligibility_errors.add(
|
|
32
|
+
:base,
|
|
33
|
+
eligibility_error_message(message_code),
|
|
34
|
+
error_code: message_code
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
success
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def product_ids_string
|
|
42
|
+
product_ids.join(",")
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def product_ids_string=(product_ids)
|
|
46
|
+
self.product_ids = product_ids.to_s.split(",").map(&:strip)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def inverse?
|
|
52
|
+
preferred_match_policy == "exclude"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|