Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
f56a3b8
add pay gem
jmilljr24 Mar 28, 2026
24506db
pay gem migrations
jmilljr24 Mar 28, 2026
c7d0360
add pay_customer to person
jmilljr24 Mar 29, 2026
aef5332
create allocations migration
jmilljr24 Mar 29, 2026
e2fa10f
change payments to sti
jmilljr24 Mar 29, 2026
ebab3ff
add refunds migration
jmilljr24 Mar 29, 2026
549f234
fix migration
jmilljr24 Mar 29, 2026
6fa31f2
update models to use allocations
jmilljr24 Mar 29, 2026
1b5949e
Merge branch 'main' into add-pay-gem
jmilljr24 Apr 6, 2026
c2dfc28
add allocation index with cash and check payment form
jmilljr24 Apr 6, 2026
84683be
create allocation form
jmilljr24 Apr 6, 2026
27a72e9
us turbo stream replace
jmilljr24 Apr 6, 2026
0c0d224
change payment totals to allocations_sum
jmilljr24 Apr 6, 2026
95c8fb1
clean up new payment form
jmilljr24 Apr 6, 2026
e8ee56b
add stimulus selection for payer typer
jmilljr24 Apr 6, 2026
4388f42
fix toggle of payer type search
jmilljr24 Apr 6, 2026
1386148
fix form style
jmilljr24 Apr 6, 2026
dc9620f
add dollars to UI
jmilljr24 Apr 6, 2026
7bf4311
add dollars to allocations and add payment index pagination
jmilljr24 Apr 6, 2026
c733ac6
titlize payment type
jmilljr24 Apr 6, 2026
6aeb0b5
display amount input with decimal
jmilljr24 Apr 6, 2026
1e11b71
add allocated amount to payment
jmilljr24 Apr 6, 2026
191f130
handle allocation payment transaction
jmilljr24 Apr 6, 2026
260dc5d
allocate payments manually
jmilljr24 Apr 6, 2026
287eaf2
add refunds to payments
jmilljr24 Apr 6, 2026
f4dc62e
show refunds and allocation source
jmilljr24 Apr 6, 2026
1860791
add refund method
jmilljr24 Apr 6, 2026
433b96b
add revert of allocation
jmilljr24 Apr 7, 2026
3167847
add event cost validation for allocation
jmilljr24 Apr 7, 2026
8d55619
add flash errors
jmilljr24 Apr 7, 2026
e35cf04
show reverted reference
jmilljr24 Apr 7, 2026
978ebe5
fix allocated amount update
jmilljr24 Apr 7, 2026
6e77bcf
change to amount remaining
jmilljr24 Apr 7, 2026
4cce141
update flash with remaining on payment
jmilljr24 Apr 7, 2026
366d0ef
add stripe env
jmilljr24 Apr 7, 2026
ef6b0f0
add stripe checkout
jmilljr24 Apr 7, 2026
d7fb872
create payment when pay charge is paid
jmilljr24 Apr 7, 2026
c6d9cbd
add stripe refund id to refunds
jmilljr24 Apr 7, 2026
f13ff0a
order payment lists newest first
jmilljr24 Apr 8, 2026
0ebcfd2
fix amount cents remaning for new payments
jmilljr24 Apr 8, 2026
fb6ed64
refactor search type selector to be generic
jmilljr24 Apr 9, 2026
587295e
add join scope to remote search concern
jmilljr24 Apr 9, 2026
d1b146b
Merge branch 'main' into add-pay-gem
jmilljr24 Apr 9, 2026
57ee01f
create payment seeds
jmilljr24 Apr 9, 2026
aa2b24f
add seed for unallocation
jmilljr24 Apr 9, 2026
c08322b
fix event create seed
jmilljr24 Apr 9, 2026
454f868
add cursor to revert button
jmilljr24 Apr 9, 2026
1929058
more refund query to controller
jmilljr24 Apr 9, 2026
8fa8800
add search boxes for payments
jmilljr24 Apr 9, 2026
8928dad
fix search on amount remaining
jmilljr24 Apr 9, 2026
eaab324
fix dropdown toggle
jmilljr24 Apr 9, 2026
6db7f34
add turbo frame results
jmilljr24 Apr 9, 2026
19a43be
exclude remove select input typing from auto submit
jmilljr24 Apr 9, 2026
ea33b8f
fix payment type select
jmilljr24 Apr 9, 2026
c8f86e4
fix new payment form type select
jmilljr24 Apr 9, 2026
eb1b580
use turbo frame top for payment view link
jmilljr24 Apr 9, 2026
e1e332c
remove redudant payment name
jmilljr24 Apr 9, 2026
dddf918
hard code stripe name
jmilljr24 Apr 9, 2026
9687507
comments
jmilljr24 Apr 9, 2026
172f753
fix check for nil on event allocation
jmilljr24 Apr 9, 2026
ba6614e
rubocop
jmilljr24 Apr 9, 2026
2ee5178
add turbo flash to allocations
jmilljr24 Apr 10, 2026
0812b91
add allocation validation
jmilljr24 Apr 10, 2026
7622884
add search to allocations
jmilljr24 Apr 10, 2026
2ae0b5e
fix turbo frame on allocation search
jmilljr24 Apr 10, 2026
b303d52
move search box columns
jmilljr24 Apr 10, 2026
a77703c
add placeholder for type select
jmilljr24 Apr 11, 2026
5b25736
clear search
jmilljr24 Apr 11, 2026
e880e14
check for outlet
jmilljr24 Apr 12, 2026
969ce18
justify end for clear filters buttons
jmilljr24 Apr 12, 2026
3ea05bd
use model card helper
jmilljr24 Apr 13, 2026
7519974
clean up
jmilljr24 Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ RAILS_SERVE_STATIC_FILES=true
ORGANIZATION_NAME=A Window Between Worlds
REPLY_TO_EMAIL=umberto.user@example.com
BLAZER_DATABASE_URL=db_url # Optional if you want to use a different db for Blazer

