podemos-info/census

View on GitHub
app/commands/payments/process_orders_batch.rb

Summary

Maintainability
A
25 mins
Test Coverage
# frozen_string_literal: true

module Payments
  # A command to process a batch of orders
  class ProcessOrdersBatch < Rectify::Command
    # Public: Initializes the command.
    #
    # orders_batch - Orders batch to be processed
    # admin - The person that is processing the orders batch
    def initialize(orders_batch:, admin:)
      @orders_batch = orders_batch
      @admin = admin
    end

    # Executes the command. Broadcasts these events:
    #
    # - :ok when the orders batch was processed.
    # - :invalid when the given data is invalid.
    # - :review if any order in the batch needs to be reviewed before processed.
    #
    # Each order processing broadcasts one of these events and the order itself as a param:
    # - :order_ok when the order was processed ok.
    # - :unprocessable_order when the order can't be processed.
    # - :payment_method_error when the payment method couldn't be saved.
    # - :order_issues when the order was processed but there were issues on this process.
    #
    # Returns nothing.
    def call
      return broadcast(:invalid) unless valid?
      return broadcast(:review) if review?

      orders_batch.update! processed_at: Time.current, processed_by: admin

      result = :ok
      OrdersBatchPaymentProcessors.for(orders_batch).each do |payment_processor|
        processor = Payments::Processor.for(payment_processor)
        processor_result = processor.process_batch(orders_batch: orders_batch, admin: admin) do
          process_orders(processor, orders_batch)
        end
        broadcast("processor_#{processor_result}", processor: payment_processor)
        result = :error unless processor_result == :ok
      end

      broadcast result
    end

    private

    attr_reader :orders_batch, :admin

    def valid?
      orders_batch && admin
    end

    def review?
      OrdersBatchIssues.for(orders_batch).merge(IssuesOpen.for).any?
    end

    def process_orders(processor, orders_batch)
      errors_count = 0
      OrdersBatchPaymentProcessorOrders.for(orders_batch, processor.name).find_each do |order|
        order_result = process_order(processor, order)

        errors_count += 1 if order_result == :order_error

        broadcast(order_result, order: order)

        return false if errors_count >= Settings.payments.orders_batch_processing_errors_limit
      end
      true
    end

    def process_order(processor, order)
      return :unprocessable_order unless order.processable?(inside_batch?: true)

      processor.process_order order: order, admin: admin

      return :order_error unless save_all(order)

      check_issues(order)
    end

    def save_all(order)
      save_payment_method(order) && save_order(order)
    end

    def save_payment_method(order)
      ret = false
      Payments::SavePaymentMethod.call(payment_method: order.payment_method, admin: admin) do
        on(:invalid) { raise Census::Payments::UnrecoverableError, "Invalid payment method information for processed order" }
        on(:error) { raise Census::Payments::UnrecoverableError, "Error saving payment method for processed order" }
        on(:ok) { ret = true }
      end
      ret
    rescue StandardError => e
      Census::Payments.handle_order_unrecoverable_error(order: order, error: e, action: "saving payment method")
      false
    end

    def save_order(order)
      order.save!
      true
    rescue StandardError => e
      Census::Payments.handle_order_unrecoverable_error(order: order, error: e, action: "saving order")
      false
    end

    def check_issues(order)
      ret = nil
      Issues::CheckIssues.call(issuable: order, admin: admin) do
        on(:new_issue) { ret ||= :order_issues }
        on(:existing_issue) { ret ||= :order_issues }
        on(:gone_issue) {}
        on(:ok) {}
        on(:error) do
          CheckPaymentIssuesJob.perform_later(issuable: order, admin: admin)
          ret = :order_issues_error
        end
      end
      ret || :ok
    end
  end
end