# Technical documentation — SHOU-1 (The Shoulder Practice)

Evidence date: 2026-10-04.

Source: `git@bitbucket.org:overdrive/shou-1.git`, `main` at `30a710c` (2026-09-22).

Provenance labels: **Documented**, **Implemented**, **Inferred**, **Unknown**.

This document describes the repository as it is. It does not prescribe a target architecture.

## Stack

| Layer | What the projects show |
|---|---|
| Language | C# |
| Web | ASP.NET Web Forms (`.aspx`, `.ascx`, `.master`). Friendly URLs via `RouteTable.MapPageRoute` in `Global.asax`. No `System.Web.Mvc` reference was found |
| Site target | `compilation targetFramework="4.8"` in `core_custom/SHOU-1/Web.config`. `httpRuntime targetFramework="4.6"` in the same file |
| Libraries | `library/library.csproj` targets .NET Framework 4.7.2. `business/business.csproj` targets .NET Framework 4.8 |
| Service | WCF (`service/framework_service.svc`), .NET Framework 4.8 |
| Data access | ADO.NET through `library/classes/dataTier.cs` for the site, library, and business layer. Entity Framework 6.2 is referenced by `tools/CAPI.Custom` and `tools/CEP` only |
| UI libraries | Telerik Web UI handlers in `Web.config`. AjaxControlToolkit. Assemblies live under `components/` |
| Other libraries | EPPlus, Spire (PDF/XLS), Newtonsoft.Json, BCrypt.Net-Next, NReco.VideoConverter, Google.Apis.Sheets, AutoMapper (present under `packages/`) |
| Database | SQL Server. Script project `data/data.sqlproj` uses the SQL Server 2016 provider (`Sql130`) |
| Logging | `library/classes/logWriter.cs` writes `ApplicationLog_yyyyMMdd.txt`. `library/classes/exception.cs` inserts `pal_Exceptions` and can email support. Scheduler writes under `logs/`. NLog is used by the CEP/CAPI tools. log4net packages are in `packages/` and were not referenced by the web configs that were read |
| Tests | No MSTest, NUnit, or xUnit project in `SHOU-1.sln`. `tools/CEP/CEP.ConsoleTests` is a manual proof-of-concept runner |

## Repository layout

| Path | Role |
|---|---|
| `SHOU-1.sln` | Primary solution: `library`, `business`, `service`, `mobile`, `data`, `core-dev`, `core_custom/SHOU-1`, plus solution folders |
| `EvolutionSoftwareUnifiedFramework.sln` | Second solution file for the wider framework. Not the tenant site by itself |
| `core_custom/SHOU-1` | Tenant website |
| `core-dev` | Upstream website template. `sonar-project.properties`: manual fork, no auto-sync |
| `core_custom/GLOB-1`, `core_custom/GLOB-3` | Other tenants. Excluded from Sonar |
| `library` | `framework_library.dll` |
| `business` | `framework_business.dll` |
| `components` | DLL drop used by the sites and tools |
| `service` | WCF website |
| `data/Scripts` | Versioned SQL, including `data/Scripts/SHOU-1` |
| `tools/scheduler` | Console batch (`tools/scheduler/scheduler.sln`) |
| `SchedulerTool` | WinForms wrapper. Not a project in `SHOU-1.sln` |
| `tools/password-migrate` | BCrypt migration console |
| `tools/constructor`, `dataImporter`, `exporter` | Operator WinForms tools |
| `tools/CAPI.Custom`, `tools/CEP` | Separate quote domain. Not the practice site |
| `mobile` | Empty-of-screens class library |
| `packages` | NuGet cache checked into the repo |
| `publish/SHOU-1`, `publish/service` | Published snapshots |
| `release/1.0.0.1`, `release/1.0.5` | Each folder contains `files.zip` and `scripts.zip` |
| `BuildProcessTemplates` | TFS 2011 XAML build templates |
| `sonar-project.properties` | SonarQube project key `overdrive_shou_1` |

`SHOU-1.sln` also lists a website project named `core` at `http://localhost:45935`. There is no `core/` directory. **Unknown** whether that IIS site is still used.

## Application structure

