# Product requirements — theshoulderpractice (The Shoulder Practice, SHOU-1)

Evidence date: 2026-10-01.

Source: `master` at `b805d89` (2025-06-20).

Provenance labels: **Documented** (written in a repo document), **Implemented** (present in code or SQL), **Inferred** (reasonable reading of that code), **Planned** (described as future work), **Unknown**.

No requirements document exists in the source repository. Every requirement below was recovered from the implementation. Nothing in this file is a new product decision.

## Product purpose

Staff at The Shoulder Practice use a web application to keep patient clinical records, book procedures, check medical-aid membership, and administer the practice. Patients can identify themselves and capture visit subjectives. The application is one installation (code `SHOU-1`) of the Evolution Software Palette framework.

Provenance: **Inferred** from `core_custom/SHOU-1`, the medical objects, and SHOU-1 SQL. The practice name is **Implemented** in `Web.config` and in `data/Scripts/SHOU-1/`.

## Users and roles

User types are **Implemented** in `framework_business.pNums.UserType`:

| Value | Name | Behaviour recovered from code |
|---|---|---|
| 1 | WebsiteUser | Lowest staff/patient-facing type. On SHOU-1, wizard tabs marked hidden stay hidden for this type. Saving a surface can send a registration email when this type supplies an email address. |
| 2 | PowerUser | Can see wizard tabs that are hidden from WebsiteUser, if the tab access level also allows it. |
| 3 | SuperUser | Higher than PowerUser. Same comparison (`userType >= accessLevel`) applies. |
| 4 | AdminUser | Highest numbered type in the enum. Also assigned to the compiled-in vendor session. |
| 5 | CustomUser | Access checks use `mimicUserType` instead of `userType`. |

A user row is stored as `oUser` and must be active (`isActive`) to sign in through the database path. The session key is `Session["user"]`.

**Unknown / Requires Confirmation:** which of these types the practice actually assigns to reception, clinicians, and billing staff. The names are framework names, not job titles.

## Major workflows

### Staff sign-in

1. User opens `Admin_SignIn.aspx` and submits email and password.
2. The page either accepts a compiled-in vendor account, or looks up `pal_` user rows where email, encrypted password, and `isActive` match.
3. The user is accepted when `customerCode` equals the installation setup code, or when `customerCode` is `EVOL-1`.
4. If setup `isWebsite` is false, the browser goes to `/home`. If it is true, the browser goes to `Admin.aspx`.
5. Failure shows `pnlResult` and does not describe which field was wrong.

A Google sign-in web method updates a `oGoogleUser` row and starts a session only when an existing user with that email already belongs to the setup code or to `EVOL-1`. Unknown Google accounts are not registered. The registration block is commented out.

Password reset uses `Reset.aspx` and query string `q`, decrypted and matched to `resetCode` on an active user for the current setup code.

### Patient self sign-in

`controls/module/medicalPatientSignin.ascx.cs`:

1. User enters a mobile number and a date of birth.
2. The control loads active surfaces named `PatientProfile`.
3. It matches `PatientInformation_PatientDetails_MobileNumber` exactly and `PatientInformation_PatientDetails_DateofBirth` after special characters are stripped.
4. A match shows the subjectives panel. No match shows `Patient not found`.
5. Save writes `oMedicalPatientVisitLog` (surface item id, sign-in time, note) and creates an active `ExamNotes` surface item linked by `ParentSurfaceItemId`, with the note copied into `ExamNotes_ExamNotes_ExamNotes`.

### Clinical surfaces

`Canvas.aspx` hosts the active canvas. It loads controls from `controls/`, `controls/module/`, `controls/surface/`, `controls/surface_custom/`, and `controls/composite/`.

Surface definitions (fields, grids, lookups) are stored in the database (`oSurface`, `oSurfaceField`, `oSurfaceItem`, `oSurfaceFieldData`). The generic renderer is `surface_form.ascx`, `surface_grid.ascx`, and `surface_view.ascx`.

Names referenced by SHOU-1 code include `PatientProfile`, `PatientManagement`, `Examinations`, `ExamNotes`, and field prefixes for follow-up and surgical images. Custom Surgical Notes controls remain under `controls/surface_custom/`.

