solidus_legacy_promotions 4.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +345 -0
- data/Rakefile +43 -0
- data/app/assets/config/solidus_legacy_promotions/manifest.js +2 -0
- data/app/assets/javascripts/spree/backend/edit_promotion.js +7 -0
- data/app/assets/javascripts/spree/backend/promotions/activation.js +26 -0
- data/app/assets/javascripts/spree/backend/promotions.js +35 -0
- data/app/assets/javascripts/spree/backend/templates/promotions/calculators/fields/tiered_flat_rate.hbs +23 -0
- data/app/assets/javascripts/spree/backend/templates/promotions/calculators/fields/tiered_percent.hbs +23 -0
- data/app/assets/javascripts/spree/backend/templates/promotions/rules/option_values.hbs +12 -0
- data/app/assets/javascripts/spree/backend/views/calculators/tiered.js +54 -0
- data/app/assets/javascripts/spree/backend/views/order/details_adjustments.js +43 -0
- data/app/assets/javascripts/spree/backend/views/promotions/option_values_rule.js +79 -0
- data/app/assets/javascripts/spree/backend/views/promotions.js +3 -0
- data/app/assets/stylesheets/solidus_legacy_promotions/promotions/_edit.scss +138 -0
- data/app/assets/stylesheets/solidus_legacy_promotions/promotions.scss +3 -0
- data/app/assets/stylesheets/spree/backend/sections/_adjustments.scss +3 -0
- data/app/decorators/solidus_legacy_promotions/controllers/solidus_admin/solidus_admin_adjustments_controller_decorator.rb +20 -0
- data/app/decorators/solidus_legacy_promotions/lib/spree_order_state_machine_decorator.rb +18 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_adjustment_decorator.rb +76 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_calculator_returns_default_refund_amount_decorator.rb +13 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_line_item_decorator.rb +11 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_order_decorator.rb +28 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_order_updater_decorator.rb +40 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_product_decorator.rb +16 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_promotion_code_batch_decorator.rb +16 -0
- data/app/decorators/solidus_legacy_promotions/models/spree_shipment_decorator.rb +13 -0
- data/app/helpers/spree/admin/promotions_helper.rb +15 -0
- data/app/helpers/spree/promotion_rules_helper.rb +12 -0
- data/app/jobs/spree/promotion_code_batch_job.rb +26 -0
- data/app/mailers/spree/promotion_code_batch_mailer.rb +15 -0
- data/app/models/spree/calculator/distributed_amount.rb +33 -0
- data/app/models/spree/calculator/flat_percent_item_total.rb +23 -0
- data/app/models/spree/calculator/flexi_rate.rb +22 -0
- data/app/models/spree/calculator/percent_on_line_item.rb +13 -0
- data/app/models/spree/calculator/tiered_flat_rate.rb +52 -0
- data/app/models/spree/calculator/tiered_percent.rb +62 -0
- data/app/models/spree/order_contents.rb +36 -0
- data/app/models/spree/order_promotion.rb +27 -0
- data/app/models/spree/permission_sets/promotion_display.rb +25 -0
- data/app/models/spree/permission_sets/promotion_management.rb +25 -0
- data/app/models/spree/promotion/actions/create_adjustment.rb +81 -0
- data/app/models/spree/promotion/actions/create_item_adjustments.rb +98 -0
- data/app/models/spree/promotion/actions/create_quantity_adjustments.rb +139 -0
- data/app/models/spree/promotion/actions/free_shipping.rb +59 -0
- data/app/models/spree/promotion/order_adjustments_recalculator.rb +92 -0
- data/app/models/spree/promotion/rules/first_order.rb +36 -0
- data/app/models/spree/promotion/rules/first_repeat_purchase_since.rb +36 -0
- data/app/models/spree/promotion/rules/item_total.rb +86 -0
- data/app/models/spree/promotion/rules/minimum_quantity.rb +59 -0
- data/app/models/spree/promotion/rules/nth_order.rb +45 -0
- data/app/models/spree/promotion/rules/one_use_per_user.rb +25 -0
- data/app/models/spree/promotion/rules/option_value.rb +50 -0
- data/app/models/spree/promotion/rules/product.rb +86 -0
- data/app/models/spree/promotion/rules/store.rb +26 -0
- data/app/models/spree/promotion/rules/taxon.rb +91 -0
- data/app/models/spree/promotion/rules/user.rb +34 -0
- data/app/models/spree/promotion/rules/user_logged_in.rb +20 -0
- data/app/models/spree/promotion/rules/user_role.rb +45 -0
- data/app/models/spree/promotion.rb +271 -0
- data/app/models/spree/promotion_action.rb +51 -0
- data/app/models/spree/promotion_advertiser.rb +10 -0
- data/app/models/spree/promotion_category.rb +8 -0
- data/app/models/spree/promotion_chooser.rb +34 -0
- data/app/models/spree/promotion_code/batch_builder.rb +68 -0
- data/app/models/spree/promotion_code.rb +54 -0
- data/app/models/spree/promotion_code_batch.rb +18 -0
- data/app/models/spree/promotion_finder.rb +9 -0
- data/app/models/spree/promotion_handler/cart.rb +75 -0
- data/app/models/spree/promotion_handler/coupon.rb +125 -0
- data/app/models/spree/promotion_handler/page.rb +26 -0
- data/app/models/spree/promotion_handler/shipping.rb +61 -0
- data/app/models/spree/promotion_rule.rb +55 -0
- data/app/models/spree/promotion_rule_store.rb +10 -0
- data/app/models/spree/promotion_rule_taxon.rb +8 -0
- data/app/models/spree/promotion_rule_user.rb +10 -0
- data/app/subscribers/spree/order_promotion_subscriber.rb +20 -0
- data/app/views/spree/order_mailer/cancel_email.html.erb +45 -0
- data/app/views/spree/order_mailer/cancel_email.text.erb +16 -0
- data/app/views/spree/order_mailer/confirm_email.html.erb +84 -0
- data/app/views/spree/order_mailer/confirm_email.text.erb +38 -0
- data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_errored.text.erb +2 -0
- data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_finished.text.erb +2 -0
- data/bin/rails +13 -0
- data/config/locales/en.yml +101 -0
- data/config/locales/promotion_categories.en.yml +6 -0
- data/config/locales/promotions.en.yml +6 -0
- data/config/routes.rb +28 -0
- data/db/migrate/20160101010001_solidus_one_four_promotions.rb +126 -0
- data/db/migrate/20161017102621_create_spree_promotion_code_batch.rb +48 -0
- data/db/migrate/20180202190713_create_promotion_rule_stores.rb +14 -0
- data/db/migrate/20180328172631_add_join_characters_to_promotion_code_batch.rb +15 -0
- data/db/migrate/20190106184413_remove_code_from_spree_promotions.rb +46 -0
- data/db/migrate/20220317165036_set_promotions_with_any_policy_to_all_if_possible.rb +20 -0
- data/db/migrate/20230322085416_remove_match_policy_from_spree_promotion.rb +7 -0
- data/db/migrate/20230325132905_remove_unused_columns_from_promotion_rules.rb +10 -0
- data/db/migrate/20231027084517_add_order_promotions_foreign_key.rb +14 -0
- data/db/migrate/20240621100123_add_promotion_code_id_to_spree_adjustments.rb +10 -0
- data/db/migrate/20240622113334_move_adjustment_eligible_to_legacy_promotions.rb +11 -0
- data/lib/components/admin/solidus_admin/orders/show/adjustments/index/source/spree_promotion_action/component.rb +17 -0
- data/lib/components/admin/solidus_admin/promotion_categories/index/component.rb +56 -0
- data/lib/components/admin/solidus_admin/promotions/index/component.rb +104 -0
- data/lib/components/admin/solidus_admin/promotions/index/component.yml +10 -0
- data/lib/components/admin/solidus_legacy_promotions/orders/index/component.rb +15 -0
- data/lib/components/admin/solidus_legacy_promotions/orders/index/component.yml +20 -0
- data/lib/controllers/admin/solidus_admin/promotion_categories_controller.rb +29 -0
- data/lib/controllers/admin/solidus_admin/promotions_controller.rb +46 -0
- data/lib/controllers/backend/spree/admin/promotion_actions_controller.rb +51 -0
- data/lib/controllers/backend/spree/admin/promotion_categories_controller.rb +8 -0
- data/lib/controllers/backend/spree/admin/promotion_code_batches_controller.rb +30 -0
- data/lib/controllers/backend/spree/admin/promotion_codes_controller.rb +48 -0
- data/lib/controllers/backend/spree/admin/promotion_rules_controller.rb +60 -0
- data/lib/controllers/backend/spree/admin/promotions_controller.rb +66 -0
- data/lib/solidus_legacy_promotions/configuration.rb +115 -0
- data/lib/solidus_legacy_promotions/engine.rb +97 -0
- data/lib/solidus_legacy_promotions/migrations/promotions_with_code_handlers.rb +68 -0
- data/lib/solidus_legacy_promotions/testing_support/factories/calculator_factory.rb +7 -0
- data/lib/solidus_legacy_promotions/testing_support/factories/order_factory.rb +22 -0
- data/lib/solidus_legacy_promotions/testing_support/factories/order_promotion_factory.rb +8 -0
- data/lib/solidus_legacy_promotions/testing_support/factories/promotion_category_factory.rb +7 -0
- data/lib/solidus_legacy_promotions/testing_support/factories/promotion_code_factory.rb +8 -0
- data/lib/solidus_legacy_promotions/testing_support/factories/promotion_factory.rb +98 -0
- data/lib/solidus_legacy_promotions/testing_support/factory_bot.rb +28 -0
- data/lib/solidus_legacy_promotions.rb +28 -0
- data/lib/tasks/solidus_legacy_promotions/delete_ineligible_adjustments.rake +8 -0
- data/lib/views/backend/spree/admin/adjustments/_adjustment.html.erb +24 -0
- data/lib/views/backend/spree/admin/orders/_adjustments.html.erb +19 -0
- data/lib/views/backend/spree/admin/orders/_order_details.html.erb +32 -0
- data/lib/views/backend/spree/admin/orders/confirm.html.erb +59 -0
- data/lib/views/backend/spree/admin/promotion_actions/create.js.erb +10 -0
- data/lib/views/backend/spree/admin/promotion_actions/destroy.js.erb +1 -0
- data/lib/views/backend/spree/admin/promotion_categories/_form.html.erb +14 -0
- data/lib/views/backend/spree/admin/promotion_categories/edit.html.erb +10 -0
- data/lib/views/backend/spree/admin/promotion_categories/index.html.erb +47 -0
- data/lib/views/backend/spree/admin/promotion_categories/new.html.erb +10 -0
- data/lib/views/backend/spree/admin/promotion_code_batches/_form_fields.html.erb +22 -0
- data/lib/views/backend/spree/admin/promotion_code_batches/download.csv.ruby +8 -0
- data/lib/views/backend/spree/admin/promotion_code_batches/index.html.erb +65 -0
- data/lib/views/backend/spree/admin/promotion_code_batches/new.html.erb +8 -0
- data/lib/views/backend/spree/admin/promotion_codes/index.csv.ruby +8 -0
- data/lib/views/backend/spree/admin/promotion_codes/index.html.erb +32 -0
- data/lib/views/backend/spree/admin/promotion_codes/new.html.erb +31 -0
- data/lib/views/backend/spree/admin/promotion_rules/create.js.erb +8 -0
- data/lib/views/backend/spree/admin/promotion_rules/destroy.js.erb +3 -0
- data/lib/views/backend/spree/admin/promotions/_actions.html.erb +37 -0
- data/lib/views/backend/spree/admin/promotions/_activations_edit.html.erb +22 -0
- data/lib/views/backend/spree/admin/promotions/_activations_new.html.erb +43 -0
- data/lib/views/backend/spree/admin/promotions/_form.html.erb +67 -0
- data/lib/views/backend/spree/admin/promotions/_promotion_action.html.erb +13 -0
- data/lib/views/backend/spree/admin/promotions/_promotion_rule.html.erb +12 -0
- data/lib/views/backend/spree/admin/promotions/_rules.html.erb +42 -0
- data/lib/views/backend/spree/admin/promotions/actions/_create_adjustment.html.erb +2 -0
- data/lib/views/backend/spree/admin/promotions/actions/_create_item_adjustments.html.erb +6 -0
- data/lib/views/backend/spree/admin/promotions/actions/_create_quantity_adjustments.html.erb +10 -0
- data/lib/views/backend/spree/admin/promotions/actions/_free_shipping.html.erb +0 -0
- data/lib/views/backend/spree/admin/promotions/actions/_promotion_calculators_with_custom_fields.html.erb +29 -0
- data/lib/views/backend/spree/admin/promotions/calculators/_default_fields.html.erb +6 -0
- data/lib/views/backend/spree/admin/promotions/calculators/distributed_amount/_fields.html.erb +56 -0
- data/lib/views/backend/spree/admin/promotions/calculators/flat_rate/_fields.html.erb +6 -0
- data/lib/views/backend/spree/admin/promotions/calculators/tiered_flat_rate/_fields.html.erb +30 -0
- data/lib/views/backend/spree/admin/promotions/calculators/tiered_percent/_fields.html.erb +30 -0
- data/lib/views/backend/spree/admin/promotions/edit.html.erb +40 -0
- data/lib/views/backend/spree/admin/promotions/index.html.erb +124 -0
- data/lib/views/backend/spree/admin/promotions/new.html.erb +14 -0
- data/lib/views/backend/spree/admin/promotions/rules/_first_order.html.erb +0 -0
- data/lib/views/backend/spree/admin/promotions/rules/_first_repeat_purchase_since.html.erb +13 -0
- data/lib/views/backend/spree/admin/promotions/rules/_item_total.html.erb +14 -0
- data/lib/views/backend/spree/admin/promotions/rules/_minimum_quantity.html.erb +5 -0
- data/lib/views/backend/spree/admin/promotions/rules/_nth_order.html.erb +12 -0
- data/lib/views/backend/spree/admin/promotions/rules/_one_use_per_user.html.erb +0 -0
- data/lib/views/backend/spree/admin/promotions/rules/_option_value.html.erb +13 -0
- data/lib/views/backend/spree/admin/promotions/rules/_product.html.erb +15 -0
- data/lib/views/backend/spree/admin/promotions/rules/_store.html.erb +6 -0
- data/lib/views/backend/spree/admin/promotions/rules/_taxon.html.erb +9 -0
- data/lib/views/backend/spree/admin/promotions/rules/_user.html.erb +4 -0
- data/lib/views/backend/spree/admin/promotions/rules/_user_logged_in.html.erb +0 -0
- data/lib/views/backend/spree/admin/promotions/rules/_user_role.html.erb +12 -0
- data/solidus_legacy_promotions.gemspec +29 -0
- metadata +262 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Spree::Admin::PromotionActionsController < Spree::Admin::BaseController
|
4
|
+
before_action :load_promotion, only: [:create, :destroy]
|
5
|
+
before_action :validate_promotion_action_type, only: :create
|
6
|
+
|
7
|
+
def create
|
8
|
+
@promotion_action = @promotion_action_type.new(params[:promotion_action])
|
9
|
+
@calculators = @promotion_action.available_calculators
|
10
|
+
@promotion_action.promotion = @promotion
|
11
|
+
if @promotion_action.save
|
12
|
+
flash[:success] = t('spree.successfully_created', resource: t('spree.promotion_action'))
|
13
|
+
end
|
14
|
+
respond_to do |format|
|
15
|
+
format.html { redirect_to spree.edit_admin_promotion_path(@promotion) }
|
16
|
+
format.js { render layout: false }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def destroy
|
21
|
+
@promotion_action = @promotion.promotion_actions.find(params[:id])
|
22
|
+
if @promotion_action.discard
|
23
|
+
flash[:success] = t('spree.successfully_removed', resource: t('spree.promotion_action'))
|
24
|
+
end
|
25
|
+
respond_to do |format|
|
26
|
+
format.html { redirect_to spree.edit_admin_promotion_path(@promotion) }
|
27
|
+
format.js { render layout: false }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def load_promotion
|
34
|
+
@promotion = Spree::Promotion.find(params[:promotion_id])
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_promotion_action_type
|
38
|
+
requested_type = params[:action_type]
|
39
|
+
promotion_action_types = Spree::Config.promotions.actions
|
40
|
+
@promotion_action_type = promotion_action_types.detect do |klass|
|
41
|
+
klass.name == requested_type
|
42
|
+
end
|
43
|
+
if !@promotion_action_type
|
44
|
+
flash[:error] = t('spree.invalid_promotion_action')
|
45
|
+
respond_to do |format|
|
46
|
+
format.html { redirect_to spree.edit_admin_promotion_path(@promotion) }
|
47
|
+
format.js { render layout: false }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module Admin
|
5
|
+
class PromotionCodeBatchesController < ResourceController
|
6
|
+
belongs_to 'spree/promotion'
|
7
|
+
|
8
|
+
create.after :build_promotion_code_batch
|
9
|
+
|
10
|
+
def download
|
11
|
+
require "csv"
|
12
|
+
|
13
|
+
@promotion_code_batch = Spree::PromotionCodeBatch.find(
|
14
|
+
params[:promotion_code_batch_id]
|
15
|
+
)
|
16
|
+
|
17
|
+
send_data(
|
18
|
+
render_to_string,
|
19
|
+
filename: "promotion-code-batch-list-#{@promotion_code_batch.id}.csv"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def build_promotion_code_batch
|
26
|
+
@promotion_code_batch.process
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module Spree
|
6
|
+
module Admin
|
7
|
+
class PromotionCodesController < Spree::Admin::ResourceController
|
8
|
+
def index
|
9
|
+
@promotion = Spree::Promotion.accessible_by(current_ability, :show).find(params[:promotion_id])
|
10
|
+
@promotion_codes = @promotion.promotion_codes.order(:value)
|
11
|
+
|
12
|
+
respond_to do |format|
|
13
|
+
format.html do
|
14
|
+
@promotion_codes = @promotion_codes.page(params[:page]).per(50)
|
15
|
+
end
|
16
|
+
format.csv do
|
17
|
+
filename = "promotion-code-list-#{@promotion.id}.csv"
|
18
|
+
headers["Content-Type"] = "text/csv"
|
19
|
+
headers["Content-disposition"] = "attachment; filename=\"#{filename}\""
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def new
|
25
|
+
@promotion = Spree::Promotion.accessible_by(current_ability, :show).find(params[:promotion_id])
|
26
|
+
if @promotion.apply_automatically
|
27
|
+
flash[:error] = t('activerecord.errors.models.spree/promotion_code.attributes.base.disallowed_with_apply_automatically')
|
28
|
+
redirect_to admin_promotion_promotion_codes_url(@promotion)
|
29
|
+
else
|
30
|
+
@promotion_code = @promotion.promotion_codes.build
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def create
|
35
|
+
@promotion = Spree::Promotion.accessible_by(current_ability, :show).find(params[:promotion_id])
|
36
|
+
@promotion_code = @promotion.promotion_codes.build(value: params[:promotion_code][:value])
|
37
|
+
|
38
|
+
if @promotion_code.save
|
39
|
+
flash[:success] = flash_message_for(@promotion_code, :successfully_created)
|
40
|
+
redirect_to admin_promotion_promotion_codes_url(@promotion)
|
41
|
+
else
|
42
|
+
flash.now[:error] = @promotion_code.errors.full_messages.to_sentence
|
43
|
+
render_after_create_error
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Spree::Admin::PromotionRulesController < Spree::Admin::BaseController
|
4
|
+
helper 'spree/promotion_rules'
|
5
|
+
|
6
|
+
before_action :load_promotion, only: [:create, :destroy]
|
7
|
+
before_action :validate_promotion_rule_type, only: :create
|
8
|
+
|
9
|
+
def create
|
10
|
+
@promotion_rule = @promotion_rule_type.new(promotion_rule_params)
|
11
|
+
@promotion_rule.promotion = @promotion
|
12
|
+
if @promotion_rule.save
|
13
|
+
flash[:success] = t('spree.successfully_created', resource: t('spree.promotion_rule'))
|
14
|
+
end
|
15
|
+
respond_to do |format|
|
16
|
+
format.html { redirect_to spree.edit_admin_promotion_path(@promotion) }
|
17
|
+
format.js { render layout: false }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def destroy
|
22
|
+
@promotion_rule = @promotion.promotion_rules.find(params[:id])
|
23
|
+
if @promotion_rule.destroy
|
24
|
+
flash[:success] = t('spree.successfully_removed', resource: t('spree.promotion_rule'))
|
25
|
+
end
|
26
|
+
respond_to do |format|
|
27
|
+
format.html { redirect_to spree.edit_admin_promotion_path(@promotion) }
|
28
|
+
format.js { render layout: false }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def load_promotion
|
35
|
+
@promotion = Spree::Promotion.find(params[:promotion_id])
|
36
|
+
end
|
37
|
+
|
38
|
+
def model_class
|
39
|
+
Spree::PromotionRule
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_promotion_rule_type
|
43
|
+
requested_type = params[:promotion_rule].delete(:type)
|
44
|
+
promotion_rule_types = Spree::Config.promotions.rules
|
45
|
+
@promotion_rule_type = promotion_rule_types.detect do |klass|
|
46
|
+
klass.name == requested_type
|
47
|
+
end
|
48
|
+
if !@promotion_rule_type
|
49
|
+
flash[:error] = t('spree.invalid_promotion_rule')
|
50
|
+
respond_to do |format|
|
51
|
+
format.html { redirect_to spree.edit_admin_promotion_path(@promotion) }
|
52
|
+
format.js { render layout: false }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def promotion_rule_params
|
58
|
+
params[:promotion_rule].permit!
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
module Admin
|
5
|
+
class PromotionsController < ResourceController
|
6
|
+
before_action :load_data
|
7
|
+
|
8
|
+
helper 'spree/promotion_rules'
|
9
|
+
|
10
|
+
def show
|
11
|
+
redirect_to action: :edit
|
12
|
+
end
|
13
|
+
|
14
|
+
def create
|
15
|
+
@promotion = Spree::Promotion.new(permitted_resource_params)
|
16
|
+
@promotion.codes.new(value: params[:single_code]) if params[:single_code].present?
|
17
|
+
|
18
|
+
if params[:promotion_code_batch]
|
19
|
+
@promotion_code_batch = @promotion.promotion_code_batches.new(promotion_code_batch_params)
|
20
|
+
end
|
21
|
+
|
22
|
+
if @promotion.save
|
23
|
+
@promotion_code_batch.process if @promotion_code_batch
|
24
|
+
flash[:success] = t('spree.promotion_successfully_created')
|
25
|
+
redirect_to location_after_save
|
26
|
+
else
|
27
|
+
flash[:error] = @promotion.errors.full_messages.to_sentence
|
28
|
+
render action: 'new'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def location_after_save
|
35
|
+
spree.edit_admin_promotion_url(@promotion)
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_data
|
39
|
+
@promotion_categories = Spree::PromotionCategory.order(:name)
|
40
|
+
end
|
41
|
+
|
42
|
+
def collection
|
43
|
+
return @collection if @collection
|
44
|
+
params[:q] ||= HashWithIndifferentAccess.new
|
45
|
+
params[:q][:s] ||= 'id desc'
|
46
|
+
|
47
|
+
@collection = super
|
48
|
+
@search = @collection.ransack(params[:q])
|
49
|
+
@collection = @search.result(distinct: true).
|
50
|
+
includes(promotion_includes).
|
51
|
+
page(params[:page]).
|
52
|
+
per(params[:per_page] || Spree::Config.promotions.promotions_per_page)
|
53
|
+
|
54
|
+
@collection
|
55
|
+
end
|
56
|
+
|
57
|
+
def promotion_code_batch_params
|
58
|
+
params.require(:promotion_code_batch).permit!
|
59
|
+
end
|
60
|
+
|
61
|
+
def promotion_includes
|
62
|
+
[:promotion_actions]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusLegacyPromotions
|
4
|
+
class Configuration < Spree::Preferences::Configuration
|
5
|
+
include Spree::Core::EnvironmentExtension
|
6
|
+
|
7
|
+
# @!attribute [rw] promotions_per_page
|
8
|
+
# @return [Integer] Promotions to show per-page in the admin (default: +15+)
|
9
|
+
preference :promotions_per_page, :integer, default: 15
|
10
|
+
|
11
|
+
# @!attribute [rw] promotion_attributes
|
12
|
+
# @return [Array<Symbol>] Attributes to be returned by the API for a promotion
|
13
|
+
preference :promotion_api_attributes, :array, default: [
|
14
|
+
:id,
|
15
|
+
:name,
|
16
|
+
:description,
|
17
|
+
:expires_at,
|
18
|
+
:starts_at,
|
19
|
+
:type,
|
20
|
+
:usage_limit,
|
21
|
+
:advertise,
|
22
|
+
:path
|
23
|
+
]
|
24
|
+
|
25
|
+
# promotion_chooser_class allows extensions to provide their own PromotionChooser
|
26
|
+
class_name_attribute :promotion_chooser_class, default: 'Spree::PromotionChooser'
|
27
|
+
|
28
|
+
# order_adjuster_class allows extensions to provide their own Order Adjuster
|
29
|
+
class_name_attribute :order_adjuster_class, default: 'Spree::Promotion::OrderAdjustmentsRecalculator'
|
30
|
+
|
31
|
+
# promotion_finder_class allows extensions to provide their own Promotion Finder
|
32
|
+
class_name_attribute :promotion_finder_class, default: 'Spree::PromotionFinder'
|
33
|
+
|
34
|
+
# Allows providing a different shipping promotion handler.
|
35
|
+
# @!attribute [rw] shipping_promotion_handler_class
|
36
|
+
# @see Spree::PromotionHandler::Shipping
|
37
|
+
# @return [Class] an object that conforms to the API of
|
38
|
+
# the standard shipping promotion handler class
|
39
|
+
# Spree::PromotionHandler::Coupon.
|
40
|
+
class_name_attribute :shipping_promotion_handler_class, default: 'Spree::PromotionHandler::Shipping'
|
41
|
+
|
42
|
+
# Allows providing your own Mailer for promotion code batch mailer.
|
43
|
+
#
|
44
|
+
# @!attribute [rw] promotion_code_batch_mailer_class
|
45
|
+
# @return [ActionMailer::Base] an object that responds to "promotion_code_batch_finished",
|
46
|
+
# and "promotion_code_batch_errored"
|
47
|
+
# (e.g. an ActionMailer with a "promotion_code_batch_finished" method) with the same
|
48
|
+
# signature as Spree::PromotionCodeBatchMailer.promotion_code_batch_finished.
|
49
|
+
class_name_attribute :promotion_code_batch_mailer_class, default: 'Spree::PromotionCodeBatchMailer'
|
50
|
+
|
51
|
+
# Allows providing a different coupon code handler.
|
52
|
+
# @!attribute [rw] coupon_code_handler_class
|
53
|
+
# @see Spree::PromotionHandler::Coupon
|
54
|
+
# @return [Class] an object that conforms to the API of
|
55
|
+
# the standard coupon code handler class
|
56
|
+
# Spree::PromotionHandler::Coupon.
|
57
|
+
class_name_attribute :coupon_code_handler_class, default: 'Spree::PromotionHandler::Coupon'
|
58
|
+
|
59
|
+
# Allows providing a different promotion advertiser.
|
60
|
+
# @!attribute [rw] advertiser_class
|
61
|
+
# @see Spree::PromotionAdvertiser
|
62
|
+
# @return [Class] an object that conforms to the API of
|
63
|
+
# the standard promotion advertiser class
|
64
|
+
# Spree::PromotionAdvertiser.
|
65
|
+
class_name_attribute :advertiser_class, default: 'Spree::PromotionAdvertiser'
|
66
|
+
|
67
|
+
add_class_set :rules, default: %w[
|
68
|
+
Spree::Promotion::Rules::ItemTotal
|
69
|
+
Spree::Promotion::Rules::Product
|
70
|
+
Spree::Promotion::Rules::User
|
71
|
+
Spree::Promotion::Rules::FirstOrder
|
72
|
+
Spree::Promotion::Rules::UserLoggedIn
|
73
|
+
Spree::Promotion::Rules::OneUsePerUser
|
74
|
+
Spree::Promotion::Rules::Taxon
|
75
|
+
Spree::Promotion::Rules::MinimumQuantity
|
76
|
+
Spree::Promotion::Rules::NthOrder
|
77
|
+
Spree::Promotion::Rules::OptionValue
|
78
|
+
Spree::Promotion::Rules::FirstRepeatPurchaseSince
|
79
|
+
Spree::Promotion::Rules::UserRole
|
80
|
+
Spree::Promotion::Rules::Store
|
81
|
+
]
|
82
|
+
|
83
|
+
add_class_set :actions, default: %w[
|
84
|
+
Spree::Promotion::Actions::CreateAdjustment
|
85
|
+
Spree::Promotion::Actions::CreateItemAdjustments
|
86
|
+
Spree::Promotion::Actions::CreateQuantityAdjustments
|
87
|
+
Spree::Promotion::Actions::FreeShipping
|
88
|
+
]
|
89
|
+
|
90
|
+
add_class_set :shipping_actions, default: %w[
|
91
|
+
Spree::Promotion::Actions::FreeShipping
|
92
|
+
]
|
93
|
+
|
94
|
+
add_nested_class_set :calculators, default: {
|
95
|
+
"Spree::Promotion::Actions::CreateAdjustment" => %w[
|
96
|
+
Spree::Calculator::FlatPercentItemTotal
|
97
|
+
Spree::Calculator::FlatRate
|
98
|
+
Spree::Calculator::FlexiRate
|
99
|
+
Spree::Calculator::TieredPercent
|
100
|
+
Spree::Calculator::TieredFlatRate
|
101
|
+
],
|
102
|
+
"Spree::Promotion::Actions::CreateItemAdjustments" => %w[
|
103
|
+
Spree::Calculator::DistributedAmount
|
104
|
+
Spree::Calculator::FlatRate
|
105
|
+
Spree::Calculator::FlexiRate
|
106
|
+
Spree::Calculator::PercentOnLineItem
|
107
|
+
Spree::Calculator::TieredPercent
|
108
|
+
],
|
109
|
+
"Spree::Promotion::Actions::CreateQuantityAdjustments" => %w[
|
110
|
+
Spree::Calculator::PercentOnLineItem
|
111
|
+
Spree::Calculator::FlatRate
|
112
|
+
]
|
113
|
+
}
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'solidus_legacy_promotions'
|
4
|
+
|
5
|
+
module SolidusLegacyPromotions
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
include SolidusSupport::EngineExtensions
|
8
|
+
|
9
|
+
initializer "solidus_legacy_promotions.add_backend_menu_item" do
|
10
|
+
if SolidusSupport.backend_available?
|
11
|
+
promotions_menu_item = Spree::BackendConfiguration::MenuItem.new(
|
12
|
+
label: :promotions,
|
13
|
+
icon: Spree::Backend::Config.admin_updated_navbar ? "ri-megaphone-line" : "bullhorn",
|
14
|
+
partial: "spree/admin/shared/promotion_sub_menu",
|
15
|
+
condition: -> { can?(:admin, Spree::Promotion) },
|
16
|
+
url: :admin_promotions_path,
|
17
|
+
data_hook: :admin_promotion_sub_tabs,
|
18
|
+
children: [
|
19
|
+
Spree::BackendConfiguration::MenuItem.new(
|
20
|
+
label: :promotions,
|
21
|
+
condition: -> { can?(:admin, Spree::Promotion) }
|
22
|
+
),
|
23
|
+
Spree::BackendConfiguration::MenuItem.new(
|
24
|
+
label: :promotion_categories,
|
25
|
+
condition: -> { can?(:admin, Spree::PromotionCategory) }
|
26
|
+
)
|
27
|
+
]
|
28
|
+
)
|
29
|
+
product_menu_item_index = Spree::Backend::Config.menu_items.find_index { |item| item.label == :products }
|
30
|
+
Spree::Backend::Config.menu_items.insert(product_menu_item_index + 1, promotions_menu_item)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
initializer "solidus_legacy_promotions.add_admin_order_index_component" do
|
35
|
+
if SolidusSupport.admin_available?
|
36
|
+
SolidusAdmin::Config.components["orders/index"] = "SolidusLegacyPromotions::Orders::Index::Component"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
initializer "solidus_legacy_promotions.add_solidus_admin_menu_items" do
|
41
|
+
if SolidusSupport.admin_available?
|
42
|
+
SolidusAdmin::Config.configure do |config|
|
43
|
+
config.menu_items << {
|
44
|
+
key: "promotions",
|
45
|
+
route: -> { spree.admin_promotions_path },
|
46
|
+
icon: "megaphone-line",
|
47
|
+
position: 30
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
initializer "solidus_legacy_promotions.add_order_search_field" do
|
54
|
+
if SolidusSupport.backend_available?
|
55
|
+
email_field_index = Spree::Backend::Config.search_fields["spree/admin/orders"].find_index do |field|
|
56
|
+
field.dig(:locals, :ransack) == :email_start
|
57
|
+
end
|
58
|
+
Spree::Backend::Config.search_fields["spree/admin/orders"].insert(email_field_index + 1, {
|
59
|
+
partial: "spree/admin/shared/search_fields/text_field",
|
60
|
+
locals: {
|
61
|
+
ransack: :order_promotions_promotion_code_value_start,
|
62
|
+
label: -> { I18n.t(:promotion, scope: :spree) }
|
63
|
+
}
|
64
|
+
})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
initializer 'solidus_legacy_promotions.core.pub_sub', after: 'spree.core.pub_sub' do |app|
|
69
|
+
app.reloader.to_prepare do
|
70
|
+
Spree::OrderPromotionSubscriber.new.subscribe_to(Spree::Bus)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
initializer "solidus_legacy_promotions.assets" do |app|
|
75
|
+
app.config.assets.precompile << "solidus_legacy_promotions/manifest.js"
|
76
|
+
end
|
77
|
+
|
78
|
+
initializer "solidus_legacy_promotions.add_factories_to_core" do
|
79
|
+
if Rails.env.test?
|
80
|
+
require "spree/testing_support/factory_bot"
|
81
|
+
require "solidus_legacy_promotions/testing_support/factory_bot"
|
82
|
+
Spree::TestingSupport::FactoryBot.definition_file_paths.concat(SolidusLegacyPromotions::TestingSupport::FactoryBot.definition_file_paths)
|
83
|
+
end
|
84
|
+
rescue LoadError
|
85
|
+
# FactoryBot is not available, we don't need factories
|
86
|
+
end
|
87
|
+
|
88
|
+
initializer "solidus_legacy_promotions", after: "spree.load_config_initializers" do
|
89
|
+
Spree::Config.order_contents_class = "Spree::OrderContents"
|
90
|
+
Spree::Config.promotions = SolidusLegacyPromotions::Configuration.new
|
91
|
+
Spree::Config.adjustment_promotion_source_types << "Spree::PromotionAction"
|
92
|
+
|
93
|
+
Spree::Api::Config.adjustment_attributes << :promotion_code_id
|
94
|
+
Spree::Api::Config.adjustment_attributes << :eligible
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spree/core'
|
4
|
+
|
5
|
+
module SolidusLegacyPromotions
|
6
|
+
module Migrations
|
7
|
+
module PromotionWithCodeHandlers
|
8
|
+
class PromotionCode < ::Spree::Base
|
9
|
+
self.table_name = "spree_promotion_codes"
|
10
|
+
end
|
11
|
+
|
12
|
+
class Base
|
13
|
+
attr_reader :migration_context, :promotions
|
14
|
+
|
15
|
+
def initialize(migration_context, promotions)
|
16
|
+
@migration_context = migration_context
|
17
|
+
@promotions = promotions
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class RaiseException < Base
|
22
|
+
def call
|
23
|
+
# Please note that this will block the current migration and rollback all
|
24
|
+
# the previous ones run with the same "rails db:migrate" command.
|
25
|
+
#
|
26
|
+
raise StandardError, "You are trying to drop 'code' column from "\
|
27
|
+
"spree_promotions table but you have at least one record with that "\
|
28
|
+
"column filled. Please take care of that or you could lose data. See:" \
|
29
|
+
"\n" \
|
30
|
+
"https://github.com/solidusio/solidus/pull/3028"\
|
31
|
+
"\n"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class MoveToSpreePromotionCode < Base
|
36
|
+
def call
|
37
|
+
# This is another possible approach, it will convert Spree::Promotion#code
|
38
|
+
# to a Spree::PromotionCode before removing the `code` field.
|
39
|
+
#
|
40
|
+
# NOTE: promotion codes will be downcased and stripped
|
41
|
+
promotions.find_each do |promotion|
|
42
|
+
normalized_code = promotion.code.downcase.strip
|
43
|
+
|
44
|
+
PromotionCode.find_or_create_by!(
|
45
|
+
value: normalized_code,
|
46
|
+
promotion_id: promotion.id
|
47
|
+
) do
|
48
|
+
migration_context.say "Creating Spree::PromotionCode with value "\
|
49
|
+
"'#{normalized_code}' for Spree::Promotion with id '#{promotion.id}'"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class DoNothing < Base
|
56
|
+
def call
|
57
|
+
# This approach will delete all codes without taking any action. At
|
58
|
+
# least we could print a message to track what we are deleting.
|
59
|
+
#
|
60
|
+
promotions.find_each do |promotion|
|
61
|
+
migration_context.say "Code '#{promotion.code}' is going to be removed "\
|
62
|
+
"from Spree::Promotion with id '#{promotion.id}'"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
FactoryBot.define do
|
4
|
+
factory :completed_order_with_promotion, parent: :order_with_line_items do
|
5
|
+
transient do
|
6
|
+
completed_at { Time.current }
|
7
|
+
promotion { nil }
|
8
|
+
end
|
9
|
+
|
10
|
+
after(:create) do |order, evaluator|
|
11
|
+
promotion = evaluator.promotion || create(:promotion, code: "test")
|
12
|
+
promotion_code = promotion.codes.first || create(:promotion_code, promotion:)
|
13
|
+
|
14
|
+
promotion.activate(order:, promotion_code:)
|
15
|
+
order.order_promotions.create!(promotion:, promotion_code:)
|
16
|
+
|
17
|
+
# Complete the order after the promotion has been activated
|
18
|
+
order.update_column(:completed_at, evaluator.completed_at)
|
19
|
+
order.update_column(:state, "complete")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|