[westside-email-agent] Add query_monthly_fee_due to email_queries.py registry #512
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/basketball-api#512
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Feature
Lineage
Standalone — discovered while scoping westside-email-agent project on 2026-04-23. The blast system is generic and the agent CLAUDE.md is wired, but the QUERY_REGISTRY has no audience query for monthly fee reminders. This is the only blocker for sending monthly billing emails today.
Repo
forgejo_admin/basketball-apiUser Story
As Lucas (or Marcus via the agent)
I want a
monthly_fee_dueaudience query registered in basketball-apiSo that I can blast monthly fee reminders to the right parents using the existing /admin/email/blast endpoint without further code changes
Context
The email blast endpoint (
routes/admin.py:1233) is fully generic — it validates query name againstQUERY_REGISTRYand supports per-recipient placeholder substitution. Adding a new audience = add a query function + register it in the dict. No endpoint changes needed.The audience for "monthly fee due": parents whose players have an active monthly subscription (Stripe subscription or recorded
monthly_feefield) where the next payment is due within N days, OR has failed in the last billing cycle. Exact filter logic TBD with Lucas at /review-ticket — likely starts as "active subscription, next payment within 5 days OR last invoice failed."The result rows must include a
cta_urlpointing to the parent's Stripe customer portal (or equivalent self-service payment update URL).File Targets
Files to modify:
src/basketball_api/services/email_queries.py— addquery_monthly_fee_duefunction (model on existingquery_unsigned_contractsshape); register inQUERY_REGISTRYtests/services/test_email_queries.py(or equivalent) — add test for the new queryFiles to NOT touch:
src/basketball_api/routes/admin.py— endpoint is already generic, no changes neededtemplates/email/compiled/— using existingactionlayout, no template changesAcceptance Criteria
query_monthly_fee_due(db, tenant, *, test_email=None)exists and returnslist[dict]with keys:to,parent_id,parent_name,player_name,cta_url(Stripe portal or payment-update URL), plus any other fields Lucas decides to template into the bodyQUERY_REGISTRYunder the key"monthly_fee_due"test_emailfilter works (returns at most 1 row when matching parent's email is provided)GETofget_available_queries()returns the new query name in the sorted listTest Expectations
test_query_monthly_fee_due— given seed data with N parents (some with active subs, some without), function returns the right N rowstest_query_monthly_fee_due_test_email_filter— filters to a single parent when test_email matchescd ~/basketball-api && pytest tests/services/test_email_queries.py -k monthlyConstraints
query_unsigned_contracts(lines 36–91 ofemail_queries.py) — it's the cleanest single-table example/review-ticketbefore implementation. Default proposal: active subscription, next billing date within 5 days OR most recent invoice failedChecklist
Related
westside-email-agent— project this serveswestside-basketball— repo this lives inarch:basketball-api— architecture component