# PRD — ieb-inde-4 (IEB Docs)

Evidence date: 2026-10-13. Inspected revision: `main` at `e2be9d5`.

Provenance labels: **Documented** (readme or config text), **Implemented** (code path), **Inferred** (reasonable reading of that code), **Unknown**.

The website root used below is `INDE-4/Evolution Software Unified Framework/Evolution Software Unified Framework/core_custom/INDE-4/`.

## Product purpose

`INDE-4/ReadME.txt` states **Docs = INDE-4 (using INDE-2 user table)**. **Documented.**

The implemented custom website keeps schools, school contacts, affiliations, subjects, and clusters. A cluster has a Document Library tab that hosts the framework file uploader, and a Discussion Forum tab. No type, page, or module is named Docs.

The same website still contains the framework's admin, catalogue, loyalty, medical, and sales surfaces. Those files are present. This PRD does not treat them as Docs requirements unless a custom INDE-4 class calls them.

The claim that users come from the INDE-2 table is **Documented** only. The active connection string names catalog `Framework_INDE-4_Demo`. User queries in the custom surfaces read `pal_User` through the current setup customer code. No connection string names an INDE-2 catalog. **Unknown / Requires Confirmation.**

## Users and roles

`business/enums/pNums.cs` defines:

| Value | Name |
|---|---|
| 1 | WebsiteUser |
| 2 | PowerUser |
| 3 | SuperUser |
| 4 | AdminUser |
| 5 | CustomUser |

**Implemented** behaviour that names these roles:

- Users created by the school-contact spreadsheet and the cluster-member spreadsheet are stored as `WebsiteUser`, active, with `customerCode` set to `handler.ReturnSetup().code`.
- Users below `PowerUser` see the school edit link only when they are an IEB liaison contact for that school. `Schools_grid.ascx.cs`.
- Users below `PowerUser` see cluster member and meeting buttons according to their cluster resource row. `ClusterInformation_form.ascx.cs`.
- `CustomUser` is compared with `mimicUserType` on several grid action columns. The schools edit-column custom block bypasses that check and forces the header visible.

School contacts are published surface rows (`oSchoolContacts`), not a separate login type in the code that was read. The contact name field stores a user id.

How the framework combines `userTypes`, `isRestricted`, and `customUsers` at page-render time was not fully traced. **Unknown / Requires Confirmation.**

## Major workflows

### 1. Save a cluster and generate its code

Trigger: save of the Cluster Information surface. `ClusterInformation_form.ascx.cs` `SaveSurfaceFormCustom` calls `PerformCustomSaveAddOn` before the generated field copy.

When the cluster-code textbox is empty, the addon builds:

`{level abbreviation}-{subject code}-{province lookup customCode}-{nn}`

All three parts must resolve (`validCount == 3`). Otherwise the code stays empty. `nn` is `01` when no `publ_ClusterInformation` code starts with that prefix. Otherwise it is one more than the count of matching codes, padded to at least two digits.

The addon return value is ignored. The generated text is written onto the textbox, and the generated save that follows reads that textbox. **Implemented.**

On the same save, if no `publ_ClusterResources` row has this cluster's `itemID` as parent, every resource with `ParentSurfaceItemId = 0` is copied onto new surface items of surface id `3048`.

### 2. Open a cluster

Trigger: populate of the cluster form. Dated 2018-05-16.

- The code textbox is always disabled.
- Once the code is non-empty, province, level, and subject name are disabled. Cluster name and area are not disabled by this block.
- Subject names are rebound from `publ_SubjectManagement` for the selected cluster level.
- For a user below PowerUser, the new-member button (surface `3046`) is visible when a resource name equals the user id. The new-meeting button (surface `3076`) is visible when that same resource type is `Cluster Administrator` or `Cluster Leader`. The loop stops at the first matching resource.

### 3. Add a cluster member

Trigger: cluster member form. Dated 2018-05-15 and 2018-05-17.

The name picklist lists active `pal_User` rows for the current customer code whose ids are school contacts of the selected school for `DateTime.Now.Year`. Choosing a user copies email, SACE number, and active year from the matching `publ_SchoolContacts` row. Those three fields are disabled.

"Add New" (`-1`) is commented out of this picklist. The modal script for `#modContactAdd` remains if a negative id is selected.

Active year is stored as 1 January of the year typed in the box.

### 4. Add a cluster resource

Trigger: cluster resource form. Dated 2018-05-16.

The name picklist is every active `pal_User` for the current customer code, plus an "Add New" item (`-1`) that opens `#modContactAdd`. Choosing a user copies that user's email. Resource type is disabled on populate.

After a contact is added, session `contAdded` selects that user and fills the email.