# Pay Gem
STRIPE_PUBLIC_KEY=test
STRIPE_PRIVATE_KEY=test
STRIPE_WEBHOOK_RECEIVE_TEST_EVENTS=true
# STRIPE_SIGNING_SECRET=
5 changes: 5 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ gem "active_storage_validations", "~> 3.0"

gem "solid_cache"

# Payments
gem "pay", "~> 11.4"
gem "stripe", "~> 18.0"
gem "receipts", "~> 2.4"

group :development do
gem "rubocop-rails-omakase", require: false
end
Expand Down
23 changes: 23 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -520,12 +520,20 @@ GEM
parser (3.3.10.1)
ast (~> 2.4.1)
racc
pay (11.4.3)
rails (>= 7.0.0)
pdf-core (0.9.0)
polyglot (0.3.5)
positioning (0.4.7)
activerecord (>= 6.1)
activesupport (>= 6.1)
pp (0.6.3)
prettyprint
prawn (2.4.0)
pdf-core (~> 0.9.0)
ttfunk (~> 1.7)
prawn-table (0.2.2)
prawn (>= 1.3.0, < 3.0.0)
premailer (1.27.0)
addressable
css_parser (>= 1.19.0)
Expand Down Expand Up @@ -604,6 +612,9 @@ GEM
erb
psych (>= 4.0.0)
tsort
receipts (2.4.0)
prawn (>= 1.3.0, < 3.0.0)
prawn-table (~> 0.2.1)
regexp_parser (2.11.3)
reline (0.6.3)
io-console (~> 0.5)
Expand Down Expand Up @@ -709,12 +720,14 @@ GEM
stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.2.0)
stripe (18.4.2)
thor (1.5.0)
timeout (0.6.1)
treetop (1.6.18)
polyglot (~> 0.3)
trilogy (2.9.0)
tsort (0.2.0)
ttfunk (1.7.0)
turbo-rails (2.0.23)
actionpack (>= 7.1.0)
railties (>= 7.1.0)
Expand Down Expand Up @@ -800,13 +813,15 @@ DEPENDENCIES
opentelemetry-instrumentation-all
opentelemetry-sdk
ostruct
pay (~> 11.4)
positioning (~> 0.4.7)
premailer-rails
pry-coolline
pry-rails
puma (~> 6.0)
rack-mini-profiler (~> 4.0)
rails (~> 8.1.0)
receipts (~> 2.4)
rspec-rails
rubocop-rails-omakase
search_cop
Expand All @@ -819,6 +834,7 @@ DEPENDENCIES
solid_queue (~> 1.3)
sprockets-rails (~> 3.2.2)
stimulus-rails (~> 1.3)
stripe (~> 18.0)
trilogy (= 2.9.0)
turbo-rails (~> 2.0)
uglifier
Expand Down Expand Up @@ -1015,9 +1031,13 @@ CHECKSUMS
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688
pay (11.4.3) sha256=f3d50b1a900ab0a7bfe9ba9fda631b2f5faf0c95236dd3d2afa9effc9bfb15e8
pdf-core (0.9.0) sha256=4f368b2f12b57ec979872d4bf4bd1a67e8648e0c81ab89801431d2fc89f4e0bb
polyglot (0.3.5) sha256=59d66ef5e3c166431c39cb8b7c1d02af419051352f27912f6a43981b3def16af
positioning (0.4.7) sha256=c9a8cfba8b99180bd2b05022cdbcc7b234ea8bb1aa9101010e06bdda9a7bb220
pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
prawn (2.4.0) sha256=82062744f7126c2d77501da253a154271790254dfa8c309b8e52e79bc5de2abd
prawn-table (0.2.2) sha256=336d46e39e003f77bf973337a958af6a68300b941c85cb22288872dc2b36addb
premailer (1.27.0) sha256=0fe2348cd82738855c482b31c915a06ecb1d3ad004578c19042905196ddbd1e7
premailer-rails (1.12.0) sha256=c13815d161b9bc7f7d3d81396b0bb0a61a90fa9bd89931548bf4e537c7710400
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
Expand Down Expand Up @@ -1045,6 +1065,7 @@ CHECKSUMS
rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe
rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
receipts (2.4.0) sha256=64a7d9d95d223694106719dc10fb9291fa362e6bc684228d5cde4b772392fa74
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb
Expand Down Expand Up @@ -1081,11 +1102,13 @@ CHECKSUMS
sprockets-rails (3.2.2) sha256=62862bce136e31d7497eededde5f7730d4096bc8ef33ef7037c41423ccf89557
stimulus-rails (1.3.4) sha256=765676ffa1f33af64ce026d26b48e8ffb2e0b94e0f50e9119e11d6107d67cb06
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
stripe (18.4.2) sha256=fd08a73ab87fc0b0ad938ca71293e1051e593001b70de5e9fcf12d1097133831
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb
treetop (1.6.18) sha256=a3043f32f1c652aa2abdf3a3848edb2d2f69897257af2675516ac61355c183da
trilogy (2.9.0) sha256=a2d63b663ba68a4758e15d1f9afb228f5d16efc7fe7cea68699e1c106ef6067f
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
ttfunk (1.7.0) sha256=2370ba484b1891c70bdcafd3448cfd82a32dd794802d81d720a64c15d3ef2a96
turbo-rails (2.0.23) sha256=ee0d90733aafff056cf51ff11e803d65e43cae258cc55f6492020ec1f9f9315f
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
uglifier (4.2.1) sha256=75d42b81b10bfd21e7a427fabb1d49ff5ea7bda3c4a5039ddb2a78d194c6f5aa
Expand Down
146 changes: 146 additions & 0 deletions app/controllers/allocations_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
class AllocationsController < ApplicationController
before_action :authenticate_user!

