solidus_subscriptions-alpha 0.0.1

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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +26 -0
  3. data/README.md +128 -0
  4. data/Rakefile +28 -0
  5. data/app/assets/javascripts/spree/backend/solidus_subscriptions.js +2 -0
  6. data/app/assets/javascripts/spree/frontend/solidus_subscriptions.js +2 -0
  7. data/app/assets/stylesheets/spree/backend/solidus_subscriptions.css +4 -0
  8. data/app/assets/stylesheets/spree/frontend/solidus_subscriptions.css +4 -0
  9. data/app/controllers/solidus_subscriptions/api/v1/line_items_controller.rb +35 -0
  10. data/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb +44 -0
  11. data/app/controllers/spree/admin/subscriptions_controller.rb +66 -0
  12. data/app/decorators/spree/controllers/api/line_items/create_subscription_line_items.rb +28 -0
  13. data/app/decorators/spree/controllers/orders/create_subscription_line_items.rb +33 -0
  14. data/app/decorators/spree/line_items/subscription_line_items_association.rb +22 -0
  15. data/app/decorators/spree/orders/after_create.rb +15 -0
  16. data/app/decorators/spree/orders/finalize_creates_subscriptions.rb +19 -0
  17. data/app/decorators/spree/orders/subscription_line_items_association.rb +15 -0
  18. data/app/decorators/spree/users/have_many_subscriptions.rb +18 -0
  19. data/app/decorators/spree/variant_pretty_name.rb +13 -0
  20. data/app/jobs/solidus_subscriptions/process_installments_job.rb +22 -0
  21. data/app/models/solidus_subscriptions/checkout.rb +137 -0
  22. data/app/models/solidus_subscriptions/dispatcher.rb +32 -0
  23. data/app/models/solidus_subscriptions/failure_dispatcher.rb +19 -0
  24. data/app/models/solidus_subscriptions/installment.rb +126 -0
  25. data/app/models/solidus_subscriptions/installment_detail.rb +23 -0
  26. data/app/models/solidus_subscriptions/interval.rb +24 -0
  27. data/app/models/solidus_subscriptions/line_item.rb +98 -0
  28. data/app/models/solidus_subscriptions/line_item_builder.rb +44 -0
  29. data/app/models/solidus_subscriptions/order_builder.rb +40 -0
  30. data/app/models/solidus_subscriptions/out_of_stock_dispatcher.rb +19 -0
  31. data/app/models/solidus_subscriptions/payment_failed_dispatcher.rb +23 -0
  32. data/app/models/solidus_subscriptions/subscription.rb +217 -0
  33. data/app/models/solidus_subscriptions/subscription_generator.rb +60 -0
  34. data/app/models/solidus_subscriptions/subscription_line_item_builder.rb +21 -0
  35. data/app/models/solidus_subscriptions/subscription_order_promotion_rule.rb +25 -0
  36. data/app/models/solidus_subscriptions/subscription_promotion_rule.rb +38 -0
  37. data/app/models/solidus_subscriptions/success_dispatcher.rb +16 -0
  38. data/app/models/solidus_subscriptions/unsubscribable_error.rb +17 -0
  39. data/app/models/solidus_subscriptions/user_mismatch_error.rb +15 -0
  40. data/app/overrides/views/admin_subscribable_checkbox.rb +6 -0
  41. data/app/overrides/views/admin_subscriptions_menu_link.rb +8 -0
  42. data/app/overrides/views/subscription_line_item_fields.rb +6 -0
  43. data/app/views/spree/admin/promotions/rules/_subscription_order_promotion_rule.html.erb +0 -0
  44. data/app/views/spree/admin/promotions/rules/_subscription_promotion_rule.html.erb +0 -0
  45. data/app/views/spree/admin/shared/_no_objects_found.html.erb +4 -0
  46. data/app/views/spree/admin/shared/_subscription_tab.html.erb +3 -0
  47. data/app/views/spree/admin/solidus_subscriptions/subscriptions/_subscription.html.erb +66 -0
  48. data/app/views/spree/admin/subscriptions/_form.html.erb +81 -0
  49. data/app/views/spree/admin/subscriptions/_legacy_form.html.erb +81 -0
  50. data/app/views/spree/admin/subscriptions/_legacy_sidebar.html.erb +28 -0
  51. data/app/views/spree/admin/subscriptions/edit.html.erb +21 -0
  52. data/app/views/spree/admin/subscriptions/index.html.erb +119 -0
  53. data/app/views/spree/admin/subscriptions/new.html.erb +9 -0
  54. data/app/views/spree/admin/variants/_subscribable_checkbox.html.erb +6 -0
  55. data/app/views/spree/frontend/products/_subscription_line_item_fields.html.erb +30 -0
  56. data/config/locales/en.yml +91 -0
  57. data/config/routes.rb +25 -0
  58. data/db/migrate/20160825164850_create_solidus_subscriptions_subscriptions.rb +11 -0
  59. data/db/migrate/20160825173548_create_solidus_subscriptions_line_items.rb +17 -0
  60. data/db/migrate/20160825202248_create_solidus_subscriptions_installments.rb +23 -0
  61. data/db/migrate/20160825211202_create_solidus_subscriptions_installment_details.rb +22 -0
  62. data/db/migrate/20160825214240_add_subscribable_to_spree_variants.rb +5 -0
  63. data/db/migrate/20160829201653_change_subscription_line_items_installments_to_max_installments.rb +5 -0
  64. data/db/migrate/20160902220242_remove_state_from_solidus_susbscriptions_installment_details.rb +5 -0
  65. data/db/migrate/20160902220604_add_successful_to_solidus_subscriptions_installment_details.rb +5 -0
  66. data/db/migrate/20160902221218_add_message_to_solidus_subscriptions_installment_details.rb +5 -0
  67. data/db/migrate/20160922164101_add_interval_length_and_units_to_subscription_line_items.rb +8 -0
  68. data/db/migrate/20161006191003_add_skip_count_to_solidus_subscriptions_subscriptions.rb +5 -0
  69. data/db/migrate/20161006191127_add_successive_skip_count_to_solidus_subscriptions_subscriptions.rb +5 -0
  70. data/db/migrate/20161014212649_allow_spree_line_item_id_to_be_null.rb +5 -0
  71. data/db/migrate/20161017155749_add_order_id_to_solidus_subscriptions_installment_details.rb +6 -0
  72. data/db/migrate/20161017175509_remove_order_id_from_solidus_subscriptions_installments.rb +6 -0
  73. data/db/migrate/20161017201944_add_subscription_order_to_spree_orders.rb +5 -0
  74. data/db/migrate/20161221155142_add_store_to_solidus_subscriptions_subscriptions.rb +6 -0
  75. data/db/migrate/20161223152905_add_address_id_to_solidus_subscriptions_subscriptions.rb +7 -0
  76. data/db/migrate/20170106224713_change_line_item_max_installments_to_end_date.rb +6 -0
  77. data/db/migrate/20170111224458_change_subscription_actionable_date_to_datetime.rb +5 -0
  78. data/db/migrate/20170111232801_change_inteval_actionable_date_to_datetime.rb +5 -0
  79. data/db/migrate/20170112012407_add_config_options_to_subscriptions.rb +7 -0
  80. data/lib/generators/solidus_subscriptions/install/install_generator.rb +30 -0
  81. data/lib/solidus_subscriptions.rb +6 -0
  82. data/lib/solidus_subscriptions/ability.rb +19 -0
  83. data/lib/solidus_subscriptions/config.rb +97 -0
  84. data/lib/solidus_subscriptions/engine.rb +56 -0
  85. data/lib/solidus_subscriptions/permitted_attributes.rb +36 -0
  86. data/lib/solidus_subscriptions/processor.rb +108 -0
  87. data/lib/solidus_subscriptions/testing_support/factories.rb +5 -0
  88. data/lib/solidus_subscriptions/testing_support/factories/installment_detail_factory.rb +7 -0
  89. data/lib/solidus_subscriptions/testing_support/factories/installment_factory.rb +21 -0
  90. data/lib/solidus_subscriptions/testing_support/factories/line_item_factory.rb +18 -0
  91. data/lib/solidus_subscriptions/testing_support/factories/spree/line_item_factory.rb +17 -0
  92. data/lib/solidus_subscriptions/testing_support/factories/spree/order_factory.rb +18 -0
  93. data/lib/solidus_subscriptions/testing_support/factories/spree_modification_factory.rb +8 -0
  94. data/lib/solidus_subscriptions/testing_support/factories/subscription_factory.rb +43 -0
  95. data/lib/solidus_subscriptions/version.rb +3 -0
  96. data/lib/tasks/process_subscriptions.rake +6 -0
  97. metadata +460 -0