Editing a cluster resource from the parent cluster repeater saves the cluster first when the cluster item is new or when the cluster has no resource rows yet.

### 5. Save a cluster meeting

Trigger: meeting save. Dated 2018-05-17.

On every save the meeting copies area, cluster code, cluster name, level name, province display, and subject name from the parent cluster. The province copy uses lookup `display`, not `customCode`. A comment in the code asks whether `customCode` was intended.

On the parent cluster's child grids, the meetings edit and remove headers are forced visible. For a user below PowerUser, the row edit and remove links follow `AllowMeetings`.

### 6. Import people from Excel

Trigger: child import on the schools form (2018-05-14) or the cluster information form (2018-05-16), and only when the child surface `isPublished`.

Both paths create an `oUser` when email plus customer code does not already exist. Existing rows are not updated. The password is a random 8-character value, stored encrypted. The import then calls `ImportDataToSurfacePublished`.

| Import | Columns read | Extra fields set |
|---|---|---|
| School contacts | Email, Mobile, Title, RSA ID Number, Name | `tel`, `title`, `idNumber` |
| Cluster members | Member Email, Member Name | none of the school-contact extras |

Neither path sets `isReset` in the block that was read.

Name splitting differs. See business rules.

### 7. Edit a school

Trigger: schools grid row. Dated 2018-05-15.

The edit column header is forced visible. For a user below PowerUser, the edit link is visible when a published school contact exists with `ParentSurfaceItemId` equal to the school item, `SchoolContacts_SchoolContacts_Name` equal to the user id, and `SchoolContacts_SchoolContacts_IEBLiaison` equal to `1`.

This block does not remove the row from the grid. View and remove still follow grid options. **Implemented.**

### 8. Choose a school contact

Trigger: school contact form. Dated 2018-05-16.

The name picklist is active users for the current customer code, plus "Add New". Choosing a user copies telephone into mobile and email into the contact email. If the user's title text matches a list item, that item's display text is replaced. Session `contAdded` rebinds the picklist the same way as cluster resources.

No assignment to `SchoolContacts_SchoolContacts_SchoolName` or `SchoolContacts_SchoolContacts_SchoolType` was found in this form. Those properties exist on `oSchoolContacts`. **Unknown** whether a database surface action fills them.

## Integrations

| System | Direction | Evidence | Failure behaviour in the custom blocks |
|---|---|---|---|
| SQL Server catalog `Framework_INDE-4_Demo` | Site reads and writes through `conn` | Active `Web.config` app setting | Custom methods catch, call `exception.HandleException`, and redirect to `/error` |
| WCF `framework_service` | Outbound from the site | Endpoints `http://localhost:58913/framework_service.svc` and `http://service.ieb.co.za/framework_service.svc` | Not traced in the custom surfaces |
| SMTP `smtp.evolutionsoftware.co.za` | Outbound mail | `Web.config` `system.net/mailSettings` | Not traced here |
| SendEasy SMS `http://sms.netgen.co.za/SMS_V2.asmx` | Outbound | Client endpoint plus SMS app settings | Credentials are present. Values omitted |
| MediSwitch QA gateway | Configured | Three QA URLs. Live URLs are commented. `MediSwitchResponses` = `0` | Feature switch off in config |
| Country web service `webservicex.net` | Configured | `getCountriesFromWebService` = `0` | Feature switch off in config |
| SonarQube | CI only | `bitbucket-pipelines.yml` | No build or deploy step |

Rocket.Chat was not found under the INDE-4 website. **Implemented** absence in this tree.

## Functional requirements

### FR-001 — Project identity

The readme identifies this repository as Docs = INDE-4, using the INDE-2 user table.

Provenance: **Documented**. The user-table half is not confirmed by a connection string. **Requires Confirmation.**

### FR-002 — Website title keys

`Web.config` contains two `website` keys, in this order: `IEB (INDE-4 Local)`, then `IEB CRM`. The mail subject key is `IEB (INDE-4 Local) - {content}`. `WebAddy` is `http://localhost:21552`. `defaultCulture` is `en-ZA` and is declared twice.

Which value a running site displays was not executed. **Requires Confirmation.**

### FR-003 — Roles

The five `pNums.UserType` values exist. Custom behaviour treats PowerUser as the threshold below which school-edit and cluster-button rules apply.

Provenance: **Implemented**.

### FR-004 — Schools

An operator can store a school: name, centre number, type, phone, physical address, postal address, primary email, affiliations, NSC year, and PSI year. `oSchools`.

Provenance: **Implemented** as the object and the schools surface files. Which of these fields are required is database validation. **Unknown.**

### FR-005 — School edit for an IEB liaison

