# Business rules — dev.knowmore (Knowmore)

Evidence date: 2026-10-17.

Source: `git@bitbucket.org:overdrive/dev.knowmore.git`, `master` at `43f9c23` (2021-02-10).

No business-rules document exists in the source repository. Rules below are taken from code and from comments next to that code. Anything marked **Inferred** is a conclusion from that evidence, not a written policy. Secrets and personal data are not reproduced.

## Identity and access

### BR-001 — Training pages require a learner session

**Implemented.**

`NemoBasic` redirects to `../second.wp.php?MID=LD9DHR` and exits when `$_SESSION[LEARNER]` is unset. The file that creates the session is not in this repository.

Evidence: `training/_nemo.basic.cls.php`.

### BR-002 — Tools pages require an advisor session

**Implemented.**

`tools/session.php` sends the browser to `../index.php` when `$_SESSION[ADVISOR]` is missing. A successful advisor login, and the learner hand-off in `training/home.tools.php`, both store the account password on the session and in the cookie `celpassword`.

Evidence: `tools/session.php`, `Login2()` in `tools/webadmin/project.functions.inc.php`, `training/home.tools.php`.

### BR-003 — Advisor login is email, MD5, and active flag

**Implemented.**

`tools/login.php` loads `tblUser` by email joined to `tblPractice`. `blnActive` must be 1. The password matches when `md5` of the posted value equals `strPasswordMD5`. Success redirects to the landing page stored in `sysSettings`. The attempt, including the clear-text password, is inserted into `sysLogin`.

Evidence: `tools/login.php`.

### BR-004 — Tools admin login does not enforce the password

**Implemented.** Requires confirmation that this file is the live staff login.

`tools/webadmin/login.php` contains:

- `if((isset($btnLogin)) || ($btnLogin = "Login"))` — the second clause assigns `"Login"`, so the condition is true whenever it is evaluated.
- `if($np->checkPassword(...) || (isset($btnLogin)))` — after that assignment, `$btnLogin` is set, so the password check is not required.

The page also includes `webadmin/_nemo.password.cls.php`, which is not in git. SQL debug is left on (`$xdb->blnDebug=1`).

The surrounding code shows the intended rule: an active `sysUser` in an active security group, checked by `NemoPassword`. That intended rule is not what the boolean expression does.

Evidence: `tools/webadmin/login.php`.

### BR-005 — Five failures were meant to lock the account

**Documented** in a comment. The calls found in the login pages are commented out.

`CheckSecurityLockout` reads the last five `tblLogin` rows for the user. If all five fail, it sets `tblUser.blnActive = 0`, `strLastUser = 'Security Lockout'`, and notifies. The message text still says "Please contact Celestis". If any of the five succeeded, or there are fewer than five failures, it clears a lock that was set by that same `strLastUser`.

`tools/webadmin/login.php` and `tools/login.php` contain the call only as a comment. Client login writes `sysLogin`, while this function reads `tblLogin`. Even if the call were enabled on the client login, it would not see those rows.

Evidence: `tools/webadmin/include/system_functions.inc.php`, comments in both login pages.

### BR-006 — Staff screen rights come from the security tables

**Implemented.**

For a logged-in `sysUser`, `LoadSecurity()` stores `sysSecurity` joined to `sysMenuLevel2` on the session, keyed by URL. `checkAdminLogin()` only tests that `$_SESSION[USER]` is set, then redirects to `$SystemSettings["LogoutRedirect"]` when it is not.

Evidence: `tools/webadmin/project.functions.inc.php`.

### BR-007 — SLA quote WPS filter is locked for non-admin groups

**Implemented.**

Security groups other than 1 and 2 get a disabled WPS filter set to that user's `vieWPS` id, or `-1` when the user is not in the view. The comment names groups 1 and 2 as admin and admin-dev.

Evidence: `webadmin/includes/sla.quote.cls.php`.

### BR-008 — Business tools need a practice

**Implemented.**

If the learner's `tblUser.refPracticeID` is 0, `training/home.tools.php` redirects to `login.business_tools.php`. A 2015 comment says the user must complete practice setup again.

Evidence: `training/home.tools.php`.

## Catalogue and CPD

### BR-009 — A course stays listed for one week after it starts

