module Returnly module Services class CreateReimbursement attr_accessor :reimbursement delegate :return_items, to: :reimbursement def initialize(reimbursement) @reimbursement = reimbursement end def perform! reimbursement.save! reimbursement.update!(total: calculated_total) performer.perform(reimbursement) if unpaid_amount_within_tolerance? reimbursement.reimbursed! Spree::Reimbursement.reimbursement_success_hooks.each { |h| h.call reimbursement } send_reimbursement_email else reimbursement.errored! Spree::Reimbursement.reimbursement_failure_hooks.each { |h| h.call reimbursement } raise incomplete_error_class, Spree.t('validation.unpaid_amount_not_zero', amount: unpaid_amount) end end private def incomplete_error_class Spree::Reimbursement::IncompleteReimbursementError end def reimbursement_models Spree::Reimbursement.reimbursement_models end def paid_amount reimbursement_models.sum do |model| model.total_amount_reimbursed_for(reimbursement) end end def unpaid_amount reimbursement.total - paid_amount end def calculated_total return_items.to_a.sum(&:total).to_d.round(2, :down) end def performer Spree::Reimbursement.reimbursement_performer end def send_reimbursement_email Spree::ReimbursementMailer.reimbursement_email(reimbursement.id).deliver_later end def unpaid_amount_within_tolerance? reimbursement_count = reimbursement_models.count do |model| model.total_amount_reimbursed_for(reimbursement) > 0 end leniency = if reimbursement_count > 0 (reimbursement_count - 1) * 0.01.to_d else 0 end unpaid_amount.abs.between?(0, leniency) end end end end