# Tenhal Multidisciplinary Healthcare Platform - Detailed Implementation Plan ## Project Status **Current Phase:** Phase 1 - Models Implementation (In Progress) **Completed:** - ✅ Phase 0: Project Foundation & Configuration - Environment configuration (.env.example) - Dependencies (pyproject.toml with all required packages) - Django settings (PostgreSQL, i18n, DRF, Celery, integrations) - All 11 Django apps created - Base model mixins (UUID, TimeStamped, TenantOwned, ClinicallySignable, SoftDelete) - Core app models complete **Next Steps:** - Continue with remaining app models (appointments, finance, notifications, nursing, medical, aba, ot, slp, referrals, integrations) --- ## Phase 1: Models Implementation (Current Phase) ### 1.1 Core App ✅ COMPLETE **Models Created:** - `Tenant` - Multi-tenancy support - `User` - Custom user with roles (ADMIN, DOCTOR, NURSE, OT, SLP, ABA, FRONT_DESK, FINANCE) - `Patient` - Demographics, bilingual names, caregiver info - `Clinic` - Specialties (MEDICAL, NURSING, ABA, OT, SLP) - `File` - Main patient file - `SubFile` - Per-clinic sub-files - `Consent` - E-signature support with multiple types - `Attachment` - Generic file attachments - `AuditLog` - Action tracking **Base Mixins Created:** - `UUIDPrimaryKeyMixin` - UUID primary keys - `TimeStampedMixin` - created_at, updated_at - `TenantOwnedMixin` - tenant FK - `ClinicallySignableMixin` - signed_by, signed_at - `SoftDeleteMixin` - soft delete support ### 1.2 Appointments App - TO DO **Models to Create:** ```python # appointments/models.py class Provider(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Provider (doctor/therapist) with specialties""" user = OneToOneField(User) specialties = ManyToManyField(Clinic) is_available = BooleanField max_daily_appointments = PositiveIntegerField class Room(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Clinic rooms""" name, room_number, clinic FK, is_available class Schedule(UUIDPrimaryKeyMixin, TimeStampedMixin): """Provider availability schedule""" provider FK, day_of_week, start_time, end_time, slot_duration, is_active class Appointment(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Main appointment model with state machine""" appointment_number (auto-generated) patient FK, clinic FK, provider FK, room FK service_type, scheduled_date, scheduled_time, duration status: BOOKED, CONFIRMED, RESCHEDULED, CANCELLED, NO_SHOW, ARRIVED, IN_PROGRESS, COMPLETED confirmation_sent_at, confirmation_method arrival_at, start_at, end_at reschedule_reason, reschedule_count cancel_reason, cancelled_by FK notes, finance_cleared, consent_verified history = HistoricalRecords() class AppointmentReminder(UUIDPrimaryKeyMixin, TimeStampedMixin): """Scheduled reminders""" appointment FK, reminder_type (SMS/WHATSAPP/EMAIL) scheduled_for, sent_at, status, message_id FK ``` ### 1.3 Finance App - TO DO **Models to Create:** ```python # finance/models.py class Service(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Billable services""" code, name_en, name_ar, clinic FK base_price (MoneyField), duration_minutes, is_active class Package(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Service packages (e.g., 10 SLP sessions)""" name_en, name_ar, services M2M total_sessions, price (MoneyField), validity_days, is_active class Invoice(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Patient invoices""" invoice_number (auto-generated) patient FK, appointment FK (nullable) issue_date, due_date subtotal, tax, discount, total status: DRAFT, ISSUED, PAID, PARTIALLY_PAID, CANCELLED notes history = HistoricalRecords() class InvoiceLineItem(UUIDPrimaryKeyMixin): """Invoice line items""" invoice FK, service FK (nullable), package FK (nullable) description, quantity, unit_price, total class Payment(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Payment records""" invoice FK, payment_date, amount method: CASH, CARD, BANK_TRANSFER, INSURANCE transaction_id, reference, status processed_by FK class Payer(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Insurance/payer information""" name, type: SELF, INSURANCE, GOVERNMENT policy_number, coverage_percentage patient FK, is_active ``` ### 1.4 Notifications App - TO DO **Models to Create:** ```python # notifications/models.py class MessageTemplate(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Message templates""" code (unique), name channel: SMS, WHATSAPP, EMAIL subject (for email), body_en, body_ar variables (JSONField), is_active class Message(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Outbound messages""" template FK, channel, recipient subject, body, variables_used (JSONField) status: QUEUED, SENT, DELIVERED, FAILED, BOUNCED sent_at, delivered_at provider_message_id, provider_response (JSONField) error_message, retry_count class NotificationPreference(UUIDPrimaryKeyMixin, TimeStampedMixin): """Patient notification preferences""" patient FK sms_enabled, whatsapp_enabled, email_enabled appointment_reminders, appointment_confirmations, results_notifications ``` ### 1.5 Nursing App - TO DO **Models to Create (Based on MD-N-F-1 form):** ```python # nursing/models.py class NursingEncounter(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """Nursing vitals and assessment""" patient FK, appointment FK (nullable) encounter_date, filled_by FK # Anthropometrics height_cm, weight_kg, head_circumference_cm bmi (calculated property) # Vitals hr_bpm, bp_systolic, bp_diastolic respiratory_rate, spo2, temperature crt: LESS_THAN_2S, MORE_THAN_2S pain_score (0-10) # Allergies allergy_present, allergy_details # Observations observations (TextField) history = HistoricalRecords() class GrowthChart(UUIDPrimaryKeyMixin): """Growth chart data points""" patient FK, measurement_date height_cm, weight_kg, head_circ_cm age_months percentile_height, percentile_weight ``` ### 1.6 Medical App - TO DO **Models to Create (Based on MD-F-1 and MD-F-2 forms):** ```python # medical/models.py class MedicalConsultation(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """Medical consultation (MD-F-1)""" patient FK, appointment FK, consultation_date, provider FK # History sections chief_complaint, present_illness_history past_medical_history, vaccination_status family_history, social_history pregnancy_history, neonatal_history # Developmental milestones developmental_motor_milestones developmental_language_milestones developmental_social_milestones developmental_cognitive_milestones # Behavioral symptoms (JSONField checklist) behavioral_symptoms # Physical exam (JSONField structured) physical_exam # Assessment & Plan clinical_summary, recommendations referrals_needed lab_orders (JSONField), radiology_orders (JSONField) history = HistoricalRecords() class MedicationPlan(UUIDPrimaryKeyMixin): """Medication details""" consultation FK drug_name, dose frequency: DAILY, BID, TID, QID, PRN, OTHER frequency_other compliance: GOOD, PARTIAL, BAD gains, side_effects target_behavior, improved class MedicalFollowUp(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """Medical follow-up (MD-F-2)""" patient FK, appointment FK previous_consultation FK followup_date, provider FK # Previous complaints status (JSONField: complaint → RESOLVED/STATIC/WORSE) previous_complaints_status new_complaints # Links nursing_vitals FK (to NursingEncounter) # Assessment assessment, recommendations family_satisfaction: ZERO, FIFTY, HUNDRED medication_snapshot (JSONField) history = HistoricalRecords() ``` ### 1.7 ABA App - TO DO **Models to Create (Based on ABA-F-1 form):** ```python # aba/models.py class ABAConsult(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """ABA consultation""" patient FK, appointment FK, consultation_date, provider FK reason_of_referral: (TextChoices with options from form) parental_concern, school_concern respondents, interviewer diagnosed_condition interaction_hours_per_day # Factors (JSONField with booleans) physiological_factors medical_factors recommendations history = HistoricalRecords() class ABABehavior(UUIDPrimaryKeyMixin): """Behavior details""" consult FK behavior_description frequency: HOURLY, DAILY, WEEKLY, LESS_THAN_WEEKLY duration intensity: MILD, MODERATE, SEVERE antecedents_likely, antecedents_least_likely consequences order (for sorting) ``` ### 1.8 OT App - TO DO **Models to Create (Based on OT-F-1 and OT-F-3 forms):** ```python # ot/models.py class OTConsult(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """OT consultation (OT-F-1)""" patient FK, appointment FK, consultation_date, provider FK reasons (ArrayField or M2M with choices) top_difficulty_areas (JSONField: 3 text fields) # Developmental & skills (JSONField matrices) developmental_motor_milestones self_help_skills feeding_participation # Behavior descriptors (JSONField) infant_behavior_descriptors current_behavior_descriptors recommendation: CONTINUE, DISCHARGE, REFER_TO_OTHER recommendation_notes history = HistoricalRecords() class OTSession(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """OT session notes (OT-F-3)""" patient FK, appointment FK (nullable), session_date, provider FK session_type: CONSULT, INDIVIDUAL, GROUP, PARENT_TRAINING cooperative_level (1-4) distraction_tolerance (1-4) activities_checklist (JSONField: "Today we work on...") observations, activities_performed recommendations history = HistoricalRecords() class OTTargetSkill(UUIDPrimaryKeyMixin): """Target skills with 0-10 scoring""" session FK skill_name score (0-10) notes order ``` ### 1.9 SLP App - TO DO **Models to Create (Based on SLP-F-1, F-2, F-3, F-4 forms):** ```python # slp/models.py class SLPConsult(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """SLP consultation (SLP-F-1 with variants)""" patient FK, appointment FK, consultation_date, provider FK consult_variant: ASD, LANGUAGE_DELAY, FLUENCY primary_concern suspected_areas (JSONField) type_of_service: CONSULT, EVAL, INTERVENTION, PARENT_TRAINING communication_modes (JSONField checklist) screen_time_hours # Variant-specific questionnaires (JSONField) variant_questionnaire # Skills to observe (JSONField matrix) skills_to_observe # Oral motor screening (JSONField) oral_motor_screening recommendations history = HistoricalRecords() class SLPAssessment(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """SLP assessment (SLP-F-2)""" patient FK, appointment FK, assessment_date, provider FK diagnosis_statement, case_history # History sections prenatal_history, perinatal_history, postnatal_history developmental_history, medical_status # Speech/Language detail (JSONField) speech_language_detail # Test scores gfta3_score, jat_score, ssi_score # Oral mechanism (JSONField) oral_mechanism # Rossetti domains (JSONField: domain → age level) rossetti_domains # Joint attention skills (JSONField) joint_attention_skills # Summary & Plan clinical_summary, recommendations frequency_per_week, session_duration_minutes referral_rules (JSONField) history = HistoricalRecords() class SLPIntervention(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """SLP intervention session (SLP-F-3)""" patient FK, appointment FK session_number, session_date, session_time provider FK previous_session FK (self-referential, nullable) history = HistoricalRecords() class SLPTarget(UUIDPrimaryKeyMixin): """Intervention targets (SOAP format)""" intervention FK target_number (1 or 2) subjective, objective, assessment, plan prompt_strategies (JSONField) class SLPProgressReport(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin, ClinicallySignableMixin): """SLP progress report (SLP-F-4)""" patient FK, report_date, provider FK sessions_scheduled, sessions_attended final_diagnosis # Objectives & progress (JSONField: objective → % accuracy) objectives_progress # Plan details (JSONField) plan_details overall_progress, participation_level attendance_rate, carryover_level prognosis, recommendations package_sessions_count reassessment_needed history = HistoricalRecords() ``` ### 1.10 Referrals App - TO DO **Models to Create:** ```python # referrals/models.py class Referral(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Inter-discipline and external referrals""" patient FK from_clinic FK, to_clinic FK (nullable) from_provider FK, to_provider FK (nullable) external_provider_name reason urgency: ROUTINE, URGENT, EMERGENCY status: PENDING, ACCEPTED, REJECTED, COMPLETED created_at, responded_at notes ``` ### 1.11 Integrations App - TO DO **Models to Create:** ```python # integrations/models.py # Lab/Radiology class ExternalOrder(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Lab and radiology orders""" patient FK order_type: LAB, RADIOLOGY order_details (JSONField) status: ORDERED, IN_PROGRESS, COMPLETED, CANCELLED result_url, result_data (JSONField) ordered_by FK, ordered_at, completed_at # NPHIES (Insurance e-Claims) class NphiesMessage(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """NPHIES FHIR messages""" direction: OUTBOUND, INBOUND resource_type: ELIGIBILITY, PRIOR_AUTH, CLAIM, PAYMENT_NOTICE, PAYMENT_RECONCILIATION fhir_json (JSONField) status: QUEUED, SENT, ACK, ERROR correlation_id response_http_status, error_code, error_message created_at, sent_at class NphiesEncounterLink(UUIDPrimaryKeyMixin, TenantOwnedMixin): """Link appointments to NPHIES encounters""" patient FK, appointment FK encounter_id, claim_id, claim_response_id class PayerContract(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """Insurance payer configurations""" payer_code, payer_name credentials (JSONField encrypted) endpoints (JSONField) supports_eligibility, supports_prior_auth, supports_claims is_active # ZATCA (E-Invoicing) class EInvoice(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """ZATCA e-invoices""" invoice FK (to finance.Invoice) uuid, xml_payload, qr_base64 clearance_status: PENDING, CLEARED, REJECTED, REPORTED zatca_document_type submission_mode: CLEARANCE, REPORTING response_payload (JSONField) error_code, error_message submitted_at class ZatcaCredential(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin): """ZATCA credentials (CSIDs)""" environment: SIMULATION, COMPLIANCE, PRODUCTION csid, certificate, private_key (encrypted) is_active, expires_at ``` --- ## Phase 2: Forms Implementation (4-5 days) ### Forms to Create Per App **Core Forms:** - `PatientForm` - Bilingual patient registration - `ConsentForm` - With signature capture - `AttachmentForm` - File upload **Appointments Forms:** - `AppointmentBookingForm` - `AppointmentConfirmForm` - `AppointmentRescheduleForm` - `AppointmentCancelForm` **Nursing Forms:** - `NursingEncounterForm` - Auto-calculate BMI **Medical Forms:** - `MedicalConsultationForm` - Sections matching MD-F-1 - `MedicationPlanFormSet` - Inline formset - `MedicalFollowUpForm` - Sections matching MD-F-2 **ABA Forms:** - `ABAConsultForm` - `ABABehaviorFormSet` - Inline formset **OT Forms:** - `OTConsultForm` - `OTSessionForm` - `OTTargetSkillFormSet` - Inline formset **SLP Forms:** - `SLPConsultForm` - Variant-specific sections - `SLPAssessmentForm` - `SLPInterventionForm` - `SLPTargetFormSet` - Inline formset - `SLPProgressReportForm` **Finance Forms:** - `InvoiceForm` - `PaymentForm` --- ## Phase 3: Views Implementation (5-7 days) ### Views Structure Per App **Core Views:** - Patient CRUD (List, Detail, Create, Update) - Patient Dashboard (hub with tabs) - Consent CRUD **Appointments Views:** - Calendar view (weekly grid) - Appointment CRUD - HTMX partials for state transitions **Clinical Document Views (per app):** - List, Detail, Create, Update for each document type - HTMX partials for dynamic forms **Finance Views:** - Invoice CRUD - Payment processing **Notifications Views:** - Message outbox - Message detail **Audit Views:** - Audit timeline - Version comparison --- ## Phase 4: URLs Configuration (1-2 days) - Namespaced URLs for all apps - HTMX partial endpoints - DRF routers for API apps --- ## Phase 5: Templates Implementation (5-7 days) - Base templates with RTL/LTR - Patient hub with tabs - Clinical form templates matching paper forms - Charts (OT/SLP target progress, growth charts) - Appointment calendar --- ## Phase 6: DRF API Implementation (3-4 days) **APIs to Create:** - Appointments API (CRUD + state transitions) - Notifications API (create, webhook receiver) - Referrals API (CRUD) - Integrations API: - NPHIES endpoints (eligibility, prior-auth, claims, payment) - ZATCA endpoints (prepare, submit, onboard) - Lab/Radiology stubs - Consent verification --- ## Phase 7: Signals, Services & Tasks (3-4 days) **Signals:** - Patient created → create File - Appointment created → create SubFile (if first for clinic) - Appointment status changed → enqueue notification **Services:** - Notification service (SMS/WhatsApp/Email) - NPHIES FHIR client - ZATCA e-invoicing service - Lab/Radiology service **Celery Tasks:** - Appointment reminders (T-24h, T-12h) - NPHIES polling - Payment reconciliation - ZATCA batch submission --- ## Phase 8: Admin, RBAC & Auditing (2-3 days) - Django admin for all models - Permission matrix implementation - Audit trail views - Export functionality --- ## Phase 9: Testing (4-5 days) - Model tests - Form tests - View tests (permissions, HTMX) - API tests - Integration tests - i18n tests --- ## Phase 10: Documentation & Deployment (2-3 days) - README.md - DEPLOYMENT.md - API_DOCS.md - RBAC_MATRIX.md - INTEGRATION_GUIDE.md --- ## Timeline Summary | Phase | Duration | Status | |-------|----------|--------| | 0: Foundation | 2-3 days | ✅ COMPLETE | | 1: Models | 7-10 days | 🔄 IN PROGRESS (Core complete) | | 2: Forms | 4-5 days | ⏳ PENDING | | 3: Views | 5-7 days | ⏳ PENDING | | 4: URLs | 1-2 days | ⏳ PENDING | | 5: Templates | 5-7 days | ⏳ PENDING | | 6: DRF APIs | 3-4 days | ⏳ PENDING | | 7: Services/Tasks | 3-4 days | ⏳ PENDING | | 8: Admin/RBAC | 2-3 days | ⏳ PENDING | | 9: Testing | 4-5 days | ⏳ PENDING | | 10: Docs/Deploy | 2-3 days | ⏳ PENDING | | **TOTAL** | **38-53 days** | **~8-11 weeks** | --- ## Next Immediate Steps 1. ✅ Complete core models 2. ⏭️ Create appointments models 3. ⏭️ Create finance models 4. ⏭️ Create notifications models 5. ⏭️ Create nursing models 6. ⏭️ Create medical models 7. ⏭️ Create aba models 8. ⏭️ Create ot models 9. ⏭️ Create slp models 10. ⏭️ Create referrals models 11. ⏭️ Create integrations models 12. ⏭️ Run migrations 13. ⏭️ Create signals for auto-generation (MRN, file numbers) 14. ⏭️ Begin Phase 2 (Forms) --- ## Critical Notes - **DO NOT skip phases** - Each phase builds on the previous - **Models must be complete** before starting forms - **All clinical documents** must use `simple_history` - **All user-facing strings** must use `gettext_lazy` - **UUID primary keys** for all models - **Tenant isolation** must be enforced everywhere - **RBAC** must be checked in every view - **Audit trail** for all clinical document changes --- ## Reference Documents - Clinical forms in project root (DOCX files) - Original prompt: `multidisciplinary_django_prompt_1.md` - Settings: `AgdarCentre/settings.py` - Environment: `.env.example`