# PRD — eitaws (eIT)

Evidence date: 2026-10-15. Source: `git@bitbucket.org:overdrive/eitaws.git`, branch `master` at `9dc596e` (2020-07-02, "API URL").

There is no prior PRD in the repository or in Silicon Ark. Requirements below were recovered from this tree. Provenance is marked on each item. Other branches (`development`, `php8.2_upgrade`, `php8.2_upgrade_josh`, `php8.2_upgrade---Wessels`) were not file-inspected.

## Product purpose

eIT is the internal system Silicon Overdrive staff use to record client support work and turn that work into invoices. A signed-in user captures billing lines, to-do items, clients, machines, licences, and insurance records, and can run a month-end invoice export.

Provenance: **Implemented** (`index.php`, `sheet.php`, `includes/menu.php`). The README calls this the working version on AWS as of 31 Mar 2020. Whether that sentence still describes production is **Unknown / Requires Confirmation**.

## Users and roles

| Role | What they can do | Evidence | Provenance |
|---|---|---|---|
| Anonymous visitor | Open the login page. Open the HR feedback form from that page. `robots.txt` disallows all crawlers. `header.php` sends `noindex,nofollow` | `index.php`, `robots.txt`, `header.php` | Implemented |
| User (level 5) | Sign in. Use Billing, To-do, Tracking, MLPA, Insurance, Clients, Reports, System, HR, and Requests. Edit only their own user row | `entities/user/view_details.php` | Implemented |
| Data Admin (level 7) | Level 5, plus edit other users, and the system lists that require level 7 or 9 | `includes/settings_entry.php` | Implemented |
| System Admin (level 9) | Level 7, plus batch operations | `includes/settings_entry.php` | Implemented |
| Flagged user | Extra rights stored on `tblUser`, independent of the three levels | `entities/user/view_details.php` | Implemented |

Security groups in the user form: `5` User, `7` Data Admin, `9` System Admin. The signed-in level is the first character of the `myCookie` cookie. Provenance: **Implemented**.

Flagged rights shown on the user form:

- Can send contact report, edit support contracts, edit ADSL services, ADSL super user, edit backup assessments, edit stock, edit software licences, edit users, edit staff leave, staff-leave super user, edit responsibilities, edit accounts, send accounts emails, access staff cost, edit request priority.
- Lock-on-save flags: Billing, Client, SLA.

The "Can Edit Request Priority" checkbox posts `blnEditRequestPriority` in its label, but the `name` attribute on that row is `blnEditResponsibility`, the same name as the responsibilities checkbox. Provenance: **Implemented** markup. Whether priority editing works is **Unknown / Requires Confirmation**.

Divisions on the user record: Hardware, Internet, Software. The report console divisions are Hardware, Linux, Software. Provenance: **Implemented** in both places. They do not use the same labels.

## Main workflows

### W1 — Sign in

Trigger: user posts the login form on `index.php`.

Inputs: `strUserName`, `strPassword`, `strExternalPin`. The form MD5-hashes the password in the browser before submit (`scripts/md5.js`).

Client checks: all three fields non-empty, otherwise an alert and the submit is cancelled.

Server checks, in order:

1. Load `sysSettings` where `strSetting = 'ExternalLoginPin'`.
2. Load `tblUser` for that username where `blnActive = 1` (`get_login()`).
3. Reject when the posted PIN is not equal to `strValue` on that settings row.
4. Accept when the posted password equals `md5` of the password column.

On success the response sets cookie `myCookie` to `strLevel` concatenated with `UserID`, lifetime 14370 seconds, and redirects to `sheet.php`.

On failure the page redisplays the form. No error text is written in the login branch that was read.

If the request has `overdrive` equal to `silicon`, the script copies request fields `username` and `password` into the login check before the button handler. Provenance: **Implemented**. Whether this is an intended single-sign-on hook is **Unknown / Requires Confirmation**.

`index.php` nulls `myCookie` in the current request and destroys the PHP session on every load, including the logout link, which only navigates to `index.php`. The snippet does not expire the cookie with `setcookie`. Provenance: **Inferred** — logout may leave `myCookie` in the browser. **Requires Confirmation** in a browser.

### W2 — Open the shell

Trigger: any `sheet.php` request.

