Files
usher-manage-stack/docs/SYSTEM_SPECIFICATION.md
2025-11-20 23:21:05 +08:00

1123 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Taiwan NPO Membership Management System
## Complete System Specification
**Version:** 1.0
**Last Updated:** 2025-11-20
**Technology Stack:** Laravel 11, PHP 8.x, MySQL, Spatie Permission
---
## Table of Contents
1. [Executive Summary](#1-executive-summary)
2. [System Architecture](#2-system-architecture)
3. [Database Schema](#3-database-schema)
4. [Core Features](#4-core-features)
5. [Workflows](#5-workflows)
6. [Security & Authorization](#6-security--authorization)
7. [Email Notifications](#7-email-notifications)
8. [File Structure](#8-file-structure)
9. [Configuration](#9-configuration)
---
## 1. Executive Summary
This system is a comprehensive membership management platform designed specifically for Taiwan NPOs (Non-Profit Organizations). It implements a complete lifecycle for member registration, payment verification, financial management, issue tracking, and budget management.
### Key Capabilities
- **Member Lifecycle Management:** Registration → Payment → 3-Tier Verification → Activation
- **Financial Management:** Budget planning, transaction tracking, finance document approval
- **Issue Tracking:** Complete work item management with time logging and collaboration
- **Multi-Tier Approval Workflows:** 3-tier verification for payments and finance documents
- **Audit Logging:** Complete audit trail for compliance and accountability
- **Role-Based Access Control:** Granular permissions using Spatie Permission package
### User Roles
1. **Public Users:** Can self-register as members
2. **Members:** Can submit payments, view membership status, request issues
3. **Cashier:** First-tier verification for payments and documents
4. **Accountant:** Second-tier verification
5. **Chair:** Third-tier final approval
6. **Membership Manager:** Activates memberships after approval
7. **Staff:** General administrative access
8. **Admin:** Full system access
---
## 2. System Architecture
### 2.1 Technology Stack
**Backend:**
- **Framework:** Laravel 11
- **PHP Version:** 8.x
- **Database:** MySQL 8.0+
- **Authentication:** Laravel Breeze
- **Authorization:** Spatie Laravel Permission
- **Queue System:** Database/Redis queue driver
**Frontend:**
- **Template Engine:** Blade
- **CSS Framework:** Tailwind CSS
- **JavaScript:** Alpine.js (via Breeze)
- **Dark Mode:** Supported
**Infrastructure:**
- **File Storage:** Laravel Storage (private disk)
- **Email:** Laravel Mail with queue support
- **Encryption:** AES-256 for sensitive data
### 2.2 Application Layers
```
┌─────────────────────────────────────────┐
│ Web Interface (Blade) │
│ - Public Registration │
│ - Member Dashboard │
│ - Admin Panel │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ HTTP Layer (Controllers) │
│ - Request Validation │
│ - Business Logic Coordination │
│ - Response Formatting │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Application Layer (Models) │
│ - Business Logic │
│ - Eloquent Relationships │
│ - Accessors & Mutators │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Data Layer (Database) │
│ - MySQL Database │
│ - Migrations & Schema │
│ - Indexes & Constraints │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ Support Services (Cross-cutting) │
│ - AuditLogger │
│ - Email Notifications │
│ - File Storage │
│ - Encryption Services │
└─────────────────────────────────────────┘
```
---
## 3. Database Schema
### 3.1 Core Tables
#### **users**
Primary authentication table.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| name | varchar(255) | NOT NULL | User's full name |
| email | varchar(255) | NOT NULL, UNIQUE | Email address |
| email_verified_at | timestamp | NULL | Email verification time |
| password | varchar(255) | NOT NULL | Bcrypt hashed password |
| is_admin | boolean | DEFAULT false | Legacy admin flag |
| profile_photo_path | varchar(2048) | NULL | Profile photo path |
| remember_token | varchar(100) | NULL | Remember me token |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Relationships:**
- HasOne: Member
- BelongsToMany: Role (via model_has_roles)
- BelongsToMany: Permission (via model_has_permissions)
---
#### **members**
Member profile information.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| user_id | bigint unsigned | FK(users), NULL, UNIQUE | Associated user account |
| full_name | varchar(255) | NOT NULL, INDEXED | Full name |
| email | varchar(255) | NOT NULL, INDEXED | Email (indexed for search) |
| phone | varchar(20) | NULL | Phone number |
| national_id_encrypted | text | NULL | AES-256 encrypted national ID |
| national_id_hash | varchar(64) | NULL, INDEXED | SHA256 hash for search |
| address_line_1 | varchar(255) | NULL | Address line 1 |
| address_line_2 | varchar(255) | NULL | Address line 2 |
| city | varchar(100) | NULL | City |
| postal_code | varchar(10) | NULL | Postal code |
| emergency_contact_name | varchar(255) | NULL | Emergency contact name |
| emergency_contact_phone | varchar(20) | NULL | Emergency contact phone |
| membership_started_at | date | NULL | Membership start date |
| membership_expires_at | date | NULL | Membership expiry date |
| membership_status | enum | DEFAULT 'pending' | Status: pending, active, expired, suspended |
| membership_type | enum | DEFAULT 'regular' | Type: regular, honorary, lifetime, student |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Relationships:**
- BelongsTo: User
- HasMany: MembershipPayment
- HasMany: FinanceDocument
- HasMany: Issue (for member requests)
**Key Methods:**
- `hasPaidMembership()` - Returns true if active with future expiry
- `canSubmitPayment()` - Returns true if pending and no pending payment
- `getPendingPayment()` - Gets payment awaiting verification
- `getMembershipStatusBadgeAttribute()` - CSS badge class
- `getMembershipStatusLabelAttribute()` - Chinese label
- `getMembershipTypeLabelAttribute()` - Chinese type label
---
#### **membership_payments**
Payment records with 3-tier verification workflow.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| member_id | bigint unsigned | FK(members), NOT NULL | Member who paid |
| paid_at | date | NOT NULL | Payment date |
| amount | decimal(10,2) | NOT NULL | Payment amount (TWD) |
| method | varchar(255) | NULL | Legacy payment method |
| reference | varchar(255) | NULL | Legacy reference |
| status | enum | DEFAULT 'pending' | Workflow status |
| payment_method | enum | NULL | Method: bank_transfer, convenience_store, cash, credit_card |
| receipt_path | varchar(255) | NULL | Receipt file path (private storage) |
| submitted_by_user_id | bigint unsigned | FK(users), NULL | User who submitted |
| verified_by_cashier_id | bigint unsigned | FK(users), NULL | Tier 1 verifier |
| cashier_verified_at | timestamp | NULL | Tier 1 timestamp |
| verified_by_accountant_id | bigint unsigned | FK(users), NULL | Tier 2 verifier |
| accountant_verified_at | timestamp | NULL | Tier 2 timestamp |
| verified_by_chair_id | bigint unsigned | FK(users), NULL | Tier 3 verifier |
| chair_verified_at | timestamp | NULL | Tier 3 timestamp |
| rejected_by_user_id | bigint unsigned | FK(users), NULL | Rejector |
| rejected_at | timestamp | NULL | Rejection timestamp |
| rejection_reason | text | NULL | Reason for rejection |
| notes | text | NULL | Admin notes |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Workflow States:**
1. `pending` - Awaiting Tier 1 verification
2. `approved_cashier` - Tier 1 approved, awaiting Tier 2
3. `approved_accountant` - Tier 2 approved, awaiting Tier 3
4. `approved_chair` - Fully approved (triggers activation)
5. `rejected` - Rejected at any tier
**Key Methods:**
- `canBeApprovedByCashier()` - Validates Tier 1 eligibility
- `canBeApprovedByAccountant()` - Validates Tier 2 eligibility
- `canBeApprovedByChair()` - Validates Tier 3 eligibility
- `getStatusLabelAttribute()` - Chinese status label
- `getPaymentMethodLabelAttribute()` - Chinese method label
---
#### **finance_documents**
Finance document approval workflow (3-tier).
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| member_id | bigint unsigned | FK(members), NULL | Related member |
| submitted_by_user_id | bigint unsigned | FK(users), NOT NULL | Submitter |
| title | varchar(255) | NOT NULL | Document title |
| amount | decimal(10,2) | NULL | Amount (if applicable) |
| status | varchar(255) | DEFAULT 'pending' | Workflow status |
| description | text | NULL | Description |
| attachment_path | varchar(255) | NULL | Attachment file path |
| approved_by_cashier_id | bigint unsigned | FK(users), NULL | Tier 1 approver |
| cashier_approved_at | timestamp | NULL | Tier 1 timestamp |
| approved_by_accountant_id | bigint unsigned | FK(users), NULL | Tier 2 approver |
| accountant_approved_at | timestamp | NULL | Tier 2 timestamp |
| approved_by_chair_id | bigint unsigned | FK(users), NULL | Tier 3 approver |
| chair_approved_at | timestamp | NULL | Tier 3 timestamp |
| rejected_by_user_id | bigint unsigned | FK(users), NULL | Rejector |
| rejected_at | timestamp | NULL | Rejection timestamp |
| rejection_reason | text | NULL | Reason for rejection |
| submitted_at | timestamp | NULL | Submission timestamp |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Same 3-tier workflow as membership_payments**
---
#### **issues**
Issue tracking system with comprehensive features.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| issue_number | varchar(50) | NOT NULL, UNIQUE | Auto: ISS-2025-001 |
| title | varchar(255) | NOT NULL | Issue title |
| description | text | NULL | Issue description |
| issue_type | enum | NOT NULL | Type: work_item, project_task, maintenance, member_request |
| status | enum | DEFAULT 'new', INDEXED | Status: new, assigned, in_progress, review, closed |
| priority | enum | DEFAULT 'medium', INDEXED | Priority: low, medium, high, urgent |
| created_by_user_id | bigint unsigned | FK(users), INDEXED | Creator |
| assigned_to_user_id | bigint unsigned | FK(users), NULL, INDEXED | Assignee |
| reviewer_id | bigint unsigned | FK(users), NULL | Reviewer |
| member_id | bigint unsigned | FK(members), NULL | Related member |
| parent_issue_id | bigint unsigned | FK(issues), NULL | Parent issue (for sub-tasks) |
| due_date | date | NULL, INDEXED | Due date |
| closed_at | timestamp | NULL | Closure timestamp |
| estimated_hours | decimal(8,2) | NULL | Estimated hours |
| actual_hours | decimal(8,2) | DEFAULT 0 | Actual hours (from time logs) |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
| deleted_at | timestamp | NULL | Soft delete timestamp |
**Relationships:**
- HasMany: IssueComment, IssueAttachment, IssueTimeLog
- BelongsToMany: IssueLabel, User (watchers)
- BelongsTo: User (creator, assignee, reviewer)
- BelongsTo: Issue (parent)
**Key Methods:**
- Status checks: `isNew()`, `isAssigned()`, `isInProgress()`, `inReview()`, `isClosed()`
- Workflow validation: `canBeAssigned()`, `canMoveToInProgress()`, `canMoveToReview()`, `canBeClosed()`
- Calculations: `getProgressPercentageAttribute()`, `getIsOverdueAttribute()`, `getTotalTimeLoggedAttribute()`
---
#### **budgets**
Budget management with lifecycle workflow.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| fiscal_year | integer | NOT NULL, INDEXED | Fiscal year (2000-2100) |
| name | varchar(255) | NOT NULL | Budget name |
| period_type | enum | NOT NULL | Type: annual, quarterly, monthly |
| period_start | date | NOT NULL | Period start date |
| period_end | date | NOT NULL | Period end date |
| status | enum | DEFAULT 'draft', INDEXED | Status: draft, submitted, approved, active, closed |
| created_by_user_id | bigint unsigned | FK(users), NOT NULL | Creator |
| approved_by_user_id | bigint unsigned | FK(users), NULL | Approver |
| approved_at | timestamp | NULL | Approval timestamp |
| notes | text | NULL | Notes |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Workflow:** draft → submitted → approved → active → closed
**Key Methods:**
- Status checks: `isDraft()`, `isApproved()`, `isActive()`, `isClosed()`
- Validation: `canBeEdited()`, `canBeApproved()`
- Calculations: `getTotalBudgetedIncomeAttribute()`, `getTotalActualExpenseAttribute()`, etc.
---
#### **budget_items**
Line items within budgets.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| budget_id | bigint unsigned | FK(budgets), NOT NULL, INDEXED | Parent budget |
| chart_of_account_id | bigint unsigned | FK(chart_of_accounts), NOT NULL | Account code |
| budgeted_amount | decimal(15,2) | NOT NULL | Planned amount |
| actual_amount | decimal(15,2) | NOT NULL, DEFAULT 0 | Actual amount spent |
| notes | text | NULL | Notes |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Composite Index:** (budget_id, chart_of_account_id)
**Key Methods:**
- `getVarianceAttribute()` - actual - budgeted
- `getVariancePercentageAttribute()` - (variance / budgeted) × 100
- `getRemainingBudgetAttribute()` - budgeted - actual
- `isOverBudget()` - Returns true if actual > budgeted
---
#### **transactions**
Financial transaction records.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| budget_item_id | bigint unsigned | FK(budget_items), NULL | Linked budget item |
| chart_of_account_id | bigint unsigned | FK(chart_of_accounts), NOT NULL | Account code |
| transaction_date | date | NOT NULL, INDEXED | Transaction date |
| amount | decimal(15,2) | NOT NULL | Amount |
| transaction_type | enum | NOT NULL, INDEXED | Type: income, expense |
| description | varchar(255) | NOT NULL | Description |
| reference_number | varchar(255) | NULL | Reference number |
| finance_document_id | bigint unsigned | FK(finance_documents), NULL | Linked document |
| membership_payment_id | bigint unsigned | FK(membership_payments), NULL | Linked payment |
| created_by_user_id | bigint unsigned | FK(users), NOT NULL | Creator |
| notes | text | NULL | Notes |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Indexes:** transaction_date, transaction_type, (budget_item_id, transaction_date)
---
#### **chart_of_accounts**
Hierarchical chart of accounts for financial tracking.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| account_code | varchar(50) | NOT NULL, UNIQUE | Account code (e.g., 4000) |
| account_name_zh | varchar(255) | NOT NULL | Chinese name |
| account_name_en | varchar(255) | NOT NULL | English name |
| account_type | enum | NOT NULL | Type: income, expense, asset, liability, net_asset |
| category | varchar(100) | NULL | Category grouping |
| parent_account_id | bigint unsigned | FK(chart_of_accounts), NULL | Parent account (hierarchy) |
| is_active | boolean | DEFAULT true | Active flag |
| display_order | integer | DEFAULT 0 | Sort order |
| description | text | NULL | Description |
| created_at | timestamp | NULL | Creation timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Hierarchical Structure:** Supports parent-child relationships for account grouping
---
#### **audit_logs**
Complete audit trail for compliance.
| Column | Type | Constraints | Description |
|--------|------|-------------|-------------|
| id | bigint unsigned | PK, AUTO_INCREMENT | Primary key |
| user_id | bigint unsigned | FK(users), NULL | User who performed action |
| action | varchar(255) | NOT NULL, INDEXED | Action name (e.g., member.created) |
| auditable_type | varchar(255) | NULL | Model class name |
| auditable_id | bigint unsigned | NULL | Model ID |
| metadata | json | NULL | Additional context |
| created_at | timestamp | NULL | Action timestamp |
| updated_at | timestamp | NULL | Last update timestamp |
**Common Actions:**
- member.self_registered, member.created, member.updated, member.activated
- payment.submitted, payment.approved_by_*, payment.rejected
- finance_document.*, issue.*, budget.*, transaction.*
---
### 3.2 Supporting Tables
#### **roles** (Spatie Permission)
- id, name, guard_name, description, timestamps
#### **permissions** (Spatie Permission)
- id, name, guard_name, timestamps
#### **model_has_roles** (Spatie Permission)
- role_id, model_type, model_id
#### **model_has_permissions** (Spatie Permission)
- permission_id, model_type, model_id
#### **role_has_permissions** (Spatie Permission)
- permission_id, role_id
#### **issue_comments**
- id, issue_id, user_id, comment_text, is_internal, timestamps
#### **issue_attachments**
- id, issue_id, user_id, file_path, file_name, file_size, mime_type, timestamps
#### **issue_labels**
- id, name (unique), color, description, timestamps
#### **issue_label_pivot**
- issue_id, issue_label_id
#### **issue_time_logs**
- id, issue_id, user_id, hours, work_date, description, timestamps
#### **issue_watchers**
- id, issue_id, user_id
#### **issue_relationships**
- id, issue_id, related_issue_id, relationship_type, timestamps
#### **custom_fields**
- id, name, field_type, options (JSON), is_required, timestamps
#### **custom_field_values**
- id, custom_field_id, customizable_type, customizable_id, value (JSON), timestamps
#### **financial_reports**
- id, budget_id, report_type, report_data (JSON), generated_by_user_id, generated_at, timestamps
---
## 4. Core Features
### 4.1 Member Registration & Lifecycle
**Public Self-Registration:**
- Route: `GET/POST /register/member`
- Controller: PublicMemberRegistrationController
- Creates User + Member records
- Sets initial status to 'pending'
- Sends welcome email with payment instructions
- Auto-login after registration
**Admin-Created Members:**
- Route: `POST /admin/members`
- Controller: AdminMemberController
- Can create member with or without user account
- Sets initial status to 'pending'
**Member States:**
1. **Pending:** Registered but not yet paid/verified
2. **Active:** Payment approved and membership activated
3. **Expired:** Membership expiry date has passed
4. **Suspended:** Admin-suspended
**Key Features:**
- National ID encryption (AES-256)
- National ID hash (SHA256) for searching without decryption
- Emergency contact information
- Address management
- Membership type management (regular, student, honorary, lifetime)
---
### 4.2 Payment Verification Workflow (3-Tier)
**Member Payment Submission:**
- Route: `POST /member/payments`
- Controller: MemberPaymentController
- Upload receipt (JPG, PNG, PDF, max 10MB)
- Specify payment method, amount, date, reference
- Creates payment with status='pending'
- Stored in private storage
- Emails sent to member (confirmation) and cashiers (notification)
**Tier 1: Cashier Verification**
- Route: `POST /admin/payment-verifications/{payment}/approve-cashier`
- Permission: `verify_payments_cashier`
- Verifies receipt legitimacy
- Updates: status=approved_cashier, cashier_verified_at, verified_by_cashier_id
- Sends email to member and accountants
**Tier 2: Accountant Verification**
- Route: `POST /admin/payment-verifications/{payment}/approve-accountant`
- Permission: `verify_payments_accountant`
- Reviews financial details
- Updates: status=approved_accountant, accountant_verified_at, verified_by_accountant_id
- Sends email to member and chairs
**Tier 3: Chair Approval**
- Route: `POST /admin/payment-verifications/{payment}/approve-chair`
- Permission: `verify_payments_chair`
- Final approval
- Updates: status=approved_chair, chair_verified_at, verified_by_chair_id
- **Automatically activates membership:**
- member.membership_status = 'active'
- member.membership_started_at = today
- member.membership_expires_at = today + 1 year (or lifetime)
- Sends activation email to member
**Rejection:**
- Route: `POST /admin/payment-verifications/{payment}/reject`
- Can be done at any tier
- Requires rejection reason
- Updates: status=rejected, rejected_by_user_id, rejected_at, rejection_reason
- Sends rejection email with reason
- Member can resubmit
**Dashboard:**
- Route: `GET /admin/payment-verifications`
- Tabbed interface: All, Cashier Queue, Accountant Queue, Chair Queue, Approved, Rejected
- Shows counts for each queue
- Search by member name, email, reference
- Permission-based filtering
---
### 4.3 Finance Document Approval
**Document Submission:**
- Route: `POST /admin/finance-documents`
- Controller: FinanceDocumentController
- Title, optional amount, optional attachment
- Status starts as 'pending'
**3-Tier Approval:**
Same workflow structure as payment verification:
1. Cashier approval (Tier 1)
2. Accountant approval (Tier 2)
3. Chair approval (Tier 3)
**Features:**
- File attachment support
- Amount tracking
- Rejection with reason
- Email notifications at each stage
---
### 4.4 Issue Tracking System
**Issue Creation:**
- Route: `POST /admin/issues`
- Controller: IssueController
- Auto-generates issue number: ISS-{YYYY}-{incrementing}
- Required: title, type, priority
- Optional: description, assignee, labels, due date, estimated hours
**Issue Types:**
- work_item: General work tasks
- project_task: Project-related tasks
- maintenance: System maintenance
- member_request: Member support requests
**Status Workflow:**
```
new → assigned → in_progress → review → closed
```
**Can reopen:** closed → assigned
**Priority Levels:**
- low (default background color: gray)
- medium (default background color: blue)
- high (default background color: orange)
- urgent (default background color: red)
**Collaboration Features:**
1. **Comments:**
- Add comments to issues
- is_internal flag hides comments from members
- Notifies watchers
2. **Attachments:**
- Upload files to issues
- Download attachments
- Delete attachments
3. **Time Logging:**
- Log hours worked on issues
- Specify work date
- Automatic summation
4. **Watchers:**
- Add users to watch issue updates
- Receive email notifications
5. **Labels:**
- Color-coded labels
- Filterable
- Multiple labels per issue
6. **Sub-tasks:**
- Create child issues via parent_issue_id
- Hierarchical structure
7. **Issue Relationships:**
- blocks, is_blocked_by
- relates_to
- duplicates, is_duplicated_by
**Automation:**
- Auto-calculation of progress percentage (0-100% based on status)
- Overdue detection (due_date < today and not closed)
- Days until due calculation
**Reports:**
- Route: `GET /admin/issue-reports`
- Status distribution
- Priority distribution
- Workload analysis
---
### 4.5 Budget Management
**Budget Creation:**
- Route: `POST /admin/budgets`
- Controller: BudgetController
- Fiscal year, period type, date range
- Status starts as 'draft'
**Budget Items:**
- Link to chart of accounts
- Set budgeted amounts
- Track actual amounts (updated via transactions)
- Calculate variances
**Workflow:**
```
draft → submitted → approved → active → closed
```
**Features:**
- Total budgeted income/expense calculation
- Total actual income/expense calculation
- Variance analysis (budgeted vs actual)
- Utilization percentage
- Over-budget detection
**Reporting:**
- Generate financial reports
- Store report snapshots (JSON)
- Historical tracking
---
### 4.6 Transaction Management
**Transaction Recording:**
- Route: `POST /admin/transactions`
- Controller: TransactionController
- Type: income or expense
- Link to budget item (optional)
- Link to chart of account (required)
- Link to finance document or membership payment (optional)
- Date, amount, description
**Features:**
- Search by date range, type, description
- Automatic budget item actual amount update
- Reference number tracking
- Notes support
---
## 5. Workflows
### 5.1 Complete Member Journey
```
┌─────────────────────────────────────────────────────────────┐
│ STEP 1: REGISTRATION │
│ User fills public registration form │
│ → Creates User account (with password) │
│ → Creates Member record (status='pending') │
│ → Sends welcome email │
│ → Auto-login │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STEP 2: PAYMENT SUBMISSION │
│ Member uploads receipt + payment details │
│ → Payment created (status='pending') │
│ → Receipt stored in private storage │
│ → Email to member (confirmation) │
│ → Email to cashiers (notification) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ STEP 3: TIER 1 VERIFICATION (Cashier) │
│ Cashier reviews receipt and basic info │
│ → Approves or rejects │
│ IF APPROVED: │
│ → status='approved_cashier' │
│ → Email to member + accountants │
│ IF REJECTED: │
│ → status='rejected' │
│ → Email to member with reason │
│ → Member must resubmit │
└─────────────────────────────────────────────────────────────┘
↓ (if approved)
┌─────────────────────────────────────────────────────────────┐
│ STEP 4: TIER 2 VERIFICATION (Accountant) │
│ Accountant verifies financial details │
│ → Approves or rejects │
│ IF APPROVED: │
│ → status='approved_accountant' │
│ → Email to member + chairs │
│ IF REJECTED: (same as Tier 1) │
└─────────────────────────────────────────────────────────────┘
↓ (if approved)
┌─────────────────────────────────────────────────────────────┐
│ STEP 5: TIER 3 APPROVAL (Chair) │
│ Chair gives final approval │
│ → Approves or rejects │
│ IF APPROVED: │
│ → status='approved_chair' │
│ → AUTOMATIC ACTIVATION: │
│ ◦ member.membership_status = 'active' │
│ ◦ member.membership_started_at = today │
│ ◦ member.membership_expires_at = today + 1 year │
│ → Email to member (activation confirmation) │
│ → Email to membership managers (FYI) │
│ IF REJECTED: (same as Tier 1 & 2) │
└─────────────────────────────────────────────────────────────┘
↓ (if approved)
┌─────────────────────────────────────────────────────────────┐
│ STEP 6: ACTIVE MEMBERSHIP │
│ Member now has: │
│ → Access to member-only resources │
│ → Active membership badge │
│ → Membership expiry date │
│ → Full dashboard access │
└─────────────────────────────────────────────────────────────┘
```
### 5.2 Issue Lifecycle
```
┌──────────┐
│ NEW │ ← Issue created
└────┬─────┘
│ assign user
┌──────────┐
│ ASSIGNED │ ← User assigned
└────┬─────┘
│ start work
┌──────────────┐
│ IN_PROGRESS │ ← Work started
└────┬─────────┘
│ ready for review
┌──────────┐
│ REVIEW │ ← Reviewing
└────┬─────┘
│ approve
┌──────────┐
│ CLOSED │ ← Done
└──────────┘
│ can reopen
└──────────
```
### 5.3 Budget Lifecycle
```
┌────────┐
│ DRAFT │ ← Create budget, add items
└───┬────┘
│ submit for approval
┌───────────┐
│ SUBMITTED │ ← Pending approval
└─────┬─────┘
│ approve
┌───────────┐
│ APPROVED │ ← Approved but not yet active
└─────┬─────┘
│ activate
┌────────┐
│ ACTIVE │ ← Currently in use, transactions linked
└───┬────┘
│ period ends
┌────────┐
│ CLOSED │ ← Period ended, archived
└────────┘
```
---
## 6. Security & Authorization
### 6.1 Authentication
**Method:** Session-based authentication via Laravel Breeze
**Features:**
- Password hashing (Bcrypt)
- Email verification (optional)
- Remember me functionality
- Password reset via email
### 6.2 Authorization (Spatie Permission)
**Roles:**
1. **admin** - Full system access
2. **staff** - Internal tools access
3. **cashier** - Payment Tier 1 verification
4. **accountant** - Payment Tier 2 verification
5. **chair** - Payment Tier 3 approval
6. **payment_cashier** - Dedicated cashier role
7. **payment_accountant** - Dedicated accountant role
8. **payment_chair** - Dedicated chair role
9. **membership_manager** - Membership activation
**Permissions:**
- `verify_payments_cashier` - Tier 1 approval
- `verify_payments_accountant` - Tier 2 approval
- `verify_payments_chair` - Tier 3 approval
- `activate_memberships` - Activate memberships
- `view_payment_verifications` - View dashboard
**Middleware:**
- `EnsureUserIsAdmin` - Protects `/admin` routes
- `CheckPaidMembership` - Verifies active paid membership
### 6.3 Data Security
**National ID Protection:**
- Stored encrypted (AES-256)
- Hashed with SHA256 for searching
- Never displayed in plain text
**Password Security:**
- Bcrypt hashing
- Minimum password requirements
- Password confirmation on sensitive actions
**CSRF Protection:**
- All POST/PATCH/DELETE requests protected
- Automatic token generation
**File Security:**
- Payment receipts stored in private disk
- Served only via authenticated controller methods
- File type validation on upload
**SQL Injection Prevention:**
- Eloquent ORM with parameter binding
- Never use raw queries without bindings
---
## 7. Email Notifications
### 7.1 Membership Emails
| Email | Trigger | Recipients |
|-------|---------|------------|
| MemberRegistrationWelcomeMail | After self-registration | New member |
| PaymentSubmittedMail | Payment submitted | Member + Cashiers |
| PaymentApprovedByCashierMail | Tier 1 approval | Member + Accountants |
| PaymentApprovedByAccountantMail | Tier 2 approval | Member + Chairs |
| PaymentFullyApprovedMail | Tier 3 approval | Member + Membership Managers |
| PaymentRejectedMail | Payment rejected | Member |
| MembershipActivatedMail | Membership activated | Member |
| MembershipExpiryReminderMail | X days before expiry | Member |
### 7.2 Finance Emails
| Email | Trigger | Recipients |
|-------|---------|------------|
| FinanceDocumentSubmitted | Document submitted | Cashiers |
| FinanceDocumentApprovedByCashier | Tier 1 approval | Submitter + Accountants |
| FinanceDocumentApprovedByAccountant | Tier 2 approval | Submitter + Chairs |
| FinanceDocumentFullyApproved | Tier 3 approval | Submitter |
| FinanceDocumentRejected | Document rejected | Submitter |
### 7.3 Issue Emails
| Email | Trigger | Recipients |
|-------|---------|------------|
| IssueAssignedMail | Issue assigned | Assignee |
| IssueStatusChangedMail | Status changed | Creator + Assignee + Watchers |
| IssueCommentedMail | New comment | Creator + Assignee + Watchers |
| IssueDueSoonMail | Due within X days | Assignee |
| IssueOverdueMail | Past due date | Assignee |
| IssueClosedMail | Issue closed | Creator + Watchers |
### 7.4 Queue Configuration
All emails implement `ShouldQueue` for async delivery:
- Queue driver: database/redis
- Failed jobs table for retry
- Queue workers handle delivery
---
## 8. File Structure
```
usher-manage-stack/
├── app/
│ ├── Http/
│ │ ├── Controllers/
│ │ │ ├── AdminMemberController.php
│ │ │ ├── AdminPaymentController.php
│ │ │ ├── PaymentVerificationController.php
│ │ │ ├── PublicMemberRegistrationController.php
│ │ │ ├── MemberPaymentController.php
│ │ │ ├── MemberDashboardController.php
│ │ │ ├── FinanceDocumentController.php
│ │ │ ├── IssueController.php
│ │ │ ├── IssueLabelController.php
│ │ │ ├── IssueReportsController.php
│ │ │ ├── BudgetController.php
│ │ │ ├── TransactionController.php
│ │ │ ├── AdminRoleController.php
│ │ │ ├── AdminAuditLogController.php
│ │ │ └── AdminDashboardController.php
│ │ └── Middleware/
│ │ ├── EnsureUserIsAdmin.php
│ │ └── CheckPaidMembership.php
│ ├── Mail/
│ │ ├── MemberRegistrationWelcomeMail.php
│ │ ├── PaymentSubmittedMail.php
│ │ ├── PaymentApprovedByCashierMail.php
│ │ ├── PaymentApprovedByAccountantMail.php
│ │ ├── PaymentFullyApprovedMail.php
│ │ ├── PaymentRejectedMail.php
│ │ ├── MembershipActivatedMail.php
│ │ ├── FinanceDocument*.php (5 files)
│ │ └── Issue*.php (6 files)
│ ├── Models/
│ │ ├── Member.php
│ │ ├── MembershipPayment.php
│ │ ├── User.php
│ │ ├── Role.php
│ │ ├── Permission.php
│ │ ├── Issue.php
│ │ ├── IssueComment.php
│ │ ├── IssueAttachment.php
│ │ ├── IssueLabel.php
│ │ ├── IssueTimeLog.php
│ │ ├── Budget.php
│ │ ├── BudgetItem.php
│ │ ├── Transaction.php
│ │ ├── ChartOfAccount.php
│ │ ├── FinanceDocument.php
│ │ └── AuditLog.php
│ └── Support/
│ └── AuditLogger.php
├── database/
│ ├── migrations/
│ │ ├── 2025_11_18_092000_create_audit_logs_table.php
│ │ ├── 2025_11_18_093000_create_finance_documents_table.php
│ │ ├── 2025_11_19_133732_create_budgets_table.php
│ │ ├── 2025_11_19_133802_create_transactions_table.php
│ │ ├── 2025_11_19_144027_create_issues_table.php
│ │ ├── 2025_11_19_155725_enhance_membership_payments_table_for_verification.php
│ │ └── 2025_11_19_155807_add_membership_status_to_members_table.php
│ └── seeders/
│ ├── RoleSeeder.php
│ ├── PaymentVerificationRolesSeeder.php
│ ├── ChartOfAccountSeeder.php
│ └── IssueLabelSeeder.php
├── resources/
│ └── views/
│ ├── admin/
│ │ ├── members/
│ │ │ ├── index.blade.php
│ │ │ ├── show.blade.php
│ │ │ ├── create.blade.php
│ │ │ ├── edit.blade.php
│ │ │ └── activate.blade.php
│ │ ├── payment-verifications/
│ │ │ ├── index.blade.php
│ │ │ └── show.blade.php
│ │ ├── issues/
│ │ ├── budgets/
│ │ └── finance-documents/
│ ├── member/
│ │ ├── dashboard.blade.php
│ │ └── submit-payment.blade.php
│ ├── register/
│ │ └── member.blade.php
│ └── emails/
│ ├── members/
│ ├── payments/
│ ├── finance-documents/
│ └── issues/
└── routes/
└── web.php
```
---
## 9. Configuration
### 9.1 Environment Variables
```env
APP_NAME="Taiwan NPO Membership System"
APP_ENV=production
APP_KEY=base64:...
APP_DEBUG=false
APP_URL=https://your-domain.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=usher_manage
DB_USERNAME=root
DB_PASSWORD=
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@your-domain.com
MAIL_FROM_NAME="${APP_NAME}"
QUEUE_CONNECTION=database
FILESYSTEM_DISK=local
```
### 9.2 Permissions Required
**Cashier:**
- verify_payments_cashier
- view_payment_verifications
**Accountant:**
- verify_payments_accountant
- view_payment_verifications
**Chair:**
- verify_payments_chair
- view_payment_verifications
**Membership Manager:**
- activate_memberships
- view_payment_verifications
**Admin:**
- All permissions (automatic grant)
---
## 10. Deployment Checklist
- [ ] Run migrations: `php artisan migrate`
- [ ] Seed roles & permissions: `php artisan db:seed --class=RoleSeeder`
- [ ] Seed payment roles: `php artisan db:seed --class=PaymentVerificationRolesSeeder`
- [ ] Seed chart of accounts: `php artisan db:seed --class=ChartOfAccountSeeder`
- [ ] Seed issue labels: `php artisan db:seed --class=IssueLabelSeeder`
- [ ] Configure mail settings
- [ ] Configure queue worker
- [ ] Set up file storage (private disk)
- [ ] Create admin user
- [ ] Assign admin role
- [ ] Test payment workflow end-to-end
- [ ] Test email delivery
- [ ] Set up SSL certificate
- [ ] Configure backup strategy
---
## Appendix A: Glossary
**Member:** A registered person in the NPO system
**Payment Verification:** 3-tier approval process for membership payments
**Tier 1/2/3:** Sequential approval levels (Cashier/Accountant/Chair)
**Issue:** Work item, task, or support request in the tracking system
**Budget:** Financial plan for a fiscal period
**Chart of Account:** Standardized account codes for financial tracking
**Audit Log:** Record of all significant system actions
---
**End of Specification Document**