Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ DATABASE_URL=trilogy://user:password@host:port/database?encoding=utf8mb4&collati

- Running mise should have run `rake db:seed`
- This will populate the basic data needed to use the app
- To add more sample data for development, run `rake db:dev:seed`
- This will add sample workshops, community news, stories, resources, FAQs, and more
- To add more sample data for development, run `rake db:seed:dev`
- This will add sample workshops, community news, stories, resources, FAQs, payments, and more
- To see your data
- The home page will show Workshops, CommunityNews, Resources, Events, and Stories
- The [Admin Home](http://localhost:3000/admin) provides CRUD access for most models
Expand Down
6 changes: 0 additions & 6 deletions db/seeds.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
# Disable email delivery during seeding
ActionMailer::Base.perform_deliveries = false

def seed(file)
require_relative "seeds/#{file}"
end

puts "Creating Users…"

# Helper: case-insensitive find-or-create by name
Expand Down Expand Up @@ -488,5 +484,3 @@ def find_or_create_by_name!(klass, name, **attrs, &block)
end
cat.update!(published: true) unless cat.published?
end

seed "payments"
85 changes: 0 additions & 85 deletions db/seeds/dummy_dev_seeds.rb → db/seeds/dev/dummy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1502,91 +1502,6 @@
end
end

puts "Creating Scholarships, Payments, and Allocations…"
# Gives the paid dev events real money + scholarship records so the event
# overview dashboard (registrants / received / outstanding / scholarships)
# shows meaningful numbers. Registrations and applications (registration-form
# submissions) are created above; this fills in the financial side.
#
# Each registration is funded at most once — the guard skips any registration
# that already has allocations, so the section is safe to re-run.

org_payer = Organization.find_by(name: "Angel Step Inn")

# Mirrors ScholarshipsController: build the scholarship with a $0 allocation,
# then set the amount + tasks_completed so sync_allocation_amount funds the
# allocation only when the recipient's tasks are complete.
award_scholarship = ->(registration, amount_cents:, tasks_completed:) do
scholarship = Scholarship.new(recipient: registration.registrant)
scholarship.build_allocation(allocatable: registration, amount: 0)
scholarship.save!
scholarship.update!(amount_cents: amount_cents, tasks_completed: tasks_completed)
scholarship
end

# payer is a Person or an Organization; kind is :cash or :check.
record_payment = ->(registration, payer:, amount_cents:, kind: :cash) do
payer_attrs = payer.is_a?(Organization) ? { organization: payer } : { person: payer }
created_at = rand(3..30).days.ago
payment = case kind
when :check
CheckPayment.create!(**payer_attrs, amount_cents: amount_cents, check_number: "CHK-#{rand(10_000..99_999)}", created_at: created_at)
else
CashPayment.create!(**payer_attrs, amount_cents: amount_cents, created_at: created_at)
end
Allocation.create!(source: payment, allocatable: registration, amount: amount_cents, created_at: created_at)
end

# Funds a registration once. `scholarship` and `payments` describe what to build.
fund_registration = ->(event, person, scholarship: nil, payments: []) do
return unless event && person
registration = EventRegistration.find_by(event: event, registrant: person)
return unless registration
return if registration.allocations.exists?

award_scholarship.(registration, **scholarship) if scholarship
payments.each { |payment| record_payment.(registration, **payment) }
end

# --- AWBW Facilitator Training ($150) ---
# Amy: pending scholarship (tasks incomplete → $0 allocated) + partial cash → still owes
fund_registration.(facilitator_training, amy_person,
scholarship: { amount_cents: 10_000, tasks_completed: false },
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])
# Maria: paid in full by cash
fund_registration.(facilitator_training, maria_j,
payments: [ { payer: maria_j, amount_cents: 15_000, kind: :cash } ])
# Anna: paid in full by check from her organization (org-payer scenario)
fund_registration.(facilitator_training, anna_g,
payments: [ { payer: org_payer || anna_g, amount_cents: 15_000, kind: :check } ])
# Mario: partial cash → still owes
fund_registration.(facilitator_training, mario_j,
payments: [ { payer: mario_j, amount_cents: 5_000, kind: :cash } ])