@@ -0,0 +1,5 @@
1
+ class AddMessageToSolidusSubscriptionsInstallmentDetails < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_column :solidus_subscriptions_installment_details, :message, :string
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class AddIntervalLengthAndUnitsToSubscriptionLineItems < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_column :solidus_subscriptions_line_items, :interval_units, :integer
4
+ add_column :solidus_subscriptions_line_items, :interval_length, :integer
5
+
6
+ remove_column :solidus_subscriptions_line_items, :interval
7
+ end
8
+ end
@@ -0,0 +1,5 @@
1
+ class AddSkipCountToSolidusSubscriptionsSubscriptions < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_column :solidus_subscriptions_subscriptions, :skip_count, :integer, default: 0, null: false
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddSuccessiveSkipCountToSolidusSubscriptionsSubscriptions < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_column :solidus_subscriptions_subscriptions, :successive_skip_count, :integer, default: 0, null: false
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AllowSpreeLineItemIdToBeNull < SolidusSupport::Migration[4.2]
2
+ def change
3
+ change_column_null :solidus_subscriptions_line_items, :spree_line_item_id, true
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddOrderIdToSolidusSubscriptionsInstallmentDetails < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_reference :solidus_subscriptions_installment_details, :order, index: true
4
+ add_foreign_key :solidus_subscriptions_installment_details, :spree_orders, column: :order_id
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class RemoveOrderIdFromSolidusSubscriptionsInstallments < SolidusSupport::Migration[4.2]
2
+ def change
3
+ remove_foreign_key :solidus_subscriptions_installments, column: :order_id
4
+ remove_column :solidus_subscriptions_installments, :order_id, :integer
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddSubscriptionOrderToSpreeOrders < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_column :spree_orders, :subscription_order, :boolean, default: false, null: false
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddStoreToSolidusSubscriptionsSubscriptions < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_reference :solidus_subscriptions_subscriptions, :store, index: true
4
+ add_foreign_key :solidus_subscriptions_subscriptions, :spree_stores, column: :store_id
5
+ end
6
+ end
@@ -0,0 +1,7 @@
1
+ class AddAddressIdToSolidusSubscriptionsSubscriptions < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_reference :solidus_subscriptions_subscriptions, :shipping_address
4
+ add_index :solidus_subscriptions_subscriptions, :shipping_address_id, name: :index_subscription_shipping_address_id
5
+ add_foreign_key :solidus_subscriptions_subscriptions, :spree_addresses, column: :shipping_address_id
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ class ChangeLineItemMaxInstallmentsToEndDate < SolidusSupport::Migration[4.2]
2
+ def change
3
+ remove_column :solidus_subscriptions_line_items, :max_installments
4
+ add_column :solidus_subscriptions_line_items, :end_date, :date
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class ChangeSubscriptionActionableDateToDatetime < SolidusSupport::Migration[4.2]
2
+ def change
3
+ change_column :solidus_subscriptions_subscriptions, :actionable_date, :datetime
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class ChangeIntevalActionableDateToDatetime < SolidusSupport::Migration[4.2]
2
+ def change
3
+ change_column :solidus_subscriptions_installments, :actionable_date, :datetime
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ class AddConfigOptionsToSubscriptions < SolidusSupport::Migration[4.2]
2
+ def change
3
+ add_column :solidus_subscriptions_subscriptions, :interval_length, :integer
4
+ add_column :solidus_subscriptions_subscriptions, :interval_units, :integer
5
+ add_column :solidus_subscriptions_subscriptions, :end_date, :datetime
6
+ end
7
+ end
@@ -0,0 +1,30 @@
1
+ module SolidusSubscriptions
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ class_option :auto_run_migrations, type: :boolean, default: false
5
+
6
+ def add_javascripts
7
+ append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_subscriptions\n"
8
+ append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_subscriptions\n"
9
+ end
10
+
11
+ def add_stylesheets
12
+ inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/solidus_subscriptions\n", before: /\*\//, verbose: true
13
+ inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/solidus_subscriptions\n", before: /\*\//, verbose: true
14
+ end
15
+
16
+ def add_migrations
17
+ run 'bundle exec rake railties:install:migrations FROM=solidus_subscriptions'
18
+ end
19
+
20
+ def run_migrations
21
+ run_migrations = options[:auto_run_migrations] || ['', 'y', 'Y'].include?(ask('Would you like to run the migrations now? [Y/n]'))
22
+ if run_migrations
23
+ run 'bundle exec rake db:migrate'
24
+ else
25
+ puts 'Skipping rake db:migrate, don\'t forget to run it!'
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ require 'solidus'
2
+ require 'solidus_support'
3
+ require "solidus_subscriptions/ability"
4
+ require 'solidus_subscriptions/engine'
5
+ require 'deface'
6
+ require 'state_machines'
@@ -0,0 +1,19 @@
1
+ module SolidusSubscriptions
2
+ class Ability
3
+ include CanCan::Ability
4
+
5
+ def initialize(user)
6
+ alias_action :create, :read, :update, :destroy, to: :crud
7
+
8
+ if user.has_spree_role?('admin')
9
+ can(:manage, LineItem)
10
+ can(:manage, Subscription)
11
+ else
12
+ can([:crud, :skip, :cancel], Subscription, user_id: user.id)
13
+ can(:crud, LineItem) do |li, order|
14
+ li.order.user == user || li.order == order
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,97 @@
1
+ module SolidusSubscriptions
2
+ module Config
3
+ class << self
4
+ # Processing Event handlers
5
+ # These handlers are pluggable, however it is highly encouraged that you
6
+ # subclass from the the dispatcher you are replacing, and call super
7
+ # from within the #dispatch method (if you override it)
8
+ #
9
+ # This handler is called when a susbcription order is successfully placed.
10
+ attr_writer :success_dispatcher_class
11
+ def success_dispatcher_class
12
+ @success_dispatcher_class ||= ::SolidusSubscriptions::SuccessDispatcher
13
+ end
14
+
15
+ # This handler is called when an order cant be placed for a group of
16
+ # installments
17
+ attr_writer :failure_dispatcher_class
18
+ def failure_dispatcher_class
19
+ @failure_dispatcher_class ||= ::SolidusSubscriptions::FailureDispatcher
20
+ end
21
+
22
+ # This handler is called when a payment fails on a subscription order
23
+ attr_writer :payment_failed_dispatcher_class
24
+ def payment_failed_dispatcher_class
25
+ @payment_failed_dispatcher_class ||= ::SolidusSubscriptions::PaymentFailedDispatcher
26
+ end
27
+
28
+ # This handler is called when installemnts cannot be fulfilled due to lack
29
+ # of stock
30
+ attr_writer :out_of_stock_dispatcher
31
+ def out_of_stock_dispatcher_class
32
+ @out_of_stock_dispatcher_class ||= ::SolidusSubscriptions::OutOfStockDispatcher
33
+ end
34
+
35
+ def default_gateway(&block)
36
+ return @gateway.call unless block_given?
37
+ @gateway = block
38
+ end
39
+ end
40
+
41
+ # Maximum number of times a user can skip their subscription before it
42
+ # must be processed
43
+ mattr_accessor(:maximum_successive_skips) { 1 }
44
+
45
+ # Limit on the number of times a user can skip thier subscription. Once
46
+ # this limit is reached, no skips are permitted
47
+ mattr_accessor(:maximum_total_skips) { nil }
48
+
49
+ # Time between an installment failing to be processed and the system
50
+ # retrying to fulfil it
51
+ mattr_accessor(:reprocessing_interval) { 1.day }
52
+
53
+ mattr_accessor(:minimum_cancellation_notice) { 1.day }
54
+
55
+ # Which queue is responsible for processing subscriptions
56
+ mattr_accessor(:processing_queue) { :default }
57
+
58
+ # SolidusSubscriptions::LineItem attributes which are allowed to
59
+ # be updated from user data
60
+ #
61
+ # This is useful in the case where certain fields should not be allowed to
62
+ # be modified by the user. This locks these attributes from being passed
63
+ # in to the orders controller (or the api controller).
64
+
65
+ # Ie. if a store does not want to allow users to configure the end date of
66
+ # a subscription. Add this to an initializer:
67
+
68
+ # ```
69
+ # SolidusSubscriptions::Config.subscription_line_item_attributes = [
70
+ # :quantity,
71
+ # :interval_length,
72
+ # :interval_units,
73
+ # :subscribable_id
74
+ # ]
75
+ # ```
76
+ # This configuration also easily allows the gem to be customized to track
77
+ # more information on the subcriptions line items.
78
+ mattr_accessor(:subscription_line_item_attributes) do
79
+ [
80
+ :quantity,
81
+ :subscribable_id,
82
+ :interval_length,
83
+ :interval_units,
84
+ :end_date
85
+ ]
86
+ end
87
+
88
+ # SolidusSubscriptions::Subscription attributes which are allowed to
89
+ # be updated from user data
90
+ mattr_accessor(:subscription_attributes) do
91
+ [
92
+ :actionable_date,
93
+ shipping_address_attributes: Spree::PermittedAttributes.address_attributes
94
+ ]
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,56 @@
1
+ module SolidusSubscriptions
2
+ class Engine < Rails::Engine
3
+ require 'spree/core'
4
+ require 'solidus_subscriptions/permitted_attributes'
5
+ require 'solidus_subscriptions/config'
6
+ require 'solidus_subscriptions/processor'
7
+
8
+ isolate_namespace SolidusSubscriptions
9
+ engine_name 'solidus_subscriptions'
10
+
11
+ # use rspec for tests
12
+ config.generators do |g|
13
+ g.test_framework :rspec
14
+ end
15
+
16
+ config.autoload_paths << config.root.join('app', 'jobs')
17
+
18
+ initializer 'configure spree subcription permitted attributes', after: 'require subscription lib helpers' do
19
+ PermittedAttributes.update_spree_permiteed_attributes
20
+ end
21
+
22
+ initializer 'solidus_subscriptions.configs', before: "require subscription lib helpers" do
23
+ end
24
+
25
+ initializer 'register_subscription_promotion_rule', after: 'spree.promo.register.promotion.rules' do |app|
26
+ app.config.spree.promotions.rules << 'SolidusSubscriptions::SubscriptionPromotionRule'
27
+ app.config.spree.promotions.rules << 'SolidusSubscriptions::SubscriptionOrderPromotionRule'
28
+ end
29
+
30
+ initializer 'subscriptions_backend' do
31
+ next unless Spree::Backend::Config.respond_to?(:menu_items)
32
+ Spree::Backend::Config.configure do |config|
33
+ config.menu_items << config.class::MenuItem.new(
34
+ [:subscriptions],
35
+ 'repeat',
36
+ url: :admin_subscriptions_path,
37
+ condition: ->{ can?(:admin, SolidusSubscriptions::Subscription) }
38
+ )
39
+ end
40
+ end
41
+
42
+ def self.activate
43
+ Dir.glob(File.join(File.dirname(__FILE__), '../../app/decorators/**/*.rb')) do |c|
44
+ Rails.configuration.cache_classes ? require(c) : load(c)
45
+ end
46
+
47
+ Spree::Ability.register_ability(SolidusSubscriptions::Ability)
48
+ end
49
+
50
+ config.to_prepare(&method(:activate).to_proc)
51
+ end
52
+
53
+ def self.table_name_prefix
54
+ 'solidus_subscriptions_'
55
+ end
56
+ end
@@ -0,0 +1,36 @@
1
+ # This module is responsible for managing what attributes can be updated
2
+ # through the api. It also overrides Spree::Permitted attributes to allow the
3
+ # solidus api to accept nested params for subscription models as well
4
+ module SolidusSubscriptions
5
+ module PermittedAttributes
6
+ class << self
7
+ def update_spree_permiteed_attributes
8
+ Spree::PermittedAttributes.line_item_attributes << {
9
+ subscription_line_items_attributes: nested(
10
+ subscription_line_item_attributes
11
+ )
12
+ }
13
+
14
+ Spree::PermittedAttributes.user_attributes << {
15
+ subscriptions_attributes: nested(subscription_attributes)
16
+ }
17
+ end
18
+
19
+ def subscription_line_item_attributes
20
+ [Config.subscription_line_item_attributes]
21
+ end
22
+
23
+ def subscription_attributes
24
+ Config.subscription_attributes | [
25
+ { line_items_attributes: nested(subscription_line_item_attributes) - [:subscribable_id] }
26
+ ]
27
+ end
28
+
29
+ private
30
+
31
+ def nested(attributes)
32
+ attributes | [:id]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,108 @@
1
+ # This class is responsible for finding subscriptions and installments
2
+ # which need to be processed. It will group them together by user and attempts
3
+ # to process them together. Subscriptions will also be grouped by their
4
+ # shiping address id.
5
+ #
6
+ # This class passes the reponsibility of actually creating the order off onto
7
+ # the consolidated installment class.
8
+ #
9
+ # This class generates `ProcessInstallmentsJob`s
10
+ module SolidusSubscriptions
11
+ class Processor
12
+ class << self
13
+ # Find all actionable subscriptions and intallments, group them together
14
+ # by user, and schedule a processing job for the group as a batch
15
+ def run
16
+ batched_users_to_be_processed.each { |batch| new(batch).build_jobs }
17
+ end
18
+
19
+ private
20
+
21
+ def batched_users_to_be_processed
22
+ subscriptions = SolidusSubscriptions::Subscription.arel_table
23
+ installments = SolidusSubscriptions::Installment.arel_table
24
+
25
+ Spree::User.
26
+ joins(:subscriptions).
27
+ joins(
28
+ subscriptions.
29
+ join(installments, Arel::Nodes::OuterJoin).
30
+ on(subscriptions[:id].eq(installments[:subscription_id])).
31
+ join_sources
32
+ ).
33
+ where(
34
+ SolidusSubscriptions::Subscription.actionable.arel.constraints.reduce(:and).
35
+ or(SolidusSubscriptions::Installment.actionable.arel.constraints.reduce(:and))
36
+ ).
37
+ distinct.
38
+ find_in_batches
39
+ end
40
+ end
41
+
42
+ # @return [Array<Spree.user_class>]
43
+ attr_reader :users
44
+
45
+ # Get a new instance of the SolidusSubscriptions::Processor
46
+ #
47
+ # @param users [Array<Spree.user_class>] A list of users with actionable
48
+ # subscriptions or installments
49
+ #
50
+ # @return [SolidusSubscriptions::Processor]
51
+ def initialize(users)
52
+ @users = users
53
+ @installments = {}
54
+ end
55
+
56
+ # Create `ProcessInstallmentsJob`s for the users used to initalize the
57
+ # instance
58
+ def build_jobs
59
+ users.map do |user|
60
+ installemts_by_address_and_user = installments(user).group_by do |i|
61
+ i.subscription.shipping_address_id
62
+ end
63
+
64
+ installemts_by_address_and_user.values.each do |grouped_installments|
65
+ ProcessInstallmentsJob.perform_later grouped_installments.map(&:id)
66
+ end
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def subscriptions_by_id
73
+ @subscriptions_by_id ||= Subscription.
74
+ actionable.
75
+ includes(:line_items, :user).
76
+ where(user_id: user_ids).
77
+ group_by(&:user_id)
78
+ end
79
+
80
+ def retry_installments
81
+ @failed_installments ||= Installment.
82
+ actionable.
83
+ includes(:subscription).
84
+ where(solidus_subscriptions_subscriptions: { user_id: user_ids }).
85
+ group_by { |i| i.subscription.user_id }
86
+ end
87
+
88
+ def installments(user)
89
+ @installments[user.id] ||= retry_installments.fetch(user.id, []) + new_installments(user)
90
+ end
91
+
92
+ def new_installments(user)
93
+ ActiveRecord::Base.transaction do
94
+ subscriptions_by_id.fetch(user.id, []).map do |sub|
95
+ sub.successive_skip_count = 0
96
+ sub.advance_actionable_date
97
+ sub.cancel! if sub.pending_cancellation?
98
+ sub.deactivate! if sub.can_be_deactivated?
99
+ sub.installments.create!
100
+ end
101
+ end
102
+ end
103
+
104
+ def user_ids
105
+ @user_ids ||= users.map(&:id)
106
+ end
107
+ end
108
+ end