class Contribution extend ActiveModel::Naming extend ::ArtfullyOseHelper include ActiveModel::Conversion include ActiveModel::Validations require 'set' attr_accessor :contributor, :order, :donation, :item, :action, :ack_action, :soft_credits, :pledge_payments, :person_id, :subtype, #Monetary, In-kind :payment_method, #Cash, Check, Credit card, Other :occurred_at, :notes, :organization_id, :creator_id, :campaign_id, :appeal_id, :check_number, :commitment_date, :ack_sent_date, :ack_method, :match_eligible, :matcher, :matcher_name, :eligible_amount, :ratio, :first_name, :last_name, :email, :company_name, :pledge, :order_parent_id, :fiscal_year # # Standard for the app is (total_donation_amount = amount + nongift_amount) # For example, if someone wrote a check for $100 for event with $25 FMV, amount = $75 and nongift_amount = $25 # attr_accessor :amount, :nongift_amount set_watch_for :occurred_at, :local_to => :organization set_watch_for :commitment_date, :local_to => :organization set_watch_for :ack_sent_date, :local_to => :organization validates :amount, :numericality => { :only_integer => true, :greater_than => 0, :message => '^Total Gift Amount must be greater than $0' } validate :nongift_is_less_than_total validates :eligible_amount, :numericality => { :only_integer => true, :greater_than => 0, :message => '^Match Eligible Amount must be greater than $0', :if => lambda {|u| matcher.to_i > 0} #present eligible_amount if matcher is presented } validates :matcher, :numericality => { :only_integer => true, :greater_than => 0, :message => '^Matcher must be selected to save a match eligible gift', :if => lambda {|u| eligible_amount.to_i > 0} #present matcher if eligible amount is presented } def initialize(params = {}) load(params) @contributor = find_contributor end #hacks to make form_for work def id self.order.try(:id) end def persisted? !self.order.nil? && self.order.persisted? end def organization @organization ||= Organization.find(self.organization_id) end def campaign(campaign_id = self.campaign_id) @campaign ||= campaign_id.present? ? Campaign.find(campaign_id) : nil end def self.for(order) item = order.donations.first donation = Donation.find(item.product_id) action = (donation.pledge ? PledgeAction : GiveAction).where(:subject_id => order.id).where(:subject_type => "Order").first ack_action = HearAction.where(:subject_id => order.id).where(:subject_type => "Order").first if donation.ack_sent_date.present? soft_credits = SoftCredit.find_all_by_donation_id_and_organization_id(donation.id, donation.organization_id) soft_credits = Array.wrap(SoftCredit.default_soft_credit) if soft_credits.empty? pledge_payments = ScheduledPledgePayment.find_all_by_order_id(order.id) pledge_payments = Array.wrap(ScheduledPledgePayment.default_pledge_payment) if pledge_payments.empty? Contribution.new.tap do |contribution| contribution.contributor = order.person contribution.occurred_at = order.created_at contribution.amount = order.donations.first.price + order.donations.first.nongift_amount.to_i contribution.nongift_amount = order.donations.first.nongift_amount contribution.payment_method = order.payment_method contribution.check_number = order.check_number contribution.organization_id = order.organization_id contribution.notes = order.notes contribution.person_id = contribution.contributor.id contribution.subtype = action.subtype contribution.creator_id = action.creator_id contribution.campaign_id = donation.campaign_id contribution.appeal_id = donation.appeal_id contribution.commitment_date = donation.commitment_date || DateTime.now contribution.ack_sent_date = donation.ack_sent_date contribution.ack_method = ack_action.subtype if ack_action.present? contribution.match_eligible = donation.match_eligible contribution.matcher = donation.matcher contribution.matcher_name = Person.find_by_id(donation.matcher) if donation.matcher contribution.eligible_amount = donation.eligible_amount contribution.ratio = donation.ratio contribution.pledge = donation.pledge contribution.fiscal_year = donation.fiscal_year contribution.order = order contribution.donation = donation contribution.item = item contribution.action = action contribution.ack_action = ack_action contribution.soft_credits = soft_credits contribution.pledge_payments = pledge_payments end end def save(order_klass = ApplicationOrder, import_id = 0, skip_email = true, state = 'settled') Order.transaction do @order = build_order(order_klass, skip_email) @order.import_id = import_id if import_id > 0 @order.save! @donation = build_donation @donation.order_id = @order.id @donation.save! @item = build_item(@order, @amount.to_i - @nongift_amount.to_i, @nongift_amount, state) @item.product_id = @donation.id @item.save! if @ack_sent_date.present? @ack_action = build_ack_action(@order, @amount, @ack_sent_date, @ack_method) @ack_action.save! end @action = build_action @action.import_id = import_id if import_id > 0 @action.save! SoftCredit.save_all(@soft_credits, @donation.id, @organization_id) ScheduledPledgePayment.save_all(@pledge_payments, @organization_id, @order.id, @donation.id, @person_id) pledge_for_matching_gift(self, @order.id).save(order_klass, import_id) if @match_eligible && @matcher.to_i > 0 @soft_credits.each do |sc| next if sc.person_id.to_i <= 0 sc_action = sc.build_sc_action(self, @order) sc_action.save! end end yield(self) if block_given? @order end def update(new_contribution) order = self.order item = self.item donation = self.donation action = self.action ack_action = self.ack_action if order.editable? item.price = new_contribution.amount.to_i - new_contribution.nongift_amount.to_i item.nongift_amount = new_contribution.nongift_amount item.realized_price = item.price item.net = item.price end action.details = action_details(new_contribution.amount, campaign(new_contribution.campaign_id), new_contribution.soft_credits, order.type, order.parent_id) action.subtype = new_contribution.subtype action.person_id = new_contribution.person_id action.occurred_at = new_contribution.occurred_at order.person_id = new_contribution.person_id order.created_at = new_contribution.occurred_at order.payment_method = new_contribution.payment_method if !new_contribution.pledge order.notes = new_contribution.notes if !new_contribution.pledge && new_contribution.payment_method == 'Check' order.check_number = new_contribution.check_number end orig_match_eligible = donation.match_eligible && donation.matcher.to_i > 0 db_soft_credits = SoftCredit.find_all_by_donation_id_and_organization_id(donation.id, donation.organization_id) db_pledge_payments = ScheduledPledgePayment.find_all_by_order_id(order.id) donation.amount = new_contribution.amount donation.campaign_id = new_contribution.campaign_id donation.appeal_id = new_contribution.appeal_id donation.created_at = new_contribution.occurred_at donation.commitment_date = new_contribution.commitment_date donation.ack_sent_date = new_contribution.ack_sent_date donation.match_eligible = new_contribution.match_eligible donation.matcher = new_contribution.matcher donation.eligible_amount = new_contribution.eligible_amount donation.ratio = new_contribution.ratio donation.pledge = new_contribution.pledge donation.fiscal_year = new_contribution.fiscal_year if new_contribution.ack_sent_date.present? && ack_action.present? ack_action.occurred_at = new_contribution.ack_sent_date ack_action.subtype = new_contribution.ack_method elsif new_contribution.ack_sent_date.present? ack_action = build_ack_action(order, new_contribution.amount, new_contribution.ack_sent_date, new_contribution.ack_method) end ActiveRecord::Base.transaction do donation.save item.save if order.editable? order.save ack_action.save if ack_action.present? action.save sc_hash = SoftCredit.update_soft_credits(db_soft_credits, new_contribution.soft_credits, donation.id, @organization_id) SoftCredit.update_sc_actions(new_contribution, order, sc_hash) ScheduledPledgePayment.update_pledge_payments(db_pledge_payments, new_contribution.pledge_payments, donation.organization_id, order.id, donation.id, new_contribution.person_id) pledge_for_matching_gift(new_contribution, order.id).save if !orig_match_eligible && donation.match_eligible && donation.matcher.to_i > 0 end true end def pledge_for_matching_gift(contr, order_id) ctb = Contribution.new ctb.creator_id = contr.creator_id ctb.amount = contr.eligible_amount ctb.occurred_at = contr.occurred_at ctb.commitment_date = contr.commitment_date ctb.fiscal_year = contr.fiscal_year ctb.person_id = contr.matcher ctb.order_parent_id = order_id ctb.organization_id = contr.organization_id ctb.campaign_id = contr.campaign_id ctb.appeal_id = contr.appeal_id ctb.subtype = 'Monetary' ctb.pledge = true ctb end def has_contributor? contributor.present? end def receive_pledge(pledge_id, parent_donation) ActiveRecord::Base.transaction do save ScheduledPledgePayment.update(pledge_id, :amount_received => @amount.to_i/100) if pledge_id.to_i > 0 Donation.update(parent_donation.id, :received_amount => parent_donation.received_amount.to_i + self.amount.to_i) unless parent_donation.nil? end end private def load(params) @subtype = params[:subtype] @payment_method = params[:payment_method] @amount = params[:amount] @nongift_amount = params[:nongift_amount] @organization_id = params[:organization_id] @campaign_id = params[:campaign_id] @appeal_id = params[:appeal_id] @occurred_at = ActiveSupport::TimeZone.create(Organization.find(@organization_id).time_zone).parse(params[:occurred_at]) if params[:occurred_at].present? @notes = params[:notes] @person_id = params[:person_id] @creator_id = params[:creator_id] @check_number = params[:check_number] @commitment_date = ActiveSupport::TimeZone.create(Organization.find(@organization_id).time_zone).parse(params[:commitment_date]) if params[:commitment_date].present? @ack_sent_date = ActiveSupport::TimeZone.create(Organization.find(@organization_id).time_zone).parse(params[:ack_sent_date]) if params[:ack_sent_date].present? @ack_method = params[:ack_method] @match_eligible = params[:match_eligible] == 'true' @matcher = params[:matcher] @eligible_amount = params[:eligible_amount] @matcher_name = Person.find_by_id(@matcher) if @match_eligible && @matcher @ratio = params[:ratio] @first_name = params[:first_name] @last_name = params[:last_name] @email = params[:email] @company_name = params[:company_name] @pledge = params[:pledge] == 'true' @soft_credits = params[:soft_credit] ? SoftCredit.build_soft_credits(params[:soft_credit]) : Array.wrap(SoftCredit.default_soft_credit) @pledge_payments = params[:scheduled_pledge_payment] ? ScheduledPledgePayment.build_pledge_payments(params[:scheduled_pledge_payment]) : Array.wrap(ScheduledPledgePayment.default_pledge_payment) @fiscal_year = params[:fiscal_year] end def find_contributor Person.find(@person_id) unless @person_id.blank? end def build_donation donation = Donation.new donation.amount = @amount donation.organization_id = @organization_id donation.campaign_id = @campaign_id donation.appeal_id = @appeal_id donation.created_at = @occurred_at donation.commitment_date = @commitment_date donation.ack_sent_date = @ack_sent_date donation.match_eligible = @match_eligible donation.matcher = @matcher donation.eligible_amount = @eligible_amount donation.ratio = @ratio donation.pledge = @pledge donation.fiscal_year = @fiscal_year donation end def build_action action = (@pledge ? PledgeAction : GiveAction).for_organization(self.organization) action.person = Person.find(@person_id) action.subject = @order action.occurred_at = @occurred_at action.subtype = @subtype action.creator_id = @creator_id action.details = action_details(@amount, campaign, @soft_credits, @order.try(:type), @order_parent_id) action end def build_order(order_klass = ApplicationOrder, skip_email = true) attributes = { :person_id => @person_id, :organization_id => @organization_id, :notes => @notes } order = order_klass.new(attributes).tap do |order| order.payment_method = @payment_method order.creator_id = @creator_id order.created_at = @occurred_at order.skip_actions = true order.skip_email = skip_email order.parent_id = @order_parent_id if @order_parent_id.present? if !@pledge order.payment_method = @payment_method if @payment_method == 'Check' order.check_number = @check_number end end end end def build_item(order, price, nongift_amount = 0, state = 'settled') nongift_amount ||= 0 Item.new({ :order_id => order.id, :product_type => "Donation", :state => state, :price => price, :nongift_amount => nongift_amount, :realized_price => price, :net => price }) end def action_details(amount, campaign, soft_credits, order_type, order_parent_id = 0) details = '' if order_parent_id.to_i > 0 parent_order = Order.find(order_parent_id) unless parent_order.nil? || (order_type.eql? 'RefundOrder') || (order_type.eql? 'ConvertOrder') details << "to match " << parent_order.person.to_s << "'s " end end details << Contribution.number_as_cents(amount) details << ' for ' << campaign.name if campaign sc_person_names = [] soft_credits.each do |sc| next if sc.person_id.to_i <= 0 sc_person_names << Person.find(sc.person_id) end details << ' (' << sc_person_names.to_sentence << ' received soft credit)' if sc_person_names.any? details end def build_ack_action(order, amount, ack_sent_date, ack_method) action = HearAction.for_organization(self.organization) action.person = order.person action.subject = order action.subtype = ack_method action.occurred_at = ack_sent_date action.creator_id = @creator_id action.details = "Gift acknowledgement sent for #{Contribution.number_as_cents(amount)} #{order.gift_type.downcase}. Order ##{order.id}" action end def nongift_is_less_than_total errors.add(:nongift_amount, "^Nondeductible Amount can not exceed Total Gift Amount") if amount.to_i > 0 && nongift_amount.to_i > amount.to_i end end