Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/models/solidus_subscriptions/line_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
3 changes: 1 addition & 2 deletions app/models/solidus_subscriptions/subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
2 changes: 2 additions & 0 deletions db/migrate/20210323165714_update_promotion_rule_names.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
@@ -1,39 +1,52 @@
<% if @product.subscribable %>
<%= content_tag :h3, t('.subscription_fields') %>
<%= fields_for :'subscription_line_item', SolidusSubscriptions::LineItem.new do |ff| %>
<div>
<%= ff.label :quantity, t('.quantity') %>
<%= ff.number_field :quantity %>
<%= ff.label :quantity, t('.quantity_suffix') %>
</div>

<div>
<%= 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 %>
</div>

<%= ff.hidden_field :subscribable_id %>
<div class="subscription-form">
<% if @product.subscribable %>
<%= content_tag :h3, t('.subscription_fields') %>
<%= fields_for :'subscription_line_item', SolidusSubscriptions::LineItem.new do |ff| %>
<div>
<%= ff.label :quantity, t('.quantity') %>
<%= ff.number_field :quantity %>
<%= ff.label :quantity, t('.quantity_suffix') %>
</div>

<div>
<%= 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 %>
</div>

<%= ff.hidden_field :subscribable_id %> <!-- Hidden field for subscribable_id -->
<% end %>
<% end %>
<% end %>
</div>

<script>
document.addEventListener("DOMContentLoaded", function(e) {
document.addEventListener("DOMContentLoaded", function() {
var cartForm = document.querySelector('form[action="/cart_line_items"]');

cartForm.addEventListener('submit', function(e) {
var variantInput = e.target.querySelector('[name*="variant_id"]:checked');
var subscribableInput = e.target.querySelector('[name*="subscription_line_item[subscribable_id]"]');

if (!variantInput) {
variantInput = cartForm.querySelector('[name="variant_id"]');
var variantRadioButtons = cartForm.querySelectorAll('[data-js="variant-radio"]');
var subscribableInput = cartForm.querySelector('[name*="subscription_line_item[subscribable_id]"]');
var subscriptionFields = document.querySelector('.subscription-form'); // Select the subscription form container

function handleVariantSelection(e) {
var variantRadioButton = e.target;
var isSubscribable = variantRadioButton.getAttribute('data-subscribable') === 'true';

if (isSubscribable) {
subscribableInput.value = variantRadioButton.value;
subscriptionFields.style.display = 'block';
} else {
subscriptionFields.style.display = 'none';
}
}

subscribableInput.value = variantInput.value;

return true;
variantRadioButtons.forEach(function(variantRadioButton) {
variantRadioButton.addEventListener('change', handleVariantSelection);
});

var selectedVariantRadioButton = cartForm.querySelector('[data-js="variant-radio"]:checked');
if (selectedVariantRadioButton) {
handleVariantSelection({ target: selectedVariantRadioButton });
}
});
</script>

8 changes: 5 additions & 3 deletions lib/solidus_subscriptions/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion solidus_subscriptions.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
91 changes: 91 additions & 0 deletions spec/controllers/concerns/create_subscription_spec.rb
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions spec/controllers/spree/api/line_items_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/spree/api/orders_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
22 changes: 22 additions & 0 deletions spec/models/solidus_subscriptions/line_item_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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