# Cross-Module Dependency Graph

## Purpose

This graph maps how modules interact with each other — events, shared tables, data flows, and cascading changes. It answers: "If I change X, what else breaks?"

---

## Core Entity: `members` Table

The `members` table is the central hub. Nearly every module in the ERP references it.

### Direct Foreign Keys TO members
| Table | FK Column | Module |
|-------|-----------|--------|
| spouses | member_id | Spouses |
| children | member_id | Children |
| temporary_members | member_id | Temporary |
| subscriptions | member_id | Subscriptions |
| payments | member_id | Payments |
| payment_requests | member_id | Cashier |
| installment_plans | member_id | Installments |
| fines | member_id | Fines |
| violations | member_id | Fines |
| transfer_requests | source_member_id, target_member_id | Transfers |
| waiver_requests | source_member_id, target_member_id | Waiver |
| death_cases | member_id, transferred_to_member_id | Death |
| divorce_cases | member_id, spouse_new_member_id | Divorce |
| carnets | member_id | Carnets |
| receipts | member_id | Receipts |
| member_notes | member_id | Members |
| seasonal_memberships | member_id | Seasonal |
| honorary_members | member_id | Honorary |
| foreign_member_details | member_id | Foreign |

---

## Module → Module Dependencies

### Members Module
| Depends On | How |
|------------|-----|
| Rules (RuleEngine) | All fee calculations, limits, rates |
| Pricing (pricing_configs) | Membership value resolution |
| ServiceCatalog | Form fee price (SVC_NEW_FORM) |
| Branches | Branch selection, branch-specific pricing |
| Qualifications | Pricing tier determination |
| Workflow | New membership workflow instance |
| Forms | Form submission tracking |
| Cashier (PaymentRequestService) | Payment request creation |

### Subscriptions Module
| Depends On | How |
|------------|-----|
| Members | member_id FK, status checks, membership_type for exemptions |
| Spouses | person_type='spouse', active spouses list |
| Children | person_type='child', active children list |
| Temporary | person_type='temporary', active temps list |
| ServiceCatalog | Rate lookup (SVC_ANNUAL_MEMBER, SVC_ANNUAL_CHILD, etc.) |
| Rules (RuleEngine) | Discount percentages, fine rates, grace months, drop years |
| Payments | payment_id FK on paid subscriptions |

### Payments Module
| Depends On | How |
|------------|-----|
| Members | member_id FK |
| Receipts | receipt_id FK |
| Cashier (PaymentRequestService) | Payment request lifecycle |
| Installments | Plan creation on down_payment |
| MembershipPaymentGuard | Activation/deactivation on payment events |
| Workflow | Workflow state transitions on payment |

### Installments Module
| Depends On | How |
|------------|-----|
| Members | member_id, status transitions |
| Payments | payment_id per schedule item |
| Pricing (BoardOffer) | Offer-based terms (grace, interest override) |
| Rules (RuleEngine) | Default rates, min down, max months |

### Fines Module
| Depends On | How |
|------------|-----|
| Members | member_id, status changes (suspended/terminated) |
| Payments | Fine payment via payment_id |
| Cashier | Auto-queue monetary fines |
| Subscriptions | Overdue fine calculation |

### Children Module
| Depends On | How |
|------------|-----|
| Members | member_id FK, membership_value for fee calc |
| Pricing | Current price lookup for fee calculation |
| Rules (RuleEngine) | Fee percentages, age limits, included count |
| Cashier | Addition fee payment requests |

### Spouses Module
| Depends On | How |
|------------|-----|
| Members | member_id FK, membership_value, acquired_member flags |
| Pricing | Current price lookup for fee calculation |
| Rules (RuleEngine) | Fee percentages per order, annual fee, max spouse count |
| Cashier | Addition fee payment requests |

### Temporary Module
| Depends On | How |
|------------|-----|
| Members | member_id FK, membership_value |
| Rules (RuleEngine) | Fee percentages, category validation, age limits |
| Cashier | Addition fee payment requests |

