# Project plan — dthf-hr

Evidence date: 2026-10-16.

Source repository: `git@bitbucket.org:overdrive/dthf-hr.git`.

Inspected revision: `master` at `93552d8` (2026-06-19, "Merged in Leave-integration (pull request #1)"). The scan-host path `~/projects/sonar_scan/repos/dthf-hr` was not on this machine. The tree was read from that git revision.

The onboarding snapshot recorded last commit `a647fe3` (2023-11-06, "PHP 8.2 updates - salary and fin levies fixes"). That commit is an ancestor of current `master`. Current `master` is 53 commits from root `7d9727e` (2019-06-14, "Initial commit"). Remote branches: `master`, `Leave-integration`.

There was no `silicon_ark/dthf-hr/` set before this one. The source README is one line: "Desmund Tutu HIV Foundation". Provenance: **Documented** in `README.md`, including that spelling. No other product plan, issue tracker, or milestone list is in the repository.

## Product

**Inferred** from the screens, tables, and cron scripts: this is the Desmond Tutu HIV Foundation HR and effort-allocation system. Staff are linked to companies and projects. Each period stores a percentage allocation. Month end freezes those allocations and produces salary, levy, HR-support, IT-support, and finance-support journals. Timesheets are generated as PDFs, captured back in (blind capture), and chased by email when outstanding.

The public hostname hard-coded for production is `www.dthf-hr.co.za` (`dthf-hr.co.za` is rewritten to that name in `system.php`).

## Scope in this snapshot

In scope of the code that current entry scripts include:

- Login, password policy, and password reset for `sysUser`.
- Menu and page rights from `sysMenuLevel1`, `sysMenuLevel2`, and `sysSecurity`.
- Companies, projects, sites, staff, staff codes, and job titles.
- Project staff allocation by period, with a status column read from the live database enum.
- Month-end checks, salary-cost Excel import, finance-levy Excel import, and company journals.
- Timesheet control, PDF generation, blind capture, bulk blind capture, and rejection.
- Public holidays used when drawing the timesheet calendar.
- Daily and hourly PHP scripts intended to be invoked by the host scheduler.
- A PaySpace / YourHCM leave pull (`leave.api.php`, `cron.employee.leave.php`) whose credentials are not in the repository, and whose display on the staff screen and timesheet PDF is commented out as of 2026-06-19.

Out of scope of the running pages, but still in the tree:

- Dated copies of classes and crons (filenames containing dates such as `20190726`, `20190919`, `20200407`, `20200519`, `20200520`).
- `xcron.daily.outstanding.timesheets.*.80.php`, `Xcron.daily.php`, `system2.php`, `report20200519.php`, `zSandBox.php`, `test.php`.
- Vendored PDF and spreadsheet libraries under `includes/tcpdf`, `includes/mpdf`, `includes/mpdf_20190305`, `PHPExcel`, `fpdi`, and `vendor/`.
- Committed generated files under `documents/` and `PDF/`.

Whether the host still executes a dated copy is **Unknown / Requires Confirmation**.

## System shape

One PHP application. Each screen is a root script (`staff.php`, `my.projects.php`, `month.end.php`, and so on). Those scripts use a custom framework whose classes are `_nemo.*.cls.php`. Domain classes live in `includes/`. There is no router, no `public/` front controller, and no framework such as Laravel or CodeIgniter.

MySQL is selected in `system.php` from `$_SERVER["SERVER_NAME"]`. Settings rows in `sysSettings` are loaded into `$SystemSettings` at startup. Page rights are loaded per security group in `Nemo::iniSecurity()`.

Scheduled work is a set of PHP files plus a queue table `sysCronJob`. The repository does not contain a crontab. The hourly script processes rows with status `New`, job name `generateTimesheet`, up to the query limit written in that file. Two other job names in the same switch are inside a block comment.

## Major components

| Component | Role | Primary files |
|---|---|---|
| Nemo shell | Session, security, menu, lists, details, email, passwords | `_nemo.cls.php`, `_nemo.basic.cls.php`, `_nemo.menu.cls.php`, `_nemo.list.cls.php`, `_nemo.email.cls.php`, `_nemo.password.cls.php` |
| Environment | Database choice, session path, blind-capture message constants | `system.php`, `system.functions.inc.php`, `project.functions.inc.php` |
| Organisation | Companies, projects, sites, staff, job titles | `company.php`, `project.php`, `sites.php`, `staff.php`, `includes/company.cls.php`, `includes/project.cls.php`, `includes/staff.cls.php` |
| Allocation | Percentage of a person on a project for a `YYYY-MM` period | `my.projects.php`, `includes/project.staff.allocation2.cls.php` |
| Month end | Checks, Excel imports, journals, roll to the next period | `month.end.php`, `month.end.checks.php`, `import.staff.costs.php`, `import.fin.levies.php`, `includes/month.end.checks.cls.php` |
| Timesheets | Control rows, PDF, blind capture, rejection, reminders | `timesheet.control.php`, `blind.capture.php`, `blind.bulk.capture.php`, `cron.daily.php`, `cron.hourly.php` |
| Leave pull | PaySpace applications into `tblLeave` | `leave.api.php`, `cron.employee.leave.php`, `database/tblLeave.sql` |
| Identity admin | Users, security groups, settings, email templates | `user.php`, `security.group.php`, `settings.php`, `email.template.php` |

