# CHM ERP Controller Map

Reference documentation for all HTTP controllers in `app/Http/Controllers/`. Generated from controller source and route analysis only (views, Livewire, and middleware internals not fully documented).

**Total controllers:** 77 (including 9 Auth controllers and base `Controller.php`)

**Route files:** `routes/web.php`, `routes/auth.php`, `routes/api.php` (minimal)

---

## Architecture Overview

| Pattern | Observation |
|---------|-------------|
| **Routing style** | Mostly legacy string/action routes via `Route::controller()` groups; newer accounting modules use REST-style named routes |
| **Authorization** | Admin area wrapped in `auth` + `roles:admin`; per-route Spatie `permission:*` middleware |
| **Validation** | Almost exclusively inline `$request->validate()` or `Validator::make()`; only 2 Form Request classes exist |
| **Services** | 3 app services (`PostingService`, `SmsService`, `CoaService`); only Posting and SMS are used from controllers |
| **Fat controllers** | Business logic, GL posting, stock FIFO, and reporting queries live directly in controllers — especially `GrnController` (~2,090 lines) and `AdminController` (~970 lines) |
| **Dual modules** | Legacy Hope Coffee controllers overlap with accounting-module controllers (advances, sales, purchases) |

### Middleware Context

| Area | Middleware |
|------|------------|
| Admin ERP | `auth`, `roles:admin`, plus route-level `permission:*` |
| Accounting submodule | Additional `permission:accounting.access` on route group |
| Job portal (applicant) | `auth:applicant` |
| Breeze profile/auth | `auth` / `guest` (standard web guard) |
| Admin login | `/admin/login` via `AdminController` (separate from Breeze `/login`) |

---

## Service Dependency Map

```
PostingService
├── GrnController          → post('payment_voucher', …)
├── HopeCoffeeController   → post('advance_to_farmer', …)
├── CoffeeSaleController   → post('coffee_sale', …)
└── PayrollController      → post('payroll', …)

SmsService
├── GrnController          → farmer notification after voucher save
└── SmsController          → manual / bulk SMS

CoaService
└── (not used by any controller)
```

**Note:** `ArPaymentController` injects `PostingService` but does not call it. Many accounting controllers post journal entries inline instead of using `PostingService`.

---

## Known Route ↔ Method Mismatches

Routes registered in `routes/web.php` that reference **missing controller methods** (will 404 at runtime):

| Controller | Missing Method(s) | Route(s) |
|------------|-------------------|----------|
| `AdminController` | `getFilteredData` | `GET /get-filtered-data` |
| `ChildController` | `ChildProfileStore` | `POST /child/profile/store` |
| `ClusterController` | `AddCluster` | `GET /add/cluster` |
| `ChurchController` | `AddChurch`, `AddPastor` | `GET /add/church`, `GET /add/pastor` |
| `LetterProgressController` | `createLetterProgress` | `GET /letter-progress/create` |
| `GiftController` | `createGift`, `show`, `update`, donation `update`/`destroy` | several `/gift/*`, `/donation/*` routes |
| `GeneralReportsController` | `cfdpReportGenerate`, `trReportGenerate` | POST report generate routes |
| `GrnController` | `getGrnId`, `AddGrnOld`, `AddGrnStoreOld` | GRN helper and legacy routes |
| `CoffeeProcessingController` | `getStockAsOfDate` | `GET /coffee/available-stock-date/{date}` |
| `HopeCoffeeController` | `EditAdvance`, `UpdateAdvance`, `DeleteAdvance` | advance edit/delete routes |
| `UserController` | `AddUserStore` | `POST /add/user` (duplicate of `store.user`) |
| `ApplicantApplicationController` | `show` | `GET portal/applications/{application}` |
| `BankReconciliationController` | `reconcile`, `store` | bank-reconciliation POST routes |
| `ReportController` | `showUploadForm`, `uploadReport`, `manageReports`, `deleteReport`, `downloadReport` | admin report upload/manage routes |
| `TaxProvisionController` | `show` | `GET /tax/provision/{provision}` |
| `ReportTypeController` | `show`, `edit`, `update`, `destroy` | resource routes beyond index/create/store |
| `CoffeeSaleController` | `rollbackSale` (private helper called by `rollback`) | — internal bug |
| `FarmerController` | `AddGroup` | `GET /add/group` |

**Unreachable controller:** `ForeignJournalController` has no registered routes (overlaps with `FxJournalController`).

**Dead controller:** `DashboardController` has no routes; superseded by `AdminController::AdminDashboard`.

**Empty stub:** `CountryController` — country CRUD lives in `SponsorController`.

---

## Cross-Cutting Refactoring Themes

| Priority | Theme | Affected Controllers |
|----------|-------|---------------------|
| 🔴 High | Fix missing route handler methods | See table above |
| 🔴 High | Dual application models (`Application` vs `ApplicantPosition`) | `PortalApplyController`, `ApplicantApplicationController` |
| 🔴 High | AR/AP data model mismatch (journal vouchers vs `ar_payments`/`ap_payments` tables) | `ArPaymentController`, `ArAgingController`, `ApPaymentController`, `CashFlowController` |
| 🔴 High | Advance repayment without balance/journal updates | `AdvancePaymentController`, `HopeCoffeeController` |
| 🟡 Medium | Extract ~1,500 lines duplicated payment voucher logic | `GrnController` |
| 🟡 Medium | FIFO stock deduction duplicated | `StockTransferController`, `CoffeeProcessingController`, `CoffeeSaleController` |
| 🟡 Medium | Hardcoded GL account/parent IDs (`5349`, `5423`, `1012`, etc.) | Accounting controllers |
| 🟡 Medium | Split mega-controllers | `AdminController`, `GrnController`, `JournalEntryController`, `ReportController` |
| 🟢 Low | Introduce Form Request classes | Nearly all controllers |
| 🟢 Low | Standardize method naming (PascalCase vs camelCase) | CFDP legacy controllers |
| 🟢 Low | Replace GET delete routes with DELETE + policies | Many CFDP controllers |

