# Munal LMS — Work Progress

> **Last Updated**: 2026-10-12
> **Current Phase**: Phase 5 Complete — Ready for Phase 6  
> **Status**: Phase 5 Complete (203/203 tests passing) — Notifications & Client Link Pages Complete

> ### 🛡️ Non-Negotiable Engineering Directive
> **Always write test cases and handle edge cases from the very beginning.**  
> Every feature, service, and API endpoint must ship with automated tests in the same step. Explicitly test boundary values, null/empty inputs, permission denials, and multi-branch tenant isolation. Zero regressions, 100% test pass rate, clean linting (`ruff`), and strict type checking (`mypy app/`) are required before marking any step complete.

---

## Phase Overview

| Phase | Name | Status | ETA |
|---|---|---|---|
| **Phase 0** | Planning & Tech Spec | ✅ Complete | Done |
| **Phase 1** | Database Schema & Migrations | ✅ Complete | Done |
| **Phase 2** | API Endpoints & Core Domain CRUDs | ✅ Complete | Done |
| **Phase 3** | Lab Workflow (Sample, Results, Verification, PDF) | ✅ Complete | Done |
| **Phase 4** | Financial (Billing, Discounts, Payments, Commissions) | ✅ Complete | Done |
| **Phase 5** | Notifications + Client Link Pages | ✅ Complete | Done |
| **Phase 6** | Dashboard & Analytics | 🟡 Next | Week 13–14 |
| **Phase 7** | Testing, Hardening, Deployment | ⬜ Planned | Week 15–16 |

---

## Phase 0 — Planning & Tech Spec ✅

### Completed
- [x] Reviewed project ERD (`Munal LMS-ERD - Final (1).png`)
- [x] Architectural decisions locked (React+Vite, FastAPI, PostgreSQL, Redis, Alembic)
- [x] Created `Tech-Reference-Spec.md` — full architecture spec
- [x] Created `Stack-Reference.md` — concise package reference
- [x] Created `Solution_Architect.md` — standing AI prompt
- [x] Created `Work_Progress.md` — this file
- [x] Created `Project_Summary.md` — compact project context
- [x] Clarified: single-tenant (not multi-tenant); `lab_id` = branch scoping
- [x] Clarified: no client portal; clients get SMS/email link for report + appointment view
- [x] Git repo created: `git@github.com:sharmadeeps/munalmedical-be.git`

### Decisions Locked
- Single-tenant, multi-branch via `lab_id`
- Client auth = token-based link (no password/portal)
- Client identity = mobile number

---

## Phase 1 — Database Schema & Migrations ✅

### Step 1: Entity List & ERD Sign-off
- [x] Produce complete entity list with justifications (sourced from ERD)
- [x] Mark inferred entities as `[ASSUMPTION]`
- [x] Produce ERD in Mermaid `erDiagram` format
- [x] **User sign-off received** — all 25 entities approved

### Step 2: Project Scaffold
- [x] `pyproject.toml` — all deps + ruff/mypy/pytest config
- [x] `docker-compose.yml` — PostgreSQL 16 + Redis 7
- [x] `.env.example` — all env vars documented
- [x] `.gitignore` — Python standard, docs/ NOT ignored
- [x] `Dockerfile` — multi-stage build
- [x] `app/__init__.py`, `app/main.py`, `app/config.py`, `app/database.py`
- [x] Stub `__init__.py` for: core, models, schemas, api, api/v1, services, repositories, tasks, utils
- [x] `tests/__init__.py` + `tests/conftest.py` with async fixtures
- [x] `README.md` — comprehensive developer guide
- [x] **Commit #1**: `chore(infra): init project scaffold, docker, and environment config` — **PUSHED ✅**

### Step 3: Model Implementation
- [x] `app/models/base.py` — Base, MetaData naming convention, UUIDPrimaryKeyMixin, TimestampMixin, SoftDeleteMixin, LabScopedMixin
- [x] `app/models/__init__.py` — imports Base, roadmap comments for all 25 models
- [x] **Commit #2**: `feat(schema): add SQLAlchemy base, mixins, and MetaData naming convention`
- [x] `app/models/lab.py` + `app/models/user.py` — LAB, USER, PERMISSION, ROLE_PERMISSION, USER_PERMISSION_OVERRIDE + UserRole enum
- [x] **Commit #3**: `feat(schema): add lab and user/access-control models`
- [x] `app/models/patient.py` — PATIENT + Gender enum
- [x] **Commit #4**: `feat(schema): add patient model`
- [x] `app/models/test.py` + `app/models/package.py` — TEST_CATEGORY, TEST, TEST_PARAMETER, TEST_PARAMETER_RANGE, TEST_PACKAGE, TEST_PACKAGE_ITEM + TestResultType enum
- [x] **Commit #5**: `feat(schema): add test catalog and package models`
- [x] `app/models/appointment.py` — APPOINTMENT_SLOT, APPOINTMENT + AppointmentStatus, BookingChannel enums
- [x] **Commit #6**: `feat(schema): add appointment models`
- [x] `app/models/order.py` — VISIT_ORDER, ORDER_TEST, ORDER_PACKAGE, SAMPLE + OrderStatus, OrderTestStatus, SampleStatus enums
- [x] **Commit #7**: `feat(schema): add visit order and sample models`
- [x] `app/models/report.py` — REPORT + ReportStatus enum
- [x] **Commit #8**: `feat(schema): add report model`
- [x] `app/models/billing.py` + `app/models/referral.py` — BILL, DISCOUNT_RULE, PAYMENT, REFERRAL, COMMISSION_ENTRY
- [x] **Commit #9**: `feat(schema): add billing, payment, and referral models`
- [x] `app/models/notification.py` — NOTIFICATION + final models/__init__.py with all imports
- [x] **Commit #10**: `feat(schema): add notification model and complete model imports`
- [x] Write `app/models/user.py` — USER, ROLE_PERMISSION, PERMISSION, USER_PERMISSION_OVERRIDE
- [x] Write `app/models/lab.py` — LAB
- [x] Write `app/models/patient.py` — PATIENT
- [x] Write `app/models/test.py` — TEST, TEST_CATEGORY, TEST_PARAMETER, TEST_PARAMETER_RANGE
- [x] Write `app/models/package.py` — TEST_PACKAGE, TEST_PACKAGE_ITEM
- [x] Write `app/models/appointment.py` — APPOINTMENT, APPOINTMENT_SLOT
- [x] Write `app/models/order.py` — VISIT_ORDER, ORDER_TEST, ORDER_PACKAGE, SAMPLE
- [x] Write `app/models/report.py` — REPORT
- [x] Write `app/models/billing.py` — BILL, DISCOUNT_RULE, PAYMENT
- [x] Write `app/models/referral.py` — REFERRAL, COMMISSION_ENTRY
- [x] Write `app/models/notification.py` — NOTIFICATION
- [x] Write `app/models/__init__.py` — import all models (all 25 models exported)

### Step 3: Alembic Setup
- [x] Initialize Alembic (`alembic.ini`, `alembic/script.py.mako`, `alembic/versions/`)
- [x] Configure `alembic/env.py` for async engine (asyncio.run + connection.run_sync)
- [x] Set `MetaData(naming_convention=...)` for deterministic constraint names
- [x] Wire `target_metadata` to import all models (`Base.metadata`)

