# Business rules — ieb-inde-1

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

Rules below are **Implemented** unless marked **Inferred** or **Unknown / Requires Confirmation**. No separate business-rules document exists in the source repository. Rules are taken from `examHandler`, `examNums`, and the custom include regions. They are not a decision about what the rules should be.

Lookup display text is database data. Where the code compares display text, that comparison is the rule. Where it uses `customCode` first, the database code wins.

## Identity of a subject

1. After a subject surface item exists, year, type, level, paper, and subject name cannot be edited on the form. Subject code stays disabled. Document password stays read-only.
2. An empty document password is filled with four random letters, a number from 0 through 999, and four random letters.
3. The five resource fields each store one user id. `GetSubjectResourceRole` returns the first match in this order: examiner, reviewer, internal moderator, external moderator, assessment specialist.

## Who may edit

4. A user whose framework type is below SuperUser cannot change the five resource dropdowns, and the subject form hides `btnSave` and `btnSaveDropdown`.
5. The status dropdown is enabled only for the user stored as reviewer.
6. A WebsiteUser is given a script that hides one hardcoded internal-collaboration "new" control. Other log controls are not hidden by that script.
7. The new document-pack button is shown for examiner and reviewer and hidden for assessment specialist, internal moderator, and external moderator.
8. On the exam-documents form, the new-document button and "document upload complete" are visible only for the examiner or the reviewer.
9. On the internal-moderation form, only the internal moderator gets `SetState(true)`. Everyone else, including role 0, gets `SetState(false)`. The form hides the main save buttons.
10. On the external-moderation form, only the external moderator gets `SetState(true)`. Same hide of the main save buttons.
11. A new attachment button is hidden for assessment specialist, internal moderator, and external moderator. Examiner and reviewer are not hidden by that switch.
12. On a surface, an assessment specialist does not get a hyperlink to the file. Other roles do.
13. Edit and remove are hidden for assessment specialist and both moderators. Remove is hidden for every user below SuperUser.
14. When the attachment field name is `ExaminationDocuments_ExaminationDocumentDetails_Files`, the edit column is hidden with CSS class `noShow`.

**Requires Confirmation:** rule 4 hides save for anyone below SuperUser, including a reviewer who is a WebsiteUser or PowerUser, while rule 5 still enables the status dropdown for that reviewer. The code does not say which user type live reviewers have.

## Status changes

15. A status-change collaboration note is written only when the signed-in user is the reviewer, the item already exists, the selected status differs from the stored status, and at least one active document pack exists for the subject.
16. That note uses the external log for: EM compliant minor changes, EM compliant translation ready, EM moderation required, EM not compliant. Every other selected status uses the internal log.
17. The note is stored only together with the first active pack in descending revision order. The pack number and revision are part of the note.
18. If the note save succeeds, status, `TimeSinceStatusChange`, and `EmailExamStatus` run. The generated save then writes the selected status again.
19. If no active pack exists, the log, the time stamp, and the status email in that block do not run. The generated assignment still writes the selected status. **Implemented.** Whether that split is intended is **Unknown / Requires Confirmation**.

## Who is emailed for a status

20. Template name must be `Examination Moderation Status`. Subject line is `IEB: {subject name} ({status display})`.
21. Recipients:

| Status | Recipient |
|---|---|
| No activity (1093) | none |
| Examiner uploaded, review ready (1083) | reviewer |
| Reviewer query (2160) | examiner |
| Reviewer uploaded, IM release ready (1084) | none |
| IM moderation required (1085) | internal moderator |
| IM not compliant (1086) | reviewer and examiner |
| IM compliant, EM release ready (1087) | reviewer and examiner |
| IM compliant, minor changes (2168) | reviewer and examiner |
| EM moderation required (1088) | external moderator |
| EM not compliant (1106) | reviewer and examiner |
| EM compliant, minor changes (1107) | reviewer and examiner |
| EM compliant, translation ready (1108) | reviewer and examiner |
| Post-translation change required (1109) | none |
| Print ready (1110) | none |

22. Where two people are named, the reviewer address is first and the examiner address is appended after a semicolon. The greeting name is `"{reviewer name} and {examiner name}"`.
23. A single recipient is merged with `utils.MergeHTMData`. The two-person cases replace `{name}` only.
24. Empty recipient skips the send.
25. BCC is app setting `bcc`. The checked-in value is empty. From address is setup `communicationEmail`.

## Upload complete