---

# Controllers by Domain

---

## 1. Admin & Authentication

### AdminController

| | |
|---|---|
| **File** | `app/Http/Controllers/AdminController.php` |
| **Responsibilities** | Admin login/logout, password reset, profile management, and the main admin dashboard aggregating CFDP sponsorship metrics, farmer/transformation stats, GeoJSON map data, and accounting KPIs |
| **Services** | None |
| **Models** | `User`, `Children`, `Cluster`, `Account`, `Sponsorship`, `Farmer`, `Group`, `District`, `PaymentVoucher`, `Invoice`, `VendorBill`, `JournalEntryLine` |

**Routes handled**

| Method | Path | Route Name |
|--------|------|------------|
| `AdminDashboard` | `GET /admin/dashboard` | `admin.dashboard` |
| `AdminLogout` | `GET /admin/logout` | `admin.logout` |
| `AdminProfile` | `GET /admin/profile` | `admin.profile` |
| `AdminProfileStore` | `POST /admin/profile/store` | `admin.profile.store` |
| `AdminChangePassword` | `GET /admin/change/password` | `admin.change.password` |
| `AdminPasswordUpdate` | `POST /admin/password/update` | `admin.password.update` |
| `getFilteredData` ⚠️ | `GET /get-filtered-data` | *(none)* |
| `AdminLogin` | `GET /admin/login` | `admin.login` |
| `showForgotPasswordForm` | `GET admin/forgot-password` | `admin.password.request` |
| `sendPasswordResetLink` | `POST admin/forgot-password` | `admin.password.email` |
| `showResetForm` | `GET admin/reset-password/{token}` | `admin.password.reset` |
| `resetPassword` | `POST admin/reset-password` | `admin.password.change` |

**Validation:** Inline `$request->validate()` for password flows; `Validator::make()` for reset; profile store has no validation.

**Business workflow**
1. Dashboard loads multi-domain aggregates (children by status/country/cluster, farmer demographics, sponsorship trends, revenue/expense/AR/AP/cash KPIs).
2. Profile store updates user fields and optional photo upload.
3. Password reset uses cache throttling, token storage, and Laravel `Password` facade.

**Refactoring opportunities:** Extract dashboard queries to `DashboardService`; fix missing `getFilteredData`; remove hardcoded country/account IDs; deduplicate query blocks; overlap with unused `DashboardController`.

---

### DashboardController *(dead)*

| | |
|---|---|
| **Responsibilities** | Legacy stub counting active sponsorships only |
| **Routes** | None registered |
| **Services** | None |
| **Models** | `Children` |
| **Validation** | None |
| **Workflow** | Counts `id_status=1` children → renders incomplete `admin.index` view |
| **Refactoring** | Remove or merge into `AdminController` |

---

### ProfileController

| | |
|---|---|
| **Responsibilities** | Breeze-style web-guard profile edit, update, account deletion |
| **Routes** | `GET/PATCH/DELETE /profile` → `profile.edit/update/destroy` |
| **Services** | None |
| **Models** | `User` |
| **Validation** | `ProfileUpdateRequest`; current password on delete |
| **Workflow** | Standard Laravel Breeze profile management |
| **Refactoring** | Overlaps with `AdminController::AdminProfile` for admin users |

---

### Auth Controllers (`app/Http/Controllers/Auth/*`)

Standard Laravel Breeze scaffolding from `routes/auth.php`. Admin users primarily use `/admin/login` via `AdminController`.

| Controller | Routes | Validation | Notes |
|------------|--------|------------|-------|
| `AuthenticatedSessionController` | login, logout | `LoginRequest` | Redirects admin role to `/admin/dashboard` |
| `RegisteredUserController` | register | name, email, password rules | Public registration may be unwanted for ERP |
| `PasswordResetLinkController` | forgot-password | email | Parallel to admin reset flow |
| `NewPasswordController` | reset-password | token, email, password | |
| `EmailVerificationPromptController` | verify-email | — | Invokable |
| `VerifyEmailController` | verify-email/{id}/{hash} | signed, throttled | Invokable |
| `EmailVerificationNotificationController` | resend verification | throttled | |
| `ConfirmablePasswordController` | confirm-password | password check | |
| `PasswordController` | PUT password | current + new password | |

---

## 2. CFDP (Child & Family Development Program)

### ChildController

| | |
|---|---|
| **Responsibilities** | Full child lifecycle: registration, profile, status/sponsorship changes, periodic field updates, graduation/mid-term tracking, sponsorship extension, notifications |
| **Services** | None |
| **Models** | `Children`, `Cluster`, `Country`, `Sponsor`, `Guardian`, `School`, `ChildStatus`, `Sponsorship`, `CStatus`, `ChildUpdate`, `User` |

