Requirement Specification

PulaHr Global — RSD

Porting configurable HR & Payroll features from PulaHr to PulaHr Global

Version 1.0Date: 24 June 2026Status: Draft

1. Purpose & Scope

This document specifies the requirements for adapting selected features of PulaHr (the Botswana-specific HR & payroll platform) into PulaHr Global, a multi-region capable version. The goal is to extract hardcoded, country-specific business logic into a configurable Policy Engine so the same codebase can serve clients across different jurisdictions and company-specific rules (e.g. Two Six Seven Restaurant's overtime formula).

Scope covers: the Overtime Engine, Tax (PAYE) Engine, Statutory Contributions, Leave deduction logic, and the Earnings/Deductions taxonomy.

2. System Vision

PulaHr Global retains the core engine (Identity, Auth, Employee database) while abstracting all calculation rules into per-company configuration records. A single shared service reads these records so every screen and backend function produces identical results — eliminating the inconsistencies present in the current PulaHr code.

ModuleCore FunctionScalability Approach
Identity EngineMulti-tenancy, Auth, Employee DBShared across all instances
Policy EngineOT, Tax, Leave, Statutory rulesConfig-driven per company / region
Data AdapterStandardised payroll interfaceUnified service layer

3. Current-State Analysis (Logic Inventory)

A full code audit of PulaHr identified the following business-logic points. These are the variables that must move into the Policy Engine for Global.

3.1 Critical Inconsistencies

The codebase currently uses three different assumptions for the same concepts: overtime divides salary by 26 days; daily hours are sometimes 8 and sometimes 8.5; and leave deductions divide by 22 days. Overtime is also detected at 8.5h/day but priced at 8h/day.

LogicLocationValueIssue
OT hourly divisorTimesheetSection (admin save)÷26 ÷ hours_per_day(8)Reads setting ✓
OT hourly divisorTimesheetSection (display fallback)÷26 ÷ 8 (hardcoded)Ignores setting ✗
OT hourly divisorAdminPayroll / syncOvertimeToPayroll÷26 ÷ hours_per_day(8)Reads setting ✓
Standard shift lengthTimesheetSection (OT detection)8.5 (hardcoded const)Mismatch vs ÷8 pricing
Leave deduction divisorcalculatePayroll÷22 working daysDiffers from OT's 26

3.2 Hardcoded Variables to Extract

DomainHardcoded Values
Overtime26 working days, 8 / 8.5 hours/day, 1.5 multiplier, rollover toggle
Tax (PAYE)Brackets 48k/84k/120k/156k; rates 5/12.5/18.75/25%; duplicated in 3 files
Currency'BWP' / 'P' symbol hardcoded throughout
StatutoryBPF employee 5%, BPF employer 5%, WCF 0.5%
Taxonomy15 earning labels + 11 deduction labels (payrollProcessor)
Leave22 working days for daily-rate; leave types fixed in enums

3.3 Already Configurable (reuse pattern)

SourceConfigurable Fields
CompanyPayrollSettingsovertime_multiplier, hours_per_day, rollover_overtime_to_next_month
SystemSettings (payroll)bpf_employee_rate, bpf_employer_rate, wcf_rate

4. Functional Requirements

4.1 Policy Engine

  • FR-1: A RegionalPolicy record SHALL define currency, working_days_per_month, hours_per_day, ot_multiplier, tax brackets, and statutory rates per company/region.
  • FR-2: All overtime calculations SHALL use one shared service reading the policy — no hardcoded divisors.
  • FR-3: The standard shift length used to detect overtime SHALL equal the hours/day used to price it.
  • FR-4: PAYE SHALL be computed from a configurable bracket array, replacing the three duplicated functions.
  • FR-5: The leave daily-rate divisor SHALL come from the same working_days_per_month value as overtime.

4.2 Overtime (Worked Example — Two Six Seven)

Formula: Basic ÷ 26 ÷ 8.5 × 1.5 × hours. With Basic = P5,000 and 2 OT hours: 5000 ÷ 26 ÷ 8.5 × 1.5 × 2 = P67.87. The Policy Engine must reproduce this exactly when hours_per_day = 8.5.

5. Non-Functional Requirements

  • Consistency: A given employee/period yields identical figures across UI, PDF and backend.
  • Maintainability: Adding a new country/client requires a config record, not code changes.
  • Backward compatibility: Existing Botswana companies default to current values (26 days, 1.5×, BW PAYE).
  • Auditability: Each payslip records which policy version produced it.

6. Implementation Roadmap

PhaseDeliverableOutcome
1Create RegionalPolicy entitySingle source of truth for all rules
2Build shared PolicyEngine serviceReplaces duplicated PAYE + OT logic
3Refactor calc sites to use serviceFixes 26/8/8.5/22 inconsistencies
4Config-driven onboardingNew clients added via config record
5Policy versioning on payslipsFull audit trail

7. Risks & Assumptions

  • Migrating historical payslips may surface figures that differ slightly from the corrected formula.
  • Assumes each company maps to exactly one active RegionalPolicy at a time.
  • Tax rules change frequently — bracket config must be easy for admins to update.
PulaHr Global Requirement Specification Document · Generated for internal planning use