# Support Module — Architecture Map

> **Last updated:** 2026-06-10
> **Status:** Living document — incrementally updated as new information is discovered

---

## 1. Purpose & Responsibilities

The Support module is an **internal IT helpdesk/ticketing system** for club staff. It provides:
- Support ticket creation with categorization and priority
- Ticket assignment to employees
- Threaded replies on tickets
- File attachment support (on tickets and replies)
- Ticket lifecycle management (open → in_progress → waiting → closed, with reopen)
- Status counts dashboard
- Searchable ticket list with multiple filters

It does **NOT** manage:
- External customer/member support (this is internal staff-to-IT)
- Automated ticket routing or SLA enforcement
- Email integration or notifications on ticket changes
- Knowledge base or FAQ

---

## 2. Directory & File Structure

```
app/Modules/Support/
├── bootstrap.php                          # Menu + permission registration
├── Routes.php                             # Web routes (9 routes)
├── Controllers/
│   └── TicketController.php              # Full ticket lifecycle (CRUD, reply, assign, close, reopen, download)
├── Models/
│   └── Ticket.php                        # Search, replies, attachments, number generation, status/priority/category labels
├── Services/
│   └── AttachmentService.php             # File upload handling (validation, storage, retrieval)
└── Views/
    ├── index.php                         # Ticket list with filters and status counts
    ├── create.php                        # New ticket form
    └── show.php                          # Ticket detail with replies and attachments
```

---

## 3. Database Schema (Production — Source of Truth)

### 3.1 `support_tickets` Table

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| ticket_number | varchar(50) | NO | UNI | | Format: TKT-YYYY-NNNNNN |
| subject | varchar(255) | NO | | | Ticket subject line |
| body | longtext | NO | | | Full description |
| priority | varchar(20) | NO | MUL | 'normal' | urgent/high/normal/low |
| status | varchar(30) | NO | MUL | 'open' | open/in_progress/waiting/closed |
| category | varchar(50) | YES | | NULL | bug/feature/question/data_fix/permissions/printing/financial/other |
| created_by_employee_id | bigint unsigned | YES | MUL | NULL | FK to employees |
| assigned_to_employee_id | bigint unsigned | YES | MUL | NULL | FK to employees |
| branch_id | bigint unsigned | YES | MUL | NULL | FK to branches |
| related_entity_type | varchar(100) | YES | | NULL | For linking to specific entities |
| related_entity_id | bigint unsigned | YES | | NULL | FK to related entity |
| closed_at | timestamp | YES | | NULL | When ticket was closed |
| closed_by | bigint unsigned | YES | | NULL | FK to employees who closed |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |
| updated_at | timestamp | NO | | CURRENT_TIMESTAMP | On update |

### 3.2 `support_ticket_replies` Table

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| ticket_id | bigint unsigned | NO | MUL | | FK to support_tickets |
| body | longtext | NO | | | Reply content |
| employee_id | bigint unsigned | YES | | NULL | FK to employees |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |

### 3.3 `support_ticket_attachments` Table

| Column | Type | Nullable | Key | Default | Notes |
|--------|------|----------|-----|---------|-------|
| id | bigint unsigned | NO | PRI | auto_increment | |
| ticket_id | bigint unsigned | YES | MUL | NULL | FK to support_tickets |
| reply_id | bigint unsigned | YES | MUL | NULL | FK to support_ticket_replies |
| original_filename | varchar(255) | NO | | | User-facing filename |
| stored_filename | varchar(255) | NO | | | On-disk filename (randomized) |
| file_path | varchar(500) | NO | | | Relative path from app root |
| file_size | bigint unsigned | NO | | 0 | Size in bytes |
| mime_type | varchar(100) | NO | | | Validated MIME type |
| uploaded_by | bigint unsigned | YES | | NULL | FK to employees |
| created_at | timestamp | NO | | CURRENT_TIMESTAMP | |

### 3.4 Related Tables

| Table | Owner Module | Relationship |
|-------|--------------|--------------|
| employees | HR | created_by/assigned_to/closed_by FK |
| branches | Branches | branch_id FK |

---

## 4. Routes

| Method | Path | Handler | Middleware | Permission | Purpose |
|--------|------|---------|-----------|------------|---------|
| GET | /support | TicketController@index | auth | support.view | List tickets |
| GET | /support/create | TicketController@create | auth | support.create | Create form |
| POST | /support | TicketController@store | auth, csrf | support.create | Store ticket |
| GET | /support/{id} | TicketController@show | auth | support.view | View ticket + replies |
| POST | /support/{id}/reply | TicketController@reply | auth, csrf | support.reply | Add reply |
| POST | /support/{id}/close | TicketController@close | auth, csrf | support.close | Close ticket |
| POST | /support/{id}/reopen | TicketController@reopen | auth, csrf | support.manage | Reopen closed ticket |
| POST | /support/{id}/assign | TicketController@assign | auth, csrf | support.assign | Assign to employee |
| GET | /support/attachments/{id} | TicketController@download | auth | support.view | Download attachment |

---

## 5. Events Dispatched

None — the Support module does not dispatch any events.

---

## 6. Events Consumed

None — the Support module does not listen to any events.

---

## 7. Cross-Module Dependencies

### 7.1 Support IMPORTS FROM:

None — fully self-contained with no imports from other modules.