**Key routes:** `/all/children`, `/add/child`, `/child/{id}/profile`, edit/update, status change, graduation/midsponsorship lists, child updates CRUD, extend sponsorship, notifications.

**Validation:** Inline `$request->validate()` with conditional rules per status type on status update.

**Business workflow**
1. **Register:** Generate `{COUNTRY}-{NNN}-{CLUSTER}` number → save child → notify permitted users.
2. **Status change:** Termination clears sponsor links; Sponsored links sponsor and sets graduation (+8yr) / mid-term (+4yr) dates.
3. **Graduation:** PDF upload → mark graduated → archive sponsorship → notify.
4. **Child updates:** Periodic field updates synced to guardian record when applicable.
5. **Extension:** Extend graduation date and restore sponsored status from `previous_sponsorship`.

**Refactoring:** Extract `ChildStatusService`/`SponsorshipService`; fix missing `ChildProfileStore`; use transactions; replace GET delete; ~1,000 lines — split by concern.

---

### SponsorController

| | |
|---|---|
| **Responsibilities** | Sponsor CRUD and country management (country logic should live in `CountryController`) |
| **Services** | None |
| **Models** | `Sponsor`, `Country` |
| **Routes** | `/all/sponsors`, add/edit/delete sponsor, `/all/countries`, add country |
| **Validation** | Inline on sponsor and country store/update |
| **Workflow** | Standard CRUD; sponsor profile shows linked children |
| **Refactoring** | Move country CRUD to `CountryController`; GET delete; hardcoded country filters |

---

### SchoolController

| | |
|---|---|
| **Responsibilities** | School CRUD for child update forms |
| **Services** | None |
| **Models** | `School` |
| **Routes** | `/all/schools`, add/edit/delete |
| **Validation** | `Validator::make()` with unique `school_name` |
| **Refactoring** | Form Requests; GET delete; check child-update FK references |

---

### GuardianController

| | |
|---|---|
| **Responsibilities** | Guardian type/category lookup (add + list only) |
| **Services** | None |
| **Models** | `Guardian` |
| **Routes** | `/all/guardians`, `/add/guardian` |
| **Validation** | `guardian` required string |
| **Refactoring** | Incomplete CRUD; misleading success message ("Category added") |

---

### ChildStatusController

| | |
|---|---|
| **Responsibilities** | Child status category lookup table |
| **Services** | None |
| **Models** | `ChildStatus` |
| **Routes** | `/all/statuses`, `/add/status` |
| **Validation** | `status_name` required |
| **Refactoring** | No edit/delete; core statuses should be seeded |

---

### LetterProgressController

| | |
|---|---|
| **Responsibilities** | Sponsor letter exchange tracking per child |
| **Services** | None |
| **Models** | `LetterProgress`, `Children`, `Letter` |
| **Routes** | `/letter-progress` CRUD |
| **Validation** | Active-child closure rule; status enum on update |
| **Workflow** | Create progress with date milestones → update status/dates → delete |
| **Refactoring** | Missing `createLetterProgress`; mass-assignment risk in update |

---

### GiftController

| | |
|---|---|
| **Responsibilities** | Monetary gifts and monthly donations to sponsored children |
| **Services** | None |
| **Models** | `Gift`, `Children`, `Donation` |
| **Routes** | `/gifts`, `/gift`, `/donations` |
| **Validation** | Active-child closure on store |
| **Workflow** | Record gift/donation for active child; donations list highlights children without current-month donation |
| **Refactoring** | Several missing methods (create, show, update); no accounting integration |

---

### GeneralReportsController

| | |
|---|---|
| **Responsibilities** | Filterable CFDP children reports and transformation farmer reports with PDF/Excel export |
| **Services** | DomPDF, Maatwebsite Excel (not app services) |
| **Models** | `Children`, `Farmer`, `Group`, `Cluster`, `Country`, `School` |
| **Routes** | `/reports/cfdp/*`, `/reports/tr/*` |
| **Validation** | None on filter params |
| **Workflow** | Apply filters → enrich with computed fields → render view or export PDF/Excel |
| **Refactoring** | Missing generate methods; duplicated filter logic; 4GB memory limit on exports; inconsistent param names |

---

### BalanceCheckController

| | |
|---|---|
| **Responsibilities** | Accounting diagnostic: journal balance check by date |
| **Services** | None |
| **Models** | Raw DB (`journal_entries`, `journal_entry_lines`, `accounts`) |
| **Routes** | `GET /balance-check` |
| **Workflow** | Sum assets vs liabilities+equity per date; flag imbalances |
| **Refactoring** | Move to service; add date range filter; use Eloquent |

---

### CountryController *(empty stub)*

Country CRUD implemented in `SponsorController`. Stub should be implemented or removed.

---

## 3. Church & Transformation

### ClusterController

| | |
|---|---|
| **Responsibilities** | Geographic/program cluster management (used in child numbering) |
| **Models** | `Cluster` |
| **Routes** | `/all/clusters`, `POST /add/cluster` |
| **Validation** | `cluster_name`, `code` on store |
| **Refactoring** | Missing `AddCluster` form method; no edit/delete; no unique code validation |

---

### ChurchController

| | |
|---|---|
| **Responsibilities** | Church and pastor registration |
| **Models** | `Church`, `Pastor` |
| **Routes** | `/all/churches`, `/add/church`, `/add/pastor`, `/all/pastors` |
| **Validation** | `church_name`, `village_found` (same fields used for pastor — likely wrong schema) |
| **Refactoring** | Missing form methods; pastor not linked to church FK |

