Members Suite — Forms
Forms — Members Suite
UX / forms specification · slug
members· 2026-05-02 Companion todocs/blueprints/members.md. Reading order: Categorize → Founder → Substantial Donor → Recurring Donor → Volunteer Skills → Volunteer Availability → Communication Preferences → Mailing List → Tag/Segment → Donor History → Volunteer Match.
This document specifies every screen-level form in the Members Suite — the CRM face of the seven-category model (Founders, Substantial Donors, Recurring Donors, Service Recipients, Casual Associates, Volunteers, Staff Index). The single canonical members table remains the source of truth for biographical fields; the forms below operate on the categorization layer (member_categorizations), the donor aggregate denormalisation (member_donor_aggregates), the volunteer skill inventory (volunteer_skills), and the communication-preferences / subscription tables. A person can hold multiple active categorizations simultaneously — donor and volunteer and parent of a student — and every form respects that multiplicity.
Section 1 — Categorization
Workflow note: Every member starts life in casual_associate on first creation (DB-level default). Categorization is then a combination of automatic and manual workflows. Substantial-donor and recurring-donor categorizations are computed nightly from member_donor_aggregates and live Razorpay subscription state; service-recipient is derived from active sevas; volunteer and founder are manual; staff_index is auto-set by Staff Details onboarding. The Categorize Member form lets an admin override or supplement these — for example, recognising a long-serving sevadar as a Founder, or pre-categorizing a substantial donor before the nightly aggregate refresh has caught up.
Form 1.1 — Categorize Member
Purpose: Add or update a member's category memberships. Multi-category allowed. Who fills: Tenant-admin or module-admin; ad-hoc.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked from URL context | current | Subject of categorization |
| Categories to add | Multi-select chips | Yes | founder / substantial_donor / recurring_donor / service_recipient / casual_associate / volunteer / staff_index | — | Existing active categories greyed and pre-checked |
| Categories to remove | Multi-select chips | No | only currently-active categories | — | Triggers decategorize for each |
| Since date (per added category) | Date | Yes | <= today | today | When this categorization began |
| Recognition level | Select | No | Bronze / Silver / Gold / Platinum / Founder / (free text) | — | Ranks a donor or founder; visible on donor wall |
| Source | Radio | Yes | Manual / Imported / Automatic-override | Manual | "Automatic-override" forces a re-eval against nightly job |
| Threshold evidence (JSON) | Textarea | No | valid JSON | — | e.g. {"trigger":"manual","fy":"FY2025-26","note":"endowment of ₹5L"} |
| Notes | Textarea | No | max 1000 | — | Internal context |
| Reason for change | Text | Yes | max 255 | — | Written to audit_event |
| Effective date | Date | No | <= today + 30 | today | Override since_date if needed |
Submit action: POST /api/members/{member_id}/categorize for adds; DELETE /api/members/{member_id}/categorize/{category} for removes — both wrapped server-side in one transaction with a single audit event member.recategorized. Refreshes category-filter chips on the member detail page.
Related forms: Form 2.1 (Founder Recognition) for additional founder fields; Form 3.1 (Substantial Donor Override) for threshold tuning.
Section 2 — Founders
Workflow note: Founders are a small, deliberately curated set — original trustees, founding archakas, primary land-donors, and equivalents. The category is rarely auto-set; it is recorded once at institutional setup (or backfilled from awardees legacy data) and rarely changed thereafter. The Founder Recognition Entry form captures the public-facing founding role, the since-date, and a recognition tier rendered on the donor wall and on annual report covers.
Form 2.1 — Founder Recognition Entry
Purpose: Record the founding role and recognition level. Augments the base categorization. Who fills: Tenant-admin or chairperson; once at recognition.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | |
| Founding role | Text | Yes | max 127 | — | e.g. Founder Trustee, Pratisthapaka Acharya, Mula Bhumi Daanam |
| Since date | Date | Yes | <= today | — | Date of original contribution / installation |
| Recognition level | Select | Yes | Bronze / Silver / Gold / Platinum / Founder / Maha-Founder | Founder | Drives wall and report rendering |
| Citation (display text) | Textarea | Yes | max 1000 | — | Shown on donor wall under name |
| Public consent | Checkbox | Yes | — | false | Required to display on public wall |
| Photo for wall | File upload | No | jpg/png, square, max 2 MB | from members.photo | Override default photo |
| Founding contribution summary | Textarea | No | max 2000 | — | Land donated, capital seed, building sponsored |
| Anniversary date (commemoration) | Date | No | day-and-month only used | — | Drives anniversary Comms |
| Trustee number / serial | Text | No | max 32 | — | If on registered trust deed |
| Notes | Textarea | No | max 2000 | — | Internal |
Submit action: POST /api/members/{member_id}/categorize with category=founder + recognition_level + threshold_data carrying citation and role. Audit event member.founder.recognized.
Related forms: Form 1.1 (Categorize) is the underlying primitive; donor wall rendering is Vitta-side.
Section 3 — Donors
Workflow note: Donor categorization runs primarily off member_donor_aggregates, refreshed nightly (and on every confirmed payment event for low-latency recategorization). Two distinct workflows: Substantial Donor Threshold Override lets an admin manually set or revoke this category in advance of or against the automatic rule; Recurring Donor Setup links a member to a Razorpay subscription and pins the auto-categorization to that subscription's lifecycle. Both forms write to audit_events and trigger an immediate compute_substantial_donor_status re-evaluation.
Form 3.1 — Substantial Donor Threshold Override
Purpose: Manually set, revoke, or pin a substantial-donor categorization against the auto rule. Who fills: Tenant-admin or module-admin; case-by-case.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | |
| Current auto-status | Display | — | computed from aggregates | — | Shows lifetime_total vs threshold |
| Action | Radio | Yes | Set as substantial donor / Revoke substantial donor / Pin (do not auto-recompute) | — | "Pin" prevents nightly job from changing this row |
| Override reason | Textarea | Yes | max 1000 | — | e.g. anonymous bulk donor, related-party adjustment, KYC mismatch |
| Recognition level | Select | No | Bronze / Silver / Gold / Platinum | derived from amount | Override if needed |
| Threshold evidence | Textarea | No | JSON | auto-prefilled with aggregate snapshot | Captures the moment-in-time numbers |
| Effective from | Date | Yes | <= today + 30 | today | |
| Pin until | Date | No | required if Action=Pin | — | Pin auto-releases after this date |
| Approver (second-eye) | Select | No (Yes for revoke) | other tenant-admin | — | Required for Revoke |
| Notes | Textarea | No | max 2000 | — |
Submit action: POST /api/members/{member_id}/categorize (or DELETE) with source='manual' and threshold_met carrying the override evidence. Audit event member.substantial_donor.overridden. If Pin, the nightly compute_substantial_donor_status job skips this member until pin_until.
Related forms: Form 3.2 (Recurring Donor) for subscription-linked donors; Form 9.1 (Donor History) shows the underlying transactions.
Form 3.2 — Recurring Donor Setup
Purpose: Link a member to a Razorpay subscription so recurring-donor status auto-tracks subscription state. Who fills: Tenant-admin or self-service via donation flow.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | |
| Razorpay subscription ID | Text | Yes | regex sub_[A-Za-z0-9]{14} |
— | From Razorpay dashboard or webhook |
| Plan | Select | Yes | active Razorpay plans for this tenant | — | Loaded via Razorpay API |
| Amount (INR) | Number | Yes | matches plan amount | from plan | Display only, locked to plan |
| Frequency | Display | — | Monthly / Quarterly / Annual | from plan | |
| Fund allocation | Select | Yes | general / annadana / building / fcra / endowment | general | Routes to fund accounting |
| Start date | Date | Yes | >= today | today | First charge date |
| End date | Date | No | > start | — | Blank for open-ended |
| 80G eligible | Checkbox | No | — | true | Drives certificate generation |
| Send acknowledgement on each charge | Checkbox | No | — | true | Triggers Comms |
| Anniversary card | Checkbox | No | — | true | Annual thank-you Comms |
| Lapse handling | Radio | No | Auto-decategorize after 90 days / Wait for explicit cancel | Auto-90d | Per Open Q #5 in blueprint |
| Notes | Textarea | No | max 1000 | — |
Submit action: POST /api/members/{member_id}/categorize with category=recurring_donor + razorpay_subscription_id stored in threshold_met. Razorpay webhook will subsequently keep state in sync. Audit event member.recurring_donor.set.
Related forms: Form 3.1 (Substantial) — recurring donors often qualify as substantial after 12 months; Form 9.1 (Donor History).
Section 4 — Volunteers
Workflow note: Volunteers are members with the volunteer categorization plus rows in volunteer_skills. The Skill Inventory and Availability forms together populate the matrix that the Volunteer-Match Tool (Form 11.1) reads. Skills are tenant-extensible — a Sangita-focused tenant adds vocal_carnatic and tabla; a Vedapathashala adds chandas_recitation. The default seed catalog covers ten common skills and matches the reference HTML.
Form 4.1 — Volunteer Skill Inventory
Purpose: Capture what services a volunteer can offer and at what proficiency. Who fills: Volunteer self-service (during sign-up) or volunteer coordinator.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | |
| Skills (multi-select with proficiency) | — | — | — | — | Each skill: tick + 1–5 stars + certified flag |
| Driving | Checkbox + Stars | No | proficiency 1–5 | unchecked | Two-wheeler / Four-wheeler / Heavy in notes |
| Cooking | Checkbox + Stars | No | 1–5 | unchecked | Vegetarian satvik / Annadana scale |
| Accounting | Checkbox + Stars | No | 1–5 | unchecked | Tally / book-keeping / GST returns |
| Physical service (kayaka seva) | Checkbox + Stars | No | 1–5 | unchecked | Cleaning / setup / heavy lifting |
| IT / digital | Checkbox + Stars | No | 1–5 | unchecked | Web, sound, projection, livestream |
| Language translation | Checkbox + Stars | No | 1–5 | unchecked | Pair captured below |
| Music (vocal / instrumental) | Checkbox + Stars | No | 1–5 | unchecked | Specify instrument in notes |
| Dance (classical) | Checkbox + Stars | No | 1–5 | unchecked | Bharatanatyam / Kuchipudi / etc |
| Pravachana | Checkbox + Stars | No | 1–5 | unchecked | Public discourse / kathakalakshepa |
| Sanskrit typing | Checkbox + Stars | No | 1–5 | unchecked | VijayaDV-aware preferred |
| Photography | Checkbox + Stars | No | 1–5 | unchecked | Stills / video / drone |
| Sound engineering | Checkbox + Stars | No | 1–5 | unchecked | Live mixing / recording |
| Samagri setup | Checkbox + Stars | No | 1–5 | unchecked | Ritual material preparation |
| Manuscript work | Checkbox + Stars | No | 1–5 | unchecked | Transcription / proofing |
| Chandas recitation | Checkbox + Stars | No | 1–5 | unchecked | Veda-recitation by chandas |
| Other skills (free) | Multi-tag | No | tenant-extensible | — | Auto-suggests existing tenant skills |
| Languages known | Multi-select | No | Sanskrit / Kannada / Tamil / Telugu / Hindi / English / Marathi / Malayalam / Other | — | Pairs with translation skill |
| Certifications | Multi-tag | No | free text | — | First-aid, food-safety, driving licence class |
Submit action: POST /api/members/{member_id}/volunteer-skills per skill (server runs upsert on (member_id, skill_code)). If member is not yet categorized as volunteer, the form silently calls Form 1.1 to add the volunteer category.
Related forms: Form 4.2 (Availability), Form 11.1 (Match Tool).
Form 4.2 — Volunteer Availability Form
Purpose: Capture when a volunteer is available, so Match-Tool can avoid impossible assignments. Who fills: Volunteer self-service or coordinator.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | |
| Availability pattern | Radio | Yes | Weekends only / Weekdays / Full time / Events only / Online only / Custom | Events only | |
| Days of week | Multi-checkbox | No | Mon–Sun | — | Shown only if Custom |
| Time-of-day windows | Multi-checkbox | No | Pratahkal (5–9am) / Morning (9–12) / Afternoon (12–4) / Sayamkal (4–8) / Night (8pm onward) | — | Maps to ritual time-slots |
| Travel radius (km) | Number | No | 0–500 | 25 | From their address — used for branch matching |
| Can travel to other branches | Checkbox | No | — | false | If true, available for any branch |
| Out-of-station dates | Date range repeater | No | up to 12 ranges | — | Blackout dates |
| Annual leave declared | Number (days) | No | 0–365 | — | Cumulative |
| Festival-only mode | Checkbox | No | — | false | Match tool only suggests during marked festivals |
| Lead-time required (days) | Number | No | 0–60 | 3 | Minimum notice |
| Languages willing to serve in | Multi-select | No | inherits from skill form | — | Translation / pravachana audiences |
| Notes | Textarea | No | max 1000 | — |
Submit action: PATCH /api/members/{member_id}/volunteer-skills writes a single availability row keyed off the volunteer's primary skill row, or a dedicated volunteer_availability JSON column on member_categorizations (the categorization row's notes/threshold_data when category=volunteer). Audit event member.volunteer.availability_updated.
Related forms: Form 11.1 (Match Tool) consumes this directly.
Section 5 — Communication & Subscriptions
Workflow note: Communication preferences are a 1:1 per-member record consulted by Comms (Agent 5) before every send. The form distinguishes campaign Comms (newsletters, festival greetings, fund-raising appeals) from transactional Comms (donation receipts, 80G certificates) — the receipt_only flag opts out of the former while preserving the legally-required latter. Mailing-list subscriptions are layered on top: a member can be on the "Annadana donors" list but off the "Festival announcements" list. Double-opt-in is the default; lists can disable it for in-person sign-ups (a tablet at the donation desk).
Form 5.1 — Communication Preferences
Purpose: Per-channel opt-ins and language preferences. The single source-of-truth Comms reads. Who fills: Member self-service preferred; admin can edit on member's behalf.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | |
| Email opt-in | Toggle | No | — | true | Master switch for email |
| WhatsApp opt-in | Toggle | No | — | false | Requires WhatsApp Business consent |
| SMS opt-in | Toggle | No | — | false | High-cost channel; sparingly |
| Postal opt-in | Toggle | No | — | true | Annual report, festival prasadam |
| Receipt-only mode | Toggle | No | — | false | If true, only transactional Comms — donation receipts, 80G — sent regardless of channel switches |
| Preferred language | Select | No | Sanskrit / Kannada / Tamil / Telugu / Hindi / English / Marathi / Malayalam | English | Sets template variant |
| Quiet hours start | Time | No | — | 21:00 | No non-urgent Comms after this |
| Quiet hours end | Time | No | — | 07:00 | No non-urgent Comms before this |
| Quiet days | Multi-checkbox | No | Mon–Sun | — | e.g. observing maunam on Saturdays |
| Festival-only mode | Checkbox | No | — | false | Only festival greetings + receipts |
| Frequency cap (per week) | Number | No | 0–14 | 3 | Max non-urgent messages per channel per week |
| Anniversary greetings | Checkbox | No | — | true | Birth-star, jubilee |
| 80G certificate delivery | Radio | No | Email / Postal / Both | Per FY | |
| Newsletter HTML or plain text | Radio | No | HTML / Plain text | HTML | Accessibility option |
| Notes | Textarea | No | max 500 | — |
Submit action: PUT /api/members/{member_id}/communication-preferences. If actor is the member themselves, sets last_updated_by_member_at. Audit event member.comm_prefs.changed.
Related forms: Form 5.2 (Mailing Lists) for list-level granularity.
Form 5.2 — Mailing List Subscription
Purpose: Subscribe a member to one or more named mailing lists with double-opt-in. Who fills: Member self-service via public form, or admin during in-person sign-up.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | |
| Available lists | Multi-checkbox | No | Monthly Newsletter / Annadana Donors / FCRA Donors / Festival Devotees / Vedapathashala Parents / Pravachana Updates / Tenant-defined… | — | Loaded from member_subscription_lists |
| Primary channel per list | Auto-display | — | from list config | — | Email / Postal / WhatsApp / Multi |
| Force-active (skip double-opt-in) | Checkbox | No (admin only) | — | false | Use only when in-person consent is clear |
| Consent captured by | Select | No | Self / Admin assist / Donation desk / Festival booth | Self | Recorded for audit |
| Consent timestamp | Auto | — | server-set | now | |
| Notes | Textarea | No | max 500 | — |
Submit action: POST /api/members/{member_id}/subscriptions per list. If requires_double_optin (per list config), status=pending_optin and Comms sends confirmation email with token. Member's click on /api/subscriptions/confirm/{token} flips status=active. Audit event member.subscription.requested then member.subscription.confirmed.
Related forms: Form 5.1 (Comm Prefs) is global; Form 5.2 is per-list.
Section 6 — Tags & Segments
Workflow note: Tags are a complement to categories — flat strings with no schema, used for ad-hoc segmentation that doesn't justify a category row. A tenant might tag members with mysuru-camp-2025, prasadam-volunteer, kishori-program, chaturmasya-2026. Tags are consumed by Comms audience builders alongside categories. The Tag/Segment Editor lets an admin create, rename, and bulk-apply tags.
Form 6.1 — Member Tag/Segment Editor
Purpose: Create, rename, and bulk-apply tags to members. Who fills: Tenant-admin or module-admin.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Mode | Radio | Yes | Create new tag / Rename existing / Bulk apply / Bulk remove | Bulk apply | |
| Tag name | Combo (typeahead) | Yes | matches existing or creates new; max 63 | — | Use snake_case or hyphen-case |
| Tag description | Text | No | max 255 | — | Internal context |
| Tag colour | Select | No | 12-swatch palette | grey | Used in chip rendering |
| Target members | Multi-select / saved-segment | Yes | typeahead by name / phone / email; or saved segment | — | Up to 10000 in one batch |
| Filter (advanced) | Builder | No | category / fund / FY / branch / age range | — | Composes a server-side query |
| Apply mode | Radio | Yes (for bulk) | Add to selection / Replace tags on selection / Remove from selection | Add | |
| Audit reason | Textarea | Yes | max 500 | — | Goes into audit_event for each affected member |
| Preview count | Display | — | live count of impacted rows | — | |
| Confirm | Checkbox | Yes | — | false | Required to enable Apply |
Submit action: Server runs the bulk operation in batched transactions (chunks of 500). One audit_event per batch with affected_count + tag + reason. Tag library exposed via existing Tag model — no new table.
Related forms: Comms audience builder consumes tags + categories together.
Section 7 — Donor History & Volunteer Match (Tools)
Workflow note: Two read-mostly tools complete the suite. Donor History merges three underlying tables (transactions, payment_events, member_donor_aggregates) into a single timeline view — useful for personalised stewardship calls and 80G certificate generation. Volunteer-Match takes an event's required skills and date and returns ranked candidates. Both are surfaced from the member detail page and from the segment dashboards.
Form 7.1 — Donor History Merged View (read-only display spec)
Purpose: Display all donation activity for one member in one place. No write operations. Who fills: No fill — admin / treasurer reads.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Member | Display | — | locked | current | Header with photo + name + categories |
| FY filter | Select | No | All / Specific FY (FY2020-21 onward) | All | Limits the timeline |
| Fund filter | Multi-select | No | general / annadana / building / fcra / endowment | All | |
| Channel filter | Multi-select | No | Razorpay / Cheque / Cash / NEFT / UPI / In-kind | All | |
| Display columns (timeline) | — | — | — | — | One row per transaction or event |
| Date | Display | — | from transactions.tx_date | — | Sortable |
| Amount (INR) | Display | — | from transactions.amount | — | |
| Fund | Display | — | from transactions.fund_code | — | Coloured chip |
| Mode | Display | — | from transactions.mode | — | |
| Receipt no | Display | — | from transactions.receipt_no | — | Click to download PDF |
| 80G eligible | Display | — | boolean | — | Tick / cross |
| Razorpay event | Display | — | from payment_events | — | Linked when matched |
| Aggregate cards (top of view) | — | — | — | — | |
| Lifetime total | Display | — | from member_donor_aggregates | — | All funds combined |
| Current FY total | Display | — | from member_donor_aggregates | — | |
| Donation count | Display | — | from member_donor_aggregates | — | |
| Last donation date | Display | — | from member_donor_aggregates | — | |
| Recurring subscription | Display | — | active / paused / cancelled | — | |
| Generate 80G certificate (action) | Button | — | per FY | — | Deep-link to Vitta certificate generator |
| Recompute aggregates (action) | Button | — | admin only | — | Calls POST /api/members/recompute-substantial-donor/{id} |
Submit action: None — read-only. Action buttons open downstream forms (80G in Vitta, Recompute API call). Related forms: Form 3.1 (Threshold Override), Form 3.2 (Recurring Setup).
Form 7.2 — Volunteer-Match Tool
Purpose: Find available volunteers whose skills match an event's requirements. Who fills: Event coordinator or volunteer coordinator.
| Field | Type | Required | Options / Validation | Default | Help |
|---|---|---|---|---|---|
| Event | Select (typeahead) | Yes | active events from Events Planner | — | Loads event date + branch |
| Event date | Date | Yes | from event | from event | Editable for what-if |
| Event branch | Select | Yes | from event | from event | Editable |
| Skills required | Multi-select chips | Yes | catalog from volunteer_skills skill_code |
— | All required skills must match |
| Minimum proficiency | Slider | No | 1–5 | 3 | Per-skill threshold |
| Volunteers needed | Number | Yes | 1–500 | 5 | Caps the candidate list |
| Travel radius cap (km) | Number | No | 0–500 | event branch radius | Filters by volunteer travel_radius |
| Languages required | Multi-select | No | inherits skill catalog | — | Pairs with translation / pravachana |
| Exclude already-rostered today | Checkbox | No | — | true | Filters by volunteer_event_participations |
| Sort by | Radio | No | Best match / Highest rating / Recently active / Distance | Best match | "Best match" weights skill fit + rating + distance |
| Notify selected | Checkbox | No | — | true | Sends Comms invite on commit |
| Invite message | Textarea | No | max 1000 | template | Pre-filled with event context |
| Result table (display, with row actions) | — | — | — | — | Ranked candidates |
| Volunteer name | Display | — | — | — | Click → member detail |
| Photo | Display | — | from members.photo | — | |
| Skill match | Display | — | matched/total | — | e.g. 3/3 |
| Average rating | Display | — | from past participations | — | |
| Distance (km) | Display | — | from address geocoding | — | |
| Last served | Display | — | from volunteer_event_participations |
— | |
| Action | Button | — | Invite / Skip | — | Click writes invite |
Submit action: POST /api/volunteers/match returns ranked list. Per-row Invite calls POST /api/volunteers/{member_id}/event-participation with status=invited and triggers Comms.
Related forms: Form 4.1 (Skill Inventory) and Form 4.2 (Availability) feed this; post-event rating closes the loop on volunteer_event_participations.