### 7.2 Other modules that reference Support:

None found — Support is a completely isolated leaf module.

---

## 8. Permissions

| Key | Description (AR) | Description (EN) |
|-----|------------------|------------------|
| support.view | عرض تذاكر الدعم | View Support Tickets |
| support.create | إنشاء تذكرة دعم | Create Support Ticket |
| support.reply | الرد على التذاكر | Reply to Tickets |
| support.assign | تعيين التذاكر | Assign Tickets |
| support.close | إغلاق التذاكر | Close Tickets |
| support.manage | إدارة تذاكر الدعم | Manage Support Tickets |

---

## 9. Sidebar Menu

| Label AR | Route | Permission | Order |
|----------|-------|------------|-------|
| الدعم الفني | /support | support.view | 900 |
| التذاكر | /support | support.view | 1 |
| تذكرة جديدة | /support/create | support.create | 2 |

---

## 10. Core Business Flows

### 10.1 Ticket Creation

```
1. GET /support/create — form with categories + employee list
2. POST /support
   - Validate: subject and body required
   - Generate ticket_number: TKT-{YEAR}-{6-digit sequence}
   - Insert support_tickets (status=open, branch from session)
   - Handle file attachments via AttachmentService
   - Redirect to ticket detail
```

### 10.2 Ticket Lifecycle

```
open → in_progress (auto on first reply or assign) → closed
                                                    ↓
                                              reopen → open

Status transitions:
- Reply on 'open' ticket → status becomes 'in_progress'
- Assign on 'open' ticket → status becomes 'in_progress'
- Close → status='closed', closed_at, closed_by set
- Reopen → status='open', closed_at/closed_by cleared
```

### 10.3 Attachment Handling

```
1. File upload (multi-file support)
2. Validate: UPLOAD_ERR_OK, size ≤ 20MB, MIME in allowlist
3. MIME detection via finfo (server-side, not trust client)
4. Store as: tkt_{ticketId}_{datetime}_{random}.{ext}
5. Path: storage/uploads/support/
6. Record in support_ticket_attachments (linked to ticket and optionally reply)
```

### 10.4 Ticket Number Generation

```
TKT-{YEAR}-{NNNNNN}
- Year: current 4-digit year
- Sequence: MAX of existing numbers in same year + 1
- Zero-padded to 6 digits
- Example: TKT-2026-000042
```

---

## 11. Ticket Categories

| Code | Arabic Label |
|------|-------------|
| bug | خلل فني |
| feature | طلب ميزة |
| question | استفسار |
| data_fix | تصحيح بيانات |
| permissions | صلاحيات |
| printing | طباعة |
| financial | مالي |
| other | أخرى |

---

## 12. Ticket Priority Levels

| Code | Arabic Label | Sort Order |
|------|-------------|-----------|
| urgent | عاجل | 1 (highest) |
| high | مرتفع | 2 |
| normal | عادي | 3 |
| low | منخفض | 4 |

---

## 13. Allowed Attachment MIME Types

| Category | Types |
|----------|-------|
| Images | jpeg, png, gif, webp, bmp |
| Documents | pdf, txt, csv, doc, docx, xls, xlsx |
| Archives | zip, rar |
| Video | mp4, quicktime |

Max file size: **20 MB** per file.

---

## 14. High-Risk Areas

### 14.1 Ticket Number Sequencing
- Uses `MAX(CAST(SUBSTRING(...) AS UNSIGNED))` — race condition on concurrent ticket creation
- No unique constraint enforcement on the computed sequence (only on ticket_number itself)
- If two tickets are created simultaneously, one will fail on the UNI constraint

### 14.2 Attachment Security
- Files stored on local filesystem (`storage/uploads/support/`)
- Download endpoint streams file directly via `readfile()` + `exit` — bypasses normal Response flow
- MIME validation uses server-side `finfo` (correct), but extension is taken from user-submitted filename
- No virus scanning on uploaded files
- `Content-Disposition: inline` means browser may render uploaded HTML/SVG (XSS risk)

### 14.3 No Notification on Ticket Events
- No events dispatched — ticket creation, assignment, replies do not trigger notifications
- Assigned employee has no way to know they were assigned except checking the UI
- No email/SMS integration

### 14.4 Entity Linking Unused
- `related_entity_type` and `related_entity_id` columns exist but are never populated in current code
- Could be used to link tickets to specific members/payments/etc. in the future

---

## 15. Known Patterns & Gotchas

1. **Fully isolated module** — no events dispatched or consumed, no cross-module imports
2. **Status auto-transitions** — first reply or assign on an 'open' ticket auto-moves to 'in_progress'
3. **Ticket model is not a real Model** — `Ticket` class does not extend `App\Core\Model`; it's a static utility class with raw SQL
4. **List ordering** — tickets sorted by status priority (open first), then priority (urgent first), then updated_at DESC
5. **Download bypasses Response** — `download()` uses `header()` + `readfile()` + `exit` instead of returning a Response object
6. **Branch from session** — ticket is linked to the employee's current branch at creation time
7. **No pagination on replies** — all replies loaded at once (could be slow on very active tickets)
8. **No edit/delete** — tickets and replies cannot be edited or deleted once created (audit-friendly)
9. **Reopen requires support.manage** — higher permission than close (support.close), preventing casual reopening