26. If the clicker is the examiner, status becomes 1083 and the log message is "Examination documents uploaded".
27. If the clicker is the reviewer, status becomes 1084 and the log message is "Revised examination documents uploaded".
28. Any other resource does not match either branch. The code still updates the subject, calls `EmailExamStatus` with the unchanged status, and writes the internal log with an empty message. **Implemented.** The upload button is hidden for those roles on populate, so this path is only reachable if the button is shown another way.
29. The log save then emails the reviewer as "System Post", in addition to any status email from rule 21. Status 1084 therefore emails the reviewer once, via the log, and does not send a status email.

## Document pack numbering

30. When revision text is `0`, the new revision is the count of active packs for the parent item, plus 1.
31. An empty pack number is `{year}-{type}-{level}-{paper}-{subject code}-Rev{revision}`.
32. On the Exam Documents, Internal, and External forms, type, level, and paper use `customCode` when it is not empty.
33. When `customCode` is empty, type "year-end" (case-insensitive) is `Y` and every other type display is `S`.
34. Level display "grade 12" through "grade 8" maps to `G12` through `G8`. Any other level display is `G0`.
35. Paper display "paper 1" through "paper 6" maps to `P1` through `P6`. Any other paper display is `P0`.
36. `ExaminationDocumentPack_form.ascx.cs` uses rules 33–35 and does not read `customCode`.
37. Opening a pack whose `recId` is 0 saves it immediately.

## Moderation completion

38. Changing the overall compliance value away from the stored value, while signed in as the matching moderator, writes the collaboration log.
39. Internal completion uses the internal log and sets `IMDateModerationComplete`. External completion uses the external log and sets `EMDateModerationComplete`.
40. The pack's internal or external moderation status text is set to the lookup display of the overall value when that value is greater than 0.
41. Completing moderation does not set the subject status enum. Moving the subject to "IM moderation required", "EM moderation required", or a compliant status remains the reviewer status dropdown (rules 15–21).
42. A compliance summary is rendered only when the overall compliance id is greater than 0. Otherwise the summary string is empty.
43. Per-criterion colour: no compliance and limited compliance are red; compliance in most respects is orange; compliance in all respects is green.
44. Overall colour: not approved, and conditionally approved that must be submitted again, are red; conditionally approved that is not to be submitted again is orange; approved is green.
45. Feedback tables read child surfaces by fixed ids (internal 3062–3073 skipping 3064; external 3088–3099 skipping 3090) through `sp_INDE_GetCriterionFeedbackData`. Missing rows display "No feedback". The column header in code is the string "Reason for Non-complianc" without the final e.

## Collaboration notes

46. A note whose field is 93 or 94 triggers `HandleEmailLog`. Other note fields do not.
47. Internal log recipients: examiner posts to the internal moderator; internal moderator posts to the examiner; reviewer or assessment specialist posts to the examiner and the internal moderator. An external moderator posting on the internal log has no recipient case.
48. External log recipients: examiner posts to the external and internal moderators; internal moderator posts to the examiner and the external moderator; external moderator posts to the examiner and the internal moderator; reviewer or assessment specialist posts to examiner, internal moderator, and external moderator.
49. `SaveCollaborationLog` does not use rules 47–48. It emails the reviewer only.

## Access to files

50. `FileProtectionHandler` redirects a GET under `upload` to `{WebAddy}/admin` when the session has no user, except when the URL is exactly the site root or exactly `{WebAddy}/admin`.

## What is not a rule of this site

51. `SubjectManagement_form` changes button text only when setup code is `STUD-1`. **Inferred** leftover, not an INDE-1 exam rule.
52. Framework user-type checks inside generic surface code (linking a WebsiteUser to a surface item when `linkUser` is set) still run. They are framework behaviour. Whether the exam surfaces set `linkUser` is database data. **Unknown / Requires Confirmation.**

## Conflicts inside the implementation

These are recorded so they are not smoothed over:

- Save buttons hidden below SuperUser, while the reviewer may still see an enabled status dropdown.
- Status email skipped for four statuses, while the log path can still email the reviewer.
- Two document-pack numbering implementations, one of which ignores `customCode`.
- `SaveCollaborationLog` always notifying the reviewer, including after a note that `HandleEmailLog` would have sent to other people.
- Pipeline `sonar.projectKey` `my-project` versus `sonar-project.properties` key `overdrive_ieb_8345eef9-efa7-4cfa-bc4a-5a046ec95189`.
