# Business rules — theshoulderpractice (The Shoulder Practice, SHOU-1)

Evidence date: 2026-10-01. Source: `master` at `b805d89` (2025-06-20).

Rules are taken from code and SQL. No business-rules document exists in the source repository. Each rule is marked **Implemented** or **Inferred**. Rules that came from a one-time script are marked as data fixes, not as ongoing validation.

Where the code disagrees with an obvious clinical expectation, the code is recorded and the conflict is left for confirmation.

## Installation and tenancy

### BR-001 — One setup code gates practice-specific behaviour

**Implemented.** Practice-specific branches compare `handler.ReturnSetup().code` to `"SHOU-1"`.

Evidence: `surface_form.ascx.cs`, `surface_grid.ascx.cs`, `SurgicalNotes_form.ascx.cs`.

### BR-002 — EVOL-1 users are valid on every installation

**Implemented.** After a successful email and password match, the user is accepted if `customerCode` equals the current setup code, or if `customerCode` is `EVOL-1`.

Evidence: `Admin_SignIn.aspx.cs`.

### BR-003 — Inactive users cannot use the database sign-in path

**Implemented.** The lookup criteria include `isActive` = 1.

Evidence: `Admin_SignIn.aspx.cs` (`email,password,isActive`).

The compiled-in vendor session does not read this table. It always creates an active in-memory admin. See BR-004.

## Authentication and access

### BR-004 — Compiled-in vendor account

**Implemented.** `Admin_SignIn.aspx.cs` accepts one specific email and password written in source. On match it stores an `oUser` with name `Palette Admin User`, email `admin@evolutionsoftware.co.za`, `customerCode` `EVOL-1`, `userType` AdminUser, `isSetup` true, `isActive` true, and `recId` -1.

The password literal is not repeated here.

This rule is what the code does. It is not evidence of an approved practice policy. **Requires Confirmation** and removal or rotation.

### BR-005 — Passwords stored for lookup are encrypted before comparison

**Implemented.** The database path encrypts the submitted password with a hardcoded passphrase in `Admin_SignIn.aspx.cs`, then matches the stored `password` column.

The passphrase is not repeated here.

### BR-006 — Reset links use a different passphrase

**Implemented.** `Reset.aspx.cs` decrypts query string `q` with a hardcoded passphrase (not the sign-in passphrase) and looks for an active user whose `resetCode` and `customerCode` match.

A comment dated 15 January 2016 says `q` must not be URL-decoded a second time, because `Request.QueryString` already decodes it and a second decode breaks `+`.

### BR-007 — Unknown Google accounts are not created

**Implemented.** `GoogleLogin` looks up `email` plus customer code `setup.code` or `EVOL-1`. If `recId` stays 0, the method does not insert a user. The insert block is commented out.

### BR-008 — Redirect depends on the website flag

**Implemented.** When `oSetup.isWebsite` is false, sign-in redirects to `/home`. When it is true, sign-in redirects to `Admin.aspx`.

**Unknown / Requires Confirmation:** the value of `isWebsite` in the SHOU-1 database.

### BR-009 — Field and group visibility follows user type, with a CustomUser exception

**Implemented.** A group is visible when:

- the user is not a CustomUser and `userType >= accessLevel`, or
- the user is a CustomUser and `mimicUserType >= accessLevel`.

User type numbers: WebsiteUser 1, PowerUser 2, SuperUser 3, AdminUser 4, CustomUser 5. A higher number is a higher type, except CustomUser, which is compared through the mimic type.

Evidence: `surface_form.ascx.cs` group loop.

### BR-010 — SHOU-1 does not apply that access check to two patient groups

**Implemented.** If the setup code is `SHOU-1` and the field name is `PatientInformation_EmployerDetailsInjuryonDuty` or `PatientInformation_MedicalAidIfapplicable`, the access-level visibility assignment is skipped.

Comment: "Ignore security on group if TSP and group is IOD groups hardcoded to show/hide depending on user selection."

Evidence: `surface_form.ascx.cs`, comment 2017-01-13.

**Inferred:** "TSP" in these comments means The Shoulder Practice. The code identifier is `SHOU-1`.

### BR-011 — Hidden wizard tabs are for PowerUser and above on SHOU-1 and GLOB-1

