Class: SolidusSubscriptions::Subscription

Inherits:
ApplicationRecord
  • Object
show all
Includes:
Interval
Defined in:
app/models/solidus_subscriptions/subscription.rb

Constant Summary collapse

PROCESSING_STATES =
[:pending, :failed, :success].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Interval

included, #interval

Class Method Details

.processing_statesObject



95
96
97
# File 'app/models/solidus_subscriptions/subscription.rb', line 95

def self.processing_states
  PROCESSING_STATES
end

.ransackable_scopes(_auth_object = nil) ⇒ Object



91
92
93
# File 'app/models/solidus_subscriptions/subscription.rb', line 91

def self.ransackable_scopes(_auth_object = nil)
  [:in_processing_state, :with_subscribable]
end

Instance Method Details

#actionable?Boolean

Returns:

  • (Boolean)


305
306
307
# File 'app/models/solidus_subscriptions/subscription.rb', line 305

def actionable?
  actionable_date && actionable_date <= Time.zone.today && ["canceled", "inactive"].exclude?(state)
end

#advance_actionable_dateDate

Advance the actionable date to the next_actionable_date value. Will modify the record.

subscription will be eligible to be processed.

Returns:

  • (Date)

    The next date after the current actionable_date this



215
216
217
218
219
220
221
# File 'app/models/solidus_subscriptions/subscription.rb', line 215

def advance_actionable_date
  create_and_emit_event(type: 'subscription_resumed') if paused?

  update! actionable_date: next_actionable_date, paused: false

  actionable_date
end

#billing_address_to_useObject



275
276
277
# File 'app/models/solidus_subscriptions/subscription.rb', line 275

def billing_address_to_use
  billing_address || user.bill_address
end

#can_be_canceled?Boolean

This method determines if a subscription may be canceled. Canceled subcriptions will not be processed. By default subscriptions may always be canceled. If this method is overridden to return false, the subscription will be moved to the :pending_cancellation state until it is canceled again and this condition is true.

USE CASE: Subscriptions can only be canceled more than 10 days before they are processed. Override this method to be:

def can_be_canceled?

return true if actionable_date.nil?
(actionable_date - 10.days.from_now.to_date) > 0

end

If a user cancels this subscription less than 10 days before it will be processed the subscription will be bumped into the :pending_cancellation state instead of being canceled. Subscriptions pending cancellation will still be processed.

Returns:

  • (Boolean)


159
160
161
162
163
164
# File 'app/models/solidus_subscriptions/subscription.rb', line 159

def can_be_canceled?
  return true if actionable_date.nil?

  cancel_by = actionable_date - SolidusSubscriptions.configuration.minimum_cancellation_notice
  cancel_by.future? || cancel_by.today?
end

#can_be_deactivated?Boolean

This method determines if a subscription can be deactivated. A deactivated subscription will not be processed. By default a subscription can be deactivated if the end_date defined on the subscription is less than the current date In this case the subscription has been fulfilled and should not be processed again. Subscriptions without an end_date value cannot be deactivated.

Returns:

  • (Boolean)


192
193
194
# File 'app/models/solidus_subscriptions/subscription.rb', line 192

def can_be_deactivated?
  active? && end_date && actionable_date && actionable_date > end_date
end

#failing_sinceObject



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'app/models/solidus_subscriptions/subscription.rb', line 279

def failing_since
  failing_details = installment_details.failed.order('solidus_subscriptions_installment_details.created_at ASC')

  last_successful_detail = installment_details
                           .succeeded
                           .order('solidus_subscriptions_installment_details.created_at DESC')
                           .first
  if last_successful_detail
    failing_details = failing_details.where(
      'solidus_subscriptions_installment_details.created_at > ?',
      last_successful_detail.created_at,
    )
  end

  first_failing_detail = failing_details.first

  first_failing_detail&.created_at
end

#maximum_reprocessing_time_reached?Boolean

Returns:

  • (Boolean)


298
299
300
301
302
303
# File 'app/models/solidus_subscriptions/subscription.rb', line 298

def maximum_reprocessing_time_reached?
  return false unless SolidusSubscriptions.configuration.maximum_reprocessing_time
  return false unless failing_since

  Time.zone.now > (failing_since + SolidusSubscriptions.configuration.maximum_reprocessing_time)
end

#next_actionable_dateDate

Get the date after the current actionable_date where this subscription will be actionable again

Returns:

  • (Date)

    The current actionable_date plus 1 interval. The next date after the current actionable_date this subscription will be eligible to be processed.



202
203
204
205
206
207
208
# File 'app/models/solidus_subscriptions/subscription.rb', line 202

def next_actionable_date
  return nil unless active?

  new_date = actionable_date || Time.zone.today

  new_date + interval
end

#pause(actionable_date: nil) ⇒ Object



223
224
225
226
227
228
229
230
231
# File 'app/models/solidus_subscriptions/subscription.rb', line 223

def pause(actionable_date: nil)
  check_invalid_pause_states
  return false if errors.any?
  return true if paused?

  result = update! paused: true, actionable_date: actionable_date && tomorrow_or_after(actionable_date)
  create_and_emit_event(type: 'subscription_paused') if result
  result
end

#payment_method_to_useObject



259
260
261
# File 'app/models/solidus_subscriptions/subscription.rb', line 259

def payment_method_to_use
  payment_method || user.wallet.default_wallet_payment_source&.payment_source&.payment_method
end

#payment_source_to_useObject



263
264
265
266
267
268
269
# File 'app/models/solidus_subscriptions/subscription.rb', line 263

def payment_source_to_use
  if payment_method
    payment_source
  else
    user.wallet.default_wallet_payment_source&.payment_source
  end
end

#processing_stateString

The state of the last attempt to process an installment associated to this subscription

Returns:

  • (String)

    pending if the no installments have been processed, failed if the last installment has not been fulfilled and, success if the last installment was fulfilled.



253
254
255
256
257
# File 'app/models/solidus_subscriptions/subscription.rb', line 253

def processing_state
  return 'pending' if installments.empty?

  installments.last.fulfilled? ? 'success' : 'failed'
end

#resume(actionable_date: nil) ⇒ Object



233
234
235
236
237
238
239
240
241
# File 'app/models/solidus_subscriptions/subscription.rb', line 233

def resume(actionable_date: nil)
  check_invalid_resume_states
  return false if errors.any?
  return true unless paused?

  result = update! paused: false, actionable_date: tomorrow_or_after(actionable_date)
  create_and_emit_event(type: 'subscription_resumed') if result
  result
end

#shipping_address_to_useObject



271
272
273
# File 'app/models/solidus_subscriptions/subscription.rb', line 271

def shipping_address_to_use
  shipping_address || user.ship_address
end

#skip(check_skip_limits: true) ⇒ Object



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'app/models/solidus_subscriptions/subscription.rb', line 166

def skip(check_skip_limits: true)
  check_invalid_skip_states

  if check_skip_limits
    check_successive_skips_exceeded
    check_total_skips_exceeded
  end

  return if errors.any?

  increment(:skip_count)
  increment(:successive_skip_count)
  save!

  advance_actionable_date.tap do
    create_and_emit_event(type: 'subscription_skipped')
  end
end

#state_with_pauseObject



243
244
245
# File 'app/models/solidus_subscriptions/subscription.rb', line 243

def state_with_pause
  active? && paused? ? 'paused' : state
end