### Waiver Module
| Depends On | How |
|------------|-----|
| Members | Source + target member records |
| Spouses/Children/Temporary | Dependent counts and comparison |
| Archive | Snapshot creation |
| Subscriptions | Cancel source subs, create target subs |
| Cashier | Waiver fee payment request |
| MembershipPaymentGuard | Target activation |

### Death Module
| Depends On | How |
|------------|-----|
| Members | Deceased member + new member creation |
| Spouses | Primary spouse becomes new member |
| Children | Child assignment to new member |
| Temporary | Temp assignment to new member |
| Archive | Snapshot, document transfer |
| Subscriptions | Transfer/create subscriptions for new member |
| Cashier | Death fee payment request |
| MembershipPaymentGuard | New member activation |

### Divorce Module
| Depends On | How |
|------------|-----|
| Members | Original member + new member creation |
| Spouses | Divorced spouse data → new member |
| Children | Child transfer to divorced spouse's new membership |
| Archive | Snapshot creation |
| Subscriptions | New member subscription creation |
| Cashier | Divorce fee payment |
| MemberNumberGenerator | New number for divorced spouse |

### Transfers Module
| Depends On | How |
|------------|-----|
| Members | Source + target member records |
| Children | Child being separated |
| Spouses | Spouse being transferred |
| Pricing | Separation fee = % of current pricing_configs price |
| Archive | Snapshot creation, number chain recording |
| Subscriptions | Sync after transfer completion |
| Cashier | Separation fee payment request |
| MemberNumberGenerator | New number assignment |

---

## Event Dependencies

### Events Dispatched → Listeners

| Event | Dispatched By | Listened By |
|-------|--------------|-------------|
| `member.created` | Members | — |
| `member.activated` | MembershipPaymentGuard | Subscriptions (syncForMember), Pricing (applyFreeSubscriptionBonus) |
| `member.dropped` | InstallmentDefaultJob, OverdueFineJob | — |
| `payment.completed` | PaymentService, RetroactiveMembershipService | Payments bootstrap (workflow transition) |
| `payment_request.completed` | Cashier | PaymentLifecycleService (activation logic) |
| `child.added` | Children | Subscriptions (syncForDependent if fee=0) |
| `child.fee_paid` | Cashier | Subscriptions (syncForDependent) |
| `spouse.added` | Spouses | Subscriptions (syncForDependent if fee=0) |
| `spouse.fee_paid` | Cashier | Subscriptions (syncForDependent) |
| `temporary.added` | Temporary | Subscriptions (syncForDependent if fee=0) |
| `temporary.fee_paid` | Cashier | Subscriptions (syncForDependent) |
| `transfer.completed` | TransferProcessor | Subscriptions (sync target, refresh source) |
| `death.completed` | DeathController | Subscriptions (refresh new member) |
| `divorce.completed` | DivorceController | — |
| `waiver.fee_paid` | Cashier | WaiverAutoCompleteJob (cron) |

---

## Database Dependencies (Shared Entities)

### `pricing_configs` (read by multiple modules)
- Members (fill-form: resolves membership_value)
- BillingService (working/sports bill calculation)
- Children/Spouses/Temporary (fee calculators use current price, NOT stored membership_value)
- Transfers (separation fee base amount)
- Divorce (fee base amount)
- Waiver (waiver fee base)

### `service_catalog` (rate lookup)
- Members (SVC_NEW_FORM for form fee)
- Subscriptions (SVC_ANNUAL_MEMBER, SVC_ANNUAL_SPOUSE, SVC_ANNUAL_CHILD, SVC_ANNUAL_TEMP, + year-specific variants)
- Transfers (FORM_TRANSFER_FEE)

### `system_config` (configuration store)
- MemberNumberGenerator (form_start_number, membership.number_start)