**Implemented.** For those two setup codes, a tab with `isHiddenFromWizzard` is shown only when the user (or mimic type) is at least PowerUser and also meets `tab.accessLevel`. Otherwise the tab element gets `display: none`.

Comment: "only show wizard tabs to website user. only power and up can see additional tabs."

Evidence: `surface_form.ascx.cs`, comment 2016-10-26.

### BR-012 — Session timeout is 60 minutes

**Implemented.** `sessionState timeout="60"` in `core_custom/SHOU-1/Web.config`.

## Patient identity and visits

### BR-013 — Patient match is mobile number plus date of birth

**Implemented.** Both values must match a row on an active surface named `PatientProfile`.

- Mobile number: exact equality with `PatientInformation_PatientDetails_MobileNumber`.
- Date of birth: both sides pass through `utils.stripSpecialCharacters`, then exact equality with `PatientInformation_PatientDetails_DateofBirth`.

The surface row must also contain `itemID`. The first match wins. Any other result, including a missing column, leaves the item id at 0 and the message is `Patient not found`.

Evidence: `medicalPatientSignin.ascx.cs`.

**Inferred:** stripping special characters is there so `1990-01-01` and `19900101` can match. The method body of `stripSpecialCharacters` was not opened, so the exact character set is **Unknown**.

### BR-014 — A matched patient sign-in opens subjectives instead of an error

**Implemented.** `SurfaceItemId > 0` shows `pnlSubjectives`, hides `pnlSignIn`, and clears the result label. The grid is bound.

### BR-015 — Saving subjectives requires the grid validator

**Implemented.** `btnSave_Click` runs only when `validateGrid()` returns true. The individual subjective rules inside `validateGrid` were not extracted. **Unknown** which answers are mandatory.

### BR-016 — A saved sign-in always creates a visit log

**Implemented.** On successful save, insert `oMedicalPatientVisitLog` with:

- `surfaceItemId` = the matched patient item
- `signInDate` = `DateTime.Now`
- `note` = the note field

### BR-017 — A saved sign-in creates an exam note owned by the patient's linked user

**Implemented.** For each active surface named `ExamNotes`, insert an active `oSurfaceItem` dated now. `createdBy` is the patient item's `userLink` when `userLink > 0`, otherwise the patient item's `createdBy`.

Field `ExamNotes_ExamNotes_ExamNotes` receives the note text. Field `ParentSurfaceItemId` receives the patient item id. Other exam-note fields are skipped by the switch.

### BR-018 — Pending status signs out old visits, then opens a new one

**Implemented.** If the patient-profile status button text is `Status: Pending`:

1. Every existing `oMedicalPatientVisitLog` for that surface item is updated with `signOutDate` = now. Comment: "first clear any existing not signed out for this patient." The code does not check whether `signOutDate` was already set. It overwrites sign-out on all of them.
2. A new log is inserted with `signInDate` = now and the same surface item id. No note is set on this path.
3. The button becomes `Status: In Progress` and uses the success CSS class.

If the button text is anything else, every log for that item is signed out and the button returns to `Status: Pending`.

Evidence: `surface_form.ascx.cs`, region "GWC Status button", comment GR 2017-05-15.

**Inferred conflict:** the comment says the first step clears logs that are not signed out. The code updates every log for the item. **Requires Confirmation** if overwriting an old sign-out time is intended.

### BR-019 — Website user save on SHOU-1 can send one registration email

**Implemented.** During surface save, if `userType` is WebsiteUser, setup code is `SHOU-1`, `mailSent` is false, and `email` is not empty, call `SendRegistrationEmail(firstName, surname, email, patientNumber, contactNumber, registrationDate)`.

Evidence: `surface_form.ascx.cs`.

The template, the sender address used at runtime, and whether `mailSent` is set afterwards were not fully traced.

## Billing and clinical capture

### BR-020 — No Charge hides the referring doctor

**Implemented.** On `SHOU-1`, when the field name ends with `_CreateBillingorNoCharge`:

- Hide the referring-doctor block when nothing is selected, or when the selected text, after removing `&nbsp;` and `&nbsp`, equals `No Charge`.
- Show it otherwise.

