solidus_subscriptions 1.0.0 → 1.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +57 -9
  3. data/.github/dependabot.yml +7 -0
  4. data/.rubocop.yml +3 -0
  5. data/CHANGELOG.md +201 -169
  6. data/Gemfile +14 -1
  7. data/README.md +17 -7
  8. data/app/controllers/solidus_subscriptions/api/v1/subscriptions_controller.rb +24 -0
  9. data/app/controllers/spree/admin/subscriptions_controller.rb +32 -4
  10. data/app/decorators/models/solidus_subscriptions/spree/wallet_payment_source/report_default_change_to_subscriptions.rb +2 -2
  11. data/app/jobs/solidus_subscriptions/create_subscription_job.rb +11 -0
  12. data/app/jobs/solidus_subscriptions/process_installment_job.rb +1 -1
  13. data/app/models/solidus_subscriptions/subscription.rb +75 -31
  14. data/app/subscribers/solidus_subscriptions/churn_buster_subscriber.rb +1 -0
  15. data/app/subscribers/solidus_subscriptions/event_storage_subscriber.rb +1 -0
  16. data/app/subscribers/solidus_subscriptions/order_subscriber.rb +16 -0
  17. data/app/views/spree/admin/shared/_subscription_actions.html.erb +27 -8
  18. data/app/views/spree/admin/shared/_subscription_sidebar.html.erb +2 -0
  19. data/app/views/spree/admin/subscriptions/_state_pill.html.erb +3 -2
  20. data/app/views/spree/admin/subscriptions/index.html.erb +50 -13
  21. data/bin/sandbox +1 -6
  22. data/config/locales/en.yml +16 -0
  23. data/config/routes.rb +9 -3
  24. data/db/migrate/20210905145955_add_paused_to_subscriptions.rb +5 -0
  25. data/lib/decorators/api/controllers/solidus_subscriptions/spree/api/line_items_controller/create_subscription_line_items.rb +14 -0
  26. data/lib/generators/solidus_subscriptions/install/install_generator.rb +16 -0
  27. data/lib/generators/solidus_subscriptions/install/templates/app/controllers/concerns/create_subscription.rb +17 -0
  28. data/lib/generators/solidus_subscriptions/install/templates/app/views/cart_line_items/_subscription_fields.html.erb +30 -0
  29. data/lib/generators/solidus_subscriptions/install/templates/initializer.rb +3 -1
  30. data/lib/solidus_subscriptions/configuration.rb +29 -6
  31. data/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher.rb +2 -2
  32. data/lib/solidus_subscriptions/dispatcher/success_dispatcher.rb +2 -2
  33. data/lib/solidus_subscriptions/engine.rb +28 -0
  34. data/lib/solidus_subscriptions/permission_sets/default_customer.rb +1 -1
  35. data/lib/solidus_subscriptions/processing_error_handlers/rails_logger.rb +25 -0
  36. data/lib/solidus_subscriptions/subscription_generator.rb +6 -0
  37. data/lib/solidus_subscriptions/version.rb +1 -1
  38. data/solidus_subscriptions.gemspec +6 -6
  39. data/spec/controllers/spree/api/line_items_controller_spec.rb +63 -28
  40. data/spec/controllers/spree/api/users_controller_spec.rb +3 -0
  41. data/spec/decorators/controllers/solidus_subscriptions/spree/orders_controller/create_subscription_line_items_spec.rb +4 -4
  42. data/spec/decorators/models/solidus_subscriptions/spree/variant/auto_delete_from_subscriptions_spec.rb +2 -2
  43. data/spec/features/admin/subscriptions_spec.rb +13 -3
  44. data/spec/jobs/solidus_subscriptions/create_subscription_job_spec.rb +20 -0
  45. data/spec/jobs/solidus_subscriptions/process_installment_job_spec.rb +17 -0
  46. data/spec/lib/solidus_subscriptions/dispatcher/payment_failed_dispatcher_spec.rb +3 -3
  47. data/spec/lib/solidus_subscriptions/dispatcher/success_dispatcher_spec.rb +3 -3
  48. data/spec/lib/solidus_subscriptions/subscription_generator_spec.rb +1 -1
  49. data/spec/models/solidus_subscriptions/installment_spec.rb +1 -1
  50. data/spec/models/solidus_subscriptions/subscription_spec.rb +893 -16
  51. data/spec/models/spree/wallet_payment_source_spec.rb +3 -3
  52. data/spec/requests/api/v1/subscriptions_spec.rb +229 -0
  53. data/spec/subscribers/solidus_subscriptions/churn_buster_subscriber_spec.rb +8 -8
  54. data/spec/subscribers/solidus_subscriptions/order_subscriber_spec.rb +13 -0
  55. metadata +24 -17
  56. data/app/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions.rb +0 -25
  57. data/config/initializers/subscribers.rb +0 -9
  58. data/spec/decorators/models/solidus_subscriptions/spree/order/finalize_creates_subscriptions_spec.rb +0 -32
  59. /data/{app → lib}/views/spree/frontend/products/_subscription_line_item_fields.html.erb +0 -0
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # solidus_subscriptions
2
2
 