# --- Facilitator Training: Trauma-Informed Art Practices ($120) ---
# Sarah: paid in full by check
fund_registration.(trauma_training, sarah_s,
payments: [ { payer: sarah_s, amount_cents: 12_000, kind: :check } ])
# Jessica: completed scholarship ($80) + cash for the remainder → paid in full
fund_registration.(trauma_training, jessica_b,
scholarship: { amount_cents: 8_000, tasks_completed: true },
payments: [ { payer: jessica_b, amount_cents: 4_000, kind: :cash } ])
# Angel: partial cash → still owes
fund_registration.(trauma_training, angel_g,
payments: [ { payer: angel_g, amount_cents: 6_000, kind: :cash } ])

# --- Mindful Art for Survivors Workshop ($50) ---
# Amy: paid in full by cash
fund_registration.(mindful_art, amy_person,
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])

[ facilitator_training, trauma_training, mindful_art ].compact.each do |event|
dashboard = EventDashboard.new(event)
puts " #{event.title}: #{dashboard.registrant_count} registrants, " \
"received #{dashboard.received_cents / 100.0}, outstanding #{dashboard.outstanding_cents / 100.0}, " \
"scholarships #{dashboard.scholarship_total_cents / 100.0} (#{dashboard.scholarship_recipient_count})"
end

puts "Creating Resources…"
10.times do |i|
kind = Resource::PUBLISHED_KINDS.sample
Expand Down
278 changes: 278 additions & 0 deletions db/seeds/dev/payments.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
# Payment seeds (dev-only) - run on their own via `rake db:seed:payments`, or
# as part of `rake db:seed:dev`.

puts "Seeding Payments, Allocations, and Refunds..."

payment_ids_start = Payment.maximum(:id) || 0
allocation_ids_start = Allocation.maximum(:id) || 0
refund_ids_start = Refund.maximum(:id) || 0
event_reg_ids_start = EventRegistration.maximum(:id) || 0

Refund.where("id > ?", refund_ids_start).delete_all
Allocation.where("id > ?", allocation_ids_start).delete_all
Payment.where("id > ?", payment_ids_start).delete_all
EventRegistration.where("id > ?", event_reg_ids_start).delete_all
Comment on lines +6 to +14

event_cost_cents = 150000

bob = Person.find_or_create_by!(email: "bob.payment@seed.example.com") do |p|
p.first_name = "Bob"
p.last_name = "Barker"
end

alice = Person.find_or_create_by!(email: "alice.payment@seed.example.com") do |p|
p.first_name = "Alice"
p.last_name = "Test"
end

charlie = Person.find_or_create_by!(email: "charlie.payment@seed.example.com") do |p|
p.first_name = "Charlie"
p.last_name = "Test"
end

diana = Person.find_or_create_by!(email: "diana.payment@seed.example.com") do |p|
p.first_name = "Diana"
p.last_name = "Test"
end

eve = Person.find_or_create_by!(email: "eve.payment@seed.example.com") do |p|
p.first_name = "Eve"
p.last_name = "Test"
end

frank = Person.find_or_create_by!(email: "frank.payment@seed.example.com") do |p|
p.first_name = "Frank"
p.last_name = "Test"
end

gary = Person.find_or_create_by!(email: "gary.payment@seed.example.com") do |p|
p.first_name = "Gary"
p.last_name = "Test"
end

holly = Person.find_or_create_by!(email: "holly.payment@seed.example.com") do |p|
p.first_name = "Holly"
p.last_name = "Test"
end

iris = Person.find_or_create_by!(email: "iris.payment@seed.example.com") do |p|
p.first_name = "Iris"
p.last_name = "Test"
end

event = Event.find_or_create_by!(title: "Test Payments Workshop") do |e|
e.start_date = 1.month.from_now.to_date
e.end_date = 1.month.from_now.to_date + 2.days
e.published = true
e.cost_cents = event_cost_cents
end
event.update!(start_date: 1.month.from_now.to_date, end_date: 1.month.from_now.to_date + 2.days, published: true, cost_cents: event_cost_cents)

reg_bob = EventRegistration.find_or_create_by!(registrant: bob, event: event)
reg_alice = EventRegistration.find_or_create_by!(registrant: alice, event: event)
reg_charlie = EventRegistration.find_or_create_by!(registrant: charlie, event: event)
reg_diana = EventRegistration.find_or_create_by!(registrant: diana, event: event)
reg_eve = EventRegistration.find_or_create_by!(registrant: eve, event: event)
reg_frank = EventRegistration.find_or_create_by!(registrant: frank, event: event)
reg_gary = EventRegistration.find_or_create_by!(registrant: gary, event: event)
reg_iris = EventRegistration.find_or_create_by!(registrant: iris, event: event)