---

### LocationController

| | |
|---|---|
| **Responsibilities** | District → SubCounty → Village hierarchy CRUD |
| **Models** | `District`, `SubCounty`, `Village` |
| **Routes** | `/locations/*` |
| **Validation** | None on store; validated on update only |
| **Workflow** | Nested create in transaction; update replaces entire sub-tree |
| **Refactoring** | Add store validation; cascade delete risks |

---

### FarmerController

| | |
|---|---|
| **Responsibilities** | Farmer and group CRUD, location cascading, factory API registration, duplicate merge, Excel import/export |
| **Services** | Maatwebsite Excel (`FarmerImport`/`FarmerExport`) |
| **Models** | `Farmer`, `Group`, `SubCounty`, `Village`, `District`, `Children`, `Grn`, `JournalEntry`, `JournalEntryLine`, `AccountEntity` |

**Key routes:** `/all/farmers`, groups CRUD, farmer CRUD, merge, import/export, AJAX sub-county/village lookups.

**Validation:** Group, farmer, factory, and import file rules via inline validate.

**Workflow**
1. Create group → register farmer with auto `CHM/{GROUP}/{NNN}` number.
2. Normalize phone to `256` prefix; store photos in public upload path.
3. Merge duplicates: reassign GRNs, consolidate account entities/journal lines, delete duplicates in transaction.

**Refactoring:** Missing `AddGroup`; wrong redirect/table name in group update; extract merge to `FarmerMergeService`; inconsistent gender casing.

---

## 4. Hope Coffee (Operations)

### GrnController

| | |
|---|---|
| **Responsibilities** | Goods Received Notes, FAQ grading, payment voucher lifecycle (generate → approve → pay → rollback), PDF/thermal previews, advance deduction, journal posting, SMS, import/export |
| **Services** | `PostingService`, `SmsService`, DomPDF, QR, Excel |
| **Models** | `Farmer`, `Grn`, `Advance`, `PaymentVoucher`, `Group`, `Account`, `User` (+ raw `coffee_stocks`, etc.) |

**Key routes:** GRN/FAQ CRUD, payment voucher workflow (~20 routes), import/export, previews.

**Validation:** Extensive rules for GRN weights/grades and voucher financial fields.

**Workflow**
```
GRN recorded → coffee_stocks created
  → Payment voucher generated
    → Advance repayments FIFO
    → PostingService posts journal
    → SMS to farmer
  → Approval → Payment
  → Rollback request → approve/deny → reversal
```

**Refactoring:** ~2,090 lines; ~1,500 lines duplicated voucher save logic; missing helper methods; extract `PaymentVoucherService`.

---

### HopeCoffeeController

| | |
|---|---|
| **Responsibilities** | Legacy advance management, coffee stock ledger, legacy sales/buyer CRUD, advance PDF |
| **Services** | `PostingService` (advance disbursement) |
| **Models** | `Advance`, `Farmer`, `CoffeeStock`, `StockTransfer`, `CoffeeSale`, `Buyer`, `Account` |
| **Routes** | `/all/advances`, advance add/disburse, stock, legacy sales/buyers |
| **Validation** | Advance, disbursement, sale, buyer rules |
| **Workflow** | Request → approve → disburse (balance check + journal post); stock ledger aggregates movements |
| **Refactoring** | Missing edit/delete methods; overlaps with `AdvancePaymentController`; complex raw SQL in stock ledger |

---

### StockTransferController

| | |
|---|---|
| **Responsibilities** | Processed coffee transfers and roast → grind → package → beverage sale pipeline |
| **Services** | None |
| **Models** | `CoffeeStock`, `StockTransfer`, `Roasting`, `Grinding`, `Packaging`, `BeverageSale`, `Customer`, `User` |
| **Routes** | `/stock-transfers/*`, `/roastings/*`, `/grindings/*`, `/packaging/*`, `/beverage-sales/*` |
| **Validation** | Per-stage quantity and date rules |
| **Workflow** | FIFO deduct ungraded stock through processing stages to customer sale |
| **Refactoring** | Update/destroy don't reverse stock; no GL posting for beverage sales; FIFO logic duplicated |

---

### CoffeeProcessingController

| | |
|---|---|
| **Responsibilities** | Coffee processing events with FIFO consumption and graded screen output |
| **Services** | None |
| **Models** | `CoffeeProcessing`, `CoffeePurchase`, `CoffeeStock` |
| **Routes** | `/coffee-processing/*`, available stock AJAX |
| **Validation** | Process type, quantity, screens array sum ≤ total |
| **Workflow** | FIFO deduct input → create screen outputs → remainder to Processed UNGRADED stock |
| **Refactoring** | Missing `getStockAsOfDate`; destroy doesn't restore stock; duplicated store/update logic

---

### SmsController

| | |
|---|---|
| **Responsibilities** | Single and bulk SMS to farmers (with GRN), children guardians, staff |
| **Services** | `SmsService` |
| **Models** | Raw DB tables |
| **Routes** | `/send-sms`, `/send-bulk-sms` |
| **Validation** | phone, message; bulk group selection |
| **Refactoring** | Synchronous bulk loop; no audit trail; phone normalization duplicated

---

## 5. Accounting & Finance

> All routes below require `auth`, `roles:admin`, and `permission:accounting.access` unless noted.

### AccountController