A user below PowerUser gets the school edit link only when they are the IEB liaison contact on that school. The edit column header is always shown. The row remains in the grid.

Provenance: **Implemented**. Principal is stored and is not used in this check.

### FR-006 — School contacts

A school contact stores a user id as its name, plus title, email, mobile, SACE, RSA id, non-RSA id, area of responsibility, active year, IEB liaison, and principal. The form can pick an existing user or open an add-contact modal.

Provenance: **Implemented** for the picklist and the copy of email, mobile, and title text. Copy of school name and school type was not found in this form.

### FR-007 — School affiliations

An affiliation stores a name and an abbreviation. `oSchoolAffiliations`. The custom populate and save addons in the affiliations form contain no INDE-4-specific body. A `STUD-1` branch exists and does nothing unless the setup code is `STUD-1`.

Provenance: **Implemented**.

### FR-008 — Subjects

Subject management stores subject name, subject code, and cluster level. `oSubjectManagement`. The custom save addon body is empty aside from the shared `STUD-1` branch.

Provenance: **Implemented**.

### FR-009 — Cluster record

A cluster stores code, name, area, level, province, subject name, subject code, a discussion-forum string, and a document-library value. The Document Library tab hosts `controls/module/fileUploader.ascx`.

Provenance: **Implemented**.

### FR-010 — Cluster code

The system generates the cluster code on save when the code box is empty and level, subject, and province all resolve. The code box is always disabled. Province, level, and subject name lock after a code exists.

Provenance: **Implemented**.

### FR-011 — Template resources

The first save of a cluster that has no child resources copies every resource whose parent item id is 0 onto surface `3048`.

Provenance: **Implemented**. What those template rows contain is **Unknown** (database).

### FR-012 — Cluster members

A member stores school, name, email, SACE, and active year. The name list is this year's contacts of the selected school. Email, SACE, and active year are filled from the contact and are not editable on this form.

Provenance: **Implemented**.

### FR-013 — Cluster meetings

A meeting stores date, time, month, location, description, status, and documents, and overwrites its reporting labels from the parent cluster on each save.

Provenance: **Implemented**.

### FR-014 — Cluster resources and meeting rights

A resource stores a user id, email, and type. Resource type is disabled. Users below PowerUser may add members when they are a resource, and may add or edit meetings when that resource type is exactly `Cluster Administrator` or `Cluster Leader`.

Provenance: **Implemented**.

### FR-015 — Spreadsheet import creates website users

Published child imports on the schools form and the cluster information form create a WebsiteUser for a new email. They do not update an existing email. Unpublished child imports skip the user-creation block and still call `ImportDataToSurface`.

Provenance: **Implemented**.

### FR-016 — Examination classes are present

`examNums` defines subject status ids, compliance ids, moderation result ids, and subject resource roles. `examHandler` contains `EmailExamStatus` and calls `sp_INDE_GetCriterionFeedbackData`. `CriterionFeedback.ascx` is a custom module. No examination surface sits under `surface_custom`.

Provenance: **Implemented** as code. Whether INDE-4 publishes these surfaces is **Unknown / Requires Confirmation**.

### FR-017 — Billing instructions module is present

`billingInstructions.ascx` is a custom module with account, supplier, tenant, and landlord fields. No Docs workflow calls it in the surfaces that were read.

Provenance: **Implemented** as a file. Product use is **Unknown**.

### FR-018 — Framework pages ship in the same site

The INDE-4 site folder includes the framework admin pages and public pages `Default.aspx`, `Canvas.aspx`, `Cart.aspx`, `Products.aspx`, `Media.aspx`, `Search.aspx`, `Reset.aspx`, `Unsubscribe.aspx`, and `Error.aspx`.

Provenance: **Implemented** as files. They are not, by themselves, Docs requirements.

## Constraints

- Custom surface behaviour depends on SQL Server rows (`publ_*`, `pal_User`, surface ids `3046`, `3048`, `3076`). Those rows are not in git.
- `data/Scripts` has no `INDE-4` folder.
- The active connection is a demo catalog. Commented keys name `Framework_INDE-4` on a local server and on a remote server.
- `WebAddy` is localhost. Mail and links that embed it will point at a developer machine unless another config overrides it. No release transform was compared line by line.
- Several custom queries concatenate selected ids and codes into SQL text.
- Secrets for the database, SMTP, and SMS are committed in `Web.config`. They are not repeated here.
- No automated test project was identified.

## Non-goals of this document

This PRD does not promote framework catalogue, loyalty, medical, or sales screens into Docs requirements. It does not treat SQL script folders for other customer codes as INDE-4 features. It does not decide whether the readme's user-table sentence is still the business rule.