On 2024-01-15, generated controls for named clinical surfaces (including Examinations, PatientManagement, SurgicalNotes, RotatorCuff, Glenohumeral, ACJ, and others) were renamed to `*.ascx.exclude`. Those files are no longer compiled as user controls. Whether the same surfaces still render through the generic renderer depends on database rows that are not in Git.

### Visit status

On a `PatientProfile_PatientStatus` button, text `Status: Pending` closes any open visit logs for that surface item, inserts a new `oMedicalPatientVisitLog` with `signInDate` now, and changes the button to `Status: In Progress`. Any other text signs out every visit log for that item (`signOutDate` now) and returns the button to `Status: Pending`.

### Procedure booking

`procedureBooking.ascx` loads places of service and macro procedures, then loads and saves `oMedicalPatientProcedureBooking` rows for the current surface item.

### Medical-aid member check

`mediswitchMSV.ascx` lists medical administrators where `msMSVAllowed` is 1, then calls `mediswitch.MSVPatientDataSingle` with a destination code and member number.

### Reference data

Each `Medical_*.aspx` page hosts one editor control under `controls/medical/`: practices, doctors, schemes, medical administrators, ICD-10, tariffs, procedures, symptoms, conditions, diagnoses, traumatic events, practice extras.

## Functional requirements

### FR-001 — Practice identity

Requirement: The running site identifies itself as installation `SHOU-1` for The Shoulder Practice, and clinical rules that are specific to this practice are gated on `handler.ReturnSetup().code == "SHOU-1"`.

Who: the application, on every request that loads setup.

Trigger: page or control initialisation.

Inputs: setup row returned by `handler.ReturnSetup()`.

Expected behaviour: SHOU-1 branches run only when the setup code is `SHOU-1`. Other codes in the same source (for example `GLOB-1`, `STUD-1`) do not take those branches.

Outputs: conditional UI and emails.

Validation: setup code string match.

Error: exceptions in these controls redirect to `/error`.

Evidence: `Web.config` subject and website keys; `surface_form.ascx.cs`; `surface_grid.ascx.cs`; `SurgicalNotes_form.ascx.cs`.

Implementation: **Implemented**.

Confidence: High for the code gate. The contents of the live setup row are **Unknown**.

### FR-002 — Staff authentication

Requirement: A person signs in with email and password. The account must be active and must belong to this installation, unless the account's customer code is `EVOL-1`, in which case it is accepted on every installation.

Who: staff user, or a vendor operator using the compiled-in account.

Trigger: `btnLogin_Click` on `Admin_SignIn.aspx`.

Inputs: email, password. Database path encrypts the password before the lookup.

Expected behaviour: matching user is stored in `Session["user"]` and the browser is redirected. Non-match shows the result panel.

Outputs: session user, redirect to `/home` or `Admin.aspx`.

Validation: active flag, email, encrypted password, customer code.

Error: exception redirects to `/error`.

Evidence: `core_custom/SHOU-1/Admin_SignIn.aspx.cs`.

Implementation: **Implemented**.

Confidence: High.

Constraint: a vendor email and password are compiled into the page as a literal comparison and create an in-memory `AdminUser` with `customerCode` `EVOL-1` and `recId` -1. The password value is not copied into this PRD. This is a security defect in the source, not an approved documented policy.

### FR-003 — Google sign-in for known users only

Requirement: Google profile data may start a session only for an email that already exists on this setup code or on `EVOL-1`. The system must not create a new user from Google profile data.

Who: a person using the Google login web method.

Trigger: `GoogleLogin`.

Inputs: name, email, profile id, given name, family name, image URL.

Expected behaviour: existing user is placed in session and the Google user row is inserted or updated. Unknown email leaves `user.recId == 0` and does not register.

Outputs: session, or no session.

Evidence: `Admin_SignIn.aspx.cs` `GoogleLogin`. The create-user block is commented out.

Implementation: **Implemented**.

Confidence: High for the code path. Whether the Google button is shown on the sign-in page in production is **Unknown**.

### FR-004 — Password reset by coded link

Requirement: An active user of the current setup can set a new password when `Reset.aspx` is opened with a `q` value that decrypts to that user's `resetCode`.

Who: the user who received the link.

Trigger: GET `Reset.aspx?q=`.

Inputs: encrypted reset code.