| | |
|---|---|
| **Responsibilities** | Chart of accounts CRUD, entity-linked sub-accounts, ledger view |
| **Models** | `Account`, `Farmer`, `Buyer`, `Employee`, `account_entities` pivot |
| **Routes** | `/all/accounts`, add/edit/view/delete |
| **Validation** | Type enum, unique code, conditional entity fields when parent is `5349` |
| **Workflow** | Hierarchical COA; entity accounts auto-named from farmer/buyer/employee |
| **Refactoring** | Magic parent ID `5349`; no delete guards; high memory on ledger view |

---

### JournalEntryController

| | |
|---|---|
| **Responsibilities** | Standard journal CRUD plus General Payment Vouchers (GPV) and General Journals (GJ) with FX fields |
| **Models** | `JournalEntry`, `JournalEntryLine`, `Account`, `Farmer`, `Group`, `Buyer`, `Employee`, `AccountEntity`, `Advance` |
| **Routes** | `/journals/*`, `/generalvouchers/*`, `/generaljournals/*` |
| **Validation** | Balanced lines; GPV bank/expense rules; GJ FX metadata |
| **Workflow** | GPV debits expenses, credits bank; may create farmer `Advance`; GJ auto-generates reference |
| **Refactoring** | Split into 3 controllers; shared logic with `ArPaymentController`; doesn't use `PostingService`

---

### InvoiceController

| | |
|---|---|
| **Responsibilities** | Customer invoicing with inline GL posting (Debit AR / Credit Revenue) |
| **Models** | `Invoice`, `InvoiceLine`, `Customer`, `Buyer`, `Account`, `JournalEntry` |
| **Routes** | `/invoices` REST |
| **Validation** | Reference unique, customer, line items; tax hardcoded to 0 |
| **Refactoring** | Use `PostingService`; apply `TaxRate`; line revenue accounts ignored in posting |

---

### ArPaymentController

| | |
|---|---|
| **Responsibilities** | AR receipts as journal vouchers (`Rcpt-*` references) |
| **Services** | `PostingService` injected but **unused** |
| **Models** | `JournalEntry`, `JournalEntryLine`, `Account`, `Farmer`, `Advance`, `AdvancePayment` |
| **Routes** | `/ar/*` |
| **Validation** | Date, reference, bank, line amounts |
| **Workflow** | Credit source accounts, debit bank; optional farmer advance repayment |
| **Refactoring** | Fix store vs update debit/credit inconsistency; link to `Invoice` model

---

### ArAgingController

| | |
|---|---|
| **Responsibilities** | AR aging buckets with PDF export |
| **Models** | Raw queries on `invoices`, `ar_payments`, `buyers`, `customers` |
| **Routes** | `/ar-aging`, `/ar-aging/pdf` |
| **Refactoring** | PDF method incomplete; mismatch with journal-based receipts

---

### VendorBillController

| | |
|---|---|
| **Responsibilities** | AP vendor bill CRUD (no automatic GL posting) |
| **Models** | `VendorBill`, `VendorBillLine`, `Vendor`, `Account` |
| **Routes** | `/ap/bills/*` |
| **Validation** | Vendor, dates, unique reference, line items |
| **Refactoring** | Add GL posting (Debit expense / Credit AP)

---

### ApPaymentController

| | |
|---|---|
| **Responsibilities** | AP payment recording against vendor bills |
| **Models** | `ApPayment`, `VendorBill` |
| **Routes** | `/ap/payments/*` |
| **Validation** | bill_id, date, amount, payment_method |
| **Refactoring** | No bank/AP journal entry; no overpayment validation

---

### ApAgingController

| | |
|---|---|
| **Responsibilities** | AP aging report with PDF |
| **Models** | `VendorBill`, `ap_payments` |
| **Routes** | `/ap-aging`, `/ap-aging/pdf` |
| **Refactoring** | PDF data retrieval broken; extract shared aging service with AR

---

### BankReconciliationController

| | |
|---|---|
| **Responsibilities** | Multiple reconciliation workflows: monthly GL view, CSV import, manual matching, balance check/save |
| **Models** | `Account`, `JournalEntryLine`, `BankAccount`, `BankTransaction`, `BankStatementLine`, `BankReconciliation` |
| **Routes** | `/bank/*`, `/bank-reconciliation/*`, `/reconciliation/*` |
| **Validation** | Month/balance on check; CSV on import |
| **Refactoring** | Missing `reconcile`/`store` methods; hardcoded parent `5423`; duplicate balance logic

---

### BankAccountController

| | |
|---|---|
| **Responsibilities** | CRUD for `bank_accounts`; index lists GL accounts under parent `5423` |
| **Models** | `BankAccount`, `Account` |
| **Routes** | `/bank/accounts/*` |
| **Validation** | name, optional iban |
| **Refactoring** | Two parallel bank account concepts (GL vs `bank_accounts` table)

---

### FixedAssetController

| | |
|---|---|
| **Responsibilities** | Fixed asset register linked to PPE GL accounts |
| **Models** | `FixedAsset`, `AssetCategory`, `Account`, `JournalEntry` |
| **Routes** | `/fixed-assets/*` |
| **Validation** | asset_code unique, cost, useful life, category, account |
| **Refactoring** | No acquisition/disposal journal posting

---

### DepreciationController

| | |
|---|---|
| **Responsibilities** | Straight-line depreciation schedule, revaluation, PDF |
| **Models** | `FixedAsset`, `DepreciationEntry` |
| **Routes** | `/fixed-assets-depreciation/*` |
| **Validation** | date, amount on revalue |
| **Refactoring** | No GL posting for depreciation runs

