Skip to content

HR & Payroll Module Documentation

Overview

The HR & Payroll Module allows the hotel to manage its most valuable asset—its people. It handles the full lifecycle of employee compensation, from monthly payroll processing to automated loan deductions and tax compliance. It is tightly integrated with the Staff Module (for profiles) and the Accounting Module (for financial posting).

Key Features

  • Payroll Batches: Create, review, and approve monthly payroll runs for all active staff.
  • Automated Calculations: Calculates Gross Pay (Salary + Allowances), Statutory Deductions (PAYE, NSSF), and Net Pay.
  • Loan Management: Integrated Staff Credit system where loan repayments are automatically deducted from payroll.
  • Accounting Integration: Automated General Ledger postings for Salaries Expense, Liabilities, and Payouts.
  • Payslip Generation: Generates digital payslips and notifies staff via email.
  • Safety Locks: Validations preventing modification of approved runs or credit limits.

Architecture

Domain Layer (app/Domain/HR)

Models (7 Models)

PayrollRun (PayrollRun.php)
  • Table: payroll_runs
  • Description: Represents a monthly payroll batch (e.g., "January 2026").
  • Key Fields:
    • year, month: Accounting period.
    • status: DRAFT | APPROVED | PAID | VOID.
    • total_gross: Sum of all lines gross pay.
    • total_net: Sum of all lines net pay.
    • approved_by, approved_at: Audit trail for approval.
    • paid_by, paid_at: Audit trail for payment.
  • Relationships:
    • lines(): Has many PayrollRunLines.
    • property(): Tenant scope.
PayrollRunLine (PayrollRunLine.php)
  • Table: payroll_run_lines
  • Description: The payslip calculation for a single employee in a run.
  • Key Fields:
    • staff_id: Link to Staff member.
    • base_salary: Snapshot of salary at time of run.
    • allowances: JSON snapshot of allowances.
    • gross_pay: Base + Allowances.
    • total_deductions: Tax + NSSF + Loans.
    • net_pay: Gross - Deductions.
  • Relationships:
    • deductions(): Has many PayrollRunLineDeductions.
PayrollRunLineDeduction (PayrollRunLineDeduction.php)
  • Table: payroll_run_line_deductions
  • Description: Specific deduction line item on a payslip.
  • Key Fields:
    • deduction_type_id: Link to configuration (PAYE, NSSF, LOAN).
    • amount: Deduction value.
    • description: Context (e.g., "Credit Repayment ID: 12").
StaffCredit (StaffCredit.php)
  • Table: staff_credits
  • Description: Tracks loans or salary advances given to staff.
  • Key Fields:
    • amount: Principal amount.
    • outstanding_balance: Remaining amount to pay.
    • status: PENDING | ACTIVE | REJECTED | REPAID | FORGIVEN.
    • credit_type: PERSONAL | HOTEL_RECEIPT (e.g., damaged goods).
    • fixed_deduction_amount: Max amount to deduct per month.
StaffCreditRepayment (StaffCreditRepayment.php)
  • Table: staff_credit_repayments
  • Description: Ledger of repayments made against a credit.
  • Key Fields:
    • amount: Repayment value.
    • source: PAYROLL | MANUAL | FORGIVEN.
    • payroll_run_line_id: Link to payroll cycle (if applicable).

Services

PayrollService (PayrollService.php)

Purpose: Orchestrates the entire monthly payroll lifecycle.

Key Methods:

createRun(year, month)

Starts a new draft run.

  • Logic: Validates uniqueness (one active run per month per property). Initializes totals to 0.
generateLineForStaff(run, staff)

Calculates pay for an employee.

  1. Earnings: Sums base_salary + active allowances.
  2. Statutory Deductions: Calls PayrollCalculationService for PAYE/NSSF.
  3. Credit Deductions:
    • Checks StaffCredit where status=ACTIVE and balance > 0.
    • Standardizes deduction amount: min(fixed_deduction, outstanding_balance, available_net_pay).
    • Creates LOAN deduction line.
  4. Save: Persists line and updates Run totals.
approveRun(run)

Finalizes the run.

  • Logic:
    1. Locks the run (Status -> APPROVED).
    2. Credit Processing: Converts LOAN deductions into StaffCreditRepayment records, reducing outstanding balances.
    3. Accounting: Posts the Liability Journal Entry (Dr Expense, Cr Payable).
payRun(run)

Records the cash payout.

  • Logic:
    1. Status -> PAID.
    2. Accounting: Posts the Payment Journal Entry (Dr Payable, Cr Cash).
    3. Notifications: Sends "Payslip Ready" emails to staff.
StaffCreditService (StaffCreditService.php)

Purpose: Manages lending mechanism to staff.

Key Methods:

requestCredit(...)

Creates a PENDING request.

  • Validation: Checks credit limit (salary * multiplier).
  • Limit: Rejects request if amount > (limit - current_outstanding).
approveCredit(...)

Approves and disburses funds.

  • Logic:
    1. Status -> ACTIVE.
    2. Accounting: Dr Employee Advances (Asset), Cr Cash (Asset).
forgiveCredit(...)

Writes off a bad debt.

  • Logic:
    1. Status -> FORGIVEN.
    2. Accounting: Dr Benefits Expense, Cr Employee Advances.

Accounting Integration

The module is tightly coupled with AccountingPoster.

Account Configuration

Account codes are dynamically resolved via the AccountingMapService using logical keys. This allows each property to configure its own Chart of Accounts without changing code.

Logical Mapping Keys:

  • Salaries Expense: expense.salary (Key: EXPENSE_SALARY)
  • Salaries Payable: liability.salary_payable (Key: LIABILITY_SALARY_PAYABLE)
  • PAYE Payable: liability.tax.paye (Key: LIABILITY_TAX_PAYE)
  • NSSF Payable: liability.tax.nssf (Key: LIABILITY_TAX_NSSF)
  • Employee Advances: asset.employee_advances (Key: ASSET_EMPLOYEE_ADVANCES)
  • Cash: asset.cash (Key: ASSET_CASH)

Journal Entries

1. Payroll Approval (Liability Booking)

AccountDr/CrAmountMemo
Salaries Expense (501)DebitTotal GrossPayroll expense - Jan
Salaries Payable (203)CreditTotal NetNet payroll payable
PAYE Payable (204)CreditTotal PAYETax payable
NSSF Payable (204)CreditTotal NSSFSocial Security
Employee Advances (110)CreditTotal LoansLoan repayments

Note: The Credit to Advances (110) reduces the "Asset" the company holds against the employee, effectively "repaying" it.

2. Payroll Payment (Cash Out)

AccountDr/CrAmountMemo
Salaries Payable (203)DebitTotal NetSalaries paid
Cash (101)CreditTotal NetDisbursement

3. Staff Advance Approval (Loan Out)

AccountDr/CrAmountMemo
Employee Advances (110)DebitLoan AmountCredit ID: 12
Cash (101)CreditLoan AmountDisbursement

Database Schema

mermaid
erDiagram
    PayrollRun ||--o{ PayrollRunLine : contains
    PayrollRunLine ||--o{ PayrollRunLineDeduction : "has items"
    Staff ||--o{ PayrollRunLine : "gets paid"

    Staff ||--o{ StaffCredit : "borrows"
    StaffCredit ||--o{ StaffCreditRepayment : "repaid by"
    PayrollRunLine ||--o{ StaffCreditRepayment : "triggers"

    payroll_runs {
        bigint id PK
        int year
        int month
        enum status "DRAFT, APPROVED, PAID"
        decimal total_gross
        decimal total_net
    }

    payroll_run_lines {
        bigint id PK
        bigint staff_id
        decimal gross_pay
        decimal net_pay
        json allowances
    }

    staff_credits {
        bigint id PK
        bigint staff_id
        decimal amount
        decimal outstanding_balance
        decimal fixed_deduction_amount
    }

Common Workflows

1. Monthly Payroll Processing

  1. Start Phase: HR Manager clicks "Create Run" (e.g., for Feb 2026).
  2. Calculation Phase: System iterates active staff.
    • Staff A: $1000 Salary + $0 Loan = $1000 Net.
    • Staff B: $1000 Salary - $200 Loan Repayment = $800 Net.
  3. Review Phase: HR reviews grid. Can navigate to Staff Profiles to adjust allowances if needed, then click "Refresh Run".
  4. Approval Phase: Finance Manager clicks "Approve".
    • System posts Accounting Liability (Dr Exp / Cr Payable).
    • System updates Staff B's loan balance (reduced by $200).
  5. Payment Phase: Cashier clicks "Mark Paid".
    • System posts Cash out.
    • Emails sent.

2. Issuing a Short-Term Loan

  1. Staff requests $500.
  2. HR checks dashboard: "Credit Limit: $2000. Outstanding: $0. Available: $2000".
  3. HR creates Request: Amount $500, Deduction/Month: $250.
  4. Manager approves.
  5. System posts Dr Advances $500 / Cr Cash $500.
  6. Need logic: $250 will automatically be deducted in the next 2 payroll runs.

Audit Findings & Improvements

Strengths

  • Smart Repayment Logic: The addCreditDeductions method is robust—it intelligently caps deductions so an employee is never left with $0 net pay (prioritizing livable wage vs loan repayment).
  • Audit Trails: Every state change (Approve, Pay, Void, Forgive) records the User ID and Timestamp.
  • Separation of Duties: Drafting, Approving, and Paying can be gated by permissions (hr.payroll.manage, hr.payroll.approve).

Issues Identified

Major

  • Hardcoded Accounts: [FIXED] PayrollService now uses AccountingMapService for dynamic resolution.
  • Configuration: Tax brackets (PayrollCalculationService) are likely hardcoded. Needs a UI.

Minor

  • Performance: generateLines runs N+1 queries. Optimization needed for organizations with >100 staff.
  • Deduction Priority: Currently hardcoded. Should allow configuring if Loan or NSSF comes first (though Statutory should always be first).

Configuration

Config File: config/hr.php (Virtual/Planned)

php
return [
    'credit_limit_multiplier' => 2.0, // Max loan = 2x Salary
    'currency' => 'USD',
    'tax_brackets' => [ ... ]
];

Module Version: 1.0 Status: Production Ready