From 9f880c75af0724a40230fe74ef9013db056a7c8b Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Sat, 9 Mar 2024 22:52:42 -0800 Subject: [PATCH 1/9] change 'donation_type' variable in donate view to 'donation_frequency' for clarity and consistency --- app/views/store/donate.html.haml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/store/donate.html.haml b/app/views/store/donate.html.haml index 33b877d89..b8be754f2 100644 --- a/app/views/store/donate.html.haml +++ b/app/views/store/donate.html.haml @@ -46,9 +46,9 @@ .form-group.form-row %label.col-form-label.text-right.col-sm-6{:for => :donation} Donation frequency .radio-group.col-sm-6.col-md-2.form-inline - = radio_button_tag 'donation_type', 'one', Option.default_donation_type == 'one', class: 'form-control', id: 'one' + = radio_button_tag 'donation_frequency', 'one', Option.default_donation_type == 'one', class: 'form-control', id: 'one' = label_tag 'donation_type_one_time', 'One Time', class: 'form-control', for: 'one' - = radio_button_tag 'donation_type', 'monthly', Option.default_donation_type == 'monthly', class: 'form-control', id: 'monthly' + = radio_button_tag 'donation_frequency', 'monthly', Option.default_donation_type == 'monthly', class: 'form-control', id: 'monthly' = label_tag 'donation_type_monthly', 'Monthly', class: 'form-control', for: 'monthly' .form-group.form-row %label.col-form-label{:for => :donation_comments} From dcccbc6e294f13982a5cd1c87eab75ce79d14488 Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Sat, 9 Mar 2024 23:20:27 -0800 Subject: [PATCH 2/9] create recurring_donation data modeling --- app/models/items/donation.rb | 7 ++--- app/models/items/recurring_donation.rb | 13 +++++++++ app/models/order.rb | 27 ++++++++++--------- ...0070441_add_recurring_donation_to_items.rb | 6 +++++ db/schema.rb | 4 +-- 5 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 app/models/items/recurring_donation.rb create mode 100644 db/migrate/20240310070441_add_recurring_donation_to_items.rb diff --git a/app/models/items/donation.rb b/app/models/items/donation.rb index de350c3c3..aea238b9f 100644 --- a/app/models/items/donation.rb +++ b/app/models/items/donation.rb @@ -6,13 +6,14 @@ class Donation < Item def self.default_code AccountCode.find(Option.default_donation_account_code) end - + belongs_to :account_code validates_associated :account_code validates_presence_of :account_code_id - + belongs_to :customer - + belongs_to :recurring_donation, class_name: 'RecurringDonation', foreign_key: :recurring_donation_id + validates_numericality_of :amount validates_inclusion_of :amount, :in => 1..10_000_000, :message => "must be at least 1 dollar" diff --git a/app/models/items/recurring_donation.rb b/app/models/items/recurring_donation.rb new file mode 100644 index 000000000..341d14e92 --- /dev/null +++ b/app/models/items/recurring_donation.rb @@ -0,0 +1,13 @@ +class RecurringDonation < Item + + belongs_to :account_code + belongs_to :customer + has_many :donations, foreign_key: :recurring_donation_id + + validates_associated :account_code + validates_presence_of :account_code_id + + validates_numericality_of :amount + validates_inclusion_of :amount, :in => 1..10_000_000, :message => "must be at least 1 dollar" + +end diff --git a/app/models/order.rb b/app/models/order.rb index df8243e47..24b30c00d 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -5,6 +5,7 @@ class Order < ActiveRecord::Base has_many :items, :autosave => true, :dependent => :destroy has_many :vouchers, :autosave => true, :dependent => :destroy has_many :donations, :autosave => true, :dependent => :destroy + has_many :recurring_donations, :autosave => true, :dependent => :destroy has_many :retail_items, :autosave => true, :dependent => :destroy attr_accessor :purchase_args @@ -14,7 +15,7 @@ class Order < ActiveRecord::Base # pending and errored states of a CC order (pending = payment has occurred but order has not # yet been finalized; errored = payment has occurred and order NEVER got finalized, eg due # to server timeout or other event that happens during that step) - + PENDING = 'pending' # string in authorization field that marks in-process CC order ERRORED = 'errored' # payment made, but timeout/something bad happened that prevented order finalization @@ -76,7 +77,7 @@ def check_purchaser_info scope :completed, ->() { where('sold_on IS NOT NULL') } scope :abandoned_since, ->(since) { where('sold_on IS NULL').where('updated_at < ?', since) } scope :pending_but_paid, ->() { where(:authorization => PENDING) } - + scope :for_customer_reporting, ->() { includes(:vouchers => [:customer, :showdate,:vouchertype]). includes(:donations => [:customer, :account_code]). @@ -98,7 +99,7 @@ def customer_name ; customer.full_name ; end def purchaser_name ; purchaser.full_name ; end def purchase_medium ; Purchasemethod.get(purchasemethod).purchase_medium ; end - + def self.new_from_donation(amount, account_code, donor) order = Order.new(:purchaser => donor, :customer => donor) order.add_donation(Donation.from_amount_and_account_code_id(amount, account_code.id)) @@ -185,10 +186,10 @@ def add_tickets_without_capacity_checks(valid_voucher, number, seats=[]) self.vouchers += new_vouchers self.save! else # since order can't proceed, DESTROY all vouchers so not orphaned - new_vouchers.each { |v| v.destroy if v.persisted? } + new_vouchers.each { |v| v.destroy if v.persisted? } end end - + def ticket_count ; vouchers.size ; end def item_count ; ticket_count + (includes_donation? ? 1 : 0) + retail_items.size; end @@ -221,7 +222,7 @@ def reserved_seating_params nil end end - + def add_donation(d) ; self.donation = d ; end def donation=(d) self.donation_data[:amount] = d.amount @@ -244,7 +245,7 @@ def includes_bundle? vouchers.any? { |v| v.bundle? } end end - + def add_retail_item(r) raise Order::NotPersistedError unless persisted? self.retail_items << r if r @@ -312,7 +313,7 @@ def streaming_access_instructions # this is almost certainly a Demeter violation...but not sure how to make better vouchers.first.showdate.access_instructions end - + def collect_notes # collect the showdate-specific (= show-specific) notes in the order items.map(&:showdate).compact.uniq.map(&:patron_notes).compact @@ -357,7 +358,7 @@ def finalize_with_existing_customer_id!(cid,processed_by,sold_on=Time.current) self.processed_by = processed_by self.finalize!(sold_on) end - + def finalize_with_new_customer!(customer,processed_by,sold_on=Time.current) customer.force_valid = true self.customer = self.purchaser = customer @@ -413,7 +414,7 @@ def finalize!(sold_on_date = Time.current) raise e end end - + def refundable? completed? && items.any? { |i| !i.kind_of?(CanceledItem) } # in case all items were ALREADY refunded and now marked as canceled @@ -451,13 +452,13 @@ def item_descriptions end def summary_of_contents - if includes_vouchers? && includes_donation? - 'order and donation' + if includes_vouchers? && includes_donation? + 'order and donation' elsif includes_donation? 'donation' else 'order' end - end + end end diff --git a/db/migrate/20240310070441_add_recurring_donation_to_items.rb b/db/migrate/20240310070441_add_recurring_donation_to_items.rb new file mode 100644 index 000000000..90a3ced90 --- /dev/null +++ b/db/migrate/20240310070441_add_recurring_donation_to_items.rb @@ -0,0 +1,6 @@ +class AddRecurringDonationToItems < ActiveRecord::Migration + def change + add_column :items, :recurring_donation_id, :integer + add_foreign_key :items, :items, column: :recurring_donation_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 53308aeae..4d4bd9f28 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,8 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20240225093946) do - +ActiveRecord::Schema.define(version: 20240310070441) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -97,6 +96,7 @@ t.boolean "finalized" t.string "seat" t.datetime "sold_on" + t.integer "recurring_donation_id" end add_index "items", ["account_code_id"], name: "index_items_on_account_code_id", using: :btree From 53a69cc91e2e8a97d7f608372b4bcf12c62c9197 Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Sun, 10 Mar 2024 03:58:23 -0700 Subject: [PATCH 3/9] add additional associations to existing models --- app/models/account_code.rb | 7 ++++--- app/models/customer.rb | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/models/account_code.rb b/app/models/account_code.rb index 16060d850..db09ac553 100644 --- a/app/models/account_code.rb +++ b/app/models/account_code.rb @@ -12,6 +12,7 @@ def donation_prompt ; '' ; end default_scope { order('code') } has_many :donations + has_many :recurring_donations has_many :vouchertypes validates_length_of :name, :maximum => 255, :allow_nil => true @@ -28,7 +29,7 @@ def name_or_code_given def self.default_account_code_id self.default_account_code.id end - + def self.default_account_code AccountCode.first end @@ -42,12 +43,12 @@ def <=>(other) end class CannotDelete < RuntimeError ; end - + # convenience accessors def name_or_code ; name.blank? ? code : name ; end def name_with_code ; sprintf("%-6.6s %s", code, name) ; end - + # cannot delete the last account code or the one associated as any # of the defaults diff --git a/app/models/customer.rb b/app/models/customer.rb index eaf1c4fd4..6724c2ea9 100644 --- a/app/models/customer.rb +++ b/app/models/customer.rb @@ -25,7 +25,7 @@ class Customer < ActiveRecord::Base def active_vouchers vouchers.select { |v| Time.current <= Time.at_end_of_season(v.season) } end - + has_many :vouchertypes, :through => :vouchers has_many :showdates, :through => :vouchers has_many :orders, -> { where( 'orders.sold_on IS NOT NULL').order(:sold_on => :desc) } @@ -37,6 +37,7 @@ def shows ; self.showdates.map(&:show).uniq ; end has_many :txns has_one :most_recent_txn, -> { order('txn_date DESC') }, :class_name=>'Txn' has_many :donations + has_many :recurring_donations has_many :retail_items has_many :items # the superclass of vouchers,donations,retail_items @@ -61,7 +62,7 @@ def restricted_email !email.blank? && !email.match( /#{domain}\z/i ) end validate :restricted_email, :if => :self_created?, :on => :create - + EMAIL_UNIQUENESS_ERROR_MESSAGE = 'has already been registered.' validates_uniqueness_of :email, :allow_blank => true, @@ -427,7 +428,7 @@ def self.csv_header ['First name', 'Last name', 'ID', 'Email', 'Street', 'City', 'State', 'Zip', 'Day/main phone', 'Eve/alt phone', "Don't mail", "Don't email"].freeze end - + def self.to_csv(custs,opts={}) CSV::Writer.generate(output='') do |csv| unless opts[:suppress_header] From 609c820439bd52a2cbc5a095dec361666a7a0652 Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Sun, 10 Mar 2024 04:00:05 -0700 Subject: [PATCH 4/9] add method to create new record from account_code and overwrite 'must overwrite' methods --- app/models/items/recurring_donation.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/models/items/recurring_donation.rb b/app/models/items/recurring_donation.rb index 341d14e92..a4d0d6b9a 100644 --- a/app/models/items/recurring_donation.rb +++ b/app/models/items/recurring_donation.rb @@ -7,7 +7,10 @@ class RecurringDonation < Item validates_associated :account_code validates_presence_of :account_code_id - validates_numericality_of :amount - validates_inclusion_of :amount, :in => 1..10_000_000, :message => "must be at least 1 dollar" + def self.from_account_code(account_code) + RecurringDonation.new(:amount => 0, :account_code => account_code) + end + def one_line_description ; end + def description_for_audit_txn ; end end From 4ef73592aaade8e6ae2952efea0b04a1e7e64334 Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Sun, 10 Mar 2024 04:00:42 -0700 Subject: [PATCH 5/9] pass donation_frequency to order model --- app/controllers/store_controller.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/store_controller.rb b/app/controllers/store_controller.rb index f47dfa590..0c6acade2 100644 --- a/app/controllers/store_controller.rb +++ b/app/controllers/store_controller.rb @@ -1,13 +1,13 @@ class StoreController < ApplicationController include StoreHelper - + skip_before_filter :verify_authenticity_token, :only => %w(show_changed showdate_changed) before_filter :set_customer, :except => %w[process_donation] before_filter :is_logged_in, :only => %w[checkout place_order] before_filter :order_is_not_empty, :only => %w[shipping_address checkout place_order] - + # ACTION INVARIANT BEFORE ACTION # ------ ----------------------- # index, subscribe, donate_to_fund valid @customer @@ -41,7 +41,7 @@ def set_customer redirect_customer = resolve_customer_in_url(logged_in_user, specified_customer) if redirect_customer == specified_customer # ok to proceed as is @customer = specified_customer - else + else redirect_to url_for(params.merge(:customer_id => redirect_customer.id, :only_path => true)) end end @@ -133,7 +133,7 @@ def process_donation redirect_route = quick_donate_path(:customer_id => @customer.id, :donation => @amount) @amount > 0 or return redirect_to(redirect_route, :alert => 'Donation amount must be provided') # Given valid donation, customer, and charge token, create & place credit card order. - @gOrderInProgress = Order.new_from_donation(@amount, Donation.default_code, @customer) + @gOrderInProgress = Order.new_from_donation(@amount, Donation.default_code, @customer, params[:donation_frequency]) @gOrderInProgress.purchasemethod = Purchasemethod.get_type_by_name('web_cc') @gOrderInProgress.purchase_args = {:credit_card_token => params[:credit_card_token]} @gOrderInProgress.processed_by = @customer @@ -195,10 +195,10 @@ def shipping_address recipient = recipient_from_params(customer_params) @recipient = recipient[0] if @recipient.email == @customer.email - flash.now[:alert] = I18n.t('store.errors.gift_diff_email_notice') + flash.now[:alert] = I18n.t('store.errors.gift_diff_email_notice') render :action => :shipping_address return - end + end if Customer.email_matches_diff_last_name?(try_customer) flash.now[:alert] = I18n.t('store.errors.gift_matching_email_diff_last_name') render :action => :shipping_address @@ -219,7 +219,7 @@ def shipping_address if Customer.email_last_name_match_diff_address?(try_customer) flash[:notice] = I18n.t('store.gift_matching_email_last_name_diff_address') elsif recipient_from_params(customer_params)[1] == "found_matching_customer" - flash[:notice] = I18n.t('store.gift_recipient_on_file') + flash[:notice] = I18n.t('store.gift_recipient_on_file') end redirect_to_checkout end From 1c0414b276b2104bd7bb701c3c8d1b8a19ae1663 Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Sun, 10 Mar 2024 04:01:43 -0700 Subject: [PATCH 6/9] create new recurring donation record when appropriate --- app/models/order.rb | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/models/order.rb b/app/models/order.rb index 24b30c00d..adc8db538 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -11,6 +11,7 @@ class Order < ActiveRecord::Base attr_accessor :purchase_args attr_accessor :comments attr_reader :donation + attr_reader :recurring_donation # pending and errored states of a CC order (pending = payment has occurred but order has not # yet been finalized; errored = payment has occurred and order NEVER got finalized, eg due @@ -100,9 +101,16 @@ def purchaser_name ; purchaser.full_name ; end def purchase_medium ; Purchasemethod.get(purchasemethod).purchase_medium ; end - def self.new_from_donation(amount, account_code, donor) + def self.new_from_donation(amount, account_code, donor, donation_frequency) order = Order.new(:purchaser => donor, :customer => donor) - order.add_donation(Donation.from_amount_and_account_code_id(amount, account_code.id)) + if donation_frequency == 'monthly' + recurring_donation = RecurringDonation.from_account_code(account_code) + order.add_recurring_donation(recurring_donation) + donation = recurring_donation.donations.new(amount: amount, account_code: account_code) + else + donation = Donation.from_amount_and_account_code_id(amount, account_code.id) + end + order.add_donation(donation) order end @@ -238,6 +246,11 @@ def includes_donation? end end + def add_recurring_donation(rd) ; self.recurring_donation = rd ; end + def recurring_donation=(rd) + @recurring_donation = rd + end + def includes_bundle? if completed? items.any? { |v| v.kind_of?(Voucher) && v.bundle? } @@ -386,6 +399,7 @@ def finalize!(sold_on_date = Time.current) self.items += vouchers self.items += retail_items self.items << donation if donation + self.items << recurring_donation if recurring_donation self.items.each do |i| i.assign_attributes(:finalized => true, :sold_on => sold_on_date, @@ -398,6 +412,7 @@ def finalize!(sold_on_date = Time.current) customer.add_items(vouchers) customer.add_items(retail_items) purchaser.add_items([donation]) if donation + purchaser.add_items([recurring_donation]) if recurring_donation customer.save! purchaser.save! self.sold_on = sold_on_date From 37be5713efe5ea9763d2a55af2932a42660b0ea7 Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Mon, 11 Mar 2024 08:12:30 -0700 Subject: [PATCH 7/9] refactor code that creates recurring donation record --- app/controllers/store_controller.rb | 3 ++- app/models/items/recurring_donation.rb | 7 ------- app/models/order.rb | 16 ++++------------ 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/app/controllers/store_controller.rb b/app/controllers/store_controller.rb index 0c6acade2..e63061186 100644 --- a/app/controllers/store_controller.rb +++ b/app/controllers/store_controller.rb @@ -133,7 +133,8 @@ def process_donation redirect_route = quick_donate_path(:customer_id => @customer.id, :donation => @amount) @amount > 0 or return redirect_to(redirect_route, :alert => 'Donation amount must be provided') # Given valid donation, customer, and charge token, create & place credit card order. - @gOrderInProgress = Order.new_from_donation(@amount, Donation.default_code, @customer, params[:donation_frequency]) + @gOrderInProgress = Order.new_from_donation(@amount, Donation.default_code, @customer) + @gOrderInProgress.add_recurring_donation() if params[:donation_frequency] == 'monthly' @gOrderInProgress.purchasemethod = Purchasemethod.get_type_by_name('web_cc') @gOrderInProgress.purchase_args = {:credit_card_token => params[:credit_card_token]} @gOrderInProgress.processed_by = @customer diff --git a/app/models/items/recurring_donation.rb b/app/models/items/recurring_donation.rb index a4d0d6b9a..f4f50e5b4 100644 --- a/app/models/items/recurring_donation.rb +++ b/app/models/items/recurring_donation.rb @@ -4,13 +4,6 @@ class RecurringDonation < Item belongs_to :customer has_many :donations, foreign_key: :recurring_donation_id - validates_associated :account_code - validates_presence_of :account_code_id - - def self.from_account_code(account_code) - RecurringDonation.new(:amount => 0, :account_code => account_code) - end - def one_line_description ; end def description_for_audit_txn ; end end diff --git a/app/models/order.rb b/app/models/order.rb index adc8db538..4357cc2af 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -101,16 +101,9 @@ def purchaser_name ; purchaser.full_name ; end def purchase_medium ; Purchasemethod.get(purchasemethod).purchase_medium ; end - def self.new_from_donation(amount, account_code, donor, donation_frequency) + def self.new_from_donation(amount, account_code, donor) order = Order.new(:purchaser => donor, :customer => donor) - if donation_frequency == 'monthly' - recurring_donation = RecurringDonation.from_account_code(account_code) - order.add_recurring_donation(recurring_donation) - donation = recurring_donation.donations.new(amount: amount, account_code: account_code) - else - donation = Donation.from_amount_and_account_code_id(amount, account_code.id) - end - order.add_donation(donation) + order.add_donation(Donation.from_amount_and_account_code_id(amount, account_code.id)) order end @@ -246,9 +239,8 @@ def includes_donation? end end - def add_recurring_donation(rd) ; self.recurring_donation = rd ; end - def recurring_donation=(rd) - @recurring_donation = rd + def add_recurring_donation() + @recurring_donation = @donation.build_recurring_donation(amount: 0, account_code_id: @donation.account_code_id) end def includes_bundle? From 3d4bed0db1630241d20794434282fee9cb495c6f Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Thu, 14 Mar 2024 00:18:12 -0700 Subject: [PATCH 8/9] declare constant enumerable of possible donation frequencies used to serve view and process donation --- app/controllers/store_controller.rb | 8 +++++++- app/views/store/donate.html.haml | 7 +++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/controllers/store_controller.rb b/app/controllers/store_controller.rb index e63061186..a75fb0ee4 100644 --- a/app/controllers/store_controller.rb +++ b/app/controllers/store_controller.rb @@ -26,6 +26,8 @@ class StoreController < ApplicationController private + DONATION_FREQUENCIES = %w[one-time monthly] + # if order is nil or empty after checkout phase, abort. This should never happen normally, # but can happen if customer does weird things trying to have multiple sessions open. def order_is_not_empty @@ -113,6 +115,7 @@ def donate_to_fund # Serve quick_donate page; POST calls #process_donation def donate reset_shopping # even if order in progress, going to donation page cancels it + @donation_frequency_options = DONATION_FREQUENCIES if @customer == Customer.anonymous_customer # handle donation as a 'guest checkout', even though may end up being tied to real customer @customer = Customer.new @@ -134,7 +137,10 @@ def process_donation @amount > 0 or return redirect_to(redirect_route, :alert => 'Donation amount must be provided') # Given valid donation, customer, and charge token, create & place credit card order. @gOrderInProgress = Order.new_from_donation(@amount, Donation.default_code, @customer) - @gOrderInProgress.add_recurring_donation() if params[:donation_frequency] == 'monthly' + case params[:donation_frequency] + when DONATION_FREQUENCIES[1] + @gOrderInProgress.add_recurring_donation() + end @gOrderInProgress.purchasemethod = Purchasemethod.get_type_by_name('web_cc') @gOrderInProgress.purchase_args = {:credit_card_token => params[:credit_card_token]} @gOrderInProgress.processed_by = @customer diff --git a/app/views/store/donate.html.haml b/app/views/store/donate.html.haml index b8be754f2..a4d7e8e25 100644 --- a/app/views/store/donate.html.haml +++ b/app/views/store/donate.html.haml @@ -46,10 +46,9 @@ .form-group.form-row %label.col-form-label.text-right.col-sm-6{:for => :donation} Donation frequency .radio-group.col-sm-6.col-md-2.form-inline - = radio_button_tag 'donation_frequency', 'one', Option.default_donation_type == 'one', class: 'form-control', id: 'one' - = label_tag 'donation_type_one_time', 'One Time', class: 'form-control', for: 'one' - = radio_button_tag 'donation_frequency', 'monthly', Option.default_donation_type == 'monthly', class: 'form-control', id: 'monthly' - = label_tag 'donation_type_monthly', 'Monthly', class: 'form-control', for: 'monthly' + - @donation_frequency_options.each do |frequency| + = radio_button_tag 'donation_frequency', frequency, Option.default_donation_type == frequency, class: 'form-control', id: frequency + = label_tag 'donation_type_#{frequency}', frequency, class: 'form-control', for: frequency .form-group.form-row %label.col-form-label{:for => :donation_comments} If you'd like to be recognized as Anonymous, or if you'd like to donate in honor From 99178bb23b51d4e37511551bf7e1a9e4b2e86d30 Mon Sep 17 00:00:00 2001 From: Winson Wan Date: Thu, 14 Mar 2024 01:07:31 -0700 Subject: [PATCH 9/9] create rspec tests for recurring donations --- spec/controllers/store_controller_spec.rb | 43 +++++++++++++++++++++-- spec/models/order_adding_items_spec.rb | 11 +++++- spec/models/order_finalizing_spec.rb | 10 ++++++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/spec/controllers/store_controller_spec.rb b/spec/controllers/store_controller_spec.rb index d006b981c..2b6fe7892 100644 --- a/spec/controllers/store_controller_spec.rb +++ b/spec/controllers/store_controller_spec.rb @@ -3,7 +3,7 @@ describe StoreController do before :each do ; @buyer = create(:customer) ; end - + shared_examples_for 'initial visit' do before :each do @r = {:controller => 'store', :action => 'index'} @@ -101,6 +101,45 @@ end end + describe 'making a recurring donation' do + before :each do + @customer = create(:customer) + end + let (:attempt_recurring_donation) { + post :process_donation, + {:customer_id => @customer.id, :donation => 5, :donation_frequency => 'monthly', :credit_card_token => 'dummy'} + } + context 'when transaction completes successfully' do + before :each do + allow(Stripe::Charge).to receive(:create).and_return(double("Stripe::Charge", id: 1)) + end + it 'creates a new RecurringDonation record' do + expect{attempt_recurring_donation}.to change {RecurringDonation.count}.by(1) + end + it 'sets new RecurringDonation record attributes to correct values' do + attempt_recurring_donation + recurring_donation = RecurringDonation.find(1) + donation = recurring_donation.donations[0] + expect(recurring_donation.account_code_id).to(equal(donation.account_code_id)) + expect(recurring_donation.customer_id).to(equal(donation.customer_id)) + end + it 'adds a recurring_donation_id foreign key to the first donation instance' do + attempt_recurring_donation + recurring_donation = RecurringDonation.find(1) + donation = recurring_donation.donations[0] + expect(donation.recurring_donation_id).to equal(recurring_donation.id) + end + end + context 'when transaction completes unsuccessfully' do + before :each do + allow(Stripe::Charge).to receive(:create).and_raise(Stripe::StripeError) + end + it 'does not create a new RecurringDonation record' do + expect{attempt_recurring_donation}.to change {RecurringDonation.count}.by(0) + end + end + end + describe 'quick donation with nonexistent customer' do before :each do @new_valid_customer = attributes_for(:customer).except(:password,:password_confirmation) @@ -147,7 +186,7 @@ expect(flash[:alert]).to match(/Incomplete or invalid donor/i) end end - + end describe "processing empty cart" do before :each do diff --git a/spec/models/order_adding_items_spec.rb b/spec/models/order_adding_items_spec.rb index b0f282b9b..e5a4f9090 100644 --- a/spec/models/order_adding_items_spec.rb +++ b/spec/models/order_adding_items_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' describe Order, 'adding' do - before :each do + before :each do @order = Order.create!(:processed_by => create(:customer)) # generic customer @vv = create(:valid_voucher) end @@ -47,6 +47,15 @@ expect(reloaded.donation.account_code_id).to eq(@donation.account_code_id) end end + describe 'recurring donations' do + before :each do + @order.add_donation(build(:donation, :amount => 17)) + end + it 'should add recurring donation' do + @order.add_recurring_donation() + expect(@order.recurring_donation).to be_a_kind_of(RecurringDonation) + end + end describe 'and getting total price' do it 'without donation' do vv2 = create(:valid_voucher) diff --git a/spec/models/order_finalizing_spec.rb b/spec/models/order_finalizing_spec.rb index 52538ac39..7458eaeff 100644 --- a/spec/models/order_finalizing_spec.rb +++ b/spec/models/order_finalizing_spec.rb @@ -57,6 +57,16 @@ def vouchers_for(showdate, vouchertype) @order.finalize! expect(@order.purchaser.donations).to include(@donation) end + it 'should add recurring donation to customer account if purchaser==recipient' do + @order.add_recurring_donation() + @order.finalize! + expect(@order.purchaser.recurring_donations).to include(@order.recurring_donation) + end + it 'should associate donation with recurring donation' do + @order.add_recurring_donation() + @order.finalize! + expect(@donation.recurring_donation_id).to equal(@order.recurring_donation.id) + end context 'when purchaser!=recipient' do before :each do @order.purchaser = create(:customer)