The block id is `ManagementPlan_OfficeConsultingRoomProcedures_ReferringDoctor` when the surface name is `Examinations`. Otherwise it is `FollowUp_OfficeConsultingRoomProceduresFollowUp_ReferringDoctor`.

Evidence: `SurgicalNotes_form.ascx.cs`, comment CVH 2017-08-29.

### BR-021 — Create Billing inspects the diagnosis ICD-10

**Implemented.** When the same radio text is exactly `Create Billing` (this comparison does not strip `&nbsp;`), the form reads ICD-10 codes from the diagnosis tab and continues into an external-cause check.

Evidence: same method. The external-cause outcomes were not fully specified in this document.

**Inferred conflict:** `No Charge` is compared after whitespace-entity stripping, and `Create Billing` is compared to the raw text. A value that differs only by a non-breaking space would fail the Create Billing branch. **Requires Confirmation** if that case is seen in the UI.

### BR-022 — Patient-management grid displays scheme name, not only the code

**Implemented.** On `SHOU-1`, if the surface name contains `PatientManagement`, replace the label text with `oMedicalScheme.name` for the matching `code`. If no row matches, keep the code. Wrapping class becomes `gridlabelwrap`.

Evidence: `surface_grid.ascx.cs`.

If the label control is missing, this method will throw on `.Text`. That null case is not guarded.

### BR-023 — Shoulder arthroscopy notes follow the drawing module

**Implemented.** On `SHOU-1`, `SurgicalImages_ShoulderArthroscopyImage_SurfaceDrawingNotes` is hidden, then shown when the controlled value equals the surface-drawing module and, in the examinations branch, the surface name is `Examinations`.

Evidence: `SurgicalNotes_form.ascx.cs`.

### BR-024 — Day clinic and consulting room have fixed rate types

**Implemented as a SQL script, not as a C# rule.** `Day Clinic` → `rateType` `IH`. `Office / Consulting Room` → `rateType` `OH`.

Evidence: `data/Scripts/SHOU-1/SHOU-1_update_place_of_service_28042020.sql`.

The script does not check the current value and does not name a database. Whether the application rejects other rate types is **Unknown**.

### BR-025 — Open-surgery general notes template is forced empty

**Implemented as SQL against `Framework_SHOU-1`.** Ensure template name `OS - General Notes` with `templateTypeId` 5. If the row already exists, set `templateContent` to an empty string. Ensure lookup category `1074` has display `General Notes`, sequence 9, active, `wantsReason` 0, when that display is absent.

Evidence: `SHOU-1_insert_new_open_surgery_type_11022020.sql`.

**Inferred:** emptying existing content is destructive if someone had typed a template. The script does that on every run. **Requires Confirmation** before anyone executes it again.

### BR-026 — Two referring-doctor values were blanked

**Implemented as a one-time SQL update.** Set `pal_MSClaimDoctor.doctorPCNSNumber` and `pal_Account.referringDocNo` to empty when the value is `4/12` or `christelle@theshoulderpractice.co.za`.

Evidence: `SHOU-1_update_referring_doc_num_01042020.sql`.

This is a data fix. It does not add a validation rule that rejects those values on later capture.

### BR-027 — MediSwitch MSV list is limited to allowed administrators

**Implemented.** The administrator query uses `msMSVAllowed` = 1, ordered by `medSchemeName`. The call is `MSVPatientDataSingle(destinationCode, memberNo)`.

Evidence: `mediswitchMSV.ascx.cs`.

### BR-028 — MediSwitch operations are member check, claim, and reversal

**Implemented as an enum.** `MediswitchOperationType`: MSV = 1, Claim = 2, Reversal = 3.

Evidence: `pNums.cs`. The claim and reversal procedures were not specified field by field in this pass.

### BR-029 — Checked-in MediSwitch host is QA

**Implemented in configuration.** Active keys point at `https://wsgateway.mediswitch.co.za/qa/wsgateway/MediswitchGatewayV2` and the `wsgateway1` / `wsgateway2` QA twins. The non-QA URLs are comments.

`MediSwitchResponses` is `0`.

**Requires Confirmation:** production may use the live URLs even though this file does not.

## Reference data

### BR-030 — Patient type is adult or paediatric

**Implemented as an enum.** Adult = 1, Paediatric = 2 (`pNums.PatientType`).

