All checks were successful
Build and Push Docker Image / build (push) Successful in 4m14s
Reference numbers (unified scheme PREFIX-YYYYMM-HOSP-NNNN, e.g. CMP-202606-HHN-0001): - new ReferenceSequence model + generate_reference() helper (apps/core) - Complaint/Inquiry/Observation/Appreciation/Suggestion emit unified refs via save() - prefix-based auto-routing in public track API (CMP/INQ/OBS trackable; APR/SGT internal-only) - removed legacy CMP-/INQ- generators in ui_views, integrations, px_sources - migrations: core.0003_referencesequence, appreciation.0006, feedback.0008, observations.0012 - unit tests (format, sanitization, monthly reset, 40-thread concurrency) QA audit: - isolated E2E hospital sandbox mirroring HH-N + 10 role users (create_e2e_isolated_env) - feedback-modules-audit.spec.ts + audit helper (headed, run-to-completion) - reports/feedback-modules-qa-report.md Also bundles accumulated in-progress work across complaints, observations, organizations, templates, and other modules.
588 lines
7.2 KiB
Markdown
588 lines
7.2 KiB
Markdown
# PX360 Organizational Structure Refactor (Final Architecture)
|
|
|
|
## Objective
|
|
|
|
Refactor the current Department Hierarchy implementation into a simpler and more maintainable model that:
|
|
|
|
* Uses a single source of truth for routing.
|
|
* Uses a single source of truth for reporting.
|
|
* Preserves legacy complaint mappings for historical records.
|
|
* Removes unnecessary hierarchy levels.
|
|
* Aligns with the latest organizational spreadsheet.
|
|
* Supports future department and section changes without impacting reporting.
|
|
|
|
---
|
|
|
|
# Key Business Findings
|
|
|
|
After reviewing:
|
|
|
|
* Legacy complaint hierarchy
|
|
* Existing reports
|
|
* Final organization spreadsheet
|
|
|
|
the following conclusions were reached.
|
|
|
|
## Legacy Structure
|
|
|
|
Historical complaints used:
|
|
|
|
Location
|
|
→ Main Section
|
|
→ Sub Section
|
|
|
|
Example:
|
|
|
|
Outpatient
|
|
→ Medical
|
|
→ Radiology
|
|
|
|
The old Sub Section was the actual operational unit receiving complaints.
|
|
|
|
---
|
|
|
|
## Current Organization Structure
|
|
|
|
The latest organization file contains:
|
|
|
|
* Main Section
|
|
* Department
|
|
* Section
|
|
|
|
Examples:
|
|
|
|
Medical
|
|
→ Pharmacy Department
|
|
→ Central Outpatient Pharmacy
|
|
|
|
Medical
|
|
→ Pharmacy Department
|
|
→ General
|
|
|
|
---
|
|
|
|
## Important Discovery
|
|
|
|
Area is not a valid hierarchy level.
|
|
|
|
Evidence:
|
|
|
|
* Frequently empty
|
|
* Often duplicates Department concepts
|
|
* Not used in reporting
|
|
* Not used in routing
|
|
* Not used in ownership
|
|
|
|
Therefore:
|
|
|
|
Area should not become a first-class model.
|
|
|
|
---
|
|
|
|
## Reporting Structure
|
|
|
|
All management reports are grouped by:
|
|
|
|
* Medical
|
|
* Non-Medical
|
|
* Nursing
|
|
* Support Services
|
|
|
|
These values come from:
|
|
|
|
Main Section
|
|
|
|
Therefore Main Section is actually the reporting category.
|
|
|
|
---
|
|
|
|
# Target Hierarchy
|
|
|
|
## Department Category
|
|
|
|
Represents:
|
|
|
|
* Medical
|
|
* Non-Medical
|
|
* Nursing
|
|
* Support Services
|
|
|
|
Model:
|
|
|
|
```python
|
|
class DepartmentCategory(models.TextChoices):
|
|
MEDICAL = "medical"
|
|
NON_MEDICAL = "non_medical"
|
|
NURSING = "nursing"
|
|
SUPPORT_SERVICES = "support_services"
|
|
```
|
|
|
|
This replaces the old concept of:
|
|
|
|
Main Section
|
|
|
|
---
|
|
|
|
## Department
|
|
|
|
Primary accountable organizational unit.
|
|
|
|
Model:
|
|
|
|
```python
|
|
class Department(models.Model):
|
|
|
|
category
|
|
|
|
name
|
|
code
|
|
|
|
champion
|
|
|
|
manager_1st
|
|
manager_2nd
|
|
manager_3rd
|
|
|
|
deputy_manager
|
|
supervisor
|
|
deputy_supervisor
|
|
|
|
is_active
|
|
```
|
|
|
|
Examples:
|
|
|
|
* Pharmacy Department
|
|
* Radiology Department
|
|
* Laboratory Department
|
|
* Emergency Department
|
|
|
|
Departments always have ownership.
|
|
|
|
Departments always have a Champion.
|
|
|
|
Departments are the fallback routing target.
|
|
|
|
---
|
|
|
|
## Section
|
|
|
|
Optional child unit under a Department.
|
|
|
|
Model:
|
|
|
|
```python
|
|
class Section(models.Model):
|
|
|
|
department = models.ForeignKey(
|
|
Department,
|
|
related_name="sections"
|
|
)
|
|
|
|
name
|
|
code
|
|
|
|
champion = models.ForeignKey(
|
|
Staff,
|
|
null=True,
|
|
blank=True
|
|
)
|
|
|
|
location_type
|
|
|
|
location_code
|
|
zone
|
|
floor
|
|
|
|
is_active
|
|
```
|
|
|
|
Examples:
|
|
|
|
* Central Outpatient Pharmacy
|
|
* ER Pharmacy
|
|
* Pediatric Pharmacy
|
|
|
|
Not every Department requires Sections.
|
|
|
|
Not every Section requires a Champion.
|
|
|
|
---
|
|
|
|
## Remove Sub-Section
|
|
|
|
Current model:
|
|
|
|
OrgSubSubSection
|
|
|
|
Findings:
|
|
|
|
* Mostly empty in source data.
|
|
* Not used in reports.
|
|
* Not used in routing.
|
|
* Not used in escalation.
|
|
|
|
Action:
|
|
|
|
Deprecate and remove.
|
|
|
|
---
|
|
|
|
# Complaint Model
|
|
|
|
## Target Structure
|
|
|
|
```python
|
|
class Complaint(models.Model):
|
|
|
|
department = models.ForeignKey(
|
|
Department
|
|
)
|
|
|
|
section = models.ForeignKey(
|
|
Section,
|
|
null=True,
|
|
blank=True
|
|
)
|
|
|
|
legacy_mapping = models.ForeignKey(
|
|
LegacyHierarchyMapping,
|
|
null=True,
|
|
blank=True
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
# Ownership Rules
|
|
|
|
Single ownership strategy.
|
|
|
|
```python
|
|
def get_owner():
|
|
|
|
if complaint.section and complaint.section.champion:
|
|
return complaint.section.champion
|
|
|
|
return complaint.department.champion
|
|
```
|
|
|
|
Rules:
|
|
|
|
1. Section Champion takes ownership when available.
|
|
2. Otherwise Department Champion owns the complaint.
|
|
|
|
This becomes the only routing rule in the system.
|
|
|
|
---
|
|
|
|
# Legacy Data Strategy
|
|
|
|
## Legacy Mapping Table
|
|
|
|
Keep:
|
|
|
|
```python
|
|
class LegacyHierarchyMapping(models.Model):
|
|
|
|
old_location
|
|
old_main_section
|
|
old_subsection
|
|
|
|
department
|
|
section
|
|
```
|
|
|
|
Purpose:
|
|
|
|
Legacy Complaint
|
|
→ Legacy Mapping
|
|
→ Department / Section
|
|
|
|
---
|
|
|
|
## Legacy Fields
|
|
|
|
The following fields become historical reference only:
|
|
|
|
* legacy_location
|
|
* legacy_main_section
|
|
* legacy_subsection
|
|
|
|
Rules:
|
|
|
|
* No reporting usage
|
|
* No routing usage
|
|
* No escalation usage
|
|
* No dashboard usage
|
|
|
|
May remain temporarily for audit purposes.
|
|
|
|
---
|
|
|
|
# Reporting Strategy
|
|
|
|
## Single Source of Truth
|
|
|
|
All reports must use:
|
|
|
|
```python
|
|
complaint.department
|
|
complaint.section
|
|
```
|
|
|
|
only.
|
|
|
|
Never use legacy hierarchy fields.
|
|
|
|
---
|
|
|
|
## Category Reports
|
|
|
|
Medical / Nursing / Support Services reports:
|
|
|
|
```python
|
|
Complaint.objects.values(
|
|
"department__category"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Department Reports
|
|
|
|
```python
|
|
Complaint.objects.values(
|
|
"department__name"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
## Section Reports
|
|
|
|
```python
|
|
Complaint.objects.values(
|
|
"section__name"
|
|
)
|
|
```
|
|
|
|
---
|
|
|
|
# Location Handling
|
|
|
|
Location is metadata.
|
|
|
|
It is not part of ownership hierarchy.
|
|
|
|
Examples:
|
|
|
|
* Outpatient Clinics
|
|
* Inpatient
|
|
* Emergency
|
|
* General Services
|
|
|
|
Used for:
|
|
|
|
* Display
|
|
* Filtering
|
|
* Analytics
|
|
|
|
Not used for routing ownership.
|
|
|
|
---
|
|
|
|
## Location Type
|
|
|
|
Retain:
|
|
|
|
* OP
|
|
* IP
|
|
* ER
|
|
* GENERAL
|
|
|
|
This becomes the preferred reporting dimension instead of string matching location names.
|
|
|
|
Example:
|
|
|
|
```python
|
|
class LocationType(models.TextChoices):
|
|
OP = "OP"
|
|
IP = "IP"
|
|
ER = "ER"
|
|
GENERAL = "GENERAL"
|
|
```
|
|
|
|
---
|
|
|
|
# Import Strategy
|
|
|
|
Department uniqueness:
|
|
|
|
```python
|
|
(category, department_name)
|
|
```
|
|
|
|
must create a single Department.
|
|
|
|
Never create duplicate Departments from repeated spreadsheet rows.
|
|
|
|
---
|
|
|
|
Sections:
|
|
|
|
```python
|
|
(department, section_name)
|
|
```
|
|
|
|
must be unique.
|
|
|
|
---
|
|
|
|
# Migration Plan
|
|
|
|
## Phase 1
|
|
|
|
Create:
|
|
|
|
* DepartmentCategory
|
|
* LocationType enums
|
|
|
|
Remove Area dependency.
|
|
|
|
---
|
|
|
|
## Phase 2
|
|
|
|
Rename:
|
|
|
|
OrgSubSection
|
|
|
|
to:
|
|
|
|
Section
|
|
|
|
Update:
|
|
|
|
* models
|
|
* serializers
|
|
* views
|
|
* APIs
|
|
* templates
|
|
* services
|
|
|
|
---
|
|
|
|
## Phase 3
|
|
|
|
Deprecate:
|
|
|
|
OrgSubSubSection
|
|
|
|
Backfill any valid data.
|
|
|
|
Remove references.
|
|
|
|
---
|
|
|
|
## Phase 4
|
|
|
|
Backfill all historical complaints.
|
|
|
|
Populate:
|
|
|
|
* department
|
|
* section
|
|
|
|
using LegacyHierarchyMapping.
|
|
|
|
Target:
|
|
|
|
100% coverage.
|
|
|
|
---
|
|
|
|
## Phase 5
|
|
|
|
Update routing services.
|
|
|
|
Replace all routing logic with:
|
|
|
|
```python
|
|
complaint.get_owner()
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 6
|
|
|
|
Update:
|
|
|
|
* Dashboards
|
|
* KPI services
|
|
* Reports
|
|
* Exports
|
|
* Analytics
|
|
|
|
to use:
|
|
|
|
* Department
|
|
* Section
|
|
* Department Category
|
|
|
|
only.
|
|
|
|
---
|
|
|
|
## Phase 7
|
|
|
|
Update forms.
|
|
|
|
Replace:
|
|
|
|
Location
|
|
→ Main Section
|
|
→ Sub Section
|
|
|
|
with:
|
|
|
|
Category
|
|
→ Department
|
|
→ Section
|
|
|
|
Section optional.
|
|
|
|
---
|
|
|
|
## Phase 8
|
|
|
|
Mark legacy hierarchy fields as deprecated.
|
|
|
|
Prevent all new writes.
|
|
|
|
Keep read-only until final cleanup release.
|
|
|
|
---
|
|
|
|
# Success Criteria
|
|
|
|
✓ Department is the primary accountable entity.
|
|
|
|
✓ Section is optional.
|
|
|
|
✓ Reporting uses Department Category.
|
|
|
|
✓ Routing uses Department / Section only.
|
|
|
|
✓ Legacy hierarchy is used only for historical mapping.
|
|
|
|
✓ Area is removed from operational design.
|
|
|
|
✓ OrgSubSubSection is removed.
|
|
|
|
✓ OrgSubSection is renamed to Section.
|
|
|
|
✓ Dashboards no longer depend on legacy hierarchy.
|
|
|
|
✓ Complaint ownership has a single source of truth.
|