`checkLogin()` redirects to `index.php` when `myCookie` is absent. When the cookie is present it refreshes the same cookie. It does not look up the user id before allowing the page. `getUserName()` later redirects to `index.php` if that id is not in `tblUser`.

Empty GET and POST shows the billing list. The menu carries year, month, and a hidden day, defaulting to today.

Menu buttons: Billing List, Todo List, Stock List (only when `blnEditStock` is 1), Tracking, MLPA, Insurance, Clients, Reports, System, HR, Accts (only when `blnAcctsEdit` is 1), Requests, Logout.

Provenance: **Implemented** (`includes/menu.php`).

The Accts handler in `sheet.php` includes the menu and no further page. What Accts is supposed to open is **Unknown / Requires Confirmation**.

### W3 — Capture a billing line

Trigger: Billing List, or a billing detail action.

The billing controller saves, copies, deletes, and saves the list. Detail fields used by the month-end export include client, date, technician, billing code, rate, multiplier, unit, fault, fix, location, kilometres, service ticket, authorised contact, and SLA.

Locations in the detail form: `ON-SITE`, `OFF-SITE`, `ON-SITE and OFF-SITE`, `ON-SITE (No Mileage)`.

Transit behaviour in the detail page script: if the client `blnTransitCharge` is not 1, the transit checkbox is cleared. If it is 1, `ON-SITE` and `ON-SITE and OFF-SITE` check it, and `OFF-SITE` and `ON-SITE (No Mileage)` clear it.

Provenance: **Implemented** (`entities/billing/view_details.php`).

A contact report and an order-number email open popup windows (`includes/mail_report.php`, `includes/mail_order_num_report.php`). Sending the contact report is also a user flag.

### W4 — Month-end invoice

Trigger: "Month End Invoice" on the billing list. The button asks for confirmation and describes the selection as billing for the selected month where:

- the invoice number is shorter than 2 characters
- the line is not a fixed SLA
- the client is not Silicon Overdrive
- the billing code marks the line to be billed at month end

The controller then:

1. Empties `tmpBilling`.
2. Inserts billing ids where invoice is empty or `0`, `Status = 1`, and the date falls in the selected year and month. If ignore-dates is posted, the date filter is dropped and the insert is limited to 1000 rows.
3. Runs `ExportBilling` in `entities/billing/action_iif_export.php`.
4. Calls `createQBInvoice()`.

The export query, which is stricter than the insert, keeps rows where the billing code is billable (`blnBill = 1`) or the code is `Labour NC`, the client name is not `Silicon Overdrive`, the billing-code type is not `Fixed SLA`, and the invoice string length is under 2. Non-SLA lines are those with `refSLAID = 0`. SLA lines are those whose billing-code type is `SLA`. A rollover rate (`dblRollOverRate`) is selected. The comment in the file says SLA time is billed at the SLA rate until the SLA hours are exceeded.

The invoice line name sent onward is the billing code (`strCode`). The header transaction date written in the export is the last day of the previous calendar month, not the year and month chosen in the menu.

`createQBInvoice()` sends the browser to `https://qb.overdrive.co.za/auth/` (`QB_API_AUTH_URL`) with the lines grouped by client, the current page URL, and `app=eit`. On success it opens the JSON response in a new window.

A separate request parameter `QBAPI` on `sheet.php` JSON-decodes a payload and, for each item that has an `Invoice`, sets `tblBilling.strInvoice` for the listed billing ids. `checkLogin()` has already run, so the request needs `myCookie`. No further permission check is in that block.

Provenance: **Implemented**. The service at `qb.overdrive.co.za` is outside this repository. Whether the confirm-dialog wording and the two SQL stages are the approved finance rule is **Requires Confirmation**.

Older IIF export buttons are commented out in the billing controller. `sheet.php` still has branches for IIF billing, IIF SLA, and IIF fixed SLA. A 2012 note, `doc new iif export pseudocode 20121207`, describes three exports: SLA, fixed SLA (show hours when they exceed the SLA amount), and ad hoc (exclude fixed SLA codes; overflow uses the rollover code rate). Provenance: **Documented** in that note, **Implemented** as the newer QuickBooks Online path plus leftover IIF branches.

