# Coding Standards

## PHP Standards

- **PHP Version**: ^8.2
- **PSR-12** coding style enforced by Laravel Pint
- **Strict types** declared in route files but NOT in most PHP classes

## Naming Conventions

| Type | Convention | Example |
|------|-----------|---------|
| Classes | PascalCase | `MeetingController` |
| Methods | snake_case (controller actions) | `create_meeting`, `meeting_otp_send` |
| Methods | camelCase (service/utility methods) | `sendOtp`, `formatMeetingResponse` |
| Variables | camelCase | `$meetingDate`, `$userName` |
| Database tables | snake_case plural | `lead_follow_ups`, `meeting_schedules` |
| Database columns | snake_case | `is_arrived`, `otp_expires_at` |
| Route paths | kebab-case | `/meeting/otp/send`, `/lead/bulk/transfer` |
| Foreign keys | `singular_id` | `lead_id`, `created_by` |
| Pivot tables | singular alphabetical | `meeting_product`, `demo_assignee` |
| Permissions | dot-notation | `module.sub_module.action` |
| Migrations | `YYYY_MM_DD_HHMMSS_description` | `2026_03_12_125411_create_meetings_table.php` |

## Controller Conventions

- Controller methods use snake_case (e.g., `create_lead`, `fetch_all_leads`)
- Routes follow `POST /{module}/{action}` pattern (NOT RESTful resources)
- Private helpers use camelCase (e.g., `formatMeetingResponse`, `send_otp`)
- Inline validation via `$request->validate()` (despite Form Request recommendation)
- Responses use `ApiResponse` trait: `$this->success()`, `$this->error()`
- Each controller action returns `JsonResponse`

## Request Validation

- **Preferred**: Form Request classes (only `UpsertUserActivityKpiRequest` exists)
- **Actual**: Inline `$request->validate([...])` in most controllers
- Validation rules are defined as arrays with pipe or array syntax
- Common patterns: `['required', 'integer', 'exists:table,column']`
- File uploads: `['required', 'file', 'mimes:pdf,doc,jpg', 'max:5120']`

## Service Layer

- Business logic SHOULD be in Service classes (9 exist)
- SERVICES often use `DB::transaction()` for atomicity
- Services are injected via constructor DI or `app()` helper
- Common service pattern: work with models, use DB transactions, return arrays

**Current gap**: Many controllers contain business logic that should be in services (especially large controllers like `MeetingController` at 1390 lines and `LeadController`).

## API Response Format

**Success:**
```json
{
    "success": true,
    "message": "Operation successful",
    "data": { ... }
}
```

**Error:**
```json
{
    "success": false,
    "message": "Error description",
    "errors": { ... }
}
```

**Unauthenticated:**
```json
{
    "success": false,
    "message": "Unauthenticated."
}
```

## Error Handling

- Authentication errors: Rendered in `bootstrap/app.php` as JSON
- Validation errors: Laravel's default `422` with `errors` key
- Business errors: `$this->error('message', 4xx)`
- Server errors: `500` with error message in `catch` blocks
- NOT using Laravel's exception handler for business logic errors

## Database Transactions

- Used in critical operations: Creates, updates spanning multiple tables
- Pattern: `DB::beginTransaction()` -> operations -> `DB::commit()` in try, `DB::rollBack()` in catch
- Examples: Lead creation (lead + phones + emails), meeting creation (meeting + schedules + products)

## Performance Practices

- **Eager loading**: Used in many queries (`->with(['lead', 'active_user'])`)
- **Scopes**: Defined on models for common filters (`scopeForUser`, `scopeForDate`)
- **Paginated responses**: Used for list endpoints (pagination params from request)
- **Caching**: Google Calendar tokens cached (1h TTL), OTP cached for registration
- **N+1 prevention**: Some controllers still have N+1 issues

## Conventions Observed in Codebase

1. **Mixed naming**: Methods use snake_case for controllers but camelCase for private helpers
2. **Inline validation**: Preferred over Form Requests in practice
3. **Direct model operations**: Many controllers use `Model::create()`, `Model::update()` inline
4. **Try-catch in controllers**: Most controller actions wrap logic in try-catch
5. **Hardcoded pagination**: Page/per_page extracted from request, pagination via `paginate()`
6. **Activity logging**: `activity()->performedOn()->causedBy()->log()` after key operations
7. **No API Resources**: Only `UserActivityKpiResource` exists; controllers format responses manually
8. **Raw cURL in controllers**: OTP sending via cURL inside `MeetingController`
9. **No Mail/Notification classes**: Notifications handled via `PushNotificationService` directly, not Laravel notifications

## What Should NOT Be Changed Without Understanding

- **OTP logic** - Spans 3 controllers with similar but not identical implementations
- **Permission system** - PermissionCatalog drives all authorization; changing naming convention breaks everything
- **Lead status transitions** - Raw -> Verified -> Client conversion pipeline
- **Meeting status flow** - Specific order: arrived -> started -> OTP -> completed
- **Attendance flags** - Boolean flags are mutually exclusive; logic depends on this
- **Recurring task types** - Daily/weekly/fortnightly/monthly/quarterly/half-yearly/yearly
- **SMS API integration** - Direct cURL to external provider; auth params are environment-based

## Testing

- **Framework**: Pest PHP v4
- **Test suites**: Unit + Feature
- **Database**: SQLite :memory: for tests
- **Config**: `phpunit.xml` sets testing env vars
- **Current state**: Only template test files exist (tests/Feature/, tests/Unit/)