Expected behaviour: the code is matched to `isActive`, `resetCode`, and `customerCode`.

Evidence: `Reset.aspx.cs`.

Implementation: **Implemented**.

Confidence: Medium. The full success path after the match was not traced line by line. How reset emails are sent is **Unknown** beyond the SMTP settings.

### FR-005 — Patient match on mobile number and date of birth

Requirement: A patient is found only when an active `PatientProfile` surface item has the same mobile number and the same date of birth after non-alphanumeric characters are removed.

Who: the person using the patient sign-in module. Role name is **Unknown** (kiosk, reception, or patient).

Trigger: login button on `medicalPatientSignin`.

Inputs: mobile number, date of birth.

Expected behaviour: first matching item id is kept. Subjectives panel replaces the sign-in panel. Otherwise the label is `Patient not found`.

Outputs: `SurfaceItemId`, visibility of `pnlSubjectives`.

Validation: exact string match on the two surface columns. No format check is visible before the query.

Error: exception redirects to `/error`.

Evidence: `controls/module/medicalPatientSignin.ascx.cs`.

Implementation: **Implemented** in code. The `PatientProfile` surface must exist in the database. That data is not in Git. **Requires Confirmation**.

Confidence: High for the code. Medium for production use.

### FR-006 — Patient sign-in creates a visit log and an exam note

Requirement: Saving subjectives after a successful patient match records a visit log and creates an exam-note surface item tied to the patient item.

Who: the same person as FR-005, after `validateGrid()` passes.

Trigger: save button.

Inputs: note text, subjective grid, current surface item id.

Expected behaviour:

- Insert `oMedicalPatientVisitLog` with `signInDate` = now and the note.
- Find active surface named `ExamNotes`.
- Create an active surface item. `createdBy` is the patient item's `userLink` when that value is greater than 0, otherwise the patient item's `createdBy`.
- Write the note into field `ExamNotes_ExamNotes_ExamNotes` and the patient item id into `ParentSurfaceItemId`.

Outputs: visit log id, exam note item id, field data rows.

Validation: `validateGrid()` must pass. The rules inside that method were not fully extracted.

Error: exception redirects to `/error`.

Evidence: `medicalPatientSignin.ascx.cs` `btnSave_Click`.

Implementation: **Implemented** in code, dependent on database surfaces.

Confidence: Medium.

### FR-007 — Wizard tab visibility

Requirement: On installations `SHOU-1` and `GLOB-1`, a wizard tab with `isHiddenFromWizzard` is visible only to PowerUser and above (or a CustomUser whose mimic type is PowerUser and above), and only when that type also meets the tab's `accessLevel`. Other users get `display: none` on the tab.

Who: the signed-in user viewing a surface form.

Trigger: tab bind in `surface_form.ascx.cs`.

Inputs: setup code, user type, mimic type, tab flags.

Expected behaviour: as above. Comment in source: "only show wizard tabs to website user. only power and up can see additional tabs".

Evidence: `surface_form.ascx.cs` around the 2016-10-26 "TSP Hardcode tab security" block.

Implementation: **Implemented**.

Confidence: High.

### FR-008 — Injury-on-duty and medical-aid groups skip the normal access check

Requirement: On `SHOU-1`, the groups `PatientInformation_EmployerDetailsInjuryonDuty` and `PatientInformation_MedicalAidIfapplicable` do not have their visibility set from `accessLevel`. The code comment says security on those groups is ignored so they can be shown or hidden from the user's own selection.

Who: any signed-in user on a SHOU-1 surface form.

Trigger: group bind.

Inputs: setup code and surface field name.

Expected behaviour: the access-level assignment is skipped for those two names.

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

Implementation: **Implemented**.

Confidence: High for the skip. The separate show/hide that follows the user's selection was not isolated as a single method. **Requires Confirmation** if a tester needs the exact toggle rule.

### FR-009 — Patient management grid shows medical-aid scheme names

Requirement: On `SHOU-1`, when the surface name contains `PatientManagement`, the grid replaces the scheme code in `lblPatientInformation_MedicalAidIfapplicable_MedicalAidSchemeName` with `oMedicalScheme.name` where `code` matches. If no scheme matches, the original code remains. The label uses CSS class `gridlabelwrap` instead of `nowrap`.