Symptom seed scripts in the framework archive store `patientType` on `pal_MedicalSymptom`, including a row `Shoulder Pain` with `patientType` 1. Those scripts sit under `data/Scripts/archive major 1.5.9/`, not under `data/Scripts/SHOU-1/`. Whether that seed is the SHOU-1 catalog is **Unknown**.

### BR-031 — Clinical lists are first-class records

**Implemented as object model.** Practices, doctors, schemes, administrators, ICD-10, procedures, tariffs and tariff plans, symptoms, conditions, diagnoses and diagnosis categories, traumatic events, and practice extras each have an `oMedical*` type and an admin page.

Mandatory fields on each editor were not extracted.

## Documents, tasks, and communications

### BR-032 — Sales and purchase documents use the same status list

**Implemented as enums** `SalesSatus` and `PurchasesSatus` (spelling in source): Draft, AwaitingAcceptance, Accepted, Declined, Unpaid, PartiallyPaid, Paid, Overdue, Unallocated, Allocated, CapturedInXero.

No transition rules (for example "Paid only after a receipt") were confirmed in this pass. **Unknown** which transitions the UI enforces.

`CapturedInXero` does not, by itself, mean an Xero integration exists.

### BR-033 — Document types

**Implemented.** Statement, Invoice, Quote, Receipt, CreditNote (`pNums.DocumentType`).

### BR-034 — Note types include clinical and billing categories

**Implemented.** General, Email, Task, SurfaceItemCreated, SurfaceItemChanged, Statement, Payment, Claim, Ideas, Billing, Confidential (`pNums.NoteType`).

A separate `NoteCategory` enum lists Landlords, Tenants, and LeaseManagement. That set is a framework leftover. No SHOU-1 branch was found that uses it.

### BR-035 — Tasks use a fixed status and priority scale

**Implemented.**

Statuses: ToDo, InProgress, ToBeTested, ReleaseReady, Reopened, Closed, OnHold, InReview.

Priorities: Blocker, Critical, Major, Minor, Trivial, Wishlist, Idea.

Categories: Scoping, ServiceDesk, Idea, Jobcard.

Evidence: `pNums.cs`. These look like the vendor's own delivery board living inside the framework. Whether the practice uses them is **Unknown**.

### BR-036 — SMS credit movements are allocated, used, or loaded

**Implemented.** `SMSCreditTransactionType`: Allocated = 1, Used = 2, Loaded = 3.

### BR-037 — Loyalty email job is switched off in config

**Implemented.** `loyaltyEmail` = `0`. Hour `20` and address `jas@evolutionsoftware.co.za` are still present. With the flag at 0, the job should not send. The scheduler method was not opened to prove the flag is honoured.

### BR-038 — Registration and system mail use Evolution Software addresses

**Implemented in config.** From address `no-reply@evolutionsoftware.co.za`. SMTP from `mailer@evolutionsoftware.co.za`. Support `graham@evolutionsoftware.co.za`. Admin, cc, and bcc `jas@evolutionsoftware.co.za`.

**Requires Confirmation:** whether production overrides this file. The practice domain appears in SQL data (`theshoulderpractice.co.za`) but not in these settings.

## Data fixes versus standing rules

| Item | Standing rule in C# | One-time or repeatable SQL |
|---|---|---|
| Place-of-service rate types | Not found in C# | Repeatable update, 2020-04-28 script |
| Referring PCNS / email wipe | Not found as validation | One update script, 2020-04-01 |
| Empty "OS - General Notes" template | Not traced in C# | Repeatable insert-or-blank, 2020-02-11 script, destructive on re-run |

## Rules that were not established

- Fee amounts, tariff rands, or medical-aid plan benefits.
- Which ICD-10 codes are mandatory for a shoulder procedure.
- Consent, POPIA retention, or who may open another clinician's notes, beyond the access-level and wizard-tab rules above.
- Whether a patient may sign in without a staff member present.
- Double-booking rules for the calendar or procedure booking.
- What happens when MediSwitch times out or rejects a claim.
- Password complexity, lockout, or expiry. None was found on the sign-in path that was read.
- A unique constraint on patient mobile number. The sign-in scan walks rows in memory and keeps the first match, so duplicates would bind the first row only. Whether the database forbids duplicates is **Unknown**.