3
- [![CircleCI](https://circleci.com/gh/solidusio-contrib/solidus_subscriptions.svg?style=shield)](https://circleci.com/gh/solidusio-contrib/solidus_subscriptions)
4
- [![codecov](https://codecov.io/gh/solidusio-contrib/solidus_subscriptions/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio-contrib/solidus_subscriptions)
3
+ [![CircleCI](https://circleci.com/gh/solidusio/solidus_subscriptions.svg?style=shield)](https://circleci.com/gh/solidusio/solidus_subscriptions)
4
+ [![codecov](https://codecov.io/gh/solidusio/solidus_subscriptions/branch/master/graph/badge.svg)](https://codecov.io/gh/solidusio/solidus_subscriptions)
5
5
 
6
6
  A Solidus extension to add subscriptions to your store.
7
7
 
@@ -10,13 +10,26 @@ A Solidus extension to add subscriptions to your store.
10
10
  Add solidus_subscriptions to your Gemfile:
11
11
 
12
12
  ```ruby
13
- gem 'solidus_subscriptions', github: 'solidusio-contrib/solidus_subscriptions'
13
+ gem 'solidus_subscriptions', github: 'solidusio/solidus_subscriptions'
14
14
  ```
15
15
 
16
16
  Bundle your dependencies and run the installation generator:
17
17
 
18
18
  ```shell
19
19
  $ bundle
20
+ $ bin/rails generate solidus_subscriptions:install --frontend=starter
21
+ ```
22
+
23
+ Please, be aware that the starter installation only works with the default
24
+ implementation of the starter frontend. Any customization to the files that
25
+ will be modified by the installer might break the installation procedure.
26
+ If that happens, try to adapt the installed code on top of the customizations
27
+ of the store.
28
+
29
+
30
+ If you are using the legacy `solidus_frontend` gem, please run this command instead:
31
+
32
+ ```shell
20
33
  $ bin/rails generate solidus_subscriptions:install
21
34
  ```
22
35
 
@@ -91,14 +104,11 @@ The task creates ActiveJob jobs which can be fulfilled by your queue library of
91
104
  We suggest using the [Whenever](https://github.com/javan/whenever) gem to schedule the task.
92
105
 
93
106
  ### Promotion rules
107
+
94
108
  This extensions adds the following [Promotion rules](https://guides.solidus.io/developers/promotions/promotion-rules.html):
95
109
  * `SolidusSubscriptions::Promotion::Rules::SubscriptionCreationOrder` which applies if the order is creating a subscription;
96
110
  * `SolidusSubscriptions::Promotion::Rules::SubscriptionInstallmentOrder` which applies if the order is an installment of a subscription.
97
111
 
98
- ### API documentation
99
-
100
- You can find the API documentation [here](https://stoplight.io/p/docs/gh/solidusio-contrib/solidus_subscriptions?group=master).
101
-
102
112
  ### Churn Buster integration
103
113
 
104
114
  This extension optionally integrates with [Churn Buster](https://churnbuster.io) for failed payment
@@ -56,6 +56,26 @@ module SolidusSubscriptions
56
56
  end
57
57
  end
58
58
 
59
+ def pause
60
+ load_subscription
61
+
62
+ if @subscription.pause(actionable_date: actionable_date_param)
63
+ render json: @subscription.to_json
64
+ else
65
+ render json: @subscription.errors.to_json, status: :unprocessable_entity
66
+ end
67
+ end
68
+
69
+ def resume
70
+ load_subscription
71
+
72
+ if @subscription.resume(actionable_date: actionable_date_param)
73
+ render json: @subscription.to_json
74
+ else
75
+ render json: @subscription.errors.to_json, status: :unprocessable_entity
76
+ end
77
+ end
78
+
59
79
  private
60
80
 
61
81
  def load_subscription
@@ -77,6 +97,10 @@ module SolidusSubscriptions
77
97
  ])
78
98
  end
79
99
 
100
+ def actionable_date_param
101
+ params[:subscription].try(:[], :actionable_date)&.presence
102
+ end
103
+
80
104
  def line_item_attributes
81
105
  SolidusSubscriptions.configuration.subscription_line_item_attributes
82
106
  end
@@ -68,10 +68,38 @@ module Spree
68
68
  def skip
69
69
  @subscription.skip(check_skip_limits: false)
70
70
 
71
- notice = I18n.t(
72
- 'spree.admin.subscriptions.successfully_skipped',
73
- date: @subscription.actionable_date
74
- )
71
+ notice = if @subscription.errors.none?
72
+ I18n.t(
73
+ 'spree.admin.subscriptions.successfully_skipped',
74
+ date: @subscription.actionable_date
75
+ )
76
+ else
77
+ @subscription.errors.full_messages.to_sentence
78
+ end
79
+
80
+ redirect_back(fallback_location: spree.admin_subscriptions_path, notice: notice)
81
+ end
82
+
83
+ def pause
84
+ @subscription.pause(actionable_date: nil)
85
+
86
+ notice = if @subscription.errors.none?
87
+ I18n.t('spree.admin.subscriptions.successfully_paused')
88
+ else
89
+ @subscription.errors.full_messages.to_sentence
90
+ end
91
+
92
+ redirect_back(fallback_location: spree.admin_subscriptions_path, notice: notice)
93
+ end
94
+
95
+ def resume
96
+ @subscription.resume(actionable_date: nil)
97
+
98
+ notice = if @subscription.errors.none?
99
+ I18n.t('spree.admin.subscriptions.successfully_resumed')
100
+ else
101
+ @subscription.errors.full_messages.to_sentence
102
+ end
75
103
 
76
104
  redirect_back(fallback_location: spree.admin_subscriptions_path, notice: notice)
77
105
  end
@@ -14,8 +14,8 @@ module SolidusSubscriptions
14
14
  return if !previous_changes.key?('default') || !default?
15
15
 
16
16
  user.subscriptions.with_default_payment_source.each do |subscription|
17
- ::Spree::Event.fire(
18
- 'solidus_subscriptions.subscription_payment_method_changed',
17
+ ::SolidusSupport::LegacyEventCompat::Bus.publish(
18
+ :'solidus_subscriptions.subscription_payment_method_changed',
19
19
  subscription: subscription,
20
20
  )
21
21
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusSubscriptions
4
+ class CreateSubscriptionJob < ApplicationJob
5
+ queue_as { SolidusSubscriptions.configuration.processing_queue }
6
+
7
+ def perform(order)
8
+ SolidusSubscriptions.configuration.subscription_generator_class.call(order)
9
+ end
10
+ end
11
+ end
@@ -7,7 +7,7 @@ module SolidusSubscriptions
7
7
  def perform(installment)
8
8
  Checkout.new(installment).process
9
9
  rescue StandardError => e
10
- SolidusSubscriptions.configuration.processing_error_handler&.call(e)
10
+ SolidusSubscriptions.configuration.processing_error_handler&.call(e, installment)
11
11
  end
12
12
  end
13
13
  end
@@ -38,7 +38,7 @@ module SolidusSubscriptions
38
38
  before_validation :set_currency
39
39
  before_create :generate_guest_token
40
40
  after_create :emit_event_for_creation
41
- before_update :update_actionable_date_if_interval_changed
41
+ before_update :update_actionable_date_if_interval_changed, unless: :paused_changed?
42
42
  after_update :emit_events_for_update
43
43
 
44
44
  # Find all subscriptions that are "actionable"; that is, ones that have an
@@ -83,8 +83,13 @@ module SolidusSubscriptions
83
83
  where(payment_method: nil, payment_source: nil)
84
84
  end)
85
85
 
86
+ # Scope for finding subscription with a specific item
87
+ scope :with_subscribable, (lambda do |id|
88
+ joins(line_items: :subscribable).where(spree_variants: { id: id })
89
+ end)
90
+
86
91
  def self.ransackable_scopes(_auth_object = nil)
87
- [:in_processing_state]
92
+ [:in_processing_state, :with_subscribable]
88
93
  end
89
94
 
90
95
  def self.processing_states
@@ -107,7 +112,7 @@ module SolidusSubscriptions
107
112
  state_machine :state, initial: :active do
108
113
  event :cancel do
109
114
  transition [:active, :pending_cancellation] => :canceled,
110
- if: ->(subscription) { subscription.can_be_canceled? }
115
+ if: ->(subscription) { subscription.can_be_canceled? }
111
116
 
112
117
  transition active: :pending_cancellation
113
118
  end
@@ -122,7 +127,7 @@ module SolidusSubscriptions
122
127
 
123
128
  event :deactivate do
124
129
  transition active: :inactive,
125
- if: ->(subscription) { subscription.can_be_deactivated? }
130
+ if: ->(subscription) { subscription.can_be_deactivated? }
126
131
  end
127
132
 
128
133
  event :activate do
@@ -159,19 +164,21 @@ module SolidusSubscriptions
159
164
  end
160
165
 
161
166
  def skip(check_skip_limits: true)
167
+ check_invalid_skip_states
168
+
162
169
  if check_skip_limits
163
170
  check_successive_skips_exceeded
164
171
  check_total_skips_exceeded
165
-
166
- return if errors.any?
167
172
  end
168
173
 
174
+ return if errors.any?
175
+
169
176
  increment(:skip_count)
170
177
  increment(:successive_skip_count)
171
178
  save!
172
179
 
173
180
  advance_actionable_date.tap do
174
- events.create!(event_type: 'subscription_skipped')
181
+ create_and_emit_event(type: 'subscription_skipped')
175
182
  end
176
183
  end
177
184
 
@@ -206,11 +213,37 @@ module SolidusSubscriptions
206
213
  # @return [Date] The next date after the current actionable_date this
207
214
  # subscription will be eligible to be processed.
208
215
  def advance_actionable_date
209
- update! actionable_date: next_actionable_date
216
+ create_and_emit_event(type: 'subscription_resumed') if paused?
217
+
218
+ update! actionable_date: next_actionable_date, paused: false
210
219
 
211
220
  actionable_date
212
221
  end
213
222
 
223
+ def pause(actionable_date: nil)
224
+ check_invalid_pause_states
225
+ return false if errors.any?
226
+ return true if paused?
227
+
228
+ result = update! paused: true, actionable_date: actionable_date && tomorrow_or_after(actionable_date)
229
+ create_and_emit_event(type: 'subscription_paused') if result
230
+ result
231
+ end
232
+
233
+ def resume(actionable_date: nil)
234
+ check_invalid_resume_states
235
+ return false if errors.any?
236
+ return true unless paused?
237
+
238
+ result = update! paused: false, actionable_date: tomorrow_or_after(actionable_date)
239
+ create_and_emit_event(type: 'subscription_resumed') if result
240
+ result
241
+ end
242
+
243
+ def state_with_pause
244
+ active? && paused? ? 'paused' : state
245
+ end
246
+
214
247
  # The state of the last attempt to process an installment associated to
215
248
  # this subscription
216
249
  #
@@ -300,6 +333,23 @@ module SolidusSubscriptions
300
333
  end
301
334
  end
302
335
 
336
+ def check_invalid_skip_states
337
+ errors.add(:paused, :cannot_skip) if paused?
338
+ errors.add(:state, :cannot_skip) if canceled? || inactive?
339
+ end
340
+
341
+ def check_invalid_pause_states
342
+ errors.add(:paused, :not_active) unless active?
343
+ end
344
+
345
+ alias check_invalid_resume_states check_invalid_pause_states
346
+
347
+ def tomorrow_or_after(date)
348
+ [date.try(:to_date), Time.zone.tomorrow].compact.max
349
+ rescue ::Date::Error
350
+ Time.zone.tomorrow
351
+ end
352
+
303
353
  def update_actionable_date_if_interval_changed
304
354
  if persisted? && (interval_length_previously_changed? || interval_units_previously_changed?)
305
355
  base_date = if installments.any?
@@ -338,13 +388,22 @@ module SolidusSubscriptions
338
388
  end
339
389
  end
340
390
 
341
- def emit_event_for_creation
342
- ::Spree::Event.fire(
343
- 'solidus_subscriptions.subscription_created',
391
+ def emit_event(type:)
392
+ ::SolidusSupport::LegacyEventCompat::Bus.publish(
393
+ :"solidus_subscriptions.#{type}",
344
394
  subscription: self,
345
395
  )
346
396
  end
347
397
 
398
+ def create_and_emit_event(type:)
399
+ events.create!(event_type: type)
400
+ emit_event(type: type)
401
+ end
402
+
403
+ def emit_event_for_creation
404
+ emit_event(type: 'subscription_created')
405
+ end
406
+
348
407
  def emit_event_for_transition
349
408
  event_type = {
350
409
  active: 'subscription_activated',
@@ -353,39 +412,24 @@ module SolidusSubscriptions
353
412
  inactive: 'subscription_ended',
354
413
  }[state.to_sym]
355
414
 
356
- ::Spree::Event.fire(
357
- "solidus_subscriptions.#{event_type}",
358
- subscription: self,
359
- )
415
+ emit_event(type: event_type)
360
416
  end
361
417
 
362
418
  def emit_events_for_update
363
419
  if previous_changes.key?('interval_length') || previous_changes.key?('interval_units')
364
- ::Spree::Event.fire(
365
- 'solidus_subscriptions.subscription_frequency_changed',
366
- subscription: self,
367
- )
420
+ emit_event(type: 'subscription_frequency_changed')
368
421
  end
369
422
 
370
423
  if previous_changes.key?('shipping_address_id')
371
- ::Spree::Event.fire(
372
- 'solidus_subscriptions.subscription_shipping_address_changed',
373
- subscription: self,
374
- )
424
+ emit_event(type: 'subscription_shipping_address_changed')
375
425
  end
376
426
 
377
427
  if previous_changes.key?('billing_address_id')
378
- ::Spree::Event.fire(
379
- 'solidus_subscriptions.subscription_billing_address_changed',
380
- subscription: self,
381
- )
428
+ emit_event(type: 'subscription_billing_address_changed')
382
429
  end
383
430
 
384
431
  if previous_changes.key?('payment_source_id') || previous_changes.key?('payment_source_type') || previous_changes.key?('payment_method_id')
385
- ::Spree::Event.fire(
386
- 'solidus_subscriptions.subscription_payment_method_changed',
387
- subscription: self,
388
- )
432
+ emit_event(type: 'subscription_payment_method_changed')
389
433
  end
390
434
  end
391
435
  end
@@ -3,6 +3,7 @@
3
3
  module SolidusSubscriptions
4
4
  module ChurnBusterSubscriber
5
5
  include ::Spree::Event::Subscriber
6
+ include ::SolidusSupport::LegacyEventCompat::Subscriber
6
7
 
7
8
  event_action :report_subscription_cancellation, event_name: 'solidus_subscriptions.subscription_canceled'
8
9
  event_action :report_subscription_ending, event_name: 'solidus_subscriptions.subscription_ended'
@@ -3,6 +3,7 @@
3
3
  module SolidusSubscriptions
4
4
  module EventStorageSubscriber
5
5
  include ::Spree::Event::Subscriber
6
+ include ::SolidusSupport::LegacyEventCompat::Subscriber
6
7
 
7
8
  event_action :track_subscription_created, event_name: 'solidus_subscriptions.subscription_created'
8
9
  event_action :track_subscription_activated, event_name: 'solidus_subscriptions.subscription_activated'
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusSubscriptions
4
+ module OrderSubscriber
5
+ include ::Spree::Event::Subscriber
6
+ include ::SolidusSupport::LegacyEventCompat::Subscriber
7
+
8
+ event_action :create_subscription, event_name: 'order_finalized'
9
+
10
+ private
11
+
12
+ def create_subscription(event)
13
+ SolidusSubscriptions::CreateSubscriptionJob.perform_later(event.payload[:order])
14
+ end
15
+ end
16
+ end
@@ -23,13 +23,32 @@
23
23
  <% end %>
24
24
 
25
25
  <% if subscription.active? %>
26
- <%=
27
- link_to(
28
- t('spree.admin.subscriptions.actions.skip'),
29
- spree.skip_admin_subscription_path(subscription),
30
- method: :post,
31
- class: 'btn btn-default',
32
- )
33
- %>
26
+ <% if subscription.paused? %>
27
+ <%=
28
+ link_to(
29
+ t('spree.admin.subscriptions.actions.resume'),
30
+ spree.resume_admin_subscription_path(subscription),
31
+ method: :post,
32
+ class: 'btn btn-default',
33
+ )
34
+ %>
35
+ <% else %>
36
+ <%=
37
+ link_to(
38
+ t('spree.admin.subscriptions.actions.pause'),
39
+ spree.pause_admin_subscription_path(subscription),
40
+ method: :post,
41
+ class: 'btn btn-default',
42
+ )
43
+ %>
44
+ <%=
45
+ link_to(
46
+ t('spree.admin.subscriptions.actions.skip'),
47
+ spree.skip_admin_subscription_path(subscription),
48
+ method: :post,
49
+ class: 'btn btn-default',
50
+ )
51
+ %>
52
+ <% end %>
34
53
  <% end %>
35
54
  <% end %>
@@ -6,6 +6,8 @@
6
6
  <nav class="menu">
7
7
  <fieldset class="no-border-top no-border-bottom" data-hook="admin_user_lifetime_stats">
8
8
  <dl id="user-lifetime-stats">
9
+ <dt><%= SolidusSubscriptions::Subscription.human_attribute_name(:user) %>:</dt>
10
+ <dd><%= link_to(subscription.user.email, admin_user_path(subscription.user)) %></dd>
9
11
  <dt><%= SolidusSubscriptions::Subscription.human_attribute_name(:created_at) %>:</dt>
10
12
  <dd><%= l(subscription.created_at.to_date) %></dd>
11
13
  <dt><%= SolidusSubscriptions::Subscription.human_attribute_name(:state) %>:</dt>
@@ -3,8 +3,9 @@
3
3
  canceled: 'error',
4
4
  pending_cancellation: 'warning',
5
5
  inactive: 'inactive',
6
- }[subscription.state.to_sym] %>
6
+ paused: 'pending'
7
+ }[subscription.state_with_pause.to_sym] %>
7
8
 
8
9
  <span class="pill pill-<%= state_class %>">
9
- <%= subscription.human_state_name %>
10
+ <%= subscription.state_with_pause.humanize %>
10
11
  </span>
@@ -48,21 +48,21 @@
48
48
  </div>
49
49
 
50
50
  <div class="row">
51
- <div class="field-block col-4">
51
+ <div class="field-block col-3">
52
52
  <div class="field">
53
53
  <%= label_tag :q_user_email_cont, Spree.user_class.human_attribute_name(:email) %>
54
54
  <%= f.text_field :user_email_cont, size: 25 %>
55
55
  </div>
56
56
  </div>
57
57
 
58
- <div class="field-block col-4">
58
+ <div class="field-block col-3">
59
59
  <div class="field">
60
60
  <%= label_tag :q_state_eq, SolidusSubscriptions::Subscription.human_attribute_name(:state) %>
61
61
  <%= f.select :state_eq, SolidusSubscriptions::Subscription.state_machines[:state].states.map {|s| [s.human_name, s.value]}, {include_blank: true}, class: 'select2 fullwidth' %>
62
62
  </div>
63
63
  </div>
64
64
 
65
- <div class="field-block col-4">
65
+ <div class="field-block col-3">
66
66
  <div class="field">
67
67
  <%= label_tag :q_processing_state_eq, SolidusSubscriptions::Subscription.human_attribute_name(:processing_state) %>
68
68
 
@@ -85,10 +85,23 @@
85
85
  %>
86
86
  </div>
87
87
  </div>
88
+ <div class="field-block col-3">
89
+ <div class="field">
90
+ <%= label_tag :q_with_subscribable_eq, SolidusSubscriptions::Subscription.human_attribute_name(:with_subscribable) %>
91
+
92
+ <%=
93
+ f.select(
94
+ :with_subscribable,
95
+ options_for_select(::Spree::Variant.includes(:product).where(id: ::SolidusSubscriptions::LineItem.distinct.pluck(:subscribable_id)).map { |subscribable| [ subscribable.name, subscribable.id ]}, params.dig(:q, :with_subscribable)),
96
+ { include_blank: true },
97
+ class: 'select2 fullwidth'
98
+ )
99
+ %>
100
+ </div>
101
+ </div>
88
102
  </div>
89
103
 
90
104
  <div class="clearfix"></div>
91
-
92
105
  <div class="actions filter-actions">
93
106
  <div data-hook="admin_subscriptions_index_search_buttons">
94
107
  <%= button_tag I18n.t('spree.filter_results'), class: 'btn btn-primary' %>
@@ -110,6 +123,7 @@
110
123
  <th><%= sort_link(@search, :line_item_interval_length, SolidusSubscriptions::Subscription.human_attribute_name(:interval)) %></th>
111
124
  <th><%= sort_link(@search, :state, [:state, 'id asc'], SolidusSubscriptions::Subscription.human_attribute_name(:state)) %></th>
112
125
  <th><%= sort_link(@search, :processing_state, [:processing_state, 'id asc'], SolidusSubscriptions::Subscription.human_attribute_name(:processing_state)) %></th>
126
+ <th><%= SolidusSubscriptions::Subscription.human_attribute_name(:line_items) %></th>
113
127
  <th data-hook="admin_subscriptions_index_header_actions" class="actions"></th>
114
128
  </tr>
115
129
  </thead>
@@ -123,6 +137,7 @@
123
137
  <td><%= subscription.interval.inspect %></td>
124
138
  <td><%= render 'state_pill', subscription: subscription %></td>
125
139
  <td><%= render 'processing_state_pill', subscription: subscription %></td>
140
+ <td><%= subscription.line_items.includes(subscribable: :product).map { |line_item| line_item.subscribable&.name }.join(", ") %></td>
126
141
  <td class="actions">
127
142
  <% if subscription.state_events.include?(:cancel) %>
128
143
  <%=
@@ -150,18 +165,40 @@
150
165
  <% end %>
151
166
 
152
167
  <% if subscription.active? %>
153
- <%=
154
- link_to_with_icon(
155
- :'fast-forward',
156
- t('spree.admin.subscriptions.actions.skip'),
157
- spree.skip_admin_subscription_path(subscription),
158
- no_text: true,
159
- method: :post
160
- )
161
- %>
168
+ <% if subscription.paused? %>
169
+ <%=
170
+ link_to_with_icon(
171
+ :'play-circle',
172
+ t('spree.admin.subscriptions.actions.resume'),
173
+ spree.resume_admin_subscription_path(subscription),
174
+ no_text: true,
175
+ method: :post
176
+ )
177
+ %>
178
+ <% else %>
179
+ <%=
180
+ link_to_with_icon(
181
+ :'pause-circle',
182
+ t('spree.admin.subscriptions.actions.pause'),
183
+ spree.pause_admin_subscription_path(subscription),
184
+ no_text: true,
185
+ method: :post
186
+ )
187
+ %>
188
+ <%=
189
+ link_to_with_icon(
190
+ :'fast-forward',
191
+ t('spree.admin.subscriptions.actions.skip'),
192
+ spree.skip_admin_subscription_path(subscription),
193
+ no_text: true,
194
+ method: :post
195
+ )
196
+ %>
197
+ <% end %>
162
198
  <% end %>
163
199
 
164
200
  <%= link_to_edit(subscription, no_text: true) %>
201
+
165
202
  </td>
166
203
  </tr>
167
204
  <% end %>
data/bin/sandbox CHANGED
@@ -50,7 +50,6 @@ fi
50
50
  cd ./sandbox
51
51
  cat <<RUBY >> Gemfile
52
52
  gem 'solidus', github: 'solidusio/solidus', branch: '$BRANCH'
53
- gem 'solidus_auth_devise', '>= 2.1.0'
54
53
  gem 'rails-i18n'
55
54
  gem 'solidus_i18n'
56
55
 
@@ -69,14 +68,10 @@ unbundled bundle exec rake db:drop db:create
69
68
 
70
69
  unbundled bundle exec rails generate solidus:install \
71
70
  --auto-accept \
72
- --user_class=Spree::User \
73
- --enforce_available_locales=true \
74
- --with-authentication=false \
75
71
  --payment-method=none \
76
72
  $@
77
73
 
78
- unbundled bundle exec rails generate solidus:auth:install
79
- unbundled bundle exec rails generate ${extension_name}:install
74
+ unbundled bundle exec rails generate ${extension_name}:install --frontend=starter --auto-run-migrations=true
80
75
 
81
76
  echo
82
77
  echo "🚀 Sandbox app successfully created for $extension_name!"
@@ -17,6 +17,13 @@ en:
17
17
  failed: This installment could not be processed
18
18
  payment_failed: The payment for this installment failed
19
19
 
20
+ cart_line_items:
21
+ subscription_fields:
22
+ quantity: I want
23
+ quantity_suffix: items
24
+ interval_length: every
25
+ subscription_fields: Subscription Settings
26
+
20
27
  spree:
21
28
  new_subscription: New Subscription
22
29
  admin:
@@ -24,11 +31,15 @@ en:
24
31
  successfully_canceled: Subscription Canceled!
25
32
  successfully_activated: Subscription Activated!
26
33
  successfully_skipped: Subscription delayed until %{date}
34
+ successfully_paused: Subscription Paused
35
+ successfully_resumed: Subscription Resumed
27
36
  actions:
28
37
  cancel: Cancel
29
38
  cancel_alert: "Are you sure you want to cancel this subscription?"
30
39
  activate: Activate
31
40
  skip: Skip One
41
+ pause: Pause
42
+ resume: Resume
32
43
  index:
33
44
  new_subscription: New Subscription
34
45
  edit:
@@ -127,3 +138,8 @@ en:
127
138
  inclusion: "is not a valid currency code"
128
139
  payment_source:
129
140
  not_owned_by_user: "does not belong to the user associated with the subscription"
141
+ paused:
142
+ cannot_skip: "cannot skip a subscription which is paused"
143
+ not_active: "cannot pause/resume a subscription which is not active"
144
+ state:
145
+ cannot_skip: cannot skip a subscription which is canceled or inactive
data/config/routes.rb CHANGED
@@ -8,6 +8,8 @@ SolidusSubscriptions::Engine.routes.draw do
8
8
  member do
9
9
  post :cancel
10
10
  post :skip
11
+ post :pause
12
+ post :resume
11
13
  end
12
14
  end
13
15
  end
@@ -19,9 +21,13 @@ Spree::Core::Engine.routes.draw do
19
21
 
20
22
  namespace :admin do
21
23
  resources :subscriptions, only: [:index, :new, :create, :edit, :update] do
22
- delete :cancel, on: :member
23
- post :activate, on: :member
24
- post :skip, on: :member
24
+ member do
25
+ delete :cancel
26
+ post :activate
27
+ post :skip
28
+ post :pause
29
+ post :resume
30
+ end
25
31
  resources :installments, only: [:index, :show]
26
32
  resources :subscription_events, only: :index
27
33
  resources :subscription_orders, path: :orders, only: :index