puts " Payment made but allocation reverted)"
payment1 = CashPayment.find_or_create_by!(
person: bob,
amount_cents: event_cost_cents
) do |p|
p.created_at = 5.days.ago
end
original_allocation1 = Allocation.create!(
source: payment1,
allocatable: reg_bob,
amount: event_cost_cents,
created_at: 5.days.ago
)
reversal_allocation1 = Allocation.create!(
source: payment1,
allocatable: reg_bob,
amount: -event_cost_cents,
created_at: 4.days.ago
)
original_allocation1.update!(reverted_id: reversal_allocation1.id)

puts " Overpayment with full allocation (Alice pays $6000, covers 4 people)"
payment2 = CashPayment.find_or_create_by!(
person: alice,
amount_cents: 600000
) do |p|
p.created_at = 4.days.ago
end
Allocation.find_or_create_by!(source: payment2, allocatable: reg_alice, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }
Allocation.find_or_create_by!(source: payment2, allocatable: reg_charlie, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }
Allocation.find_or_create_by!(source: payment2, allocatable: reg_diana, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }
Allocation.find_or_create_by!(source: payment2, allocatable: reg_eve, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }

puts " Payment with remaining available ($2000 payment, $1500 allocated, $500 remaining)"
payment3 = CashPayment.find_or_create_by!(
person: frank,
amount_cents: 200000
) do |p|
p.created_at = 3.days.ago
end
Allocation.find_or_create_by!(
source: payment3,
allocatable: reg_frank,
amount: 150000
) do |a|
a.created_at = 3.days.ago
end

puts " Full refund ($1500 payment, fully allocated, fully refunded)"
payment4 = CashPayment.find_or_create_by!(
person: gary,
amount_cents: event_cost_cents
) do |p|
p.created_at = 2.days.ago
end
original_alloc = Allocation.create!(
source: payment4,
allocatable: reg_gary,
amount: event_cost_cents,
created_at: 2.days.ago
)
reversal_alloc = Allocation.create!(
source: payment4,
allocatable: reg_gary,
amount: -event_cost_cents,
created_at: 1.day.ago
)
original_alloc.update!(reverted_id: reversal_alloc.id)
Refund.create!(
refundable: payment4,
recipient: gary,
amount_cents: event_cost_cents,
method: "check",
created_at: 1.day.ago
)

puts " Creating Scenario 8: Payment with no allocations ($10000, full amount remaining)"
CashPayment.find_or_create_by!(
person: holly,
amount_cents: 1000000
) do |p|
p.created_at = 7.days.ago
end

puts " Partial payment"
payment9 = CashPayment.find_or_create_by!(
person: iris,
amount_cents: 100000
) do |p|
p.created_at = 3.days.ago
end
Allocation.find_or_create_by!(
source: payment9,
allocatable: reg_iris,
amount: 100000
) do |a|
a.created_at = 3.days.ago
end