---

### FxJournalController

| | |
|---|---|
| **Responsibilities** | Foreign-currency journal entries with line-level FX metadata |
| **Models** | `JournalEntry`, `JournalEntryLine`, `Account`, `Currency` |
| **Routes** | `/fx-journals/*` |
| **Validation** | Header + line FX fields; no explicit balance check |
| **Refactoring** | Overlaps with `ForeignJournalController` and GJ module

---

### FxRevaluationController

| | |
|---|---|
| **Responsibilities** | Period-end FX revaluation preview and adjusting entries |
| **Models** | `JournalEntryLine`, `Currency`, `FxRate` |
| **Routes** | `/fx-revaluation/*` |
| **Refactoring** | Hardcoded gain/loss accounts; no period selector

---

### FxRateController

| | |
|---|---|
| **Responsibilities** | FX rate CRUD per currency |
| **Models** | `FxRate`, `Currency` |
| **Routes** | `/fx‑rates/*` (Unicode hyphen in prefix) |
| **Validation** | currency_id, date, rate |
| **Refactoring** | Enforce unique (currency_id, date); fix route prefix character

---

### BudgetController

| | |
|---|---|
| **Responsibilities** | Budget CRUD with line items and variance vs actual GL |
| **Models** | `Budget`, `BudgetLine`, `Account` |
| **Routes** | `/budgets/*` including `{budget}/variance` |
| **Validation** | Header dates + line account/period/amount |
| **Refactoring** | Extract `BudgetVarianceService`

---

### ProjectController

| | |
|---|---|
| **Responsibilities** | Project/job costing CRUD, detail view, P&L report |
| **Models** | `Project` |
| **Routes** | `/projects/*`, `{project}/pl` |
| **Validation** | name, description, optional dates/budget |
| **Refactoring** | Debug logging in store; unconventional resource route registration

---

### ResourceCostController

| | |
|---|---|
| **Responsibilities** | Nested CRUD for project resource costs (hours × rate) |
| **Models** | `Project`, `Resource`, `ProjectResourceCost` |
| **Routes** | `/projects/{project}/resource-costs/*` (shallow) |
| **Validation** | resource_id, date, hours, rate |
| **Refactoring** | No GL posting to project WIP

---

### EntityController

| | |
|---|---|
| **Responsibilities** | Multi-entity consolidation setup with parent hierarchy and currency |
| **Models** | `Entity`, `Currency` |
| **Routes** | `/entities/*` |
| **Validation** | name, parent_id, currency_id |
| **Refactoring** | Prevent circular parent references

---

### IntercompanyController

| | |
|---|---|
| **Responsibilities** | Intercompany transaction register between entities |
| **Models** | `Intercompany`, `Entity` |
| **Routes** | `/intercompany/*` |
| **Validation** | from/to entity, date, amount |
| **Refactoring** | No mirror journals; no from≠to validation

---

### CashFlowController

| | |
|---|---|
| **Responsibilities** | Cash flow statement — direct and indirect methods (12-month rolling) |
| **Models** | Raw DB queries |
| **Routes** | `/reports/cash-flow`, `/reports/cash-flow/indirect` |
| **Refactoring** | Uses `ar_payments`/`ap_payments` which may not align with journal-based flows

---

### TaxRateController

| | |
|---|---|
| **Responsibilities** | Tax rate definitions CRUD |
| **Models** | `TaxRate` |
| **Routes** | `/tax/rates` resource |
| **Validation** | name, rate, type |
| **Refactoring** | No type enum; not applied in `InvoiceController`

---

### WithholdingController

| | |
|---|---|
| **Responsibilities** | Read-only withholding tax entry listing |
| **Models** | `WithholdingEntry` |
| **Routes** | `GET /tax/withholding` |
| **Refactoring** | No create route; add filters/export

---

### TaxProvisionController

| | |
|---|---|
| **Responsibilities** | Monthly tax provision listing and batch generation from P&L |
| **Models** | `TaxProvision`, `TaxRate` |
| **Routes** | `/tax/provision`, `/tax/provision-generate` |
| **Refactoring** | Missing `show` method; uses sales tax rate for PBT provision

---

### AdvancePaymentController

| | |
|---|---|
| **Responsibilities** | Accounting-module advance repayment CRUD |
| **Models** | `AdvancePayment`, `Advance`, `User` |
| **Routes** | `/advances/*` (accounting group) |
| **Validation** | advance, amount, date, method, paid_by |
| **Workflow** | Simple CRUD — does not update advance balances or post journals |
| **Refactoring** | Critical gap vs operational advance deduction in `GrnController`

---

### CoffeePurchaseController

| | |
|---|---|
| **Responsibilities** | Accounting coffee purchase records |
| **Models** | `CoffeePurchase`, `Farmer` |
| **Routes** | `/coffee-purchases/*` |
| **Validation** | date, reference, farmer, weight, price |
| **Refactoring** | No stock or journal integration; likely redundant with GRN flow

---

### CoffeeSaleController