### Step 4: Migrations
- [x] Generate initial migration (`alembic revision --autogenerate -m "initial_schema"`)
- [x] Manually review every line of generated migration
- [x] Correct any autogenerate blind spots (enums, server defaults, indexes, circular FKs)
- [x] Run `alembic upgrade head` on fresh PostgreSQL 16 — zero errors
- [x] Run `alembic downgrade base` — clean reversal (all 25 tables + 15 ENUMs dropped)
- [x] Run `alembic upgrade head` again — idempotency confirmed (zero schema drift)

### Step 5: Verification
- [x] `mypy app/models` — clean, zero errors across all 13 model modules
- [x] `ruff check .` — clean (0 errors)
- [x] `ruff format --check .` — clean (31 files checked)
- [x] Round-trip migration test documented (`upgrade head` -> `downgrade base` -> `upgrade head`)

### Step 6: Documentation
- [x] `SCHEMA.md` written (table-by-table: purpose, columns, relationships, decisions)
- [x] `README.md` fully updated — Windows/uv setup, schema overview, migration commands, Phase 1 status
- [x] `.env.example` fixed — pydantic-settings v2 JSON array format for list fields, inline comments removed
- [x] Local dev server verified — `uvicorn` running on `:8000`, `/health` returns `200 OK`

### Step 7: Git Commits
- [x] `chore(infra): init project scaffold, docker, environment config, and engineering docs` (Commit #1 - Pushed ✅)
- [x] `feat(schema): add SQLAlchemy base, mixins, and MetaData naming convention` (Commit #2)
- [x] `feat(schema): add lab and user/access-control models` (Commit #3)
- [x] `feat(schema): add patient model` (Commit #4)
- [x] `feat(schema): add test catalog and package models` (Commit #5)
- [x] `feat(schema): add appointment models` (Commit #6)
- [x] `feat(schema): add visit order and sample models` (Commit #7)
- [x] `feat(schema): add report model` (Commit #8)
- [x] `feat(schema): add billing, payment, and referral models` (Commit #9)
- [x] `feat(schema): add notification model and complete model imports` (Commit #10)
- [x] `chore(migrations): wire Alembic for async SQLAlchemy` (Commit #11)
- [x] `feat(migrations): add initial schema migration` (Commit #12)
- [x] `style(lint): fix ruff lint, formatting, and enum StrEnum inheritance` (Commit #13)
- [x] `docs(schema): add SCHEMA.md and update Phase 1 completion status` (Commit #14)

### Phase 1 Definition of Done
- [x] Entity list + ERD reviewed and approved before any code
- [x] All models fully type-annotated, `mypy` clean (strict mode, 0 errors)
- [x] Every table has timestamps, UUID PK, branch-scoping (or documented exception)
- [x] Every FK indexed; every enum is a named, tracked type
- [x] `alembic upgrade head` succeeds on fresh PostgreSQL 16
- [x] `alembic downgrade base` → `alembic upgrade head` round-trip proven
- [x] Autogenerated migrations manually read and corrected
- [x] Local dev server running (`uvicorn` on `:8000`, `/health` → `200 OK`)
- [x] `SCHEMA.md` written and matches actual schema
- [x] Assumptions, deferred items, risks documented

---

## Phase 2 — API Endpoints & Core Domain CRUDs ✅

> **Branch**: `phase2_api_endpoints`  
> **Status**: ✅ Complete — All 27 atomic commits, 97 passing unit/integration tests, 0 lint errors.

### Step 1: Core Foundation & Security Utilities
- [x] `app/core/response.py` — Standard generic response envelopes (`SuccessResponse[T]`, `PaginatedResponse[T]`, `PaginationMeta`, `ErrorResponse`, `ErrorDetail`)
- [x] `app/core/exceptions.py` — App-wide domain exception hierarchy with automatic FastAPI exception handlers
- [x] `app/core/pagination.py` — Standard `PaginationParams` dependency (page, limit with max 100 guard)
- [x] `app/core/security.py` — Argon2/bcrypt password hashing (`verify_password`, `get_password_hash`) and JWT encoding/decoding (`create_access_token`, `create_refresh_token`, `decode_token`)
- [x] `app/core/redis.py` — Refresh token session tracking + access token revocation blacklisting on logout
- [x] `app/core/auth_deps.py` — HTTPBearer authorization scheme with blacklisted token validation
- [x] **Commit #15**: `feat(core): add response envelope, exception classes, pagination, and DB dependency`
- [x] **Commit #16**: `feat(core): add JWT security utilities and password hashing`

### Step 2: Routing Skeleton, Auth & RBAC
- [x] `app/api/router.py` + `app/api/v1/*.py` — Central router aggregator mounting 8 domain routers, OpenAPI docs configured
- [x] `app/api/v1/auth.py` — Authentication endpoints (`POST /api/v1/auth/bootstrap`, `POST /api/v1/auth/login`, `POST /api/v1/auth/refresh`, `POST /api/v1/auth/logout`, `GET /api/v1/auth/me`)
- [x] **Bootstrap Security**: One-time initial Superadmin account creation with strict permanent `403 Forbidden` lockout on subsequent attempts
- [x] **Login Format**: Login using either `phone` or `email` + `password` (`username` field removed)
- [x] **Logout Revocation**: Stateful Redis blacklisting of active access token so post-logout requests are rejected (`401 Unauthorized`)
- [x] `app/core/dependencies.py` — RBAC permission resolution (`get_current_user`, `require_permission(PermissionCode.*)`), caching permissions in request context
- [x] **Commit #17**: `feat(api): wire all routers, mount API v1, and expose OpenAPI`
- [x] **Commit #18**: `feat(auth): implement login, token refresh, and /me endpoints`
- [x] **Commit #19**: `feat(rbac): implement permission resolution and protect all endpoints`

### Step 3: Domain CRUDs & Business Workflows
- [x] **Lab Management**: CRUD endpoints (`POST`, `GET /`, `GET /{id}`, `PATCH /{id}`, `DELETE /{id}`) with branch validation.
  - **Commit #20**: `feat(lab): implement lab CRUD schemas, repository, service, and endpoints`
- [x] **User Management**: Admin user creation, profile updates, password resets, role-permission overrides. Superadmin privilege escalation guards (only superadmins can assign superadmin role).
  - **Commit #21**: `feat(user): implement user management CRUD schemas, service, and endpoints`
- [x] **Patient Registration & Search**: Patient CRUD, search by mobile/name, concise readable sequential MRN `P{YY}-{seq:04d}` (e.g. `P26-0001`).
  - **Commit #22**: `feat(patient): implement patient CRUD and search schemas, service, and endpoints`
- [x] **Test Catalog & Parameters**: Test categories, lab tests, test parameters, and age/gender-specific reference ranges.
  - **Commit #23**: `feat(test-catalog): implement test categories, tests, parameters, and reference ranges`
- [x] **Health Packages**: Packages and package item composition CRUD with active status filtering.
  - **Commit #24**: `feat(package): implement health packages and package items schemas, service, and endpoints`
- [x] **Appointments & Slots**: Daily slot generation, appointment booking, channel attribution, and cancellation.
  - **Commit #25**: `feat(appointment): implement appointments, slot booking, and cancellation`
- [x] **Visit Orders & Samples**: Order creation with automatic price aggregation, sequential order number `ORD/{YY}/{6-digit-seq}`, auto sample collection record generation, order status lifecycle.
  - **Commit #26**: `feat(order): implement visit orders, order tests/packages, and order lifecycle`
- [x] **Commit #27**: `feat(auth): lock down bootstrap endpoint after first user, enforce superadmin privilege escalation guards, and remove seeder`

### Phase 2 Verification & Test Results
- [x] **97 Tests Passing** (100% pass rate across appointments, auth, catalog, foundation, labs, orders, packages, patients, rbac, routes, security, users, and end-to-end integration).
- [x] **Clean Linting**: `ruff check .` passes with 0 warnings or errors; `ruff format` applied across codebase.
- [x] **Strict Typing**: `mypy app/` passes with 0 errors across 66 source files.
- [x] **WSL Developer Tooling**: Added `scripts/setup.sh` and root `Makefile` (`make setup`, `make up`, `make migrate`, `make run`, `make test`, `make lint`) for seamless 1-step onboarding after clone.

---

## Phase 3 — Lab & Clinical Workflow ✅ (Complete)

> **Status**: All 4 Steps of Phase 3 are complete with 134 automated tests passing (100% pass rate). Ready for Phase 4 (Financial & Billing).

### Step 3.1: Sample Collection & Specimen Tracking Lifecycle ✅
- [x] `app/schemas/sample.py` — Pydantic schemas (`SampleCreate`, `SampleCollectRequest`, `SampleRejectRequest`, `SampleResponse`, `SampleDetailResponse`).
- [x] `app/repositories/sample_repository.py` — Year-aware sequential `sample_code` (`S/26/000001`), date-aware `barcode` (`SAM-YYYYMMDD-000001`), specimen lookups by ID, order, barcode, and filters.
- [x] `app/services/sample_service.py` — Specimen lifecycle engine:
  - Phlebotomist collection (`PENDING` → `COLLECTED`) with automatic order status cascade (`SAMPLE_COLLECTED`).
  - Specimen laboratory reception (`COLLECTED` → `PROCESSING`) with order and test status cascade (`PROCESSING`).
  - Specimen testing completion (`PROCESSING` → `COMPLETED`).
  - Specimen rejection with documented reason and timestamp logging.
  - Barcode scanner lookup returning patient demographics and order details.
- [x] `app/api/v1/samples.py` + `app/api/v1/orders.py` — REST endpoints (`GET /api/v1/samples`, `GET /api/v1/samples/{id}`, `GET /api/v1/samples/barcode/{barcode}`, `POST /api/v1/orders/{order_id}/samples`, `POST /api/v1/samples/{id}/collect`, `POST /api/v1/samples/{id}/receive`, `POST /api/v1/samples/{id}/complete`, `POST /api/v1/samples/{id}/reject`).
- [x] `app/core/permissions.py` — Added `SAMPLE_READ` and `SAMPLE_WRITE` capabilities mapped to Phlebotomists, Pathologists, Receptionists, Doctors, and Managers.
- [x] `tests/test_samples.py` — 12 integration tests passing (100% pass rate; 109 tests total in app suite).
- [x] **Linting & Typing**: `ruff` clean, `mypy app/` passes with 0 errors across 70 source files.

### Step 3.2: Clinical Result Entry & Reference Range Interpretation Engine ✅
- [x] `app/models/report.py` — Added `result_data: JSONB` column to persist structured clinical observations, reference intervals, and auto-flagging metadata.
- [x] `alembic/versions/20260921_1740_475b2397161a_add_result_data_to_report.py` — Database schema migration.
- [x] `app/schemas/result.py` — Worksheet and result entry models (`ParameterWorksheetItem`, `TestWorksheetItem`, `OrderWorksheetResponse`, `ParameterResultInput`, `OrderResultEntryRequest`, `EvaluatedParameterResult`, `OrderResultEntryResponse`).
- [x] `app/services/clinical_engine.py` — Pure domain interpretation engine:
  - Demographic reference range selector matching patient sex and age interval with specificity ranking (exact gender + age bounds > unisex).
  - Patient age calculator from DOB with fallback age.
  - Multi-type evaluator for numeric ranges (`N`, `L`, `H`), calculation factor scaling (`calc_factor`), and text-normal findings (`N` vs `ABNORMAL`).
- [x] `app/repositories/report_repository.py` — Report database operations (`get_by_id`, `get_latest_by_order_id`, `get_by_order_and_version`, `list`, `count`, `create`).
- [x] `app/repositories/order_repository.py` — Eager loaded `order_tests -> test -> parameters -> ranges`.
- [x] `app/services/result_service.py` — Laboratory worksheet generation tailored to demographics, incremental parameter result entry with auto-flagging, `Report` draft persistence in `result_data`, and automatic order status progression to `PROCESSING`.
- [x] `app/api/v1/results.py` + central router — Endpoints (`GET /api/v1/orders/{order_id}/results/worksheet`, `POST /api/v1/orders/{order_id}/results`).
- [x] `app/core/permissions.py` — Added `RESULT_READ` and `RESULT_WRITE` capabilities mapped across user roles.
- [x] `tests/test_results.py` — 10 unit and integration tests passing (119/119 total tests passing across entire suite).
- [x] **Linting & Typing**: `ruff` clean, `mypy app/` passes with 0 errors across 75 source files.

### Step 3.3: Pathologist Verification Sign-off & Versioned Amendments ✅
- [x] `app/schemas/report.py` — Pydantic schemas (`ReportSubmitRequest`, `ReportVerifyRequest`, `ReportRejectRequest`, `ReportAmendRequest`, `ReportReleaseRequest`, `ReportResponse`, `ReportDetailResponse`, `ReportVerifierSummary`).
- [x] `app/repositories/report_repository.py` — Added `get_all_versions_by_order_id` and eager loading for `order_tests -> test -> parameters -> ranges`.
- [x] `app/services/report_service.py` — Complete clinical lifecycle logic:
  - Submission for verification (`DRAFT` / `REJECTED` → `PENDING_VERIFICATION`).
  - Pathologist electronic sign-off (`PENDING_VERIFICATION` / `DRAFT` → `VERIFIED`) with verifier signature, clinical impressions, order progression to `COMPLETED`, and order tests to `VERIFIED`.
  - Pathologist rejection with documented rationale (`REJECTED`).
  - Immutable versioned amendments (`VERIFIED` / `RELEASED` → new `Report` record with `version = N+1`, preserving predecessor via `previous_report_id` for forensic audit).
  - Official release to patient (`RELEASED`).
- [x] `app/api/v1/reports.py` + central router — REST endpoints (`GET /api/v1/reports`, `GET /api/v1/reports/{id}`, `GET /api/v1/reports/order/{order_id}/versions`, `POST /api/v1/reports/{id}/submit`, `POST /api/v1/reports/{id}/verify`, `POST /api/v1/reports/{id}/reject`, `POST /api/v1/reports/{id}/amend`, `POST /api/v1/reports/{id}/release`).
- [x] `app/core/permissions.py` — Added `REPORT_READ`, `REPORT_WRITE`, and `REPORT_VERIFY` capabilities; strictly restricted `REPORT_VERIFY` to medical authorities with 403 enforcement for phlebotomists/receptionists.
- [x] `tests/test_reports.py` — 8 integration tests passing (127/127 total tests passing across entire suite).
- [x] **Linting & Typing**: `ruff` clean, `mypy app/` passes with 0 errors across 78 source files.

### Step 3.4: Dynamic PDF Report Rendering (WeasyPrint + Jinja2) ✅
- [x] `app/templates/reports/diagnostic_report.html` — Clean, medical-grade diagnostic report template with CSS paged media (`@page` A4 margins, page numbering, diagonal draft/rejected watermarks, amendment alerts, demographics table, structured analyte results with abnormal flags, digital certification signature block, and clinical disclaimer).
- [x] `app/services/pdf_service.py` — `PDFReportService` generating HTML and streaming binary PDF via `weasyprint.HTML(string=...).write_pdf()` on-the-fly without saving static files on disk.
- [x] `app/repositories/report_repository.py` — Eager loaded `VisitOrder.lab` and `VisitOrder.samples` in `get_by_id`.
- [x] `app/services/report_service.py` — Integrated `render_report_pdf` and `render_report_html` with branch isolation checks.
- [x] `app/api/v1/reports.py` — Endpoints:
  - `GET /api/v1/reports/{id}/pdf` — Streams binary PDF with `inline; filename="report_{order_number}_v{version}.pdf"`.
  - `GET /api/v1/reports/{id}/preview` — Returns rendered HTML for browser/iframe preview.
- [x] `tests/test_reports.py` — Added 7 PDF & preview integration tests (all 15 report tests pass; 134/134 total tests passing across entire suite).
- [x] **Linting & Typing**: `ruff` clean across 97 files, strict `mypy app/` passes with 0 errors across 79 source files.

---

## Phase 4 — Financial ✅ (Complete)

### Step 4.1: Discount Rule Management & Engine ✅
- [x] `app/core/permissions.py` — Added `DISCOUNT_READ`, `DISCOUNT_WRITE`, `BILL_READ`, `BILL_WRITE`, `PAYMENT_READ`, `PAYMENT_WRITE`, `REFERRAL_READ`, `REFERRAL_WRITE`, `COMMISSION_READ`, `COMMISSION_WRITE` codes and default role mappings.
- [x] `app/schemas/billing.py` — `DiscountRuleCreate`, `DiscountRuleUpdate`, `DiscountRuleResponse` with percentage (0.01-100%) and fixed amount (> 0) boundary validators.
- [x] `app/repositories/discount_repository.py` — CRUD operations, soft delete, branch scoping, search, and duplicate name checks.
- [x] `app/services/discount_service.py` — Branch validation, duplicate name enforcement (409 Conflict), and calculation engine (`calculate_discount`).
- [x] `app/api/v1/discounts.py` — REST endpoints (`POST`, `GET`, `GET /{id}`, `PATCH /{id}`, `DELETE /{id}`) with branch resolution and RBAC guards.
- [x] `app/api/router.py` — Mounted `discounts` router under `/api/v1`.
- [x] `tests/test_discounts.py` — 13 comprehensive integration and unit tests covering positive flows, validation boundaries, 409 duplicate rejection, soft-deletion, branch isolation, and calculation engine clamping.
- [x] **Verification**: 147/147 tests passing across entire suite, 0 ruff errors, strict mypy 0 issues across 83 source files.

### Step 4.2: Bill Creation (1:1 with Order) & Balance Ledger ✅
- [x] `app/schemas/billing.py` — `BillCreate`, `BillUpdate`, `BillVoidRequest`, `BillResponse`, `BillDetailResponse` with model validators for discount exclusivity and non-negative currency boundaries.
- [x] `app/repositories/bill_repository.py` — Year-aware sequential invoice number generator (`INV/{YY}/{6-digit-seq}` e.g. `INV/26/000001`), eager-loaded bill queries by ID and order ID, and filterable paginated list.
- [x] `app/services/billing_service.py` — Financial engine computing subtotal from immutable `price_at_order` across tests and packages, discount application (via rule or manual), total clamping (`>= 0.00`), initial status determination (`PAID` if total is 0.00, else `UNPAID`), 1:1 order-to-bill enforcement (409 Conflict), and voiding rules (preventing void when `paid_amount > 0`).
- [x] `app/api/v1/bills.py` — REST endpoints (`POST /bills`, `GET /bills`, `GET /bills/{id}`, `GET /bills/order/{order_id}`, `PATCH /bills/{id}`, `POST /bills/{id}/void`) guarded by RBAC permissions (`BILL_READ`, `BILL_WRITE`).
- [x] `app/api/router.py` — Mounted `bills` router under `/api/v1`.
- [x] `tests/test_billing.py` — 16 integration tests covering order pricing aggregation, sequential invoice numbering, percentage/fixed/manual discounts, subtotal clamping, zero-total auto-paid, 409 duplicate rejection, voiding audit trails, payment guardrails, cross-branch tenant isolation, and RBAC permissions.
- [x] **Verification**: 163/163 tests passing across entire suite (100% pass rate), 0 ruff errors, strict mypy clean across 86 source files.

### Step 4.3: Multi-Tender Payment Recording & Real-Time Settlement ✅
- [x] `app/schemas/billing.py` — Added `PaymentCreate` and `PaymentResponse` schemas with amount boundaries, tender modes (`PaymentMode`: cash, card, mobile_banking, transfer, cheque, other), and nested payment history on `BillDetailResponse`.
- [x] `app/repositories/payment_repository.py` — Query methods for payment persistence, by-bill chronological retrieval, and paginated lab-wide transactions with mode filters.
- [x] `app/services/payment_service.py` — Real-time invoice balance settlement:
  - Validates bill existence and branch ownership.
  - Guards against payment on voided invoices (422) and already settled invoices (422).
  - Enforces overpayment guards (`amount <= due_amount`).
  - Real-time balance ledger arithmetic: updates `bill.paid_amount`, recalculates `bill.due_amount`, and updates status (`PARTIAL` or `PAID`).
  - Triggers automated referral commission accrual for orders referred by registered partners.
- [x] `app/api/v1/bills.py` & `app/api/v1/payments.py` — REST endpoints:
  - `POST /api/v1/bills/{bill_id}/payments` (`PAYMENT_WRITE`) — Record payment receipt against invoice.
  - `GET /api/v1/bills/{bill_id}/payments` (`PAYMENT_READ`) — List payment transactions for an invoice.
  - `GET /api/v1/payments` (`PAYMENT_READ`) — Paginated payment transactions with tender mode filter.
  - `GET /api/v1/payments/{id}` (`PAYMENT_READ`) — Get single payment receipt.
- [x] `app/api/router.py` — Mounted `payments` router under `/api/v1`.
- [x] `tests/test_payments.py` — 11 integration tests covering partial payments, exact full settlements, split tenders (cash + card), overpayment rejection, payments on paid/void bills rejection, void prevention on paid bills, list pagination/filters, cross-branch isolation, and RBAC enforcement.

### Step 4.4: Referral Partner Management & Commission Ledger ✅
- [x] `app/schemas/referral.py` — `ReferralCreate`, `ReferralUpdate`, `ReferralResponse`, `CommissionEntryResponse`, and `CommissionSettleRequest` schemas with rate boundary validators (0.00% to 100.00%).
- [x] `app/repositories/referral_repository.py` — CRUD operations, soft-delete handling (`deleted_at.is_(None)`), branch-unique code validation, and partner search.
- [x] `app/repositories/commission_repository.py` — Immutable ledger persistence, order+partner idempotency queries, un-settled partner aggregations, and paginated branch ledger listings.
- [x] `app/services/referral_service.py` — Full partner management and financial ledger accounting:
  - Branch-unique code conflict checks (409 Conflict).
  - Idempotent commission auto-accrual: calculates commission base from order total and partner rate (`order_amount * commission_rate / 100`).
  - Single-entry payout settlement (`/commissions/{id}/settle`).
  - Bulk partner payout settlement (`/referrals/{id}/settle-commissions`).
- [x] `app/api/v1/referrals.py` & `app/api/v1/commissions.py` — REST endpoints:
  - `POST /api/v1/referrals` (`REFERRAL_WRITE`), `GET /api/v1/referrals` (`REFERRAL_READ`), `GET /api/v1/referrals/{id}`, `PATCH /api/v1/referrals/{id}`, `DELETE /api/v1/referrals/{id}`.
  - `GET /api/v1/referrals/{id}/commissions` (`COMMISSION_READ`), `POST /api/v1/referrals/{id}/settle-commissions` (`COMMISSION_WRITE`).
  - `GET /api/v1/commissions` (`COMMISSION_READ`), `GET /api/v1/commissions/{id}`, `POST /api/v1/commissions/{id}/settle` (`COMMISSION_WRITE`).
- [x] `app/api/router.py` — Mounted `referrals` and `commissions` routers under `/api/v1`.
- [x] `tests/test_referrals.py` — 8 integration tests covering partner CRUD, duplicate code rejection in same branch, multi-branch code isolation, soft-deletion, commission calculation, idempotency, single settlement, bulk settlement, and RBAC permissions.
- [x] **Verification**: 182/182 tests passing across entire suite (100% pass rate), 0 ruff errors, strict mypy 0 issues across 95 source files.

---

## Phase 5 — Notifications + Client Link Pages ✅ (Complete)

> **Status**: ✅ Complete — All Phase 5 services, templates, client endpoints, and staff notification management implemented with 203 automated tests passing (100% pass rate). Ready for Phase 6 (Dashboard & Analytics).

### Step 5.1: Client Link Token Generation & Redis Store ✅
- [x] `app/config.py` — Configured `CLIENT_APP_BASE_URL` (`http://localhost:5173`) and `CLIENT_TOKEN_TTL_HOURS` (`72`).
- [x] `app/core/redis.py` — Added `store_client_token`, `get_client_token_payload`, and `revoke_client_token` using Redis prefix `client_link:{token}`.
- [x] `app/services/client_link_service.py` — Cryptographic URL-safe token generator (`secrets.token_urlsafe(32)`), token-to-payload metadata encapsulation (`report` vs `appointment`), and verification engine with typed exceptions (`404 Not Found` on expired/invalid token, `422 Unprocessable Entity` on resource type mismatch).

### Step 5.2: Public Client Link REST Endpoints ✅
- [x] `app/schemas/notification.py` — Pydantic response schemas (`ClientReportDetailResponse`, `ClientAppointmentDetailResponse`, `ClientPatientSummary`, `ClientLabSummary`).
- [x] `app/api/v1/client.py` — Public unauthenticated REST endpoints gated strictly by time-limited (72h) Redis tokens:
  - `GET /api/v1/client/reports/{token}`: Patient view of verified report observations, reference ranges, abnormal flags, and lab branding.
  - `GET /api/v1/client/reports/{token}/pdf`: Direct streaming of official WeasyPrint PDF report (`application/pdf`) with `Content-Disposition: inline`.
  - `GET /api/v1/client/appointments/{token}`: Patient view of confirmed appointment schedule, slot time, and branch address.
  - Guards against unreleased reports (`403 Forbidden`) and token type mismatches (`422`).
- [x] `app/api/router.py` — Mounted `client_router` under `/api/v1`.
- [x] `tests/test_client_links.py` — 11 automated unit and integration tests covering positive report/PDF/appointment retrieval, unreleased report rejection, token revocation, expired token 404, and resource type mismatch 422.

### Step 5.3: Email Templates, EmailService & Notification Engine ✅
- [x] `app/templates/emails/report_ready.html` — Responsive Jinja2 HTML email with lab branding, patient greeting, order number, verification timestamp, 72h security disclaimer, and CTA button.
- [x] `app/templates/emails/appointment_confirmed.html` — Responsive Jinja2 HTML email with slot timing, branch address, fasting/preparation instructions, and CTA button.
- [x] `app/services/email_service.py` — Async email dispatch using `fastapi-mail` with Jinja2 rendering, placeholder credential detection, and resilient error catching.
- [x] `app/repositories/notification_repository.py` — Full CRUD and filtering on PostgreSQL `notification` table with branch scoping via patient join.
- [x] `app/services/notification_service.py` — Central communication coordinator:
  - `send_report_released_notification`: Generates client token, creates `Notification` record in DB, and dispatches HTML email. Gracefully handles patients without email (`status=FAILED` without throwing exception).
  - `send_appointment_confirmed_notification`: Generates client token and dispatches appointment confirmation.
  - `send_custom_notification`: Staff-initiated manual notification dispatch.
  - `list_notifications` & `get_notification`: Branch-scoped audit logs for staff review.

### Step 5.4: Workflow Hooks & Staff Notification APIs ✅
- [x] `app/services/report_service.py` — Hooked `release_report` to automatically trigger `NotificationService.send_report_released_notification` on transition to `RELEASED`.
- [x] `app/services/appointment_service.py` — Hooked `confirm_appointment` to automatically trigger `NotificationService.send_appointment_confirmed_notification`.
- [x] `app/core/permissions.py` — Added `NOTIFICATION_READ` and `NOTIFICATION_WRITE` permissions mapped across Admin, Manager, and Receptionist roles.
- [x] `app/api/v1/notifications.py` — Staff endpoints (`GET /api/v1/notifications`, `GET /api/v1/notifications/{id}`, `POST /api/v1/notifications/send`) with branch scoping and RBAC guards.
- [x] `app/api/router.py` — Mounted `notifications_router` under `/api/v1`.
- [x] `tests/test_notifications.py` — 10 automated unit and integration tests covering template rendering, missing email fallback, SMTP error handling, auto-triggers on release and confirmation, branch scoping, manual send, and 403 permission enforcement.

### Phase 5 Verification & Test Results
- [x] **203 Tests Passing** (100% pass rate across entire test suite: 182 existing + 21 new Phase 5 tests).
- [x] **Clean Linting**: `ruff check .` and `ruff format --check .` clean across 132 files (0 errors).
- [x] **Strict Typing**: `mypy app/` passes with 0 errors across 102 source files.

---

## Phase 6 — Dashboard & Analytics ✅ (Completed 2026-09-22)

### Step 6.1: RBAC Permissions & Redis Cache Infrastructure ✅
- [x] `app/core/permissions.py` — Added `DASHBOARD_READ = "dashboard:read"` to `PermissionCode`, mapped across Superadmin, Admin, Manager, Doctor, Pathologist, Radiologist, Receptionist, and Accountant roles.
- [x] `app/core/redis.py` — Implemented `store_dashboard_cache` and `get_dashboard_cache` with 300s (5-minute) TTL and structured JSON serialization.

### Step 6.2: Schemas & Metrics Architecture ✅
- [x] `app/schemas/dashboard.py` — Defined Pydantic models:
  - `OperationalStats`: total patients, new patients in period, total orders, orders by status funnel, pending samples backlog, pending verifications backlog, today's appointments, and appointment status breakdown.
  - `FinancialStats`: total invoiced (excluding VOID bills), total collected, total outstanding dues, discounts given, revenue by payment tender mode (`cash`, `card`, `transfer`, etc.), accrued referral commissions, and unsettled commissions.
  - `DailyTimeSeriesPoint`: contiguous daily points for date, order count, revenue, and patient count for frontend chart rendering.
  - `TopItemMetric` & `TopReferralMetric`: top tests, packages, and referring doctors ranked by volume and revenue.
  - `TopPerformersResponse` & `DashboardOverviewResponse`: consolidated executive payload.

### Step 6.3: SQL Aggregate Queries & Multi-Branch Scoping ✅
- [x] `app/repositories/dashboard_repository.py` — Implemented performant server-side SQL aggregations:
  - `get_operational_stats`: counts patients, orders by status, sample backlog, draft/verification report queues, appointments.
  - `get_financial_stats`: computes financial aggregates excluding VOID bills, joins payment receipts for tender breakdown, calculates commissions.
  - `get_daily_trends`: builds unbroken contiguous date ranges with day-by-day order, revenue, and patient metrics.
  - `get_top_tests`, `get_top_packages`, `get_top_referrers`: top 5 rankings by frequency and monetary contribution.

### Step 6.4: Service Layer & Cache Invalidation ✅
- [x] `app/services/dashboard_service.py` — Orchestrates:
  - Date range resolution: defaults to trailing 30 days if omitted; validates `start_date <= end_date` (raises 422 if invalid).
  - Multi-branch tenant isolation: Superadmin can inspect org-wide or filter by branch; branch staff are strictly scoped to `current_user.lab_id` (cross-branch queries raise 403 Forbidden).
  - 5-minute Redis caching: cache key `dashboard:{lab_id|all}:{start}:{end}:{section}`; bypasses cache and recomputes on demand when `refresh=true`.

### Step 6.5: Endpoints & Routing ✅
- [x] `app/api/v1/dashboard.py` — REST API endpoints:
  - `GET /api/v1/dashboard/overview` — Consolidated executive view.
  - `GET /api/v1/dashboard/operational` — Operational volumes and status funnels.
  - `GET /api/v1/dashboard/financial` — Revenue, tender breakdown, and commissions.
  - `GET /api/v1/dashboard/trends` — Daily time-series trends for charts.
  - `GET /api/v1/dashboard/top-performers` — Top tests, packages, and referrers.
- [x] `app/api/router.py` — Mounted `dashboard_router` under `/api/v1`.

### Step 6.6: Automated Integration Tests ✅
- [x] `tests/test_dashboard.py` — 9 comprehensive tests:
  - `test_dashboard_unauthorized`: 401 for unauthenticated requests.
  - `test_dashboard_date_validation`: 422 for `start_date > end_date`.
  - `test_dashboard_empty_state`: 200 with zero counts and empty lists for empty DB.
  - `test_dashboard_operational_stats_accuracy`: counts patients, orders by status, pending samples, pending verification backlogs, appointments.
  - `test_dashboard_financial_stats_excludes_void_bills`: excludes VOID bills, aggregates payments by tender mode, tracks commissions.
  - `test_dashboard_top_performers_ranking`: ranks top tests, top packages, and top referrers by volume and value.
  - `test_dashboard_branch_isolation`: 403 when non-superadmin queries another branch.
  - `test_dashboard_superadmin_consolidation`: org-wide vs branch-specific queries.
  - `test_dashboard_caching_and_refresh_invalidation`: verifies 5m Redis caching and cache busting via `refresh=true`.

### Phase 6 Verification & Test Results
- [x] **212 Tests Passing** (100% pass rate across entire test suite: 203 existing + 9 new Phase 6 tests).
- [x] **Clean Linting**: `ruff check .` and `ruff format --check .` clean across 137 files (0 errors).
- [x] **Strict Typing**: `mypy app/` passes with 0 errors across 106 source files.

---

## Phase 7 — Testing, Hardening & DevOps ✅ (Completed 2026-09-23)

### Step 7.1: Comprehensive End-to-End Diagnostic Lifecycle Integration Test ✅
- [x] `tests/test_e2e_journey.py` — Complete 14-step integration test validating the entire diagnostic journey:
  1. Health and readiness verification (`/health` and `/health/ready`).
  2. Patient registration (NPR phone format validation, age computation).
  3. Appointment booking (with tokenized confirmation notification enqueued).
  4. Work order creation from appointment (linking CBC test + Executive Health Package).
  5. Sample barcode generation and accessioning.
  6. Phlebotomy specimen collection (status advancement to `COLLECTED`).
  7. Laboratory accessioning and sample receipt (`RECEIVED_IN_LAB`).
  8. Multi-parameter test result recording (CBC Hemoglobin, WBC, Platelets, Fasting Blood Sugar with automated abnormal flagging).
  9. Pathologist review, electronic sign-off, and WeasyPrint PDF compilation (with embedded QR code and verification signatures).
  10. Diagnostic report release and patient dispatch (`RELEASED`).
  11. Public client portal link resolution (72-hour unauthenticated tokenized access for report data and PDF download).
  12. Split-tender billing settlement (NPR 1,500 Cash + NPR 1,000 Card completing total invoice balance).
  13. Referral partner commission accounting (10% commission accrued and marked settled).
  14. Real-time executive dashboard aggregation reflecting all operational and financial metrics without cache lag.

### Step 7.2: Production Health & Readiness Probes ✅
- [x] `app/main.py` — Implemented `/health/ready` probe verifying:
  - Live PostgreSQL connectivity with `SELECT 1`.
  - Live Redis connectivity with `.ping()`.
  - HTTP 200 OK with `{"database": "healthy", "redis": "healthy"}` when all operational; HTTP 503 Service Unavailable with diagnostic status if either subsystem is degraded.

### Step 7.3: Database Migration Drift & Integrity Audit ✅
- [x] Ran `alembic check` against PostgreSQL 16:
  - Confirmed: `"No new upgrade operations detected"`.
  - 100% synchronization between SQLAlchemy ORM models and Alembic revision history.

### Step 7.4: CI/CD Pipeline (GitHub Actions) ✅
- [x] `.github/workflows/ci.yml` — Automated dual-job CI pipeline:
  - `lint-and-typecheck`: Ubuntu 24.04, Python 3.12, system dependencies (`libpango`, `libcairo`, `libffi`), `ruff format --check .`, `ruff check .`, strict `mypy app/`.
  - `test-and-migration`: Containerized PostgreSQL 16 and Redis 7 health-checked service containers, automated `alembic upgrade head`, `alembic check` drift verification, and full `pytest` execution.

### Step 7.5: Production Docker Orchestration & Reverse Proxy ✅
- [x] `Dockerfile` — Lean multi-stage build (`base` -> `dependencies` -> `development` -> `production`) with non-root security execution (`appuser:appgroup`), native WeasyPrint rendering libraries, and Gunicorn / Uvicorn worker process management.
- [x] `docker-compose.prod.yml` — Production container stack:
  - `postgres` (PostgreSQL 16 Alpine, healthchecks, persistent volumes).
  - `redis` (Redis 7 Alpine, password protected, persistent volumes).
  - `web` (FastAPI production target, healthcheck via `/health/ready`).
  - `nginx` (Nginx 1.25 Alpine reverse proxy).
- [x] `docker/nginx/nginx.conf` — Reverse proxy configuration with client max body size (25MB), gzip compression, upstream keepalive, and security headers (`X-Frame-Options`, `X-Content-Type-Options`, `X-XSS-Protection`, `Referrer-Policy`).

### Phase 7 Verification & Test Results
- [x] **213 Tests Passing** (100% pass rate across entire backend test suite: 212 existing + 1 comprehensive E2E lifecycle test).
- [x] **Zero Migration Drift**: `alembic check` clean.
- [x] **Clean Linting**: `ruff check .` and `ruff format --check .` clean across 138 files (0 errors).
- [x] **Strict Typing**: `mypy app/` passes with 0 errors across 106 source files.
- [x] **Backend Status**: All 7 planned backend phases are 100% complete and hardened. Ready for Frontend SPA integration.

---

## Decisions Log

| Date | Decision | Reason |
|---|---|---|
| 2026-09-20 | React + Vite (not Next.js) | Internal SPA, no SEO needed, simpler deployment |
| 2026-09-20 | FastAPI (not NestJS) | Alembic alignment, Python PDF/data ecosystem, less boilerplate |
| 2026-09-20 | Single-tenant | One lab organization, `lab_id` = branch scoping only |
| 2026-09-20 | No client portal | Clients get token link for report + appointment view only |
| 2026-09-20 | Client identity = mobile number | As per product requirement |
| 2026-09-20 | Monitoring: Structlog + Loki + Prometheus + Grafana | All free/OSS, no vendor lock-in |
| 2026-09-26 | Micro-organized Analyte vs Test Panel Pricing | Support standalone tests (fixed panel price) and composite panels with individual analyte pricing (e.g. CBC vs RBC/WBC) |
| 2026-09-26 | Soft-Delete Revival for Categories | Prevent unique constraint collision on `uq_test_category_lab_id_name` when recreating previously archived categories |

---

## Risks & Open Items

| Risk | Severity | Mitigation |
|---|---|---|
| Alembic async env.py misconfiguration | HIGH | Explicit setup per `Solution_Architect.md` §6 |
| Autogenerate missing enum/index changes | MEDIUM | Manual migration review required |
| SMS gateway (Sparrow/Aakash) API availability | LOW | Phase 5 concern, abstract behind interface |
| PDF generation (WeasyPrint) system dependencies | MEDIUM | Docker image must include required system libs |

### Patient directory audit trail (2026-10-03)
- Patient create, changed profile fields, portal credential reset, and deactivation now record CLINICAL audit events with actor and patient target IDs. Event details contain only changed field names or boolean action flags; patient contact data and generated portal PINs are excluded.
- Write routes inject the existing PATIENT_WRITE permission dependency's authenticated user into the service. Patient route tests pass: 11 passed in tests/test_patients.py.

### Reception dashboard appointment date filter (2026-10-03)
- `GET /api/v1/appointments?date=YYYY-MM-DD` now scopes scheduled bookings by slot start in `Asia/Kathmandu`, and includes slotless walk-ins created in that same clinic-local day. Optional `active_only` and existing lab/patient/status filters continue to compose with the date condition.
- Query filtering applies to both result and count queries; existing response pagination and permission checks are unchanged. Existing frontend dashboard and registry callers that send `date` now receive the intended bounded result.
- Added integration coverage for current-day and previous-day walk-in inclusion and booked-slot date filtering. Focused appointment lifecycle/date-filter integration tests pass (2 tests).

### Reception patient registry API (2026-10-03)
- Added `activity_date` to patient list filtering. The clinic-local date query uses correlated `EXISTS` for same-day non-deleted visit orders and active appointments, plus same-day registrations; count and page queries share the filter. Fiscal-year bounds apply to registration matches without hiding returning patients active today.
- Bound patient list/detail/create/update/deactivate/portal-reset routes to the authenticated lab. Cross-branch patient reads return not found; attempts to query another lab are forbidden. Added integration coverage for activity day and receptionist branch isolation.
- Python syntax compilation and `git diff --check` pass. Integration pytest stalled during test-database startup before collecting output; lint/type checks remain deferred.

### Reception appointments branch scope (2026-10-03)
- Authenticated appointment and appointment-slot endpoints now resolve the effective lab branch from the current user. Staff cannot select another lab; cross-branch appointment/slot IDs return not found across read, update, delete, check-in, cancel, confirm, complete, and reschedule paths.
- Appointment creation and quick booking bind patient and slot validation to the same resolved branch. Existing appointment permissions remain enforced, and public booking endpoints retain their intended branch selection behavior.
- Added receptionist regression coverage for cross-branch list, check-in, and slot-detail access. Modified backend modules and tests pass Python syntax compilation; `git diff --check` passes. The targeted pytest invocation did not return before its 60-second database-startup timeout. Lint/type checks remain deferred.

### Reception visit-order branch scope (2026-10-03)
- Authenticated order list/create/detail/update/cancel/attach-tests/attach-packages/create-sample routes now resolve the current user's lab branch. Cross-branch order IDs are not found, and an explicit branch mismatch is forbidden.
- Order creation validates that the patient, appointment, tests, packages, and referral partner all belong to that branch. Existing `ORDER_READ`, `ORDER_WRITE`, and `SAMPLE_WRITE` permission dependencies remain enforced.
- Added receptionist integration coverage for cross-branch list/detail/cancel. Modified backend modules and test compile with Python. The targeted integration run is bounded at 45 seconds while the test database starts; status will be recorded when it returns. Lint/type checks remain deferred.


## Package integration and full verification pass — 2026-10-04

- Activated TanStack Router across the frontend while preserving existing role guards, lazy page dispatch, URL query state, public links, and browser navigation. Router regression tests cover route registration, deep links, query/hash preservation, and back navigation.
- Removed unused frontend date/Radix packages and eslint-plugin-import; synchronized npm manifests. Node minimum is 20.19. Backend bcrypt is direct, HTTPX is dev-only, unused passlib/factory-boy are removed, and workers/TOTP/XLSX/S3 remain optional extras. `uv.lock` is synchronized.
- Query/Zustand/Zod continue their established ownership: server cache, client session/UI state, and boundary validation. Shared report detail/version schemas now validate API data. Fixed hook dependencies, accessible form labels, and specimen request cancellation without adding unneeded React 19 primitives.
- Backend fixes: SMTP error guidance fits Ruff limits; clinical credential updates cannot clear mandatory registration; package-only pathology orders accept results; cancelled panels cannot accept result values; invoice reads refresh eager-loaded specimens; dashboard timestamps and daily trends use Asia/Kathmandu, and today's scheduled arrivals use slot date with a walk-in fallback.
- Test repairs preserve business/security assertions: staff fixtures have the permissions appropriate to branch scoping; public tracker uses `/api/v1/client/orders/track`; barcode assertions match issued identifiers; direct specimen receipt checks collection metadata; invalid report-token types return the public 404 contract. Dashboard cache is isolated per test while cache-hit/refresh behavior remains exercised. Test bcrypt uses four rounds; production remains twelve. jsdom API tests use the native Fetch AbortSignal realm.
- User authorized tests and lint on both repositories. Final results are recorded below when the complete runs finish. Type checks remain deferred.


### Permission discovery and test runner follow-up — 2026-10-04

- Permission discovery now uses PostgreSQL batch inserts with conflict handling and one definition read. Existing disabled permission flags and administrator role choices remain intact; concurrent discovery/startup inserts are safe. A regression checks retained values, stable row counts, and the bounded SELECT count.
- The frontend runner uses at most two jsdom workers and allows ten seconds for multi-interaction tests. Imaging tests wait explicitly for the report/order/catalog/view query chain; all functional assertions are retained.
- Browser smoke check: live booking packages and hours loaded, patient sign-in opened, and the legacy `/client?tab=appointments#upcoming` link redirected to `/patient` with query and hash intact; no console errors were observed.


## Security, performance and reporting implementation — 2026-10-04

- Implemented financial/operational Management Reporting with custom dates, Nepali months/FYs, scoped live aggregates, CSV/PDF and export audit trails; `/owner/reporting` and permission-delegated `/reporting`. Added Accountant workspace, `reporting:read`, `reporting:export` and separate `audit:read` governance.
- Hardened phone/identifier tracking, global concurrent-safe specimen allocation, manual label-code fallback, custody transitions and audit branch access. Removed automatic demonstration audit inserts from reads.
- Added privacy/timing/security/CSRF policy, production TrustedHost and settings guards; shared Redis rate counters. Production frontend uses cookie sessions rather than durable JS token storage; patient logout revokes JWTs. JWTs use PyJWT/cryptography with unique IDs and required claims.
- Moved bcrypt/image/PDF work out of the request loop; changed 32 analytics filters to index-friendly timestamp bounds and made pool capacity configurable.
- Updated compatible vulnerable frontend tooling/DOMPurify; backend and deployed frontend scans show no known vulnerabilities. Tailwind's five build-only advisories remain documented for a separately reviewed major migration.
- Updated frozen-lock Docker builds, secret exclusions, production setting names, frontend/API connectivity and persistent report/signature storage.
- Styled shared dropdown popup/trigger and reused Nepali calendars. Follow-up requested by user: center selected dropdown value vertically with flex alignment and zero block padding; no tests run for that styling-only adjustment.
- Evidence so far: FE complete 134 files / 425 tests passed with Vitest 4.1.11; additional cookie-session suite 4 passed. Latest production bundle and FE lint passed. BE focused security/reporting/tracking/auth/analytics run: 87 passed; complete backend validation and latest audit/concurrency regressions are being finalized. Type checks remain deferred by user instruction.
- Full review and release gates: `docs/Security_Performance_Handover_Review.md`. Local serial timings are not production capacity proof; staging role journeys, release type checks, container/TLS checks, representative load, hardware and backup restore/sign-off remain required.

## 2026-10-12 — Native Python/PostgreSQL hosting

- Pulled current main before proceeding and created the local feat/python-hosting branch in both repositories.
- Removed Docker deployment files, Redis state/limiter dependencies and unused Celery extra. PostgreSQL is the required external service.
- Added shared PostgreSQL expiring state for sessions, revocations, OTPs, client links, cache and rate limits, with atomic counters/OTP consumption and expiry cleanup.
- Replaced PDF rendering with ReportLab while retaining existing clinical context builders and management accounting calculations; bundled fonts, signatures, QR links, watermarks and pagination are covered.
- Added native ASGI startup configuration, pinned production dependencies, env examples, preserved local env settings, and setup/transition/backups instructions.
- Corrected pre-existing build/type errors and production cookie refresh validation; existing clinical and financial services remain in place.
- Final regression, migration, production installation/restart and PDF visual checks are recorded below when complete. Historical phase entries describe the implementation at their original dates.
### Validation results

- Complete backend suite: 347 tests passed; includes full clinical/billing lifecycle, tenant permissions, patient portal, report amendments, auth/reset/session revocation, and new PostgreSQL state/rate-limit/PDF coverage.
- Ruff lint and formatting passed for 213 Python files; mypy passed across 142 application source files.
- Alembic upgrade and schema consistency checks passed; comment migration downgrade/re-upgrade was also checked on the isolated database.
- Synthetic released, imaging/amended, multipage draft and management PDFs were generated and visually reviewed. Bundled fonts, signatures/QRs, repeated headers, readable watermarks and pagination were checked.
- Local interrupted venv/pip setup was repaired. Restored private app secrets after the example env was copied over local settings. Neither local PostgreSQL service contained a Munal database; created dedicated native PostgreSQL development/test databases and migrated the development database to head.
- Actual local env changes are private and untracked. No existing project databases or patient/financial records were modified by the automated tests.
- Fresh production wheel smoke passed: font/template packaging and PDF generation, supervised ASGI readiness, bootstrap, cookie login, refresh after process restart, and logout revocation. Redis and WeasyPrint were absent from the production environment.
- Frontend validation: 438 tests across 136 files, lint, TypeScript and production build passed. Rebuilt the local dist using its preserved API env rather than the synthetic production-check URL.

### Local startup reconfirmation

- Real local PostgreSQL connection succeeded at 127.0.0.1:5432; migration is 20261012_column_comments. Local database had zero staff/patient records at reconfirmation.
- Live API liveness/readiness/docs and frontend page/module/proxy requests returned HTTP 200. Both direct and proxied readiness returned database connected and session_store ready; localhost:5173 CORS and anonymous authentication rejection were checked.
- .env.example parses correctly; optional SMTP/SMS are disabled until configured. Redis, Celery and WeasyPrint are absent from the actual local venv and ReportLab is present.
- Updated both READMEs for the shared port 8765, preserved existing env files, matching PostgreSQL credentials, initial admin bootstrap, and incomplete WSL environments. Setup script reuses environments and installs from the frozen lock.
