From 3eefb145d3fbd7996b06dd65f36561b766ee3e1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 Aug 2025 18:04:10 +0000 Subject: [PATCH 1/2] Initial plan From 8860e036c70049d16265b2eddfb4f7dee9f9d8e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 9 Aug 2025 18:25:37 +0000 Subject: [PATCH 2/2] Complete EMR SRS documentation suite with all required documents Co-authored-by: kakadanhem <6958462+kakadanhem@users.noreply.github.com> --- API_SPECIFICATION.md | 1056 ++++++++++++++++++ DATABASE_DESIGN.md | 741 +++++++++++++ IMPLEMENTATION_GUIDE.md | 1384 ++++++++++++++++++++++++ SOFTWARE_REQUIREMENTS_SPECIFICATION.md | 727 +++++++++++++ 4 files changed, 3908 insertions(+) create mode 100644 API_SPECIFICATION.md create mode 100644 DATABASE_DESIGN.md create mode 100644 IMPLEMENTATION_GUIDE.md create mode 100644 SOFTWARE_REQUIREMENTS_SPECIFICATION.md diff --git a/API_SPECIFICATION.md b/API_SPECIFICATION.md new file mode 100644 index 0000000..2da01ad --- /dev/null +++ b/API_SPECIFICATION.md @@ -0,0 +1,1056 @@ +# API Specification Document +# EMR Backend System for Cambodia Healthcare + +## Version Information +- **Version**: 1.0 +- **Date**: 2025 +- **API Version**: v1 +- **Framework**: Laravel 12 +- **Authentication**: Laravel Sanctum + +--- + +## Table of Contents + +1. [API Overview](#1-api-overview) +2. [Authentication](#2-authentication) +3. [Request/Response Format](#3-requestresponse-format) +4. [Error Handling](#4-error-handling) +5. [Endpoint Specifications](#5-endpoint-specifications) +6. [Data Validation](#6-data-validation) +7. [Rate Limiting](#7-rate-limiting) +8. [API Testing](#8-api-testing) + +--- + +## 1. API Overview + +### 1.1 Base URL Structure + +**Production:** `https://api.emr.example.com/api/v1` +**Development:** `http://localhost:8000/api/v1` + +### 1.2 API Design Principles + +- **RESTful Architecture**: Standard HTTP methods and status codes +- **Resource-Based URLs**: Clear, predictable endpoint structure +- **JSON Communication**: All requests and responses in JSON format +- **Stateless**: Each request contains all necessary information +- **Versioned**: API versioning through URL path (`/v1/`) +- **Consistent**: Standardized response formats across all endpoints + +### 1.3 Supported HTTP Methods + +- `GET`: Retrieve resources +- `POST`: Create new resources +- `PUT`: Update entire resources +- `PATCH`: Partial resource updates +- `DELETE`: Remove resources + +### 1.4 Content Types + +**Request Content-Type:** `application/json` +**Response Content-Type:** `application/json` +**File Upload Content-Type:** `multipart/form-data` + +--- + +## 2. Authentication + +### 2.1 Authentication Method + +The API uses **Laravel Sanctum** for token-based authentication. + +### 2.2 Authentication Flow + +#### 2.2.1 User Login +```http +POST /api/v1/login +Content-Type: application/json + +{ + "email": "user@example.com", + "password": "password123", + "device_name": "Mobile App" +} +``` + +**Response:** +```json +{ + "data": { + "user": { + "id": 1, + "ulid": "01HKXXX...", + "name": "John Doe", + "email": "user@example.com" + }, + "token": "1|abc123...xyz789", + "expires_at": "2025-01-15T10:30:00Z" + } +} +``` + +#### 2.2.2 Token Usage +Include the token in all subsequent requests: + +```http +Authorization: Bearer 1|abc123...xyz789 +``` + +#### 2.2.3 User Registration +```http +POST /api/v1/register +Content-Type: application/json + +{ + "name": "Jane Smith", + "email": "jane@example.com", + "password": "password123", + "password_confirmation": "password123" +} +``` + +#### 2.2.4 Logout +```http +POST /api/v1/logout +Authorization: Bearer 1|abc123...xyz789 +``` + +### 2.3 Token Management + +#### 2.3.1 Token Refresh +```http +POST /api/v1/refresh +Authorization: Bearer 1|abc123...xyz789 +``` + +#### 2.3.2 Device Management +```http +GET /api/v1/devices +Authorization: Bearer 1|abc123...xyz789 + +POST /api/v1/devices/disconnect +Authorization: Bearer 1|abc123...xyz789 +{ + "device_id": "device_uuid" +} +``` + +--- + +## 3. Request/Response Format + +### 3.1 Standard Response Structure + +All API responses follow this consistent structure: + +```json +{ + "data": {}, + "meta": { + "current_page": 1, + "last_page": 10, + "per_page": 15, + "total": 150 + }, + "links": { + "first": "http://api.example.com/api/v1/patients?page=1", + "last": "http://api.example.com/api/v1/patients?page=10", + "prev": null, + "next": "http://api.example.com/api/v1/patients?page=2" + } +} +``` + +### 3.2 Single Resource Response + +```json +{ + "data": { + "id": 1, + "ulid": "01HKXXX...", + "code": "PAT001", + "facility_id": 1, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +} +``` + +### 3.3 Collection Response + +```json +{ + "data": [ + { + "id": 1, + "ulid": "01HKXXX...", + "code": "PAT001" + }, + { + "id": 2, + "ulid": "01HKYYY...", + "code": "PAT002" + } + ], + "meta": { + "current_page": 1, + "total": 2 + } +} +``` + +### 3.4 Query Parameters + +#### 3.4.1 Pagination +- `page`: Page number (default: 1) +- `per_page`: Items per page (default: 15, max: 100) + +#### 3.4.2 Filtering +- `filter[field]`: Filter by field value +- `filter[created_at][gte]`: Date range filtering +- `filter[status]`: Status filtering + +#### 3.4.3 Sorting +- `sort`: Sort field (default: id) +- `sort_direction`: Sort direction (asc/desc, default: asc) + +#### 3.4.4 Includes +- `include`: Related resources to include (comma-separated) + +**Example:** +``` +GET /api/v1/patients?page=2&per_page=20&filter[facility_id]=1&sort=created_at&sort_direction=desc&include=demographics,visits +``` + +--- + +## 4. Error Handling + +### 4.1 Error Response Format + +```json +{ + "error": { + "code": "VALIDATION_ERROR", + "message": "The given data was invalid.", + "details": { + "email": ["The email field is required."], + "password": ["The password must be at least 8 characters."] + } + } +} +``` + +### 4.2 HTTP Status Codes + +| Status Code | Description | +|-------------|-------------| +| 200 | OK - Request successful | +| 201 | Created - Resource created successfully | +| 204 | No Content - Request successful, no content returned | +| 400 | Bad Request - Invalid request data | +| 401 | Unauthorized - Authentication required | +| 403 | Forbidden - Access denied | +| 404 | Not Found - Resource not found | +| 422 | Unprocessable Entity - Validation errors | +| 429 | Too Many Requests - Rate limit exceeded | +| 500 | Internal Server Error - Server error | + +### 4.3 Error Codes + +| Error Code | Description | +|------------|-------------| +| VALIDATION_ERROR | Input validation failed | +| AUTHENTICATION_FAILED | Invalid credentials | +| AUTHORIZATION_FAILED | Insufficient permissions | +| RESOURCE_NOT_FOUND | Requested resource not found | +| RATE_LIMIT_EXCEEDED | API rate limit exceeded | +| SERVER_ERROR | Internal server error | + +--- + +## 5. Endpoint Specifications + +### 5.1 Authentication Endpoints + +#### 5.1.1 Login +```http +POST /api/v1/login +``` + +**Request Body:** +```json +{ + "email": "required|email", + "password": "required|string", + "device_name": "optional|string" +} +``` + +**Response (200):** +```json +{ + "data": { + "user": { + "id": 1, + "ulid": "01HKXXX...", + "name": "John Doe", + "email": "user@example.com", + "avatar": "https://example.com/avatar.jpg" + }, + "token": "1|abc123...xyz789", + "expires_at": "2025-01-15T10:30:00Z" + } +} +``` + +#### 5.1.2 Register +```http +POST /api/v1/register +``` + +**Request Body:** +```json +{ + "name": "required|string|max:255", + "email": "required|email|unique:users", + "password": "required|string|min:8|confirmed" +} +``` + +#### 5.1.3 Logout +```http +POST /api/v1/logout +Authorization: Bearer {token} +``` + +**Response (204):** No content + +#### 5.1.4 Get Current User +```http +GET /api/v1/user +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": { + "id": 1, + "ulid": "01HKXXX...", + "name": "John Doe", + "email": "user@example.com", + "roles": ["doctor", "admin"], + "permissions": ["patients.view", "patients.create"] + } +} +``` + +### 5.2 Patient Management Endpoints + +#### 5.2.1 List Patients +```http +GET /api/v1/patients +Authorization: Bearer {token} +``` + +**Query Parameters:** +- `page`: Page number +- `per_page`: Items per page (max 100) +- `filter[facility_id]`: Filter by facility +- `filter[active]`: Filter by active status +- `search`: Search in patient name/code +- `include`: Include related data (demographics,visits,addresses) + +**Response (200):** +```json +{ + "data": [ + { + "id": 1, + "ulid": "01HKXXX...", + "code": "PAT001", + "facility_id": 1, + "demographics": { + "name": { + "given": ["John"], + "family": "Doe" + }, + "birthdate": "1990-01-01", + "sex": "Male" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } + ], + "meta": { + "current_page": 1, + "last_page": 10, + "per_page": 15, + "total": 150 + } +} +``` + +#### 5.2.2 Create Patient +```http +POST /api/v1/patients +Authorization: Bearer {token} +Content-Type: application/json +``` + +**Request Body:** +```json +{ + "code": "required|string|max:255", + "facility_id": "required|integer|exists:facilities,id", + "demographics": { + "name": { + "given": ["required|array"], + "family": "required|string" + }, + "birthdate": "required|date", + "sex": "required|in:Male,Female", + "nationality_id": "required|integer|exists:terms,id", + "telephone": "nullable|string|max:20" + }, + "addresses": [ + { + "type_id": "required|integer|exists:terms,id", + "use_id": "required|integer|exists:terms,id", + "line1": "nullable|string|max:255", + "city": "nullable|string|max:100", + "province_id": "nullable|integer|exists:provinces,id" + } + ], + "identities": [ + { + "type_id": "required|integer|exists:terms,id", + "value": "required|string|max:255", + "system": "nullable|string|max:255" + } + ] +} +``` + +**Response (201):** +```json +{ + "data": { + "id": 1, + "ulid": "01HKXXX...", + "code": "PAT001", + "facility_id": 1, + "demographics": { + "name": { + "given": ["John"], + "family": "Doe" + }, + "birthdate": "1990-01-01", + "sex": "Male", + "nationality_id": 1, + "telephone": "+855123456789" + }, + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +} +``` + +#### 5.2.3 Get Patient +```http +GET /api/v1/patients/{id} +Authorization: Bearer {token} +``` + +**Query Parameters:** +- `include`: Include related data (demographics,visits,addresses,identities,cards) + +**Response (200):** +```json +{ + "data": { + "id": 1, + "ulid": "01HKXXX...", + "code": "PAT001", + "facility_id": 1, + "demographics": { + "name": { + "given": ["John"], + "family": "Doe" + }, + "birthdate": "1990-01-01", + "sex": "Male", + "nationality_id": 1, + "telephone": "+855123456789" + }, + "addresses": [ + { + "id": 1, + "type_id": 1, + "use_id": 1, + "line1": "123 Main St", + "city": "Phnom Penh", + "province_id": 1 + } + ], + "created_at": "2025-01-01T12:00:00Z", + "updated_at": "2025-01-01T12:00:00Z" + } +} +``` + +#### 5.2.4 Update Patient +```http +PUT /api/v1/patients/{id} +Authorization: Bearer {token} +Content-Type: application/json +``` + +**Request Body:** Same as Create Patient + +**Response (200):** Updated patient data + +#### 5.2.5 Delete Patient +```http +DELETE /api/v1/patients/{id} +Authorization: Bearer {token} +``` + +**Response (204):** No content + +#### 5.2.6 Search Patients +```http +GET /api/v1/patients/search/{query} +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": [ + { + "id": 1, + "ulid": "01HKXXX...", + "code": "PAT001", + "demographics": { + "name": { + "given": ["John"], + "family": "Doe" + } + } + } + ] +} +``` + +#### 5.2.7 Patient Summary +```http +GET /api/v1/patients/{id}/summary +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": { + "patient": { + "id": 1, + "code": "PAT001", + "demographics": {} + }, + "summary": { + "total_visits": 5, + "active_visits": 1, + "last_visit": "2025-01-01T12:00:00Z", + "conditions": 3, + "medications": 2 + } + } +} +``` + +### 5.3 Visit Management Endpoints + +#### 5.3.1 List Visits +```http +GET /api/v1/visits +Authorization: Bearer {token} +``` + +**Query Parameters:** +- `filter[patient_id]`: Filter by patient +- `filter[facility_id]`: Filter by facility +- `filter[status]`: Filter by status (active, discharged) +- `filter[admitted_at][gte]`: Date range filtering +- `include`: Include related data (patient,encounters,caretakers) + +**Response (200):** +```json +{ + "data": [ + { + "id": 1, + "ulid": "01HKXXX...", + "patient_id": 1, + "facility_id": 1, + "visit_type_id": 1, + "admission_type_id": 1, + "admitted_at": "2025-01-01T10:00:00Z", + "discharged_at": null, + "status": "active", + "patient": { + "code": "PAT001", + "demographics": { + "name": { + "given": ["John"], + "family": "Doe" + } + } + } + } + ] +} +``` + +#### 5.3.2 Create Visit +```http +POST /api/v1/visits +Authorization: Bearer {token} +``` + +**Request Body:** +```json +{ + "patient_id": "required|integer|exists:patients,id", + "facility_id": "required|integer|exists:facilities,id", + "visit_type_id": "nullable|integer|exists:terms,id", + "admission_type_id": "required|integer|exists:terms,id", + "admitted_at": "required|datetime", + "caretakers": [ + { + "name": "required|string|max:255", + "relationship_id": "required|integer|exists:terms,id", + "telecom": "nullable|array" + } + ] +} +``` + +#### 5.3.3 Discharge Visit +```http +POST /api/v1/visits/{id}/discharge +Authorization: Bearer {token} +``` + +**Request Body:** +```json +{ + "discharged_at": "required|datetime", + "discharge_type_id": "required|integer|exists:terms,id", + "visit_outcome_id": "nullable|integer|exists:terms,id", + "notes": "nullable|string" +} +``` + +#### 5.3.4 Visit Timeline +```http +GET /api/v1/visits/{id}/timeline +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": { + "visit": { + "id": 1, + "admitted_at": "2025-01-01T10:00:00Z" + }, + "timeline": [ + { + "datetime": "2025-01-01T10:00:00Z", + "type": "admission", + "description": "Patient admitted" + }, + { + "datetime": "2025-01-01T11:00:00Z", + "type": "encounter", + "description": "Consultation with Dr. Smith" + }, + { + "datetime": "2025-01-01T12:00:00Z", + "type": "observation", + "description": "Vital signs recorded" + } + ] + } +} +``` + +### 5.4 Encounter Management Endpoints + +#### 5.4.1 List Encounters +```http +GET /api/v1/encounters +Authorization: Bearer {token} +``` + +**Query Parameters:** +- `filter[visit_id]`: Filter by visit +- `filter[practitioner_id]`: Filter by practitioner +- `filter[status_id]`: Filter by status +- `include`: Include related data (visit,practitioner,observations,conditions) + +#### 5.4.2 Create Encounter +```http +POST /api/v1/encounters +Authorization: Bearer {token} +``` + +**Request Body:** +```json +{ + "visit_id": "required|integer|exists:visits,id", + "practitioner_id": "nullable|integer|exists:practitioners,id", + "department_id": "nullable|integer|exists:departments,id", + "room_id": "nullable|integer|exists:rooms,id", + "encounter_type_id": "required|integer|exists:terms,id", + "status_id": "required|integer|exists:terms,id", + "class_id": "required|integer|exists:terms,id", + "priority_id": "nullable|integer|exists:terms,id", + "started_at": "required|datetime", + "reason": "nullable|array", + "diagnosis": "nullable|array" +} +``` + +#### 5.4.3 Get Encounter Observations +```http +GET /api/v1/encounters/{id}/observations +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": [ + { + "id": 1, + "category_id": 1, + "code_id": 1, + "status_id": 1, + "effective_datetime": "2025-01-01T12:00:00Z", + "value_type": "quantity", + "value_data": { + "value": 120, + "unit": "mmHg" + } + } + ] +} +``` + +### 5.5 Observation Management Endpoints + +#### 5.5.1 Create Observation +```http +POST /api/v1/encounters/{encounter_id}/observations +Authorization: Bearer {token} +``` + +**Request Body:** +```json +{ + "category_id": "required|integer|exists:terms,id", + "code_id": "required|integer|exists:terms,id", + "status_id": "required|integer|exists:terms,id", + "effective_datetime": "required|datetime", + "value_type": "required|in:quantity,string,boolean,datetime,codeable_concept", + "value_data": "required|array", + "unit_id": "nullable|integer|exists:terms,id", + "reference_range": "nullable|array", + "interpretation": "nullable|array", + "note": "nullable|string", + "method_id": "nullable|integer|exists:terms,id", + "performer_id": "nullable|integer|exists:practitioners,id" +} +``` + +#### 5.5.2 Update Observation +```http +PUT /api/v1/observations/{id} +Authorization: Bearer {token} +``` + +**Request Body:** Same as Create Observation + +### 5.6 Reference Data Endpoints + +#### 5.6.1 Get Taxonomy Values +```http +GET /api/v1/taxonomy/{type} +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": [ + { + "id": 1, + "name": "Male", + "code": "M" + }, + { + "id": 2, + "name": "Female", + "code": "F" + } + ] +} +``` + +#### 5.6.2 Get Facilities +```http +GET /api/v1/facilities +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": [ + { + "id": 1, + "name": "General Hospital", + "code": "GH001" + } + ] +} +``` + +#### 5.6.3 Get Gazetteers +```http +GET /api/v1/gazetteers/{type} +Authorization: Bearer {token} +``` + +**Parameters:** +- `{type}`: province, district, commune, village + +**Response (200):** +```json +{ + "data": [ + { + "id": 1, + "name": "Phnom Penh", + "code": "PP", + "parent_id": null + } + ] +} +``` + +### 5.7 Dashboard Endpoints + +#### 5.7.1 Dashboard Statistics +```http +GET /api/v1/dashboard/stats +Authorization: Bearer {token} +``` + +**Response (200):** +```json +{ + "data": { + "total_patients": 1500, + "active_visits": 45, + "today_registrations": 12, + "pending_discharges": 8 + } +} +``` + +#### 5.7.2 Active Patients +```http +GET /api/v1/dashboard/active-patients +Authorization: Bearer {token} +``` + +#### 5.7.3 Recent Activity +```http +GET /api/v1/dashboard/recent-activity +Authorization: Bearer {token} +``` + +### 5.8 Clinical Forms Endpoints + +#### 5.8.1 List Clinical Forms +```http +GET /api/v1/clinical-forms +Authorization: Bearer {token} +``` + +#### 5.8.2 Create Clinical Form +```http +POST /api/v1/clinical-forms +Authorization: Bearer {token} +``` + +#### 5.8.3 Preview Clinical Form +```http +GET /api/v1/clinical-forms/{id}/preview +Authorization: Bearer {token} +``` + +--- + +## 6. Data Validation + +### 6.1 Common Validation Rules + +#### 6.1.1 Required Fields +- All primary identifiers (IDs, codes) +- Essential demographic information +- Clinical timestamps +- Status indicators + +#### 6.1.2 Data Types +- **Strings**: Max length validation +- **Integers**: Positive integers for IDs +- **Dates**: Valid date format (ISO 8601) +- **Emails**: Valid email format +- **Phone Numbers**: Valid format with country code + +#### 6.1.3 Business Rules +- Patient code unique within facility +- Discharge date after admission date +- Encounter dates within visit date range +- Age calculation from birthdate + +### 6.2 Custom Validation Rules + +#### 6.2.1 Cambodia Phone Number +```php +'telephone' => 'nullable|regex:/^\+855[0-9]{8,9}$/' +``` + +#### 6.2.2 Patient Code Format +```php +'code' => 'required|regex:/^[A-Z]{3}[0-9]{3,6}$/' +``` + +#### 6.2.3 Date Range Validation +```php +'discharged_at' => 'nullable|date|after:admitted_at' +``` + +--- + +## 7. Rate Limiting + +### 7.1 Rate Limit Rules + +| Endpoint Group | Limit | Window | +|----------------|-------|--------| +| Authentication | 5 requests | 1 minute | +| General API | 60 requests | 1 minute | +| File Upload | 10 requests | 1 minute | +| Search | 30 requests | 1 minute | + +### 7.2 Rate Limit Headers + +Response headers indicate current rate limit status: + +```http +X-RateLimit-Limit: 60 +X-RateLimit-Remaining: 59 +X-RateLimit-Reset: 1640995200 +``` + +### 7.3 Rate Limit Exceeded Response + +```json +{ + "error": { + "code": "RATE_LIMIT_EXCEEDED", + "message": "Too many requests. Please try again later.", + "retry_after": 60 + } +} +``` + +--- + +## 8. API Testing + +### 8.1 Test Environment + +**Base URL:** `http://localhost:8000/api/v1` + +### 8.2 Test Data + +Use the provided seeders to populate test data: + +```bash +php artisan db:seed --class=TestDataSeeder +``` + +### 8.3 Authentication for Testing + +Use the test user credentials: + +```json +{ + "email": "test@example.com", + "password": "password" +} +``` + +### 8.4 Postman Collection + +A comprehensive Postman collection is available with: +- Pre-configured requests for all endpoints +- Environment variables for different stages +- Automated tests for response validation +- Authentication flow examples + +### 8.5 API Documentation + +Interactive API documentation is available at: +- **Development:** `http://localhost:8000/docs/api` +- **Production:** `https://api.emr.example.com/docs/api` + +Generated using Laravel Scramble with: +- Request/response examples +- Authentication requirements +- Field descriptions +- Error scenarios + +--- + +## 9. Appendices + +### 9.1 Status Codes Reference + +Complete list of HTTP status codes used in the API with their meanings and usage contexts. + +### 9.2 Error Codes Reference + +Detailed list of custom error codes with descriptions and troubleshooting information. + +### 9.3 Field Reference + +Complete field specifications for all resources including data types, constraints, and examples. + +### 9.4 Changelog + +Version history with breaking changes, new features, and deprecations. + +--- + +*This API specification provides complete documentation for integrating with the EMR Backend System, enabling developers to build robust healthcare applications for Cambodia's healthcare infrastructure.* \ No newline at end of file diff --git a/DATABASE_DESIGN.md b/DATABASE_DESIGN.md new file mode 100644 index 0000000..a154347 --- /dev/null +++ b/DATABASE_DESIGN.md @@ -0,0 +1,741 @@ +# Database Design Document +# EMR Backend System for Cambodia Healthcare + +## Version Information +- **Version**: 1.0 +- **Date**: 2025 +- **Database Engine**: MySQL 8.0+ / PostgreSQL 14+ +- **Framework**: Laravel 12 + +--- + +## Table of Contents + +1. [Database Overview](#1-database-overview) +2. [Entity Relationship Diagram](#2-entity-relationship-diagram) +3. [Table Specifications](#3-table-specifications) +4. [Relationships and Constraints](#4-relationships-and-constraints) +5. [Indexing Strategy](#5-indexing-strategy) +6. [Data Types and Constraints](#6-data-types-and-constraints) +7. [Migration Implementation](#7-migration-implementation) + +--- + +## 1. Database Overview + +### 1.1 Database Architecture + +The EMR database follows a normalized relational design with the following principles: +- Third Normal Form (3NF) compliance +- Referential integrity through foreign key constraints +- Soft delete implementation for all clinical data +- Complete audit trail for all operations +- JSON columns for flexible data structures where appropriate +- ULID support for external system integration + +### 1.2 Common Patterns + +**Standard Fields Pattern (commonFields)** +Every primary business entity includes these standard audit and lifecycle fields: + +```sql +created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, +created_by BIGINT UNSIGNED NULL, +updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, +updated_by BIGINT UNSIGNED NULL, +deleted_at TIMESTAMP NULL, +deleted_by BIGINT UNSIGNED NULL +``` + +**Indexing Strategy:** +- Primary keys as auto-incrementing BIGINT +- Index on all foreign keys +- Index on commonly searched fields +- Index on temporal fields (dates, timestamps) +- Composite indexes for multi-column searches + +### 1.3 Database Modules + +The database is organized into logical modules: + +1. **Master Data Module**: Terminologies, Concepts, Geographic data +2. **Healthcare Facility Module**: Organizations, Facilities, Departments +3. **Patient Module**: Patient demographics, addresses, identities +4. **Clinical Module**: Visits, Encounters, Observations, Conditions +5. **Medication Module**: Instructions, Requests, Dispensing +6. **Service Module**: Laboratory, Imaging, Procedures +7. **Financial Module**: Invoicing, Billing +8. **User Management Module**: Authentication, Authorization + +--- + +## 2. Entity Relationship Diagram + +### 2.1 Core Entities Overview + +``` +Organizations + ↓ +Facilities → Departments → Rooms + ↓ ↓ +Patients → Visits → Encounters → Observations + ↓ ↓ +Demographics Conditions + ↓ +Addresses +``` + +### 2.2 Master Data Relationships + +``` +Terminologies → Concept Categories → Concepts → Terms + ↓ + Used throughout system +``` + +### 2.3 Geographic Relationships + +``` +Provinces → Districts → Communes → Villages + ↓ + Gazetteers (Used for addresses) +``` + +### 2.4 Clinical Workflow Relationships + +``` +Patient → Visit → Encounter → Observation + ↓ ↓ + Condition Laboratory/Imaging + ↓ + Medication (Requests/Dispenses) +``` + +--- + +## 3. Table Specifications + +### 3.1 Master Data Tables + +#### 3.1.1 Terminologies Table +```sql +CREATE TABLE terminologies ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + code VARCHAR(100) NOT NULL UNIQUE, + version VARCHAR(50) NULL, + description TEXT NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_terminologies_code (code), + INDEX idx_terminologies_active (is_active), + INDEX idx_terminologies_created_at (created_at), + INDEX idx_terminologies_created_by (created_by), + INDEX idx_terminologies_updated_at (updated_at), + INDEX idx_terminologies_updated_by (updated_by), + INDEX idx_terminologies_deleted_at (deleted_at), + INDEX idx_terminologies_deleted_by (deleted_by) +); +``` + +#### 3.1.2 Concept Categories Table +```sql +CREATE TABLE concept_categories ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + terminology_id BIGINT UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + code VARCHAR(100) NOT NULL, + description TEXT NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_concept_categories_terminology_id (terminology_id), + INDEX idx_concept_categories_code (code), + INDEX idx_concept_categories_active (is_active), + UNIQUE KEY uk_concept_categories_terminology_code (terminology_id, code), + FOREIGN KEY (terminology_id) REFERENCES terminologies(id) ON DELETE CASCADE +); +``` + +#### 3.1.3 Concepts Table +```sql +CREATE TABLE concepts ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + code VARCHAR(50) NOT NULL, + system_id BIGINT UNSIGNED NOT NULL, + concept_category_id BIGINT UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + parent_id BIGINT UNSIGNED NULL, + sort_order INT DEFAULT 0, + is_active BOOLEAN DEFAULT TRUE, + description TEXT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_concepts_code (code), + INDEX idx_concepts_system_id (system_id), + INDEX idx_concepts_concept_category_id (concept_category_id), + INDEX idx_concepts_parent_id (parent_id), + INDEX idx_concepts_active (is_active), + UNIQUE KEY uk_concepts_category_code (concept_category_id, code), + FOREIGN KEY (concept_category_id) REFERENCES concept_categories(id) ON DELETE CASCADE, + FOREIGN KEY (parent_id) REFERENCES concepts(id) ON DELETE SET NULL +); +``` + +#### 3.1.4 Terms Table +```sql +CREATE TABLE terms ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + concept_id BIGINT UNSIGNED NOT NULL, + language_code VARCHAR(10) NOT NULL DEFAULT 'en', + name VARCHAR(255) NOT NULL, + description TEXT NULL, + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_terms_concept_id (concept_id), + INDEX idx_terms_language_code (language_code), + INDEX idx_terms_active (is_active), + UNIQUE KEY uk_terms_concept_language (concept_id, language_code), + FOREIGN KEY (concept_id) REFERENCES concepts(id) ON DELETE CASCADE +); +``` + +### 3.2 Geographic Tables + +#### 3.2.1 Provinces Table +```sql +CREATE TABLE provinces ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(255) NOT NULL, + name_en VARCHAR(255) NULL, + code VARCHAR(20) NOT NULL UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_provinces_code (code), + INDEX idx_provinces_name (name) +); +``` + +#### 3.2.2 Districts Table +```sql +CREATE TABLE districts ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + province_id BIGINT UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + name_en VARCHAR(255) NULL, + code VARCHAR(20) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_districts_province_id (province_id), + INDEX idx_districts_code (code), + INDEX idx_districts_name (name), + UNIQUE KEY uk_districts_province_code (province_id, code), + FOREIGN KEY (province_id) REFERENCES provinces(id) ON DELETE CASCADE +); +``` + +#### 3.2.3 Communes Table +```sql +CREATE TABLE communes ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + district_id BIGINT UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + name_en VARCHAR(255) NULL, + code VARCHAR(20) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_communes_district_id (district_id), + INDEX idx_communes_code (code), + INDEX idx_communes_name (name), + UNIQUE KEY uk_communes_district_code (district_id, code), + FOREIGN KEY (district_id) REFERENCES districts(id) ON DELETE CASCADE +); +``` + +#### 3.2.4 Villages Table +```sql +CREATE TABLE villages ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + commune_id BIGINT UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL, + name_en VARCHAR(255) NULL, + code VARCHAR(20) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_villages_commune_id (commune_id), + INDEX idx_villages_code (code), + INDEX idx_villages_name (name), + UNIQUE KEY uk_villages_commune_code (commune_id, code), + FOREIGN KEY (commune_id) REFERENCES communes(id) ON DELETE CASCADE +); +``` + +#### 3.2.5 Gazetteers Table +```sql +CREATE TABLE gazetteers ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + type VARCHAR(50) NOT NULL, + name VARCHAR(255) NOT NULL, + code VARCHAR(50) NULL, + parent_id BIGINT UNSIGNED NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + + INDEX idx_gazetteers_type (type), + INDEX idx_gazetteers_code (code), + INDEX idx_gazetteers_parent_id (parent_id), + INDEX idx_gazetteers_name (name), + FOREIGN KEY (parent_id) REFERENCES gazetteers(id) ON DELETE SET NULL +); +``` + +### 3.3 Healthcare Facility Tables + +#### 3.3.1 Organizations Table +```sql +CREATE TABLE organizations ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + ulid CHAR(26) NOT NULL UNIQUE, + name VARCHAR(255) NOT NULL, + identifier VARCHAR(100) UNIQUE, + type VARCHAR(100) NULL, + active BOOLEAN DEFAULT TRUE, + telecom JSON NULL, + address JSON NULL, + contact JSON NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_organizations_ulid (ulid), + INDEX idx_organizations_identifier (identifier), + INDEX idx_organizations_active (active), + INDEX idx_organizations_type (type), + INDEX idx_organizations_created_at (created_at), + INDEX idx_organizations_created_by (created_by), + INDEX idx_organizations_updated_at (updated_at), + INDEX idx_organizations_updated_by (updated_by), + INDEX idx_organizations_deleted_at (deleted_at), + INDEX idx_organizations_deleted_by (deleted_by) +); +``` + +#### 3.3.2 Facilities Table +```sql +CREATE TABLE facilities ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + code VARCHAR(255) NOT NULL UNIQUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_facilities_code (code), + INDEX idx_facilities_created_at (created_at), + INDEX idx_facilities_created_by (created_by), + INDEX idx_facilities_updated_at (updated_at), + INDEX idx_facilities_updated_by (updated_by), + INDEX idx_facilities_deleted_at (deleted_at), + INDEX idx_facilities_deleted_by (deleted_by) +); +``` + +### 3.4 Patient Management Tables + +#### 3.4.1 Patients Table +```sql +CREATE TABLE patients ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + ulid CHAR(26) NOT NULL, + code VARCHAR(255) NOT NULL, + facility_id BIGINT UNSIGNED NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_patients_ulid (ulid), + INDEX idx_patients_code (code), + INDEX idx_patients_facility_id (facility_id), + INDEX idx_patients_created_at (created_at), + INDEX idx_patients_created_by (created_by), + INDEX idx_patients_updated_at (updated_at), + INDEX idx_patients_updated_by (updated_by), + INDEX idx_patients_deleted_at (deleted_at), + INDEX idx_patients_deleted_by (deleted_by), + FOREIGN KEY (facility_id) REFERENCES facilities(id) +); +``` + +#### 3.4.2 Patient Demographics Table +```sql +CREATE TABLE patient_demographics ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + patient_id BIGINT UNSIGNED NOT NULL, + name JSON NOT NULL, + birthdate DATE NOT NULL, + telecom JSON NULL, + address JSON NULL, + sex ENUM('Female', 'Male') NOT NULL, + nationality_id BIGINT UNSIGNED NOT NULL, + telephone VARCHAR(20) NULL, + died_at DATETIME NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_demographics_patient_id (patient_id), + INDEX idx_demographics_birthdate (birthdate), + INDEX idx_demographics_sex (sex), + INDEX idx_demographics_nationality_id (nationality_id), + INDEX idx_demographics_telephone (telephone), + INDEX idx_demographics_died_at (died_at), + FOREIGN KEY (patient_id) REFERENCES patients(id) ON DELETE CASCADE +); +``` + +### 3.5 Clinical Workflow Tables + +#### 3.5.1 Visits Table +```sql +CREATE TABLE visits ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + ulid CHAR(26) NOT NULL, + patient_id BIGINT UNSIGNED NOT NULL, + facility_id BIGINT UNSIGNED NOT NULL, + visit_type_id BIGINT UNSIGNED NULL, + admission_type_id BIGINT UNSIGNED NOT NULL, + admitted_at DATETIME NOT NULL, + discharged_at DATETIME NULL, + discharge_type_id BIGINT UNSIGNED NULL, + visit_outcome_id BIGINT UNSIGNED NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_visits_ulid (ulid), + INDEX idx_visits_patient_id (patient_id), + INDEX idx_visits_facility_id (facility_id), + INDEX idx_visits_visit_type_id (visit_type_id), + INDEX idx_visits_admission_type_id (admission_type_id), + INDEX idx_visits_admitted_at (admitted_at), + INDEX idx_visits_discharged_at (discharged_at), + INDEX idx_visits_discharge_type_id (discharge_type_id), + INDEX idx_visits_visit_outcome_id (visit_outcome_id), + FOREIGN KEY (patient_id) REFERENCES patients(id), + FOREIGN KEY (facility_id) REFERENCES facilities(id) ON DELETE CASCADE, + FOREIGN KEY (visit_type_id) REFERENCES terms(id) ON DELETE CASCADE, + FOREIGN KEY (admission_type_id) REFERENCES terms(id) ON DELETE CASCADE, + FOREIGN KEY (discharge_type_id) REFERENCES terms(id) ON DELETE CASCADE, + FOREIGN KEY (visit_outcome_id) REFERENCES terms(id) ON DELETE CASCADE +); +``` + +#### 3.5.2 Encounters Table +```sql +CREATE TABLE encounters ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + visit_id BIGINT UNSIGNED NOT NULL, + practitioner_id BIGINT UNSIGNED NULL, + department_id BIGINT UNSIGNED NULL, + room_id BIGINT UNSIGNED NULL, + encounter_type_id BIGINT UNSIGNED NOT NULL, + status_id BIGINT UNSIGNED NOT NULL, + class_id BIGINT UNSIGNED NOT NULL, + priority_id BIGINT UNSIGNED NULL, + started_at DATETIME NOT NULL, + ended_at DATETIME NULL, + reason JSON NULL, + diagnosis JSON NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_encounters_visit_id (visit_id), + INDEX idx_encounters_practitioner_id (practitioner_id), + INDEX idx_encounters_department_id (department_id), + INDEX idx_encounters_room_id (room_id), + INDEX idx_encounters_type_id (encounter_type_id), + INDEX idx_encounters_status_id (status_id), + INDEX idx_encounters_class_id (class_id), + INDEX idx_encounters_priority_id (priority_id), + INDEX idx_encounters_started_at (started_at), + INDEX idx_encounters_ended_at (ended_at), + FOREIGN KEY (visit_id) REFERENCES visits(id) ON DELETE CASCADE, + FOREIGN KEY (practitioner_id) REFERENCES practitioners(id), + FOREIGN KEY (department_id) REFERENCES departments(id), + FOREIGN KEY (room_id) REFERENCES rooms(id), + FOREIGN KEY (encounter_type_id) REFERENCES terms(id), + FOREIGN KEY (status_id) REFERENCES terms(id), + FOREIGN KEY (class_id) REFERENCES terms(id), + FOREIGN KEY (priority_id) REFERENCES terms(id) +); +``` + +--- + +## 4. Relationships and Constraints + +### 4.1 Primary Key Constraints + +All tables use auto-incrementing BIGINT UNSIGNED primary keys: +- Provides sufficient range for large-scale systems +- Ensures uniqueness and order +- Optimizes join performance + +### 4.2 Foreign Key Relationships + +**Cascade Rules:** +- `ON DELETE CASCADE`: Child records are automatically deleted +- `ON DELETE SET NULL`: Foreign key is set to NULL +- `ON DELETE RESTRICT`: Prevents deletion if references exist + +**Key Relationships:** +- Patient → Facility (RESTRICT) +- Visit → Patient (RESTRICT) +- Visit → Facility (CASCADE) +- Encounter → Visit (CASCADE) +- Observation → Encounter (CASCADE) +- Condition → Encounter (CASCADE) + +### 4.3 Unique Constraints + +**Business Key Uniqueness:** +- Patient code unique within facility +- Facility code globally unique +- Practitioner code globally unique +- Identity values unique within type and system + +### 4.4 Check Constraints + +**Data Validation:** +- Discharge date must be after admission date +- End dates must be after start dates +- Effective dates cannot be in the future +- Age calculation from birthdate must be reasonable + +--- + +## 5. Indexing Strategy + +### 5.1 Primary Indexes + +**Auto-created indexes:** +- Primary key indexes on all tables +- Unique constraint indexes +- Foreign key indexes + +### 5.2 Secondary Indexes + +**Search Optimization:** +- Patient name search (JSON functional index) +- Date range queries (temporal columns) +- Status-based filtering +- Geographic location searches + +**Composite Indexes:** +```sql +-- Patient search by facility and code +CREATE INDEX idx_patients_facility_code ON patients(facility_id, code); + +-- Visit date range queries +CREATE INDEX idx_visits_facility_date ON visits(facility_id, admitted_at); + +-- Active encounters by practitioner +CREATE INDEX idx_encounters_practitioner_active ON encounters(practitioner_id, status_id, started_at); +``` + +### 5.3 JSON Indexes + +**MySQL 8.0+ JSON functional indexes:** +```sql +-- Patient name search +CREATE INDEX idx_demographics_given_name ON patient_demographics((JSON_UNQUOTE(JSON_EXTRACT(name, '$.given[0]')))); + +-- Practitioner name search +CREATE INDEX idx_practitioners_family_name ON practitioners((JSON_UNQUOTE(JSON_EXTRACT(name, '$.family')))); +``` + +### 5.4 Full-Text Indexes + +**Text Search Optimization:** +```sql +-- Clinical notes search +CREATE FULLTEXT INDEX ft_observations_note ON observations(note); + +-- Condition descriptions +CREATE FULLTEXT INDEX ft_conditions_note ON conditions(note); +``` + +--- + +## 6. Data Types and Constraints + +### 6.1 Standard Data Types + +**Numeric Types:** +- `BIGINT UNSIGNED`: Primary keys, foreign keys +- `INT`: Counters, small numbers +- `DECIMAL(10,2)`: Currency amounts +- `BOOLEAN`: True/false flags + +**String Types:** +- `VARCHAR(255)`: Names, descriptions +- `VARCHAR(100)`: Codes, identifiers +- `VARCHAR(20)`: Phone numbers, postal codes +- `TEXT`: Long descriptions, notes +- `CHAR(26)`: ULID values + +**Temporal Types:** +- `TIMESTAMP`: Audit timestamps +- `DATETIME`: Clinical timestamps +- `DATE`: Birth dates, start/end dates + +**Structured Types:** +- `JSON`: Complex data structures +- `ENUM`: Fixed value lists + +### 6.2 JSON Structure Specifications + +**Patient Name JSON:** +```json +{ + "use": "official", + "family": "Surname", + "given": ["FirstName", "MiddleName"], + "prefix": ["Mr.", "Dr."], + "suffix": ["Jr.", "III"] +} +``` + +**Address JSON:** +```json +{ + "use": "home", + "type": "postal", + "line": ["123 Main St", "Apt 4B"], + "city": "Phnom Penh", + "district": "Daun Penh", + "province": "Phnom Penh", + "postalCode": "12000", + "country": "KH" +} +``` + +**Telecom JSON:** +```json +[ + { + "system": "phone", + "value": "+855123456789", + "use": "mobile", + "rank": 1 + }, + { + "system": "email", + "value": "patient@example.com", + "use": "work", + "rank": 2 + } +] +``` + +--- + +## 7. Migration Implementation + +### 7.1 Migration Order + +**Foundation Migrations:** +1. User and authentication tables +2. Master data tables (terminologies, concepts) +3. Geographic tables (provinces, districts, communes, villages) +4. Organization and facility tables +5. Patient tables +6. Practitioner tables +7. Clinical workflow tables +8. Service and medication tables +9. Financial tables +10. Audit and logging tables + +### 7.2 Laravel Migration Examples + +**Base Migration Pattern:** +```php +id(); + // Business fields + $table->commonFields(); + + // Indexes + // Foreign keys + }); + } + + public function down(): void + { + Schema::dropIfExists('table_name'); + } +}; +``` + +**Common Fields Macro:** +```php +// In AppServiceProvider boot() method +Blueprint::macro('commonFields', function () { + $this->timestamp('created_at')->useCurrent()->index(); + $this->foreignId('created_by')->nullable()->index(); + $this->timestamp('updated_at')->nullable()->useCurrentOnUpdate()->index(); + $this->foreignId('updated_by')->nullable()->index(); + $this->softDeletes($column = 'deleted_at', $precision = 0)->index(); + $this->foreignId('deleted_by')->nullable()->index(); +}); +``` + +--- + +This database design provides a comprehensive foundation for the EMR system, ensuring data integrity, performance, and scalability while supporting Cambodia's specific healthcare requirements. \ No newline at end of file diff --git a/IMPLEMENTATION_GUIDE.md b/IMPLEMENTATION_GUIDE.md new file mode 100644 index 0000000..2ecd10b --- /dev/null +++ b/IMPLEMENTATION_GUIDE.md @@ -0,0 +1,1384 @@ +# Implementation Guide +# EMR Backend System for Cambodia Healthcare + +## Version Information +- **Version**: 1.0 +- **Date**: 2025 +- **Framework**: Laravel 12 +- **PHP Version**: 8.2+ + +--- + +## Table of Contents + +1. [Getting Started](#1-getting-started) +2. [Environment Setup](#2-environment-setup) +3. [Database Setup](#3-database-setup) +4. [Laravel Configuration](#4-laravel-configuration) +5. [Implementation Steps](#5-implementation-steps) +6. [Testing Strategy](#6-testing-strategy) +7. [Deployment Guide](#7-deployment-guide) +8. [Troubleshooting](#8-troubleshooting) + +--- + +## 1. Getting Started + +### 1.1 Prerequisites + +**System Requirements:** +- PHP 8.2 or higher +- Composer 2.0+ +- Node.js 18+ and NPM +- MySQL 8.0+ or PostgreSQL 14+ +- Redis 6.0+ (for caching and sessions) +- Git version control + +**Development Tools:** +- IDE/Editor (VS Code, PhpStorm) +- API testing tool (Postman, Insomnia) +- Database management tool (phpMyAdmin, Adminer) + +### 1.2 Project Structure Overview + +``` +emr-backend/ +├── app/ +│ ├── Actions/ # Business actions +│ ├── Console/ # Artisan commands +│ ├── Helpers/ # Helper classes +│ ├── Http/ +│ │ ├── Controllers/ # API controllers +│ │ ├── Middleware/ # Custom middleware +│ │ ├── Requests/ # Form request validation +│ │ └── Resources/ # API resources +│ ├── Models/ # Eloquent models +│ ├── Providers/ # Service providers +│ └── Rules/ # Custom validation rules +├── config/ # Configuration files +├── database/ +│ ├── factories/ # Model factories +│ ├── migrations/ # Database migrations +│ └── seeders/ # Database seeders +├── resources/ +│ ├── lang/ # Localization files +│ └── views/ # View templates +├── routes/ +│ ├── api.php # API routes +│ ├── web.php # Web routes +│ └── console.php # Console routes +├── storage/ # File storage +├── tests/ +│ ├── Feature/ # Feature tests +│ └── Unit/ # Unit tests +└── vendor/ # Composer dependencies +``` + +--- + +## 2. Environment Setup + +### 2.1 Create New Laravel Project + +```bash +# Create new Laravel 12 project +composer create-project laravel/laravel emr-backend "^12.0" +cd emr-backend + +# Install additional packages +composer require laravel/sanctum +composer require laravel/passport +composer require spatie/laravel-permission +composer require spatie/laravel-activitylog +composer require spatie/laravel-query-builder +composer require jenssegers/agent +composer require dedoc/scramble + +# Development dependencies +composer require --dev laravel/pail +composer require --dev laravel/sail +``` + +### 2.2 Environment Configuration + +Create `.env` file from `.env.example`: + +```bash +cp .env.example .env +``` + +Configure environment variables: + +```env +# Application +APP_NAME="EMR Backend System" +APP_ENV=local +APP_KEY=base64:generated_key_here +APP_DEBUG=true +APP_URL=http://localhost:8000 +APP_TIMEZONE=Asia/Phnom_Penh + +# Frontend URL (for email links) +FRONTEND_URL=http://localhost:3000 + +# Database +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=emr_backend +DB_USERNAME=root +DB_PASSWORD=password + +# Redis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +# Cache +CACHE_DRIVER=redis +SESSION_DRIVER=redis +QUEUE_CONNECTION=redis + +# Mail +MAIL_MAILER=smtp +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="noreply@emr.example.com" +MAIL_FROM_NAME="${APP_NAME}" + +# Sanctum +SANCTUM_STATEFUL_DOMAINS=localhost,127.0.0.1,::1 + +# Logging +LOG_CHANNEL=stack +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug +``` + +### 2.3 Generate Application Key + +```bash +php artisan key:generate +``` + +--- + +## 3. Database Setup + +### 3.1 Create Database + +```sql +-- MySQL +CREATE DATABASE emr_backend CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; + +-- PostgreSQL +CREATE DATABASE emr_backend WITH ENCODING 'UTF8'; +``` + +### 3.2 Configure Database Connection + +Update `.env` with correct database credentials and test connection: + +```bash +php artisan migrate:status +``` + +### 3.3 Install Sanctum + +```bash +php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider" +``` + +--- + +## 4. Laravel Configuration + +### 4.1 Configure Service Providers + +Add to `config/app.php`: + +```php +'providers' => [ + // ... existing providers + Spatie\Permission\PermissionServiceProvider::class, + Spatie\Activitylog\ActivitylogServiceProvider::class, + Spatie\QueryBuilder\QueryBuilderServiceProvider::class, +], +``` + +### 4.2 Configure AppServiceProvider + +Update `app/Providers/AppServiceProvider.php`: + +```php +timestamp('created_at')->useCurrent()->index(); + $this->foreignId('created_by')->nullable()->index(); + $this->timestamp('updated_at')->nullable()->useCurrentOnUpdate()->index(); + $this->foreignId('updated_by')->nullable()->index(); + $this->softDeletes($column = 'deleted_at', $precision = 0)->index(); + $this->foreignId('deleted_by')->nullable()->index(); + }); + + // Configure Sanctum + Sanctum::usePersonalAccessTokenModel( + \App\Models\PersonalAccessToken::class + ); + } +} +``` + +### 4.3 Configure Middleware + +Update `bootstrap/app.php`: + +```php +withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware) { + $middleware->api(prepend: [ + \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, + ]); + + $middleware->alias([ + 'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class, + ]); + + $middleware->throttle([ + 'api' => 60, + 'login' => 5, + 'uploads' => 10, + ]); + }) + ->withExceptions(function (Exceptions $exceptions) { + // + })->create(); +``` + +--- + +## 5. Implementation Steps + +### 5.1 Step 1: Create Base Models + +#### 5.1.1 Create User Model Extensions + +```bash +php artisan make:model PersonalAccessToken +``` + +```php + 'json', + 'last_used_at' => 'datetime', + 'expires_at' => 'datetime', + ]; +} +``` + +#### 5.1.2 Create Base Model Class + +```bash +php artisan make:model BaseModel +``` + +```php + 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + ]; + + public function getActivitylogOptions(): LogOptions + { + return LogOptions::defaults() + ->logOnly(['*']) + ->logOnlyDirty() + ->dontSubmitEmptyLogs(); + } + + // Relationships for audit fields + public function creator() + { + return $this->belongsTo(User::class, 'created_by'); + } + + public function updater() + { + return $this->belongsTo(User::class, 'updated_by'); + } + + public function deleter() + { + return $this->belongsTo(User::class, 'deleted_by'); + } +} +``` + +### 5.2 Step 2: Create Master Data Models + +#### 5.2.1 Terminology Model + +```bash +php artisan make:model Terminology +``` + +```php + 'boolean', + ]; + + public function conceptCategories(): HasMany + { + return $this->hasMany(ConceptCategory::class); + } +} +``` + +#### 5.2.2 Province Model + +```bash +php artisan make:model Province +``` + +```php +hasMany(District::class); + } +} +``` + +### 5.3 Step 3: Create Patient Management Models + +#### 5.3.1 Patient Model + +```bash +php artisan make:model Patient +``` + +```php +ulid)) { + $model->ulid = str()->ulid(); + } + }); + } + + // Relationships + public function facility(): BelongsTo + { + return $this->belongsTo(Facility::class); + } + + public function demographics(): HasOne + { + return $this->hasOne(PatientDemographic::class); + } + + public function addresses(): HasMany + { + return $this->hasMany(PatientAddress::class); + } + + public function identities(): HasMany + { + return $this->hasMany(PatientIdentity::class); + } + + public function visits(): HasMany + { + return $this->hasMany(Visit::class); + } + + public function cards(): HasMany + { + return $this->hasMany(Card::class); + } + + // Scopes + public function scopeByFacility($query, $facilityId) + { + return $query->where('facility_id', $facilityId); + } + + public function scopeByCode($query, $code) + { + return $query->where('code', $code); + } +} +``` + +### 5.4 Step 4: Create API Controllers + +#### 5.4.1 Base API Controller + +```bash +php artisan make:controller Api/V1/BaseController +``` + +```php +json($response, $code); + } + + protected function error($message, $code = 400, $errors = null): JsonResponse + { + $response = [ + 'error' => [ + 'message' => $message, + 'code' => $code, + ] + ]; + + if ($errors !== null) { + $response['error']['details'] = $errors; + } + + return response()->json($response, $code); + } +} +``` + +#### 5.4.2 Patient Controller + +```bash +php artisan make:controller Api/V1/PatientController --api +``` + +```php +allowedFilters(['facility_id', 'code']) + ->allowedIncludes(['demographics', 'addresses', 'visits']) + ->allowedSorts(['id', 'code', 'created_at']) + ->defaultSort('-created_at') + ->paginate($request->get('per_page', 15)); + + return PatientResource::collection($patients); + } + + public function store(PatientRequest $request) + { + $patient = Patient::create($request->validated()); + + // Create demographics if provided + if ($request->has('demographics')) { + $patient->demographics()->create($request->demographics); + } + + // Create addresses if provided + if ($request->has('addresses')) { + foreach ($request->addresses as $address) { + $patient->addresses()->create($address); + } + } + + return new PatientResource($patient->load(['demographics', 'addresses'])); + } + + public function show(Patient $patient) + { + return new PatientResource($patient->load([ + 'demographics', + 'addresses', + 'identities', + 'visits' + ])); + } + + public function update(PatientRequest $request, Patient $patient) + { + $patient->update($request->validated()); + + // Update demographics if provided + if ($request->has('demographics')) { + $patient->demographics()->updateOrCreate( + ['patient_id' => $patient->id], + $request->demographics + ); + } + + return new PatientResource($patient->load(['demographics', 'addresses'])); + } + + public function destroy(Patient $patient) + { + $patient->delete(); + + return response()->noContent(); + } + + public function search(Request $request, $query) + { + $patients = Patient::with('demographics') + ->where('code', 'LIKE', "%{$query}%") + ->orWhereHas('demographics', function ($q) use ($query) { + $q->whereRaw("JSON_UNQUOTE(JSON_EXTRACT(name, '$.given[0]')) LIKE ?", ["%{$query}%"]) + ->orWhereRaw("JSON_UNQUOTE(JSON_EXTRACT(name, '$.family')) LIKE ?", ["%{$query}%"]); + }) + ->limit(20) + ->get(); + + return PatientResource::collection($patients); + } +} +``` + +### 5.5 Step 5: Create Form Requests + +```bash +php artisan make:request PatientRequest +``` + +```php + 'required|string|max:255', + 'facility_id' => 'required|integer|exists:facilities,id', + ]; + + if ($this->has('demographics')) { + $rules = array_merge($rules, [ + 'demographics.name' => 'required|array', + 'demographics.name.given' => 'required|array', + 'demographics.name.family' => 'required|string|max:255', + 'demographics.birthdate' => 'required|date', + 'demographics.sex' => 'required|in:Male,Female', + 'demographics.nationality_id' => 'required|integer|exists:terms,id', + 'demographics.telephone' => 'nullable|string|max:20', + ]); + } + + if ($this->has('addresses')) { + $rules = array_merge($rules, [ + 'addresses' => 'array', + 'addresses.*.type_id' => 'required|integer|exists:terms,id', + 'addresses.*.use_id' => 'required|integer|exists:terms,id', + 'addresses.*.line1' => 'nullable|string|max:255', + 'addresses.*.city' => 'nullable|string|max:100', + 'addresses.*.province_id' => 'nullable|integer|exists:provinces,id', + ]); + } + + return $rules; + } +} +``` + +### 5.6 Step 6: Create API Resources + +```bash +php artisan make:resource PatientResource +``` + +```php + $this->id, + 'ulid' => $this->ulid, + 'code' => $this->code, + 'facility_id' => $this->facility_id, + 'demographics' => new PatientDemographicResource($this->whenLoaded('demographics')), + 'addresses' => PatientAddressResource::collection($this->whenLoaded('addresses')), + 'identities' => PatientIdentityResource::collection($this->whenLoaded('identities')), + 'visits' => VisitResource::collection($this->whenLoaded('visits')), + 'created_at' => $this->created_at, + 'updated_at' => $this->updated_at, + ]; + } +} +``` + +### 5.7 Step 7: Run Migrations and Seeders + +```bash +# Run migrations +php artisan migrate + +# Publish permission migrations +php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" + +# Create seeders +php artisan make:seeder DatabaseSeeder +php artisan make:seeder UserSeeder +php artisan make:seeder TerminologySeeder +php artisan make:seeder ProvinceSeeder + +# Run seeders +php artisan db:seed +``` + +--- + +## 6. Testing Strategy + +### 6.1 Setup Testing Environment + +#### 6.1.1 Configure Test Database + +Add to `.env.testing`: + +```env +DB_CONNECTION=sqlite +DB_DATABASE=:memory: +CACHE_DRIVER=array +QUEUE_CONNECTION=sync +SESSION_DRIVER=array +``` + +#### 6.1.2 Create Test Base Class + +```php +seed(); + } + + protected function authenticatedUser() + { + $user = \App\Models\User::factory()->create(); + $this->actingAs($user, 'sanctum'); + return $user; + } +} +``` + +### 6.2 Feature Tests + +#### 6.2.1 Patient API Tests + +```bash +php artisan make:test PatientApiTest +``` + +```php +authenticatedUser(); + + Patient::factory()->count(3)->create(); + + $response = $this->getJson('/api/v1/patients'); + + $response->assertStatus(200) + ->assertJsonStructure([ + 'data' => [ + '*' => ['id', 'ulid', 'code', 'facility_id'] + ] + ]); + } + + public function test_can_create_patient() + { + $this->authenticatedUser(); + + $facility = Facility::factory()->create(); + + $patientData = [ + 'code' => 'PAT001', + 'facility_id' => $facility->id, + 'demographics' => [ + 'name' => [ + 'given' => ['John'], + 'family' => 'Doe' + ], + 'birthdate' => '1990-01-01', + 'sex' => 'Male', + 'nationality_id' => 1, + ] + ]; + + $response = $this->postJson('/api/v1/patients', $patientData); + + $response->assertStatus(201) + ->assertJsonFragment(['code' => 'PAT001']); + + $this->assertDatabaseHas('patients', ['code' => 'PAT001']); + } + + public function test_cannot_create_patient_without_authentication() + { + $response = $this->postJson('/api/v1/patients', []); + + $response->assertStatus(401); + } +} +``` + +### 6.3 Unit Tests + +#### 6.3.1 Model Tests + +```bash +php artisan make:test PatientModelTest --unit +``` + +```php +create(); + + $this->assertNotNull($patient->ulid); + $this->assertEquals(26, strlen($patient->ulid)); + } + + public function test_patient_belongs_to_facility() + { + $facility = Facility::factory()->create(); + $patient = Patient::factory()->create(['facility_id' => $facility->id]); + + $this->assertInstanceOf(Facility::class, $patient->facility); + $this->assertEquals($facility->id, $patient->facility->id); + } + + public function test_scope_by_facility() + { + $facility1 = Facility::factory()->create(); + $facility2 = Facility::factory()->create(); + + Patient::factory()->create(['facility_id' => $facility1->id]); + Patient::factory()->create(['facility_id' => $facility2->id]); + + $patients = Patient::byFacility($facility1->id)->get(); + + $this->assertCount(1, $patients); + $this->assertEquals($facility1->id, $patients->first()->facility_id); + } +} +``` + +### 6.4 Run Tests + +```bash +# Run all tests +php artisan test + +# Run specific test file +php artisan test tests/Feature/PatientApiTest.php + +# Run with coverage +php artisan test --coverage + +# Run in parallel +php artisan test --parallel +``` + +--- + +## 7. Deployment Guide + +### 7.1 Production Environment Setup + +#### 7.1.1 Server Requirements + +**Minimum Specifications:** +- CPU: 2 cores +- RAM: 4GB +- Storage: 50GB SSD +- PHP 8.2+ with extensions: BCMath, Ctype, Fileinfo, JSON, Mbstring, OpenSSL, PDO, Tokenizer, XML + +#### 7.1.2 Production Environment Variables + +```env +APP_ENV=production +APP_DEBUG=false +APP_URL=https://api.emr.example.com + +LOG_CHANNEL=daily +LOG_LEVEL=info + +CACHE_DRIVER=redis +SESSION_DRIVER=redis +QUEUE_CONNECTION=redis + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=secure_password + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=emr_production +DB_USERNAME=emr_user +DB_PASSWORD=secure_database_password + +MAIL_MAILER=smtp +MAIL_HOST=smtp.example.com +MAIL_PORT=587 +MAIL_USERNAME=noreply@emr.example.com +MAIL_PASSWORD=secure_mail_password +MAIL_ENCRYPTION=tls +``` + +### 7.2 Deployment Steps + +#### 7.2.1 Initial Deployment + +```bash +# Clone repository +git clone https://github.com/your-org/emr-backend.git +cd emr-backend + +# Install dependencies +composer install --no-dev --optimize-autoloader + +# Set permissions +sudo chown -R www-data:www-data storage bootstrap/cache +sudo chmod -R 775 storage bootstrap/cache + +# Copy environment file +cp .env.production .env + +# Generate application key +php artisan key:generate + +# Run migrations +php artisan migrate --force + +# Seed essential data +php artisan db:seed --class=ProductionSeeder + +# Cache configuration +php artisan config:cache +php artisan route:cache +php artisan view:cache + +# Create symbolic link for storage +php artisan storage:link +``` + +#### 7.2.2 Web Server Configuration + +**Nginx Configuration:** + +```nginx +server { + listen 80; + listen [::]:80; + server_name api.emr.example.com; + root /var/www/emr-backend/public; + + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-Content-Type-Options "nosniff"; + + index index.php; + + charset utf-8; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_pass unix:/var/run/php/php8.2-fpm.sock; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + } + + location ~ /\.(?!well-known).* { + deny all; + } +} +``` + +### 7.3 SSL Configuration + +```bash +# Install Certbot +sudo apt install certbot python3-certbot-nginx + +# Obtain SSL certificate +sudo certbot --nginx -d api.emr.example.com + +# Auto-renewal +sudo crontab -e +# Add: 0 12 * * * /usr/bin/certbot renew --quiet +``` + +### 7.4 Process Management + +#### 7.4.1 Queue Workers with Supervisor + +Create `/etc/supervisor/conf.d/emr-worker.conf`: + +```ini +[program:emr-worker] +process_name=%(program_name)s_%(process_num)02d +command=php /var/www/emr-backend/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 +autostart=true +autorestart=true +stopasgroup=true +killasgroup=true +user=www-data +numprocs=2 +redirect_stderr=true +stdout_logfile=/var/www/emr-backend/storage/logs/worker.log +stopwaitsecs=3600 +``` + +```bash +sudo supervisorctl reread +sudo supervisorctl update +sudo supervisorctl start emr-worker:* +``` + +### 7.5 Monitoring and Logging + +#### 7.5.1 Log Management + +```bash +# Set up log rotation +sudo nano /etc/logrotate.d/emr-backend + +# Content: +/var/www/emr-backend/storage/logs/*.log { + daily + missingok + rotate 14 + compress + notifempty + create 0640 www-data www-data +} +``` + +#### 7.5.2 Health Checks + +Create monitoring endpoint: + +```php +// routes/web.php +Route::get('/health', function () { + return response()->json([ + 'status' => 'ok', + 'timestamp' => now(), + 'version' => config('app.version'), + 'database' => DB::connection()->getPdo() ? 'connected' : 'disconnected', + 'cache' => Cache::store()->get('health-check') !== null ? 'working' : 'not working' + ]); +}); +``` + +--- + +## 8. Troubleshooting + +### 8.1 Common Issues + +#### 8.1.1 Database Connection Issues + +**Problem:** `SQLSTATE[HY000] [2002] Connection refused` + +**Solutions:** +```bash +# Check MySQL service +sudo systemctl status mysql + +# Restart MySQL +sudo systemctl restart mysql + +# Check port availability +sudo netstat -tlnp | grep :3306 + +# Test connection +mysql -u username -p -h hostname +``` + +#### 8.1.2 Permission Issues + +**Problem:** `The stream or file could not be opened in append mode` + +**Solutions:** +```bash +# Fix storage permissions +sudo chown -R www-data:www-data storage bootstrap/cache +sudo chmod -R 775 storage bootstrap/cache + +# Clear cache +php artisan cache:clear +php artisan config:clear +php artisan route:clear +php artisan view:clear +``` + +#### 8.1.3 Memory Issues + +**Problem:** `Fatal error: Allowed memory size exhausted` + +**Solutions:** +```bash +# Increase PHP memory limit +sudo nano /etc/php/8.2/fpm/php.ini +# Set: memory_limit = 512M + +# Restart PHP-FPM +sudo systemctl restart php8.2-fpm + +# Optimize Composer +composer install --optimize-autoloader --no-dev +``` + +#### 8.1.4 Queue Issues + +**Problem:** Jobs not processing + +**Solutions:** +```bash +# Check queue status +php artisan queue:monitor + +# Restart queue workers +sudo supervisorctl restart emr-worker:* + +# Check failed jobs +php artisan queue:failed + +# Retry failed jobs +php artisan queue:retry all +``` + +### 8.2 Performance Optimization + +#### 8.2.1 Database Optimization + +```bash +# Enable query log temporarily +mysql -u root -p +SET global general_log = 1; +SET global log_output = 'table'; + +# Check slow queries +SELECT * FROM mysql.slow_log; + +# Add indexes for slow queries +php artisan make:migration add_indexes_for_performance +``` + +#### 8.2.2 Caching Optimization + +```bash +# Enable OPcache +sudo nano /etc/php/8.2/fpm/php.ini +# Add: +opcache.enable=1 +opcache.memory_consumption=128 +opcache.max_accelerated_files=4000 + +# Cache configuration +php artisan config:cache +php artisan route:cache +php artisan view:cache +``` + +### 8.3 Debugging Tools + +#### 8.3.1 Enable Debug Mode + +```env +APP_DEBUG=true +LOG_LEVEL=debug +``` + +#### 8.3.2 Laravel Telescope (Development) + +```bash +composer require laravel/telescope --dev +php artisan telescope:install +php artisan migrate +``` + +#### 8.3.3 Query Debugging + +```php +// Enable query logging +DB::enableQueryLog(); + +// Your code here + +// Get queries +dd(DB::getQueryLog()); +``` + +--- + +## 9. Maintenance + +### 9.1 Regular Tasks + +#### 9.1.1 Database Backup + +```bash +#!/bin/bash +# backup.sh +DATE=$(date +%Y%m%d_%H%M%S) +BACKUP_DIR="/var/backups/emr" +mkdir -p $BACKUP_DIR + +mysqldump -u $DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/emr_backup_$DATE.sql +gzip $BACKUP_DIR/emr_backup_$DATE.sql + +# Keep only last 7 days +find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete +``` + +#### 9.1.2 Log Cleanup + +```bash +# Add to crontab +0 2 * * * find /var/www/emr-backend/storage/logs -name "*.log" -mtime +30 -delete +``` + +### 9.2 Updates and Deployment + +#### 9.2.1 Zero-Downtime Deployment + +```bash +#!/bin/bash +# deploy.sh + +# Pull latest code +git pull origin main + +# Install dependencies +composer install --no-dev --optimize-autoloader + +# Run migrations +php artisan migrate --force + +# Clear and cache +php artisan config:cache +php artisan route:cache +php artisan view:cache + +# Restart services +sudo supervisorctl restart emr-worker:* +sudo systemctl reload php8.2-fpm +``` + +--- + +This implementation guide provides a comprehensive roadmap for building the EMR Backend System using Laravel 12, ensuring scalability, security, and maintainability for Cambodia's healthcare infrastructure. \ No newline at end of file diff --git a/SOFTWARE_REQUIREMENTS_SPECIFICATION.md b/SOFTWARE_REQUIREMENTS_SPECIFICATION.md new file mode 100644 index 0000000..949c795 --- /dev/null +++ b/SOFTWARE_REQUIREMENTS_SPECIFICATION.md @@ -0,0 +1,727 @@ +# Software Requirements Specification (SRS) +# EMR Backend System for Cambodia Healthcare + +## Version Information +- **Version**: 1.0 +- **Date**: 2025 +- **Target Framework**: Laravel 12 +- **Language**: PHP 8.2+ +- **Database**: MySQL/PostgreSQL + +--- + +## Table of Contents + +1. [Introduction & Overview](#1-introduction--overview) +2. [System Architecture](#2-system-architecture) +3. [Functional Requirements](#3-functional-requirements) +4. [Technical Requirements](#4-technical-requirements) +5. [Implementation Guidelines](#5-implementation-guidelines) +6. [Data Models Specification](#6-data-models-specification) +7. [Security Requirements](#7-security-requirements) +8. [Performance Requirements](#8-performance-requirements) +9. [Cambodia-Specific Requirements](#9-cambodia-specific-requirements) + +--- + +## 1. Introduction & Overview + +### 1.1 System Purpose and Scope + +The Electronic Medical Record (EMR) Backend System is designed to serve Cambodia's healthcare infrastructure, providing a comprehensive digital platform for managing patient records, clinical workflows, and healthcare operations. The system supports the full spectrum of healthcare delivery from primary care clinics to tertiary hospitals. + +### 1.2 Target Audience + +**Primary Users:** +- Healthcare providers (doctors, nurses, medical assistants) +- Administrative staff (registration, billing, records management) +- Healthcare facility administrators +- System administrators +- Integration developers (third-party systems) + +**Secondary Users:** +- Patients (limited access via patient portals) +- Government health officials (reporting and analytics) +- Insurance providers (claims processing) + +### 1.3 Technology Stack Specifications + +**Backend Framework:** +- Laravel 12.x +- PHP 8.2 or higher +- Composer for dependency management + +**Database:** +- Primary: MySQL 8.0+ or PostgreSQL 14+ +- Redis for caching and sessions +- Full-text search capabilities + +**Authentication & Authorization:** +- Laravel Sanctum for API authentication +- Laravel Passport for OAuth2 (optional) +- Spatie Laravel Permission for role-based access control + +**Additional Packages:** +- Spatie Laravel Activity Log for audit trails +- Spatie Laravel Query Builder for API filtering +- Dedoc Scramble for API documentation +- Laravel Socialite for social authentication + +### 1.4 System Boundaries + +**Included:** +- Patient registration and demographics management +- Clinical workflow management +- Medication management +- Laboratory and imaging orders +- Billing and invoicing +- Reporting and analytics +- Master data management +- User management and authentication + +**Excluded:** +- Medical device integrations (HL7/FHIR - separate module) +- Payment processing gateways +- External laboratory system integrations +- Telemedicine capabilities + +--- + +## 2. System Architecture + +### 2.1 Overall System Design + +The EMR system follows a modular, service-oriented architecture built on Laravel's MVC pattern with the following architectural principles: + +- **RESTful API Design**: All client interactions via HTTP REST API +- **Domain-Driven Design**: Business logic organized by healthcare domains +- **Event-Driven Architecture**: Asynchronous processing for non-critical operations +- **Multi-tenancy**: Support for multiple healthcare facilities +- **Microservices Ready**: Modular structure for future service extraction + +### 2.2 Laravel 12 Framework Requirements + +**Core Components:** +- Eloquent ORM for database abstraction +- Laravel Artisan CLI for system management +- Laravel Queue for background job processing +- Laravel Cache for performance optimization +- Laravel Validation for input validation +- Laravel Middleware for request filtering + +**Required Laravel Features:** +- Database migrations and seeders +- Model factories for testing +- API resources for data transformation +- Form requests for validation +- Service providers for dependency injection +- Event listeners for business logic decoupling + +### 2.3 Database Design Specifications + +**Database Architecture:** +- Normalized relational database design +- Soft delete support for all clinical data +- Complete audit trail tracking (created_by, updated_by, deleted_by) +- Timestamp tracking for all operations +- Foreign key constraints for referential integrity +- Indexing strategy for performance optimization + +**Common Field Pattern:** +All primary business entities include: +```php +$table->timestamp('created_at')->useCurrent()->index(); +$table->foreignId('created_by')->nullable()->index(); +$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate()->index(); +$table->foreignId('updated_by')->nullable()->index(); +$table->softDeletes($column = 'deleted_at', $precision = 0)->index(); +$table->foreignId('deleted_by')->nullable()->index(); +``` + +### 2.4 API Architecture Patterns + +**RESTful Design Principles:** +- Resource-based URLs (`/api/v1/patients`, `/api/v1/visits`) +- HTTP methods for operations (GET, POST, PUT, DELETE) +- JSON request/response format +- Consistent error response structure +- API versioning support (v1, v2, etc.) + +**Authentication Pattern:** +- Bearer token authentication via Laravel Sanctum +- Device-specific tokens for mobile applications +- Token expiration and refresh mechanisms +- Rate limiting for API protection + +**Response Standardization:** +```json +{ + "data": {}, + "meta": { + "current_page": 1, + "total": 100 + }, + "links": { + "first": "url", + "last": "url" + } +} +``` + +--- + +## 3. Functional Requirements + +### 3.1 Master Data Management + +**FR-MD-001: Terminology Management** +- System shall manage medical terminologies and coding systems +- Support for ICD-10, ICD-11, and Cambodia-specific medical codes +- Hierarchical concept organization with parent-child relationships +- Multi-language support (English, Khmer) +- Version control for terminology updates + +**FR-MD-002: Geographic Data Management** +- Complete Cambodia administrative hierarchy: Province → District → Commune → Village +- Gazetteer support for geographic locations +- Address standardization and validation +- GPS coordinate support for facilities + +**FR-MD-003: Healthcare Facility Management** +- Organization hierarchy support +- Facility registration and licensing tracking +- Department and room management +- Service capability definition +- Operating hours and capacity management + +### 3.2 Patient Management + +**FR-PM-001: Patient Registration** +- Unique patient identification across the system +- Support for multiple identifier types (National ID, Passport, etc.) +- Patient demographics with full name support (Given, Family, Middle) +- Contact information management (phone, email, address) +- Emergency contact registration + +**FR-PM-002: Patient Demographics** +- Birth date and age calculation +- Gender/sex designation +- Nationality and ethnicity tracking +- Language preferences +- Marital status and occupation +- Insurance information + +**FR-PM-003: Patient Identity Management** +- Multiple identity document support +- Identity verification workflows +- Duplicate patient detection and merging +- Patient card issuance and management +- Photo identification support + +### 3.3 Clinical Workflow Management + +**FR-CW-001: Visit Management** +- Patient visit registration and scheduling +- Admission type classification (Emergency, Scheduled, etc.) +- Visit status tracking (Active, Discharged, Transferred) +- Discharge planning and documentation +- Visit outcome recording + +**FR-CW-002: Encounter Management** +- Clinical encounter documentation +- Provider assignment and tracking +- Encounter type classification +- Service location recording +- Time and duration tracking + +**FR-CW-003: Clinical Observations** +- Vital signs recording (Blood pressure, Temperature, etc.) +- Laboratory result documentation +- Clinical assessment notes +- Diagnosis and condition tracking +- Progress note documentation + +### 3.4 Medication Management + +**FR-MM-001: Medication Ordering** +- Electronic prescription creation +- Drug interaction checking +- Allergy alerts and contraindications +- Dosage calculation and validation +- Prescription modification tracking + +**FR-MM-002: Medication Administration** +- Medication dispensing workflows +- Administration recording and verification +- Medication reconciliation +- Adverse reaction reporting +- Inventory management integration + +### 3.5 Diagnostic Services + +**FR-DS-001: Laboratory Services** +- Laboratory test ordering +- Specimen collection tracking +- Result reporting and interpretation +- Reference range validation +- Critical value alerts + +**FR-DS-002: Imaging Services** +- Radiology order management +- Image study tracking +- Report generation and approval +- DICOM image reference support +- Comparison with previous studies + +### 3.6 Financial Management + +**FR-FM-001: Billing and Invoicing** +- Service charge calculation +- Insurance claim generation +- Payment processing integration +- Revenue reporting +- Accounts receivable management + +**FR-FM-002: Cost Management** +- Service pricing management +- Insurance plan configuration +- Copayment calculation +- Financial assistance programs +- Cost reporting and analytics + +--- + +## 4. Technical Requirements + +### 4.1 Database Schema Specifications + +**TR-DB-001: Entity Relationship Design** +All entities must follow standardized relationship patterns: +- Primary keys as auto-incrementing integers +- ULID support for external references +- Foreign key constraints with appropriate cascade rules +- Junction tables for many-to-many relationships +- JSON columns for flexible data structures + +**TR-DB-002: Data Integrity Requirements** +- NOT NULL constraints on required fields +- CHECK constraints for data validation +- Unique constraints on business keys +- Default values for system fields +- Proper indexing for query performance + +### 4.2 API Endpoint Definitions + +**TR-API-001: Authentication Endpoints** +``` +POST /api/v1/login +POST /api/v1/register +POST /api/v1/logout +GET /api/v1/user +POST /api/v1/refresh +``` + +**TR-API-002: Patient Management Endpoints** +``` +GET /api/v1/patients +POST /api/v1/patients +GET /api/v1/patients/{id} +PUT /api/v1/patients/{id} +DELETE /api/v1/patients/{id} +GET /api/v1/patients/search/{query} +``` + +**TR-API-003: Clinical Workflow Endpoints** +``` +GET /api/v1/visits +POST /api/v1/visits +GET /api/v1/visits/{id} +PUT /api/v1/visits/{id} +POST /api/v1/visits/{id}/discharge +GET /api/v1/encounters +POST /api/v1/encounters +``` + +### 4.3 Authentication and Authorization + +**TR-AUTH-001: Token-Based Authentication** +- Laravel Sanctum implementation +- Device-specific token generation +- Token expiration management (24 hours default) +- Refresh token support +- Multi-device login support + +**TR-AUTH-002: Role-Based Access Control** +- Spatie Laravel Permission implementation +- Role hierarchy support +- Permission-based access control +- Context-aware permissions (facility-specific) +- Dynamic permission checking + +### 4.4 Data Models and Relationships + +**TR-DM-001: Model Structure Requirements** +- All models extend base Model class +- Implement HasFactory trait for testing +- Use SoftDeletes trait for clinical data +- Define fillable fields explicitly +- Implement proper relationship methods + +**TR-DM-002: Relationship Patterns** +- BelongsTo for parent relationships +- HasMany for child collections +- BelongsToMany for many-to-many +- MorphMany for polymorphic relationships +- HasManyThrough for indirect relationships + +--- + +## 5. Implementation Guidelines + +### 5.1 Coding Standards + +**IG-CS-001: PSR Compliance** +- PSR-4 autoloading standard +- PSR-12 extended coding style +- Laravel coding conventions +- Consistent naming conventions +- Proper documentation standards + +**IG-CS-002: Code Organization** +- Controllers in app/Http/Controllers/V1/ +- Models in app/Models/ +- Resources in app/Http/Resources/ +- Requests in app/Http/Requests/ +- Services in app/Services/ + +### 5.2 Testing Requirements + +**IG-TR-001: Test Coverage** +- Unit tests for all business logic +- Feature tests for API endpoints +- Model factory definitions +- Database seeder implementations +- Integration test scenarios + +**IG-TR-002: Test Structure** +- PHPUnit for test framework +- Feature tests in tests/Feature/ +- Unit tests in tests/Unit/ +- Database testing with transactions +- Mock external dependencies + +### 5.3 Security Considerations + +**IG-SC-001: Input Validation** +- Validate all user inputs +- Sanitize data before storage +- Use Laravel validation rules +- Implement custom validation rules +- Error message standardization + +**IG-SC-002: Data Protection** +- Encrypt sensitive patient data +- Implement audit logging +- Use secure password hashing +- HTTPS enforcement +- SQL injection prevention + +### 5.4 Performance Requirements + +**IG-PR-001: Database Optimization** +- Proper indexing strategy +- Query optimization +- Eager loading for relationships +- Database query monitoring +- Connection pooling + +**IG-PR-002: Caching Strategy** +- Redis for session storage +- Cache frequently accessed data +- API response caching +- Database query caching +- Cache invalidation strategies + +--- + +## 6. Data Models Specification + +### 6.1 Core Entity Definitions + +**Patient Entity** +```php +class Patient extends Model +{ + protected $fillable = ['code', 'facility_id']; + + // Relationships + public function facility(): BelongsTo + public function demographics(): HasOne + public function addresses(): HasMany + public function identities(): HasMany + public function visits(): HasMany + public function cards(): HasMany +} +``` + +**Visit Entity** +```php +class Visit extends Model +{ + protected $fillable = [ + 'patient_id', 'facility_id', 'visit_type_id', + 'admission_type_id', 'admitted_at', 'discharged_at', + 'discharge_type_id', 'visit_outcome_id' + ]; + + protected $casts = [ + 'admitted_at' => 'datetime', + 'discharged_at' => 'datetime' + ]; +} +``` + +### 6.2 Relationship Mappings + +**Patient Relationships** +- Patient → Facility (BelongsTo) +- Patient → Demographics (HasOne) +- Patient → Addresses (HasMany) +- Patient → Identities (HasMany) +- Patient → Visits (HasMany) +- Patient → Cards (HasMany) + +**Visit Relationships** +- Visit → Patient (BelongsTo) +- Visit → Facility (BelongsTo) +- Visit → Encounters (HasMany) +- Visit → Caretakers (BelongsToMany) +- Visit → Subjects (BelongsToMany) + +### 6.3 Field Specifications with Types and Constraints + +**Patients Table** +```sql +CREATE TABLE patients ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + ulid CHAR(26) NOT NULL, + code VARCHAR(255) NOT NULL, + facility_id BIGINT UNSIGNED NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_patients_ulid (ulid), + INDEX idx_patients_code (code), + INDEX idx_patients_facility_id (facility_id), + FOREIGN KEY (facility_id) REFERENCES facilities(id) +); +``` + +**Patient Demographics Table** +```sql +CREATE TABLE patient_demographics ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + patient_id BIGINT UNSIGNED NOT NULL, + name JSON NOT NULL, + birthdate DATE NOT NULL, + telecom JSON NULL, + address JSON NULL, + sex ENUM('Female', 'Male') NOT NULL, + nationality_id BIGINT UNSIGNED NOT NULL, + telephone VARCHAR(20) NULL, + died_at DATETIME NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + created_by BIGINT UNSIGNED NULL, + updated_at TIMESTAMP NULL ON UPDATE CURRENT_TIMESTAMP, + updated_by BIGINT UNSIGNED NULL, + deleted_at TIMESTAMP NULL, + deleted_by BIGINT UNSIGNED NULL, + + INDEX idx_demographics_patient_id (patient_id), + INDEX idx_demographics_birthdate (birthdate), + INDEX idx_demographics_sex (sex), + INDEX idx_demographics_nationality_id (nationality_id), + INDEX idx_demographics_telephone (telephone), + INDEX idx_demographics_died_at (died_at), + FOREIGN KEY (patient_id) REFERENCES patients(id) +); +``` + +### 6.4 Business Rules for Each Entity + +**Patient Business Rules** +- Each patient must be associated with a primary facility +- Patient code must be unique within the facility +- ULID must be globally unique +- Patient cannot be hard deleted if visits exist + +**Visit Business Rules** +- Admitted date cannot be in the future +- Discharged date must be after admitted date +- Only one active visit per patient per facility +- Visit type and admission type are required +- Discharge type required only when discharged + +**Encounter Business Rules** +- Must be associated with an active visit +- Cannot extend beyond visit date range +- Practitioner must have appropriate privileges +- Service location must be valid for the facility + +--- + +## 7. Security Requirements + +### 7.1 Authentication Security + +**SR-AUTH-001: Password Security** +- Minimum 8 characters with complexity requirements +- Password hashing using Laravel's Hash facade +- Password history tracking (last 5 passwords) +- Account lockout after failed attempts +- Password expiration policies + +**SR-AUTH-002: Session Management** +- Secure session configuration +- Session timeout after inactivity +- Single sign-on support +- Device registration and tracking +- Logout from all devices capability + +### 7.2 Data Security + +**SR-DATA-001: Encryption** +- Encrypt patient identifiable information +- Database encryption at rest +- TLS encryption in transit +- Key management and rotation +- Backup encryption + +**SR-DATA-002: Access Control** +- Role-based access control implementation +- Principle of least privilege +- Context-aware permissions +- Audit trail for all access +- Permission inheritance rules + +### 7.3 Compliance Requirements + +**SR-COMP-001: Healthcare Data Protection** +- HIPAA-equivalent privacy protections +- Data anonymization capabilities +- Patient consent management +- Data retention policies +- Right to be forgotten implementation + +--- + +## 8. Performance Requirements + +### 8.1 Response Time Requirements + +**PR-RT-001: API Response Times** +- Patient search: < 200ms for simple queries +- Patient registration: < 500ms +- Visit creation: < 300ms +- Clinical data entry: < 200ms +- Report generation: < 2 seconds + +### 8.2 Scalability Requirements + +**PR-SCALE-001: System Capacity** +- Support 10,000 concurrent users +- Handle 1 million patient records +- Process 100,000 visits per day +- Store 10 years of clinical data +- 99.9% uptime availability + +### 8.3 Database Performance + +**PR-DB-001: Query Performance** +- All queries under 100ms average +- Proper indexing on search fields +- Connection pooling implementation +- Query caching for static data +- Database monitoring and alerting + +--- + +## 9. Cambodia-Specific Requirements + +### 9.1 Language Support + +**CSR-LANG-001: Multilingual Implementation** +- Primary language: Khmer +- Secondary language: English +- Unicode support for Khmer text +- Locale-specific date/time formatting +- Currency formatting (Riel/USD) + +### 9.2 Regulatory Compliance + +**CSR-REG-001: Healthcare Regulations** +- Ministry of Health guidelines compliance +- National health information standards +- Medical practice regulations +- Pharmaceutical regulations +- Quality assurance requirements + +### 9.3 Administrative Structure + +**CSR-ADMIN-001: Geographic Organization** +- 25 Provinces (Khet) +- 162 Districts (Srok/Khan) +- 1,652 Communes (Khum/Sangkat) +- 14,073 Villages (Phum) +- Administrative boundary validation + +### 9.4 Healthcare System Integration + +**CSR-HEALTH-001: National Integration** +- Health facility licensing integration +- Practitioner registration validation +- National patient identifier support +- Disease surveillance reporting +- Health statistics compilation + +--- + +## 10. Appendices + +### 10.1 Glossary of Terms + +**Clinical Terms:** +- **EMR**: Electronic Medical Record +- **EHR**: Electronic Health Record +- **HL7**: Health Level 7 International +- **FHIR**: Fast Healthcare Interoperability Resources +- **ICD**: International Classification of Diseases + +**Technical Terms:** +- **API**: Application Programming Interface +- **REST**: Representational State Transfer +- **CRUD**: Create, Read, Update, Delete +- **ORM**: Object-Relational Mapping +- **MVC**: Model-View-Controller + +### 10.2 References + +- Laravel 12 Documentation: https://laravel.com/docs +- Cambodia Administrative Divisions +- WHO Health Information Standards +- HL7 FHIR Specification +- Healthcare Data Security Guidelines + +### 10.3 Document Control + +| Version | Date | Author | Changes | +|---------|------|--------|---------| +| 1.0 | 2025 | Development Team | Initial version | + +--- + +*This document serves as the comprehensive technical specification for implementing the EMR Backend System using Laravel 12 framework, specifically designed for Cambodia's healthcare infrastructure.* \ No newline at end of file