# Module: Platform, Auth, Roles & Settings (platform)

## Purpose
The infrastructure layer of the legacy OB/GYN system ("aw framework"). Covers: user authentication (session + remember-me cookie via a bundled "php-login" style library), a 4-level RBAC system (role -> controller -> action(prop) -> UI button, plus role -> menu visibility), the dynamic sidebar menu tree, the singleton program-settings record that toggles almost every clinical feature, multi-branch/floor/device scaffolding (mostly dead), the waiting-room display app (`screen/`), the patient-facing mobile JSON API (`mobileservices.php`), help articles, and a handful of junk/test tables.

## Controllers & Views (file paths)
- `/home/amrtechogate/public_html/obgy/core/controllers/login.php` — login/logout/forgot-password; wraps `_library/login_system`. Contains a commented-out licensing check against `excelinfo.serial` + device id.
- `/home/amrtechogate/public_html/obgy/_library/login_system/classes/Login.php` (+ `Registration.php`, translations `ar.php`/`en.php`) — PDO auth against `awusers`: bcrypt verify, failed-login throttle (3 tries / 30s), remember-me cookie (`user_id:token:sha256`), writes `$_SESSION['user_id']`, `$_SESSION['role_id']`, etc.
- `/home/amrtechogate/public_html/obgy/core/controllers/imp/_autho.php` — `autho::checkautho()` (logged-in check) and `autho::checkauthoize($controllname, $hosturl, $roleid)` — resolves `awcontroll` by name, `awcontrollprop` by action (`?ac=`), then `awrolecontrollprop` override; falls back to the prop's default `checkval`. Called at the top of every action in every controller.
- `/home/amrtechogate/public_html/obgy/core/controllers/_role.php` (598 lines) — role CRUD; permission matrix editor: writes `awrolemenu`, `awrolecontrollprop`, and `awrolebtn` (hardcoded buttons: btn_id 1=add patient, 2=add visit, 3=edit visit, 4=delete visit, rendered in `displaybtns()`). Views: `core/views/obgy/_role/`.
- `/home/amrtechogate/public_html/obgy/core/controllers/_member.php` (356) — user CRUD on `awusers` (raw INSERT/UPDATE with `password_hash`), username-uniqueness AJAX, and AJAX loaders for `awroleposition`/`awrolespecialize` filtered by `roleid`.
- `/home/amrtechogate/public_html/obgy/core/controllers/_menu.php` (363) — `awmenu` CRUD + drag-and-drop reordering (`arrangemain`/`arrangesub` update `sort`/`parent`); icon image upload.
- `/home/amrtechogate/public_html/obgy/core/controllers/_controll.php` (303) — registry CRUD for `awcontroll` + its `awcontrollprop` action list (`delprop` action).
- `/home/amrtechogate/public_html/obgy/core/controllers/_sidebar.php` — builds the sidebar from `awmenu` (recursive on `parent`), filters by `programesetting.simpleview` vs `awmenu.form` (0=complete,1=simple,2=both), checks `awrolemenu` per role, and `withpatient` flag appends current patient id to links.
- `/home/amrtechogate/public_html/obgy/core/controllers/programesetting.php` (296) — single form that updates `programesetting` row id=1 (~75 columns of toggles), logo/background uploads.
- `/home/amrtechogate/public_html/obgy/core/controllers/setup.php` (225) — protected by hardcoded password `123456`; database "reset" utility (`settingdatabase()` truncates all tables except a hardcoded keep-list); links an external ERP database via `R::addDatabase('erpDB', ...)`, stores `programesetting.erpdb` / `erpdbsave` and autocompletes ERP `save` (treasury) records.
- `/home/amrtechogate/public_html/obgy/core/controllers/help.php` (215) — CRUD on `help` table.
- `/home/amrtechogate/public_html/obgy/core/controllers/mobileservices.php` (671) — patient mobile JSON API, **no auth** (autho calls commented out), `Access-Control-Allow-Origin: *`. Endpoints: `addPatient` (creates `patients` row with `patient_password_hash`, `devid`), `updatePatient`, `getPatientData`, `patientLogin` (mobile+password; raw string-concatenated SQL — injectable), `addVisit`/`updateVisit`/`delVisit`/`visitType`, `queryAllVisitsByPatientAndDate`, `queryAllVisitsByDate`, `roshetat`+`showprescription` (reads `gynadrugs`+`drugs`), `selectChatting` (reads `chat` join `patients`; hardcoded `http://localhost/obgy_new/` paths), ERP client sync (`erpClient`, `curlAddClient`, `curlUpdateClient`, `erpGetClientTreeId`) calling the ERP app via curl.
- `/home/amrtechogate/public_html/obgy/screen/controllers/index.php` — waiting-room display: reads `screen_slider` images, today's `visits` (view/end_visit flags) joined to `patients.wifename` for "now serving / next 5" queue; performs runtime `ALTER TABLE visits ADD end_visit` if missing; `loadInfo` is the AJAX refresh. Views: `/home/amrtechogate/public_html/obgy/screen/views/`.
- `/home/amrtechogate/public_html/obgy/_public/aw_config.php` — global config: hardcoded DB credentials (`amrtechogate_obgy` / `53i#WRqAw8r121618`), `$hosturlApi = 'http://api.gt4it.com'` (external API "like sms, invests"), RedBeanPHP bootstrap.
- `/home/amrtechogate/public_html/obgy/_public/api_config.php` — same DB creds + `putenv` of ERP api_key/api_user/api_password in plaintext.
- `/home/amrtechogate/public_html/obgy/_library/php-jwt-master/` — Firebase php-jwt bundled but **no `JWT::encode/decode` call exists anywhere outside the library** — dead dependency.

## Tables

### awcontroll (registry of controllers)
| column | type |
|---|---|
| id | int(11) unsigned PK AI |
| label | varchar(191) |
| name | varchar(191) |
Seeded 81 rows mapping every controller file to a permission unit: `_role`, `_menu`, `_member`, `_controll`, `_myprofile`, pharmacy (`drugs`,`medicine`,`buys`,`receipt`,`store`), board (`requests`,`sessions`), clinical (`gyna`,`investigation`,`patienthistory`,`antenalvisit`,`drugsex`,`instruction`,`programesetting`,`help`,`sonar`,`addinvestigation`,`expected`,`completereport`,`financialreport`,`examination`,`_phmain`,`patients`,`operativedetails`,`gtimage`,`visits`,`backup`,`setup`,`excel`,`infertility`,`record`,`followup`,`ultrasound*`,`termination`,`risktype`,`merge`,`combinedreport`,`Followupcard`,`Fullreport`,`monitoring`, sheets `Infertilitysheet`/`Ancsheet`/`Gynasheet`/`Ivfsheet`, reports `iui`/`epc`/`Deliveries`/`Operations`/`edd`/`Ivfstatistics`/`Completesreport`,`Addpresenthistory`,`adddevices`(80),`branches`(81)). Note ids 80/81 registered but **no `adddevices.php`/`branches.php` controller file exists**.

### awcontrollprop (actions per controller)
| column | type |
|---|---|
| id | int(11) unsigned PK AI |
| proplabel | varchar(191) — action name matched against `?ac=` (index, addit, show, edit, updateit, del, delprop, setting, ...) |
| checkval | tinyint(1) — default allow(1)/deny(0) when no role-specific row exists |
| awcontroll_id | int(11) unsigned FK -> awcontroll.id (indexed) |
~590 seeded rows (one per action per controller).

### awmenu (sidebar menu tree)
| column | type |
|---|---|
| id | int PK AI |
| label | varchar(191) |
| link | varchar(191) — relative controller URL |
| parent | varchar(191) — self-ref awmenu.id, '0' = root |
| sort | int |
| icon | varchar(191) |
| active_name | varchar(191) — highlight key |
| display | varchar(191) — 'image' or 'icon' |
| image | varchar(191) — uploaded file |
| form | int default 0 — 0=complete-form mode only, 1=simple mode only, 2=both (filtered by `programesetting.simpleview`) |
| withpatient | int default 1 — 1 = link carries current patient context |
102 seeded rows (Dashboard, Roles, Members, Controlls, Pharmacy, Board, clinical sheets, Reports tree, Setup, Branches at id 102 pointing to nonexistent `Branches.Php`).

### awrole
`id` int PK, `name` varchar(191). Seeded: 1 Management, 2 pharmacist, 3 reception, 4 doctors, 5 rays, 6 assistants, 7/8 (empty names), 10 'lobna role'.

### awrolebtn (per-role UI button rights)
`id`, `role_id` -> awrole.id, `btn_id` (hardcoded 1=add patient, 2=add visit, 3=edit visit, 4=delete visit), `checkval`. Rows auto-created lazily by `_role.php::displaybtns()`. Dump empty (AUTO_INCREMENT=1).

### awrolecontrollprop (role x action grants)
`id`, `role_id` -> awrole.id, `controllprop_id` -> awcontrollprop.id, `checkval` tinyint. ~3000 rows seeded. Deleted in bulk when a role is deleted (`_role.php` del).

### awrolemenu (role x menu visibility)
`id`, `role_id` -> awrole.id, `menu_id` -> awmenu.id, `checkval`. ~490 rows.

### awroleposition (job positions, scoped to a role)
`id`, `roleid` -> awrole.id, `name`. Seeded: (role 4 doctors) Council Amin, Council Head, Council Staff; (role 5 rays) Sonographer, Supervisor.

### awrolespecialize (specializations, scoped to a role)
`id`, `roleid` -> awrole.id, `name`. Seeded: Anasthests + operation for roles 4 (doctors) and 6 (assistants).

### awusers (staff accounts) — MyISAM, utf8
| column | type |
|---|---|
| user_id | int PK AI |
| user_name | varchar(64) |
| user_password_hash | varchar(255) bcrypt |
| user_email | varchar(64) |
| user_active | tinyint(1) |
| user_activation_hash | varchar(40) |
| user_password_reset_hash | char(40) |
| user_password_reset_timestamp | bigint |
| user_rememberme_token | varchar(64) |
| user_failed_logins | tinyint(1) |
| user_last_failed_login | int(10) |
| user_registration_datetime | datetime |
| user_registration_ip | varchar(39) |
| role_id | int FK -> awrole.id |
| name | varchar(2000) display name |
| specialid | int FK -> awrolespecialize.id |
| positionid | int FK -> awroleposition.id |
| attend | int (attendance flag; no code usage found in this module) |
| branch_id | varchar(11) default '0' FK -> branches.id (استنتاج; seed user has '-1' = all branches, inferred) |
Seed: single user `manager` (role 1, branch_id '-1').

### programesetting (singleton settings, row id=1) — MyISAM
~75 columns. Groups: clinic identity (`centername`,`doctorname`,`spacial`,`address`,`phone`,`logo`,`background`); print layout (`color`,`above`,`buttom`,`pleft`,`pright`,`center`,`fontsizeen`,`fontsizear`,`alignttt`,`namefontsize`,`printserial`,`barcode_print`,`print_diag`); feature toggles (`ultrasound`,`patientnoaut`,`addshours`,`ishide`,`simpleview`,`displaydoctorname`,`entryorder`,`investshow`,`prescriptioncashing`,`diagnosshow`,`visitsform`,`hideid`,`programview`,`patient_doctor`,`previous_marriage`,`infertility`,`visit_period`,`art_internal`,`art_external`); patient-history section toggles (`balance`,`head`,`chest`,`abdomen`,`pelvis`,`extremitis`,`btype`,`hbtype`,`risk`,`notes`,`menstrual`,`contraception`,`obstetric`,`past`,`family`,`medical`,`surgical`,`gynecological`,`art`,`entrydetails`,`gynalmp`); backup (`backupdest`,`backupdriver`,`phppath`); ERP link (`erpdb`,`erpdbsave`); email creds in plaintext (`email`,`password`); receipt serials (`serial_month` e.g. '2024-04', `last_serial`, `last_refund_serial`); `branches` int flag; sheet links (`gynalink`,`antenatal`).

### branches
`id` int unsigned PK, `name` varchar(191), `created_at`, `updated_at` datetime. **Empty, no controller file** (menu id 102 + awcontroll id 81 reference a missing `Branches.php`). Referenced only by `awusers.branch_id` and `programesetting.branches` flag.

### floors
`id`, `name` varchar(255), `deleted` int, `default_value` int. Empty; no code usage found.

### devices
`id`, `device_name` varchar(255), `location` int, `floor_no` int (FK -> floors.id, استنتاج), `deleted` int. Empty; `adddevices` registered in awcontroll but controller file missing.

### device_tracking (polymorphic patient-flow tracking)
`id`, `patient_id` int (FK -> patients.id), `device_id` int (FK -> devices.id), `target_id` int + `target_table` varchar(255) (polymorphic pointer to a clinical record), `visit_date` date, `visit_time` time, `user_id` int (FK -> awusers.user_id), `deleted`, `control` varchar(255). Empty; no live code usage found — planned/abandoned feature (استنتاج).

### messages (internal user-to-user messages)
`id`, `message` varchar(191), `sender_id` int unsigned (FK -> awusers.user_id, indexed), `receiver_id` int unsigned (FK -> awusers.user_id, indexed), `created_at` datetime. Empty; no controller usage found. Note: actual patient chat uses a separate `chat` table consumed by `mobileservices.php::selectChatting`.

### screen_slider (waiting-room slideshow)
`id`, `image` varchar(191), `create_date` date. Read by `screen/controllers/index.php`; empty in dump.

### help (help articles)
`id`, `name` varchar(255), `content` varchar(255), `tempdelete` char(1) default '0' (soft delete). CRUD via `help.php`.

### Junk / test tables (all empty, no code references)
- `table2`: `id`, `name2` varchar(50)
- `table3`: `id`, `name` varchar(100)
- `tablename`: `id`, `del` int — clearly a leftover from a CREATE TABLE template
- `testtbl1`: `id`, `name2` varchar(50)
- `testtbl2`: `id`, `name` varchar(55)
All five are developer test artifacts — safe to drop.

## Relationships (inferred; no declared FKs except RedBean index hints)
- awcontrollprop.awcontroll_id -> awcontroll.id
- awrolecontrollprop.role_id -> awrole.id
- awrolecontrollprop.controllprop_id -> awcontrollprop.id
- awrolemenu.role_id -> awrole.id
- awrolemenu.menu_id -> awmenu.id
- awrolebtn.role_id -> awrole.id
- awmenu.parent -> awmenu.id (self-reference, stored as varchar)
- awusers.role_id -> awrole.id
- awusers.specialid -> awrolespecialize.id
- awusers.positionid -> awroleposition.id
- awusers.branch_id -> branches.id (varchar, '-1' = all)
- awroleposition.roleid -> awrole.id
- awrolespecialize.roleid -> awrole.id
- device_tracking.patient_id -> patients.id; device_tracking.device_id -> devices.id; device_tracking.user_id -> awusers.user_id; device_tracking.(target_table,target_id) -> polymorphic
- devices.floor_no -> floors.id
- messages.sender_id / messages.receiver_id -> awusers.user_id
- Cross-module: screen app reads visits.patientid -> patients.id; mobileservices writes patients/visits, reads gynadrugs.drugid -> drugs.id, chat.patient_id -> patients.id; setup.php links external ERP DB table `save`; programesetting.erpdb/erpdbsave -> external ERP database.

## Business Workflows (traced from code)
1. **Login**: `login.php` -> `Login` class -> PDO select on `awusers` by user_name -> bcrypt verify -> session gets `user_id`, `user_name`, `role_id`; optional remember-me cookie writes `user_rememberme_token`. Failed logins throttled (3 attempts / 30 seconds). Logout destroys session. A device-serial licensing check against `excelinfo` exists but is commented out.
2. **Per-request authorization**: every controller constructor includes `_autho.php`; every action calls `autho::checkautho()` then `autho::checkauthoize(controllname, hosturl, $_SESSION['role_id'])`, which resolves awcontroll -> awcontrollprop(action) -> awrolecontrollprop(role) and redirects to `error.php?ac=autho` on deny. Default falls back to `awcontrollprop.checkval`.
3. **Role administration** (`_role.php`): create role -> open matrix editor: tree of menus (`awrolemenu` show/hide radio), per-controller action grid (`awrolecontrollprop` allow/deny), and four hardcoded clinical UI buttons (`awrolebtn`). Deleting a role bulk-deletes its awrolemenu + awrolecontrollprop rows.
4. **User administration** (`_member.php`): add user -> AJAX username uniqueness -> raw INSERT into awusers with bcrypt hash, role, and role-scoped `specialid`/`positionid` loaded via AJAX from `awrolespecialize`/`awroleposition`.
5. **Menu administration** (`_menu.php`): CRUD + drag-drop sort of the two-level sidebar; sidebar rendering (`_sidebar.php`) filters by `simpleview` mode (`awmenu.form`), per-role `awrolemenu`, and appends patient context when `withpatient=1`.
6. **Program settings** (`programesetting.php`): one giant form updates the singleton row; toggles drive sidebar mode, visible history sections, printing layout, serials, ART internal/external, etc. — this row is read by virtually every clinical controller.
7. **Setup / ERP link** (`setup.php`): password `123456` gate -> set ERP database name (`programesetting.erpdb`) -> RedBean `addDatabase('erpDB')` -> pick default treasury (`save`) record -> `erpdbsave`. Also a destructive "reset database" routine truncating all but a hardcoded keep-list.
8. **Waiting-room screen** (`screen/`): unauthenticated display page polling `loadInfo`: shows `screen_slider` images, the last called patient (`visits.view=1, end_visit=0`) and next 5 waiting (`view=0`) for today, with `patients.wifename`.
9. **Mobile patient API** (`mobileservices.php`): patient self-registration (`addPatient` with mobile+password), `patientLogin`, visit booking (`addVisit` types: kashf/ultrasound/i3ada), prescription viewing (`roshetat`/`showprescription` from `gynadrugs`+`drugs`), chat history (`chat` table), and bidirectional client sync with the external ERP via curl + api_key.