```text
core_custom/SHOU-1
  Global.asax          EnvBootstrap, routes, cache keepalive
  Web.config           appSettings, Telerik handlers, session
  *.aspx               page entry points (54 at the site root)
  Core.master          public shell
  Administration.master
  App_Code             MediSwitch, MedicalSchemeDisplay, site helpers
  controls/admin       back-office controls
  controls/medical     reference-data controls
  controls/module      calendar, enquiry, debtors, patient sign-in, MSV, …
  controls/module_custom/billingInstructions.ascx
  controls/surface     generated/shared surface grid, form, view
  controls/surface_custom/SurgicalNotes_*
  controls/structure   navigation and chrome
  Bin                  framework_library.dll, framework_business.dll, CodeDom/Roslyn
  SessionHeartbeat.ashx
```

Session keys used by the handler include `Session["user"]` (`oUser`) and `Session["osetup"]` (`oSetup`). Timeout is 60 minutes (`sessionState`). Anonymous identification is enabled.

`business/handlers/handler.cs` is the web helper: current setup and user, canvas checks, module permission, banner HTML, central user create/update, login delegation.

`business/handlers/surfaceHandler.cs` emits surface ASCX markup.

`business/classes/xData.cs` is the main CRUD and query façade over `dataTier`.

## Configuration and secrets

`library/classes/EnvBootstrap.cs` loads a `.env` file at process start (`Global.asax` `Application_Start`, and tool mains). `ENV_FILE` overrides the path. The loader walks parent directories.

`library/classes/ConfigResolver.cs` resolves:

| Variable | Used for |
|---|---|
| `DB_CONNECTION_STRING` | Tenant connection (`conn`) |
| `DB_LIVE_CONNECTION_STRING` | Central auth (`connLive`) |
| `DB_EXT_CONNECTION_STRING` | Optional secondary |
| `DB_CENTRAL_CATALOG`, `DB_TENANT_CATALOG` | Catalog name overrides |
| `SMS_USERNAME`, `SMS_PASSWORD` | SMS gateway credentials |
| `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_ENABLE_SSL` | Mail |
| `GOOGLE_SHEETS_CREDENTIALS_FILE` | Service account JSON |
| `BCRYPT_WORK_FACTOR`, `PASSWORD_MIN_LENGTH`, `LOGIN_MAX_ATTEMPTS` | Password and lockout |

The tenant `Web.config` comment says DB, SMS, and SMTP come from `.env`. No SQL connection string was found in that `Web.config`.

Committed appSettings that are not secrets but do change behaviour: `WebAddy` `http://localhost:21904`, `support` / `admin` / `cc` / `bcc` / `loyaltyEmailAddy` (an Overdrive mailbox), `from` `no-reply@theshoulderpractice.net`, MediSwitch QA URLs, `MediSwitchResponses` `0`, `loyaltyEmail` `0`, `getCountriesFromWebService` `0`, `compilation debug="true"`.

`.gitignore` excludes `.env`, `GoogleSheetAPISecretes.json`, `business/client_secrets.json`, scheduler copies of the Google file, and `.generated-surface-controls/`.

Do not copy live passwords or service-account keys into documentation. The example files are placeholders.

## Databases

| Catalog | Role | Evidence |
|---|---|---|
| `Framework_SHOU-1` | Tenant data | `.env.example` |
| `Framework_EVOL-1` | Central `pal_User` and `pal_UserPassword` | `.env.example`, password scripts |
| `Framework_SHOU-1_qa`, `Framework_EVOL-1_qa` | QA names used by the migration README and `deploy-qa-tenant-db.ps1` | **Documented** |
| `Framework_REAP-1` | Commented secondary connection | `.env.example` only |

Table prefix in scheduler comments is `pal_`. Objects named in SHOU-1 scripts and code include `pal_User`, `pal_UserPassword`, `pal_vUserShared`, `pal_vUserPasswordShared`, `pal_Exceptions`, `pal_Account`.

`data/data.sqlproj` does not model the schema as SSDT objects. Change scripts are files under `data/Scripts/`.

SHOU-1 scripts:

| File | What the name and README state |
|---|---|
| `SHOU-1_3.0.01_create_pal_UserPassword_EVOL-1.sql` | Central password table. Run on the central catalog |
| `SHOU-1_3.0.02_create_pal_vUserPasswordShared.sql` | Tenant view. Requires SQLCMD variable `CentralCatalog` |
| `SHOU-1_3.0.03_null_legacy_passwords_SHOU-1.sql` | Clears legacy passwords on the tenant |
| `SHOU-1_3.0.04_null_legacy_passwords_local_SHOU-1.sql` | Local variant of that clear |
| `SHOU-1_3.0.05_cleanup_phase3_test_users.sql` | Test-user cleanup |
| `SHOU-1_3.0.06_refresh_pal_vUserShared_login_lockout.sql` | View includes lockout columns |
| `SHOU-1_3.0.07_add_pal_User_deactivation_EVOL-1.sql` | Deactivation columns on the central user |
| `SHOU-1_3.0.08_refresh_pal_vUserShared_deactivation.sql` | View includes deactivation |
| `SHOU-1_qa_remediate_crossdb_refs.sql` | QA cross-database names |
| `deploy-qa-tenant-db.ps1` | Applies the QA tenant set |
| `SHOU-1_insert_new_open_surgery_type_11022020.sql` | Open-surgery note template data |
| `SHOU-1_update_place_of_service_28042020.sql` | Place of service data |
| `SHOU-1_update_referring_doc_num_01042020.sql` | Referring doctor number data |

Archive folders (`archive major 1` through `archive major 1.6.1`) hold older numbered framework scripts. They are not all SHOU-1-specific.

## WCF service

`service/framework_service.svc` implements `Iframework_service`:

| Operation | Behaviour |
|---|---|
| `ProcessInteraction` | Runs a serialised `dynamicInteraction` against a connection key |
| `GetVersion` | Returns file bytes for a customer code, licence key, version, and file name |
| `SaveVersion` | Stores version bytes |

`service/Web.config`: `wsHttpBinding`, large reader quotas, `security mode="None"`, `serviceMetadata` HTTP GET enabled, `includeExceptionDetailInFaults="false"`, `directoryBrowse enabled="true"`.

The tenant site has a matching client endpoint. Treat the open metadata and directory browse as properties of the committed service config. Whether IIS locks them down is **Unknown**.

## Integrations

| Integration | Where | Trigger |
|---|---|---|
| MediSwitch SOAP | `Web.config` `Mediswitch.Gateway*`; `tools/scheduler/MediSwitch.cs`; `App_Code/MediSwitch.cs`; `controls/module/mediswitchMSV.ascx` | Staff MSV search; scheduler delayed responses. Committed URLs are the QA gateway. Live URLs are comments |
| SMTP | `communication.cs` via `ConfigResolver.ConfigureSmtp` | Registration, statements, exceptions, campaign threshold |
| SMS HTTP | `communication.SendSMSMessage` | AppSettings gateway plus env credentials |
| SendEasy SOAP | `xSendEasy`, endpoint `http://sms.netgen.co.za/SMS_V2.asmx` | Campaign and SMS sends that use this client |
| Google Sheets | `GoogleSheetsHelper`, `GoogleSheetImports` | Scheduler or `SchedulerTool` argument `googlesheetimport` |
| Paygenius | `library/Payment/PaygeniusClient.cs` | HMAC headers `X-Token` and `X-Signature`. Call from the SHOU-1 pages was not confirmed |
| Country ASMX | `webservicex.net` URLs in `Web.config` | Only if `getCountriesFromWebService` is `1`. Committed value is `0` |
| QuickBooks | `Interop.QBXMLRP2` hint path in `library/library.csproj` | Usage in this tree is **Unknown** |
| SonarQube | `sonar-project.properties` | Comment names host `scan.appmonitor.co.za`. Pipeline file not in the repo |

`Global.asax` `RemovedCallback` pings `WebAddy` to keep the site alive, and skips that ping for localhost. MediSwitch work inside that callback is commented.

## Background jobs

`tools/scheduler` is the batch process. Default run covers campaigns, a loyalty-email send that checks `loyaltyEmail` and `loyaltyEmailHour`, a transaction lock, MediSwitch, and surface data import. Logs go to `logs/Scheduler_Log*.txt`.

`SchedulerTool` is a WinForms executable that returns immediately with no arguments and otherwise forwards Google Sheet import.

How either executable is scheduled on a server is **Unknown**.

## Build and deploy

From `.env.example` and the project files:

1. Copy `.env.example` to `.env`.
2. `nuget restore SHOU-1.sln` (Visual Studio restore is the alternative named in the example).
3. Build `library`. AfterBuild runs `core_custom/SHOU-1/deploy-codedom.ps1` and copies `framework_library.dll` and Newtonsoft into `core_custom/SHOU-1/Bin` and `service/Bin`.
4. Build `business`. AfterBuild copies `framework_business.dll`, BCrypt, and related binding redirects into those `Bin` folders.
5. `deploy-codedom.ps1` copies Microsoft.CodeDom.Providers.DotNetCompilerPlatform (default 4.1.0) and `csc.exe` into `Bin/roslyn`. The script comment says website projects do not run NuGet's MSBuild targets, so this copy is required after a clean `Bin`.

