Skip to content

Commit f81b949

Browse files
maebealeclaude
andauthored
Consolidate dev payment seeds into db:seed:dev (#1573)
* Consolidate dev payment seeds into db:seed:dev Payment seed data (test people, a sample event, and payment/allocation/ refund scenarios) was loaded from the base db/seeds.rb, so it ran on every db:seed — including production. It is sample data for exercising the payments UI, not required base data, so it does not belong in the production seed run. - Move dev-only seeds under db/seeds/dev/ to signal they never run in prod - Split seeding into rake tasks: db:seed:dummy and db:seed:payments, both orchestrated by db:seed:dev (which still runs the base db:seed first) - Drop the payment seed call (and now-unused seed helper) from db/seeds.rb - Rename the dev task db:dev:seed -> db:seed:dev to sit alongside the built-in db:seed:replant Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Clarify payment seed header comment rake db:seed:payments already runs the file on its own, so the prior 'can also be run independently' wording was confusing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Move all payment seeding from dummy into dev/payments.rb PR #1565 (merged to main) added a Scholarships/Payments/Allocations section to the dummy dev seeds to give the event-overview dashboard real numbers. With dev/payments.rb now the single home for payment sample data, keeping payment-record creation in dummy split that concern across two files. - Move the scholarship/payment/allocation funding (CashPayment, CheckPayment, Allocation, Scholarship) out of dummy.rb into payments.rb, re-resolving the events/people/registrations it funds via find_by since those are still created in dummy.rb (run first by db:seed:dev) - The funding guards on missing events, so db:seed:payments on its own is a no-op for dummy events and only funds them as part of db:seed:dev - Event cost_cents, the scholarship application form, and registration scholarship flags stay in dummy.rb — they're event/registration setup, not payment records - Normalize payments.rb's leftover global indentation to top level Verified via `rake db:seed:payments`: dashboards report the expected received/outstanding/scholarship totals. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2b665c4 commit f81b949

6 files changed

Lines changed: 292 additions & 276 deletions

File tree

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ DATABASE_URL=trilogy://user:password@host:port/database?encoding=utf8mb4&collati
100100

101101
- Running mise should have run `rake db:seed`
102102
- This will populate the basic data needed to use the app
103-
- To add more sample data for development, run `rake db:dev:seed`
104-
- This will add sample workshops, community news, stories, resources, FAQs, and more
103+
- To add more sample data for development, run `rake db:seed:dev`
104+
- This will add sample workshops, community news, stories, resources, FAQs, payments, and more
105105
- To see your data
106106
- The home page will show Workshops, CommunityNews, Resources, Events, and Stories
107107
- The [Admin Home](http://localhost:3000/admin) provides CRUD access for most models

db/seeds.rb

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
# Disable email delivery during seeding
22
ActionMailer::Base.perform_deliveries = false
33

4-
def seed(file)
5-
require_relative "seeds/#{file}"
6-
end
7-
84
puts "Creating Users…"
95

106
# Helper: case-insensitive find-or-create by name
@@ -488,5 +484,3 @@ def find_or_create_by_name!(klass, name, **attrs, &block)
488484
end
489485
cat.update!(published: true) unless cat.published?
490486
end
491-
492-
seed "payments"
Lines changed: 0 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,91 +1502,6 @@
15021502
end
15031503
end
15041504

1505-
puts "Creating Scholarships, Payments, and Allocations…"
1506-
# Gives the paid dev events real money + scholarship records so the event
1507-
# overview dashboard (registrants / received / outstanding / scholarships)
1508-
# shows meaningful numbers. Registrations and applications (registration-form
1509-
# submissions) are created above; this fills in the financial side.
1510-
#
1511-
# Each registration is funded at most once — the guard skips any registration
1512-
# that already has allocations, so the section is safe to re-run.
1513-
1514-
org_payer = Organization.find_by(name: "Angel Step Inn")
1515-
1516-
# Mirrors ScholarshipsController: build the scholarship with a $0 allocation,
1517-
# then set the amount + tasks_completed so sync_allocation_amount funds the
1518-
# allocation only when the recipient's tasks are complete.
1519-
award_scholarship = ->(registration, amount_cents:, tasks_completed:) do
1520-
scholarship = Scholarship.new(recipient: registration.registrant)
1521-
scholarship.build_allocation(allocatable: registration, amount: 0)
1522-
scholarship.save!
1523-
scholarship.update!(amount_cents: amount_cents, tasks_completed: tasks_completed)
1524-
scholarship
1525-
end
1526-
1527-
# payer is a Person or an Organization; kind is :cash or :check.
1528-
record_payment = ->(registration, payer:, amount_cents:, kind: :cash) do
1529-
payer_attrs = payer.is_a?(Organization) ? { organization: payer } : { person: payer }
1530-
created_at = rand(3..30).days.ago
1531-
payment = case kind
1532-
when :check
1533-
CheckPayment.create!(**payer_attrs, amount_cents: amount_cents, check_number: "CHK-#{rand(10_000..99_999)}", created_at: created_at)
1534-
else
1535-
CashPayment.create!(**payer_attrs, amount_cents: amount_cents, created_at: created_at)
1536-
end
1537-
Allocation.create!(source: payment, allocatable: registration, amount: amount_cents, created_at: created_at)
1538-
end
1539-
1540-
# Funds a registration once. `scholarship` and `payments` describe what to build.
1541-
fund_registration = ->(event, person, scholarship: nil, payments: []) do
1542-
return unless event && person
1543-
registration = EventRegistration.find_by(event: event, registrant: person)
1544-
return unless registration
1545-
return if registration.allocations.exists?
1546-
1547-
award_scholarship.(registration, **scholarship) if scholarship
1548-
payments.each { |payment| record_payment.(registration, **payment) }
1549-
end
1550-
1551-
# --- AWBW Facilitator Training ($150) ---
1552-
# Amy: pending scholarship (tasks incomplete → $0 allocated) + partial cash → still owes
1553-
fund_registration.(facilitator_training, amy_person,
1554-
scholarship: { amount_cents: 10_000, tasks_completed: false },
1555-
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])
1556-
# Maria: paid in full by cash
1557-
fund_registration.(facilitator_training, maria_j,
1558-
payments: [ { payer: maria_j, amount_cents: 15_000, kind: :cash } ])
1559-
# Anna: paid in full by check from her organization (org-payer scenario)
1560-
fund_registration.(facilitator_training, anna_g,
1561-
payments: [ { payer: org_payer || anna_g, amount_cents: 15_000, kind: :check } ])
1562-
# Mario: partial cash → still owes
1563-
fund_registration.(facilitator_training, mario_j,
1564-
payments: [ { payer: mario_j, amount_cents: 5_000, kind: :cash } ])
1565-
1566-
# --- Facilitator Training: Trauma-Informed Art Practices ($120) ---
1567-
# Sarah: paid in full by check
1568-
fund_registration.(trauma_training, sarah_s,
1569-
payments: [ { payer: sarah_s, amount_cents: 12_000, kind: :check } ])
1570-
# Jessica: completed scholarship ($80) + cash for the remainder → paid in full
1571-
fund_registration.(trauma_training, jessica_b,
1572-
scholarship: { amount_cents: 8_000, tasks_completed: true },
1573-
payments: [ { payer: jessica_b, amount_cents: 4_000, kind: :cash } ])
1574-
# Angel: partial cash → still owes
1575-
fund_registration.(trauma_training, angel_g,
1576-
payments: [ { payer: angel_g, amount_cents: 6_000, kind: :cash } ])
1577-
1578-
# --- Mindful Art for Survivors Workshop ($50) ---
1579-
# Amy: paid in full by cash
1580-
fund_registration.(mindful_art, amy_person,
1581-
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])
1582-
1583-
[ facilitator_training, trauma_training, mindful_art ].compact.each do |event|
1584-
dashboard = EventDashboard.new(event)
1585-
puts " #{event.title}: #{dashboard.registrant_count} registrants, " \
1586-
"received #{dashboard.received_cents / 100.0}, outstanding #{dashboard.outstanding_cents / 100.0}, " \
1587-
"scholarships #{dashboard.scholarship_total_cents / 100.0} (#{dashboard.scholarship_recipient_count})"
1588-
end
1589-
15901505
puts "Creating Resources…"
15911506
10.times do |i|
15921507
kind = Resource::PUBLISHED_KINDS.sample

