Skip to content

Add downloadable receipt for paid bulk payments#2011

Open
maebeale wants to merge 3 commits into
mainfrom
maebeale/bulk-payment-receipt
Open

Add downloadable receipt for paid bulk payments#2011
maebeale wants to merge 3 commits into
mainfrom
maebeale/bulk-payment-receipt

Conversation

@maebeale

@maebeale maebeale commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

🤖 suggested review level: 3 Read 📖 small backend flow mirroring the existing registration receipt; new presenter method + gated slug route

What is the goal of this PR and why is this important?

  • Bulk payers had only an invoice. Once their payment is recorded they now get a receipt (proof money changed hands) they can download for their records.

How did you approach the change?

  • Added EventReceipt.from_bulk_payment alongside the existing from_registration — one itemized line per attendee, the payer's single payment as the ledger entry.
  • A bulk payment has no owed-balance / paid-in-full concept, unlike a registration. EventReceipt now carries a balance_settlement flag: the shared receipt document shows the "Total charged / Balance due $0 / Paid in full" reconciliation only for registrations, and a plain "Amount paid" + "Payment received" for bulk payments.
  • New public, slug-based bulk_payment/:slug/receipt route/action/policy (direct action on BulkPaymentsController, matching how the registration receipt lives on RegistrationsController), gated on FormSubmission#bulk_payment_receipt_available? (a payment is on file); redirects back to the ticket while pending.
  • Reuses the shared events/receipts/_receipt + events/invoices/_actions (print-to-PDF) partials.
  • Refactored AllocationLedgerLabel to label a Payment directly (not only via an allocation), so invoice and receipt stay in sync.

Ticket integration (rebased onto main's ticket redesign)

  • Rebased onto main after the bulk-payment ticket redesign (Payer + payment-status headline, short_date_range, "Registrant details"); kept that redesign wholesale.
  • Folded the receipt in by turning main's standalone Invoice card into a Documents group: invoice (always) + receipt (green "View receipt" link once paid, locked "Available once your payment is received" card until then).

Anything else to add?

  • Specs: presenter (from_bulk_payment), request (receipt gating + amount-paid framing + ticket Documents card states), model predicate. Registration receipt path unchanged (still asserts "Paid in full"/"Balance due"). All green; RuboCop clean.
  • Gating is "any payment on file" (matches the ticket's existing "Payment received" signal) — flag if you'd prefer a stricter check.

Copilot AI review requested due to automatic review settings July 22, 2026 13:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Bulk payers previously had only an invoice; once a payment is on file they
now get a matching paid-in-full receipt from the ticket's Documents section,
mirroring the registration receipt flow. The receipt card is locked until a
payment is recorded, so the payer sees what unlocks it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@maebeale
maebeale force-pushed the maebeale/bulk-payment-receipt branch from 84961a2 to 318399f Compare July 22, 2026 13:41
Copilot AI review requested due to automatic review settings July 22, 2026 13:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@maebeale
maebeale marked this pull request as ready for review July 22, 2026 13:58
The old comment mostly restated the method body. Keep only the point a reader
can't infer: the gate is deliberately looser than the registration receipt's
paid-in-full check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 22, 2026 14:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

A bulk payment has no owed-balance concept, so its receipt shouldn't borrow the
registration receipt's "Total charged / Balance due / Paid in full" framing — it
just records whatever was paid. EventReceipt now carries a balance_settlement
flag; the shared receipt document shows the reconciliation summary + "Paid in
full" badge only when settling a balance (registrations), and a plain "Amount
paid" + "Payment received" for bulk payments.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 22, 2026 14:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@maebeale
maebeale requested a review from jmilljr24 July 25, 2026 12:32

@jmilljr24 jmilljr24 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The receipt doesn't reflect who paid, it's showing who submitted the form. I don't know how critical it is, but I could see someone getting their receipt with wrong payer info and wanted it fixed.

Do we actually need a name/org on the receipt? Invoice makes sense but the receipt is completely transactional. This amount was paid to awbw for xyz. But I don't know if some business (payer) needs so see their name on a receipt.
Image

# payment is on file. Mirrors Registrations#receipt: gated on availability,
# redirecting back to the ticket while payment is still pending.
def receipt
authorize! :bulk_payment, to: :receipt?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
authorize! :bulk_payment, to: :receipt?
authorize! :bulk_payment

@jmilljr24

Copy link
Copy Markdown
Collaborator

Also, being that the receipt is generated based on the event details, if a payment is made when an event is $1500 the receipt reflects that. If the event cost were to change (unlikely but out of principle) then receipt would then be updated. Shouldn't a receipt be static and captured at the time of payment?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants