Skip to content

Commit

Permalink
Processor also sends reminders for upcoming subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
lsirivong committed Jun 19, 2018
1 parent 67988f3 commit 18f4bcc
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 5 deletions.
25 changes: 25 additions & 0 deletions app/jobs/solidus_subscriptions/subscription_reminder_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# This job is responsible for sending reminders for upcoming subscription
# fulfillments

module SolidusSubscriptions
class SubscriptionReminderJob < ActiveJob::Base
queue_as Config.processing_queue

# Process a collection of subscriptions
#
# @param subscription_ids [Array<Integer>] The ids of the
# subscriptions to be processed together and reminded by the same email
#
# @return [SolidusSubscriptions::Subscription] The subscriptions that were sent reminders
def perform(subscription_ids)
return if subscription_ids.empty?

subscriptions = SolidusSubscriptions::Subscription.where(id: subscription_ids)
subscriptions.each do |subscription|
subscription.remind!
end

subscriptions
end
end
end
12 changes: 11 additions & 1 deletion app/models/solidus_subscriptions/subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ class Subscription < ActiveRecord::Base
where.not(state: ["canceled", "inactive"])
end)

scope :remindable, (lambda do
where("#{table_name}.actionable_date > ? AND #{table_name}.actionable_date <= ? AND reminded = ?", Time.zone.now, Time.zone.now + Config.reminder_notice, false).
where.not(state: ["canceled", "inactive"])
end)

def remind!
send_reminder_email
update!(reminded: true)
end

# Find subscriptions based on their processing state. This state is not a
# model attrubute.
#
Expand Down Expand Up @@ -168,7 +178,7 @@ def next_actionable_date
# @return [Date] The next date after the current actionable_date this
# subscription will be eligible to be processed.
def advance_actionable_date
update! actionable_date: next_actionable_date
update! actionable_date: next_actionable_date, reminded: false
actionable_date
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddRemindedFlagToSolidusSubscriptionsSubscriptions < ActiveRecord::Migration[5.1]
def change
add_column :solidus_subscriptions_subscriptions, :reminded, :boolean, null: false, default: false
end
end
3 changes: 3 additions & 0 deletions lib/solidus_subscriptions/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,8 @@ def default_gateway(&block)
shipping_address_attributes: Spree::PermittedAttributes.address_attributes
]
end

# Time before a subscriptions actionable date to send a reminder email
mattr_accessor(:reminder_notice) { 4.days }
end
end
16 changes: 16 additions & 0 deletions lib/solidus_subscriptions/processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ def initialize(users)
# instance
def build_jobs
users.map do |user|
remindable_subscriptions = remindable_subscriptions(user)
if remindable_subscriptions.any?
SubscriptionReminderJob.perform_later remindable_subscriptions.map(&:id)
end

installemts_by_address_and_user = installments(user).group_by do |i|
i.subscription.shipping_address_id
end
Expand All @@ -77,6 +82,13 @@ def subscriptions_by_id
group_by(&:user_id)
end

def remindable_subscriptions_by_id
@remindable_subscriptions_by_id ||= Subscription.
remindable.
where(user_id: user_ids).
group_by(&:user_id)
end

def retry_installments
@failed_installments ||= Installment.
actionable.
Expand All @@ -85,6 +97,10 @@ def retry_installments
group_by { |i| i.subscription.user_id }
end

def remindable_subscriptions(user)
remindable_subscriptions_by_id.fetch(user.id, [])
end

def installments(user)
@installments[user.id] ||= retry_installments.fetch(user.id, []) + new_installments(user)
end
Expand Down
22 changes: 18 additions & 4 deletions spec/lib/solidus_subscriptions/processor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
end
end

let!(:actionable_subscriptions) { create_list(:subscription, 2, :actionable, user: user) }
let!(:actionable_subscriptions) { create_list(:subscription, 2, :actionable, user: user, reminded: true) }
let!(:pending_cancellation_subscriptions) do
create_list(:subscription, 2, :pending_cancellation, user: user)
end
Expand All @@ -28,9 +28,9 @@
)
end

let!(:future_subscriptions) { create_list(:subscription, 2, :not_actionable) }
let!(:canceled_subscriptions) { create_list(:subscription, 2, :canceled) }
let!(:inactive) { create_list(:subscription, 2, :inactive) }
let!(:future_subscriptions) { create_list(:subscription, 2, :not_actionable, user: user) }
let!(:canceled_subscriptions) { create_list(:subscription, 2, :canceled, user: user) }
let!(:inactive) { create_list(:subscription, 2, :inactive, user: user) }

let!(:successful_installments) { create_list :installment, 2, :success }
let!(:failed_installments) do
Expand Down Expand Up @@ -97,6 +97,20 @@
from('active').to('inactive')
end

it 'marks future subscriptions as reminded' do
reminded = future_subscriptions.first
expect { subject }.
to change { reminded.reload.reminded }.
from(false).to(true)
end

it 'prepares actionable subscriptions to be reminded in the future' do
actionable = actionable_subscriptions.first
expect { subject }.
to change { actionable.reload.reminded }.
from(true).to(false)
end

context 'the subscriptions have different shipping addresses' do
let!(:sub_to_different_address) do
create(:subscription, :actionable, :with_address, user: user)
Expand Down
10 changes: 10 additions & 0 deletions spec/models/solidus_subscriptions/subscription_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -280,4 +280,14 @@
subject { described_class.processing_states }
it { is_expected.to match_array [:pending, :success, :failed] }
end

describe '#send_reminder_email' do
it 'should send a reminder email' do
mail_message = double "Mail::Message"
expect(Spree::SubscriptionMailer).to receive(:reminder_email).with(subject).and_return mail_message
expect(mail_message).to receive :deliver_later

subject.send_reminder_email
end
end
end

0 comments on commit 18f4bcc

Please sign in to comment.