db/seeds/dev/payments.rb

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
# Payment seeds (dev-only) - run on their own via `rake db:seed:payments`, or
2+
# as part of `rake db:seed:dev`.
3+
4+
puts "Seeding Payments, Allocations, and Refunds..."
5+
6+
payment_ids_start = Payment.maximum(:id) || 0
7+
allocation_ids_start = Allocation.maximum(:id) || 0
8+
refund_ids_start = Refund.maximum(:id) || 0
9+
event_reg_ids_start = EventRegistration.maximum(:id) || 0
10+
11+
Refund.where("id > ?", refund_ids_start).delete_all
12+
Allocation.where("id > ?", allocation_ids_start).delete_all
13+
Payment.where("id > ?", payment_ids_start).delete_all
14+
EventRegistration.where("id > ?", event_reg_ids_start).delete_all
15+
16+
event_cost_cents = 150000
17+
18+
bob = Person.find_or_create_by!(email: "bob.payment@seed.example.com") do |p|
19+
p.first_name = "Bob"
20+
p.last_name = "Barker"
21+
end
22+
23+
alice = Person.find_or_create_by!(email: "alice.payment@seed.example.com") do |p|
24+
p.first_name = "Alice"
25+
p.last_name = "Test"
26+
end
27+
28+
charlie = Person.find_or_create_by!(email: "charlie.payment@seed.example.com") do |p|
29+
p.first_name = "Charlie"
30+
p.last_name = "Test"
31+
end
32+
33+
diana = Person.find_or_create_by!(email: "diana.payment@seed.example.com") do |p|
34+
p.first_name = "Diana"
35+
p.last_name = "Test"
36+
end
37+
38+
eve = Person.find_or_create_by!(email: "eve.payment@seed.example.com") do |p|
39+
p.first_name = "Eve"
40+
p.last_name = "Test"
41+
end
42+
43+
frank = Person.find_or_create_by!(email: "frank.payment@seed.example.com") do |p|
44+
p.first_name = "Frank"
45+
p.last_name = "Test"
46+
end
47+
48+
gary = Person.find_or_create_by!(email: "gary.payment@seed.example.com") do |p|
49+
p.first_name = "Gary"
50+
p.last_name = "Test"
51+
end
52+
53+
holly = Person.find_or_create_by!(email: "holly.payment@seed.example.com") do |p|
54+
p.first_name = "Holly"
55+
p.last_name = "Test"
56+
end
57+
58+
iris = Person.find_or_create_by!(email: "iris.payment@seed.example.com") do |p|
59+
p.first_name = "Iris"
60+
p.last_name = "Test"
61+
end
62+
63+
event = Event.find_or_create_by!(title: "Test Payments Workshop") do |e|
64+
e.start_date = 1.month.from_now.to_date
65+
e.end_date = 1.month.from_now.to_date + 2.days
66+
e.published = true
67+
e.cost_cents = event_cost_cents
68+
end
69+
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)
70+
71+
reg_bob = EventRegistration.find_or_create_by!(registrant: bob, event: event)
72+
reg_alice = EventRegistration.find_or_create_by!(registrant: alice, event: event)
73+
reg_charlie = EventRegistration.find_or_create_by!(registrant: charlie, event: event)
74+
reg_diana = EventRegistration.find_or_create_by!(registrant: diana, event: event)
75+
reg_eve = EventRegistration.find_or_create_by!(registrant: eve, event: event)
76+
reg_frank = EventRegistration.find_or_create_by!(registrant: frank, event: event)
77+
reg_gary = EventRegistration.find_or_create_by!(registrant: gary, event: event)
78+
reg_iris = EventRegistration.find_or_create_by!(registrant: iris, event: event)
79+
80+
puts " Payment made but allocation reverted)"
81+
payment1 = CashPayment.find_or_create_by!(
82+
person: bob,
83+
amount_cents: event_cost_cents
84+
) do |p|
85+
p.created_at = 5.days.ago
86+
end
87+
original_allocation1 = Allocation.create!(
88+
source: payment1,
89+
allocatable: reg_bob,
90+
amount: event_cost_cents,
91+
created_at: 5.days.ago
92+
)
93+
reversal_allocation1 = Allocation.create!(
94+
source: payment1,
95+
allocatable: reg_bob,
96+
amount: -event_cost_cents,
97+
created_at: 4.days.ago
98+
)
99+
original_allocation1.update!(reverted_id: reversal_allocation1.id)
100+
101+
puts " Overpayment with full allocation (Alice pays $6000, covers 4 people)"
102+
payment2 = CashPayment.find_or_create_by!(
103+
person: alice,
104+
amount_cents: 600000
105+
) do |p|
106+
p.created_at = 4.days.ago
107+
end
108+
Allocation.find_or_create_by!(source: payment2, allocatable: reg_alice, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }
109+
Allocation.find_or_create_by!(source: payment2, allocatable: reg_charlie, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }
110+
Allocation.find_or_create_by!(source: payment2, allocatable: reg_diana, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }
111+
Allocation.find_or_create_by!(source: payment2, allocatable: reg_eve, amount: event_cost_cents) { |a| a.created_at = 4.days.ago }
112+
113+
puts " Payment with remaining available ($2000 payment, $1500 allocated, $500 remaining)"
114+
payment3 = CashPayment.find_or_create_by!(
115+
person: frank,
116+
amount_cents: 200000
117+
) do |p|
118+
p.created_at = 3.days.ago
119+
end
120+
Allocation.find_or_create_by!(
121+
source: payment3,
122+
allocatable: reg_frank,
123+
amount: 150000
124+
) do |a|
125+
a.created_at = 3.days.ago
126+
end
127+
128+
puts " Full refund ($1500 payment, fully allocated, fully refunded)"
129+
payment4 = CashPayment.find_or_create_by!(
130+
person: gary,
131+
amount_cents: event_cost_cents
132+
) do |p|
133+
p.created_at = 2.days.ago
134+
end
135+
original_alloc = Allocation.create!(
136+
source: payment4,
137+
allocatable: reg_gary,
138+
amount: event_cost_cents,
139+
created_at: 2.days.ago
140+
)
141+
reversal_alloc = Allocation.create!(
142+
source: payment4,
143+
allocatable: reg_gary,
144+
amount: -event_cost_cents,
145+
created_at: 1.day.ago
146+
)
147+
original_alloc.update!(reverted_id: reversal_alloc.id)
148+
Refund.create!(
149+
refundable: payment4,
150+
recipient: gary,
151+
amount_cents: event_cost_cents,
152+
method: "check",
153+
created_at: 1.day.ago
154+
)
155+
156+
puts " Creating Scenario 8: Payment with no allocations ($10000, full amount remaining)"
157+
CashPayment.find_or_create_by!(
158+
person: holly,
159+
amount_cents: 1000000
160+
) do |p|
161+
p.created_at = 7.days.ago
162+
end
163+
164+
puts " Partial payment"
165+
payment9 = CashPayment.find_or_create_by!(
166+
person: iris,
167+
amount_cents: 100000
168+
) do |p|
169+
p.created_at = 3.days.ago
170+
end
171+
Allocation.find_or_create_by!(
172+
source: payment9,
173+
allocatable: reg_iris,
174+
amount: 100000
175+
) do |a|
176+
a.created_at = 3.days.ago
177+
end
178+
179+
puts "Creating Scholarships, Payments, and Allocations for dev events…"
180+
# Funds registrations on the paid dev events with real money + scholarship
181+
# records so the event overview dashboard (registrants / received / outstanding
182+
# / scholarships) shows meaningful numbers. The events, people, and registrations
183+
# are created in db/seeds/dev/dummy.rb, so this only fills in the financial side
184+
# when the dummy seeds have already run (e.g. via `rake db:seed:dev`); on its own
185+
# `rake db:seed:payments` simply skips events that aren't present.
186+
#
187+
# Each registration is funded at most once — the guard skips any registration
188+
# that already has allocations, so the section is safe to re-run.
189+
190+
amy_person = User.find_by(email: "amy.user@example.com")&.person
191+
maria_j = Person.find_by(first_name: "Maria", last_name: "Johnson")
192+
anna_g = Person.find_by(first_name: "Anna", last_name: "Garcia")
193+
sarah_s = Person.find_by(first_name: "Sarah", last_name: "Smith")
194+
jessica_b = Person.find_by(first_name: "Jessica", last_name: "Brown")
195+
mario_j = Person.find_by(first_name: "Mario", last_name: "Johnson")
196+
angel_g = Person.find_by(first_name: "Angel", last_name: "Garcia")
197+
198+
facilitator_training = Event.find_by(title: "AWBW Facilitator Training")
199+
trauma_training = Event.find_by(title: "Facilitator Training: Trauma-Informed Art Practices")
200+
mindful_art = Event.find_by(title: "Mindful Art for Survivors Workshop")
201+
202+
org_payer = Organization.find_by(name: "Angel Step Inn")
203+
204+
# Mirrors ScholarshipsController: build the scholarship with a $0 allocation,
205+
# then set the amount + tasks_completed so sync_allocation_amount funds the
206+
# allocation only when the recipient's tasks are complete.
207+
award_scholarship = ->(registration, amount_cents:, tasks_completed:) do
208+
scholarship = Scholarship.new(recipient: registration.registrant)
209+
scholarship.build_allocation(allocatable: registration, amount: 0)
210+
scholarship.save!
211+
scholarship.update!(amount_cents: amount_cents, tasks_completed: tasks_completed)
212+
scholarship
213+
end
214+
215+
# payer is a Person or an Organization; kind is :cash or :check.
216+
record_payment = ->(registration, payer:, amount_cents:, kind: :cash) do
217+
payer_attrs = payer.is_a?(Organization) ? { organization: payer } : { person: payer }
218+
created_at = rand(3..30).days.ago
219+
payment = case kind
220+
when :check
221+
CheckPayment.create!(**payer_attrs, amount_cents: amount_cents, check_number: "CHK-#{rand(10_000..99_999)}", created_at: created_at)
222+
else
223+
CashPayment.create!(**payer_attrs, amount_cents: amount_cents, created_at: created_at)
224+
end
225+
Allocation.create!(source: payment, allocatable: registration, amount: amount_cents, created_at: created_at)
226+
end
227+
228+
# Funds a registration once. `scholarship` and `payments` describe what to build.
229+
fund_registration = ->(event, person, scholarship: nil, payments: []) do
230+
return unless event && person
231+
registration = EventRegistration.find_by(event: event, registrant: person)
232+
return unless registration
233+
return if registration.allocations.exists?
234+
235+
award_scholarship.(registration, **scholarship) if scholarship
236+
payments.each { |payment| record_payment.(registration, **payment) }
237+
end
238+
239+
# --- AWBW Facilitator Training ($150) ---
240+
# Amy: pending scholarship (tasks incomplete → $0 allocated) + partial cash → still owes
241+
fund_registration.(facilitator_training, amy_person,
242+
scholarship: { amount_cents: 10_000, tasks_completed: false },
243+
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])
244+
# Maria: paid in full by cash
245+
fund_registration.(facilitator_training, maria_j,
246+
payments: [ { payer: maria_j, amount_cents: 15_000, kind: :cash } ])
247+
# Anna: paid in full by check from her organization (org-payer scenario)
248+
fund_registration.(facilitator_training, anna_g,
249+
payments: [ { payer: org_payer || anna_g, amount_cents: 15_000, kind: :check } ])
250+
# Mario: partial cash → still owes
251+
fund_registration.(facilitator_training, mario_j,
252+
payments: [ { payer: mario_j, amount_cents: 5_000, kind: :cash } ])
253+
254+
# --- Facilitator Training: Trauma-Informed Art Practices ($120) ---
255+
# Sarah: paid in full by check
256+
fund_registration.(trauma_training, sarah_s,
257+
payments: [ { payer: sarah_s, amount_cents: 12_000, kind: :check } ])
258+
# Jessica: completed scholarship ($80) + cash for the remainder → paid in full
259+
fund_registration.(trauma_training, jessica_b,
260+
scholarship: { amount_cents: 8_000, tasks_completed: true },
261+
payments: [ { payer: jessica_b, amount_cents: 4_000, kind: :cash } ])
262+
# Angel: partial cash → still owes
263+
fund_registration.(trauma_training, angel_g,
264+
payments: [ { payer: angel_g, amount_cents: 6_000, kind: :cash } ])
265+
266+
# --- Mindful Art for Survivors Workshop ($50) ---
267+
# Amy: paid in full by cash
268+
fund_registration.(mindful_art, amy_person,
269+
payments: [ { payer: amy_person, amount_cents: 5_000, kind: :cash } ])
270+
271+
[ facilitator_training, trauma_training, mindful_art ].compact.each do |event|
272+
dashboard = EventDashboard.new(event)
273+
puts " #{event.title}: #{dashboard.registrant_count} registrants, " \
274+
"received #{dashboard.received_cents / 100.0}, outstanding #{dashboard.outstanding_cents / 100.0}, " \
275+
"scholarships #{dashboard.scholarship_total_cents / 100.0} (#{dashboard.scholarship_recipient_count})"
276+
end
277+
278+
puts " Payment seeds complete!"

0 commit comments

Comments
 (0)