`my.projects.php` includes `includes/project.staff.allocation2.cls.php`. `includes/project.staff.allocation.cls.php` is an older sibling.

## Milestones visible in Git

These are commit facts, not an approved roadmap.

| When | Commit | What landed |
|---|---|---|
| 2019-06-14 | `7d9727e` | Initial commit |
| 2019–2021 | several | Outstanding-timesheet report and staff filter changes |
| 2022-09-28 | `0cfaae1` | "DTHF Repo Updates" |
| 2023-03 | `e4612c5`, `42ebedc`, `48a5841` | PHP updates and month-end report fixes |
| 2023-10 | `af32517` and neighbours | PHP 8 dynamic-property and null fixes; PhpSpreadsheet installed in place of PHPExcel |
| 2023-10-19 | `0bf1436` | Database connection for a local vhost |
| 2023-11-01 | `ddfa1f1` | QA database details changed |
| 2023-11-06 | `a647fe3` | PHP 8.2 salary and finance-levy fixes. This was the onboarding scan tip |
| 2026-02-06 | `92b8332`, `ea1d04e`, `cd7fdab` | Additional cron and PDF changes; debug code removed |
| 2026-06-19 | `fe527e3` | "Disabled Leave Integration changes" |
| 2026-06-19 | `93552d8` | Merge of branch `Leave-integration`, pull request #1. Current `master` |

File comments inside the initial tree go back to 2012 (`_nemo.cls.php`, "20121204"). Those comments predate the 2019 initial commit. Provenance: **Documented** as comments. The Git history before 2019-06-14 is **Unknown**.

## Implementation plan

No forward plan is written in the repository. The only stated temporary change is the 2026-06-19 comment "Temporarily remove leave from Timesheet" in `includes/timesheet.control.cls.php` and `staff.php`. That comment sits on code that:

- still queries `tblLeave` while building a PDF, then does not paint leave codes because the paint block is commented out;
- still has `Staff::renderStaffCompanyLeave()`, then does not call it because the call in `staff.php` is commented out;
- still has `cron.employee.leave.php` calling `getEmployeeLeaves()` for the current year and month.

Whether leave display and the PaySpace pull are meant to be turned back on is **Unknown / Requires Confirmation**.

## Known dependencies

- PHP, with PHP 8.2 called out by the 2023-11-06 commit message. `composer.json` does not constrain a PHP version.
- Composer packages: `mpdf/mpdf` ^8.1, `phpoffice/phpspreadsheet` ^1.29. `composer.lock` is committed. `vendor/` is committed.
- Older libraries still in the tree: TCPDF, mPDF (2019 tree), FPDF, FPDI, PHPExcel.
- MySQL via `mysqli`, on Hetzner hostnames for live and QA, and `localhost` for the `lamp` server name.
- SMTP settings read from `sysSettings` keys `SMTP Send As` and `SMTP BCC`. The mail transport implementation is `NemoEmail` in `_nemo.email.cls.php`.
- PaySpace OData (`https://api.payspace.com`) and YourHCM identity (`https://identity.yourhcm.com`).
- Bitbucket Pipelines image `python:3.5.7`, installing `zip`, `git`, and `git-ftp`. The committed pipeline runs `git ftp init` and has `git ftp push` commented out.
- Host cron. A comment in `cron.daily.initialize.timesheets.php` says the project already had the maximum of 10 cron jobs, which is why that script was not the place the 4th-of-month job stayed. The crontab itself is not in Git.

## Current status

**Implemented** and present on `master` as of 2026-06-19: the allocation, month-end, timesheet, and email-reminder application described above.

**Implemented, then switched off in the UI** on the same day as the leave merge: painting approved leave onto the timesheet calendar, and showing the leave table on the staff record.

**Not runnable from this tree alone:** the PaySpace token call. `leave.api.php` reads `$CLIENT_ID` and `$CLIENT_SECRET`. Those variables are not assigned anywhere in the repository. `authenticate()` returns HTTP 400 "Missing credentials" when they are empty.

**Not established:** who operates the host cron, whether production FTP deploy still uses the commented pipeline, and whether the security-group names in code comments still match `sysSecurityGroup` in the live database.