`publish/SHOU-1` and `publish/service` are checked-in outputs. No script was found that regenerates them. **Inferred**: they are Visual Studio publish or a manual copy. Confirm before treating them as the current build.

`release/1.0.0.1` and `release/1.0.5` each contain `files.zip` and `scripts.zip`. Their internal manifest was not unpacked for this document. What they correspond to operationally is **Unknown / Requires Confirmation**.

`BuildProcessTemplates/*.xaml` are TFS 2011 definitions (build, test pattern `**\*test*.dll`, label, deploy). No `bitbucket-pipelines.yml` was found. Current CI is **Unknown**. The Sonar comment is the only scanner instruction in-repo.

## NuGet package ids

From `packages.config` files in the tree: `AutoMapper`, `BCrypt.Net-Next`, `EntityFramework`, `Microsoft.AspNet.WebApi.Client`, `Microsoft.AspNet.WebApi.Core`, `Microsoft.CodeDom.Providers.DotNetCompilerPlatform`, `Microsoft.CodeDom.Providers.DotNetCompilerPlatform.WebSites`, `Microsoft.Net.Compilers`, `Microsoft.Net.Compilers.Toolset`, `Newtonsoft.Json`, `NLog`, `NLog.Config`, `NLog.Schema`, `NReco.VideoConverter`, `System.ValueTuple`.

`business/business.csproj` also references `Google.Apis`, `Google.Apis.Auth`, `Google.Apis.Core`, and `Google.Apis.Sheets.v4` by hint path. Several `Microsoft.CodeDom.Providers.DotNetCompilerPlatform` versions (1.0.0 through 4.1.0) are present under `packages/`. The deploy script's default is 4.1.0.

## Security controls that are in the tree

| Control | Evidence |
|---|---|
| Secrets in `.env`, not in `Web.config` connection strings | `ConfigResolver`, `.gitignore`, `Web.config` comment |
| BCrypt for new passwords, with a legacy cipher fallback on login | `PasswordService` |
| Password complexity and denylist | `PasswordPolicy` |
| Failed-attempt lockout | `LOGIN_MAX_ATTEMPTS` |
| Deactivation flag checked before login | `IsLoginAllowed` |
| Registration inactive until the mail link when `securePath` is set | `Register.aspx.cs` |
| Session timeout 60 minutes | `Web.config` |
| SMTP SSL flag default true in `.env.example` | `SMTP_ENABLE_SSL=true` |
| Service faults hide exception detail | `includeExceptionDetailInFaults="false"` |

Not found in the committed tenant `Web.config`: `httpCookies requireSSL`, HSTS, or a rewrite rule that forces HTTPS. WCF security mode is None. Service directory browsing is enabled. `compilation debug` is true. These are properties of the file that is in git. Production IIS may differ. **Unknown / Requires Confirmation**.

`support`, `admin`, `cc`, and `bcc` in the committed file are an Overdrive mailbox, and `WebAddy` is localhost. That is a development configuration, not evidence of the production contact route.

## Development process as written

- Restore NuGet, build `library` and `business`, deploy CodeDom, configure `.env`, apply SQL by hand, then run `tools/password-migrate` if legacy hashes remain.
- Sonar exclusions (vendor JS, `packages/`, `publish/`, `bin/`, `obj/`, `components/`, GLOB forks, tool build outputs) are listed in `sonar-project.properties`. The same file says shared JS and ASCX fixes must be applied in both `core-dev` and `core_custom/SHOU-1` when both trees are scanned.
- There is no automated test suite to run for the site.

## Local run

The committed `WebAddy` is `http://localhost:21904`. The solution's `core` website project points at `http://localhost:45935`. Neither port is started by a script in the repo. How a developer attaches IIS Express or full IIS is **Unknown / Requires Confirmation** beyond opening the website project in Visual Studio.

## Related Silicon Ark documents

- `project-plan.md` — scope, history, status
- `prd.md` — users, workflows, functional requirements
- `features.md` — module catalogue and recent changes
- `business-rules.md` — rules enforced in code
