From 6ab7473ef7b402fc7d64ab514fa966bf91f0da85 Mon Sep 17 00:00:00 2001 From: Eric Schultz Date: Mon, 8 Feb 2021 14:55:31 -0600 Subject: [PATCH] donation.* event publishing --- app/models/donation.rb | 2 + app/models/modern_donation.rb | 55 +++++++++++++++++++ app/models/transaction.rb | 1 + .../20210204210627_create_modern_donations.rb | 10 ++++ ...43_convert_donation_dedication_to_jsonb.rb | 17 ++++++ db/structure.sql | 36 +++++++++++- .../Nonprofit/Transaction/Donation.ts | 30 ++++++++++ lib/insert/insert_donation.rb | 8 ++- lib/pay_recurring_donation.rb | 7 +++ lib/query/query_payments.rb | 2 +- lib/update/update_donation.rb | 17 +++++- spec/factories/modern_donations.rb | 9 +++ spec/lib/insert/insert_donation_spec.rb | 21 ++++--- .../insert/insert_recurring_donation_spec.rb | 8 +-- spec/lib/query/query_payments_spec.rb | 2 +- spec/lib/update/update_donation_spec.rb | 45 ++++++++++++++- spec/models/modern_donation_spec.rb | 54 ++++++++++++++++++ .../shared_rd_donation_value_context.rb | 2 +- 18 files changed, 304 insertions(+), 22 deletions(-) create mode 100644 app/models/modern_donation.rb create mode 100644 db/migrate/20210204210627_create_modern_donations.rb create mode 100644 db/migrate/20210204223643_convert_donation_dedication_to_jsonb.rb create mode 100644 docs/event_definitions/Nonprofit/Transaction/Donation.ts create mode 100644 spec/factories/modern_donations.rb create mode 100644 spec/models/modern_donation_spec.rb diff --git a/app/models/donation.rb b/app/models/donation.rb index 81d30085..4390080f 100644 --- a/app/models/donation.rb +++ b/app/models/donation.rb @@ -45,5 +45,7 @@ class Donation < ApplicationRecord belongs_to :campaign belongs_to :event + has_one :modern_donation, inverse_of: :legacy_donation + scope :anonymous, -> { where(anonymous: true) } end diff --git a/app/models/modern_donation.rb b/app/models/modern_donation.rb new file mode 100644 index 00000000..afc0abf1 --- /dev/null +++ b/app/models/modern_donation.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +# 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 +class ModernDonation < ApplicationRecord + include Model::Houidable + include Model::Jbuilder + include Model::Eventable + setup_houid :don + + # TODO must associate with events and campaigns somehow + add_builder_expansion :nonprofit, :supporter + add_builder_expansion :trx, + json_attrib: :transaction + + has_one :transaction_assignment, as: :assignable + has_one :trx, through: :transaction_assignment + has_one :supporter, through: :trx + has_one :nonprofit, through: :supporter + belongs_to :legacy_donation, class_name: 'Donation', foreign_key: :donation_id, inverse_of: :modern_donation + + delegate :designation, :dedication, to: :legacy_donation + + def to_builder(*expand) + init_builder(*expand) do |json| + json.(self, :id, :designation) + json.object 'donation' + + json.dedication do + json.type dedication['type'] + json.name dedication['name'] + contact = dedication['contact'] + json.contact do + json.email contact['email'] if contact['email'] + json.address contact['address'] if contact['address'] + json.phone contact['phone'] if contact['phone'] + end if contact + end if dedication + # TODO the line above is a hacky solution + + json.amount do + json.value_in_cents amount + json.currency nonprofit.currency + end + end + end + + def publish_created + Houdini.event_publisher.announce(:donation_created, to_event('donation.created', :nonprofit, :supporter, :trx).attributes!) + end + + def publish_updated + Houdini.event_publisher.announce(:donation_updated, to_event('donation.updated', :nonprofit, :supporter, :trx).attributes!) + end +end diff --git a/app/models/transaction.rb b/app/models/transaction.rb index 4edf61c6..f41922bd 100644 --- a/app/models/transaction.rb +++ b/app/models/transaction.rb @@ -15,6 +15,7 @@ class Transaction < ApplicationRecord has_many :transaction_assignments + has_many :donations, through: :transaction_assignments, source: :assignable, source_type: 'ModernDonation' has_many :ticket_purchases, through: :transaction_assignments, source: :assignable, source_type: 'TicketPurchase' validates :supporter, presence: true diff --git a/db/migrate/20210204210627_create_modern_donations.rb b/db/migrate/20210204210627_create_modern_donations.rb new file mode 100644 index 00000000..76bf950d --- /dev/null +++ b/db/migrate/20210204210627_create_modern_donations.rb @@ -0,0 +1,10 @@ +class CreateModernDonations < ActiveRecord::Migration[6.1] + def change + create_table :modern_donations, id: :string do |t| + t.integer :amount + t.references :donation + + t.timestamps + end + end +end diff --git a/db/migrate/20210204223643_convert_donation_dedication_to_jsonb.rb b/db/migrate/20210204223643_convert_donation_dedication_to_jsonb.rb new file mode 100644 index 00000000..6438838d --- /dev/null +++ b/db/migrate/20210204223643_convert_donation_dedication_to_jsonb.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +# 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 +class ConvertDonationDedicationToJsonb < ActiveRecord::Migration[6.1] + def up + execute <<-SQL + ALTER TABLE "donations" ALTER COLUMN "dedication" TYPE jsonb USING dedication::jsonb + SQL + end + + def down + execute <<-SQL + ALTER TABLE "donations" ALTER COLUMN "dedication" TYPE text USING dedication::text + SQL + end +end diff --git a/db/structure.sql b/db/structure.sql index 32275806..f704abeb 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -848,7 +848,7 @@ CREATE TABLE public.donations ( recurring_donation_id integer, comment text, recurring boolean, - dedication text, + dedication jsonb, event_id integer, imported_at timestamp without time zone, charge_id integer, @@ -1436,6 +1436,19 @@ CREATE SEQUENCE public.miscellaneous_np_infos_id_seq ALTER SEQUENCE public.miscellaneous_np_infos_id_seq OWNED BY public.miscellaneous_np_infos.id; +-- +-- Name: modern_donations; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.modern_donations ( + id character varying NOT NULL, + amount integer, + donation_id bigint, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + -- -- Name: nonprofit_keys; Type: TABLE; Schema: public; Owner: - -- @@ -3062,6 +3075,14 @@ ALTER TABLE ONLY public.miscellaneous_np_infos ADD CONSTRAINT miscellaneous_np_infos_pkey PRIMARY KEY (id); +-- +-- Name: modern_donations modern_donations_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.modern_donations + ADD CONSTRAINT modern_donations_pkey PRIMARY KEY (id); + + -- -- Name: bank_accounts nonprofit_bank_accounts_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -3423,6 +3444,13 @@ CREATE INDEX index_exports_on_user_id ON public.exports USING btree (user_id); CREATE INDEX index_import_requests_on_nonprofit_id ON public.import_requests USING btree (nonprofit_id); +-- +-- Name: index_modern_donations_on_donation_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_modern_donations_on_donation_id ON public.modern_donations USING btree (donation_id); + + -- -- Name: index_payments_on_created_at; Type: INDEX; Schema: public; Owner: - -- @@ -4286,7 +4314,9 @@ INSERT INTO "schema_migrations" (version) VALUES ('20210122203303'), ('20210127193411'), ('20210128215402'), -('20210204013426'), -('20210204172319'); +('20210204172319'), +('20210204174909'), +('20210204210627'), +('20210204223643'); diff --git a/docs/event_definitions/Nonprofit/Transaction/Donation.ts b/docs/event_definitions/Nonprofit/Transaction/Donation.ts new file mode 100644 index 00000000..be58bfc5 --- /dev/null +++ b/docs/event_definitions/Nonprofit/Transaction/Donation.ts @@ -0,0 +1,30 @@ +// License: LGPL-3.0-or-later +import type { Amount, HoudiniObject, IdType, HouID, HoudiniEvent } from "../../common"; +import type Nonprofit from '../'; +import type Supporter from "../Supporter"; +import type Transaction from './'; + +interface Dedication { + contact?: { + address?: string; + email?: string; + phone?: string; + }; + name: string; + note?: string; + type: 'honor' | 'memory'; +} + +export interface Donation extends HoudiniObject { + amount: Amount; + dedication?: Dedication | null; + designation?: string | null; + nonprofit: IdType | Nonprofit; + object: 'donation'; + supporter: IdType | Supporter; + transaction: HouID | Transaction; +} + +export type DonationCreated = HoudiniEvent<'donation.created', Donation>; +export type DonationUpdated = HoudiniEvent<'donation.updated', Donation>; +export type DonationDeleted = HoudiniEvent<'donation.deleted', Donation>; diff --git a/lib/insert/insert_donation.rb b/lib/insert/insert_donation.rb index 8b1b5586..d64ac97b 100644 --- a/lib/insert/insert_donation.rb +++ b/lib/insert/insert_donation.rb @@ -42,7 +42,13 @@ module InsertDonation # Create the donation record result['donation'] = insert_donation(data, entities) + trx = entities[:supporter_id].transactions.build(amount: data['amount']) update_donation_keys(result) + + don = trx.donations.build(amount: result['donation'].amount, legacy_donation: result['donation']) + trx.save! + don.save! + don.publish_created result['activity'] = InsertActivities.for_one_time_donations([result['payment'].id]) Houdini.event_publisher.announce(:donation_create, result['donation'], result['donation'].supporter.locale) result @@ -222,7 +228,7 @@ module InsertDonation nonprofit_id: { required: true, is_reference: true }, supporter_id: { required: true, is_reference: true }, designation: { is_a: String }, - dedication: { is_a: String }, + dedication: { is_a: Hash }, campaign_id: { is_reference: true }, event_id: { is_reference: true } } diff --git a/lib/pay_recurring_donation.rb b/lib/pay_recurring_donation.rb index c9ad7338..97903a1e 100644 --- a/lib/pay_recurring_donation.rb +++ b/lib/pay_recurring_donation.rb @@ -64,6 +64,7 @@ module PayRecurringDonation raise ParamValidation::ValidationError.new("#{rd['donation_id']} is not a valid donation", {}) end + trx = nonprofit.transactions.build(amount: donation['amount']) result = {} result = result.merge(InsertDonation.insert_charge( 'card_id' => donation['card_id'], @@ -78,7 +79,13 @@ module PayRecurringDonation if result['charge']['status'] != 'failed' rd.update(n_failures: 0) result['recurring_donation'] = rd + Houdini.event_publisher.announce(:recurring_donation_payment_succeeded, donation, donation&.supporter&.locale || 'en') + + donation = trx.donations.build(amount: donation['amount'], designation: donation['designation'], dedication: donation["dedication"]) + trx.save! + donation.save! + donation.publish_created InsertActivities.for_recurring_donations([result['payment']['id']]) else diff --git a/lib/query/query_payments.rb b/lib/query/query_payments.rb index 885cafe1..230308d0 100644 --- a/lib/query/query_payments.rb +++ b/lib/query/query_payments.rb @@ -328,7 +328,7 @@ module QueryPayments end def self.get_dedication_or_empty(*path) - "json_extract_path_text(coalesce(nullif(trim(both from donations.dedication), ''), '{}')::json, #{path.map { |i| "'#{i}'" }.join(',')})" + "json_extract_path_text(coalesce(donations.dedication, '{}')::json, #{path.map { |i| "'#{i}'" }.join(',')})" end def self.export_selects diff --git a/lib/update/update_donation.rb b/lib/update/update_donation.rb index 5995ca13..db7776a2 100644 --- a/lib/update/update_donation.rb +++ b/lib/update/update_donation.rb @@ -86,6 +86,8 @@ module UpdateDonation donation.date = data[:date] if data[:date] end + modern_donation = donation.modern_donation + trx = modern_donation.trx # edits_to_payments if is_offsite # if offline, set date, gross_amount, fee_total, net_amount @@ -95,7 +97,13 @@ module UpdateDonation existing_payment.fee_total = data[:fee_total] if data[:fee_total] existing_payment.net_amount = existing_payment.gross_amount - existing_payment.fee_total - existing_payment.save! if existing_payment.changed? + if existing_payment.changed? + existing_payment.save! + if existing_payment.previous_changes.has_key? 'gross_amount' + modern_donation.amount = existing_payment.gross_amount + trx.amount = existing_payment.gross_amount + end + end else if donation.designation Payment.where('donation_id = ?', donation.id).update_all(towards: donation.designation, updated_at: Time.now) @@ -111,7 +119,14 @@ module UpdateDonation offsite_payment.save! if offsite_payment.changed? end + trx.save! if trx.changed? donation.save! if donation.changed? + md_changed = modern_donation.changed? + modern_donation.save! if modern_donation.changed? + if (['dedication', 'designation'].any? {|i| donation.previous_changes.has_key? i} || md_changed) + modern_donation.publish_updated + end + existing_payment.reload diff --git a/spec/factories/modern_donations.rb b/spec/factories/modern_donations.rb new file mode 100644 index 00000000..22e414a2 --- /dev/null +++ b/spec/factories/modern_donations.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +# 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 +FactoryBot.define do + factory :modern_donation do + amount { 1 } + end +end diff --git a/spec/lib/insert/insert_donation_spec.rb b/spec/lib/insert/insert_donation_spec.rb index f2258b9e..8ac9f4e0 100644 --- a/spec/lib/insert/insert_donation_spec.rb +++ b/spec/lib/insert/insert_donation_spec.rb @@ -13,6 +13,9 @@ describe InsertDonation do include_context :shared_rd_donation_value_context describe 'param validation' do + before(:each) do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_created,any_args) + end it 'does basic validation' do validation_basic_validation { InsertDonation.with_stripe(designation: 34_124, dedication: 35_141, event_id: 'bad', campaign_id: 'bad') } end @@ -83,24 +86,27 @@ describe InsertDonation do end it 'charge returns failed' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_created,any_args) handle_charge_failed { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token) } end describe 'success' do before(:each) do before_each_success + allow(Houdini.event_publisher).to receive(:announce) + expect(Houdini.event_publisher).to receive(:announce).with(:donation_created,any_args) end it 'process event donation' do - process_event_donation { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: event.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation') } + process_event_donation { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: event.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') } end it 'process campaign donation' do expect(Houdini.event_publisher).to receive(:announce).with(:campaign_create, any_args) - process_campaign_donation { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: campaign.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation') } + process_campaign_donation { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: campaign.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') } end it 'processes general donation' do - process_general_donation { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation') } + process_general_donation { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') } end end end @@ -113,18 +119,17 @@ describe InsertDonation do before_each_sepa_success end it 'process event donation' do - process_event_donation(sepa: true) { InsertDonation.with_sepa(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, direct_debit_detail_id: direct_debit_detail.id, event_id: event.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation') } + process_event_donation(sepa: true) { InsertDonation.with_sepa(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, direct_debit_detail_id: direct_debit_detail.id, event_id: event.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') } end it 'process campaign donation' do - expect(Houdini.event_publisher).to receive(:announce).with(:supporter_created, anything) - expect(Houdini.event_publisher).to receive(:announce).with(:supporter_address_created, anything) + allow(Houdini.event_publisher).to receive(:announce) expect(Houdini.event_publisher).to receive(:announce).with(:campaign_create, any_args) - process_campaign_donation(sepa: true) { InsertDonation.with_sepa(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, direct_debit_detail_id: direct_debit_detail.id, campaign_id: campaign.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation') } + process_campaign_donation(sepa: true) { InsertDonation.with_sepa(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, direct_debit_detail_id: direct_debit_detail.id, campaign_id: campaign.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') } end it 'processes general donation' do - process_general_donation(sepa: true) { InsertDonation.with_sepa(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, direct_debit_detail_id: direct_debit_detail.id, profile_id: profile.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation') } + process_general_donation(sepa: true) { InsertDonation.with_sepa(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, direct_debit_detail_id: direct_debit_detail.id, profile_id: profile.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') } end end end diff --git a/spec/lib/insert/insert_recurring_donation_spec.rb b/spec/lib/insert/insert_recurring_donation_spec.rb index 2bc73746..e25c847c 100644 --- a/spec/lib/insert/insert_recurring_donation_spec.rb +++ b/spec/lib/insert/insert_recurring_donation_spec.rb @@ -128,17 +128,17 @@ describe InsertRecurringDonation do before_each_success end it 'process event donation' do - process_event_donation(recurring_donation: { paydate: nil, interval: 1, time_unit: 'year', start_date: Time.current.beginning_of_day }) { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: event.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation', recurring_donation: { time_unit: 'year' }) } + process_event_donation(recurring_donation: { paydate: nil, interval: 1, time_unit: 'year', start_date: Time.current.beginning_of_day }) { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: event.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation', recurring_donation: { time_unit: 'year' }) } end it 'process campaign donation' do expect(Houdini.event_publisher).to receive(:announce).with(:campaign_create, any_args) - process_campaign_donation(recurring_donation: { paydate: nil, interval: 2, time_unit: 'month', start_date: Time.current.beginning_of_day }) { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: campaign.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation', recurring_donation: { interval: 2 }) } + process_campaign_donation(recurring_donation: { paydate: nil, interval: 2, time_unit: 'month', start_date: Time.current.beginning_of_day }) { InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: campaign.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation', recurring_donation: { interval: 2 }) } end it 'processes general donation with no recurring donation hash' do process_general_donation(recurring_donation: { paydate: Time.now.day, interval: 1, time_unit: 'month', start_date: Time.now.beginning_of_day }) do - InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: Time.now.to_s, dedication: 'dedication', designation: 'designation') + InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: Time.now.to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') end end end @@ -150,7 +150,7 @@ describe InsertRecurringDonation do it 'processes general donation' do process_general_donation(expect_payment: false, expect_charge: false, recurring_donation: { paydate: (Time.now + 5.days).day, interval: 1, time_unit: 'month', start_date: (Time.now + 5.days).beginning_of_day }) do - InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: (Time.now + 1.day).to_s, dedication: 'dedication', designation: 'designation', recurring_donation: { start_date: (Time.now + 5.days).to_s }) + InsertRecurringDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: profile.id, date: (Time.now + 1.day).to_s, dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation', recurring_donation: { start_date: (Time.now + 5.days).to_s }) end end end diff --git a/spec/lib/query/query_payments_spec.rb b/spec/lib/query/query_payments_spec.rb index de9e045b..54e1f030 100644 --- a/spec/lib/query/query_payments_spec.rb +++ b/spec/lib/query/query_payments_spec.rb @@ -186,7 +186,7 @@ describe QueryPayments do token: token, date: date, - dedication: 'dedication', + dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation' } input[:event_id] = h[:event_id] if h[:event_id] diff --git a/spec/lib/update/update_donation_spec.rb b/spec/lib/update/update_donation_spec.rb index 02d09b3a..f11e86c9 100644 --- a/spec/lib/update/update_donation_spec.rb +++ b/spec/lib/update/update_donation_spec.rb @@ -24,6 +24,13 @@ describe UpdateDonation do supporter: supporter) end + let(:trx) { + trx = force_create(:transaction, supporter: supporter, amount: initial_amount) + trx.donations.create(amount: initial_amount, legacy_donation: donation) + + trx + } + let(:payment) do force_create(:payment, nonprofit: np, donation: donation, towards: initial_designation, @@ -76,12 +83,14 @@ describe UpdateDonation do let(:payment2_date) { initial_date + 10.days } before(:each) do + allow(Houdini.event_publisher).to receive(:announce) initial_time payment end describe '.update_payment' do describe 'param validation' do it 'basic validation' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) expect { UpdateDonation.update_payment(nil, nil) }.to raise_error { |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [{ key: :id, name: :required }, @@ -92,6 +101,7 @@ describe UpdateDonation do end it 'validates whether payment is valid' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) expect { UpdateDonation.update_payment(5555, {}) }.to raise_error { |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [{ key: :id }]) @@ -132,6 +142,7 @@ describe UpdateDonation do end it 'for offsite donations' do offsite_payment + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) expect { UpdateDonation.update_payment(donation.id, expanded_invalid_arguments) }.to(raise_error do |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, initial_validation_errors.concat([ @@ -146,6 +157,7 @@ describe UpdateDonation do end it 'for online donation' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) expect { UpdateDonation.update_payment(donation.id, expanded_invalid_arguments) }.to(raise_error do |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, initial_validation_errors) @@ -154,6 +166,7 @@ describe UpdateDonation do end it 'validate campaign_id' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) expect { UpdateDonation.update_payment(donation.id, campaign_id: 444, event_id: 444) }.to(raise_error do |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [{ key: :campaign_id }]) @@ -161,6 +174,7 @@ describe UpdateDonation do end it 'validate event_id' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) expect { UpdateDonation.update_payment(donation.id, event_id: 4444, campaign_id: campaign.id) }.to(raise_error do |error| expect(error).to be_a ParamValidation::ValidationError expect_validation_errors(error.data, [{ key: :event_id }]) @@ -168,26 +182,33 @@ describe UpdateDonation do end it 'validates campaign belongs to payment org' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) campaign_belongs { UpdateDonation.update_payment(donation.id, campaign_id: other_campaign.id, event_id: event.id) } end it 'validates event belongs to payment org' do + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) event_belongs { UpdateDonation.update_payment(donation.id, event_id: other_event.id, campaign_id: campaign.id) } end end describe 'most of the values arent changed if not provided' do it 'online donation' do + trx payment2 result = verify_nothing_changed - + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) expect(result).to eq donation.attributes.merge(payment: payment2.attributes) + trx.reload + expect(trx.amount).to eq initial_amount + expect(trx.donations.first.amount).to eq initial_amount end it 'offsite donation' do + trx offsite_payment result = verify_nothing_changed - + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) p2_attributes = payment2.attributes payment2.reload expect(p2_attributes).to eq payment2.attributes @@ -196,9 +217,13 @@ describe UpdateDonation do offsite_payment.reload expect(o_attributes).to eq offsite_payment.attributes expect(result).to eq donation.attributes.merge(payment: payment.attributes, offsite_payment: offsite_payment.attributes) + trx.reload + expect(trx.amount).to eq initial_amount + expect(trx.donations.first.amount).to eq initial_amount end def verify_nothing_changed + expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_updated,anything) result = UpdateDonation.update_payment(donation.id, campaign_id: '', event_id: '') p_attributes = payment.attributes @@ -209,6 +234,9 @@ describe UpdateDonation do donation.reload expect(d_attributes).to eq donation.attributes + trx.reload + expect(trx.amount).to eq initial_amount + expect(trx.donations.first.amount).to eq initial_amount result end end @@ -227,8 +255,10 @@ describe UpdateDonation do date: new_date_input } end it 'online donation' do + trx payment2 Timecop.freeze(1.day) do + expect(Houdini.event_publisher).to receive(:announce).with(:donation_updated,anything) result = UpdateDonation.update_payment(donation.id, new_data) expected_donation = donation.attributes.merge(designation: new_designation, @@ -259,8 +289,10 @@ describe UpdateDonation do end it 'offline donation' do + trx offsite_payment Timecop.freeze(1.day) do + expect(Houdini.event_publisher).to receive(:announce).with(:donation_updated,anything) result = UpdateDonation.update_payment(donation.id, new_data) expected_donation = donation.attributes.merge( @@ -276,6 +308,7 @@ describe UpdateDonation do updated_at: Time.now ).with_indifferent_access + trx.reload donation.reload expect(donation.attributes).to eq expected_donation @@ -291,6 +324,10 @@ describe UpdateDonation do expect(offsite_payment.attributes).to eq expected_offsite_payment expect(result).to eq create_expected_result(donation, payment, offsite_payment) + + expect(trx.amount).to eq new_amount + + expect(trx.donations.first.amount).to eq new_amount end end @@ -309,8 +346,10 @@ describe UpdateDonation do end it 'online donation' do + trx payment2 Timecop.freeze(1.day) do + expect(Houdini.event_publisher).to receive(:announce).with(:donation_updated,anything) UpdateDonation.update_payment(donation.id, new_data) result = UpdateDonation.update_payment(donation.id, blank_data) @@ -342,8 +381,10 @@ describe UpdateDonation do end it 'offline donation' do + trx offsite_payment Timecop.freeze(1.day) do + expect(Houdini.event_publisher).to receive(:announce).with(:donation_updated,anything) UpdateDonation.update_payment(donation.id, new_data) result = UpdateDonation.update_payment(donation.id, blank_data) diff --git a/spec/models/modern_donation_spec.rb b/spec/models/modern_donation_spec.rb new file mode 100644 index 00000000..e3d9394a --- /dev/null +++ b/spec/models/modern_donation_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +# 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 +require 'rails_helper' + +RSpec.describe ModernDonation, type: :model do + include_context :shared_donation_charge_context + # TODO Why are we manually setting everything here? It's not clear what order things should + # go in for a transaction. Therefore, we don't assume the order for now and just make sure the + # the output of to_builder is right + let(:trx) { force_create(:transaction, supporter: supporter, amount: 1200)} + let(:legacy_donation) { force_create(:donation, amount: 1200) } + let(:dedication) {{ + type: 'honor', + name: "Grandma Schultz" + }} + + let(:legacy_donation_with_dedication_and_designation) { force_create(:donation, amount: 1200, designation: 'designation', dedication: dedication) } + describe 'to_builder' do + let(:don_default) do + { + 'id' => match_houid('don'), + 'object' => 'donation', + 'nonprofit' => nonprofit.id, + 'supporter' => supporter.id, + 'amount' => {'currency' => 'usd', 'value_in_cents' => 1200}, + 'transaction' => trx.id, + 'designation' => nil + } + end + it 'without dedication or designation' do + donation = trx.donations.create(amount: 1200) + donation.legacy_donation = legacy_donation + donation.save! + expect(donation.to_builder.attributes!).to match(don_default) + end + + + it 'with designation and dedication' do + donation = trx.donations.create(amount: 1200) + donation.legacy_donation = legacy_donation_with_dedication_and_designation + donation.save! + + expect(donation.to_builder.attributes!).to match(don_default.merge({ + 'designation' => 'designation', + 'dedication' => { + 'type' => 'honor', + 'name' => 'Grandma Schultz' + } + })) + end + end +end diff --git a/spec/support/contexts/shared_rd_donation_value_context.rb b/spec/support/contexts/shared_rd_donation_value_context.rb index 0071dd78..067449ab 100644 --- a/spec/support/contexts/shared_rd_donation_value_context.rb +++ b/spec/support/contexts/shared_rd_donation_value_context.rb @@ -48,7 +48,7 @@ RSpec.shared_context :shared_rd_donation_value_context do amount: charge_amount, comment: nil, category: nil, - dedication: 'dedication', + dedication: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation', imported_at: nil, manual: nil,