### `board_offers` (promotional pricing)
- Members/BillingService (show bill with offer)
- MemberController (payment request creation with offer snapshot)
- Installments (plan creation uses offer terms)

### `special_discounts`
- Members (assigned per member)
- BillingService (discount line item)
- Pricing/SpecialDiscountService (bonus free subscription years)

### `regulatory_discounts` (bylaw discounts — Arts 97-102, 110)
- Pricing/RegulatoryDiscountService (eligibility evaluation)
- Pricing/PricingEngine (applyRegulatoryDiscount integration)
- Members (cross-branch verification via members table)
- HR (club employee verification via hr_employee_profiles)

### `regulatory_discount_applications` (audit trail)
- Pricing/RegulatoryDiscountController (approval workflow)
- Members (member_id FK for applied discounts)

---

## Workflow Dependencies

### New Membership Workflow (workflow_definition: 'new_membership')
```
potential → under_review → [interview_scheduled] → accepted → payment_pending → active
```
- Created in MemberController::store()
- Transitions driven by PaymentLifecycleService on payment completion
- workflow_instance_id stored on members table

---

## Financial Year Dependencies

All modules that reference `financial_year`:
- Subscriptions (primary consumer)
- Members/MembershipRulesService (rate lookup)
- Members/MembershipValidationService (current FY subscription check)
- AutoFreezeService (subscription block check)
- OverdueFineApplicator (fine progression by year)
- SubscriptionGenerator (batch generation per year)
- MemberController::show() (current FY display)

**Critical**: FY starts July 1. Format: "2025/2026". Determined by `financial_year()` helper in `app/Core/Helpers.php`.

---

## Cascading Impact Analysis

### If `members.status` changes:
- Subscriptions: exempt types skip generation; active status required for dependent sync
- Payments: PaymentGuard validates status for activation
- Carnets: canPrintCarnet() checks status
- AccessMatrix: facility access gated by active status
- Installments: dropped status on default

### If `members.membership_value` changes:
- BillingService: recalculates bill
- Spouses/Children/Temporary fee calculators: use pricing_configs (NOT stored value), so impact is limited to display
- NOTE: Fee calculators use `pricing_configs` current price, not `members.membership_value`

### If `pricing_configs` changes:
- ALL new fee calculations use new prices (separation, waiver, divorce, child addition, spouse addition)
- Existing members' stored `membership_value` does NOT auto-update
- Impact: new members get new prices; existing member fees remain as calculated at time of creation

### If a dependent is added/removed:
- Subscriptions: syncForDependent creates/removes subscription rows
- BillingService: recalculates totals
- Transfers: dependent counts affect separation eligibility
- Waiver: dependent comparison between source and target

### If a payment is voided:
- MembershipPaymentGuard: may deactivate member + all dependents
- Installments: if down_payment voided, plan affected
- Subscriptions: if annual_subscription voided, status reverts
- Fines: if fine payment voided, fine status reverts to imposed

### If subscription rates change (RuleEngine/ServiceCatalog):
- Only affects NEXT year's generation (existing rows are immutable)
- SubscriptionDataMigration can retroactively fix amounts

### If an installment plan defaults:
- Member dropped immediately
- All pending schedule items marked overdue
- Carnet blocked, subscription generation skipped

---

## National ID Deduplication (Cross-Table Constraint)

National ID uniqueness is enforced ACROSS tables:
- `members.national_id` (unique index)
- `spouses.national_id` (checked against members + children)
- `children.national_id` (checked against members + spouses)
- `temporary_members.national_id` (checked against all above)

This is application-level validation, NOT database-level cross-table constraints.

---

## Permission Dependencies

### Shared Permission Patterns
- `member.*` → Members module
- `subscription.*` → Subscriptions module
- `installment.*` → Installments module
- `fine.*` → Fines module
- `transfer.*` / `separation.*` → Transfers module
- `waiver.*` → Waiver module
- `child.*` → Children module
- `spouse.*` → Spouses module
- `temp.*` → Temporary module
- `carnet.*` → Carnets module
- `payment.*` → Payments module

