diff --git a/README.md b/README.md index 0252e6d1..477e4838 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ Try Spree Product Subscriptions for Spree master with direct deployment on Herok [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/vinsol-spree-contrib/spree-demo-heroku/tree/spree-product-subscriptions-master) +Try Spree Product Subscriptions for Spree 4 with direct deployment on Heroku: + +[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/vinsol-spree-contrib/spree-demo-heroku/tree/spree-product-subscriptions-4-1) + Try Spree Product Subscriptions for Spree 3-4 with direct deployment on Heroku: [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/vinsol-spree-contrib/spree-demo-heroku/tree/spree-product-subscriptions-3-4) diff --git a/app/assets/javascripts/spree/backend/subscribable.js b/app/assets/javascripts/spree/backend/subscribable.js index 863978d3..92a386fb 100644 --- a/app/assets/javascripts/spree/backend/subscribable.js +++ b/app/assets/javascripts/spree/backend/subscribable.js @@ -23,12 +23,12 @@ Subscribable.prototype.bindParentCheckboxEvent = function() { }; Subscribable.prototype.enableChildCheckboxes = function() { - this.$childCheckboxesDiv.removeClass("hidden"); + this.$childCheckboxesDiv.removeClass("is-hidden"); this.$childCheckboxesDiv.find("input").removeAttr("disabled"); }; Subscribable.prototype.disableChildCheckboxes = function() { - this.$childCheckboxesDiv.addClass("hidden"); + this.$childCheckboxesDiv.addClass("is-hidden"); this.$childCheckboxesDiv.find("input").attr("disabled", "disabled"); } diff --git a/app/assets/javascripts/spree/frontend/cart_form_subscription_button.js b/app/assets/javascripts/spree/frontend/cart_form_subscription_button.js new file mode 100644 index 00000000..10effb34 --- /dev/null +++ b/app/assets/javascripts/spree/frontend/cart_form_subscription_button.js @@ -0,0 +1,14 @@ +Spree.ready(function($) { + Spree.addToCartFormSubmissionOptions = function() { + $cartForm = $(ADD_TO_CART_FORM_SELECTOR); + if($cartForm.find('.cart_radio_button:checked').val() == ".subscription_options") { + options = { + subscribe: true, + subscription_frequency_id: $cartForm.find('#subscription_subscription_frequency_id option:selected').val(), + delivery_number: parseInt($cartForm.find('#subscription_delivery_number').val()) + }; + return options; + } + return {}; + } +}) \ No newline at end of file diff --git a/app/assets/javascripts/spree/frontend/cart_radio_button.js b/app/assets/javascripts/spree/frontend/cart_radio_button.js index 1f699b32..86ab7082 100644 --- a/app/assets/javascripts/spree/frontend/cart_radio_button.js +++ b/app/assets/javascripts/spree/frontend/cart_radio_button.js @@ -14,17 +14,14 @@ CartRadioButton.prototype.bindEvents = function() { }; CartRadioButton.prototype.toggleDiv = function($checkBox) { - $($checkBox.val()).show(); - this.hideOtherDivs(); -}; - -CartRadioButton.prototype.hideOtherDivs = function() { - $.each(this.$radioButtons, function(index, value) { - $checkBox = $(value); - if (!$checkBox.prop("checked")) { - $($checkBox.val()).hide(); - } - }); + let value = $checkBox.val(); + $(value).show(); + if (value.includes('subscription_options')) { + $('#add-to-cart-button').text('SUBSCRIBE NOW'); + } else { + $('#add-to-cart-button').text('ADD TO CART'); + $(value).siblings('.subscription_options').hide(); + } }; $(function() { diff --git a/app/assets/javascripts/spree/frontend/spree_product_subscriptions.js b/app/assets/javascripts/spree/frontend/spree_product_subscriptions.js index 4aea1b53..2bc93b92 100644 --- a/app/assets/javascripts/spree/frontend/spree_product_subscriptions.js +++ b/app/assets/javascripts/spree/frontend/spree_product_subscriptions.js @@ -1,6 +1,7 @@ // Placeholder manifest file. //= require spree/frontend/cart_radio_button.js //= require spree/frontend/datepicker +//= require spree/frontend/cart_form_subscription_button //= require spree/frontend/ajax_handler.js //= require spree/frontend/update_quantity_in_cart // the installer will append this file to the app vendored assets here: vendor/assets/javascripts/spree/frontend/all.js' diff --git a/app/controllers/spree/api/v1/line_items_controller_decorator.rb b/app/controllers/spree/api/v1/line_items_controller_decorator.rb deleted file mode 100644 index 180c4c63..00000000 --- a/app/controllers/spree/api/v1/line_items_controller_decorator.rb +++ /dev/null @@ -1,5 +0,0 @@ -Spree::Api::V1::LineItemsController.class_eval do - - self.line_item_options += [:subscribe, :delivery_number, :subscription_frequency_id] - -end diff --git a/app/controllers/spree/base_controller_decorator.rb b/app/controllers/spree/base_controller_decorator.rb deleted file mode 100644 index 8cea204b..00000000 --- a/app/controllers/spree/base_controller_decorator.rb +++ /dev/null @@ -1,3 +0,0 @@ -Spree::BaseController.class_eval do - add_flash_types :success, :error -end diff --git a/app/controllers/spree/orders_controller_decorator.rb b/app/controllers/spree/orders_controller_decorator.rb deleted file mode 100644 index cb77923e..00000000 --- a/app/controllers/spree/orders_controller_decorator.rb +++ /dev/null @@ -1,22 +0,0 @@ -Spree::OrdersController.class_eval do - - before_action :add_subscription_fields, only: :populate, if: -> { params[:subscribe].present? } - before_action :restrict_guest_subscription, only: :update, unless: :spree_current_user - - private - - def restrict_guest_subscription - redirect_to login_path, error: Spree.t(:required_authentication) if @order.subscriptions.present? - end - - def add_subscription_fields - is_subscribed = params.fetch(:subscribe, "").present? - - existing_options = {options: params.fetch(:options, {}).permit!} - updated_subscription_params = params.fetch(:subscription, {}).merge({subscribe: is_subscribed}).permit! - existing_options[:options].merge!(updated_subscription_params) - updated_params = params.merge!(existing_options) - updated_params - end - -end diff --git a/app/controllers/spree/users_controller_decorator.rb b/app/controllers/spree/users_controller_decorator.rb deleted file mode 100644 index 195ec98e..00000000 --- a/app/controllers/spree/users_controller_decorator.rb +++ /dev/null @@ -1,12 +0,0 @@ -Spree::UsersController.class_eval do - - before_action :load_subscriptions, only: :show - - private - - def load_subscriptions - @orders = @user.orders.complete.order(completed_at: :desc) - @subscriptions = Spree::Subscription.active.order(created_at: :desc).with_parent_orders(@orders) - end - -end diff --git a/app/controllers/spree_product_subscriptions/base_controller_decorator.rb b/app/controllers/spree_product_subscriptions/base_controller_decorator.rb new file mode 100644 index 00000000..fce890ae --- /dev/null +++ b/app/controllers/spree_product_subscriptions/base_controller_decorator.rb @@ -0,0 +1,11 @@ +module SpreeProductSubscriptions + module BaseControllerDecorator + + def self.prepended(base) + base.add_flash_types :success, :error + end + + end +end + +Spree::BaseController.prepend SpreeProductSubscriptions::BaseControllerDecorator diff --git a/app/controllers/spree_product_subscriptions/line_items_controller_decorator.rb b/app/controllers/spree_product_subscriptions/line_items_controller_decorator.rb new file mode 100644 index 00000000..24f06f6b --- /dev/null +++ b/app/controllers/spree_product_subscriptions/line_items_controller_decorator.rb @@ -0,0 +1,10 @@ +module SpreeProductSubscriptions::LineItemsControllerDecorator + + def self.prepended(base) + base.line_item_options += [:subscribe, :delivery_number, :subscription_frequency_id] + end + + +end + +Spree::Api::V1::LineItemsController.prepend SpreeProductSubscriptions::LineItemsControllerDecorator \ No newline at end of file diff --git a/app/controllers/spree_product_subscriptions/orders_controller_decorator.rb b/app/controllers/spree_product_subscriptions/orders_controller_decorator.rb new file mode 100644 index 00000000..b3105030 --- /dev/null +++ b/app/controllers/spree_product_subscriptions/orders_controller_decorator.rb @@ -0,0 +1,14 @@ +module SpreeProductSubscriptions::OrdersControllerDecorator + + def self.prepended(base) + base.before_action :restrict_guest_subscription, only: :update, unless: :spree_current_user + end + + private + + def restrict_guest_subscription + redirect_to login_path, error: Spree.t(:required_authentication) if @order.subscriptions.present? + end +end + +Spree::OrdersController.prepend SpreeProductSubscriptions::OrdersControllerDecorator diff --git a/app/controllers/spree/admin/payments_controller_decorator.rb b/app/controllers/spree_product_subscriptions/payments_controller_decorator.rb similarity index 70% rename from app/controllers/spree/admin/payments_controller_decorator.rb rename to app/controllers/spree_product_subscriptions/payments_controller_decorator.rb index 8fbe173f..a10d4798 100644 --- a/app/controllers/spree/admin/payments_controller_decorator.rb +++ b/app/controllers/spree_product_subscriptions/payments_controller_decorator.rb @@ -1,4 +1,4 @@ -Spree::Admin::PaymentsController.class_eval do +module SpreeProductSubscriptions::PaymentsControllerDecorator private @@ -13,3 +13,5 @@ def available_payment_methods end end + +Spree::Admin::PaymentsController.prepend SpreeProductSubscriptions::PaymentsControllerDecorator diff --git a/app/controllers/spree_product_subscriptions/users_controller_decorator.rb b/app/controllers/spree_product_subscriptions/users_controller_decorator.rb new file mode 100644 index 00000000..947e1d76 --- /dev/null +++ b/app/controllers/spree_product_subscriptions/users_controller_decorator.rb @@ -0,0 +1,16 @@ +module SpreeProductSubscriptions::UsersControllerDecorator + + def self.prepended(base) + base.before_action :load_subscriptions, only: :show + end + + private + + def load_subscriptions + @orders = @user.orders.complete.order(completed_at: :desc) + @subscriptions = Spree::Subscription.active.order(created_at: :desc).with_parent_orders(@orders) + end + +end + +Spree::UsersController.prepend SpreeProductSubscriptions::UsersControllerDecorator diff --git a/app/models/spree/order_decorator.rb b/app/models/spree/order_decorator.rb deleted file mode 100644 index 87452c93..00000000 --- a/app/models/spree/order_decorator.rb +++ /dev/null @@ -1,46 +0,0 @@ -Spree::Order.class_eval do - - has_one :order_subscription, class_name: "Spree::OrderSubscription", dependent: :destroy - has_one :parent_subscription, through: :order_subscription, source: :subscription - has_many :subscriptions, class_name: "Spree::Subscription", - foreign_key: :parent_order_id, - dependent: :restrict_with_error - - self.state_machine.after_transition to: :complete, do: :enable_subscriptions, if: :any_disabled_subscription? - - after_update :update_subscriptions - - def available_payment_methods - if subscriptions.exists? - @available_payment_methods = Spree::Gateway.active.available_on_front_end - else - @available_payment_methods ||= Spree::PaymentMethod.active.available_on_front_end - end - end - - private - - def enable_subscriptions - subscriptions.each do |subscription| - subscription.update( - source: payments.from_credit_card.first.source, - enabled: true, - ship_address: ship_address.clone, - bill_address: bill_address.clone - ) - end - end - - def any_disabled_subscription? - subscriptions.disabled.any? - end - - def update_subscriptions - line_items.each do |line_item| - if line_item.subscription_attributes_present? - subscriptions.find_by(variant: line_item.variant).update(line_item.updatable_subscription_attributes) - end - end - end - -end diff --git a/app/models/spree/product_decorator.rb b/app/models/spree/product_decorator.rb deleted file mode 100644 index 89dda1b2..00000000 --- a/app/models/spree/product_decorator.rb +++ /dev/null @@ -1,15 +0,0 @@ -Spree::Product.class_eval do - - has_many :subscriptions, through: :variants_including_master, source: :subscriptions, dependent: :restrict_with_error - has_many :product_subscription_frequencies, class_name: "Spree::ProductSubscriptionFrequency", dependent: :destroy - has_many :subscription_frequencies, through: :product_subscription_frequencies, dependent: :destroy - - alias_attribute :subscribable, :is_subscribable - - self.whitelisted_ransackable_attributes += %w( is_subscribable ) - - scope :subscribable, -> { where(subscribable: true) } - - validates :subscription_frequencies, presence: true, if: :subscribable? - -end diff --git a/app/models/spree/subscription.rb b/app/models/spree/subscription.rb index 68efe1ee..237db7df 100644 --- a/app/models/spree/subscription.rb +++ b/app/models/spree/subscription.rb @@ -95,20 +95,20 @@ def number_of_deliveries_left def pause run_callbacks :pause do - update_attributes(paused: true) + update(paused: true) end end def unpause run_callbacks :unpause do - update_attributes(paused: false) + update(paused: false) end end def cancel self.cancelled = true run_callbacks :cancel do - update_attributes(cancelled_at: Time.current) + update(cancelled_at: Time.current) end end @@ -189,7 +189,12 @@ def make_new_order end def add_variant_to_order(order) - order.contents.add(variant, quantity) + # order.contents.add(variant, quantity) Contents removed from Spree 4 + Spree::Cart::AddItem.call( + order: order, + variant: variant, + quantity: quantity + ) order.next end @@ -236,7 +241,7 @@ def confirm_order(order) def order_attributes { currency: parent_order.currency, - guest_token: parent_order.guest_token, + token: parent_order.token, store: parent_order.store, user: parent_order.user, created_by: parent_order.user, diff --git a/app/models/spree/variant_decorator.rb b/app/models/spree/variant_decorator.rb deleted file mode 100644 index 899149b7..00000000 --- a/app/models/spree/variant_decorator.rb +++ /dev/null @@ -1,7 +0,0 @@ -Spree::Variant.class_eval do - - has_many :subscriptions, class_name: "Spree::Subscription", dependent: :restrict_with_error - delegate :variants_including_master, to: :product, prefix: true - alias_method :product_variants, :product_variants_including_master - -end diff --git a/app/models/spree/line_item_decorator.rb b/app/models/spree_product_subscriptions/line_item_decorator.rb similarity index 70% rename from app/models/spree/line_item_decorator.rb rename to app/models/spree_product_subscriptions/line_item_decorator.rb index a0d24dfa..b478c4f3 100644 --- a/app/models/spree/line_item_decorator.rb +++ b/app/models/spree_product_subscriptions/line_item_decorator.rb @@ -1,11 +1,13 @@ -Spree::LineItem.class_eval do +module SpreeProductSubscriptions::LineItemDecorator - attr_accessor :subscription_frequency_id, :delivery_number, :subscribe - after_create :create_subscription!, if: :subscribable? - after_update :update_subscription_quantity, if: :can_update_subscription_quantity? - after_update :update_subscription_attributes, if: :can_update_subscription_attributes? - after_destroy :destroy_associated_subscription!, if: :subscription? + def self.prepended(base) + base.after_create :create_subscription!, if: :subscribable? + base.after_update :update_subscription_quantity, if: :can_update_subscription_quantity? + base.after_update :update_subscription_attributes, if: :can_update_subscription_attributes? + base.after_destroy :destroy_associated_subscription!, if: :subscription? + base.attr_accessor :subscription_frequency_id, :delivery_number, :subscribe + end def subscription_attributes_present? subscription_frequency_id.present? || delivery_number.present? @@ -67,3 +69,5 @@ def can_update_subscription_quantity? end end + +Spree::LineItem.prepend SpreeProductSubscriptions::LineItemDecorator diff --git a/app/models/spree_product_subscriptions/order_decorator.rb b/app/models/spree_product_subscriptions/order_decorator.rb new file mode 100644 index 00000000..80aa466d --- /dev/null +++ b/app/models/spree_product_subscriptions/order_decorator.rb @@ -0,0 +1,40 @@ +module SpreeProductSubscriptions::OrderDecorator + + def self.prepended(base) + base.has_one :order_subscription, class_name: "Spree::OrderSubscription", dependent: :destroy + base.has_one :parent_subscription, through: :order_subscription, source: :subscription + base.has_many :subscriptions, class_name: "Spree::Subscription", + foreign_key: :parent_order_id, + dependent: :restrict_with_error + base.after_update :update_subscriptions + base.state_machine.after_transition to: :complete, do: :enable_subscriptions, if: :any_disabled_subscription? + base.alias_attribute :guest_token, :token + end + + private + + def enable_subscriptions + subscriptions.each do |subscription| + subscription.update( + source: payments.from_credit_card.first.source, + enabled: true, + ship_address: ship_address.clone, + bill_address: bill_address.clone + ) + end + end + + def any_disabled_subscription? + subscriptions.disabled.any? + end + + def update_subscriptions + line_items.each do |line_item| + if line_item.subscription_attributes_present? + subscriptions.find_by(variant: line_item.variant).update(line_item.updatable_subscription_attributes) + end + end + end +end + +Spree::Order.prepend SpreeProductSubscriptions::OrderDecorator diff --git a/app/models/spree_product_subscriptions/product_decorator.rb b/app/models/spree_product_subscriptions/product_decorator.rb new file mode 100644 index 00000000..2c79b4a8 --- /dev/null +++ b/app/models/spree_product_subscriptions/product_decorator.rb @@ -0,0 +1,15 @@ +module SpreeProductSubscriptions::ProductDecorator + + def self.prepended(base) + base.has_many :subscriptions, through: :variants_including_master, source: :subscriptions, dependent: :restrict_with_error + base.has_many :product_subscription_frequencies, class_name: "Spree::ProductSubscriptionFrequency", dependent: :destroy + base.has_many :subscription_frequencies, through: :product_subscription_frequencies, dependent: :destroy + base.validates :subscription_frequencies, presence: true, if: :subscribable? + base.scope :subscribable, -> { where(subscribable: true) } + base.whitelisted_ransackable_attributes += %w( is_subscribable ) + base.alias_attribute :subscribable, :is_subscribable + end + +end + +Spree::Product.prepend SpreeProductSubscriptions::ProductDecorator diff --git a/app/models/spree_product_subscriptions/variant_decorator.rb b/app/models/spree_product_subscriptions/variant_decorator.rb new file mode 100644 index 00000000..a4d47ae0 --- /dev/null +++ b/app/models/spree_product_subscriptions/variant_decorator.rb @@ -0,0 +1,12 @@ +module SpreeProductSubscriptions::VariantDecorator + + + def self.prepended(base) + base.has_many :subscriptions, class_name: "Spree::Subscription", dependent: :restrict_with_error + base.delegate :variants_including_master, to: :product, prefix: true + base.alias_method :product_variants, :product_variants_including_master + end + +end + +Spree::Variant.prepend SpreeProductSubscriptions::VariantDecorator diff --git a/app/overrides/add_checkboxes_to_product_show_page.rb b/app/overrides/add_checkboxes_to_product_show_page.rb index b9c7bc29..de43b6d3 100644 --- a/app/overrides/add_checkboxes_to_product_show_page.rb +++ b/app/overrides/add_checkboxes_to_product_show_page.rb @@ -1,6 +1,6 @@ Deface::Override.new( virtual_path: "spree/products/_cart_form", name: "add_checkboxes_to_cart_form", - insert_before: ".add-to-cart", + insert_before: "erb[loud]:contains('hidden_field_tag')", partial: "spree/products/cart_checkboxes" ) diff --git a/app/overrides/add_class_to_add_to_cart_div_on_products_show_page.rb b/app/overrides/add_class_to_add_to_cart_div_on_products_show_page.rb new file mode 100644 index 00000000..e69de29b diff --git a/app/overrides/add_subscribable_fields_to_products_show_page.rb b/app/overrides/add_subscribable_fields_to_products_show_page.rb index 49c7b389..566f9c73 100644 --- a/app/overrides/add_subscribable_fields_to_products_show_page.rb +++ b/app/overrides/add_subscribable_fields_to_products_show_page.rb @@ -1,6 +1,13 @@ +Deface::Override.new( + virtual_path: "spree/products/_cart_form", + name: "add_class_to_add_to_cart_div", + set_attributes: "#inside-product-cart-form div:nth-of-type(3)", + attributes: { class: 'add-to-cart' } +) + Deface::Override.new( virtual_path: "spree/products/_cart_form", name: "add_subscribable_fields_to_products_show", - insert_after: ".add-to-cart", + insert_after: "erb[loud]:contains('hidden_field_tag')", partial: "spree/products/subscription_fields" ) diff --git a/app/overrides/add_subscribed_field_in_cart_listing.rb b/app/overrides/add_subscribed_field_in_cart_listing.rb index 515fd7e0..e79ced4a 100644 --- a/app/overrides/add_subscribed_field_in_cart_listing.rb +++ b/app/overrides/add_subscribed_field_in_cart_listing.rb @@ -1,7 +1,7 @@ Deface::Override.new( virtual_path: "spree/orders/_line_item", name: "add_subscribed_field_to_cart_listing", - insert_bottom: ".line-item", + insert_bottom: ".shopping-cart-item", partial: "spree/orders/subscription_field" ) @@ -10,11 +10,4 @@ name: "edit_header_in_cart_listing", insert_bottom: "[data-hook='cart_items_headers']", partial: "spree/orders/cart_subscription_header" -) - -Deface::Override.new( - virtual_path: "spree/orders/_form", - name: "edit_footer_in_cart_listing", - insert_bottom: ".cart-total", - partial: "spree/orders/cart_subscription_footer" -) +) \ No newline at end of file diff --git a/app/overrides/add_subscription_number_to_order_page.rb b/app/overrides/add_subscription_number_to_order_page.rb index b0b00837..a8e47126 100644 --- a/app/overrides/add_subscription_number_to_order_page.rb +++ b/app/overrides/add_subscription_number_to_order_page.rb @@ -1,6 +1,6 @@ Deface::Override.new( virtual_path: 'spree/orders/show', name: 'add_subscription_number_to_order_page', - insert_after: "fieldset#order_summary h1", + insert_after: ".order-show-number", partial: 'spree/orders/subscription_number' ) diff --git a/app/overrides/add_subscriptions_admin_tab.rb b/app/overrides/add_subscriptions_admin_tab.rb index daf9caac..d9441586 100644 --- a/app/overrides/add_subscriptions_admin_tab.rb +++ b/app/overrides/add_subscriptions_admin_tab.rb @@ -1,6 +1,6 @@ Deface::Override.new( - virtual_path: 'spree/layouts/admin', + virtual_path: 'spree/admin/shared/_main_menu', name: 'subscriptions_admin_tab', - insert_bottom: '#main-sidebar', + insert_bottom: 'nav', partial: 'spree/admin/shared/subscriptions_sidebar_menu' ) diff --git a/app/views/spree/admin/products/_subscribable.html.erb b/app/views/spree/admin/products/_subscribable.html.erb index 8d3ddccd..ce27e12a 100644 --- a/app/views/spree/admin/products/_subscribable.html.erb +++ b/app/views/spree/admin/products/_subscribable.html.erb @@ -5,7 +5,7 @@ <%= f.error_message_on :subscribable %> <% end %> -