| | |
|---|---|
| **Responsibilities** | Accounting coffee sales with FIFO stock deduction, COGS, journal posting, rollback |
| **Services** | `PostingService` |
| **Models** | `CoffeeSale`, `CoffeeStock`, `Buyer`, `PaymentVoucher`, `Invoice`, `JournalEntry`, `Account` |
| **Routes** | `/coffee-sales/*`, rollback, old stock sale, post-journal |
| **Validation** | Sale type, weights, deductions, price, date |
| **Workflow** | FIFO deduct → compute COGS → post journal → rollback reverses both |
| **Refactoring** | `rollback()` calls undefined `rollbackSale()`; wrong `PostingService` signature in update; extract `CoffeeSaleService`

---

### PayrollController

| | |
|---|---|
| **Responsibilities** | Monthly payroll with PAYE, NSSF, deductions, journal posting |
| **Services** | `PostingService` |
| **Models** | `Payroll`, `PayrollLine`, `Employee`, `Account` |
| **Routes** | `/payroll/*` |
| **Validation** | Store vs update use inconsistent line schemas |
| **Refactoring** | Schema mismatch between store/update/show; hardcoded department/account IDs

---

### ReportController

| | |
|---|---|
| **Responsibilities** | **Two domains in one controller:** (1) financial reports (trial balance, P&L, balance sheet, GL, income statement) and (2) admin report workflow (list, approve, reject, comments) |
| **Models** | `Account`, `JournalEntryLine`, `Report`, `ReportWorkflow` |
| **Routes** | `/finance/reports/*` (accounting reports); `/reports/*` (admin workflow) |
| **Validation** | reason/comment on workflow actions; optional date params on financial reports |
| **Refactoring** | Split into two controllers; missing upload/manage methods; account type casing inconsistency; N+1 in trial balance

---

### ReportTypeController

| | |
|---|---|
| **Responsibilities** | Admin report type/template management (JSON form templates) |
| **Models** | `ReportType` |
| **Routes** | `/report-types` resource (only index/create/store implemented) |
| **Validation** | name, template JSON string |
| **Refactoring** | Missing show/edit/update/destroy; validate JSON structure

---

### ForeignJournalController *(unreachable)*

| | |
|---|---|
| **Responsibilities** | Alternative FX journal with header-level currency conversion |
| **Models** | `JournalEntry`, `Currency`, `Account` |
| **Routes** | None registered |
| **Validation** | FC debit/credit lines with exchange rate |
| **Refactoring** | Register routes or remove; consolidate with `FxJournalController`

---

## 6. HR & Administration

### EmployeeController

| | |
|---|---|
| **Responsibilities** | Employee CRUD and department CRUD (combined) |
| **Models** | `Employee`, `Department` |
| **Routes** | `/all/employees/*`, `/all/departments/*` |
| **Validation** | Employee and department fields via inline validate |
| **Refactoring** | Split into two controllers; inconsistent view paths |

---

### JobController

| | |
|---|---|
| **Responsibilities** | Admin HR job posting CRUD; public careers portal |
| **Models** | `Job`, `JobSection`, `Applicant` |
| **Routes** | `hr/jobs/*` (admin); `/careers`, `/jobs/{job}` (public) |
| **Validation** | Job fields + section arrays |
| **Workflow** | Admin creates job with sections → portal shows open jobs → tracks applicant applications |
| **Refactoring** | Section update deletes/recreates all; add `Job::scopeOpen()`

---

### ApplicantController

| | |
|---|---|
| **Responsibilities** | HR applicant listing and status updates with email notification |
| **Models** | `Applicant`, `ApplicantPosition` |
| **Routes** | `hr/applicants/*` |
| **Validation** | applicant_id, job_id, status enum |
| **Workflow** | Update status → save → send `ApplicantStatusUpdated` mailable |
| **Refactoring** | Queue email; add job-scoped authorization

---

### UserController

| | |
|---|---|
| **Responsibilities** | Admin user CRUD with Spatie role assignment and password-setup email |
| **Models** | `User`, Spatie `Role`, `Permission` |
| **Routes** | `/all/users`, add/store/edit/update/delete |
| **Validation** | None in controller |
| **Workflow** | Create user → assign role → send password setup notification |
| **Refactoring** | Missing `AddUserStore`; duplicate add routes; add Form Request

---

### RoleController

| | |
|---|---|
| **Responsibilities** | Permission CRUD with import/export; role CRUD; role-permission assignment |
| **Models** | Spatie `Role`, `Permission`, `PermissionGroup`, `User` |
| **Routes** | `/all/permission`, `/all/roles`, role-permission routes |
| **Validation** | Minimal |
| **Refactoring** | Duplicate permission inserts; mix of raw DB and Spatie APIs

---

### SettingController

| | |
|---|---|
| **Responsibilities** | Site branding settings and SMTP configuration |
| **Models** | `SiteSetting`, `SmtpSetting` |
| **Routes** | `/site/setting`, `/smtp/setting` |
| **Validation** | None |
| **Refactoring** | SMTP password stored plaintext; add validation

---

### ChatController

| | |
|---|---|
| **Responsibilities** | Render chat UI shell |
| **Routes** | `GET /chat` |
| **Models/Services** | None — logic likely in Livewire/JS |
| **Refactoring** | Document frontend chat implementation separately

---

## 7. Job Portal (Applicant-Facing)

### PortalAuthController

| | |
|---|---|
| **Responsibilities** | Applicant guard login, registration, logout |
| **Models** | `Applicant` |
| **Routes** | `portal/login`, `portal/register`, `portal/logout` |
| **Validation** | email/password; register with confirmed password (min 6) |
| **Refactoring** | Weaker password rules than admin; add rate limiting

---

