diff --git a/app/models/solidus_subscriptions/line_item.rb b/app/models/solidus_subscriptions/line_item.rb index 2583ecc5..efd9d662 100644 --- a/app/models/solidus_subscriptions/line_item.rb +++ b/app/models/solidus_subscriptions/line_item.rb @@ -38,5 +38,12 @@ class LineItem < ApplicationRecord validates :subscribable_id, presence: true validates :quantity, numericality: { greater_than: 0 } validates :interval_length, numericality: { greater_than: 0 }, unless: -> { subscription } + validate :ensure_subscribable_valid + + def ensure_subscribable_valid + return unless subscribable && subscribable.subscribable != true + + errors.add(:subscribable, :cannot_subscribe) + end end end diff --git a/app/models/solidus_subscriptions/subscription.rb b/app/models/solidus_subscriptions/subscription.rb index d261d6e4..62a4db75 100644 --- a/app/models/solidus_subscriptions/subscription.rb +++ b/app/models/solidus_subscriptions/subscription.rb @@ -434,12 +434,11 @@ def emit_events_for_update end def self.ransackable_attributes(_auth_object = nil) - %w[actionable_date created_at end_date state updated_at user_id] + %w[actionable_date created_at end_date state updated_at user_id] end def self.ransackable_associations(_auth_object = nil) %w[events user] end - end end diff --git a/config/locales/en.yml b/config/locales/en.yml index c1c1fce3..f58c203c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -136,3 +136,7 @@ en: not_active: "cannot pause/resume a subscription which is not active" state: cannot_skip: cannot skip a subscription which is canceled or inactive + solidus_subscriptions/line_item: + attributes: + subscribable: + cannot_subscribe: "The requested item cannot be subscribed" diff --git a/db/migrate/20210323165714_update_promotion_rule_names.rb b/db/migrate/20210323165714_update_promotion_rule_names.rb index f6ee6c23..1adfe1b6 100644 --- a/db/migrate/20210323165714_update_promotion_rule_names.rb +++ b/db/migrate/20210323165714_update_promotion_rule_names.rb @@ -5,6 +5,8 @@ class UpdatePromotionRuleNames < ActiveRecord::Migration[5.2] }.freeze def change + return unless Object.const_defined?("Spree::Promotion") + reversible do |dir| dir.up do TYPE_RENAMES.each do |old_type, new_type| diff --git a/lib/generators/solidus_subscriptions/install/install_generator.rb b/lib/generators/solidus_subscriptions/install/install_generator.rb index e9805d87..2081deab 100644 --- a/lib/generators/solidus_subscriptions/install/install_generator.rb +++ b/lib/generators/solidus_subscriptions/install/install_generator.rb @@ -30,6 +30,10 @@ def copy_starter_frontend_files RUBY end + + inject_into_file 'app/views/cart_line_items/_product_variants.html.erb', + " \"data-subscribable\" => variant.subscribable,\n", + before: " \"data-price\" => variant.price_for_options(current_pricing_options)&.money&.to_html\n" end def add_migrations diff --git a/lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription.rb b/lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription.rb index ed9cf2fb..f0f013b8 100644 --- a/lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription.rb +++ b/lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription.rb @@ -5,7 +5,7 @@ module CreateSubscription include SolidusSubscriptions::SubscriptionLineItemBuilder included do - after_action :handle_subscription_line_items, only: :create, if: ->{ params[:subscription_line_item] } + after_action :handle_subscription_line_items, only: :create, if: :valid_subscription_line_item_params? end private @@ -14,4 +14,9 @@ def handle_subscription_line_items line_item = @current_order.line_items.find_by(variant_id: params[:variant_id]) create_subscription_line_item(line_item) end + + def valid_subscription_line_item_params? + subscription_params = params[:subscription_line_item] + %i[subscribable_id quantity interval_length].all? { |key| subscription_params[key].present? } + end end diff --git a/lib/generators/solidus_subscriptions/install/templates/app/views/cart_line_items/_subscription_fields.html.erb b/lib/generators/solidus_subscriptions/install/templates/app/views/cart_line_items/_subscription_fields.html.erb index dfda14f1..2fffa2f2 100644 --- a/lib/generators/solidus_subscriptions/install/templates/app/views/cart_line_items/_subscription_fields.html.erb +++ b/lib/generators/solidus_subscriptions/install/templates/app/views/cart_line_items/_subscription_fields.html.erb @@ -1,39 +1,52 @@ -<% if @product.subscribable %> - <%= content_tag :h3, t('.subscription_fields') %> - <%= fields_for :'subscription_line_item', SolidusSubscriptions::LineItem.new do |ff| %> -
- <%= ff.label :quantity, t('.quantity') %> - <%= ff.number_field :quantity %> - <%= ff.label :quantity, t('.quantity_suffix') %> -
- -
- <%= ff.label :interval_length, t('.interval_length') %> - <%= ff.number_field :interval_length %> - - <%= ff.collection_radio_buttons :interval_units, SolidusSubscriptions::LineItem.interval_units.to_a, :first, :first %> -
- - <%= ff.hidden_field :subscribable_id %> +
+ <% if @product.subscribable %> + <%= content_tag :h3, t('.subscription_fields') %> + <%= fields_for :'subscription_line_item', SolidusSubscriptions::LineItem.new do |ff| %> +
+ <%= ff.label :quantity, t('.quantity') %> + <%= ff.number_field :quantity %> + <%= ff.label :quantity, t('.quantity_suffix') %> +
+ +
+ <%= ff.label :interval_length, t('.interval_length') %> + <%= ff.number_field :interval_length %> + + <%= ff.collection_radio_buttons :interval_units, SolidusSubscriptions::LineItem.interval_units.to_a, :first, :first %> +
+ + <%= ff.hidden_field :subscribable_id %> + <% end %> <% end %> -<% end %> +
diff --git a/lib/solidus_subscriptions/engine.rb b/lib/solidus_subscriptions/engine.rb index 682ebccc..f8fd7c29 100644 --- a/lib/solidus_subscriptions/engine.rb +++ b/lib/solidus_subscriptions/engine.rb @@ -31,9 +31,11 @@ class Engine < Rails::Engine } end - initializer 'solidus_subscriptions.register_promotion_rules', after: 'spree.promo.register.promotion.rules' do |app| - app.config.spree.promotions.rules << 'SolidusSubscriptions::Promotion::Rules::SubscriptionCreationOrder' - app.config.spree.promotions.rules << 'SolidusSubscriptions::Promotion::Rules::SubscriptionInstallmentOrder' + if Object.const_defined?("Spree::Promotion") + initializer 'solidus_subscriptions.register_promotion_rules', after: 'spree.promo.register.promotion.rules' do |app| + app.config.spree.promotions.rules << 'SolidusSubscriptions::Promotion::Rules::SubscriptionCreationOrder' + app.config.spree.promotions.rules << 'SolidusSubscriptions::Promotion::Rules::SubscriptionInstallmentOrder' + end end initializer 'solidus_subscriptions.configure_backend' do diff --git a/solidus_subscriptions.gemspec b/solidus_subscriptions.gemspec index 47e3f9cd..9659c349 100644 --- a/solidus_subscriptions.gemspec +++ b/solidus_subscriptions.gemspec @@ -33,7 +33,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'httparty', '~> 0.18' spec.add_dependency 'i18n' spec.add_dependency 'solidus_core', '>= 2.11', '< 5' - spec.add_dependency 'solidus_support', '~> 0.9' + spec.add_dependency 'solidus_support', '~> 0.11' spec.add_dependency 'state_machines' spec.add_development_dependency 'rspec-activemodel-mocks' diff --git a/spec/controllers/concerns/create_subscription_spec.rb b/spec/controllers/concerns/create_subscription_spec.rb new file mode 100644 index 00000000..59338b83 --- /dev/null +++ b/spec/controllers/concerns/create_subscription_spec.rb @@ -0,0 +1,91 @@ +require 'spec_helper' +require_relative '../../../lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription' + +RSpec.describe CreateSubscription, type: :controller do + subject(:controller_instance) do + Class.new(ApplicationController) do + include CreateSubscription + attr_accessor :params, :current_order + + def initialize(params = {}) + @params = params + @current_order = nil + end + end.new + end + + let(:variant) { create(:variant) } + let(:order) { create(:order) } + + before do + controller_instance.current_order = order + end + + describe '#subscription_line_item_params_present?' do + context 'when all required params are present' do + it 'returns true' do + controller_instance.params = { + subscription_line_item: { + subscribable_id: 1, + quantity: 2, + interval_length: 1 + } + } + expect(controller_instance.send(:valid_subscription_line_item_params?)).to be true + end + end + + context 'when required params are missing' do + it 'returns false' do + controller_instance.params = { + subscription_line_item: { + subscribable_id: '', + quantity: '', + interval_length: '' + } + } + expect(controller_instance.send(:valid_subscription_line_item_params?)).to be false + end + end + end + + describe '#handle_subscription_line_items' do + context 'when subscription params are missing' do + it 'does not invoke handle_subscription_line_items and does not create a subscription line item' do + order.line_items.count + + controller_instance.params = { + variant_id: variant.id, + subscription_line_item: {} + } + + expect(controller_instance.send(:valid_subscription_line_item_params?)).to be false + + expect(controller_instance).not_to receive(:handle_subscription_line_items) + + expect(controller_instance).not_to receive(:create_subscription_line_item) + end + end + + context 'when subscription params are present' do + it 'calls create_subscription_line_item with the correct line item' do + line_item = create(:line_item, order: order, variant: variant) + + controller_instance.params = { + variant_id: variant.id, + subscription_line_item: { + subscribable_id: 1, + quantity: 2, + interval_length: 1 + } + } + + allow(order.line_items).to receive(:find_by).with(variant_id: variant.id).and_return(line_item) + + expect(controller_instance).to receive(:create_subscription_line_item).with(line_item) + + controller_instance.send(:handle_subscription_line_items) + end + end + end +end diff --git a/spec/controllers/spree/api/line_items_controller_spec.rb b/spec/controllers/spree/api/line_items_controller_spec.rb index 478f9fdc..3293fc9a 100644 --- a/spec/controllers/spree/api/line_items_controller_spec.rb +++ b/spec/controllers/spree/api/line_items_controller_spec.rb @@ -11,7 +11,7 @@ subject(:post_create) { post :create, params: params } let(:params) { line_item_params } - let!(:variant) { create :variant } + let!(:variant) { create :variant, subscribable: true } let!(:order) { create :order } let(:line_item_params) do @@ -70,7 +70,7 @@ let(:params) { line_item_params } context 'when adding subscription information' do - let(:variant) { create :variant } + let(:variant) { create :variant, subscribable: true } let(:order) { create :order } let(:line_item) { create :line_item, order: order, variant: variant } let(:line_item_params) do diff --git a/spec/controllers/spree/api/orders_controller_spec.rb b/spec/controllers/spree/api/orders_controller_spec.rb index 5f32a569..81a0f234 100644 --- a/spec/controllers/spree/api/orders_controller_spec.rb +++ b/spec/controllers/spree/api/orders_controller_spec.rb @@ -8,7 +8,7 @@ routes { Spree::Core::Engine.routes } let(:order) { create :order } - let(:variant) { create :variant } + let(:variant) { create :variant, subscribable: true } describe 'patch /update' do subject(:subscription_line_items) do diff --git a/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb b/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb index 90bc9d17..67543923 100644 --- a/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb +++ b/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb @@ -16,7 +16,7 @@ describe 'POST /orders/populate' do subject(:populate) { post :populate, params: params } - let!(:variant) { create :variant } + let!(:variant) { create :variant, subscribable: true } let(:params) { line_item_params } let(:line_item_params) do { diff --git a/spec/jobs/solidus_subscriptions/process_subscription_job_spec.rb b/spec/jobs/solidus_subscriptions/process_subscription_job_spec.rb index 1a3cf6fe..511c34af 100644 --- a/spec/jobs/solidus_subscriptions/process_subscription_job_spec.rb +++ b/spec/jobs/solidus_subscriptions/process_subscription_job_spec.rb @@ -5,7 +5,7 @@ it 'voids the actionable date of the unfulfilled installments' do stub_config(clear_past_installments: true) subscription = create(:subscription) - unfulfilled_installment = create(:installment, :failed, subscription: subscription) + unfulfilled_installment = create(:installment, :failed, subscription: subscription) described_class.perform_now(subscription) diff --git a/spec/models/solidus_subscriptions/line_item_spec.rb b/spec/models/solidus_subscriptions/line_item_spec.rb index d7c1bdba..de9bc3c2 100644 --- a/spec/models/solidus_subscriptions/line_item_spec.rb +++ b/spec/models/solidus_subscriptions/line_item_spec.rb @@ -26,4 +26,26 @@ expect(interval.from_now).to eq Date.parse("2016-10-22") end end + + describe "custom validation" do + context "when subscribable is not true" do + let(:subscribable) { create(:variant, subscribable: false) } + let(:line_item) { build(:subscription_line_item, subscribable: subscribable) } + + it "adds an error to subscribable" do + line_item.valid? + expect(line_item.errors[:subscribable]).to include("The requested item cannot be subscribed") + end + end + + context "when subscribable is true" do + let(:subscribable) { create(:variant, subscribable: true) } + let(:line_item) { build(:subscription_line_item, subscribable: subscribable) } + + it "does not add an error to subscribable" do + line_item.valid? + expect(line_item.errors[:subscribable]).to be_empty + end + end + end end