Skip to content

Commit

Permalink
Streamline and simplify SolidusSubscriptions::Checkout
Browse files Browse the repository at this point in the history
This service object contained a lot of indirection and took on too
many responsibilities. The new version is much more streamlined and
the flow of operations should be much clearer.
  • Loading branch information
aldesantis committed Nov 19, 2020
1 parent c26452e commit 21dbc3a
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 495 deletions.
2 changes: 1 addition & 1 deletion app/jobs/solidus_subscriptions/process_installment_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class ProcessInstallmentJob < ApplicationJob
queue_as SolidusSubscriptions.configuration.processing_queue

def perform(installment)
Checkout.new([installment]).process
Checkout.new(installment).process
end
end
end
181 changes: 59 additions & 122 deletions app/services/solidus_subscriptions/checkout.rb
Original file line number Diff line number Diff line change
@@ -1,155 +1,92 @@
# frozen_string_literal: true

# This class takes a collection of installments and populates a new spree
# order with the correct contents based on the subscriptions associated to the
# intallments. This is to group together subscriptions being
# processed on the same day for a specific user
module SolidusSubscriptions
class Checkout
# @return [Array<Installment>] The collection of installments to be used
# when generating a new order
attr_reader :installments
attr_reader :installment

delegate :user, to: :subscription

# Get a new instance of a Checkout
#
# @param installments [Array<Installment>] The collection of installments
# to be used when generating a new order
def initialize(installments)
@installments = installments
raise UserMismatchError.new(installments) if different_owners?
def initialize(installment)
@installment = installment
end

# Generate a new Spree::Order based on the information associated to the
# installments
#
# @return [Spree::Order]
def process
populate

# Installments are removed and set for future processing if they are
# out of stock. If there are no line items left there is nothing to do
return if installments.empty?

if checkout
SolidusSubscriptions.configuration.success_dispatcher_class.new(installments, order).dispatch
return order
end
order = create_order
populate_order(order)

if finalize_order(order)
SolidusSubscriptions
.configuration
.success_dispatcher_class
.new([installment], order)
.dispatch

order
elsif order.payments.any?(&:failed?)
SolidusSubscriptions
.configuration
.payment_failed_dispatcher_class
.new([installment], order)
.dispatch

# A new order will only have 1 payment that we created
if order.payments.any?(&:failed?)
SolidusSubscriptions.configuration.payment_failed_dispatcher_class.new(installments, order).dispatch
installments.clear
nil
end
ensure
# Any installments that failed to be processed will be reprocessed
unfulfilled_installments = installments.select(&:unfulfilled?)
if unfulfilled_installments.any?
SolidusSubscriptions.configuration.failure_dispatcher_class.
new(unfulfilled_installments, order).dispatch
unless installment.fulfilled?
SolidusSubscriptions
.configuration
.failure_dispatcher_class
.new([installment], order)
.dispatch
end
end

# The order fulfilling the consolidated installment
#
# @return [Spree::Order]
def order
@order ||= ::Spree::Order.create(
user: user,
email: user.email,
store: subscription.store || ::Spree::Store.default,
private

def create_order
::Spree::Order.create(
user: installment.subscription.user,
email: installment.subscription.user.email,
store: installment.subscription.store || ::Spree::Store.default,
subscription_order: true,
subscription: subscription
subscription: installment.subscription
)
end

private
def populate_order(order)
line_items = installment.line_item_builder.spree_line_items

if line_items.empty?
SolidusSubscriptions
.configuration
.out_of_stock_dispatcher_class
.new([installment])
.dispatch
end

OrderBuilder.new(order).add_line_items(line_items)
order.recalculate
end

def checkout
def finalize_order(order)
::Spree::PromotionHandler::Cart.new(order).activate
order.recalculate
apply_promotions

order.checkout_steps[0...-1].each do
case order.state
when "address"
order.ship_address = ship_address
order.bill_address = bill_address
when "payment"
create_payment
when 'address'
order.ship_address = installment.subscription.shipping_address_to_use
order.bill_address = installment.subscription.billing_address_to_use
when 'payment'
order.payments.create(
payment_method: installment.subscription.payment_method_to_use,
source: installment.subscription.payment_source_to_use,
amount: order.total,
)
end

order.next!
end

# Do this as a separate "quiet" transition so that it returns true or
# false rather than raising a failed transition error
order.complete
end

def populate
unfulfilled_installments = []

order_line_items = installments.flat_map do |installment|
line_items = installment.line_item_builder.spree_line_items

unfulfilled_installments.push(installment) if line_items.empty?

line_items
end

# Remove installments which had no stock from the active list
# They will be reprocessed later
@installments -= unfulfilled_installments
if unfulfilled_installments.any?
SolidusSubscriptions.configuration.out_of_stock_dispatcher_class.new(unfulfilled_installments).dispatch
end

return if installments.empty?

order_builder.add_line_items(order_line_items)
end

def order_builder
@order_builder ||= OrderBuilder.new(order)
end

def subscription
installments.first.subscription
end

def ship_address
subscription.shipping_address_to_use
end

def bill_address
subscription.billing_address_to_use
end

def payment_source
subscription.payment_source_to_use
end

def payment_method
subscription.payment_method_to_use
end

def create_payment
order.payments.create(
source: payment_source,
amount: order.total,
payment_method: payment_method,
)
end

def apply_promotions
::Spree::PromotionHandler::Cart.new(order).activate
order.updater.update # reload totals
end

def different_owners?
installments.map { |i| i.subscription.user }.uniq.length > 1
end
end
end
Loading

0 comments on commit 21dbc3a

Please sign in to comment.