Skip to content

Accounting Integration Improvement Plan

Date: January 2026 Status: COMPLETED (Jan 27, 2026)

Implementation Summary (Completed)

We have successfully implemented the AccountingMapService to abstract General Ledger Account Codes.

Achievements:

  1. AccountingKey Enum: Defined logical keys for all financial touchpoints.
  2. accounting_mappings Table: Created property-scoped mapping table.
  3. Service Refactoring: Updated PayrollService, WalletService, InvoiceService, and BillService to use dynamic mapping.
  4. Seeder Update: Updated AccountingSeeder to populate default mappings for new properties.

1. The Problem

Currently, multiple modules interact with the Accounting System by hardcoding General Ledger (GL) Account Codes.

Examples:

  • PayrollService: Hardcodes '501' (Salaries Expense), '203' (Salaries Payable).
  • WalletService: Hardcodes '102-SX' (Wallet Clearing).
  • InvoiceService: Hardcodes '103' (Accounts Receivable).

Risks:

  1. Fragility: If the Finance Manager changes the Chart of Accounts (e.g., renames '103' to '1200'), the application breaks.
  2. Rigidity: Different properties (Tenants) might want to map "Room Revenue" to different accounts, but the code forces a single global value.
  3. Testing: Unit tests rely on these specific database records existing.

2. Proposed Solution: AccountingMapService

We will introduce a central service to resolve Logical Keys to Account IDs.

A. The "Logical Keys" Enum

We will define a set of constants representing every financial touchpoint in the system.

php
enum AccountingKey: string {
    case REVENUE_ROOM = 'revenue.room';
    case REVENUE_FOOD = 'revenue.food';
    case ASSET_INVENTORY = 'asset.inventory';
    case LIABILITY_TAX_PAYE = 'liability.tax.paye';
    case LIABILITY_WALLET = 'liability.wallet';
    // ...
}

B. The Mapping Table accounting_mappings

A new table to store the configuration per property.

ColumnTypeDescription
idbigintPK
property_idbigintTenant Scope
keystringEnum Value (e.g. 'revenue.room')
account_idbigintFK to accounting_accounts

C. The Resolver Service

php
class AccountingMapService {
    public function getAccount(AccountingKey $key, int $propertyId): Account {
        // 1. Try DB Mapping
        $mapping = AccountingMapping::where('property_id', $propertyId)
            ->where('key', $key->value)
            ->first();

        if ($mapping) return $mapping->account;

        // 2. Fallback to "Default" config (optional)
        // 3. Throw Exception "Accounting Configuration Missing for {$key}"
    }
}

3. Implementation Steps

Phase 1: Preparation (The Schema)

  1. Create AccountingKey Enum.
  2. Create migration for accounting_mappings table.
  3. Seed default mappings for the Demo Property based on current hardcoded values.

Phase 2: Refactoring (The Code)

Iterate through each module and replace hardcoded lookups.

Before (InvoiceService):

php
$arAccount = Account::where('code', '103')->first();

After (InvoiceService):

php
$arAccount = $this->accountingMap->getAccount(AccountingKey::ASSET_AR_GUEST, $propertyId);

Phase 3: The UI (The Interface)

Create a new Settings page Finance > Accounting Setup.

  • List all Logical Keys (grouped by Module).
  • Dropdown to select an active GL Account for each key.

4. Affected Modules

  1. HR/Payroll: 6 Keys (Salary Exp, Payable, PAYE, NSSF, Loan, Advance).
  2. Wallet: 2 Keys (Liability, Clearing Asset).
  3. Inventory: 2 Keys (Asset, COGS - per Category ideally).
  4. FrontDesk: 2 Keys (Cash, Room Revenue).
  5. Gym: 2 Keys (Membership Revenue, Tax).
  6. AP/AR: 2 Keys (AP Control, AR Control).
  7. Restaurant: 2 Keys (Food Revenue, COGS).
  8. Laundry: 1 Key (Laundry Revenue).