app/models/contribution.rb in artfully_ose-1.2.0 vs app/models/contribution.rb in artfully_ose-1.3.0.pre1

- old
+ new

@@ -1,167 +1,432 @@ class Contribution extend ActiveModel::Naming + extend ::ArtfullyOseHelper include ActiveModel::Conversion + include ActiveModel::Validations - attr_accessor :contributor, - :person_id, - :subtype, #Monetary, In-kind + 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, - :details, - :organization_id, - :creator_id, - :order, - :action - + :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, + 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 self.for(order) - action = GiveAction.where(:subject_id => order.id).where(:subject_type => "Order").first + + 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.order = order contribution.contributor = order.person contribution.occurred_at = order.created_at - contribution.amount = order.items.first.price - contribution.nongift_amount = order.items.first.nongift_amount + 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.subtype = action.subtype - contribution.details = action.details + 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.person_id = contribution.contributor.id + contribution.ack_action = ack_action + contribution.soft_credits = soft_credits + contribution.pledge_payments = pledge_payments end end - def save(order_klass = ApplicationOrder, &block) - @order = build_order(order_klass) + 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! - @order.update_attribute(:created_at, @occurred_at) - @item = build_item(@order, @amount, @nongift_amount) + + @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 has_contributor? - contributor.present? - end - + def update(new_contribution) order = self.order - item = self.order.items.first + item = self.item + donation = self.donation action = self.action - - item.price = new_contribution.amount - item.nongift_amount = new_contribution.nongift_amount - item.realized_price = new_contribution.amount - item.net = new_contribution.amount - - action.details = new_contribution.details + 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 - order.details = new_contribution.details - + 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 - item.save + 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? - @details = params[:details] + @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 - person = Person.find(@person_id) - action = GiveAction.for_organization(self.organization) + 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.details = @details - - action.person = person - action.subject = person - action.creator_id = @creator_id - action.subject = @order + + action.details = action_details(@amount, campaign, @soft_credits, @order.try(:type), @order_parent_id) action end - def build_order(order_klass = ApplicationOrder) + def build_order(order_klass = ApplicationOrder, skip_email = true) attributes = { :person_id => @person_id, :organization_id => @organization_id, - :details => @details + :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 = 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) + def build_item(order, price, nongift_amount = 0, state = 'settled') nongift_amount ||= 0 Item.new({ :order_id => order.id, :product_type => "Donation", - :state => "settled", + :state => state, :price => price, :nongift_amount => nongift_amount, :realized_price => price, :net => price }) end -end \ No newline at end of file + + 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