summaryrefslogtreecommitdiff
path: root/lib/solidus_subscriptions/processor.rb
blob: 97b39dfc1079e814f29cd815f7b1fd1e1f0d61de (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# This class is responsible for finding subscriptions and installments
# which need to be processed. It will group them together by user and attempts
# to process them together.
#
# This class passes the reponsibility of actually creating the order off onto
# the consolidated installment class.
#
# This class generates `ProcessInstallmentsJob`s
module SolidusSubscriptions
  class Processor
    class << self
      # Find all actionable subscriptions and intallments, group them together
      # by user, and schedule a processing job for the group as a batch
      def run
        batched_users_to_be_processed.each { |batch| new(batch).build_jobs }
      end

      private

      def batched_users_to_be_processed
        subscriptions = Subscription.arel_table
        installments = Installment.arel_table

        Spree::User.
          joins(:subscriptions).
          joins(
            subscriptions.
              join(installments, Arel::Nodes::OuterJoin).
              on(subscriptions[:id].eq(installments[:subscription_id])).
              join_sources
          ).
          where(
            Subscription.actionable.arel.constraints.reduce(:and).
              or(Installment.actionable.arel.constraints.reduce(:and))
          ).
          distinct.
          find_in_batches
      end
    end

    # @return [Array<Spree.user_class>]
    attr_reader :users

    # Get a new instance of the SolidusSubscriptions::Processor
    #
    # @param users [Array<Spree.user_class>] A list of users with actionable
    #   subscriptions or installments
    #
    # @return [SolidusSubscriptions::Processor]
    def initialize(users)
      @users = users
      @installments = {}
    end

    # Create `ProcessInstallmentsJob`s for the users used to initalize the
    # instance
    def build_jobs
      users.map { |user| ProcessInstallmentsJob.perform_later installments(user) }
    end

    private

    def subscriptions_by_id
      @subscriptions_by_id ||= Subscription.
        actionable.
        where(user_id: user_ids).
        group_by(&:user_id)
    end

    def retry_installments
      @failed_installments ||= Installment.
        actionable.
        includes(:subscription).
        where(solidus_subscriptions_subscriptions: { user_id: user_ids }).
        group_by { |i| i.subscription.user_id }
    end

    def installments(user)
      @installments[user.id] ||= retry_installments.fetch(user.id, []) + new_installments(user)
    end

    def new_installments(user)
      subscriptions_by_id.fetch(user.id, []).map { |sub| sub.installments.create! }
    end

    def user_ids
      @user_ids ||= users.map(&:id)
    end
  end
end