### PortalApplyController

| | |
|---|---|
| **Responsibilities** | Job application with CV upload |
| **Models** | `Job`, `ApplicantPosition` |
| **Routes** | `portal/jobs/{job}/apply` |
| **Validation** | cv_file PDF max 4MB |
| **Workflow** | Check duplicate → upload CV → create ApplicantPosition → email applicant and admin |
| **Refactoring** | Overlaps with `ApplicantApplicationController`; CV in public path

---

### ApplicantDashboardController

| | |
|---|---|
| **Responsibilities** | Portal home: application stats, open jobs, notification count |
| **Models** | `Job`, `Applicant` (auth) |
| **Routes** | `portal/dashboard` |
| **Refactoring** | Flawed `inReviewJobsCount` query; N+1 risk

---

### ApplicantProfileController

| | |
|---|---|
| **Responsibilities** | View/update applicant profile and photo |
| **Models** | `Applicant` |
| **Routes** | `portal/profile`, `portal/profile/update` |
| **Validation** | full_name, phone, photo |

---

### ApplicantApplicationController

| | |
|---|---|
| **Responsibilities** | List applications; alternate apply flow (cover letter + resume) — partially orphaned |
| **Models** | `Job`, `Application`, `Applicant` |
| **Routes** | `portal/applications`, `portal/applications/{application}` ⚠️ show missing |
| **Refactoring** | Consolidate `Application` vs `ApplicantPosition` models

---

### ApplicantDocumentController

| | |
|---|---|
| **Responsibilities** | Supplementary document upload/list |
| **Models** | `ApplicantDocument`, `Applicant` |
| **Routes** | `portal/documents`, upload |
| **Validation** | pdf/jpg/png max 2MB |

---

### ApplicantNotificationController

| | |
|---|---|
| **Responsibilities** | Notification listing; mark-as-read (no route) |
| **Routes** | `portal/notifications` |
| **Refactoring** | Uses `Auth::user()` instead of `auth:applicant` guard — likely bug

---

## Appendix: Controller Index

| Controller | Domain | Route Count (approx.) |
|------------|--------|----------------------|
| AdminController | Admin | 12 |
| ChildController | CFDP | 22 |
| SponsorController | CFDP | 10 |
| SchoolController | CFDP | 6 |
| GuardianController | CFDP | 3 |
| ChildStatusController | CFDP | 3 |
| LetterProgressController | CFDP | 6 |
| GiftController | CFDP | 10 |
| GeneralReportsController | CFDP/TR Reports | 7 |
| BalanceCheckController | Accounting diagnostic | 1 |
| ClusterController | Church | 3 |
| ChurchController | Church | 6 |
| LocationController | Transformation | 6 |
| FarmerController | Transformation | 20 |
| GrnController | Hope Coffee | 37 |
| HopeCoffeeController | Hope Coffee | 16 |
| StockTransferController | Hope Coffee | 15 |
| CoffeeProcessingController | Hope Coffee | 8 |
| SmsController | Communications | 3 |
| AccountController | Accounting | 7 |
| JournalEntryController | Accounting | 21 |
| InvoiceController | Accounting | 7 |
| ArPaymentController | Accounting | 8 |
| ArAgingController | Accounting | 2 |
| VendorBillController | Accounting | 7 |
| ApPaymentController | Accounting | 7 |
| ApAgingController | Accounting | 2 |
| BankReconciliationController | Accounting | 10 |
| BankAccountController | Accounting | 6 |
| FixedAssetController | Accounting | 7 |
| DepreciationController | Accounting | 4 |
| FxJournalController | Accounting | 4 |
| FxRevaluationController | Accounting | 2 |
| FxRateController | Accounting | 6 |
| BudgetController | Accounting | 8 |
| ProjectController | Accounting | 8 |
| ResourceCostController | Accounting | 6 |
| EntityController | Accounting | 6 |
| IntercompanyController | Accounting | 6 |
| CashFlowController | Accounting | 2 |
| TaxRateController | Accounting | 6 |
| WithholdingController | Accounting | 1 |
| TaxProvisionController | Accounting | 3 |
| AdvancePaymentController | Accounting | 7 |
| CoffeePurchaseController | Accounting | 7 |
| CoffeeSaleController | Accounting | 12 |
| PayrollController | Accounting | 8 |
| ReportController | Accounting + Admin | 16 |
| ReportTypeController | Admin Reports | 3+ resource |
| ForeignJournalController | Accounting (dead) | 0 |
| EmployeeController | HR | 12 |
| JobController | HR | 8 |
| ApplicantController | HR | 3 |
| UserController | Admin | 6 |
| RoleController | Admin | 21 |
| SettingController | Admin | 4 |
| ChatController | Admin | 1 |
| ProfileController | Auth | 3 |
| PortalAuthController | Portal | 5 |
| PortalApplyController | Portal | 2 |
| ApplicantDashboardController | Portal | 1 |
| ApplicantProfileController | Portal | 2 |
| ApplicantApplicationController | Portal | 3 |
| ApplicantDocumentController | Portal | 2 |
| ApplicantNotificationController | Portal | 1 |
| DashboardController | Admin (dead) | 0 |
| CountryController | CFDP (stub) | 0 |
| Auth/* (9 controllers) | Auth | 12 |

---

*Generated as part of CHM ERP architecture analysis. See also `documentation/MODEL_MAP.md` and `documentation/CHM_ERP_CONTEXT.md`.*
