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