### W5 — To-do

Trigger: Todo List.

Save, save-and-new, save-and-stay, delete, colour, and copy are handled in `sheet.php`. On create and update, `sendTodoEmail()` runs. Mail is sent only when the assignee `blnEmailToDo` is 1. The subject is `Todo Item - {id} - NEW` or `UPDATED`. If `boolManager` is 1, the manager address is copied. The body includes client, date, time, note, stock, and fault.

`system notes.txt` says to-do status `1` is active, `0` is deleted, and `2` is completed.

A billing line can be raised from a to-do (`btnAddBillingFromTodo`).

Provenance: **Implemented**, with the status legend **Documented** in `system notes.txt`.

### W6 — Clients

Trigger: Clients.

The client screen holds the name, one-way transit kilometres, notes, and flags used by billing and reports. Report filters name these client selectors: Accounts on Hold, Active Support, Hand Delivered, Must Get Auth, No Support Allowed, Order number required, Only Upfront Payments Allowed, PDF Invoice, Posted, Problematic Payments, Quote COD Rates Only, Sign book, Special Labour Rate, inactive clients, active clients.

`blnLocked` on the client (added in `DB changes.txt`, 2013-02-07, comment "if lock, record should not be editable") shows a Locked or Not Locked label. When the client is locked and the current user does not have `blnClientLock`, controls that use `$lockControl` are rendered readonly. The client-name input in the section that was read does not use `$lockControl`. Provenance: **Implemented** for controls that include `$lockControl`. Full coverage of every field was not verified.

### W7 — Tracking and MLPA

Trigger: Tracking or MLPA.

Tracking stores a machine against a client: supplier, antivirus, hard drive, memory, motherboard, Office, Windows, and the tracking row. Selected rows can be downloaded as an HTML table with an Excel content type.

MLPA uses `tblMLPA`. A 2019-08-05 comment says table references were changed from `tblTracking` to `tblMLPA`. The list filters by client, device type, supplier, and warranty window (already ended, within 3 months, within 6 months, still in warranty). Actions: add, delete, mark inactive (2019-12-17), export. A PDF path is titled "IT Asset & License Register". The PDF code that was read opens a PDO connection with an empty DSN and alerts when the PDF button is clicked. Provenance: **Implemented**. Whether that PDF path succeeds is **Unknown / Requires Confirmation**.

Device types, suppliers, disk, memory, motherboards, Windows, Office, antivirus, accounts, design, other, software licences, software type, and connectivity lists (3G, ADSL, BlackBerry, FTP, router) are under System, Tracking. A comment dates Device Types, Design, and Other to 2020-06-07.

### W8 — Insurance

Trigger: Insurance menu. Added in a 2019-10-01 comment. Save, delete, screen export, and insurance PDF exist on the controller. The controller also still contains MLPA export branches. Provenance: **Implemented**.

### W9 — Reports

Trigger: Reports, which opens `includes/report_console.php` in a window.

Filters: division, technician, client, support type, category, payment type, date range, client selector, client active flag, contact filter, billing code.

Reports: Billing Report Detail, Billing Report Summary, Billing Report Consolidated, Client Report by Selector, Client Report By Technician, Client Mileage Report By Technician, Contact Report Client, Contact Report Receivers, Accounts Report Receivers, Mileage Tracking Report, Mileage Tracking Report By Date, Mileage Report, No Contacts and No Accounts Report, Software Expiry Report, Tools Assigned Report, eIT Data Export, SLA Report Detail, Client Timesheet Report Detail, No Charge Monthly Billing Report (optional exclude Gold SLA clients), Active Clients With No Billing.

Provenance: **Implemented** as form actions. Each report file was not executed.

### W10 — System administration

Trigger: System. Renders `includes/settings_entry.php`.

Visible to every signed-in user: System Users, Company Vehicles, Client Backup Assessment Settings, and the Tracking lookup lists.

Visible when level is 7 or 9: transit rate per km, geographical area, billing codes, labour fix codes, labour fault codes, job status, client status, leave type.

Visible when `blnADSLService` is 1: ADSL Services.

Visible when level is 9, under the heading "Batch Operations": move all contact billing and to-do to another contact; mark all "Old User" client contacts inactive.