**Inferred** from an inline comment dated 2012-10-16, repeated on the four training purchase pages, citing KnowMore course TEA/6. The comment says the course is shown again for one week after the start date.

Evidence: `training/purchase.php` and the sibling purchase pages.

### BR-010 — User category and WPS are hidden on the overview

**Implemented.**

Comments dated 2017-03-08 say both blocks are permanently hidden. The markup sets `display: none` for both. A missing practice WPS still defaults the id to `-10` for the query that is no longer shown.

Evidence: `training/index.php`.

### BR-011 — Overview BAT charts use questionnaire 4 and calendar years

**Implemented.**

Current-year and previous-year charts read `dwfBatResults_2020` for the learner's practice, limited to `refQuestionnaireID = 4`, and sum red, amber, and green. A chart block is rendered only when at least one year has a non-zero total.

Evidence: `training/index.php`.

### BR-012 — BAT itself is version 1 and licence-gated

**Implemented.**

`tools/bat.engine.php` sets `BATVERSION = 1` and states that this value is the questionnaire id inside `bat.class.php`. It checks product access before continuing. A 2016 comment says switching between a BAT1 database and a BAT2 database is deprecated.

Evidence: `tools/bat.engine.php`.

This questionnaire id (1) is not the id used on the overview (4). **Requires confirmation** which questionnaire is the business assessment a learner is meant to see.

### BR-013 — Wealth Intelligence CPD is awarded on every third item

**Inferred** from comments in `training/my.cpd.points.php`.

2017-08-11 changed the points from 0.5 to 0.1667. 2017-08-19 changed that to 0.5 or 0, with a rule that every third Wealth Intelligence item is 0.5. 2017-08-24 adds blurb about dividing those points into thirds.

The arithmetic was not line-traced. The comments are the evidence.

### BR-014 — CPD cycles named in comments

**Documented** in comments dated 2019-04-17.

The 2018 cycle is 1 January 2018 to 31 May 2019. The 2019 cycle is 1 June 2019 to 31 May 2020. Later cycles are not named in that comment. The hard-coded catalogue in `training/my.assignments.php` lists item end dates in 2021.

Evidence: `training/my.cpd.points.php`, `training/my.assignments.php`.

### BR-015 — A learner with a blank ID number is asked for one

**Documented** in a 2015-03-03 comment on `training/my.cpd.points.php`: request the learner ID number if it is blank. The CPD PDF comments also add ID number and CPD cycle to the report (2019-04-17) and a disclaimer (2019-04-16).

### BR-016 — New learner defaults from phase 2

**Documented** in comments on `training/my.cpd.points.php`.

2016-10-25: default practice to null on a new learner. 2016-10-26: default `blnSuperUser` and `blnPracticeContact` to 1.

Whether those defaults still run from `learner.cls.php` was not re-traced. Treat the comments as the record of the rule.

### BR-017 — Publications label, Wealth Intelligence system key

**Implemented.**

The visible menu item is Publications. The session system and iframe path remain Wealth Intelligence. `tools/menu.php` comment dated 2019-04-23 says to replace the Wealth Intelligence label with Articles, and the code echoes "Publications".

Evidence: `training/_nemo.basic.cls.php`, `tools/menu.php`.

## Commerce

### BR-018 — VAT rate changes on 1 April 2018

**Documented** in comments, and **implemented** where the code reads `$SystemSettings[VAT]`.

Comments dated 2018-03-22 say VAT moves from 14% to 15% for the countrywide increase, using the system setting. SLA quotes created before 2018-04-01 stay on 14%.

Evidence: `tools/payGateResults.php` header, `webadmin/includes/sla.quote.cls.php` header.

The numeric rate is data in `sysSettings`, not a literal in those comments.

### BR-019 — PayGate return must match the checksum

**Implemented.**

The return page rebuilds an MD5 from the PayGate fields plus a secret and compares it to `CHECKSUM`. The secret committed in `tools/payGateResults.php` is the literal `test`. A mismatch is treated as a failed transaction.

### BR-020 — Netcash completion steps do not run

**Implemented.**

`tools/purchase.netcash.php` comments describe updating the transaction, inserting the invoice, generating and activating the licence, then showing a message. The next executable line prints and exits. Those steps are unreachable.

### BR-021 — Licence renewal notices

**Implemented** in the cron file headers.

