Commit 20850756 authored by Mahmoud Aglan's avatar Mahmoud Aglan

feat(waiver): comprehensive debt check, board sees all dependents, detailed receipts

1. Debt check now covers ALL financial types: subscriptions, fines, installments,
   payment requests, unpaid sales, and seasonal memberships — for member + all dependents.
2. Board approval screen shows ALL target dependents (not just excess) with full details
   (name, age, DOB, category, within-allowance flag). Board decides per-person fees independently.
3. Receipt/payment request includes per-individual itemized breakdown with calculation
   method, person type, age category — stored as structured JSON for audit trail.
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 99e7632a
This diff is collapsed.
# Waiver Module — Architecture Map
> **Last updated:** 2026-06-26 (Round 2: comprehensive debt check with per-person details, children age/DOB display, payment in target's name, send-to-cashier flow, status indicators)
> **Last updated:** 2026-07-02 (Round 3: fully comprehensive debt check covering sales/seasonal/all payment types, board sees ALL dependents not just excess, per-individual detailed receipts with audit trail)
> **Status:** Living document — incrementally updated as new information is discovered
---
......@@ -120,71 +120,72 @@ requested → approved → fee_paid → completed
## 5. Core Business Flows
### 5.1 Waiver Request Flow (Bylaws-Compliant — 2026-06-22)
### 5.1 Waiver Request Flow (2026-07-02)
```
1. GET /waivers/create/{memberId}
- Validate member exists and is not archived
- Run WaiverProcessor::checkDebts(memberId) — checks subscriptions, fines, payment_requests
- If debts exist: show debt table, BLOCK form submission
- Run WaiverProcessor::checkDebtsComprehensive(memberId) — checks ALL financial types:
subscriptions, fines, installments, payment_requests, sales, seasonal_memberships
- Checks cover member + ALL dependents (via subscriptions.person_type)
- If debts exist: show detailed debt table per-person, BLOCK form submission
- Calculate waiver fee: WAIVER_FEE percentage × CURRENT membership value (from pricing_configs)
- Run WaiverProcessor::countDependents(memberId) — counts spouses, children, temporary
- Display: fee breakdown, dependent grid, debt status, document upload fields
2. POST /waivers/store/{memberId}
- VALIDATE: member exists, has membership_number
- VALIDATE: checkDebts → must be clear (blocks if any unpaid)
- VALIDATE: checkDebtsComprehensive(source) → must be clear (blocks if any unpaid)
- VALIDATE: checkDebtsComprehensive(target) → must be clear (blocks if any unpaid)
- Calculate fee: WAIVER_FEE (default 30%) × current pricing_configs value
- Count all active dependents (spouses + children + temp)
- Handle file uploads: waiver_request_doc, target_form_doc (PDF/JPG/PNG, max 5MB)
- Optionally accept target_member_id
- Create WaiverRequest (status='requested', debts_cleared=1)
- Dispatch: waiver.requested
- Send payment request to Cashier queue (waiver_fee amount)
3. POST /waivers/{id}/approve
- Must be status='requested'
- Board sets: board_decision_reference, excess_fee_percentage (if target has excess dependents)
- If target exists AND target dependents > original count:
excess_count = target_deps - original_count
excess_fee = (excess_fee_percentage × membership_value / 100) × excess_count
- Set approved_by, approved_at, excess_dependent_count, excess_fee_amount
- Board sees ALL target dependents (not just excess) via getAllTargetDependents()
- Each person shows: name, age, DOB, category, within-allowance indicator
- Board decides per-person: "بدون رسوم" / مبلغ ثابت / نسبة من قيمة العضوية
- Per-individual fees saved to waiver_individual_fees table
- Set approved_by, approved_at, per-category totals
- Status → 'approved'
- Sends combined payment request (waiver_fee + excess_fee) to Cashier
4. POST /waivers/{id}/reject
- Must be status='requested'
- Accepts rejection_reason in notes
- Status → 'rejected'
5. POST /waivers/{id}/pay (or via Cashier queue)
- Process payment: waiver_fee + excess_fee combined
5. POST /waivers/{id}/send-to-cashier
- Must be status='approved'
- Creates payment request with DETAILED receipt items:
waiver_fee + per-individual fees (names, ages, categories, calculation method)
- Receipt JSON includes: fee_breakdown, receipt_items[], totals{}
- Status stays 'approved' until payment is made
6. Payment via Cashier → waiver.fee_paid event → autoComplete()
- Status → 'fee_paid'
- Dispatch: waiver.fee_paid
- autoComplete validates: debts clear, board reviewed, target set
- If all conditions met → auto-executes transfer
6. POST /waivers/{id}/complete
- Optionally update target_member_id from POST data
- PRE-VALIDATION (before processor runs):
7. POST /waivers/{id}/complete (manual fallback)
- PRE-VALIDATION:
a. Target member must be set
b. checkDebts(source) — ALL financial obligations must be clear
c. countDependents(target) check:
- If target > original AND no excess_fee_amount set → block (board must approve first)
d. Status must be 'approved' or 'fee_paid'
e. Records new_dependent_count, debts_cleared=1
b. checkDebtsComprehensive(source) — ALL obligations clear
c. checkDebtsComprehensive(target) — ALL obligations clear
d. If excess dependents: board must have reviewed (individual_fees exist)
e. Status must be 'fee_paid'
f. Board approval required
- Execute WaiverProcessor::execute():
a. Validate: status must be 'approved' or 'fee_paid'
b. Validate: target_member_id must be set
c. Transaction:
- Archive snapshot of source member (full data preserved in archive_snapshots)
- Archive source member FIRST (number=NULL, status='waived', is_archived=1) — releases unique constraint
- Transfer membership_number to target member (set number, status='active', membership_type inherited)
- Record number chain via ArchiveService::recordNumberTransfer
a. Transaction:
- Archive snapshot of source member
- Archive source member (number=NULL, status='waived', is_archived=1)
- Transfer membership_number to target member
- Record number chain via ArchiveService
- Status → 'completed'
d. Dispatch: waiver.completed
- POST-STATE:
- Source member: archived, browsable via Archive module
- Target member: active, owns the membership number
- All historical data preserved (no hard deletes)
b. Dispatch: waiver.completed
```
### 5.2 Fee Calculation (Per-Category — 2026-06-23)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment