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.
- Earnings: Sums
base_salary+ activeallowances. - Statutory Deductions: Calls
PayrollCalculationServicefor PAYE/NSSF. - Credit Deductions:
- Checks
StaffCreditwhere status=ACTIVE and balance > 0. - Standardizes deduction amount:
min(fixed_deduction, outstanding_balance, available_net_pay). - Creates
LOANdeduction line.
- Checks
- Save: Persists line and updates Run totals.
approveRun(run)
Finalizes the run.
- Logic:
- Locks the run (Status -> APPROVED).
- Credit Processing: Converts
LOANdeductions intoStaffCreditRepaymentrecords, reducing outstanding balances. - Accounting: Posts the Liability Journal Entry (Dr Expense, Cr Payable).
payRun(run)
Records the cash payout.
- Logic:
- Status -> PAID.
- Accounting: Posts the Payment Journal Entry (Dr Payable, Cr Cash).
- 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:
- Status -> ACTIVE.
- Accounting: Dr Employee Advances (Asset), Cr Cash (Asset).
forgiveCredit(...)
Writes off a bad debt.
- Logic:
- Status -> FORGIVEN.
- 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)
| Account | Dr/Cr | Amount | Memo |
|---|---|---|---|
| Salaries Expense (501) | Debit | Total Gross | Payroll expense - Jan |
| Salaries Payable (203) | Credit | Total Net | Net payroll payable |
| PAYE Payable (204) | Credit | Total PAYE | Tax payable |
| NSSF Payable (204) | Credit | Total NSSF | Social Security |
| Employee Advances (110) | Credit | Total Loans | Loan repayments |
Note: The Credit to Advances (110) reduces the "Asset" the company holds against the employee, effectively "repaying" it.
2. Payroll Payment (Cash Out)
| Account | Dr/Cr | Amount | Memo |
|---|---|---|---|
| Salaries Payable (203) | Debit | Total Net | Salaries paid |
| Cash (101) | Credit | Total Net | Disbursement |
3. Staff Advance Approval (Loan Out)
| Account | Dr/Cr | Amount | Memo |
|---|---|---|---|
| Employee Advances (110) | Debit | Loan Amount | Credit ID: 12 |
| Cash (101) | Credit | Loan Amount | Disbursement |
Database Schema
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
- Start Phase: HR Manager clicks "Create Run" (e.g., for Feb 2026).
- Calculation Phase: System iterates active staff.
- Staff A: $1000 Salary + $0 Loan = $1000 Net.
- Staff B: $1000 Salary - $200 Loan Repayment = $800 Net.
- Review Phase: HR reviews grid. Can navigate to Staff Profiles to adjust allowances if needed, then click "Refresh Run".
- Approval Phase: Finance Manager clicks "Approve".
- System posts Accounting Liability (Dr Exp / Cr Payable).
- System updates Staff B's loan balance (reduced by $200).
- Payment Phase: Cashier clicks "Mark Paid".
- System posts Cash out.
- Emails sent.
2. Issuing a Short-Term Loan
- Staff requests $500.
- HR checks dashboard: "Credit Limit: $2000. Outstanding: $0. Available: $2000".
- HR creates Request: Amount $500, Deduction/Month: $250.
- Manager approves.
- System posts Dr Advances $500 / Cr Cash $500.
- Need logic: $250 will automatically be deducted in the next 2 payroll runs.
Audit Findings & Improvements
Strengths
- Smart Repayment Logic: The
addCreditDeductionsmethod 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]
PayrollServicenow usesAccountingMapServicefor dynamic resolution. - Configuration: Tax brackets (PayrollCalculationService) are likely hardcoded. Needs a UI.
Minor
- Performance:
generateLinesruns 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)
return [
'credit_limit_multiplier' => 2.0, // Max loan = 2x Salary
'currency' => 'USD',
'tax_brackets' => [ ... ]
];Module Version: 1.0 Status: Production Ready