def index
authorize!
if params[:allocatable_sgid].present?
@allocatable = GlobalID::Locator.locate_signed(params[:allocatable_sgid])
@allocations = @allocatable.allocations.includes(:source).order(created_at: :desc).paginate(page: params[:page], per_page: 10)
else
if turbo_frame_request?
@allocations = Allocation.search_by_params(params).includes(:source).order(created_at: :desc).paginate(page: params[:page], per_page: 10)
render :allocation_results
else
@allocations = Allocation.search_by_params(params).includes(:source).order(created_at: :desc).paginate(page: params[:page], per_page: 10)
end
end
end

def new
authorize!

if params[:source_sgid].present?
@source = GlobalID::Locator.locate_signed(params[:source_sgid])
end

@allocation = Allocation.new(source: @source)
end

def create
authorize!

amount_val = allocation_params[:amount_dollars].present? ? (allocation_params[:amount_dollars].to_d * 100).to_i : 0

@allocation = Allocation.new(
source_type: allocation_params[:source_type],
source_id: allocation_params[:source_id],
allocatable_type: allocation_params[:allocatable_type],
allocatable_id: allocation_params[:allocatable_id],
amount: amount_val
)

if @allocation.source_type && @allocation.source_id
@source = @allocation.source_type.constantize.find_by(id: @allocation.source_id)
end