puts "Creating Scholarships, Payments, and Allocations for dev events…"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Moved here from dev/dummy.rb (added by #1565) so all payment seeding lives in one file. It funds dummy-created registrations via find_by, so it only does anything when run after db:seed:dummy (which db:seed:dev guarantees); standalone db:seed:payments safely no-ops these via the fund_registration guards.

# Funds registrations on the paid dev events with real money + scholarship
# records so the event overview dashboard (registrants / received / outstanding
# / scholarships) shows meaningful numbers. The events, people, and registrations
# are created in db/seeds/dev/dummy.rb, so this only fills in the financial side
# when the dummy seeds have already run (e.g. via `rake db:seed:dev`); on its own
# `rake db:seed:payments` simply skips events that aren't present.
#
# Each registration is funded at most once — the guard skips any registration
# that already has allocations, so the section is safe to re-run.

amy_person = User.find_by(email: "amy.user@example.com")&.person
maria_j = Person.find_by(first_name: "Maria", last_name: "Johnson")
anna_g = Person.find_by(first_name: "Anna", last_name: "Garcia")
sarah_s = Person.find_by(first_name: "Sarah", last_name: "Smith")
jessica_b = Person.find_by(first_name: "Jessica", last_name: "Brown")
mario_j = Person.find_by(first_name: "Mario", last_name: "Johnson")
angel_g = Person.find_by(first_name: "Angel", last_name: "Garcia")

facilitator_training = Event.find_by(title: "AWBW Facilitator Training")
trauma_training = Event.find_by(title: "Facilitator Training: Trauma-Informed Art Practices")
mindful_art = Event.find_by(title: "Mindful Art for Survivors Workshop")

org_payer = Organization.find_by(name: "Angel Step Inn")

# Mirrors ScholarshipsController: build the scholarship with a $0 allocation,
# then set the amount + tasks_completed so sync_allocation_amount funds the
# allocation only when the recipient's tasks are complete.
award_scholarship = ->(registration, amount_cents:, tasks_completed:) do
scholarship = Scholarship.new(recipient: registration.registrant)
scholarship.build_allocation(allocatable: registration, amount: 0)
scholarship.save!
scholarship.update!(amount_cents: amount_cents, tasks_completed: tasks_completed)
scholarship
end

# payer is a Person or an Organization; kind is :cash or :check.
record_payment = ->(registration, payer:, amount_cents:, kind: :cash) do
payer_attrs = payer.is_a?(Organization) ? { organization: payer } : { person: payer }
created_at = rand(3..30).days.ago
payment = case kind
when :check
CheckPayment.create!(**payer_attrs, amount_cents: amount_cents, check_number: "CHK-#{rand(10_000..99_999)}", created_at: created_at)
else
CashPayment.create!(**payer_attrs, amount_cents: amount_cents, created_at: created_at)
end
Allocation.create!(source: payment, allocatable: registration, amount: amount_cents, created_at: created_at)
end

# Funds a registration once. `scholarship` and `payments` describe what to build.
fund_registration = ->(event, person, scholarship: nil, payments: []) do
return unless event && person
registration = EventRegistration.find_by(event: event, registrant: person)
return unless registration
return if registration.allocations.exists?

award_scholarship.(registration, **scholarship) if scholarship
payments.each { |payment| record_payment.(registration, **payment) }
end

# --- AWBW Facilitator Training ($150) ---
# Amy: pending scholarship (tasks incomplete → $0 allocated) + partial cash → still owes
fund_registration.(facilitator_training, amy_person,
scholarship: { amount_cents: 10_000, tasks_completed: false },
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])
# Maria: paid in full by cash
fund_registration.(facilitator_training, maria_j,
payments: [ { payer: maria_j, amount_cents: 15_000, kind: :cash } ])
# Anna: paid in full by check from her organization (org-payer scenario)
fund_registration.(facilitator_training, anna_g,
payments: [ { payer: org_payer || anna_g, amount_cents: 15_000, kind: :check } ])
# Mario: partial cash → still owes
fund_registration.(facilitator_training, mario_j,
payments: [ { payer: mario_j, amount_cents: 5_000, kind: :cash } ])

# --- Facilitator Training: Trauma-Informed Art Practices ($120) ---
# Sarah: paid in full by check
fund_registration.(trauma_training, sarah_s,
payments: [ { payer: sarah_s, amount_cents: 12_000, kind: :check } ])
# Jessica: completed scholarship ($80) + cash for the remainder → paid in full
fund_registration.(trauma_training, jessica_b,
scholarship: { amount_cents: 8_000, tasks_completed: true },
payments: [ { payer: jessica_b, amount_cents: 4_000, kind: :cash } ])
# Angel: partial cash → still owes
fund_registration.(trauma_training, angel_g,
payments: [ { payer: angel_g, amount_cents: 6_000, kind: :cash } ])

# --- Mindful Art for Survivors Workshop ($50) ---
# Amy: paid in full by cash
fund_registration.(mindful_art, amy_person,
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])

[ facilitator_training, trauma_training, mindful_art ].compact.each do |event|
dashboard = EventDashboard.new(event)
puts " #{event.title}: #{dashboard.registrant_count} registrants, " \
"received #{dashboard.received_cents / 100.0}, outstanding #{dashboard.outstanding_cents / 100.0}, " \
"scholarships #{dashboard.scholarship_total_cents / 100.0} (#{dashboard.scholarship_recipient_count})"
end

puts " Payment seeds complete!"
Loading