Super admin check: `employee_roles JOIN roles WHERE role_code = 'super_admin'`

---

## HR Module Dependencies

### HR Module
| Depends On | How |
|------------|-----|
| `employees` table (Auth) | `employee_id` FK — links HR profile to system login |
| Workflow | Approval chains: `hr_leave_approval`, `hr_loan_approval`, `hr_contract_approval` |
| Members (NationalIdParser) | Parses 14-digit national ID for DOB + gender during employee creation |
| `system_config` | 40+ HR config keys for all calculations |

### Modules That Depend On HR
| Module | How |
|--------|-----|
| Accounting (bootstrap) | Listens to `hr.payroll.paid` → auto-posts salary journal entry |
| Accounting (GLSyncService) | Reads `hr_payroll_runs` WHERE status='paid' for batch GL sync |
| Pricing (RegulatoryDiscounts) | Reads `hr_employee_profiles` to verify club employee status for Art.102 discount |

### HR Events → Listeners
| Event | Dispatched By | Listened By |
|-------|--------------|-------------|
| `hr.leave.submitted` | LeaveService | — |
| `hr.leave.approved` | LeaveService | HR bootstrap → SMS dispatch |
| `hr.leave.rejected` | LeaveService | HR bootstrap → SMS dispatch |
| `hr.payroll.paid` | PayrollController | HR bootstrap → SMS; Accounting → journal |
| `hr.loan.approved` | LoanService | HR bootstrap → SMS dispatch |
| `hr.contract.renewed` | ContractService | — |
| `hr.contract.terminated` | ContractService | — |
| `hr.attendance.recorded` | AttendanceController | HR bootstrap → AttendanceViolationService |
| `attendance.violations_detected` | AttendanceViolationService | — |

### HR Database Dependencies (Shared Read)
| Table | Read By |
|-------|---------|
| `employees` | HR (employee_id link) |
| `system_config` (hr.* keys) | All HR services (insurance, tax, leave, overtime, discipline, EOS) |
| `hr_employee_profiles` | Pricing/RegulatoryDiscounts (employee verification) |
| `hr_payroll_runs` | Accounting/GLSyncService (salary journal sync) |

### If `hr_employee_profiles.basic_salary` changes:
- Payroll: next period calculates new gross/net
- Insurance: new contribution amounts
- Tax: new bracket placement
- Loans: installment vs salary ratio affected
- End of Service: settlement based on last salary

### If `hr_employee_profiles.employment_status` changes to terminated/resigned:
- Active contract should be terminated
- Leave balances frozen
- Outstanding loans may be deducted from EOS settlement
- Biometric punches ignored
- Payroll excludes from next period

### If insurance config (rates/caps) changes:
- Applies to next payroll calculation
- Previous periods' records are immutable
- Form 1/2/6 reports reflect current config

### HR Permission Keys
- `hr.department.*` — Department management
- `hr.job_title.*` — Job titles
- `hr.employee.*` — Employee profiles & salary
- `hr.contract.*` — Contracts
- `hr.attendance.*` — Attendance
- `hr.leave.*` — Leaves
- `hr.payroll.*` — Payroll
- `hr.insurance.*` — Insurance
- `hr.tax.*` — Tax
- `hr.disciplinary.*` — Disciplinary
- `hr.loan.*` — Loans
- `hr.eos.*` — End of Service
- `hr.performance.*` — Performance
- `hr.document.*` — Documents
- `hr.holiday.*` — Holidays
- `hr.schedule.*` — Work schedules
- `hr.overtime.*` — Overtime
- `hr.shifts.*` — Shifts
- `hr.permissions.*` — Permission requests (hourly leaves)
- `hr.biometric.*` — Biometric devices
- `hr.report.*` — Reports
- `hr.payslip.view_own` — Self-service payslip