if @allocation.allocatable_type == "EventRegistration" && @allocation.allocatable.present?
unless validate_event_registration_cost(amount_val)
flash.now[:error] = @allocation.errors.full_messages.join(", ")
respond_to do |format|
format.turbo_stream { render turbo_stream: turbo_stream.replace("flash_now", partial: "shared/flash_messages"), status: :unprocessable_content }
format.html { render :new, status: :unprocessable_content }
end
return
end
end

unless @source.present?
@allocation.errors.add(:base, "Source is required")
render :new, status: :unprocessable_content
return
end

@source.with_lock do
if @source.is_a?(Payment)
if @allocation.amount > @source.amount_cents_remaining
@allocation.errors.add(:base, "Cannot exceed remaining amount ($#{@source.remaining_dollars})")

flash.now[:error] = @allocation.errors.full_messages.join(", ")
respond_to do |format|
format.turbo_stream { render turbo_stream: turbo_stream.replace("flash_now", partial: "shared/flash_messages"), status: :unprocessable_content }
format.html { render :new, status: :unprocessable_content }
end
return
end

if @allocation.save
@source.update!(amount_cents_remaining: @source.amount_cents_remaining - amount_val)
flash[:notice] = "Allocation created. $#{'%.2f' % @source.remaining_dollars} remaining on payment."
redirect_to payment_path(@source)
else
render :new, status: :unprocessable_content
end
end
end
end

def revert
authorize!

@allocation = Allocation.find(params[:id])

if @allocation.reverted?
flash[:error] = "This allocation has already been reverted"
redirect_to payment_path(@allocation.source)
return
end

@revert = Allocation.new(
source: @allocation.source,
allocatable: @allocation.allocatable,
amount: -@allocation.amount
)

payment = @allocation.source

payment.with_lock do
ActiveRecord::Base.transaction do
if @revert.save
@allocation.update!(reverted_id: @revert.id)

payment.update!(amount_cents_remaining: payment.amount_cents_remaining + @allocation.amount)

redirect_to payment_path(payment), notice: "Allocation reverted"
else
flash[:error] = @revert.errors.full_messages.join(", ")
redirect_to payment_path(@allocation.source)
end
end
end
end

private

def validate_event_registration_cost(amount_val)
event_reg = @allocation.allocatable
event = event_reg.event
if event.cost_cents.blank?
@allocation.errors.add(:base, "Cannot allocate to a free event.")
return false
end
current_allocated = event_reg.allocations_sum || 0
new_total = current_allocated + amount_val

if new_total > event.cost_cents
remaining = [ event.cost_cents - current_allocated, 0 ].max
@allocation.errors.add(:base, "Cannot allocate more than remaining event cost. remaining: $#{'%.2f' % (remaining / 100.0)}")
return false
end

true
end

def allocation_params
params.expect(allocation: [ :source_type, :source_id, :allocatable_type, :allocatable_id, :amount_dollars ])
end
end
4 changes: 2 additions & 2 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def manage
authorize! @event, to: :manage?
@event = @event.decorate
scope = @event.event_registrations
.includes(:payments, :comments, :organizations, registrant: [ :user, :contact_methods, { avatar_attachment: :blob } ])
.includes(:comments, :organizations, registrant: [ :user, :contact_methods, { avatar_attachment: :blob } ])
.joins(:registrant)
scope = scope.keyword(params[:keyword]) if params[:keyword].present?
scope = scope.attendance_status(params[:attendance_status]) if params[:attendance_status].present?
Expand Down Expand Up @@ -202,7 +202,7 @@ def event_registration_csv_row(registration, cost_required)
.select { |a| !a.inactive? && (a.end_date.nil? || a.end_date >= Date.current) }
.map(&:organization).compact.uniq
org_names = orgs.map(&:name).join("; ")
total_cents = registration.successful_payments_total_cents
total_cents = registration.allocations_sum
payment_total = total_cents.positive? ? format("%.2f", total_cents / 100.0) : ""
payment_status = cost_required ? (registration.paid_in_full? ? "Paid in full" : "Not paid in full") : ""
[
Expand Down
Loading
Loading