Who: a user opening that grid.

Trigger: grid data bind.

Inputs: scheme code on the row, `oMedicalScheme` list ordered by name.

Expected behaviour: as above.

Evidence: `surface_grid.ascx.cs` `SHOU1_SetMedicalAidSchemeColumn`.

Implementation: **Implemented**.

Confidence: High.

### FR-010 — Registration email for website users

Requirement: When a `WebsiteUser` saves a SHOU-1 surface and an email address is present and a registration mail has not already been sent in that operation, the system sends a registration email with first name, surname, email, patient number, contact number, and registration date.

Who: WebsiteUser.

Trigger: surface save.

Inputs: those identity fields.

Expected behaviour: `SendRegistrationEmail(...)`.

Evidence: `surface_form.ascx.cs`.

Implementation: **Implemented** as a call. The email template body was not extracted.

Confidence: Medium.

### FR-011 — Create-billing shows the referring doctor and checks ICD-10

Requirement: On `SHOU-1`, a field whose name ends with `_CreateBillingorNoCharge` controls a referring-doctor block.

- If nothing is selected, or the selected text is `No Charge`, the referring-doctor block is hidden.
- Otherwise it is shown.
- If the selected text is `Create Billing`, the form looks up the ICD-10 chosen on the diagnosis tab and treats external-cause coding as part of that billing path.

The referring-doctor control id depends on the surface: `ManagementPlan_OfficeConsultingRoomProcedures_ReferringDoctor` on `Examinations`, and `FollowUp_OfficeConsultingRoomProceduresFollowUp_ReferringDoctor` otherwise.

Who: the user editing Examinations or the follow-up surface.

Trigger: radio-button change handling in the custom surgical-notes form.

Inputs: selected radio text, diagnosis ICD-10.

Expected behaviour: visibility toggle, then ICD-10 inspection when creating billing.

Evidence: `controls/surface_custom/SurgicalNotes_form.ascx.cs`, comment dated 2017-08-29.

Implementation: **Implemented** in the custom control.

Confidence: Medium. The external-cause branch continues for many lines and was not fully specified here.

### FR-012 — Shoulder arthroscopy drawing on examinations

Requirement: On `SHOU-1` examinations, the shoulder arthroscopy surface-drawing notes control is hidden until the controlled module value is the surface-drawing module, and then it is shown.

Who: a user on the Examinations surface.

Trigger: control visibility pass in the custom surgical-notes form.

Evidence: `SurgicalNotes_form.ascx.cs` (`SurgicalImages_ShoulderArthroscopyImage_SurfaceDrawingNotes`).

Implementation: **Implemented**.

Confidence: Medium.

### FR-013 — Medical reference data maintenance

Requirement: An authorised user can maintain practices, doctors, tariffs, procedures, medical schemes, ICD-10 codes, medical administrators, symptoms, conditions, diagnoses, traumatic events, and practice extras.

Who: a user who can open the matching `Medical_*.aspx` page. The menu permission row is database-driven and was not in Git.

Trigger: open the admin page.

Inputs: the fields on each `controls/medical/*.ascx` editor.

Expected behaviour: the page hosts that single editor.

Outputs: rows in the corresponding `oMedical*` types.

Evidence: `Medical_Practices.aspx` and the sibling pages; `pNums.Module`; `business/objects/oMedical*.cs`.

Implementation: **Implemented** as pages and objects. Field-level validation inside each editor was not extracted one control at a time.

Confidence: Medium.

### FR-014 — Procedure booking

Requirement: A surface that hosts the procedure-booking module can load places of service and macro procedures and can save booking rows for the current surface item.

Who: the user editing that surface item.

Trigger: module reload and save.

Inputs: place of service, procedures, existing `oMedicalPatientProcedureBooking` rows.

Evidence: `controls/module/procedureBooking.ascx.cs`.

Implementation: **Implemented**.

Confidence: Medium.

### FR-015 — Place-of-service rate types

Requirement: Place of service `Day Clinic` uses rate type `IH`. Place of service `Office / Consulting Room` uses rate type `OH`.

Who: a database update, not a screen rule.

Trigger: script `SHOU-1_update_place_of_service_28042020.sql`.

Evidence: that script. It has no `USE` database clause.