Visible when `blnSupportContract` is 1: Client SLA, client backup assessment, backup drives, client software licensing purchased, client ISP accounts, client domains, client DynDNS. Client ADSL services also require `blnADSLService`.

Visible when `blnCanEmail` is 1: Send General Emails.

Provenance: **Implemented**.

### W11 — HR

Trigger: HR. Links: Staff Leave, Responsibilities, Public Holidays, Feedback. Staff-cost links (monthly staff cost, and cost objects for ADSL, cards, contracts, cell, and vehicles) render only when `blnAccessStaffCost` is 1.

Leave save checks for an existing row and alerts "Error:Duplicate leave entry for the same user on that same date". A sentinel staff id `999999999` skips the update.

Feedback is also linked from the login page. The login copy says the submission is anonymous. The feedback controller saves on submit. Provenance: **Documented** on the login page, **Implemented** as a save. Whether the stored row omits the author was not verified in the feedback save action.

### W12 — Internal requests

Trigger: the Requests link, which opens the `requests` entity.

`change_requests.php` inserts or updates `tblX` with request text, hours, priority, section, and status. `checkLogin()` in that file is commented out. The menu path goes through `sheet.php`, which does call `checkLogin()`. Provenance: **Implemented**. Whether `change_requests.php` is reachable on its own URL is **Unknown / Requires Confirmation**.

## Functional requirements

### FR-001 — Staff authentication

The system shows a login form and enters the shell only after an active username, a password check, and the shared external PIN succeed.

- Actor: staff member.
- Trigger: submit on `index.php`.
- Output: `myCookie`, redirect to `sheet.php`.
- Validation: three fields required in the browser. Server requires an active user, PIN match, and `md5(stored password)` equal to the posted password.
- Errors: empty fields alert in the browser. A wrong PIN or password redisplays the form with no message in the code that was read.
- Evidence: `index.php`, `includes/db_sql.php` `get_login()`, `includes/system.php` `checkLogin()`.
- Implementation: Implemented.
- Confidence: High.

### FR-002 — Session cookie

The shell treats `myCookie` as the session. The first character is the level. The rest is the user id.

- Evidence: `getUserLevel()`, `getUserID()`.
- Implementation: Implemented.
- Confidence: High.
- Note: the cookie is not signed in the code that was read. **Inferred** risk. **Requires Confirmation** if a later branch changed this.

### FR-003 — Module shell

After login, Billing is the default module. The menu switches Billing, To-do, Tracking, MLPA, Insurance, and Clients through an entity folder. Stock, System, and HR stay on the legacy dispatcher inside `sheet.php`.

- Evidence: `sheet.php` entity include.
- Implementation: Implemented.
- Confidence: High.

### FR-004 — Billing capture

A user can add, save, copy, and delete billing lines for a client, with location, labour, distance, billing code, and SLA.

- Evidence: `entities/billing/_controller.php`, `entities/billing/view_details.php`.
- Implementation: Implemented.
- Confidence: High for the actions listed. Field-level validation beyond the transit script was not fully extracted.

### FR-005 — Month-end QuickBooks handoff

A user can run Month End Invoice for the selected month. The system builds an invoice payload and asks `https://qb.overdrive.co.za/auth/` to continue, with `app=eit`. A later `QBAPI` post writes invoice numbers back onto `tblBilling`.

- Evidence: `entities/billing/view_list.php`, `entities/billing/_controller.php`, `entities/billing/action_iif_export.php`, `sheet.php`.
- Implementation: Implemented on `master`.
- Confidence: High for the call shape. The external service contract is not in this repo.

### FR-006 — To-do with optional email

A user can keep to-do items. The assignee is emailed when `blnEmailToDo` is 1.

- Evidence: `sheet.php` save branches, `includes/system.php` `sendTodoEmail()`.
- Implementation: Implemented.
- Confidence: High.

### FR-007 — Client maintenance

A user can maintain clients, including transit distance, support flags, and a lock that restricts users who do not have `blnClientLock`.

- Evidence: `entities/client/view_details.php`, `DB changes.txt`.
- Implementation: Implemented.
- Confidence: Medium. Lock coverage is partial in the section that was read.

### FR-008 — Asset registers

