hospital-management/tools/markdown/tenhal_detailed_refactor_plan.md
Marwan Alwali 263292f6be update
2025-11-04 00:50:06 +03:00

434 lines
38 KiB
Markdown

# Tenhal — Detailed Step-by-Step Refactor Plan (Auto-generated)
**Source artifacts:** `/mnt/data/model_map.json`, `/mnt/data/overlaps.json`
This document details, **model-by-model and field-by-field**, what to add, what to remove, and how to route behavior via canonical services.
**Apps scanned:** 20
**Total models found:** 153
**Name collisions:** 4
**Inventory leak flags:** 6
**Canonical conflicts:** 1
## Execution Order (Top-Level)
1. Create canonical `documentation/` app (models + services).
2. Add/extend `inventory/services.py` (`record_stock_movement`).
3. Resolve **name collisions** (keep canonical, remove others).
4. Fix **inventory leaks** (remove stock-like fields in clinical apps; add FKs to inventory; route via services).
5. Centralize **notes/reports** → replace with `documentation.Document` + `DocumentLink`.
6. Resolve **canonical conflicts** (move models to expected owners).
7. Drop DB, run migrations, and reseed; then smoke-test flows.
## A) Name Collisions — Keep One Canonical Owner
| Model | App | Action | File | Fields | FKs | M2Ms | Canonical Owner |
|---|---|---|---|---|---|---|---|
| `IntegrationLog` | `core` | REMOVE (replace with FK/API to canonical) | core/models.py | `tenant`:ForeignKey, `log_id`:UUIDField, `integration_type`:CharField, `direction`:CharField, `external_system`:CharField, `endpoint`:CharField, `message_type`:CharField, `message_id`:CharField, `correlation_id`:UUIDField, `request_data`:TextField, `response_data`:TextField, `status`:CharField, `error_code`:CharField, `error_message`:TextField, `processing_time_ms`:PositiveIntegerField, `timestamp`:DateTimeField, `created_at`:DateTimeField | Tenant | _none_ | `integration` |
| `IntegrationLog` | `integration` | KEEP (canonical owner) | integration/models.py | `log_id`:UUIDField, `external_system`:ForeignKey, `endpoint`:ForeignKey, `execution`:ForeignKey, `level`:CharField, `category`:CharField, `message`:TextField, `details`:JSONField, `correlation_id`:CharField, `user`:ForeignKey, `timestamp`:DateTimeField, `metadata`:JSONField | ExternalSystem, IntegrationEndpoint, IntegrationExecution, settings.AUTH_USER_MODEL | _none_ | `integration` |
| `InventoryLocation` | `blood_bank` | REMOVE (replace with FK/API to canonical) | blood_bank/models.py | `name`:CharField, `location_type`:CharField, `temperature_range`:CharField, `temperature`:FloatField, `capacity`:PositiveIntegerField, `current_stock`:PositiveIntegerField, `is_active`:BooleanField, `notes`:TextField | _none_ | _none_ | `inventory` |
| `InventoryLocation` | `inventory` | KEEP (canonical owner) | inventory/models.py | `tenant`:ForeignKey, `location_id`:UUIDField, `location_code`:CharField, `name`:CharField, `description`:TextField, `location_type`:CharField, `building`:CharField, `floor`:CharField, `room`:CharField, `zone`:CharField, `aisle`:CharField, `shelf`:CharField, `bin`:CharField, `capacity_cubic_feet`:DecimalField, `max_weight_pounds`:DecimalField, `temperature_controlled`:BooleanField, `temperature_min`:DecimalField, `temperature_max`:DecimalField, `humidity_controlled`:BooleanField, `humidity_min`:PositiveIntegerField, `humidity_max`:PositiveIntegerField, `secure_location`:BooleanField, `access_control`:CharField, `is_active`:BooleanField, `parent_location`:ForeignKey, `location_manager`:ForeignKey, `notes`:TextField, `created_at`:DateTimeField, `updated_at`:DateTimeField, `created_by`:ForeignKey | core.Tenant, self, settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL | _none_ | `inventory` |
| `QualityControl` | `blood_bank` | REMOVE (replace with FK/API to canonical) | blood_bank/models.py | `test_type`:CharField, `test_date`:DateTimeField, `equipment_tested`:CharField, `parameters_tested`:TextField, `expected_results`:TextField, `actual_results`:TextField, `status`:CharField, `performed_by`:ForeignKey, `reviewed_by`:ForeignKey, `review_date`:DateTimeField, `review_notes`:TextField, `corrective_action`:TextField, `next_test_date`:DateTimeField, `capa_initiated`:BooleanField, `capa_number`:CharField, `capa_priority`:CharField, `capa_initiated_by`:ForeignKey, `capa_date`:DateTimeField, `capa_assessment`:TextField, `capa_status`:CharField | settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL | _none_ | `laboratory` |
| `QualityControl` | `laboratory` | KEEP (canonical owner) | laboratory/models.py | `tenant`:ForeignKey, `test`:ForeignKey, `result`:ForeignKey, `qc_id`:UUIDField, `control_material`:CharField, `control_lot`:CharField, `control_level`:CharField, `target_value`:DecimalField, `acceptable_range_low`:DecimalField, `acceptable_range_high`:DecimalField, `run_datetime`:DateTimeField, `observed_value`:DecimalField, `status`:CharField, `performed_by`:ForeignKey, `reviewed_by`:ForeignKey, `analyzer`:CharField, `comments`:TextField, `corrective_action`:TextField, `created_at`:DateTimeField, `updated_at`:DateTimeField | core.Tenant, LabTest, LabResult, settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL | _none_ | `laboratory` |
| `InsuranceClaim` | `patients` | REMOVE (replace with FK/API to canonical) | patients/models.py | `claim_number`:CharField, `patient`:ForeignKey, `insurance_info`:ForeignKey, `claim_type`:CharField, `status`:CharField, `priority`:CharField, `service_date`:DateField, `service_provider`:CharField, `service_provider_license`:CharField, `facility_name`:CharField, `facility_license`:CharField, `primary_diagnosis_code`:CharField, `primary_diagnosis_description`:TextField, `secondary_diagnosis_codes`:JSONField, `procedure_codes`:JSONField, `billed_amount`:DecimalField, `approved_amount`:DecimalField, `paid_amount`:DecimalField, `patient_responsibility`:DecimalField, `discount_amount`:DecimalField, `submitted_date`:DateTimeField, `processed_date`:DateTimeField, `payment_date`:DateTimeField, `saudi_id_number`:CharField, `insurance_card_number`:CharField, `authorization_number`:CharField, `denial_reason`:TextField, `denial_code`:CharField, `appeal_date`:DateTimeField, `appeal_reason`:TextField, `notes`:TextField, `attachments`:JSONField, `created_at`:DateTimeField, `updated_at`:DateTimeField, `created_by`:ForeignKey | PatientProfile, InsuranceInfo, settings.AUTH_USER_MODEL | _none_ | `billing` |
| `InsuranceClaim` | `billing` | KEEP (canonical owner) | billing/models.py | `medical_bill`:ForeignKey, `claim_id`:UUIDField, `claim_number`:CharField, `insurance_info`:ForeignKey, `claim_type`:CharField, `submission_date`:DateField, `service_date_from`:DateField, `service_date_to`:DateField, `billed_amount`:DecimalField, `allowed_amount`:DecimalField, `paid_amount`:DecimalField, `patient_responsibility`:DecimalField, `deductible_amount`:DecimalField, `coinsurance_amount`:DecimalField, `copay_amount`:DecimalField, `status`:CharField, `clearinghouse`:CharField, `batch_number`:CharField, `response_date`:DateField, `check_number`:CharField, `check_date`:DateField, `denial_reason`:CharField, `denial_code`:CharField, `prior_auth_number`:CharField, `notes`:TextField, `original_claim`:ForeignKey, `resubmission_count`:PositiveIntegerField, `created_at`:DateTimeField, `updated_at`:DateTimeField, `created_by`:ForeignKey | MedicalBill, patients.InsuranceInfo, self, settings.AUTH_USER_MODEL | _none_ | `billing` |
**Canonical Owner Decisions (per duplicated model):**
- `IntegrationLog`**integration**
- `InventoryLocation`**inventory**
- `QualityControl`**laboratory**
- `InsuranceClaim`**billing**
## B) Inventory Leaks in Clinical Apps — Field-by-Field Remediation
### `blood_bank.InventoryLocation`
File: `blood_bank/models.py`
- Tokens detected: `stock`
- **Remove/rename fields** (move semantics to Inventory): `current_stock`
- **Add FKs**:
- ForeignKey('inventory.Item', on_delete=PROTECT) # replace local item/sku fields
- ForeignKey('inventory.StockLocation', on_delete=PROTECT) # replace local location/warehouse fields
- **Behavior**: call `inventory.services.record_stock_movement(...)` instead of mutating local stock fields.
### `pharmacy.Prescription`
File: `pharmacy/models.py`
- Tokens detected: `quantity`
- **Remove/rename fields** (move semantics to Inventory): `quantity_prescribed`, `quantity_unit`
- **Add FKs**:
- ForeignKey('inventory.Item', on_delete=PROTECT) # replace local item/sku fields
- ForeignKey('inventory.StockLocation', on_delete=PROTECT) # replace local location/warehouse fields
- **Behavior**: call `inventory.services.record_stock_movement(...)` instead of mutating local stock fields.
### `pharmacy.MedicationInventoryItem`
File: `pharmacy/models.py`
- Tokens detected: `quantity`
- **Remove/rename fields** (move semantics to Inventory): `max_dispense_quantity`
- **Add FKs**:
- ForeignKey('inventory.Item', on_delete=PROTECT) # replace local item/sku fields
- ForeignKey('inventory.StockLocation', on_delete=PROTECT) # replace local location/warehouse fields
- **Behavior**: call `inventory.services.record_stock_movement(...)` instead of mutating local stock fields.
### `pharmacy.DispenseRecord`
File: `pharmacy/models.py`
- Tokens detected: `quantity, stock`
- **Remove/rename fields** (move semantics to Inventory): `inventory_stock`, `quantity_dispensed`, `quantity_remaining`
- **Add FKs**:
- ForeignKey('inventory.Item', on_delete=PROTECT) # replace local item/sku fields
- ForeignKey('inventory.StockLocation', on_delete=PROTECT) # replace local location/warehouse fields
- **Behavior**: call `inventory.services.record_stock_movement(...)` instead of mutating local stock fields.
### `operating_theatre.EquipmentUsage`
File: `operating_theatre/models.py`
- Tokens detected: `quantity`
- **Remove/rename fields** (move semantics to Inventory): `quantity_used`
- **Add FKs**:
- ForeignKey('inventory.Item', on_delete=PROTECT) # replace local item/sku fields
- ForeignKey('inventory.StockLocation', on_delete=PROTECT) # replace local location/warehouse fields
- **Behavior**: call `inventory.services.record_stock_movement(...)` instead of mutating local stock fields.
### `insurance_approvals.InsuranceApprovalRequest`
File: `insurance_approvals/models.py`
- Tokens detected: `quantity`
- **Remove/rename fields** (move semantics to Inventory): `requested_quantity`, `approved_quantity`
- **Add FKs**:
- ForeignKey('inventory.Item', on_delete=PROTECT) # replace local item/sku fields
- ForeignKey('inventory.StockLocation', on_delete=PROTECT) # replace local location/warehouse fields
- **Behavior**: call `inventory.services.record_stock_movement(...)` instead of mutating local stock fields.
## C) Notes & Reports Centralization — Model Replacement Plan
All the models below should be replaced by creating `documentation.Document` records and linking back via `DocumentLink`. Suggested mapping of field semantics:
### `patients.PatientNote`
File: `patients/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `title`, `content`, `category`, `priority`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `inpatients.DischargeSummary`
File: `inpatients/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `admission_diagnosis`, `final_diagnosis`, `secondary_diagnoses`, `procedures_performed`, `hospital_course`, `complications`, `discharge_medications`, `medication_changes`, `activity_restrictions`, `diet_instructions`, `wound_care`, `special_instructions`, `follow_up_appointments`, `follow_up_instructions`, `warning_signs`, `when_to_call`, `discharge_disposition`, `discharge_location`, `transportation_method`, `durable_medical_equipment`, `supplies_provided`, `education_provided`, `education_materials`, `patient_understanding`, `readmission_risk`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `emr.ClinicalNote`
File: `emr/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `note_type`, `title`, `content`, `structured_data`, `status`, `signature_method`, `amendment_reason`, `compliance_flags`, `access_restrictions`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `emr.NoteTemplate`
File: `emr/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `name`, `description`, `note_type`, `specialty`, `template_content`, `structured_fields`, `version`, `quality_indicators`, `compliance_requirements`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `radiology.RadiologyReport`
File: `radiology/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `clinical_history`, `technique`, `findings`, `impression`, `recommendations`, `status`, `structured_data`, `addendum`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `radiology.ReportTemplate`
File: `radiology/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `name`, `description`, `modality`, `body_part`, `clinical_history_template`, `technique_template`, `findings_template`, `impression_template`, `recommendations_template`, `structured_fields`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `operating_theatre.SurgicalNote`
File: `operating_theatre/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `preoperative_diagnosis`, `planned_procedure`, `indication`, `procedure_performed`, `surgical_approach`, `findings`, `technique`, `postoperative_diagnosis`, `condition`, `disposition`, `complications`, `blood_transfusion`, `specimens`, `implants`, `drains`, `closure`, `postop_instructions`, `follow_up`, `status`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `operating_theatre.SurgicalNoteTemplate`
File: `operating_theatre/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `name`, `description`, `procedure_type`, `specialty`, `preoperative_diagnosis_template`, `planned_procedure_template`, `indication_template`, `procedure_performed_template`, `surgical_approach_template`, `findings_template`, `technique_template`, `postoperative_diagnosis_template`, `complications_template`, `specimens_template`, `implants_template`, `closure_template`, `postop_instructions_template`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `analytics.Report`
File: `analytics/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `name`, `description`, `report_type`, `query_config`, `output_format`, `template_config`, `schedule_type`, `schedule_config`, `recipients`, `distribution_config`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `analytics.ReportExecution`
File: `analytics/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `execution_type`, `status`, `error_message`, `output_file_path`, `execution_parameters`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
### `quality.IncidentReport`
File: `quality/models.py`
- Likely narrative fields → `Document.body_markdown/body_json`: `incident_number`, `title`, `description`, `incident_type`, `severity`, `category`, `location`, `witness_information`, `status`, `priority`, `root_cause`, `contributing_factors`, `corrective_actions`, `preventive_actions`
- Keep metadata (author, timestamps) by setting `authored_by`, `authored_at`, `signed_at/signed_by` as needed.
- Link to source domain object via `DocumentLink(role='source'|'result'|'context')`.
- **Action**: delete this model post-migration (DB reset allowed).
## D) Canonical Ownership Conflicts — Move to Expected App
| Model | Current App | Expected Owner(s) | File | Fields | FKs | M2Ms |
|---|---|---|---|---|---|---|
| `Encounter` | `emr` | `core` | emr/models.py | `objects`:EncounterManager, `tenant`:ForeignKey, `encounter_id`:UUIDField, `patient`:ForeignKey, `provider`:ForeignKey, `encounter_type`:CharField, `encounter_class`:CharField, `start_datetime`:DateTimeField, `end_datetime`:DateTimeField, `status`:CharField, `location`:CharField, `room_number`:CharField, `appointment`:ForeignKey, `admission`:ForeignKey, `chief_complaint`:TextField, `reason_for_visit`:TextField, `priority`:CharField, `acuity_level`:PositiveIntegerField, `documentation_complete`:BooleanField, `signed_off`:BooleanField, `signed_by`:ForeignKey, `signed_datetime`:DateTimeField, `billable`:BooleanField, `billing_codes`:JSONField, `quality_measures`:JSONField, `created_at`:DateTimeField, `updated_at`:DateTimeField, `created_by`:ForeignKey | core.Tenant, patients.PatientProfile, settings.AUTH_USER_MODEL, appointments.AppointmentRequest, inpatients.Admission, settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL | _none_ |
**Action:** move the model(s) to listed expected owner apps; update all FKs/imports accordingly.
## E) Reverse Dependency Hints (what references what)
<details><summary>Show reference map</summary>
- `core.Tenant` ⟵ core:AuditLogEntry/SystemConfiguration/SystemNotification/IntegrationLog, patients:PatientProfile/ConsentTemplate, appointments:AppointmentRequest/SlotAvailability/WaitingQueue/AppointmentTemplate/WaitingList, inpatients:Ward/Admission, emr:Encounter/ProblemList/CarePlan/NoteTemplate/ClinicalRecommendation/AllergyAlert/TreatmentProtocol/ClinicalGuideline/CriticalAlert/DiagnosticSuggestion, pharmacy:Medication/Prescription/MedicationInventoryItem/DrugInteraction, laboratory:LabTest/LabOrder/QualityControl, radiology:ImagingStudy/ReportTemplate/ImagingOrder, operating_theatre:OperatingRoom/SurgicalNoteTemplate, billing:MedicalBill/BillingConfiguration, inventory:InventoryItem/InventoryLocation/PurchaseOrder/Supplier, hr:Employee/Department/TrainingPrograms, analytics:Dashboard/DataSource/Report/MetricDefinition, communications:Message/NotificationTemplate/AlertRule/CommunicationChannel, integration:ExternalSystem, quality:QualityIndicator/QualityMeasurement/IncidentReport/RiskAssessment/AuditPlan/AuditFinding/ImprovementProject, facility_management:Building/Vendor, insurance_approvals:InsuranceApprovalRequest/ApprovalTemplate
- `settings.AUTH_USER_MODEL` ⟵ core:AuditLogEntry/SystemConfiguration/SystemNotification/SystemNotification, blood_bank:Donor/BloodUnit/BloodTest/BloodTest/CrossMatch/CrossMatch/BloodRequest/BloodRequest/BloodRequest/BloodIssue/BloodIssue/Transfusion/Transfusion/Transfusion/Transfusion/AdverseReaction/AdverseReaction/QualityControl/QualityControl/QualityControl, patients:PatientProfile/InsuranceClaim/ClaimDocument/ClaimStatusHistory/ConsentTemplate/ConsentForm/PatientNote, appointments:AppointmentRequest/AppointmentRequest/AppointmentRequest/AppointmentRequest/SlotAvailability/SlotAvailability/WaitingQueue/WaitingQueue/QueueEntry/QueueEntry/TelemedicineSession/AppointmentTemplate/WaitingList/WaitingList/WaitingList/WaitingListContactLog, inpatients:Ward/Ward/Ward/Bed/Bed/Bed/Admission/Admission/Admission/Admission/Admission/DischargeSummary/DischargeSummary/DischargeSummary/DischargeSummary/Transfer/Transfer/Transfer/Transfer, emr:Encounter/Encounter/Encounter/VitalSigns/VitalSigns/ProblemList/ProblemList/ProblemList/ProblemList/CarePlan/CarePlan/CarePlan/CarePlan/ClinicalNote/ClinicalNote/NoteTemplate/ClinicalRecommendation/ClinicalRecommendation/ClinicalRecommendation/ClinicalRecommendation/AllergyAlert/TreatmentProtocol/CriticalAlert/CriticalAlert/DiagnosticSuggestion/DiagnosticSuggestion, pharmacy:Medication/Prescription/Prescription/MedicationInventoryItem/DispenseRecord/DispenseRecord/MedicationAdministration/MedicationAdministration/MedicationAdministration/DrugInteraction, laboratory:LabTest/LabOrder/Specimen/Specimen/LabResult/LabResult/QualityControl/QualityControl/ReferenceRange, radiology:ImagingStudy/ImagingStudy/ImagingStudy/RadiologyReport/RadiologyReport/RadiologyReport/RadiologyReport/ReportTemplate/ImagingOrder, operating_theatre:OperatingRoom/ORBlock/ORBlock/ORBlock/SurgicalCase/SurgicalCase/SurgicalCase/SurgicalCase/SurgicalCase/SurgicalCase/SurgicalNote/EquipmentUsage/SurgicalNoteTemplate, billing:MedicalBill/MedicalBill/MedicalBill/BillLineItem/BillLineItem/InsuranceClaim/Payment/Payment/ClaimStatusUpdate/BillingConfiguration, inventory:InventoryItem/InventoryLocation/InventoryLocation/PurchaseOrder/PurchaseOrder/PurchaseOrder/Supplier, hr:Employee/Employee/Employee/Schedule/Schedule/TimeEntry/PerformanceReview/TrainingPrograms/TrainingSession/TrainingRecord/TrainingCertificates/TrainingCertificates, communications:Message/MessageRecipient/NotificationTemplate/AlertRule/AlertRule/AlertInstance/AlertInstance/CommunicationChannel, integration:ExternalSystem/IntegrationEndpoint/DataMapping/IntegrationExecution/WebhookEndpoint/IntegrationLog, quality:QualityIndicator/QualityMeasurement/QualityMeasurement/IncidentReport/IncidentReport/RiskAssessment/RiskAssessment/AuditPlan/AuditPlan/AuditPlan/AuditFinding/AuditFinding/AuditFinding/ImprovementProject/ImprovementProject/ImprovementProject/ImprovementProject, facility_management:Building/Asset/MaintenanceRequest/MaintenanceRequest/MaintenanceSchedule/ServiceContract/Inspection/EnergyReading/SpaceReservation/SpaceReservation, insurance_approvals:InsuranceApprovalRequest/InsuranceApprovalRequest/InsuranceApprovalRequest/InsuranceApprovalRequest/ApprovalDocument/ApprovalStatusHistory/ApprovalCommunicationLog/ApprovalTemplate
- `blood_bank.BloodGroup` ⟵ blood_bank:Donor/BloodUnit/BloodRequest
- `blood_bank.Donor` ⟵ blood_bank:BloodUnit
- `blood_bank.BloodComponent` ⟵ blood_bank:BloodUnit/BloodRequest
- `blood_bank.BloodUnit` ⟵ blood_bank:BloodTest/CrossMatch/BloodIssue
- `patients.PatientProfile` ⟵ blood_bank:CrossMatch/BloodRequest, patients:EmergencyContact/InsuranceInfo/InsuranceClaim/ConsentForm/PatientNote, appointments:AppointmentRequest/QueueEntry/WaitingList, inpatients:Admission/Transfer, emr:Encounter/VitalSigns/ProblemList/CarePlan/ClinicalNote/ClinicalRecommendation/AllergyAlert/CriticalAlert/DiagnosticSuggestion, pharmacy:Prescription/MedicationAdministration, laboratory:LabOrder, radiology:ImagingStudy/ImagingOrder, operating_theatre:SurgicalCase, billing:MedicalBill, quality:IncidentReport, insurance_approvals:InsuranceApprovalRequest
- `hr.Department` ⟵ blood_bank:BloodRequest, appointments:WaitingList, hr:Employee/ScheduleAssignment/TimeEntry, quality:QualityIndicator/AuditPlan/ImprovementProject
- `blood_bank.BloodRequest` ⟵ blood_bank:BloodIssue
- `blood_bank.CrossMatch` ⟵ blood_bank:BloodIssue
- `blood_bank.BloodIssue` ⟵ blood_bank:Transfusion
- `blood_bank.Transfusion` ⟵ blood_bank:AdverseReaction
- `hr.Employee` ⟵ patients:InsuranceInfo/ConsentForm, hr:Department/Department/Schedule/TimeEntry/PerformanceReview/TrainingPrograms/TrainingSession/TrainingRecord/TrainingCertificates
- `patients.InsuranceInfo` ⟵ patients:InsuranceClaim, billing:MedicalBill/MedicalBill/InsuranceClaim, insurance_approvals:InsuranceApprovalRequest
- `patients.InsuranceClaim` ⟵ patients:ClaimDocument/ClaimStatusHistory, billing:Payment/ClaimStatusUpdate
- `patients.ConsentTemplate` ⟵ patients:ConsentForm
- `appointments.WaitingQueue` ⟵ appointments:QueueEntry
- `appointments.AppointmentRequest` ⟵ appointments:QueueEntry/TelemedicineSession/WaitingList, emr:Encounter
- `appointments.WaitingList` ⟵ appointments:WaitingListContactLog
- `facility_management.Building` ⟵ inpatients:Ward, facility_management:Floor/Asset/MaintenanceRequest/MaintenanceSchedule/ServiceContract/Inspection/EnergyMeter
- `facility_management.Floor` ⟵ inpatients:Ward, facility_management:Room/Asset/MaintenanceRequest/Inspection
- `inpatients.Ward` ⟵ inpatients:Bed/Admission/Transfer/Transfer
- `inpatients.Admission` ⟵ inpatients:Bed/DischargeSummary/Transfer, emr:Encounter, operating_theatre:SurgicalCase, billing:MedicalBill
- `facility_management.Asset` ⟵ inpatients:Bed, facility_management:MaintenanceRequest/MaintenanceSchedule/Inspection
- `inpatients.Bed` ⟵ inpatients:Admission/Transfer/Transfer
- `emr.Encounter` ⟵ emr:VitalSigns/ProblemList/ClinicalNote/ClinicalRecommendation/CriticalAlert, pharmacy:Prescription/MedicationAdministration, laboratory:LabOrder, radiology:ImagingStudy/ImagingOrder, operating_theatre:SurgicalCase, billing:MedicalBill
- `emr.ProblemList` ⟵ emr:CarePlan/ClinicalNote/ClinicalRecommendation
- `emr.NoteTemplate` ⟵ emr:ClinicalNote
- `emr.CarePlan` ⟵ emr:ClinicalNote
- `pharmacy.Medication` ⟵ pharmacy:Prescription/MedicationInventoryItem/DrugInteraction/DrugInteraction
- `inventory.InventoryItem` ⟵ pharmacy:MedicationInventoryItem, inventory:InventoryStock/PurchaseOrderItem
- `pharmacy.Prescription` ⟵ pharmacy:DispenseRecord/MedicationAdministration
- `inventory.InventoryStock` ⟵ pharmacy:DispenseRecord
- `laboratory.LabTest` ⟵ laboratory:LabOrder/LabResult/QualityControl/ReferenceRange
- `laboratory.LabOrder` ⟵ laboratory:Specimen/LabResult
- `laboratory.Specimen` ⟵ laboratory:LabResult
- `laboratory.LabResult` ⟵ laboratory:QualityControl
- `radiology.ImagingOrder` ⟵ radiology:ImagingStudy
- `radiology.ImagingStudy` ⟵ radiology:ImagingSeries/RadiologyReport
- `radiology.ImagingSeries` ⟵ radiology:DICOMImage
- `radiology.ReportTemplate` ⟵ radiology:RadiologyReport
- `operating_theatre.OperatingRoom` ⟵ operating_theatre:ORBlock
- `operating_theatre.ORBlock` ⟵ operating_theatre:SurgicalCase
- `operating_theatre.SurgicalCase` ⟵ operating_theatre:SurgicalNote/EquipmentUsage
- `operating_theatre.SurgicalNoteTemplate` ⟵ operating_theatre:SurgicalNote
- `billing.MedicalBill` ⟵ billing:BillLineItem/InsuranceClaim/Payment
- `inventory.Supplier` ⟵ inventory:InventoryItem/InventoryStock/PurchaseOrder
- `blood_bank.InventoryLocation` ⟵ inventory:InventoryStock/PurchaseOrder
- `inventory.PurchaseOrder` ⟵ inventory:InventoryStock/PurchaseOrderItem
- `hr.Schedule` ⟵ hr:ScheduleAssignment
- `hr.TrainingPrograms` ⟵ hr:ProgramModule/ProgramPrerequisite/ProgramPrerequisite/TrainingSession/TrainingRecord/TrainingCertificates
- `hr.TrainingSession` ⟵ hr:TrainingRecord
- `hr.TrainingRecord` ⟵ hr:TrainingAttendance/TrainingAssessment/TrainingCertificates
- `analytics.Dashboard` ⟵ analytics:DashboardWidget
- `analytics.DataSource` ⟵ analytics:DashboardWidget/Report/MetricDefinition
- `analytics.Report` ⟵ analytics:ReportExecution
- `analytics.MetricDefinition` ⟵ analytics:MetricValue
- `communications.Message` ⟵ communications:MessageRecipient/DeliveryLog
- `communications.NotificationTemplate` ⟵ communications:AlertRule
- `communications.AlertRule` ⟵ communications:AlertInstance
- `communications.MessageRecipient` ⟵ communications:DeliveryLog
- `communications.CommunicationChannel` ⟵ communications:DeliveryLog
- `integration.ExternalSystem` ⟵ integration:IntegrationEndpoint/WebhookEndpoint/IntegrationLog
- `integration.IntegrationEndpoint` ⟵ integration:DataMapping/IntegrationExecution/IntegrationLog
- `integration.DataMapping` ⟵ integration:WebhookEndpoint
- `integration.WebhookEndpoint` ⟵ integration:WebhookExecution
- `integration.IntegrationExecution` ⟵ integration:IntegrationLog
- `quality.QualityIndicator` ⟵ quality:QualityMeasurement
- `quality.IncidentReport` ⟵ quality:RiskAssessment
- `quality.AuditPlan` ⟵ quality:AuditFinding
- `facility_management.AssetCategory` ⟵ facility_management:Asset
- `facility_management.Room` ⟵ facility_management:Asset/MaintenanceRequest/MaintenanceSchedule/Inspection/SpaceReservation
- `facility_management.MaintenanceType` ⟵ facility_management:MaintenanceRequest/MaintenanceSchedule
- `facility_management.Vendor` ⟵ facility_management:ServiceContract
- `facility_management.EnergyMeter` ⟵ facility_management:EnergyReading
- `insurance_approvals.InsuranceApprovalRequest` ⟵ insurance_approvals:ApprovalDocument/ApprovalStatusHistory/ApprovalCommunicationLog
</details>
## F) Service Stubs to Route Behavior
### `inventory/services.py`
```python
from django.db import transaction
from .models import Item, StockLocation, StockLedger
class StockMovementType:
ISSUE = "ISSUE"
RECEIPT = "RECEIPT"
ADJUSTMENT = "ADJUSTMENT"
TRANSFER = "TRANSFER"
@transaction.atomic
def record_stock_movement(*, tenant, item_id, location_id, qty, movement_type, reference=None, performed_by=None, note=""):
item = Item.objects.select_for_update().get(id=item_id, tenant=tenant)
loc = StockLocation.objects.select_for_update().get(id=location_id, tenant=tenant)
if movement_type == StockMovementType.ISSUE:
loc.on_hand = (loc.on_hand or 0) - qty
elif movement_type == StockMovementType.RECEIPT:
loc.on_hand = (loc.on_hand or 0) + qty
loc.save(update_fields=["on_hand"])
StockLedger.objects.create(
tenant=tenant, item=item, location=loc, qty=qty, movement_type=movement_type,
reference=str(reference or ""), note=note, performed_by=performed_by)
return loc.on_hand
```
### `documentation/services.py`
```python
from django.utils import timezone
from django.contrib.contenttypes.models import ContentType
from .models import Document, DocumentLink, DocumentVersion
def create_document_and_link(*, tenant, patient, encounter=None, doc_type:str,
title:str, body_markdown:str="", body_json=None,
links=None, author=None, sign=False):
doc = Document.objects.create(
tenant=tenant, patient=patient, encounter=encounter,
doc_type=doc_type, title=title, body_markdown=body_markdown,
body_json=body_json or {}, authored_by=author,
status="final" if sign else "draft",
signed_at=timezone.now() if sign else None, signed_by=author if sign else None
)
DocumentVersion.objects.create(document=doc, version=1,
snapshot_markdown=body_markdown,
snapshot_json=body_json or {}, changed_by=author)
for ln in links or []:
ct = ContentType.objects.get(app_label=ln["app_label"], model=ln["model"].lower())
DocumentLink.objects.create(document=doc, content_type=ct, object_id=str(ln["pk"]),
role=ln.get("role", "context"))
return doc
```
### `integration/services.py`
```python
def log_event(*, tenant, channel, payload, direction, status, ref=""):
from .models import IntegrationLog
return IntegrationLog.objects.create(
tenant=tenant, channel=channel, payload=payload,
direction=direction, status=status, ref=ref)
```
## G) Per-App Checklist (Add / Remove / Modify)
### `accounts`
- **Models detected (4):** `TwoFactorDevice`, `SocialAccount`, `UserSession`, `PasswordHistory`
### `analytics`
- **Models detected (7):** `Dashboard`, `DashboardWidget`, `DataSource`, `Report`, `ReportExecution`, `MetricDefinition`, `MetricValue`
- **Notes centralization:**
- `Report`: replace with `documentation.Document`; link via `DocumentLink`.
- `ReportExecution`: replace with `documentation.Document`; link via `DocumentLink`.
### `appointments`
- **Models detected (8):** `AppointmentRequest`, `SlotAvailability`, `WaitingQueue`, `QueueEntry`, `TelemedicineSession`, `AppointmentTemplate`, `WaitingList`, `WaitingListContactLog`
### `billing`
- **Models detected (6):** `MedicalBill`, `BillLineItem`, `InsuranceClaim`, `Payment`, `ClaimStatusUpdate`, `BillingConfiguration`
- **Collision actions:**
- `InsuranceClaim` → KEEP (canonical owner) (canonical: `billing`)
### `blood_bank`
- **Models detected (12):** `BloodGroup`, `Donor`, `BloodComponent`, `BloodUnit`, `BloodTest`, `CrossMatch`, `BloodRequest`, `BloodIssue`, `Transfusion`, `AdverseReaction`, `InventoryLocation`, `QualityControl`
- **Collision actions:**
- `InventoryLocation` → REMOVE (replace with FK/API to canonical) (canonical: `inventory`)
- `QualityControl` → REMOVE (replace with FK/API to canonical) (canonical: `laboratory`)
- **Inventory leak fixes:**
- `InventoryLocation`: remove fields `current_stock`; add inventory FKs; use `record_stock_movement`.
### `communications`
- **Models detected (7):** `Message`, `MessageRecipient`, `NotificationTemplate`, `AlertRule`, `AlertInstance`, `CommunicationChannel`, `DeliveryLog`
### `core`
- **Models detected (5):** `Tenant`, `AuditLogEntry`, `SystemConfiguration`, `SystemNotification`, `IntegrationLog`
- **Collision actions:**
- `IntegrationLog` → REMOVE (replace with FK/API to canonical) (canonical: `integration`)
### `emr`
- **Models detected (13):** `Encounter`, `VitalSigns`, `ProblemList`, `CarePlan`, `ClinicalNote`, `NoteTemplate`, `Icd10`, `ClinicalRecommendation`, `AllergyAlert`, `TreatmentProtocol`, `ClinicalGuideline`, `CriticalAlert`, `DiagnosticSuggestion`
- **Notes centralization:**
- `ClinicalNote`: replace with `documentation.Document`; link via `DocumentLink`.
- `NoteTemplate`: replace with `documentation.Document`; link via `DocumentLink`.
- **Canonical conflicts:**
- `Encounter`: move to `core`
### `facility_management`
- **Models detected (14):** `Building`, `Floor`, `Room`, `AssetCategory`, `Asset`, `MaintenanceType`, `MaintenanceRequest`, `MaintenanceSchedule`, `Vendor`, `ServiceContract`, `Inspection`, `EnergyMeter`, `EnergyReading`, `SpaceReservation`
### `hr`
- **Models detected (14):** `Employee`, `Department`, `Schedule`, `ScheduleAssignment`, `TimeEntry`, `PerformanceReview`, `TrainingPrograms`, `ProgramModule`, `ProgramPrerequisite`, `TrainingSession`, `TrainingRecord`, `TrainingAttendance`, `TrainingAssessment`, `TrainingCertificates`
### `inpatients`
- **Models detected (5):** `Ward`, `Bed`, `Admission`, `DischargeSummary`, `Transfer`
- **Notes centralization:**
- `DischargeSummary`: replace with `documentation.Document`; link via `DocumentLink`.
### `insurance_approvals`
- **Models detected (5):** `InsuranceApprovalRequest`, `ApprovalDocument`, `ApprovalStatusHistory`, `ApprovalCommunicationLog`, `ApprovalTemplate`
- **Inventory leak fixes:**
- `InsuranceApprovalRequest`: remove fields `requested_quantity`, `approved_quantity`; add inventory FKs; use `record_stock_movement`.
### `integration`
- **Models detected (7):** `ExternalSystem`, `IntegrationEndpoint`, `DataMapping`, `IntegrationExecution`, `WebhookEndpoint`, `WebhookExecution`, `IntegrationLog`
- **Collision actions:**
- `IntegrationLog` → KEEP (canonical owner) (canonical: `integration`)
### `inventory`
- **Models detected (6):** `InventoryItem`, `InventoryStock`, `InventoryLocation`, `PurchaseOrder`, `PurchaseOrderItem`, `Supplier`
- **Collision actions:**
- `InventoryLocation` → KEEP (canonical owner) (canonical: `inventory`)
### `laboratory`
- **Models detected (6):** `LabTest`, `LabOrder`, `Specimen`, `LabResult`, `QualityControl`, `ReferenceRange`
- **Collision actions:**
- `QualityControl` → KEEP (canonical owner) (canonical: `laboratory`)
### `operating_theatre`
- **Models detected (6):** `OperatingRoom`, `ORBlock`, `SurgicalCase`, `SurgicalNote`, `EquipmentUsage`, `SurgicalNoteTemplate`
- **Inventory leak fixes:**
- `EquipmentUsage`: remove fields `quantity_used`; add inventory FKs; use `record_stock_movement`.
- **Notes centralization:**
- `SurgicalNote`: replace with `documentation.Document`; link via `DocumentLink`.
- `SurgicalNoteTemplate`: replace with `documentation.Document`; link via `DocumentLink`.
### `patients`
- **Models detected (9):** `PatientProfile`, `EmergencyContact`, `InsuranceInfo`, `InsuranceClaim`, `ClaimDocument`, `ClaimStatusHistory`, `ConsentTemplate`, `ConsentForm`, `PatientNote`
- **Collision actions:**
- `InsuranceClaim` → REMOVE (replace with FK/API to canonical) (canonical: `billing`)
- **Notes centralization:**
- `PatientNote`: replace with `documentation.Document`; link via `DocumentLink`.
### `pharmacy`
- **Models detected (6):** `Medication`, `Prescription`, `MedicationInventoryItem`, `DispenseRecord`, `MedicationAdministration`, `DrugInteraction`
- **Inventory leak fixes:**
- `Prescription`: remove fields `quantity_prescribed`, `quantity_unit`; add inventory FKs; use `record_stock_movement`.
- `MedicationInventoryItem`: remove fields `max_dispense_quantity`; add inventory FKs; use `record_stock_movement`.
- `DispenseRecord`: remove fields `inventory_stock`, `quantity_dispensed`, `quantity_remaining`; add inventory FKs; use `record_stock_movement`.
### `quality`
- **Models detected (7):** `QualityIndicator`, `QualityMeasurement`, `IncidentReport`, `RiskAssessment`, `AuditPlan`, `AuditFinding`, `ImprovementProject`
- **Notes centralization:**
- `IncidentReport`: replace with `documentation.Document`; link via `DocumentLink`.
### `radiology`
- **Models detected (6):** `ImagingStudy`, `ImagingSeries`, `DICOMImage`, `RadiologyReport`, `ReportTemplate`, `ImagingOrder`
- **Notes centralization:**
- `RadiologyReport`: replace with `documentation.Document`; link via `DocumentLink`.
- `ReportTemplate`: replace with `documentation.Document`; link via `DocumentLink`.
## H) Smoke Tests (Post-Reset)
1. Create Patient + Encounter.
2. Inpatients: create Admission → create `ADMISSION` Document linked to Admission.
3. Radiology: finalize Study → `RADIOLOGY_REPORT` Document linked as result.
4. Laboratory: publish narrative → `LAB_REPORT` Document linked to Order.
5. Pharmacy: dispense Item → `record_stock_movement` ISSUE; on_hand decreases; ledger row created.
6. Billing: create Claim → appears only in `billing`; no duplicates in `patients`.
7. Patient timeline: fetch all `documentation.Document` for patient; verify docs appear.