Implementation: **Implemented** as a script in the repo. Whether it was applied to the live database is **Unknown**.

Confidence: High for the script text. Low for production data.

### FR-016 — MediSwitch member validation

Requirement: The MSV module offers only medical administrators flagged `msMSVAllowed = 1`, and requests patient data from MediSwitch for a destination code and member number.

Who: a user of `mediswitchMSV`.

Trigger: the MSV action on that control.

Inputs: administrator destination, member number.

Expected behaviour: `MSVPatientDataSingle`.

Outputs: response bound back onto the update panel.

Evidence: `mediswitchMSV.ascx.cs`. Operation types MSV, Claim, and Reversal exist on `pNums.MediswitchOperationType`.

Implementation: **Implemented** for the MSV screen. Claim and reversal screens were not opened in this pass beyond the enum and account-claim view objects (`ovAccountsClaimable`, `ovAccountClaimHistory`, and related).

Confidence: Medium.

The checked-in gateway URLs are the MediSwitch **QA** hosts. Live hosts are present only as comments. Which host production uses is **Unknown / Requires Confirmation**.

### FR-017 — Open-surgery general-notes template

Requirement: Template `OS - General Notes` exists as template type 5. If it is missing, insert it with empty content. If it exists, set its content to empty. Lookup category `1074` has an active value `General Notes` at sequence 9 when that display is missing.

Who: a DBA or release process running the script.

Trigger: `SHOU-1_insert_new_open_surgery_type_11022020.sql` against database `Framework_SHOU-1`.

Evidence: that script.

Implementation: **Implemented** as SQL. Application behaviour that reads this template was not traced.

Confidence: High for the script. Medium for clinical use.

### FR-018 — Referring-doctor number cleanup

Requirement: Clear `doctorPCNSNumber` on `pal_MSClaimDoctor` and `referringDocNo` on `pal_Account` where the stored value is `4/12` or `christelle@theshoulderpractice.co.za`.

Who: a one-time data fix.

Trigger: `SHOU-1_update_referring_doc_num_01042020.sql`.

Evidence: that script. It does not name a database.

Implementation: **Implemented** as a script. It is a data repair, not an ongoing validation rule. Whether the application still allows an email address in the PCNS field is **Unknown**.

Confidence: High for the script.

### FR-019 — Session lifetime

Requirement: The authenticated session remains for 60 minutes of inactivity, using ASP.NET session state.

Who: any signed-in browser session.

Trigger: `sessionState timeout="60"` in `Web.config`.

Evidence: `core_custom/SHOU-1/Web.config`.

Implementation: **Implemented**.

Confidence: High. A `SessionHeartbeat.ashx` file exists. Its effect on the timeout was not read.

### FR-020 — Patient age band

Requirement: Patient type is Adult (`1`) or Paediatric (`2`).

Who: data entry against `pNums.PatientType` and `oMedicalPatientType`.

Evidence: `pNums.cs`. Seed SQL in older framework scripts inserts symptoms with `patientType`.

Implementation: **Implemented** as an enum. Which screens require it is **Unknown**.

Confidence: Medium.

### FR-021 — Visit and clinical child records

Requirement: The system can store, per patient surface item, visit logs, subjective logs, symptoms, complaints, history, listings, overview, diagnosis, practice extras, and procedure bookings.

Who: modules and surfaces that call `xData` for those types.

Evidence: `oMedicalPatientVisitLog`, `oMedicalPatientSubjectivesLog`, `oMedicalPatientSymptom`, `oMedicalPatientComplaints`, `oMedicalPatientHistory`, `oMedicalPatientListing`, `oMedicalPatientOverview`, `oMedicalPatientDiagnosis`, `oMedicalPatientPracticeExtra`, `oMedicalPatientProcedureBooking`.

Implementation: **Implemented** as types. Not every type was traced to a screen in this pass.

Confidence: Medium.

### FR-022 — Framework administration that ships with the site

Requirement: The SHOU-1 site includes the Palette admin pages for banners, calendar, campaigns, canvas, content, customers, FAQ, gallery, job cards, loyalty groups and stores, media, product brands, categories, and products, projects, promotions, releases, reports, setup, SMS credit, support, surfaces and surface code, tasks, templates, and users.

Who: a user who can open `Admin_*.aspx`.