## ERP Migration Notes
- **Auth**: replace the bundled login_system with Laravel's native auth (Sanctum for the Angular SPA + mobile API). Map `awusers` -> `users` (id, name, username, email, password, is_active, branch_id FK nullable, role via spatie). Drop activation/reset/rememberme columns (framework handles them). `attend` appears unused — verify then drop.
- **RBAC**: replace the 4-table matrix (awcontroll/awcontrollprop/awrolecontrollprop/awrolemenu/awrolebtn) with **spatie/laravel-permission**: `roles`, `permissions` (one permission per controller-action, e.g. `visits.delete`, plus the four button rights as `patients.create-button` etc.), `role_has_permissions`. Menus become frontend route guards driven by permissions, not a DB menu table — or keep a `menu_items` table only if runtime menu editing is a real requirement.
- **Positions/specializations**: `awroleposition`/`awrolespecialize` -> simple `positions` and `specializations` lookup tables (or enum) with `role` scope column; FK from `users`.
- **Settings**: explode the 75-column `programesetting` into a key-value `settings` table (e.g. spatie/laravel-settings) grouped as: clinic_identity, printing, features, history_sections, serials. Move `email`/`password` (plaintext SMTP creds) to `.env`/encrypted settings. The receipt serial counters (`serial_month`,`last_serial`,`last_refund_serial`) must move to a transactional `sequences` mechanism to avoid race conditions.
- **Multi-branch**: `branches` exists but is dead code (controller missing). In the ERP make `branches` a first-class model with `branch_id` (int FK, not varchar) on users/visits/financials; replace the magic '-1 = all branches' with a pivot `branch_user` table.
- **Devices/floors/device_tracking**: abandoned patient-flow feature. If queue/room tracking is wanted, redesign as `rooms`, `devices`, and a `patient_flow_events` table (polymorphic `trackable`) — otherwise drop all three.
- **messages**: empty and unused — drop; if staff chat is needed use a dedicated package. Patient chat (`chat` table, other module) should become a proper `conversations`/`messages` schema.
- **screen_slider + screen app**: keep as a `screen_slides` model + a public Angular display route consuming a websocket/polling "queue" endpoint; remove the runtime `ALTER TABLE` hack (`visits.end_visit` must be a real migration in the visits module).
- **Mobile API**: rebuild `mobileservices.php` as authenticated REST (Sanctum tokens). Current code has critical issues to NOT carry over: no auth on endpoints, `Access-Control-Allow-Origin: *`, SQL injection in `patientLogin` (string-concatenated mobile) and `getErpSave` (LIKE concat), ERP api_key/password hardcoded in `api_config.php`, hardcoded `http://localhost/obgy_new/` paths in chat.
- **JWT library**: bundled `php-jwt-master` is never called — drop.
- **Junk tables**: drop `table2`, `table3`, `tablename`, `testtbl1`, `testtbl2` — developer leftovers, all empty.
- **Setup utility**: do not migrate the truncate-database routine or the hardcoded `123456` password; replace with artisan commands + proper authorization.
- Proposed Laravel models: `User`, `Role`/`Permission` (spatie), `Position`, `Specialization`, `Branch`, `Setting`, `MenuItem` (optional), `ScreenSlide`, `HelpArticle`, `Room`/`Device`/`PatientFlowEvent` (optional redesign).
