Skip to content

Expense Module Documentation

Overview

The Expense Module is a critical component for managing operational cash flow. It handles the full lifecycle of business expenditures—from submission (by staff) to approval (by managers) and final settlement (by finance). It is tightly integrated with Budgeting to track spending in real-time and Accounting for automated General Ledger postings.

Key Features

  • Petty Cash & Billable Expenses: Unified flow for small cash claims and larger vendor bills.
  • Budget Control: Real-time tracking of spend against monthly category limits.
  • Approval Chains: Configurable rules (e.g., "Auto-approve if < $50", "Require Manager if > $500").
  • Financial Posting: Automated double-entry bookkeeping upon payment.
  • Reversal Logic: Robust "Void" function that reverses both the GL entries and the budget utilization.

Architecture

Domain Layer (app/Domain/Expense)

Models (5 Models)

Expense (Expense.php)
  • Table: expenses
  • Description: The header record for a claim.
  • Key Fields:
    • expense_no: Sequential ID (e.g., EXP-202601-0012).
    • status: DRAFT | PENDING_APPROVAL | APPROVED | REJECTED | PAID | VOID.
    • total_amount: Sum of all lines + tax.
    • payment_method: CASH | BANK | MOBILE_MONEY.
    • expense_category_id: Primary category for routing approvals.
    • department_id: Cost center.
  • Relationships:
    • lines(): HasMany ExpenseLine.
    • journalEntry(): MorphOne JournalEntry.
ExpenseLine (ExpenseLine.php)
  • Table: expense_lines
  • Description: Granular detail allowing split-coding (e.g., one receipt split between "Food" and "Cleaning").
  • Key Fields:
    • expense_account_id: The GL Account to debit.
    • amount: Line item cost.
    • tax_amount: VAT portion.
ExpenseBudget (ExpenseBudget.php)
  • Table: expense_budgets
  • Description: Monthly spending limit.
  • Key Fields:
    • year, month: Fiscal period.
    • limit_amount: Allowed spend.
    • spent_amount: Actual utilization.
    • is_hard_limit: Boolean (Stop vs Warn).

Services

ExpenseService (ExpenseService.php)

Purpose: Orchestrates the expense lifecycle, ensuring financial and budgetary consistency.

Key Methods:

createExpense(data, lines)

Initializes a DRAFT.

  • Logic:
    1. Generates expense_no (Prefix + Sequence).
    2. Calculates subtotal, tax, total.
    3. Persists Header and Lines.
submitForApproval(expense)

Triggers the workflow.

  • Logic:
    1. Status -> PENDING_APPROVAL.
    2. Auto-Approval Check: Calls isWithinAutoApprovalLimit(). If Category allows it and Amount < Limit, auto-transitions to APPROVED.
approveExpense(expense, approver)

Authorizes the spend.

  • Logic:
    1. Status -> APPROVED.
    2. Budget Impact: Calls updateBudgetSpent(..., 'add'). Increments expense_budgets.spent_amount.
payExpense(expense, method)

Finalizes the financial transaction.

  • Logic:
    1. Status -> PAID.
    2. Accounting: Calls postExpenseToAccounting (Dr Expense, Cr Cash).
voidExpense(expense, reason)

Safety mechanism for errors.

  • Logic:
    1. Reversal: If previously PAID, calls AccountingPoster::voidEntry() to reverse GL.
    2. Budget: If previously APPROVED, decrements expense_budgets.spent_amount.
    3. Status -> VOID.

Accounting Integration

The module is tightly coupled with AccountingPoster.

Account Resolution

Credit accounts (Source of Funds) are resolved dynamically based on the selected Payment Method using config() lookups.

Payment MethodConfig KeyDefault Code
CASHaccounting.accounts.cash_in_hand1010
BANKaccounting.accounts.bank1020
MOBILE_MONEYaccounting.accounts.mobile_money1030
PETTY_CASHaccounting.accounts.petty_cash1010

Journal Entries

1. Expense Payment

AccountDr/CrAmountMemo
Expense Account (from Line)DebitLine TotalLine Description
Cash/Bank (1010/1020)CreditExpense TotalPayment for EXP-001

Audit Findings & Improvements

Strengths

  • Reversal Logic: The voidExpense method is excellent—it handles both the Financial Reversal (via voidEntry) and the Budget Restoration (via decrementSpent) atomically. This ensures reports are always accurate.
  • Budget Tracking: Real-time updates to budget utilization happen during the approval phase, preventing "surprise" overages at month-end.
  • Granular Lines: Supporting multiple lines per expense allows for proper Cost Center accounting (e.g., a single supermarket receipt containing both "Kitchen Supplies" and "Office Supplies").

Issues Identified

Major

  • Race Condition in Numbering: generateExpenseNumber querys for the "last number" and then increments it. Concurrent requests could generate duplicate Expense Numbers.
    • Fix: Use a database sequence or atomic lock.
  • Hardcoded Config Dependency: The service relies on config keys like accounting.accounts.cash_in_hand. If these keys are missing in config/accounting.php, it falls back to '1010', which might be incorrect for a specific tenant's Chart of Accounts.

Minor

  • Soft Budget Limits: Currently, the system only tracks the budget (updateBudgetSpent). It does not explicitly block approval if the budget is exceeded. It relies on the human approver checking the dashboard.

Configuration

Config File: config/expense.php (Virtual)

php
return [
    'auto_approval_limit' => 50.00, // USD
    'budget_enforcement' => 'soft', // 'soft' (warn) or 'hard' (block)
];

Module Version: 1.0 Status: Production Ready