Trigger: navigation to that page.

Expected behaviour: each page hosts the matching control under `controls/admin/`.

Evidence: the `Admin_*.aspx` files in `core_custom/SHOU-1`.

Implementation: **Implemented** as pages. Which items the practice menu shows is stored in the database and is **Unknown**.

Confidence: Medium. Several of these (loyalty, products, cart) may be unused by the practice. See features.

### FR-023 — Public home canvas

Requirement: `Default.aspx` renders banners, home content, an optional promotion placeholder, and a task menu that can start a "New Snapshot". The featured-products heading is commented out in the markup.

Who: a visitor who can open the public master page. Whether the site is public is controlled by setup `isWebsite`, which is data.

Evidence: `Default.aspx`.

Implementation: **Implemented** as a page. Content is data-driven.

Confidence: Medium.

### FR-024 — Unsubscribe page

Requirement: `Unsubscribe.aspx` loads and does nothing in `Page_Load`.

Evidence: `Unsubscribe.aspx.cs`.

Implementation: **Implemented** as an empty page. No unsubscribe rule was found.

Confidence: High that no behaviour is implemented there.

## Integrations

| System | Direction | Trigger | Data | Auth in repo | Failure |
|---|---|---|---|---|---|
| SQL Server `Framework_SHOU-1_Dev` | Read/write | Every data call through `xData` | `pal_` rows | Integrated Security on `NB-DEWALD` | Exceptions redirect to `/error` in the controls reviewed |
| WCF `framework_service.svc` | Request/response | Client endpoint in `Web.config` | `ProcessInteraction` byte arrays; version file get/save | Binding security mode `None`. Address is localhost | **Unknown** beyond WCF timeouts (send 10 minutes) |
| MediSwitch | Outbound | MSV module and claim types | Member and claim payloads | Gateway URL only. Credentials **Unknown** | **Unknown** |
| Netgen SMS `SMS_V2.asmx` | Outbound | SMS features and `smsU` / `smsP` app settings | SMS SOAP | Username and password in `appSettings` | **Unknown** |
| SMTP `smtp.evolutionsoftware.co.za:25` | Outbound | Mail from the site | Messages from `mailer@evolutionsoftware.co.za` | Username and password in `system.net` | **Unknown** |
| Google sign-in | Inbound profile | `GoogleLogin` | Name, email, profile id, image | Matches an existing user | Unknown emails are not registered |
| Google Sheets | Inbound | `SchedulerTool` argument `googlesheetimport*` | Sheet rows for fabric/trim/zip, colours, and details | JSON file under `SchedulerTool/` | Logged by `LogWriter`, then the process exits |
| Country web services | Outbound | Only if `getCountriesFromWebService` is `1` | Country, currency, GMT | None in config | Setting is `0`, so this path is off |
| SonarQube | Outbound from CI | Push to `master`, or any pull request | Source scan | `SONAR_TOKEN`, `SONAR_HOST_URL` pipeline variables | Pipeline failure. No deploy is tied to it |

Xero appears only as sales and purchase status `CapturedInXero = 11`. No Xero client was found. Provenance: **Implemented** as a label. Integration: **Unknown**.

## Important constraints

- Culture default in app settings is `en-ZA`, twice. `globalization` is `uiCulture="en"` and `culture="en-US"`. Which culture a given page actually uses **Requires Confirmation**.
- Date format setting is `yyyy-MM-dd hh:mm tt`.
- Upload limit is `maxRequestLength="10048"` (kilobytes).
- JSON serialisation limit is 50,000,000.
- Anonymous identification is enabled.
- Support, from, admin, cc, and bcc addresses in `Web.config` are Evolution Software mailboxes, not `@theshoulderpractice.co.za`.
- The second `website` app setting overwrites the first. Effective website name in configuration is `Evolution Software Framework`.

## Error behaviour

Controls reviewed in the medical and surface path catch exceptions, call `exception.HandleException` with the session user, and `Response.Redirect("/error", false)`. `Error.aspx` exists. The text shown to the user was not extracted.

## What is not a requirement

- There is no written backlog, roadmap, or "planned" feature in the repository.
- Excluding surface markup in 2024 is a change that happened. It is not documented as an approved product decision. Whether those clinical forms should remain unavailable **Requires Confirmation**.
