2019-07-30 21:29:24 +00:00
# frozen_string_literal: true
2020-06-12 20:03:43 +00:00
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
2018-03-25 17:30:42 +00:00
require 'query/query_recurring_donations'
require 'insert/insert_supporter_notes'
require 'format/date'
require 'format/currency'
module UpdateRecurringDonations
# Update the card id and name for a given recurring donation (provide rd['donation_id'])
def self . update_card_id ( rd , token )
2018-08-07 21:34:58 +00:00
rd = rd & . with_indifferent_access
2019-07-30 21:29:24 +00:00
ParamValidation . new ( { rd : rd , token : token } ,
rd : { is_hash : true , required : true } ,
token : { format : UUID :: Regex , required : true } )
2018-03-25 17:30:42 +00:00
ParamValidation . new ( rd ,
2019-07-30 21:29:24 +00:00
id : { is_reference : true , required : true } )
2018-03-25 17:30:42 +00:00
source_token = QuerySourceToken . get_and_increment_source_token ( token , nil )
tokenizable = source_token . tokenizable
2019-07-30 21:29:24 +00:00
entities = RetrieveActiveRecordItems . retrieve_from_keys ( rd , RecurringDonation = > :id )
2018-03-25 17:30:42 +00:00
validate_entities ( entities [ :id ] , tokenizable )
Qx . transaction do
rec_don = entities [ :id ]
donation = rec_don . donation
2019-07-30 21:29:24 +00:00
# TODO: This is stupid but the two are used together inconsistently. We should scrap one or the other.
2018-03-25 17:30:42 +00:00
donation . card = tokenizable
rec_don . card_id = tokenizable
rec_don . n_failures = 0
rec_don . save!
donation . save!
2019-07-30 21:29:24 +00:00
InsertSupporterNotes . create ( [ { content : " This supporter updated their card for their recurring donation with ID #{ rec_don . id } " , supporter_id : rec_don . supporter . id , user_id : 540 } ] )
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
QueryRecurringDonations . fetch_for_edit ( rd [ :id ] ) [ 'recurring_donation' ]
2018-03-25 17:30:42 +00:00
end
# Update the paydate for a given recurring donation (provide rd['id'])
def self . update_paydate ( rd , paydate )
2019-07-30 21:29:24 +00:00
return ValidationError . new ( [ 'Invalid paydate' ] ) unless ( 1 .. 28 ) . cover? ( paydate . to_i )
Psql . execute ( Qexpr . new . update ( :recurring_donations , paydate : paydate ) . where ( 'id=$id' , id : rd [ 'id' ] ) )
2021-06-07 18:56:03 +00:00
recurring_donation = RecurringDonation . find ( rd [ 'id' ] )
recurring_donation . recurrence . publish_updated
2018-03-25 17:30:42 +00:00
rd [ 'paydate' ] = paydate
2019-07-30 21:29:24 +00:00
rd
2018-03-25 17:30:42 +00:00
end
# @param [RecurringDonation] rd
# @param [String] token
# @param [Integer] amount
def self . update_amount ( rd , token , amount )
2019-07-30 21:29:24 +00:00
ParamValidation . new ( { amount : amount , rd : rd , token : token } ,
amount : { is_integer : true , min : 50 , required : true } ,
rd : { required : true , is_a : RecurringDonation } ,
token : { required : true , format : UUID :: Regex } )
2018-03-25 17:30:42 +00:00
source_token = QuerySourceToken . get_and_increment_source_token ( token , nil )
tokenizable = source_token . tokenizable
validate_entities ( rd , tokenizable )
previous_amount = rd . amount
donation = rd . donation
Qx . transaction do
2019-07-30 21:29:24 +00:00
# TODO: This is stupid but the two are used together inconsistently. We should scrap one or the other.
2018-03-25 17:30:42 +00:00
rd . card = tokenizable
rd . amount = amount
2019-07-30 21:29:24 +00:00
rd . n_failures = 0
2018-03-25 17:30:42 +00:00
donation . card = tokenizable
donation . amount = amount
2021-06-07 18:56:03 +00:00
rd . recurrence . amount = amount
rd . recurrence . save!
rd . recurrence . publish_updated
2018-03-25 17:30:42 +00:00
rd . save!
donation . save!
end
2019-11-07 18:45:57 +00:00
RecurringDonationChangeAmountJob . perform_later ( rd , previous_amount )
2018-03-25 17:30:42 +00:00
rd
end
def self . update_from_start_dates
2019-07-30 21:29:24 +00:00
RecurringDonation . inactive . where ( 'start_date >= ?' , Date . today ) . update_all ( active : true )
2018-03-25 17:30:42 +00:00
end
def self . update_from_end_dates
2019-07-30 21:29:24 +00:00
RecurringDonation . active . where ( 'end_date < ?' , Date . today ) . update_all ( active : false )
2018-03-25 17:30:42 +00:00
end
# Cancel a recurring donation (set active='f') and record the supporter/user email who did it
2019-07-30 21:29:24 +00:00
def self . cancel ( rd_id , email , dont_notify_nonprofit = false )
2018-03-25 17:30:42 +00:00
Psql . execute (
2019-07-30 21:29:24 +00:00
Qexpr . new . update ( :recurring_donations ,
active : false ,
cancelled_by : email ,
cancelled_at : Time . current )
. where ( 'id=$id' , id : rd_id . to_i )
2018-03-25 17:30:42 +00:00
)
rd = QueryRecurringDonations . fetch_for_edit ( rd_id ) [ 'recurring_donation' ]
2021-02-15 23:22:19 +00:00
InsertSupporterNotes . create ( { supporter : Supporter . find ( rd [ 'supporter_id' ] ) , user : nil , content : " This supporter's recurring donation for $ #{ Format :: Currency . cents_to_dollars ( rd [ 'amount' ] ) } was cancelled by #{ rd [ 'cancelled_by' ] } on #{ Format :: Date . simple ( rd [ 'cancelled_at' ] ) } " } )
2019-07-30 21:29:24 +00:00
unless dont_notify_nonprofit
2019-11-07 20:10:22 +00:00
RecurringDonationCancelledJob . perform_later ( Donation . find ( rd [ 'donation_id' ] ) )
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
rd
2018-03-25 17:30:42 +00:00
end
def self . update ( rd , params )
params = set_defaults ( params )
if params [ :donation ]
2020-05-20 21:03:16 +00:00
rd . donation . update ( params [ :donation ] )
2018-03-25 17:30:42 +00:00
return rd . donation unless rd . donation . valid?
2019-07-30 21:29:24 +00:00
2018-03-25 17:30:42 +00:00
params = params . except ( :donation )
end
2020-05-20 21:03:16 +00:00
rd . update ( params )
2019-07-30 21:29:24 +00:00
rd
2018-03-25 17:30:42 +00:00
end
def self . set_defaults ( params )
if params [ :donation ] && params [ :donation ] [ :dollars ]
params [ :donation ] [ :amount ] = Format :: Currency . dollars_to_cents ( params [ :donation ] [ :dollars ] )
params [ :donation ] = params [ :donation ] . except ( :dollars )
params [ :amount ] = params [ :donation ] [ :amount ]
end
if params [ :end_date_str ]
if params [ :end_date_str ] . blank? || params [ :end_date_str ] == 'None'
params [ :end_date ] = nil
else
params [ :end_date ] = Chronic . parse ( params [ :end_date_str ] )
end
params = params . except ( :end_date_str )
end
2019-07-30 21:29:24 +00:00
params
2018-03-25 17:30:42 +00:00
end
# @param [RecurringDonation] rd
# @param [Card] tokenizable
def self . validate_entities ( rd , tokenizable )
2019-07-30 21:29:24 +00:00
if rd . cancelled_at
2018-03-25 17:30:42 +00:00
raise ParamValidation :: ValidationError . new ( " Recurring Donation #{ rd . id } is already cancelled. " , key : :id )
end
2019-07-30 21:29:24 +00:00
if tokenizable . deleted
2018-03-25 17:30:42 +00:00
raise ParamValidation :: ValidationError . new ( " Tokenized card #{ tokenizable . id } is not valid. " , key : :token )
end
if tokenizable . holder != rd . supporter
raise ParamValidation :: ValidationError . new ( " Supporter #{ rd . supporter . id } does not own card #{ tokenizable . id } " , key : :token )
end
end
end