# Step 16 — Expense Management — Implementation Report

**Status:** COMPLETED  
**Verified:** 12 August 2026  
**Pull request:** #28

## Expense workflow

Implemented approval-controlled operating expenses with the roadmap fields:

- Category
- Description
- Amount
- Expense date
- Branch
- Paid-from financial account
- Supplier / payee
- Private receipt / supporting evidence
- Entered by
- Approved by / rejected by
- Decision comments
- Notes

New expenses are created as `pending`. Creating a pending expense does **not** change any financial account balance or accounting ledger.

Only an authorized approver can financially post a pending expense. When maker-checker is enabled, the employee who entered the expense cannot approve the same expense.

Rejected expenses permanently retain their decision history and never create financial movement.

## Approved expense accounting

Approval is atomic and database-lock protected.

Before posting, the system rechecks:

- expense is still pending
- paid-from account still belongs to the expense branch
- paid-from account is active
- account has sufficient available balance
- maker-checker restrictions when enabled

A successful approval:

1. Reduces the selected financial-account balance.
2. Creates a permanent `financial_account_transactions` record with type `expense`.
3. Creates a balanced accounting journal:
   - Debit: `expenses`
   - Credit: `assets`
4. Stores the account transaction link and accounting journal number on the expense.
5. Records approver and approval timestamp.
6. Makes the expense directly available to future Profit & Loss reporting through the existing `expenses` accounting category.

Duplicate approval/posting is blocked and insufficient-funds failures are atomic.

## Expense categories

Added configurable business-wide expense categories.

Default categories are seeded per business:

- Rent
- Internet
- Security
- Software
- Insurance
- Other

Authorized users can create additional categories, change descriptions/codes and activate/deactivate them without rewriting historical expenses.

## Recurring expenses

Added recurring expense schedules with:

- Branch
- Category
- Default paid-from account
- Description
- Amount
- Supplier
- First / next due date
- Optional end date
- Notes
- Active/inactive status
- Reminder lead time

Supported recurrence frequencies:

- Weekly
- Monthly
- Quarterly
- Yearly
- Custom number of days

The recurring engine never auto-pays a bill. On a due date it generates a normal **pending expense**, which must still pass the same approval and financial controls as a manually entered expense.

Generated occurrences are unique per schedule and due date, so repeated scheduler runs cannot create duplicate expenses.

## Expense reminders

Added permanent reminder records for recurring expenses.

A reminder can be created before the first or subsequent due date according to `reminder_days_before`. Reminder processing is date-safe and idempotent across repeated runs.

Reminder statuses:

- Open
- Generated
- Acknowledged

When the due-date expense is generated, the reminder links to that expense. Authorized staff can acknowledge reminder records without deleting history.

## Automation

Added command:

`expenses:process-recurring`

It is scheduled daily at **07:30** in the application timezone, before the existing morning loan-reminder dispatch.

The command supports an optional `--date=YYYY-MM-DD` argument for deterministic processing and testing.

## Private receipt handling

Expense receipts/supporting evidence are stored through the existing private secure-file service.

Receipt downloads require:

- authenticated user
- `expenses.view` permission
- access to the expense branch

Cross-branch receipt and expense access is blocked.

## Database changes

Migration: `2026_08_12_170000_create_expense_management.php`

Created:

- `expense_categories`
- `recurring_expenses`
- `expenses`
- `expense_reminders`

Expense records permanently retain approval/rejection and financial-posting references.

## Permissions

Added:

- `expenses.view`
- `expenses.create`
- `expenses.approve`
- `expenses.categories.manage`
- `expenses.recurring.manage`

The existing legacy `expenses.manage` permission is retained for compatibility.

Role grants separate read-only audit access from entry, approval, category configuration and recurring-expense management.

## User interface

Added Finance → **Expenses** navigation and screens for:

- Expense register with search, status, category and date filters
- Pending / approved / rejected amount summaries
- New expense entry
- Expense detail and approval/rejection
- Private receipt download
- Configurable expense categories
- Recurring expense schedule list
- Recurring expense create/edit
- Reminder history and acknowledgement

The UI follows the application’s shared toolbar, table, form and compact card patterns.

## Audit events

Added:

- `expense.created`
- `expense.approved`
- `expense.rejected`
- `expense_category.created`
- `expense_category.updated`
- `recurring_expense.created`
- `recurring_expense.updated`
- `expense_reminder.acknowledged`

## Verification

GitHub Actions Foundation CI on pull request #28:

- Fresh migrations: PASS
- Seeders: PASS
- Full Laravel test suite: **124 passed**
- Assertions: **1,172**
- Existing Step 1–15 regression tests: PASS
- Step 16 feature tests: **6 passed**

Step 16 tests cover:

1. Pending expense entry, authorization, private receipt storage and no pre-approval financial movement.
2. Approved expense account deduction, permanent account transaction, balanced expense ledger and duplicate-post prevention.
3. Maker-checker, insufficient-funds protection and atomic rejection behavior.
4. Branch isolation, private receipt access and configurable categories.
5. Recurring expense pre-due reminders, pending due-date occurrence generation, idempotency and no automatic payment.
6. Daily scheduler registration and Finance UI access.

## Remaining Step 16 work

None.

## Next roadmap module

**Step 17 — Payroll**.
