Initial Transaction and OfflineTransaction support
Co-authored-by: Clarissa Lima Borges <clarissa@commitchange.com>
This commit is contained in:
parent
fca3c7cd6a
commit
2e8821efdf
69 changed files with 1991 additions and 376 deletions
|
@ -569,7 +569,6 @@ AllCops:
|
|||
- 'spec/lib/format/dedication_spec.rb'
|
||||
- 'spec/lib/insert/insert_refunds_spec.rb'
|
||||
- 'spec/lib/insert/insert_recurring_donation_spec.rb'
|
||||
- 'spec/lib/insert/insert_donation_spec.rb'
|
||||
- 'spec/lib/insert/insert_charge_spec.rb'
|
||||
- 'spec/lib/insert/insert_disputes_spec.rb'
|
||||
- 'spec/lib/insert/insert_bank_account_spec.rb'
|
||||
|
@ -745,3 +744,7 @@ Style/FrozenStringLiteralComment:
|
|||
|
||||
Style/PreferredHashMethods:
|
||||
Enabled: false
|
||||
|
||||
Style/LambdaCall:
|
||||
# jbuilder uses this a lot so we want it.
|
||||
Enabled: false
|
||||
|
|
9
Procfile.dev
Normal file
9
Procfile.dev
Normal file
|
@ -0,0 +1,9 @@
|
|||
# You can run these commands in separate shells
|
||||
web: rails s -p 3000
|
||||
|
||||
# Next line runs a watch process with webpack to compile the changed files.
|
||||
# When making frequent changes to client side assets, you will prefer building webpack assets
|
||||
# upon saving rather than when you refresh your browser page.
|
||||
# Note, if using React on Rails localization you will need to run
|
||||
# `bundle exec rake react_on_rails:locale` before you run bin/webpack
|
||||
client: sh -c 'rm -rf public/packs/* || true && bin/webpack -w'
|
26
Procfile.dev-hmr
Normal file
26
Procfile.dev-hmr
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Procfile for development using HMR
|
||||
|
||||
web: rails s -p 3000
|
||||
|
||||
# Note, hot and live reloading don't work with the default generator setup on
|
||||
# top of the rails/webpacker Webpack config with server rendering.
|
||||
# If you have server rendering enabled (prerender is true), you either need to
|
||||
# a. Ensure that you have dev_server.hmr and dev_server.inline BOTH set to false,
|
||||
# and you have this option in your config/initializers/react_on_rails.rb:
|
||||
# config.same_bundle_for_client_and_server = true
|
||||
# If you have either config/webpacker.yml option set to true, you'll see errors like
|
||||
# "ReferenceError: window is not defined" (if hmr is true)
|
||||
# "TypeError: Cannot read property 'prototype' of undefined" (if inline is true)
|
||||
# b. Skip using the webpack-dev-server. bin/webpack --watch is typically
|
||||
fast enough.
|
||||
# c. See the React on Rails README for a link to documentation for how to setup
|
||||
# SSR with HMR and React hot loading using the webpack-dev-server only for the
|
||||
# client bundles and a static file for the server bundle.
|
||||
|
||||
# Run the webpack-dev-server for client and maybe server files
|
||||
webpack-dev-server: bin/webpack-dev-server
|
||||
|
||||
# Keep the JS fresh for server rendering. Remove if not server rendering.
|
||||
# Especially if you have not configured generation of a server bundle without a hash.
|
||||
# as that will conflict with the manifest created by the bin/webpack-dev-server
|
||||
# rails-server-assets: SERVER_BUNDLE_ONLY=yes bin/webpack --watch
|
|
@ -143,7 +143,7 @@ This will download the latest Houdini code. Change to the
|
|||
`houdini` directory and we can set the rest of Houdini up.
|
||||
|
||||
Let's run the Houdini project setup and we'll be ready to go!
|
||||
|
||||
P
|
||||
```bash
|
||||
bin/setup
|
||||
```
|
||||
|
|
|
@ -40,8 +40,6 @@ class Campaign < ApplicationRecord
|
|||
# :reason_for_supporting,
|
||||
# :default_reason_for_supporting
|
||||
|
||||
add_builder_expansion :nonprofit
|
||||
|
||||
validate :end_datetime_cannot_be_in_past, on: :create
|
||||
validates :profile, presence: true
|
||||
validates :nonprofit, presence: true
|
||||
|
@ -202,6 +200,8 @@ class Campaign < ApplicationRecord
|
|||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :name)
|
||||
|
||||
json.add_builder_expansion :nonprofit
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -30,8 +30,6 @@ class CampaignGiftOption < ApplicationRecord
|
|||
after_update_commit :publish_updated
|
||||
after_destroy_commit :publish_deleted
|
||||
|
||||
add_builder_expansion :campaign, :nonprofit
|
||||
|
||||
has_one :nonprofit, through: :campaign
|
||||
|
||||
|
||||
|
@ -90,6 +88,8 @@ class CampaignGiftOption < ApplicationRecord
|
|||
json.type desc[:recurrence][:type]
|
||||
end if desc[:recurrence]
|
||||
end
|
||||
|
||||
json.add_builder_expansion :campaign, :nonprofit
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -10,9 +10,6 @@ class CampaignGiftPurchase < ApplicationRecord
|
|||
belongs_to :campaign
|
||||
has_many :campaign_gifts, class_name: 'ModernCampaignGift'
|
||||
|
||||
add_builder_expansion :campaign
|
||||
|
||||
|
||||
# TODO replace with Discard gem
|
||||
define_model_callbacks :discard
|
||||
|
||||
|
@ -28,22 +25,28 @@ class CampaignGiftPurchase < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def to_id
|
||||
::Jbuilder.new do |json|
|
||||
json.id id
|
||||
json.object 'campaign_gift_purchase'
|
||||
json.type 'trx_assignment'
|
||||
end
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :deleted)
|
||||
json.type 'trx_assignment'
|
||||
|
||||
json.amount do
|
||||
json.cents amount
|
||||
json.currency nonprofit.currency
|
||||
end
|
||||
|
||||
if expand.include? :campaign_gifts
|
||||
json.campaign_gifts campaign_gifts do |gift|
|
||||
json.merge! gift.to_builder.attributes!
|
||||
end
|
||||
else
|
||||
json.campaign_gifts campaign_gifts.pluck(:id)
|
||||
end
|
||||
json.add_builder_expansion :campaign, :nonprofit, :supporter
|
||||
json.add_builder_expansion :trx, json_attribute: "transaction"
|
||||
json.add_builder_expansion :campaign_gifts, enum_type: :expandable
|
||||
end
|
||||
end
|
||||
|
||||
|
|
16
app/models/concerns/model/created_timeable.rb
Normal file
16
app/models/concerns/model/created_timeable.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# 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
|
||||
module Model::CreatedTimeable
|
||||
extend ActiveSupport::Concern
|
||||
included do
|
||||
after_initialize :set_created_if_needed
|
||||
|
||||
private
|
||||
|
||||
def set_created_if_needed
|
||||
self[:created] = Time.current unless self[:created]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -14,8 +14,8 @@ module Model::Eventable
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
raise NotImplementedError.new("to_builder must be implemented in your model")
|
||||
end
|
||||
|
||||
end
|
|
@ -92,6 +92,15 @@ module Model::Jbuilder
|
|||
def builder_expansions
|
||||
@builder_expansions ||= BuilderExpansionSet.new
|
||||
end
|
||||
|
||||
def init_builder(model, *expand)
|
||||
JbuilderWithExpansions.new(model, *expand) do | json|
|
||||
json.(model, :id)
|
||||
json.object model.class.name.underscore
|
||||
|
||||
yield(json)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_id
|
||||
|
@ -152,11 +161,18 @@ module Model::Jbuilder
|
|||
return ->(model,be=self) {
|
||||
value = be.get_attribute_value model
|
||||
if be.expandable_enum?
|
||||
value&.map{|i| i&.to_id}
|
||||
value&.map do |i|
|
||||
id_result = i&.to_id
|
||||
if ::Jbuilder === id_result
|
||||
id_result.attributes!
|
||||
else
|
||||
id_result
|
||||
end
|
||||
end
|
||||
elsif be.flat_enum?
|
||||
value
|
||||
else
|
||||
value&.to_id
|
||||
value&.to_id
|
||||
end
|
||||
}
|
||||
end
|
||||
|
@ -165,7 +181,7 @@ module Model::Jbuilder
|
|||
return ->(model,be=self) {
|
||||
value = be.get_attribute_value model
|
||||
if be.expandable_enum?
|
||||
value&.map{|i| i&.to_builder}
|
||||
value&.map{|i| i&.to_builder.attributes!}
|
||||
elsif be.flat_enum?
|
||||
value
|
||||
else
|
||||
|
@ -183,20 +199,32 @@ module Model::Jbuilder
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
def init_builder(*expand)
|
||||
builder_expansions = self.class.builder_expansions
|
||||
Jbuilder.new do | json|
|
||||
json.(self, :id)
|
||||
json.object self.class.name.underscore
|
||||
def init_builder(*expand, &block)
|
||||
self.class.init_builder(self, *expand, &block)
|
||||
end
|
||||
|
||||
|
||||
class JbuilderWithExpansions < ::Jbuilder
|
||||
attr_reader :model, :expand
|
||||
|
||||
delegate_missing_to :@jbuilder
|
||||
|
||||
def initialize(model, *expand, &block)
|
||||
@model = model
|
||||
@expand = expand
|
||||
super(&block)
|
||||
end
|
||||
|
||||
def add_builder_expansion( ... )
|
||||
builder_expansions = BuilderExpansionSet.new
|
||||
builder_expansions.add_builder_expansion( ... )
|
||||
builder_expansions.keys.each do |k|
|
||||
if expand.include? k
|
||||
json.set! builder_expansions.get_by_key(k).json_attribute, builder_expansions.get_by_key(k).to_builder.(self)
|
||||
set! builder_expansions.get_by_key(k).json_attribute, builder_expansions.get_by_key(k).to_builder.(model)
|
||||
else
|
||||
json.set! builder_expansions.get_by_key(k).json_attribute, builder_expansions.get_by_key(k).to_id.(self)
|
||||
set! builder_expansions.get_by_key(k).json_attribute, builder_expansions.get_by_key(k).to_id.(model)
|
||||
end
|
||||
end
|
||||
yield(json)
|
||||
end
|
||||
end
|
||||
end
|
20
app/models/concerns/model/subtransactable.rb
Normal file
20
app/models/concerns/model/subtransactable.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# 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
|
||||
module Model::Subtransactable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include Model::Houidable
|
||||
include Model::Jbuilder
|
||||
include Model::Eventable
|
||||
|
||||
has_one :subtransaction, as: :subtransactable, dependent: :nullify
|
||||
has_one :trx, through: :subtransaction
|
||||
has_one :supporter, through: :trx
|
||||
has_one :nonprofit, through: :trx
|
||||
|
||||
has_many :subtransaction_payments, through: :subtransaction
|
||||
end
|
||||
end
|
20
app/models/concerns/model/subtransaction_paymentable.rb
Normal file
20
app/models/concerns/model/subtransaction_paymentable.rb
Normal file
|
@ -0,0 +1,20 @@
|
|||
# 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
|
||||
module Model::SubtransactionPaymentable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include Model::Houidable
|
||||
include Model::Jbuilder
|
||||
include Model::Eventable
|
||||
|
||||
has_one :subtransaction_payment, as: :paymentable, touch: true, dependent: :destroy
|
||||
has_one :trx, through: :subtransaction_payment
|
||||
has_one :supporter, through: :subtransaction_payment
|
||||
has_one :nonprofit, through: :subtransaction_payment
|
||||
|
||||
has_one :subtransaction, through: :subtransaction_payment
|
||||
end
|
||||
end
|
|
@ -16,8 +16,8 @@ module Model::TrxAssignable
|
|||
json_attribute: :transaction
|
||||
|
||||
has_one :transaction_assignment, as: :assignable
|
||||
has_one :trx, through: :transaction_assignment
|
||||
has_one :supporter, through: :trx
|
||||
has_one :nonprofit, through: :supporter
|
||||
has_one :trx, through: :transaction_assignment, class_name: 'Transaction', foreign_key: 'transaction_id'
|
||||
has_one :supporter, through: :transaction_assignment
|
||||
has_one :nonprofit, through: :transaction_assignment
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,8 +14,6 @@ class CustomFieldMaster < ApplicationRecord
|
|||
|
||||
scope :not_deleted, -> { where(deleted: false) }
|
||||
|
||||
add_builder_expansion :nonprofit
|
||||
|
||||
after_create_commit :publish_created
|
||||
|
||||
# TODO replace with Discard gem
|
||||
|
@ -42,6 +40,8 @@ class CustomFieldMaster < ApplicationRecord
|
|||
init_builder(*expand) do |json|
|
||||
json.(self, :name, :deleted)
|
||||
json.object 'custom_field_definition'
|
||||
|
||||
json.add_builder_expansion :nonprofit
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
class Event < ApplicationRecord
|
||||
include Image::AttachmentExtensions
|
||||
include Model::Jbuilder
|
||||
add_builder_expansion :nonprofit
|
||||
|
||||
# :deleted, #bool for soft-delete
|
||||
# :name, # str
|
||||
# :tagline, # str
|
||||
|
@ -102,6 +102,7 @@ class Event < ApplicationRecord
|
|||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :name)
|
||||
json.add_builder_expansion :nonprofit
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ class EventDiscount < ApplicationRecord
|
|||
include Model::Eventable
|
||||
include Model::Jbuilder
|
||||
|
||||
add_builder_expansion :nonprofit, :event
|
||||
# :code,
|
||||
# :event_id,
|
||||
# :name,
|
||||
|
@ -36,13 +35,16 @@ class EventDiscount < ApplicationRecord
|
|||
json.percent percent
|
||||
end
|
||||
|
||||
if expand.include? :ticket_levels
|
||||
json.ticket_levels ticket_levels do |tl|
|
||||
json.merge! tl.to_builder.attributes!
|
||||
end
|
||||
else
|
||||
json.ticket_levels ticket_levels.pluck(:id)
|
||||
end
|
||||
json.add_builder_expansion :nonprofit, :event
|
||||
json.add_builder_expansion :ticket_levels, enum_type: :expandable
|
||||
|
||||
# if expand.include? :ticket_levels
|
||||
# json.ticket_levels ticket_levels do |tl|
|
||||
# json.merge! tl.to_builder.attributes!
|
||||
# end
|
||||
# else
|
||||
# json.ticket_levels ticket_levels.pluck(:id)
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -7,10 +7,6 @@ class ModernCampaignGift < ApplicationRecord
|
|||
include Model::Jbuilder
|
||||
include Model::Eventable
|
||||
setup_houid :cgift
|
||||
|
||||
add_builder_expansion :nonprofit, :supporter, :campaign, :campaign_gift_option, :campaign_gift_purchase
|
||||
add_builder_expansion :trx,
|
||||
json_attribute: :transaction
|
||||
|
||||
belongs_to :campaign_gift_purchase
|
||||
belongs_to :legacy_campaign_gift, class_name: 'CampaignGift', foreign_key: :campaign_gift_id, inverse_of: :modern_campaign_gift
|
||||
|
@ -42,6 +38,17 @@ class ModernCampaignGift < ApplicationRecord
|
|||
init_builder(*expand) do |json|
|
||||
json.(self, :deleted)
|
||||
json.object 'campaign_gift'
|
||||
|
||||
json.add_builder_expansion :nonprofit, :supporter, :campaign, :campaign_gift_option
|
||||
json.add_builder_expansion :trx,
|
||||
json_attribute: :transaction
|
||||
|
||||
if (expand.include? :campaign_gift_purchase)
|
||||
json.campaign_gift_purchase campaign_gift_purchase.to_builder
|
||||
else
|
||||
json.campaign_gift_purchase campaign_gift_purchase.id
|
||||
end
|
||||
|
||||
json.amount do
|
||||
json.cents amount
|
||||
json.currency nonprofit.currency
|
||||
|
|
|
@ -11,10 +11,19 @@ class ModernDonation < ApplicationRecord
|
|||
|
||||
delegate :designation, :dedication, to: :legacy_donation
|
||||
|
||||
def to_id
|
||||
::Jbuilder.new do |json|
|
||||
json.id id
|
||||
json.object 'donation'
|
||||
json.type 'trx_assignment'
|
||||
end
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :designation)
|
||||
json.object 'donation'
|
||||
json.type 'trx_assignment'
|
||||
|
||||
json.dedication do
|
||||
json.type dedication['type']
|
||||
|
@ -32,14 +41,19 @@ class ModernDonation < ApplicationRecord
|
|||
json.cents amount
|
||||
json.currency nonprofit.currency
|
||||
end
|
||||
|
||||
json.add_builder_expansion :nonprofit, :supporter
|
||||
json.add_builder_expansion :trx, json_attribute: :transaction
|
||||
end
|
||||
end
|
||||
|
||||
def publish_created
|
||||
Houdini.event_publisher.announce(:donation_created, to_event('donation.created', :nonprofit, :supporter, :trx).attributes!)
|
||||
Houdini.event_publisher.announce(:trx_assignment_created, to_event('trx_assignment.created', :nonprofit, :supporter, :trx).attributes!)
|
||||
end
|
||||
|
||||
def publish_updated
|
||||
Houdini.event_publisher.announce(:donation_updated, to_event('donation.updated', :nonprofit, :supporter, :trx).attributes!)
|
||||
Houdini.event_publisher.announce(:trx_assignment_updated, to_event('trx_assignment.updated', :nonprofit, :supporter, :trx).attributes!)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ class Nonprofit < ApplicationRecord
|
|||
Categories = ['Public Benefit', 'Human Services', 'Education', 'Civic Duty', 'Human Rights', 'Animals', 'Environment', 'Health', 'Arts, Culture, Humanities', 'International', 'Children', 'Religion', 'LGBTQ', "Women's Rights", 'Disaster Relief', 'Veterans'].freeze
|
||||
|
||||
include Image::AttachmentExtensions
|
||||
include Model::Jbuilder
|
||||
|
||||
# :name, # str
|
||||
# :stripe_account_id, # str
|
||||
# :summary, # text: paragraph-sized organization summary
|
||||
|
@ -260,9 +260,13 @@ class Nonprofit < ApplicationRecord
|
|||
Houdini.intl.all_currencies[currency.downcase.to_sym][:symbol]
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :name)
|
||||
concerning :JBuilder do
|
||||
include Model::Jbuilder
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :id, :name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -302,5 +306,8 @@ private
|
|||
def user_is_valid
|
||||
(user && user.is_a?(User)) || errors.add(:user_id, "is not a valid user")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
68
app/models/offline_transaction.rb
Normal file
68
app/models/offline_transaction.rb
Normal file
|
@ -0,0 +1,68 @@
|
|||
# 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
|
||||
# rubocop:disable Metrics/BlockLength, Metrics/AbcSize, Metrics/MethodLength
|
||||
class OfflineTransaction < ApplicationRecord
|
||||
include Model::Subtransactable
|
||||
delegate :created, to: :subtransaction
|
||||
|
||||
def net_amount
|
||||
subtransaction_payments.sum(&:net_amount)
|
||||
end
|
||||
|
||||
concerning :JBuilder do
|
||||
included do
|
||||
setup_houid :offlinetrx
|
||||
end
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.type 'subtransaction'
|
||||
json.created created.to_i
|
||||
json.initial_amount do
|
||||
json.cents amount || 0
|
||||
json.currency nonprofit.currency
|
||||
end
|
||||
|
||||
json.net_amount do
|
||||
json.cents net_amount
|
||||
json.currency nonprofit.currency
|
||||
end
|
||||
|
||||
if expand.include? :payments
|
||||
json.payments subtransaction_payments do |py|
|
||||
json.merge! py.to_builder.attributes!
|
||||
end
|
||||
else
|
||||
json.payments subtransaction_payments do |py|
|
||||
json.merge! py.to_id.attributes!
|
||||
end
|
||||
end
|
||||
|
||||
json.add_builder_expansion :nonprofit, :supporter
|
||||
|
||||
json.add_builder_expansion(
|
||||
:trx,
|
||||
json_attribute: :transaction
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def to_id
|
||||
::Jbuilder.new do |json|
|
||||
json.(self, :id)
|
||||
json.object 'offline_transaction'
|
||||
json.type 'subtransaction'
|
||||
end
|
||||
end
|
||||
|
||||
def publish_created
|
||||
Houdini.event_publisher.announce(
|
||||
:offline_transaction_created,
|
||||
to_event('offline_transaction.created', :nonprofit, :trx, :supporter,
|
||||
:payments).attributes!
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable all
|
78
app/models/offline_transaction_charge.rb
Normal file
78
app/models/offline_transaction_charge.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# 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
|
||||
|
||||
# rubocop:disable Metrics/MethodLength, Metrics/BlockLength, Metrics/AbcSize
|
||||
class OfflineTransactionCharge < ApplicationRecord
|
||||
include Model::SubtransactionPaymentable
|
||||
belongs_to :payment
|
||||
|
||||
delegate :gross_amount, :net_amount, :fee_total, to: :payment
|
||||
|
||||
delegate :currency, to: :nonprofit
|
||||
|
||||
concerning :JBuilder do
|
||||
included do
|
||||
setup_houid :offtrxchrg
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.object 'offline_transaction_charge'
|
||||
json.gross_amount do
|
||||
json.cents gross_amount
|
||||
json.currency currency
|
||||
end
|
||||
|
||||
json.net_amount do
|
||||
json.cents net_amount
|
||||
json.currency currency
|
||||
end
|
||||
|
||||
json.fee_total do
|
||||
json.cents fee_total
|
||||
json.currency currency
|
||||
end
|
||||
|
||||
json.created payment.date.to_i
|
||||
|
||||
json.type 'payment'
|
||||
|
||||
json.add_builder_expansion :nonprofit, :supporter, :subtransaction
|
||||
|
||||
json.add_builder_expansion :trx, json_attribute: :transaction
|
||||
end
|
||||
end
|
||||
|
||||
def to_id
|
||||
::Jbuilder.new do |json|
|
||||
json.(self, :id)
|
||||
json.object 'offline_transaction_charge'
|
||||
json.type 'payment'
|
||||
end
|
||||
end
|
||||
|
||||
def publish_created
|
||||
Houdini.event_publisher.announce(
|
||||
:offline_transaction_charge_created,
|
||||
to_event('offline_transaction_charge.created',
|
||||
:nonprofit,
|
||||
:trx,
|
||||
:supporter,
|
||||
:subtransaction).attributes!
|
||||
)
|
||||
Houdini.event_publisher.announce(
|
||||
:payment_created,
|
||||
to_event(
|
||||
'payment.created',
|
||||
:nonprofit,
|
||||
:trx,
|
||||
:supporter,
|
||||
:subtransaction
|
||||
).attributes!
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable all
|
|
@ -7,12 +7,6 @@
|
|||
# If connected to an offsite_payment, this is money the nonprofit is recording for convenience.
|
||||
|
||||
class Payment < ApplicationRecord
|
||||
# :towards,
|
||||
# :gross_amount,
|
||||
# :refund_total,
|
||||
# :fee_total,
|
||||
# :kind,
|
||||
# :date
|
||||
|
||||
belongs_to :supporter
|
||||
belongs_to :nonprofit
|
||||
|
@ -26,4 +20,10 @@ class Payment < ApplicationRecord
|
|||
has_many :events, through: :tickets
|
||||
has_many :payment_payouts
|
||||
has_many :charges
|
||||
|
||||
has_one :subtransaction_payment
|
||||
|
||||
has_one :subtransaction, through: :subtransaction_payment
|
||||
has_one :trx, through: :subtransaction_payment
|
||||
|
||||
end
|
||||
|
|
26
app/models/subtransaction.rb
Normal file
26
app/models/subtransaction.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# 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 Subtransaction < ApplicationRecord
|
||||
include Model::CreatedTimeable
|
||||
|
||||
concerning :JBuilder do
|
||||
include Model::Houidable
|
||||
included do
|
||||
setup_houid :subtrx
|
||||
end
|
||||
end
|
||||
|
||||
belongs_to :trx, class_name: 'Transaction', foreign_key: 'transaction_id', inverse_of: :subtransactions
|
||||
has_one :supporter, through: :trx
|
||||
has_one :nonprofit, through: :trx
|
||||
|
||||
has_many :subtransaction_payments # rubocop:disable Rails/HasManyOrHasOneDependent
|
||||
|
||||
delegated_type :subtransactable, types: %w[OfflineTransaction]
|
||||
|
||||
scope :with_subtransactables, -> { includes(:subtransactable) }
|
||||
|
||||
delegate :to_builder, :to_event, :to_id, :publish_created, :publish_updated, :publish_deleted, to: :subtransactable
|
||||
end
|
28
app/models/subtransaction_payment.rb
Normal file
28
app/models/subtransaction_payment.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
# 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 SubtransactionPayment < ApplicationRecord
|
||||
include Model::Houidable
|
||||
include Model::CreatedTimeable
|
||||
|
||||
setup_houid :subtrxentity
|
||||
|
||||
belongs_to :subtransaction
|
||||
has_one :trx, class_name: 'Transaction', foreign_key: 'transaction_id', through: :subtransaction
|
||||
has_one :supporter, through: :subtransaction
|
||||
has_one :nonprofit, through: :subtransaction
|
||||
|
||||
delegated_type :paymentable, types: ['OfflineTransactionCharge']
|
||||
|
||||
delegate :gross_amount, :fee_total, :net_amount, to: :paymentable
|
||||
|
||||
scope :with_entities, -> { includes(:paymentable) }
|
||||
|
||||
delegate :to_builder,
|
||||
:to_event,
|
||||
:to_id,
|
||||
:publish_created,
|
||||
:publish_updated,
|
||||
:publish_deleted, to: :paymentable
|
||||
end
|
|
@ -54,7 +54,7 @@ class Supporter < ApplicationRecord
|
|||
validates :nonprofit, presence: true
|
||||
scope :not_deleted, -> { where(deleted: false) }
|
||||
|
||||
add_builder_expansion :nonprofit
|
||||
|
||||
|
||||
# TODO replace with Discard gem
|
||||
define_model_callbacks :discard
|
||||
|
@ -86,47 +86,56 @@ class Supporter < ApplicationRecord
|
|||
h
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
supporter_addresses = [self]
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :name, :organization, :phone, :anonymous, :deleted)
|
||||
if expand.include? :supporter_address
|
||||
json.supporter_addresses supporter_addresses do |i|
|
||||
json.merge! i.to_supporter_address_builder.attributes!
|
||||
concerning :Jbuilder do
|
||||
included do
|
||||
def to_builder(*expand)
|
||||
supporter_addresses = [self]
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :name, :organization, :phone, :anonymous, :deleted)
|
||||
json.add_builder_expansion :nonprofit, :merged_into
|
||||
if expand.include? :supporter_address
|
||||
json.supporter_addresses supporter_addresses do |i|
|
||||
json.merge! i.to_supporter_address_builder.attributes!
|
||||
end
|
||||
else
|
||||
json.supporter_addresses [id]
|
||||
end
|
||||
|
||||
# unless merged_into.nil?
|
||||
# if expand.include? :merged_into
|
||||
# json.merged_into merged_into.to_builder
|
||||
# else
|
||||
# json.merged_into merged_into.id
|
||||
# end
|
||||
# else
|
||||
# json.merged_into nil
|
||||
# end
|
||||
end
|
||||
else
|
||||
json.supporter_addresses [id]
|
||||
end
|
||||
|
||||
unless merged_into.nil?
|
||||
if expand.include? :merged_into
|
||||
json.merged_into merged_into.to_builder
|
||||
else
|
||||
json.merged_into merged_into.id
|
||||
def to_supporter_address_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :address, :state_code, :city, :country, :zip_code, :deleted)
|
||||
json.object 'supporter_address'
|
||||
if expand.include? :supporter
|
||||
json.supporter to_builder
|
||||
else
|
||||
json.supporter id
|
||||
end
|
||||
|
||||
# if expand.include? :nonprofit
|
||||
# json.nonprofit nonprofit.to_builder
|
||||
# else
|
||||
# json.nonprofit nonprofit.id
|
||||
# end
|
||||
|
||||
json.add_builder_expansion :nonprofit
|
||||
end
|
||||
else
|
||||
json.merged_into nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def to_supporter_address_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :address, :state_code, :city, :country, :zip_code, :deleted)
|
||||
json.object 'supporter_address'
|
||||
if expand.include? :supporter
|
||||
json.supporter to_builder
|
||||
else
|
||||
json.supporter id
|
||||
end
|
||||
|
||||
if expand.include? :nonprofit
|
||||
json.nonprofit nonprofit.to_builder
|
||||
else
|
||||
json.nonprofit nonprofit.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def full_address
|
||||
Format::Address.full_address(address, city, state_code)
|
||||
|
@ -167,7 +176,7 @@ class Supporter < ApplicationRecord
|
|||
|
||||
# we do something custom here since Supporter and SupporterAddress are in the same model
|
||||
def to_event(event_type, *expand)
|
||||
Jbuilder.new do |event|
|
||||
::Jbuilder.new do |event|
|
||||
event.id "objevt_" + SecureRandom.alphanumeric(22)
|
||||
event.object 'object_event'
|
||||
event.type event_type
|
||||
|
|
|
@ -18,8 +18,6 @@ class SupporterNote < ApplicationRecord
|
|||
validates :supporter_id, presence: true
|
||||
# TODO replace with Discard gem
|
||||
|
||||
add_builder_expansion :supporter, :nonprofit, :user
|
||||
|
||||
define_model_callbacks :discard
|
||||
|
||||
after_discard :publish_deleted
|
||||
|
@ -38,6 +36,8 @@ class SupporterNote < ApplicationRecord
|
|||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :deleted, :content)
|
||||
|
||||
json.add_builder_expansion :supporter, :nonprofit, :user
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@ class TagMaster < ApplicationRecord
|
|||
include Model::Eventable
|
||||
include Model::Jbuilder
|
||||
|
||||
add_builder_expansion :nonprofit
|
||||
# TODO replace with Discard gem
|
||||
define_model_callbacks :discard
|
||||
|
||||
|
@ -46,6 +45,8 @@ class TagMaster < ApplicationRecord
|
|||
init_builder(*expand) do |json|
|
||||
json.(self, :name, :deleted)
|
||||
json.object 'tag_definition'
|
||||
|
||||
json.add_builder_expansion :nonprofit
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -15,9 +15,7 @@ class TicketLevel < ApplicationRecord
|
|||
# :admin_only, #bool, only admins can create tickets for this level
|
||||
# :limit, #int: for limiting the number of tickets to be sold
|
||||
# :order #int: order in which to be displayed
|
||||
|
||||
|
||||
add_builder_expansion :nonprofit, :event
|
||||
# TODO replace with Discard gem
|
||||
define_model_callbacks :discard
|
||||
|
||||
|
@ -67,13 +65,16 @@ class TicketLevel < ApplicationRecord
|
|||
end
|
||||
json.available_to admin_only ? 'admins' : 'everyone'
|
||||
|
||||
if expand.include? :event_discounts
|
||||
json.event_discounts event_discounts do |disc|
|
||||
json.merge! disc.to_builder.attributes!
|
||||
end
|
||||
else
|
||||
json.event_discounts event_discounts.pluck(:id)
|
||||
end
|
||||
json.add_builder_expansion :nonprofit, :event
|
||||
|
||||
json.add_builder_expansion :event_discounts, enum_type: :expandable
|
||||
# if expand.include? :event_discounts
|
||||
# json.event_discounts event_discounts do |disc|
|
||||
# json.merge! disc.to_builder.attributes!
|
||||
# end
|
||||
# else
|
||||
# json.event_discounts event_discounts.pluck(:id)
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,9 +6,6 @@ class TicketPurchase < ApplicationRecord
|
|||
include Model::TrxAssignable
|
||||
setup_houid :tktpur
|
||||
|
||||
add_builder_expansion :event, :event_discount
|
||||
|
||||
|
||||
before_create :set_original_discount
|
||||
|
||||
belongs_to :event_discount
|
||||
|
@ -18,8 +15,18 @@ class TicketPurchase < ApplicationRecord
|
|||
|
||||
validates :event, presence: true
|
||||
|
||||
def to_id
|
||||
::Jbuilder.new do |json|
|
||||
json.id id
|
||||
json.object 'ticket_purchase'
|
||||
json.type 'trx_assignment'
|
||||
end
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.type 'trx_assignment'
|
||||
|
||||
json.original_discount do
|
||||
json.percent original_discount
|
||||
end if original_discount
|
||||
|
@ -29,19 +36,22 @@ class TicketPurchase < ApplicationRecord
|
|||
json.currency nonprofit.currency
|
||||
end
|
||||
|
||||
json.add_builder_expansion :event, :event_discount, :nonprofit, :supporter
|
||||
json.add_builder_expansion :ticket_to_legacy_tickets, enum_type: :expandable, json_attribute: 'tickets'
|
||||
json.add_builder_expansion :trx, json_attribute: :transaction
|
||||
|
||||
if expand.include? :tickets
|
||||
json.tickets ticket_to_legacy_tickets do |i|
|
||||
i.to_builder.attributes!
|
||||
end
|
||||
else
|
||||
json.tickets ticket_to_legacy_tickets.pluck(:id)
|
||||
end
|
||||
# if expand.include? :tickets
|
||||
# json.tickets ticket_to_legacy_tickets do |i|
|
||||
# i.to_builder.attributes!
|
||||
# end
|
||||
# else
|
||||
# json.tickets ticket_to_legacy_tickets.pluck(:id)
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
def publish_created
|
||||
Houdini.event_publisher.announce(:ticket_purchase_created, to_event('ticket_purchase.created', :event, :nonprofit, :supporter, :trx, :event_discount).attributes!)
|
||||
Houdini.event_publisher.announce(:ticket_purchase_created, to_event('ticket_purchase.created', :event, :nonprofit, :supporter, :trx, :event_discount, :ticket_to_legacy_tickets).attributes!)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -21,8 +21,6 @@ class TicketToLegacyTicket < ApplicationRecord
|
|||
|
||||
setup_houid :tkt
|
||||
|
||||
add_builder_expansion :ticket_purchase, :ticket_level, :supporter, :event, :nonprofit, :event_discount
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.(self, :checked_in, :deleted, :note)
|
||||
|
@ -38,6 +36,8 @@ class TicketToLegacyTicket < ApplicationRecord
|
|||
json.percent original_discount
|
||||
end
|
||||
end
|
||||
|
||||
json.add_builder_expansion :ticket_purchase, :ticket_level, :supporter, :event, :nonprofit, :event_discount
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,36 +3,81 @@
|
|||
# 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 Transaction < ApplicationRecord
|
||||
include Model::Houidable
|
||||
include Model::Jbuilder
|
||||
include Model::Eventable
|
||||
|
||||
setup_houid :trx
|
||||
add_builder_expansion :nonprofit, :supporter
|
||||
include Model::CreatedTimeable
|
||||
|
||||
belongs_to :supporter
|
||||
has_one :nonprofit, through: :supporter
|
||||
|
||||
has_many :transaction_assignments
|
||||
has_many :transaction_assignments, inverse_of: 'trx'
|
||||
|
||||
has_many :donations, through: :transaction_assignments, source: :assignable, source_type: 'ModernDonation'
|
||||
has_many :ticket_purchases, through: :transaction_assignments, source: :assignable, source_type: 'TicketPurchase'
|
||||
has_many :campaign_gift_purchases, through: :transaction_assignments, source: :assignable, source_type: 'CampaignGiftPurchase'
|
||||
has_many :donations, through: :transaction_assignments, source: :assignable, source_type: 'ModernDonation', inverse_of: 'trx'
|
||||
has_many :ticket_purchases, through: :transaction_assignments, source: :assignable, source_type: 'TicketPurchase', inverse_of: 'trx'
|
||||
has_many :campaign_gift_purchases, through: :transaction_assignments, source: :assignable, source_type: 'CampaignGiftPurchase', inverse_of: 'trx'
|
||||
|
||||
|
||||
has_one :subtransaction
|
||||
has_many :subtransaction_payments, through: :subtransaction
|
||||
|
||||
validates :supporter, presence: true
|
||||
|
||||
|
||||
concerning :JBuilder do
|
||||
include Model::Houidable
|
||||
include Model::Jbuilder
|
||||
include Model::Eventable
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.amount do
|
||||
json.cents amount || 0
|
||||
json.currency nonprofit.currency
|
||||
end
|
||||
included do
|
||||
setup_houid :trx
|
||||
end
|
||||
|
||||
def to_builder(*expand)
|
||||
init_builder(*expand) do |json|
|
||||
json.amount do
|
||||
json.cents amount || 0
|
||||
json.currency nonprofit.currency
|
||||
end
|
||||
json.created created.to_i
|
||||
|
||||
json.add_builder_expansion :nonprofit, :supporter, :subtransaction
|
||||
json.add_builder_expansion :subtransaction_payments, enum_type: :expandable
|
||||
json.add_builder_expansion :transaction_assignments, enum_type: :expandable
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def publish_created
|
||||
Houdini.event_publisher.announce(:transaction_created,
|
||||
to_event('transaction.created', :nonprofit, :supporter).attributes!)
|
||||
concerning :ObjectEvents do
|
||||
|
||||
include JBuilder
|
||||
def publish_created
|
||||
Houdini.event_publisher.announce(:transaction_created,
|
||||
to_event('transaction.created', :nonprofit, :supporter, :subtransaction_payments, :transaction_assignments, :subtransaction).attributes!)
|
||||
end
|
||||
|
||||
def publish_updated
|
||||
Houdini.event_publisher.announce(:transaction_updated,
|
||||
to_event('transaction.updated', :nonprofit, :supporter, :subtransaction_payments, :transaction_assignments, :subtransaction).attributes!)
|
||||
end
|
||||
|
||||
def publish_refunded
|
||||
Houdini.event_publisher.announce(:transaction_refunded,
|
||||
to_event('transaction.refunded', :nonprofit, :supporter, :subtransaction_payments, :transaction_assignments, :subtransaction).attributes!)
|
||||
end
|
||||
|
||||
def publish_disputed
|
||||
Houdini.event_publisher.announce(:transaction_disputed,
|
||||
to_event('transaction.refunded', :nonprofit, :supporter, :subtransaction_payments, :transaction_assignments).attributes!)
|
||||
end
|
||||
|
||||
def publish_deleted
|
||||
Houdini.event_publisher.announce(:transaction_deleted,
|
||||
to_event('transaction.deleted', :nonprofit, :supporter, :subtransaction_payments, :transaction_assignments).attributes!)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def set_created_if_needed
|
||||
write_attribute(:created, Time.now) unless read_attribute(:created)
|
||||
end
|
||||
end
|
||||
|
||||
ActiveSupport.run_load_hooks(:houdini_transaction, Transaction)
|
||||
|
|
|
@ -6,7 +6,16 @@ class TransactionAssignment < ApplicationRecord
|
|||
include Model::Houidable
|
||||
setup_houid :trxassign
|
||||
|
||||
belongs_to :assignable, polymorphic: true
|
||||
delegated_type :assignable, types: ['ModernDonation', 'CampaignGiftPurchase', 'TicketPurchase']
|
||||
|
||||
delegate :to_id,
|
||||
:to_builder,
|
||||
:publish_created,
|
||||
:publish_updated,
|
||||
:publish_deleted, to: :assignable
|
||||
|
||||
belongs_to :trx, class_name: 'Transaction', foreign_key: "transaction_id"
|
||||
has_one :supporter, through: :trx
|
||||
has_one :nonprofit, through: :trx
|
||||
|
||||
end
|
||||
|
|
31
db/migrate/20210329213633_add_subtransaction_hierarchy.rb
Normal file
31
db/migrate/20210329213633_add_subtransaction_hierarchy.rb
Normal file
|
@ -0,0 +1,31 @@
|
|||
# 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 AddSubtransactionHierarchy < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :subtransactions, id: :string do |t|
|
||||
t.references :transaction, foreign_key: true, type: :string, null: false
|
||||
t.references :subtransactable, polymorphic: true, type: :string, null: false, index: {unique: true}
|
||||
t.datetime "created", comment: 'the moment that the subtransaction was created. Could be earlier than created_at if the transaction was in the past.'
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table "offline_transactions", id: :string do |t|
|
||||
t.integer "amount", null: false
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :subtransaction_payments, id: :string do |t|
|
||||
t.references :subtransaction, type: :string, foreign_key: true
|
||||
t.references :paymentable, polymorphic: true, type: :string
|
||||
t.datetime "created", comment: 'the moment that the subtransaction_payment was created. Could be earlier than created_at if the transaction was in the past.'
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :offline_transaction_charges, id: :string do |t|
|
||||
t.references :payment, foreign_key: true, null: false
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
41
db/schema.rb
41
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2021_02_23_204824) do
|
||||
ActiveRecord::Schema.define(version: 2021_03_29_213633) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
|
@ -599,6 +599,19 @@ ActiveRecord::Schema.define(version: 2021_02_23_204824) do
|
|||
t.index ["nonprofit_id"], name: "index_object_event_hook_configs_on_nonprofit_id"
|
||||
end
|
||||
|
||||
create_table "offline_transaction_charges", id: :string, force: :cascade do |t|
|
||||
t.bigint "payment_id", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["payment_id"], name: "index_offline_transaction_charges_on_payment_id"
|
||||
end
|
||||
|
||||
create_table "offline_transactions", id: :string, force: :cascade do |t|
|
||||
t.integer "amount", null: false
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
end
|
||||
|
||||
create_table "offsite_payments", id: :serial, force: :cascade do |t|
|
||||
t.integer "gross_amount"
|
||||
t.string "kind", limit: 255
|
||||
|
@ -769,6 +782,28 @@ ActiveRecord::Schema.define(version: 2021_02_23_204824) do
|
|||
t.index ["tokenizable_id", "tokenizable_type"], name: "index_source_tokens_on_tokenizable_id_and_tokenizable_type"
|
||||
end
|
||||
|
||||
create_table "subtransaction_payments", id: :string, force: :cascade do |t|
|
||||
t.string "subtransaction_id"
|
||||
t.string "paymentable_type"
|
||||
t.string "paymentable_id"
|
||||
t.datetime "created", comment: "the moment that the subtransaction_payment was created. Could be earlier than created_at if the transaction was in the past."
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["paymentable_type", "paymentable_id"], name: "index_subtransaction_payments_on_paymentable"
|
||||
t.index ["subtransaction_id"], name: "index_subtransaction_payments_on_subtransaction_id"
|
||||
end
|
||||
|
||||
create_table "subtransactions", id: :string, force: :cascade do |t|
|
||||
t.string "transaction_id", null: false
|
||||
t.string "subtransactable_type", null: false
|
||||
t.string "subtransactable_id", null: false
|
||||
t.datetime "created", comment: "the moment that the subtransaction was created. Could be earlier than created_at if the transaction was in the past."
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["subtransactable_type", "subtransactable_id"], name: "index_subtransactions_on_subtransactable", unique: true
|
||||
t.index ["transaction_id"], name: "index_subtransactions_on_transaction_id"
|
||||
end
|
||||
|
||||
create_table "supporter_notes", id: :serial, force: :cascade do |t|
|
||||
t.text "content"
|
||||
t.integer "supporter_id"
|
||||
|
@ -925,6 +960,7 @@ ActiveRecord::Schema.define(version: 2021_02_23_204824) do
|
|||
t.integer "amount"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.datetime "created", comment: "the moment that the offline_transaction was created. Could be earlier than created_at if the transaction was in the past."
|
||||
t.index ["supporter_id"], name: "index_transactions_on_supporter_id"
|
||||
end
|
||||
|
||||
|
@ -971,6 +1007,9 @@ ActiveRecord::Schema.define(version: 2021_02_23_204824) do
|
|||
add_foreign_key "modern_campaign_gifts", "campaign_gift_purchases"
|
||||
add_foreign_key "modern_campaign_gifts", "campaign_gifts"
|
||||
add_foreign_key "object_event_hook_configs", "nonprofits"
|
||||
add_foreign_key "offline_transaction_charges", "payments"
|
||||
add_foreign_key "subtransaction_payments", "subtransactions"
|
||||
add_foreign_key "subtransactions", "transactions"
|
||||
add_foreign_key "ticket_purchases", "event_discounts"
|
||||
add_foreign_key "ticket_purchases", "events"
|
||||
add_foreign_key "ticket_to_legacy_tickets", "ticket_purchases"
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HouID, HoudiniObject, HoudiniEvent, Amount, IDType} from '../../common';
|
||||
import type Nonprofit from '..';
|
||||
import type Campaign from '.';
|
||||
import type { CampaignGiftPurchase, CampaignGiftOption } from '.';
|
||||
import type Supporter from '../Supporter';
|
||||
import { TrxDescendent } from '../Transaction';
|
||||
|
||||
export interface TransactionAddress {
|
||||
address: string;
|
||||
|
@ -13,7 +12,7 @@ export interface TransactionAddress {
|
|||
zip_code: string;
|
||||
}
|
||||
|
||||
export interface CampaignGift extends HoudiniObject<HouID> {
|
||||
export interface CampaignGift extends HoudiniObject<HouID>, TrxDescendent {
|
||||
address?: TransactionAddress;
|
||||
amount: Amount;
|
||||
campaign: IDType | Campaign;
|
||||
|
@ -21,9 +20,7 @@ export interface CampaignGift extends HoudiniObject<HouID> {
|
|||
campaign_gift_purchase: IDType | CampaignGiftPurchase;
|
||||
deleted: boolean;
|
||||
event: IDType | Event;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'campaign_gift';
|
||||
supporter: IDType | Supporter;
|
||||
}
|
||||
|
||||
export type CampaignGiftCreated = HoudiniEvent<'campaign_gift.created', CampaignGift>;
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HouID, HoudiniObject, HoudiniEvent, Amount, IDType} from '../../common';
|
||||
import type { HouID, HoudiniEvent, Amount, IDType} from '../../common';
|
||||
import type Nonprofit from '..';
|
||||
import type Campaign from '.';
|
||||
import type { CampaignGift } from '.';
|
||||
import Supporter from '../Supporter';
|
||||
import { Transaction } from '../Supporter';
|
||||
import type { TrxAssignment } from '../Transaction';
|
||||
|
||||
|
||||
export interface CampaignGiftPurchase extends HoudiniObject<HouID> {
|
||||
export interface CampaignGiftPurchase extends TrxAssignment {
|
||||
amount: Amount;
|
||||
campaign: IDType | Campaign;
|
||||
campaign_gifts: HouID[] | CampaignGift[];
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'campaign_gift_purchase';
|
||||
supporter: IDType | Supporter;
|
||||
transaction: HouID | Transaction;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import { IDType, HoudiniObject } from '../../common';
|
||||
import Nonprofit from '../';
|
||||
import Nonprofit from '..';
|
||||
|
||||
export default interface Campaign extends HoudiniObject {
|
||||
name: string;
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HouID, HoudiniObject, HoudiniEvent, Amount, IDType} from '../../common';
|
||||
import type Nonprofit from '..';
|
||||
import type Event from '.';
|
||||
import type { TicketLevel , TicketPurchase} from '.';
|
||||
import type Supporter from '../Supporter';
|
||||
import { TrxDescendent } from '../Transaction';
|
||||
|
||||
export interface Ticket extends HoudiniObject<HouID> {
|
||||
export interface Ticket extends HoudiniObject<HouID>, TrxDescendent {
|
||||
amount: Amount;
|
||||
checked_in: boolean;
|
||||
deleted: boolean;
|
||||
event: IDType | Event;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
note: string;
|
||||
object: 'ticket';
|
||||
supporter: IDType | Supporter;
|
||||
ticket_level: IDType | TicketLevel;
|
||||
ticket_purchase: HouID | TicketPurchase;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,14 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HouID, HoudiniObject, HoudiniEvent, Amount, IDType} from '../../common';
|
||||
import type Nonprofit from '..';
|
||||
import type { HouID, HoudiniEvent, IDType} from '../../common';
|
||||
import type Event from '.';
|
||||
import type { EventDiscount, Ticket } from '.';
|
||||
import Supporter from '../Supporter';
|
||||
import { Transaction } from '../Supporter';
|
||||
import type { TrxAssignment } from '../Transaction';
|
||||
|
||||
|
||||
export interface TicketPurchase extends HoudiniObject<HouID> {
|
||||
amount: Amount;
|
||||
export interface TicketPurchase extends TrxAssignment {
|
||||
event: IDType | Event;
|
||||
event_discount?: IDType | EventDiscount | null;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'ticket_purchase';
|
||||
supporter: IDType | Supporter;
|
||||
tickets: Ticket[] | HouID[];
|
||||
transaction: HouID | Transaction;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import { IDType, HoudiniObject } from '../../common';
|
||||
import Nonprofit from '../';
|
||||
import Nonprofit from '..';
|
||||
|
||||
export default interface Event extends HoudiniObject {
|
||||
end_date: Date;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { IDType, HoudiniObject, HoudiniEvent } from '../../common';
|
||||
import type Nonprofit from '..';
|
||||
import Supporter from '.';
|
||||
import Supporter from '..';
|
||||
import type { User } from '../../User';
|
||||
|
||||
export interface SupporterNote extends HoudiniObject {
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { Amount, HoudiniObject, IDType, HouID } from "../../common";
|
||||
import type Nonprofit from '..';
|
||||
import type Supporter from ".";
|
||||
|
||||
/**
|
||||
* Represents a transaction made by a supporter
|
||||
*/
|
||||
export interface Transaction extends HoudiniObject<HouID> {
|
||||
amount: Amount;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'transaction';
|
||||
payment_methods: Array<unknown>;
|
||||
payments: Array<unknown>;
|
||||
status: string;
|
||||
supporter: IDType | Supporter;
|
||||
transaction_assignments: Array<unknown>;
|
||||
}
|
|
@ -4,6 +4,7 @@ import type Nonprofit from '../';
|
|||
import type { SupporterAddress } from './SupporterAddress';
|
||||
|
||||
export default interface Supporter extends HoudiniObject {
|
||||
addresses: IDType[] | SupporterAddress[];
|
||||
anonymous: boolean;
|
||||
deleted: boolean;
|
||||
email: string;
|
||||
|
@ -13,7 +14,6 @@ export default interface Supporter extends HoudiniObject {
|
|||
object: "supporter";
|
||||
organization: string;
|
||||
phone: string;
|
||||
supporter_addresses: IDType[] | SupporterAddress[];
|
||||
}
|
||||
|
||||
export type SupporterCreated = HoudiniEvent<'supporter_address.created', Supporter>;
|
||||
|
@ -22,4 +22,3 @@ export type SupporterDeleted = HoudiniEvent<'supporter_address.deleted', Support
|
|||
|
||||
export * from './SupporterNote';
|
||||
export * from './SupporterAddress';
|
||||
export * from './Transaction';
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
// 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 './';
|
||||
import type { HoudiniEvent } from "../../common";
|
||||
import type { TrxAssignment } from './';
|
||||
|
||||
interface Dedication {
|
||||
contact?: {
|
||||
|
@ -15,14 +13,10 @@ interface Dedication {
|
|||
type: 'honor' | 'memory';
|
||||
}
|
||||
|
||||
export interface Donation extends HoudiniObject<HouID> {
|
||||
amount: Amount;
|
||||
export interface Donation extends TrxAssignment {
|
||||
dedication?: Dedication | null;
|
||||
designation?: string | null;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'donation';
|
||||
supporter: IDType | Supporter;
|
||||
transaction: HouID | Transaction;
|
||||
}
|
||||
|
||||
export type DonationCreated = HoudiniEvent<'donation.created', Donation>;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HoudiniEvent } from "../../../common";
|
||||
import type { CommonOfflineTransactionPayment } from '.';
|
||||
|
||||
export interface Charge extends CommonOfflineTransactionPayment {
|
||||
object: 'offline_transaction_charge';
|
||||
}
|
||||
|
||||
export type ChargeCreated = HoudiniEvent<'offline_transaction_charge.created', Charge>;
|
||||
export type ChargeUpdated = HoudiniEvent<'offline_transaction_charge.updated', Charge>;
|
||||
export type ChargeDeleted = HoudiniEvent<'offline_transaction_charge.deleted', Charge>;
|
|
@ -0,0 +1,11 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HoudiniEvent } from "../../../common";
|
||||
import type { CommonOfflineTransactionPayment } from '.';
|
||||
|
||||
export interface Dispute extends CommonOfflineTransactionPayment {
|
||||
object: 'offline_transaction_dispute';
|
||||
}
|
||||
|
||||
export type DisputeCreated = HoudiniEvent<'offline_transaction_dispute.created', Dispute>;
|
||||
export type DisputeUpdated = HoudiniEvent<'offline_transaction_dispute.updated', Dispute>;
|
||||
export type DisputeDeleted = HoudiniEvent<'offline_transaction_dispute.deleted', Dispute>;
|
|
@ -0,0 +1,11 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HoudiniEvent } from "../../../common";
|
||||
import type { CommonOfflineTransactionPayment } from '.';
|
||||
|
||||
export interface Refund extends CommonOfflineTransactionPayment {
|
||||
object: 'offline_transaction_refund';
|
||||
}
|
||||
|
||||
export type RefundCreated = HoudiniEvent<'offline_transaction_refund.created', Refund>;
|
||||
export type RefundUpdated = HoudiniEvent<'offline_transaction_refund.updated', Refund>;
|
||||
export type RefundDeleted = HoudiniEvent<'offline_transaction_refund.deleted', Refund>;
|
|
@ -0,0 +1,32 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { HouID, HoudiniEvent } from "../../../common";
|
||||
import type { Payment, Subtransaction} from "..";
|
||||
import type { Charge, Refund, Dispute } from '.';
|
||||
|
||||
export interface CommonOfflineTransactionPayment extends Payment {
|
||||
// The kind of offline charge. Could be cash, check or something else
|
||||
// NOT implemented yet
|
||||
kind: string|null;
|
||||
// NOT implemented yet
|
||||
// the ID related to the kind. As example, you could put a check number here.
|
||||
kind_id: string|null;
|
||||
subtransaction: HouID | OfflineTransaction;
|
||||
}
|
||||
|
||||
export default interface OfflineTransaction extends Subtransaction {
|
||||
charges: HouID[] | Charge[];
|
||||
deleted: boolean;
|
||||
disputes: HouID[] | Dispute[];
|
||||
object: 'offline_transaction';
|
||||
refunds: HouID[] | Refund[];
|
||||
}
|
||||
|
||||
export type OfflineTransactionCreated = HoudiniEvent<'offline_transaction.created', OfflineTransaction>;
|
||||
export type OfflineTransactionUpdated = HoudiniEvent<'offline_transaction.updated', OfflineTransaction>;
|
||||
export type OfflineTransactionRefunded = HoudiniEvent<'offline_transaction.refunded', OfflineTransaction>;
|
||||
export type OfflineTransactionDisputed = HoudiniEvent<'offline_transaction.disputed', OfflineTransaction>;
|
||||
export type OfflineTransactionDeleted = HoudiniEvent<'offline_transaction.deleted', OfflineTransaction>;
|
||||
|
||||
export * from './Charge';
|
||||
export * from './Dispute';
|
||||
export * from './Refund';
|
|
@ -1,16 +1,19 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { Amount, HoudiniObject, IDType, HouID } from "../../common";
|
||||
import type Nonprofit from '../';
|
||||
import type Supporter from "../Supporter";
|
||||
import type Transaction from './';
|
||||
|
||||
|
||||
export interface Payment extends HoudiniObject<HouID> {
|
||||
amount: Amount;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'payment';
|
||||
supporter: IDType | Supporter;
|
||||
transaction: HouID | Transaction;
|
||||
import type { Amount, HoudiniObject, HouID, HoudiniEvent } from "../../common";
|
||||
import type { Subtransaction, TrxDescendent } from ".";
|
||||
|
||||
export interface Payment extends HoudiniObject<HouID>, TrxDescendent {
|
||||
created: number;
|
||||
deleted: boolean;
|
||||
fees: Amount;
|
||||
gross_amount: Amount;
|
||||
net_amount: Amount;
|
||||
status: string;
|
||||
subtransaction: HouID | Subtransaction;
|
||||
type: 'payment';
|
||||
}
|
||||
|
||||
export type PaymentCreated = HoudiniEvent<'payment.created', Payment>;
|
||||
export type PaymentUpdated = HoudiniEvent<'payment.updated', Payment>;
|
||||
export type PaymentDeleted = HoudiniEvent<'payment.deleted', Payment>;
|
||||
|
||||
|
|
438
docs/event_definitions/Nonprofit/Transaction/examples.ts
Normal file
438
docs/event_definitions/Nonprofit/Transaction/examples.ts
Normal file
|
@ -0,0 +1,438 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
// Supporter wants to make a single donation to Nonprofit with ID 1
|
||||
|
||||
/* IN PROGRESS - some examples of transaction requests */
|
||||
|
||||
|
||||
/* we disable since this is just an example file */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any*/
|
||||
|
||||
import Transaction, { Donation, OfflineTransaction } from ".";
|
||||
import {Charge, Charge as OfflineTransactionCharge} from './OfflineTransaction';
|
||||
|
||||
//`POST api/nonprofit/1/transaction`
|
||||
|
||||
|
||||
const donation_request = {
|
||||
|
||||
// donation is a `transaction assignment`. Others are `campaign_gift_purchase`
|
||||
// or `ticket_purchase`
|
||||
donations: [{
|
||||
// since this is in the nonprofit's currency they could do `amount: 10000` and
|
||||
// we'll autoexpand
|
||||
// if there's only one transaction assignment, this can be figured out.
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
designation: 'Special account',
|
||||
}],
|
||||
|
||||
offline_transactions: [{
|
||||
// this amount must match all of the transaction assignments
|
||||
amount: 10000,
|
||||
method: 'check',
|
||||
}],
|
||||
// information about the supporter donating. This either creates a new supporter
|
||||
// or finds one with the same email
|
||||
supporter: {
|
||||
email: 'penelope@fightingpoverty.org',
|
||||
name: 'Penelope Schultz',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const donation_result: Transaction = {
|
||||
object: 'transaction',
|
||||
id: 'trx_313435ncan',
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
// timestamp
|
||||
created: 124541254,
|
||||
deleted: false,
|
||||
transaction_assignments: [{
|
||||
object: 'donation',
|
||||
id: 'don_2454',
|
||||
// from transaction
|
||||
supporter: 340,
|
||||
// from supporter
|
||||
nonprofit: 1235,
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
designation: 'Special account',
|
||||
// it's a transaction assignment.
|
||||
type: 'trx_assignment',
|
||||
transaction: 'trx_313425ncan',
|
||||
} as Donation],
|
||||
|
||||
subtransaction: {
|
||||
id: 'offltrx_415h5io',
|
||||
object: 'offline_transaction',
|
||||
deleted: false,
|
||||
created: 4144,
|
||||
// based upon adding up the first charge
|
||||
original_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
|
||||
amount_disputed: {
|
||||
cents: 0,
|
||||
currency: 'usd',
|
||||
},
|
||||
amount_pending: {
|
||||
cents: 0,
|
||||
currency: 'usd',
|
||||
},
|
||||
amount_refunded: {
|
||||
cents: 0,
|
||||
currency: 'usd',
|
||||
},
|
||||
fee_total: {
|
||||
cents: 0,
|
||||
currency: 'usd',
|
||||
},
|
||||
//this is based upon adding up all of the charges, refunds, disputes and adjustments
|
||||
net_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
// method: 'check',
|
||||
// status: 'success',
|
||||
disputes: [],
|
||||
refunds: [],
|
||||
charges: [
|
||||
{
|
||||
id: 'offchrg_4325n3fnfewE',
|
||||
object: 'offline_transaction_charge',
|
||||
// gross_amount - fees
|
||||
net_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
gross_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
fees: { cents: 0, currency: 'usd'},
|
||||
//from transaction
|
||||
supporter: 340,
|
||||
// from supporter
|
||||
nonprofit: 1235,
|
||||
// from subtransaction
|
||||
transaction: 'trx_313435ncan',
|
||||
subtransaction: 'offltrx_415h5io',
|
||||
type: 'payment',
|
||||
//timestamp of creation
|
||||
created: 133543588,
|
||||
},
|
||||
] as Charge[],
|
||||
//it's a subtransaction
|
||||
type: 'subtransaction',
|
||||
//from transaction
|
||||
supporter: 340,
|
||||
// from supporter
|
||||
nonprofit: 1235,
|
||||
transaction: 'trx_313435ncan',
|
||||
} as OfflineTransaction,
|
||||
|
||||
nonprofit: {
|
||||
id: 1235,
|
||||
object: 'nonprofit',
|
||||
name: "Nonprofit's name",
|
||||
},
|
||||
|
||||
// we include all payments here from all of the subtransactions for ease of use
|
||||
subtransaction_payments: [{
|
||||
id: 'offchrg_4325n3fnfewE',
|
||||
object: 'offline_transaction_charge',
|
||||
deleted: false,
|
||||
amount:{
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
amount_pending: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
// gross_amount - fees
|
||||
net_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
gross_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
fees: null,
|
||||
status: 'success',
|
||||
subtransaction_entity: 'offchrg_4325n3fnfewE',
|
||||
// from subtransaction_entity
|
||||
subtransaction: 'offltrx_415h5io',
|
||||
//from transaction
|
||||
supporter: 340,
|
||||
// from supporter
|
||||
nonprofit: 1235,
|
||||
// subtransaction
|
||||
transaction: 'trx_313435ncan',
|
||||
created: 133543588,
|
||||
type: 'payment',
|
||||
} as OfflineTransactionCharge],
|
||||
// information about the supporter donating
|
||||
supporter: {
|
||||
id: 340,
|
||||
email: 'penelope@fightingpoverty.org',
|
||||
name: 'Penelope Schultz',
|
||||
nonprofit: 1235,
|
||||
anonymous: false,
|
||||
deleted: false,
|
||||
merged_into: null,
|
||||
organization: null,
|
||||
phone: null,
|
||||
object: 'supporter',
|
||||
addresses: [],
|
||||
},
|
||||
} as Transaction;
|
||||
|
||||
|
||||
// Supporter wants to make a ticket purchase from Nonprofit 1 and tickets are available
|
||||
|
||||
const ticket_purchase_request:any = {
|
||||
// donation is a `transaction assignment`. Others are `campaign_gift_purchase`
|
||||
// or `ticket_purchase`
|
||||
ticket_purchase: {
|
||||
// since this is in the nonprofit's currency they could do `amount: 10000` and
|
||||
// we'll autoexpand
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
event: 1,
|
||||
ticket_requests: [{
|
||||
ticket_level: 'tktlvl_r3453j90942',
|
||||
quantity: 5,
|
||||
note: "Seat with Penelope's Father",
|
||||
},
|
||||
{
|
||||
ticket_level: 'tktlvl_434',
|
||||
quantity: 1,
|
||||
}],
|
||||
},
|
||||
|
||||
offline_transactions: [{
|
||||
// this amount must match all of the transaction assignments
|
||||
amount: 10000,
|
||||
method: 'check',
|
||||
}],
|
||||
// information about the supporter donating. This either creates a new supporter
|
||||
// or finds one with the same email
|
||||
supporter: {
|
||||
email: 'penelope@fightingpoverty.org',
|
||||
name: 'Penelope Schultz',
|
||||
},
|
||||
};
|
||||
|
||||
const ticket_purchase_result:Transaction = {
|
||||
object: 'transaction',
|
||||
id: 'trx_313435ncan',
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
ticket_purchases: [{
|
||||
// since this is in the nonprofit's currency they could do `amount: 10000` and
|
||||
// we'll autoexpand
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
event: 1,
|
||||
tickets: [{
|
||||
amount: {
|
||||
cents: 2000,
|
||||
currency: 'usd',
|
||||
},
|
||||
ticket_level: 'tktlvl_r3453j90942',
|
||||
checked_in: false,
|
||||
deleted: false,
|
||||
note: "Seat with Penelope's Father",
|
||||
id: 'tkt_werikhti35N',
|
||||
},
|
||||
{
|
||||
amount: {
|
||||
cents: 2000,
|
||||
currency: 'usd',
|
||||
},
|
||||
ticket_level: 'tktlvl_r3453j90942',
|
||||
checked_in: false,
|
||||
deleted: false,
|
||||
note: "Seat with Penelope's Father",
|
||||
id: 'tkt_werikVti35N',
|
||||
},
|
||||
//... and 3 more for tktlvl_r3453j90942
|
||||
{
|
||||
ticket_level: 'tktlvl_434',
|
||||
checked_in: false,
|
||||
deleted: false,
|
||||
id: 'tkt_535nrfuoh',
|
||||
amount: {
|
||||
cents: 0,
|
||||
currency: 'usd',
|
||||
},
|
||||
}],
|
||||
|
||||
id: 'tktpur_34235nrf',
|
||||
object: 'ticket_purchase',
|
||||
subtype: 'trx_assignment',
|
||||
}],
|
||||
offline_transactions: [{
|
||||
id: 'offltrx_415h5io',
|
||||
object: 'offline_transasction',
|
||||
// based upon adding up the first charge
|
||||
original_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
//this is based upon adding up all of the charges, refunds, disputes and adjustments
|
||||
net_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
method: 'check',
|
||||
status: 'success',
|
||||
charges: [
|
||||
{
|
||||
id: 'offchrg_4325n3fnfewE',
|
||||
object: 'offline_charge',
|
||||
// gross_amount - fees
|
||||
net_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
gross_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
fees: null,
|
||||
status: 'success',
|
||||
//from transaction
|
||||
supporter: 340,
|
||||
// from supporter
|
||||
nonprofit: 1235,
|
||||
// from subtransaction
|
||||
transaction: 'trx_313435ncan',
|
||||
subtransaction: 'offltrx_415h5io',
|
||||
subtype: 'payment',
|
||||
//timestamp of creation
|
||||
created: 133543588,
|
||||
},
|
||||
],
|
||||
//it's a subtransaction
|
||||
subtype: 'subtransaction',
|
||||
//from transaction
|
||||
supporter: 340,
|
||||
// from supporter
|
||||
nonprofit: 1235,
|
||||
transaction: 'trx_313435ncan',
|
||||
}],
|
||||
|
||||
nonprofit: {
|
||||
id: 1235,
|
||||
object: 'nonprofit',
|
||||
name: "Nonprofit's name",
|
||||
},
|
||||
|
||||
// we include all payments here from all of the subtransactions for ease of use
|
||||
subtransaction_payments: [{
|
||||
id: 'offchrg_4325n3fnfewE',
|
||||
object: 'offline_charge',
|
||||
// gross_amount - fees
|
||||
net_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
gross_amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
fees: null,
|
||||
status: 'success',
|
||||
// from subtransaction_entity
|
||||
subtransaction: 'offltrx_415h5io',
|
||||
//from transaction
|
||||
supporter: 340,
|
||||
// from supporter
|
||||
nonprofit: 1235,
|
||||
// subtransaction
|
||||
transaction: 'trx_313435ncan',
|
||||
created: 133543588,
|
||||
}],
|
||||
// information about the supporter donating
|
||||
supporter: {
|
||||
id: 340,
|
||||
email: 'penelope@fightingpoverty.org',
|
||||
name: 'Penelope Schultz',
|
||||
nonprofit: 1235,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
// Supporter wants to make a ticket purchase from Nonprofit 1 and tickets are NOT available
|
||||
|
||||
// TODO
|
||||
|
||||
|
||||
// Supporter wants to make a Stripe charge for a campaign_gift from Nonprofit 1
|
||||
|
||||
const stripe_campaign_gift_request:any = {
|
||||
campaign_gift_purchase: {
|
||||
// since this is in the nonprofit's currency they could do `amount: 10000` and
|
||||
// we'll autoexpand
|
||||
amount: {
|
||||
cents: 10000,
|
||||
currency: 'usd',
|
||||
},
|
||||
campaign: 1,
|
||||
campaign_gifts: [{
|
||||
campaign_gift_option: 'cgo_535n35n',
|
||||
}],
|
||||
},
|
||||
|
||||
stripe_transactions: [
|
||||
{
|
||||
amount: 10000,
|
||||
},
|
||||
],
|
||||
// information about the supporter donating. This either creates a new supporter
|
||||
// or finds one with the same email
|
||||
supporter: {
|
||||
email: 'penelope@fightingpoverty.org',
|
||||
name: 'Penelope Schultz',
|
||||
},
|
||||
};
|
||||
|
||||
const stripe_campaign_gift_result:any = {
|
||||
// donation is a `transaction assignment`. Others are `campaign_gift_purchase`
|
||||
// or `ticket_purchase`
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// Supporter wants to start a recurring donation and charge immediately
|
||||
|
||||
//`POST /nonprofit/1/recurring_donation`
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,20 +1,77 @@
|
|||
// License: LGPL-3.0-or-later
|
||||
import type { Amount, HoudiniObject, IDType, HouID } from "../../common";
|
||||
import type { Amount, HoudiniObject, IDType, HouID, HoudiniEvent } from "../../common";
|
||||
import type Nonprofit from '../';
|
||||
import type Supporter from "../Supporter";
|
||||
import type { Payment } from "./Payment";
|
||||
|
||||
export default interface Transaction extends HoudiniObject<HouID> {
|
||||
amount: Amount;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'transaction';
|
||||
payments: IDType[] | Payment[];
|
||||
status: "not-submitted"|"created" | "waiting-on-supporter" | "failed" | "completed";
|
||||
supporter: IDType | Supporter;
|
||||
/**
|
||||
* We don't specify more for now
|
||||
*/
|
||||
transaction_assignments: { id: HouID,object: string }[];
|
||||
export interface Subtransaction extends HoudiniObject<HouID>, TrxDescendent {
|
||||
amount: Amount;
|
||||
amount_disputed: Amount;
|
||||
amount_pending: Amount;
|
||||
amount_refunded: Amount;
|
||||
created: number;
|
||||
fee_total: Amount;
|
||||
net_amount: Amount;
|
||||
type: 'subtransaction';
|
||||
}
|
||||
|
||||
/**
|
||||
* Every descendent of a Transaction object will have the following three fields
|
||||
*/
|
||||
export interface TrxDescendent {
|
||||
/**
|
||||
* The nonprofit of the transaction is assigned to.
|
||||
*/
|
||||
nonprofit: IDType | Nonprofit;
|
||||
/**
|
||||
* The supporter of the transaction
|
||||
*/
|
||||
supporter: IDType | Supporter;
|
||||
/**
|
||||
* The transaction itself
|
||||
*/
|
||||
transaction: HouID | Transaction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every transaction assignment, including Donation, TicketPurchase, CampaignGiftPurchase
|
||||
* must have an amount and the type 'trx_assignment' set.
|
||||
*/
|
||||
export interface TrxAssignment extends HoudiniObject<HouID>, TrxDescendent {
|
||||
amount: Amount;
|
||||
type: 'trx_assignment';
|
||||
}
|
||||
|
||||
export interface TrxAssignmentAsId extends HoudiniObject<HouID> {
|
||||
type: 'trx_assignment';
|
||||
}
|
||||
|
||||
export interface SubtransactionAsId extends HoudiniObject<HouID> {
|
||||
type: 'subtransaction';
|
||||
}
|
||||
|
||||
export default interface Transaction extends HoudiniObject<HouID> {
|
||||
amount: Amount;
|
||||
// amount_disputed: Amount;
|
||||
// amount_refunded: Amount;
|
||||
created: number;
|
||||
deleted: boolean;
|
||||
// net_amount: Amount;
|
||||
nonprofit: IDType | Nonprofit;
|
||||
object: 'transaction';
|
||||
subtransaction: SubtransactionAsId | Subtransaction;
|
||||
subtransaction_payments: IDType[] | Payment[];
|
||||
supporter: IDType | Supporter;
|
||||
transaction_assignments: TrxAssignmentAsId[] | TrxAssignment[];
|
||||
}
|
||||
|
||||
export type TransactionCreated = HoudiniEvent<'transaction.created', Transaction>;
|
||||
export type TransactionUpdated = HoudiniEvent<'transaction.updated', Transaction>;
|
||||
export type TransactionRefunded = HoudiniEvent<'transaction.refunded', Transaction>;
|
||||
export type TransactionDisputed = HoudiniEvent<'transaction.disputed', Transaction>;
|
||||
export type TransactionDeleted = HoudiniEvent<'transaction.deleted', Transaction>;
|
||||
|
||||
export * from './Payment';
|
||||
export * from './Donation';
|
||||
export * from './OfflineTransaction';
|
||||
export {default as OfflineTransaction} from './OfflineTransaction';
|
||||
|
|
|
@ -15,7 +15,7 @@ export type HouID = string;
|
|||
* Describes a monetary value in the minimum unit for this current. Corresponds to Money class in
|
||||
* Ruby and Typescript
|
||||
*/
|
||||
export type Amount = { cents: string, currency: string };
|
||||
export type Amount = { cents: number, currency: string };
|
||||
|
||||
/**
|
||||
* A more flexible version of Amount. In cases where we can assume what the currency is,
|
||||
|
@ -72,6 +72,7 @@ export interface HoudiniObject<ID extends IDType|HouID=IDType> {
|
|||
object: string;
|
||||
}
|
||||
|
||||
export type PolymorphicID<ID extends IDType|HouID=IDType> = HoudiniObject<ID>;
|
||||
|
||||
type HoudiniObjectOfAllIDs = HoudiniObject<IDType> | HoudiniObject<HouID>;
|
||||
/**
|
||||
|
|
|
@ -49,6 +49,7 @@ module InsertDonation
|
|||
trx.save!
|
||||
don.save!
|
||||
don.publish_created
|
||||
trx.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
|
||||
|
@ -77,6 +78,9 @@ module InsertDonation
|
|||
|
||||
data = date_from_data(data)
|
||||
result = { 'donation' => insert_donation(data.except('offsite_payment'), entities) }
|
||||
trx = entities[:supporter_id].transactions.build(amount: data['amount'], created: data['date'])
|
||||
don = trx.donations.build(amount: result['donation'].amount, legacy_donation: result['donation'])
|
||||
|
||||
result['payment'] = insert_payment('OffsitePayment', 0, result['donation']['id'], data)
|
||||
result['offsite_payment'] = Psql.execute(
|
||||
Qexpr.new.insert(:offsite_payments, [
|
||||
|
@ -90,8 +94,23 @@ module InsertDonation
|
|||
)
|
||||
]).returning('*')
|
||||
).first
|
||||
|
||||
off_t = trx.build_subtransaction(
|
||||
subtransactable: OfflineTransaction.new(amount: data['amount']),
|
||||
subtransaction_payments:[
|
||||
SubtransactionPayment.new(
|
||||
paymentable: OfflineTransactionCharge.new(payment: Payment.find(result['payment']['id'])))
|
||||
],
|
||||
created: data['date']
|
||||
);
|
||||
trx.save!
|
||||
don.save!
|
||||
off_t.save!
|
||||
off_t.subtransaction_payments.each{|stp| stp.publish_created}
|
||||
off_t.publish_created
|
||||
don.publish_created
|
||||
trx.publish_created
|
||||
result['activity'] = InsertActivities.for_offsite_donations([result['payment']['id']])
|
||||
WeMoveExecuteForDonationsJob.perform_later(result['donation'])
|
||||
{ status: 200, json: result }
|
||||
end
|
||||
|
||||
|
@ -159,7 +178,7 @@ module InsertDonation
|
|||
end
|
||||
end
|
||||
|
||||
# Insert a payment row for a donation
|
||||
# Insert a payment row for a donationValidationError
|
||||
def self.insert_payment(kind, fee_total, donation_id, data)
|
||||
Psql.execute(
|
||||
Qexpr.new.insert(:payments, [{
|
||||
|
|
9
spec/factories/offline_transaction_charges.rb
Normal file
9
spec/factories/offline_transaction_charges.rb
Normal file
|
@ -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 :offline_transaction_charge do
|
||||
amount { 1 }
|
||||
end
|
||||
end
|
9
spec/factories/offline_transactions.rb
Normal file
9
spec/factories/offline_transactions.rb
Normal file
|
@ -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 :offline_transaction do
|
||||
amount { 1 }
|
||||
end
|
||||
end
|
|
@ -2,150 +2,600 @@
|
|||
|
||||
# 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
|
||||
|
||||
# rubocop:disable RSpec/MessageSpies, RSpec/NamedSubject, RSpec/MultipleExpectations,RSpec/MultipleMemoizedHelpers, RSpec/ExpectInHook
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = nil
|
||||
describe InsertDonation do
|
||||
describe '.with_stripe' do
|
||||
before(:each) do
|
||||
Houdini.payment_providers.stripe.connect = true
|
||||
end
|
||||
|
||||
include_context :shared_rd_donation_value_context
|
||||
describe '.with_stripe' do
|
||||
before do
|
||||
Houdini.payment_providers.stripe.connect = true
|
||||
end
|
||||
|
||||
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
|
||||
include_context :shared_rd_donation_value_context
|
||||
|
||||
it 'errors out if token is invalid' do
|
||||
validation_invalid_token { InsertDonation.with_stripe(amount: 1, nonprofit_id: 1, supporter_id: 1, token: fake_uuid) }
|
||||
end
|
||||
describe 'param validation' do
|
||||
before do
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:transaction_created, any_args)
|
||||
end
|
||||
|
||||
it 'errors out if token is unauthorized' do
|
||||
validation_unauthorized { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: 1, supporter_id: 1, token: fake_uuid) }
|
||||
end
|
||||
it 'does basic validation' do
|
||||
validation_basic_validation do
|
||||
described_class.with_stripe(designation: 34_124, dedication: 35_141, event_id: 'bad', campaign_id: 'bad')
|
||||
end
|
||||
end
|
||||
|
||||
it 'errors out if token is expired' do
|
||||
validation_expired { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: 1, supporter_id: 1, token: fake_uuid) }
|
||||
end
|
||||
it 'errors out if token is invalid' do
|
||||
validation_invalid_token do
|
||||
described_class.with_stripe(amount: 1, nonprofit_id: 1, supporter_id: 1, token: fake_uuid)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'errors during find if' do
|
||||
it 'supporter is invalid' do
|
||||
find_error_supporter { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: 55_555, token: source_token.token) }
|
||||
end
|
||||
it 'errors out if token is unauthorized' do
|
||||
validation_unauthorized do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: 1, supporter_id: 1, token: fake_uuid)
|
||||
end
|
||||
end
|
||||
|
||||
it 'nonprofit is invalid' do
|
||||
find_error_nonprofit { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: 55_555, supporter_id: supporter.id, token: source_token.token) }
|
||||
end
|
||||
it 'errors out if token is expired' do
|
||||
validation_expired do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: 1, supporter_id: 1, token: fake_uuid)
|
||||
end
|
||||
end
|
||||
|
||||
it 'campaign is invalid' do
|
||||
find_error_campaign { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: 5555) }
|
||||
end
|
||||
describe 'errors during find if' do
|
||||
it 'supporter is invalid' do
|
||||
find_error_supporter do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: 55_555,
|
||||
token: source_token.token)
|
||||
end
|
||||
end
|
||||
|
||||
it 'event is invalid' do
|
||||
find_error_event { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: 5555) }
|
||||
end
|
||||
it 'nonprofit is invalid' do
|
||||
find_error_nonprofit do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: 55_555, supporter_id: supporter.id,
|
||||
token: source_token.token)
|
||||
end
|
||||
end
|
||||
|
||||
it 'profile is invalid' do
|
||||
find_error_profile { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, profile_id: 5555) }
|
||||
end
|
||||
end
|
||||
it 'campaign is invalid' do
|
||||
find_error_campaign do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token, campaign_id: 5555)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'errors during relationship comparison if' do
|
||||
it 'supporter is deleted' do
|
||||
validation_supporter_deleted { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token) }
|
||||
end
|
||||
it 'event is invalid' do
|
||||
find_error_event do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token, event_id: 5555)
|
||||
end
|
||||
end
|
||||
|
||||
it 'event is deleted' do
|
||||
validation_event_deleted { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: event.id) }
|
||||
end
|
||||
it 'profile is invalid' do
|
||||
find_error_profile do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token, profile_id: 5555)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'campaign is deleted' do
|
||||
validation_campaign_deleted { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: campaign.id) }
|
||||
end
|
||||
describe 'errors during relationship comparison if' do
|
||||
it 'supporter is deleted' do
|
||||
validation_supporter_deleted do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token)
|
||||
end
|
||||
end
|
||||
|
||||
it 'supporter doesnt belong to nonprofit' do
|
||||
validation_supporter_not_with_nonprofit { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: other_nonprofit_supporter.id, token: source_token.token) }
|
||||
end
|
||||
it 'event is deleted' do
|
||||
validation_event_deleted do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token, event_id: event.id)
|
||||
end
|
||||
end
|
||||
|
||||
it 'campaign doesnt belong to nonprofit' do
|
||||
validation_campaign_not_with_nonprofit { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, campaign_id: other_campaign.id) }
|
||||
end
|
||||
it 'campaign is deleted' do
|
||||
validation_campaign_deleted do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token, campaign_id: campaign.id)
|
||||
end
|
||||
end
|
||||
|
||||
it 'event doesnt belong to nonprofit' do
|
||||
validation_event_not_with_nonprofit { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: source_token.token, event_id: other_event.id) }
|
||||
end
|
||||
it 'supporter doesnt belong to nonprofit' do
|
||||
validation_supporter_not_with_nonprofit do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id,
|
||||
supporter_id: other_nonprofit_supporter.id, token: source_token.token)
|
||||
end
|
||||
end
|
||||
|
||||
it 'card doesnt belong to supporter' do
|
||||
validation_card_not_with_supporter { InsertDonation.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id, token: other_source_token.token) }
|
||||
end
|
||||
end
|
||||
end
|
||||
it 'campaign doesnt belong to nonprofit' do
|
||||
validation_campaign_not_with_nonprofit do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token, campaign_id: other_campaign.id)
|
||||
end
|
||||
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
|
||||
it 'event doesnt belong to nonprofit' do
|
||||
validation_event_not_with_nonprofit do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token, event_id: other_event.id)
|
||||
end
|
||||
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: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') }
|
||||
end
|
||||
it 'card doesnt belong to supporter' do
|
||||
validation_card_not_with_supporter do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: other_source_token.token)
|
||||
end
|
||||
end
|
||||
end
|
||||
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: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') }
|
||||
end
|
||||
it 'charge returns failed' do
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:transaction_created, any_args)
|
||||
handle_charge_failed do
|
||||
described_class.with_stripe(amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
token: source_token.token)
|
||||
end
|
||||
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: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') }
|
||||
end
|
||||
end
|
||||
end
|
||||
describe 'success' do
|
||||
before do
|
||||
before_each_success
|
||||
allow(Houdini.event_publisher).to receive(:announce)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:transaction_created, any_args)
|
||||
end
|
||||
|
||||
describe '#with_sepa' do
|
||||
include_context :shared_rd_donation_value_context
|
||||
it 'process event donation' do
|
||||
process_event_donation do
|
||||
described_class.with_stripe(
|
||||
amount: charge_amount,
|
||||
nonprofit_id: nonprofit.id,
|
||||
supporter_id: supporter.id,
|
||||
token: source_token.token,
|
||||
event_id: event.id,
|
||||
date: (Time.zone.now + 1.day).to_s,
|
||||
dedication: {
|
||||
'type' => 'honor',
|
||||
'name' => 'a name'
|
||||
},
|
||||
designation: 'designation'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'saves donation' do
|
||||
before(:each) 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: {'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 do
|
||||
described_class.with_stripe(
|
||||
amount: charge_amount,
|
||||
nonprofit_id: nonprofit.id,
|
||||
supporter_id: supporter.id,
|
||||
token: source_token.token,
|
||||
campaign_id: campaign.id,
|
||||
date: (Time.zone.now + 1.day).to_s,
|
||||
dedication: { 'type' => 'honor', 'name' => 'a name' },
|
||||
designation: 'designation'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'process campaign donation' do
|
||||
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: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') }
|
||||
end
|
||||
it 'processes general donation' do
|
||||
process_general_donation do
|
||||
described_class.with_stripe(
|
||||
amount: charge_amount,
|
||||
nonprofit_id: nonprofit.id,
|
||||
supporter_id: supporter.id,
|
||||
token: source_token.token,
|
||||
profile_id: profile.id,
|
||||
date: (Time.zone.now + 1.day).to_s,
|
||||
dedication: { 'type' => 'honor', 'name' => 'a name' },
|
||||
designation: 'designation'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
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: {'type' => 'honor', 'name' => 'a name'}, designation: 'designation') }
|
||||
end
|
||||
end
|
||||
end
|
||||
# it 'saves donation' do
|
||||
# expect { InsertDonation.with_sepa(data) }.to change(Donation, :count).by(1)
|
||||
# end
|
||||
#
|
||||
# it 'returns a json hash' do
|
||||
# result = InsertDonation.with_sepa(data)
|
||||
#
|
||||
# expect(result).to be_a(Hash)
|
||||
# expect(result[:json]['donation']).to include data
|
||||
# end
|
||||
# end
|
||||
describe '#with_sepa' do
|
||||
include_context :shared_rd_donation_value_context
|
||||
|
||||
it '.offsite', pending: true do
|
||||
raise
|
||||
end
|
||||
describe 'saves donation' do
|
||||
before do
|
||||
before_each_sepa_success
|
||||
end
|
||||
|
||||
it 'process event donation' do
|
||||
process_event_donation(sepa: true) do
|
||||
described_class.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.zone.now + 1.day).to_s,
|
||||
dedication: {
|
||||
'type' => 'honor',
|
||||
'name' => 'a name'
|
||||
},
|
||||
designation: 'designation'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'process campaign donation' do
|
||||
allow(Houdini.event_publisher).to receive(:announce)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:campaign_create, any_args)
|
||||
process_campaign_donation(sepa: true) do
|
||||
described_class.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.zone.now + 1.day).to_s,
|
||||
dedication: {
|
||||
'type' => 'honor',
|
||||
'name' => 'a name'
|
||||
},
|
||||
designation: 'designation'
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'processes general donation' do
|
||||
process_general_donation(sepa: true) do
|
||||
described_class.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.zone.now + 1.day).to_s,
|
||||
dedication: {
|
||||
'type' => 'honor',
|
||||
'name' => 'a name'
|
||||
},
|
||||
designation: 'designation'
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '.offsite' do
|
||||
include_context :shared_rd_donation_value_context
|
||||
describe 'failures' do
|
||||
before do
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:payment_created, any_args)
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:offline_transaction_charge_created, any_args)
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:offline_transaction_created, any_args)
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:trx_assignment_created, any_args)
|
||||
expect(Houdini.event_publisher).to_not receive(:announce).with(:transaction_created, any_args)
|
||||
end
|
||||
|
||||
it 'fails if amount is missing' do
|
||||
expect do
|
||||
described_class.offsite(
|
||||
{
|
||||
nonprofit_id: nonprofit.id,
|
||||
supporter_id: supporter.id
|
||||
}.with_indifferent_access
|
||||
)
|
||||
end.to raise_error(ParamValidation::ValidationError)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'success' do
|
||||
before do
|
||||
allow(Houdini.event_publisher).to receive(:announce)
|
||||
end
|
||||
|
||||
describe 'general offsite create' do
|
||||
subject do
|
||||
described_class.offsite({ amount: charge_amount, nonprofit_id: nonprofit.id, supporter_id: supporter.id,
|
||||
date: created_time.to_s }.with_indifferent_access)
|
||||
end
|
||||
|
||||
let(:created_time) { Time.current + 1.day }
|
||||
let(:common_builder) do
|
||||
{ 'supporter' => supporter.id,
|
||||
'nonprofit' => nonprofit.id }
|
||||
end
|
||||
|
||||
let(:common_builder_expanded) do
|
||||
{
|
||||
'supporter' => supporter_builder_expanded,
|
||||
'nonprofit' => np_builder_expanded
|
||||
}
|
||||
end
|
||||
|
||||
let(:common_builder_with_trx_id) do
|
||||
common_builder.merge(
|
||||
{
|
||||
'transaction' => match_houid('trx')
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:common_builder_with_trx) do
|
||||
common_builder.merge(
|
||||
{
|
||||
'transaction' => transaction_builder
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:np_builder_expanded) do
|
||||
{
|
||||
'id' => nonprofit.id,
|
||||
'name' => nonprofit.name,
|
||||
'object' => 'nonprofit'
|
||||
}
|
||||
end
|
||||
|
||||
let(:supporter_builder_expanded) do
|
||||
supporter_to_builder_base.merge({ 'name' => 'Fake Supporter Name' })
|
||||
end
|
||||
|
||||
let(:transaction_builder) do
|
||||
common_builder.merge(
|
||||
{
|
||||
'id' => match_houid('trx'),
|
||||
'object' => 'transaction',
|
||||
'amount' => {
|
||||
'cents' => charge_amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
'created' => created_time.to_i,
|
||||
'subtransaction' => offline_transaction_id_only,
|
||||
'subtransaction_payments' => [offline_transaction_charge_id_only],
|
||||
'transaction_assignments' => [donation_id_only]
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:transaction_builder_expanded) do
|
||||
transaction_builder.merge(
|
||||
common_builder_expanded,
|
||||
{
|
||||
'subtransaction' => offline_transaction_builder,
|
||||
'subtransaction_payments' => [offline_transaction_charge_builder],
|
||||
'transaction_assignments' => [donation_builder]
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:offline_transaction_id_only) do
|
||||
{
|
||||
'id' => match_houid('offlinetrx'),
|
||||
'object' => 'offline_transaction',
|
||||
'type' => 'subtransaction'
|
||||
}
|
||||
end
|
||||
|
||||
let(:offline_transaction_builder) do
|
||||
offline_transaction_id_only.merge(
|
||||
common_builder_with_trx_id,
|
||||
{
|
||||
'initial_amount' => {
|
||||
'cents' => charge_amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
|
||||
'net_amount' => {
|
||||
'cents' => charge_amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
|
||||
'payments' => [offline_transaction_charge_id_only],
|
||||
'created' => created_time.to_i
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:offline_transaction_builder_expanded) do
|
||||
offline_transaction_builder.merge(
|
||||
common_builder_with_trx,
|
||||
common_builder_expanded,
|
||||
{
|
||||
'payments' => [offline_transaction_charge_builder]
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:offline_transaction_charge_id_only) do
|
||||
{
|
||||
'id' => match_houid('offtrxchrg'),
|
||||
'object' => 'offline_transaction_charge',
|
||||
'type' => 'payment'
|
||||
}
|
||||
end
|
||||
|
||||
let(:offline_transaction_charge_builder) do
|
||||
offline_transaction_charge_id_only.merge(
|
||||
common_builder_with_trx_id,
|
||||
{
|
||||
'gross_amount' => {
|
||||
'cents' => charge_amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
'net_amount' => {
|
||||
'cents' => charge_amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
'fee_total' => {
|
||||
'cents' => 0,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
'subtransaction' => offline_transaction_id_only,
|
||||
'created' => created_time.to_i
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:offline_transaction_charge_builder_expanded) do
|
||||
offline_transaction_charge_builder.merge(
|
||||
common_builder_with_trx,
|
||||
common_builder_expanded,
|
||||
{
|
||||
'subtransaction' => offline_transaction_builder
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
let(:donation_id_only) do
|
||||
{
|
||||
'id' => match_houid('don'),
|
||||
'object' => 'donation',
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
|
||||
let(:donation_builder) do
|
||||
donation_id_only.merge(common_builder_with_trx_id, {
|
||||
'amount' => {
|
||||
'cents' => charge_amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
'designation' => nil
|
||||
})
|
||||
end
|
||||
|
||||
let(:donation_builder_expanded) do
|
||||
donation_builder.merge(common_builder_with_trx, common_builder_expanded)
|
||||
end
|
||||
|
||||
describe 'event publishing' do
|
||||
it 'has fired transaction.created' do
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:payment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_charge_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:trx_assignment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(
|
||||
:transaction_created, {
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
'type' => 'transaction.created',
|
||||
'data' => {
|
||||
'object' => transaction_builder_expanded
|
||||
}
|
||||
}
|
||||
)
|
||||
subject
|
||||
end
|
||||
|
||||
it 'has fired offline_transaction_charge.created' do
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(
|
||||
:offline_transaction_charge_created,
|
||||
{
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
'type' => 'offline_transaction_charge.created',
|
||||
'data' => {
|
||||
'object' => offline_transaction_charge_builder_expanded
|
||||
}
|
||||
}
|
||||
)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:payment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:trx_assignment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:transaction_created, any_args)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'has fired payment.created' do
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_charge_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(
|
||||
:payment_created,
|
||||
{
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
'type' => 'payment.created',
|
||||
'data' => {
|
||||
'object' => offline_transaction_charge_builder_expanded
|
||||
}
|
||||
}
|
||||
)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:trx_assignment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:transaction_created, any_args)
|
||||
|
||||
subject
|
||||
end
|
||||
|
||||
it 'has fired offline_transaction.created' do
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_charge_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:payment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(
|
||||
:offline_transaction_created,
|
||||
{
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
'type' => 'offline_transaction.created',
|
||||
'data' => {
|
||||
'object' => offline_transaction_builder_expanded
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:trx_assignment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:transaction_created, any_args)
|
||||
subject
|
||||
end
|
||||
|
||||
it 'has fired donation.created' do
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_charge_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:payment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(
|
||||
:donation_created,
|
||||
{
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
'type' => 'donation.created',
|
||||
'data' => {
|
||||
'object' => donation_builder_expanded
|
||||
}
|
||||
}
|
||||
)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:trx_assignment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:transaction_created, any_args)
|
||||
subject
|
||||
end
|
||||
|
||||
it 'has fired trx_assignment.created' do
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_charge_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:payment_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:offline_transaction_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:donation_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(
|
||||
:trx_assignment_created,
|
||||
{
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
'type' => 'trx_assignment.created',
|
||||
'data' => {
|
||||
'object' => donation_builder_expanded
|
||||
}
|
||||
}
|
||||
)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:transaction_created, any_args)
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable all
|
||||
|
|
|
@ -45,7 +45,7 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
},
|
||||
}],
|
||||
'campaign' => kind_of(Numeric),
|
||||
'nonprofit' => kind_of(Numeric)
|
||||
'nonprofit' => nonprofit.id
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -69,8 +69,22 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
'cents' => trx.amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
'supporter' => kind_of(Numeric),
|
||||
'nonprofit' => kind_of(Numeric)
|
||||
'created' => Time.current.to_i,
|
||||
'supporter' => supporter.id,
|
||||
'nonprofit' => nonprofit.id,
|
||||
'subtransaction' => nil,
|
||||
'subtransaction_payments' => [],
|
||||
'transaction_assignments' => [
|
||||
cgp_builder_to_id
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
let(:cgp_builder_to_id) do
|
||||
{
|
||||
'id' => match_houid('cgpur'),
|
||||
'object' => 'campaign_gift_purchase',
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -102,9 +116,9 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
'campaign_gift_purchase' => match_houid('cgpur'),
|
||||
'deleted' => false,
|
||||
'id' => match_houid('cgift'),
|
||||
'nonprofit'=> kind_of(Numeric),
|
||||
'nonprofit'=> nonprofit.id,
|
||||
'object' => 'campaign_gift',
|
||||
'supporter' => kind_of(Numeric),
|
||||
'supporter' => supporter.id,
|
||||
'transaction' => match_houid('trx')
|
||||
}
|
||||
}
|
||||
|
@ -114,6 +128,7 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
|
||||
it 'announces created properly when called' do
|
||||
allow(Houdini.event_publisher).to receive(:announce)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:campaign_gift_option_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:campaign_gift_purchase_created, {
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
|
@ -131,7 +146,8 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
'supporter' => supporter_builder_expanded,
|
||||
'nonprofit' => np_builder_expanded,
|
||||
'transaction' => transaction_builder_expanded,
|
||||
'deleted' => false
|
||||
'deleted' => false,
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -141,6 +157,7 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
|
||||
it 'announces updated properly when called' do
|
||||
allow(Houdini.event_publisher).to receive(:announce)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:campaign_gift_option_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:campaign_gift_purchase_updated, {
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
|
@ -158,7 +175,8 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
'supporter' => supporter_builder_expanded,
|
||||
'nonprofit' => np_builder_expanded,
|
||||
'transaction' => transaction_builder_expanded,
|
||||
'deleted' => false
|
||||
'deleted' => false,
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -168,6 +186,7 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
|
||||
it 'announces updated deleted properly when called' do
|
||||
allow(Houdini.event_publisher).to receive(:announce)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:campaign_gift_option_created, any_args)
|
||||
expect(Houdini.event_publisher).to receive(:announce).with(:campaign_gift_purchase_deleted, {
|
||||
'id' => match_houid('objevt'),
|
||||
'object' => 'object_event',
|
||||
|
@ -185,7 +204,8 @@ RSpec.describe CampaignGiftPurchase, type: :model do
|
|||
'supporter' => supporter_builder_expanded,
|
||||
'nonprofit' => np_builder_expanded,
|
||||
'transaction' => transaction_builder_expanded,
|
||||
'deleted' => true
|
||||
'deleted' => true,
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -9,14 +9,21 @@ RSpec.describe Model::Jbuilder do
|
|||
let(:model) do
|
||||
stub_const('HasToBuilderAndToId', Struct.new(:to_id, :to_builder))
|
||||
|
||||
stub_const('ModelClass', Struct.new(:filled, :unfilled, :enumerable))
|
||||
stub_const('ModelClass', Struct.new(:filled, :unfilled, :enumerable, :json_based_id, :flat_enumerable))
|
||||
|
||||
ModelClass.new(
|
||||
HasToBuilderAndToId.new('id_result', 'builder_result'),
|
||||
nil,
|
||||
[
|
||||
HasToBuilderAndToId.new('enumerable_id_result_1', 'enumerable_builder_result_1'),
|
||||
HasToBuilderAndToId.new('enumerable_id_result_2', 'enumerable_builder_result_2')
|
||||
HasToBuilderAndToId.new('enumerable_id_result_1', ::Jbuilder.new { |json| json.id 'enumerable_builder_result_1' }),
|
||||
HasToBuilderAndToId.new('enumerable_id_result_2', ::Jbuilder.new { |json| json.id 'enumerable_builder_result_2' })
|
||||
],
|
||||
HasToBuilderAndToId.new(::Jbuilder.new do |json|
|
||||
json.id 'json_based_id'
|
||||
end, 'expanded'),
|
||||
%w[
|
||||
flat_id_result_1
|
||||
flat_id_result_2
|
||||
]
|
||||
)
|
||||
end
|
||||
|
@ -24,7 +31,7 @@ RSpec.describe Model::Jbuilder do
|
|||
let(:unfilled_expansion) { described_class.new(key: :unfilled) }
|
||||
let(:nonexistent_expansion) { described_class.new(key: :nonexistent) }
|
||||
let(:expandable_expansion) { described_class.new(key: :enumerable, enum_type: :expandable) }
|
||||
let(:flat_expansion) { described_class.new(key: :enumerable, enum_type: :flat) }
|
||||
let(:flat_expansion) { described_class.new(key: :flat_enumerable, enum_type: :flat) }
|
||||
|
||||
describe 'expansion where the attribute is filled' do
|
||||
subject { filled_expansion }
|
||||
|
@ -118,7 +125,7 @@ RSpec.describe Model::Jbuilder do
|
|||
describe '#to_builder' do
|
||||
subject { expandable_expansion.to_builder.call(model) }
|
||||
|
||||
it { is_expected.to match(%w[enumerable_builder_result_1 enumerable_builder_result_2]) }
|
||||
it { is_expected.to match([{ 'id' => 'enumerable_builder_result_1' }, { 'id' => 'enumerable_builder_result_2' }]) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -127,7 +134,7 @@ RSpec.describe Model::Jbuilder do
|
|||
|
||||
it {
|
||||
is_expected.to have_attributes(
|
||||
json_attribute: 'enumerable',
|
||||
json_attribute: 'flat_enumerable',
|
||||
enumerable?: true,
|
||||
flat_enum?: true,
|
||||
expandable_enum?: false
|
||||
|
@ -135,15 +142,15 @@ RSpec.describe Model::Jbuilder do
|
|||
}
|
||||
|
||||
describe '#to_id' do
|
||||
subject { expandable_expansion.to_id.call(model) }
|
||||
subject { flat_expansion.to_id.call(model) }
|
||||
|
||||
it { is_expected.to match(%w[enumerable_id_result_1 enumerable_id_result_2]) }
|
||||
it { is_expected.to match(%w[flat_id_result_1 flat_id_result_2]) }
|
||||
end
|
||||
|
||||
describe '#to_builder' do
|
||||
subject { expandable_expansion.to_builder.call(model) }
|
||||
subject { flat_expansion.to_builder.call(model) }
|
||||
|
||||
it { is_expected.to match(%w[enumerable_builder_result_1 enumerable_builder_result_2]) }
|
||||
it { is_expected.to match(%w[flat_id_result_1 flat_id_result_2]) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -219,7 +219,7 @@ RSpec.describe EventDiscount, type: :model do
|
|||
'available_to' => 'everyone',
|
||||
'nonprofit' => nonprofit.id,
|
||||
'event' => event.id,
|
||||
'event_discounts' => []
|
||||
'event_discounts' => [kind_of(Numeric)]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -69,8 +69,20 @@ RSpec.describe ModernCampaignGift, type: :model do
|
|||
'cents' => trx.amount,
|
||||
'currency' => 'usd'
|
||||
},
|
||||
'created' => Time.current.to_i,
|
||||
'supporter' => kind_of(Numeric),
|
||||
'nonprofit' => kind_of(Numeric)
|
||||
'nonprofit' => kind_of(Numeric),
|
||||
'subtransaction' => nil,
|
||||
'subtransaction_payments' => [],
|
||||
'transaction_assignments' => [cgp_builder_to_id]
|
||||
}
|
||||
end
|
||||
|
||||
let(:cgp_builder_to_id) do
|
||||
{
|
||||
'id' => match_houid('cgpur'),
|
||||
'object' => 'campaign_gift_purchase',
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -87,12 +99,10 @@ RSpec.describe ModernCampaignGift, type: :model do
|
|||
'supporter' => kind_of(Numeric),
|
||||
'nonprofit' => kind_of(Numeric),
|
||||
'transaction' => match_houid('trx'),
|
||||
'deleted' => false
|
||||
'deleted' => false,
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
it 'announces created properly when called' do
|
||||
allow(Houdini.event_publisher).to receive(:announce)
|
||||
|
|
|
@ -26,7 +26,8 @@ RSpec.describe ModernDonation, type: :model do
|
|||
'supporter' => supporter.id,
|
||||
'amount' => {'currency' => 'usd', 'cents' => 1200},
|
||||
'transaction' => trx.id,
|
||||
'designation' => nil
|
||||
'designation' => nil,
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
it 'without dedication or designation' do
|
||||
|
|
9
spec/models/offline_transaction_charge_spec.rb
Normal file
9
spec/models/offline_transaction_charge_spec.rb
Normal file
|
@ -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
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe OfflineTransactionCharge, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
9
spec/models/offline_transaction_spec.rb
Normal file
9
spec/models/offline_transaction_spec.rb
Normal file
|
@ -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
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe OfflineTransaction, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
9
spec/models/subtransaction_payment_spec.rb
Normal file
9
spec/models/subtransaction_payment_spec.rb
Normal file
|
@ -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
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SubtransactionPayment, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
9
spec/models/subtransaction_spec.rb
Normal file
9
spec/models/subtransaction_spec.rb
Normal file
|
@ -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
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Subtransaction, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
|
@ -57,7 +57,8 @@ RSpec.describe TicketPurchase, type: :model do
|
|||
'amount' => {'currency' => 'usd', 'cents' => 1200},
|
||||
'original_discount' => { 'percent' => 0},
|
||||
'event_discount' => nil,
|
||||
'transaction' => trx.id
|
||||
'transaction' => trx.id,
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -39,6 +39,14 @@ RSpec.describe TicketToLegacyTicket, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
let(:ticket_purchase_to_id) do
|
||||
{
|
||||
'id' => ticket_purchase.id,
|
||||
'object' => 'ticket_purchase',
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
|
||||
let(:ticket_default) do
|
||||
{
|
||||
'id' => match_houid('tkt'),
|
||||
|
@ -49,7 +57,7 @@ RSpec.describe TicketToLegacyTicket, type: :model do
|
|||
'nonprofit' => nonprofit.id,
|
||||
'event' => event.id,
|
||||
'supporter' => supporter.id,
|
||||
'ticket_purchase' => ticket_purchase.id,
|
||||
'ticket_purchase' => ticket_purchase_to_id,
|
||||
'original_discount' => { 'percent' => 0},
|
||||
'event_discount' => nil
|
||||
}
|
||||
|
@ -124,6 +132,14 @@ RSpec.describe TicketToLegacyTicket, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
let(:ticket_purchase_to_id) do
|
||||
{
|
||||
'id' => ticket_purchase.id,
|
||||
'object'=> 'ticket_purchase',
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
end
|
||||
|
||||
let(:ticket_default) do
|
||||
{
|
||||
'id' => match_houid('tkt'),
|
||||
|
@ -134,7 +150,7 @@ RSpec.describe TicketToLegacyTicket, type: :model do
|
|||
'nonprofit' => nonprofit.id,
|
||||
'event' => event.id,
|
||||
'supporter' => supporter.id,
|
||||
'ticket_purchase' => ticket_purchase.id,
|
||||
'ticket_purchase' => ticket_purchase_to_id,
|
||||
'original_discount' => { 'percent' => 20},
|
||||
'event_discount' => event_discount.id
|
||||
}
|
||||
|
|
|
@ -8,17 +8,30 @@ RSpec.describe Transaction, type: :model do
|
|||
include_context :shared_donation_charge_context
|
||||
|
||||
describe 'to_builder' do
|
||||
subject { supporter.transactions.create(amount: 1000).to_builder.attributes!}
|
||||
subject { supporter.transactions.create(
|
||||
amount: 1000,
|
||||
transaction_assignments: [TransactionAssignment.new(assignable:ModernDonation.new(amount: 1000))]
|
||||
).to_builder.attributes!}
|
||||
it 'will create a proper builder result' do
|
||||
is_expected.to match({
|
||||
'id' => match('trx_[a-zA-Z0-9]{22}'),
|
||||
'id' => match_houid('trx'),
|
||||
'nonprofit' => nonprofit.id,
|
||||
'supporter' => supporter.id,
|
||||
'object' => 'transaction',
|
||||
'created' => Time.current.to_i,
|
||||
'amount' => {
|
||||
'cents' => 1000,
|
||||
'currency' => 'usd'
|
||||
}
|
||||
},
|
||||
'subtransaction' => nil,
|
||||
'subtransaction_payments' => [],
|
||||
'transaction_assignments' => [
|
||||
{
|
||||
'object' => 'donation',
|
||||
'id' => match_houid('don'),
|
||||
'type' => 'trx_assignment'
|
||||
}
|
||||
]
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,6 +35,7 @@ RSpec.configure do |config|
|
|||
# # => "be bigger than 2"
|
||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||
expectations.on_potential_false_positives = :nothing
|
||||
expectations.max_formatted_output_length = nil
|
||||
end
|
||||
|
||||
# rspec-mocks config goes here. You can use an alternate test double
|
||||
|
|
Loading…
Reference in a new issue