A user can keep a machine tracking register and an MLPA register, filter MLPA by warranty, mark MLPA rows inactive, and export.

- Evidence: `entities/tracking/`, `entities/mlpa/`.
- Implementation: Implemented.
- Confidence: High for list and save. Medium for the PDF export, which contains an empty DSN in the section that was read.

### FR-009 — Reports

A user can open a report console and run the reports named in W9, filtered by the fields on that form.

- Evidence: `includes/report_console.php`.
- Implementation: Implemented as a console. Individual report queries were not all read.
- Confidence: Medium.

### FR-010 — Administration by level and flag

System lists, batch tools, support lists, ADSL, stock, accounts, and staff cost appear only when the level or the matching `tblUser` flag allows them.

- Evidence: `includes/menu.php`, `includes/settings_entry.php`, `includes/settings_entry_hr.php`.
- Implementation: Implemented.
- Confidence: High for the gates that were read. Not every `system_settings` include was audited against a direct URL.

### FR-011 — HR leave and feedback

Staff leave rejects a second entry for the same user and date. The login page offers an HR feedback form and says the submission is anonymous.

- Evidence: `sheet.php` leave branch, `index.php`, `entities/feedback/_controller.php`.
- Implementation: Implemented for the duplicate alert and the form link.
- Confidence: Medium on anonymity. The save action was not read line by line.

### FR-012 — QuickBooks Web Connector

The tree contains a QuickBooks Web Connector application with SOAP endpoints `qbwc.php`, `qbwcEIT.php`, and `qbwcTS.php`, aimed at a MySQL schema `quickbook_webservice`.

- Evidence: `quickbooks_webservices/`.
- Implementation: Present on `master`. Whether it is still started in production is **Unknown / Requires Confirmation**.
- Confidence: High that the code is present. Low that it is the live path, because month-end now calls the Online auth URL.

## Integrations

| System | Direction | Trigger | Data | Auth | Failure |
|---|---|---|---|---|---|
| `https://qb.overdrive.co.za/auth/` | Browser calls out | Month End Invoice | Invoice lines, `app=eit`, current page URL | Not in this repo. Response is opened in a new window | Ajax error handler alerts the response |
| QuickBooks via `QBAPI` | Inbound POST to `sheet.php` | After the external step. Caller not named in this repo | Invoice number and billing ids | `myCookie`, because `checkLogin()` runs first | No user-facing error in that branch |
| QuickBooks Web Connector | SOAP. QuickBooks desktop pulls | Web Connector schedule. `cron.hourly.php` is in the folder | Customers and invoices in the connector queue | Username and password in `quickbooks_webservices/config.php` | Library can mail an error. The live recipient was not confirmed |
| `mail()` | Outbound | To-do save when the user flag is on | To-do summary | PHP `mail()`. From header is the assignee address | No retry in `sendTodoEmail()` |
| FTP deploy | Pipeline pushes `development` | Bitbucket pipeline on that branch | Application files | FTP username, password, and host from pipeline variables | Not described in the pipeline file |

## Constraints

- Database credentials for the app live in `includes/db_connect.php`, which `.gitignore` excludes. They are not in git.
- `quickbooks_webservices/config.php` contains a database DSN, including a password. The value is intentionally omitted here.
- `display_errors` is enabled in `sheet.php` and `includes/system.php`.
- `phpinfo.php` and `info.php` call `phpinfo()`.
- Passwords are compared with MD5. The check is posted value equals `md5` of the column, and the browser already MD5s the typed password, which matches a column that stores the original password. Provenance: **Inferred** from that comparison. **Requires Confirmation**.
- The external PIN is one shared value in `sysSettings`, not a per-user second factor. The vendored OTP library is unused by application code.
- Time zone `Africa/Johannesburg` is set in the Web Connector config.
- A meta refresh of 14400 seconds in `header.php` is commented out.

## Behaviour that needs a product decision

- `master` stopped in July 2020. Later branches exist and were not read.
- README says AWS. The only deploy script is FTP of `development`.
- Month-end header date uses the previous calendar month at runtime, while the button talks about the selected month.
- Logout does not show a cookie expiry.
- The MLPA PDF builder uses an empty database DSN in the lines that were read.
- The Accts menu button has no destination page in the handler that was read.