Daily: licences expiring in 6 weeks, 2 weeks, or 3 days, and licences that expired yesterday. If no renewal row exists, insert one with response 0. Expired licences are marked expired and emailed. Not-yet-expired licences get a warning email. Each send is queued on `tblCronJobToDo`.

Weekly: a single email listing licences expiring within `+8 weeks` (the header says "the next 2 months").

Hourly: send up to 400 queued jobs. A job left in status 1 for 90 minutes is reset to status 0. A 2012 comment says the host allowed cron at most every two hours and at most 500 emails.

Evidence: `tools/cron.daily.php`, `tools/cron.weekly.php`, `tools/cron.hourly.php`.

### BR-022 — CAT reminders and consulting hours

**Implemented** as the second and third daily jobs.

CAT: clients who have not completed the survey and have not opted out are reminded. The header says expiry for that reminder is today plus one week.

Consulting hours: import `ConsultingHours.csv`. Marketing subjects are stored with `dblHours = 0` (comment 2011-03-17).

Evidence: `tools/cron.daily.php`.

### BR-023 — SLA proposal status stays "proposal"

**Documented** in a 2016-03-14 comment: finalize was deprecated because status is always proposal.

A proposal that contains inactive services cannot be edited (comment 2018-03-20). Partner types in the filter are Partner rows plus the constants Strategic Partner, Corporate Partner, and Strategic Partner (Acsis Legacy only). Package comments name Knowmore Standard, Plus, BCH M, BCH H, and Consulting Services.

Evidence: `webadmin/includes/sla.quote.cls.php`.

## Content and operations

### BR-024 — Ads were removed in phase 3

**Implemented** as a comment and a commented include.

2017-01-11: the ads table was deleted in phase 3. `Learner_Right_Content()` does not include the ads module.

Evidence: `training/_nemo.basic.cls.php`.

### BR-025 — Logout always uses the non-WordPress URL

**Implemented.**

Both the training header and the tools header set the logout URL inside `if($_SERVER[SERVER_NAME] == "lamp" || TRUE)`. The `TRUE` makes the WordPress `wp-login.php` branch unreachable.

Evidence: `training/_nemo.basic.cls.php`, `tools/header.php`.

### BR-026 — Missing host name means live

**Implemented.**

If `SERVER_NAME` is not set, `tools/webadmin/system.php` sets it to `www.knowmore.co.za` before choosing the database. Cron jobs that include this file and have no host therefore use the live database. Bare hosts `knowmore.co.za` and `qa-celestis.co.za` are rewritten to the `www` names.

Evidence: `tools/webadmin/system.php`.

### BR-027 — Sessions last two hours

**Implemented.**

`session.gc_maxlifetime` is 7200 seconds. The session file path is the host's `sessionpath`. A comment says the tools session path must be the Knowmore session path or tools will not work.

Advisor cookies from `Login2()` expire in 3600 seconds. `tools/session.php` sets them for 7200 seconds. The two callers disagree.

Evidence: `tools/webadmin/system.php`, `Login2()`, `tools/session.php`.

### BR-028 — Contact details shown to learners

**Implemented** as footer copy, not as a setting.

Phone +27 21 524 4669. Email info@knowmore.co.za. Copyright KnowMore 2018. Developer Silicon Overdrive. Invoice PDFs name accounts@knowmore.co.za and www.knowmore.co.za.

Evidence: `training/_nemo.basic.cls.php`, `webadmin/includes/invoicePDF.cls.php`.

## Conflicts

| Topic | What the code does |
|---|---|
| Product name | KnowMore in the footer and mail. Celestis in cookies, the forgot-password title, and old database names. American Shutters in `fim_2.0`. |
| BAT questionnaire | Engine uses version 1. Overview charts filter questionnaire 4. |
| Login audit table | Client login writes `sysLogin` and also reads it for "last logged in". Lockout reads `tblLogin`. Admin login writes both, via `tblLoginInsert` and via `sqlObject("tblLogin")`. |
| Password storage | Advisor front uses MD5. Admin is written to call `NemoPassword`, then skips that call. Passwords are also stored on the session and in a cookie. |
| FIM target | Framework class reports on KnowMore. `fim_2.0` config connects to American Shutters. |
| CPD list | Database-driven page versus a hard-coded HTML catalogue with 2021 end dates. |
