11-registration-and-onboardingwave: W6filled5 citations

Organization Management — Clinics, Departments and Locations

Audiences: clinical-buyer, developer, internal

Organization Management — Clinics, Departments and Locations

After a clinic is provisioned by the tenant provisioner, all day-to-day configuration of its structure — departments, locations, healthcare services, operating hours, branding, and AI-model settings — is managed through the organization domain in Dudoxx HMS.

Business Purpose

Clinic administrators need to configure their organization without engineering support: add a radiology department, open a second location, upload a logo, set their AI diagnostic model preference, or edit emergency contact procedures. The organization management domain handles all post-provisioning configuration. It deliberately cannot create organizations (that is the tenant provisioner's exclusive responsibility) — it can only read, update, and enrich them. This separation prevents accidental partial-provisioning of FHIR partitions or MinIO buckets through the regular REST API.

Audiences

  • Investor: Multi-location and multi-department support allows Dudoxx to address group practices and hospital networks, not just single-physician clinics — a significant market expansion.
  • Clinical buyer (doctor/nurse/receptionist): Clinic admins configure departments (e.g. Cardiology, Dermatology), assign practitioners to locations, set opening hours, and upload the clinic logo — all through the admin portal without any code deployment.
  • Developer/partner: Organization data is aggregated from two sources: the Prisma Organization model (settings, branding, config) and the FHIR Organization resource (clinical references). OrganizationsService merges both. Never call POST /organizations for creation — use POST /super-admin/tenants.
  • Internal (ops/support): Organization settings changes are audited via SettingsAuditService. The organizations-ddxllm controller exposes an AI-context descriptor endpoint consumed by TUCAN tools for org-aware clinical suggestions.

Architecture

diagram
organization/
├── organizations/              Core org CRUD + settings + FHIR aggregation
│   ├── organizations.service.ts      Reads Prisma + FHIR; delegates creation to TenantService
│   ├── organizations.controller.ts   GET/PATCH /organizations
│   ├── organizations-settings.service.ts   Per-org settings CRUD
│   ├── organizations-settings.controller.ts
│   ├── settings-audit.service.ts     Tracks settings changes to audit_trail
│   ├── organizations-logo.service.ts  Logo upload → MinIO {slug}-branding bucket
│   ├── organizations-ddxllm.controller.ts  AI context descriptor for TUCAN
│   └── organizations.tokens.ts       ORGANIZATION_FACADE DI token
│
├── departments/                Department CRUD (per-organization)
│   ├── departments.service.ts
│   └── departments.controller.ts
│
├── locations/                  Physical/virtual locations CRUD
│   ├── locations.service.ts
│   └── locations.controller.ts
│
├── healthcare-services/        HealthcareService definitions (FHIR HealthcareService)
│   └── healthcare-services.service.ts
│
├── settings/                   Global org settings schema + validation
├── super-admin/                SUPER_ADMIN-only ops (tenant CRUD, stats, platform settings)
├── inventory/                  Clinic inventory management
└── hr/                         Human resources (employees, contracts, timesheets, leave)

OrganizationsService aggregates data from three sources:

  1. Prisma (ddx_api_main Organization model) — app-specific settings, branding, timezone, locale
  2. FHIR (ORGANIZATION_FACADE / FHIR_CLIENT) — FHIR Organization resource with clinical references
  3. Computed stats — aggregated member counts, appointment stats via OrganizationStatsResponseDto

Organization creation is exclusively TenantService.createTenant() via POST /super-admin/tenants. The OrganizationsService source code documents this explicitly: "NOTE: Organization creation has been REMOVED from this service." (organizations.service.ts:59-60).

Tech Stack & Choices

ConcernChoiceRationale
Prisma modelddx_api_main OrganizationSettings, branding, slug, timezone; organizationId is the multi-tenant FK across all models
FHIR aggregationORGANIZATION_FACADE + FHIR_CLIENT tokensResourceFacade<OrganizationResource> for FHIR writes; IFhirClient for reads
Logo storageMinIO {slug}-branding bucket via OrganizationsLogoServiceConsistent bucket strategy; presigned upload URL pattern
Settings auditSettingsAuditServiceaudit_trail in ddx_api_logEvery settings change tracked with oldValue + newValue
AI contextorganizations-ddxllm.controller.tsExposes OrganizationDescriptor for TUCAN tools needing org-level clinical context
DepartmentsPrisma Department model (separate table)One-to-many per org; practitioners are assigned to departments
LocationsPrisma Location model + FHIR Location resourcePhysical/virtual clinic addresses; used by appointment booking for location-based slots

Data Flow

Org admin updates settings:

  1. Admin PATCHes /organizations/:id/settings with UpdateOrgSettingsRequestDto.
  2. OrganizationsSettingsController delegates to OrganizationsSettingsService.update().
  3. SettingsAuditService.record() reads old settings, writes diff to audit_trail.
  4. OrganizationsSettingsService calls prisma.organizationSettings.upsert().
  5. AuditService.logAudit() captures the change in ddx_api_log.

Org admin uploads logo:

  1. Admin POSTs multipart to /organizations/:id/logo.
  2. OrganizationsLogoService calls StorageService.upload() → MinIO {slug}-branding bucket.
  3. Returns presigned URL; Organization.logoUrl updated in Prisma.

TUCAN reads org context:

  1. organizations-ddxllm.controller.ts returns OrganizationDescriptor JSON (AI-optimized representation of the org's departments, services, and settings).
  2. TUCAN tools inject this descriptor as clinical context for diagnosis suggestions.

Dept/location CRUD:

  • POST /departmentsDepartmentsService.create()prisma.department.create() scoped by organizationId.
  • POST /locationsLocationsService.create()prisma.location.create() + optional FHIR Location resource.

Implicated Code

  • ddx-api/src/organization/organizations/organizations.service.ts:41OrganizationsService; aggregates Prisma + FHIR data; note at :59-60 documents that org creation was removed from this service (use POST /super-admin/tenants)
  • ddx-api/src/organization/organizations/settings-audit.service.ts:64SettingsAuditService; diff-based audit of settings changes to audit_trail
  • ddx-api/src/organization/departments/departments.service.ts:39DepartmentsService; per-org department CRUD scoped by organizationId
  • ddx-api/src/organization/locations/locations.service.ts:34LocationsService; location CRUD with optional FHIR Location resource sync
  • ddx-api/src/organization/organizations/organizations-settings.service.tsOrganizationsSettingsService; settings CRUD with upsert pattern
  • ddx-api/src/organization/organizations/organizations-logo.service.tsOrganizationsLogoService; MinIO branding bucket upload
  • ddx-api/src/organization/organizations/organizations-ddxllm.controller.ts — AI context descriptor endpoint for TUCAN
  • ddx-api/src/organization/healthcare-services/healthcare-services.service.tsHealthcareServicesService; FHIR HealthcareService resources

Operational Notes

  • Never use POST /organizations to create: This endpoint exists for reads only post-migration. All creation goes through POST /super-admin/tenants. Violating this leaves the tenant without a FHIR partition and without MinIO buckets. This is listed as a forbidden pattern in ddx-api/CLAUDE.md.
  • Slug is the immutable routing key: Once a tenant slug is set, changing it requires updating MinIO bucket names, FHIR X-Clinic-ID header routing, and all cached references. Treat slugs as immutable post-provisioning.
  • Settings vs. OrganizationSettings: There are two settings tables — Organization (top-level fields like timezone, defaultLocale, primaryColor) and OrganizationSettings (a separate joined model for extended config). OrganizationsSettingsService manages the latter.
  • AI/LLM model override: organizations-ddxllm.controller.ts returns an org-level AI model preference that TUCAN tools respect. This allows per-clinic LLM model selection without code changes.
  • FHIR Organization resource: Automatically created by the tenant provisioner at onboarding. OrganizationsService reads it via FHIR_CLIENT to assemble OrganizationSummaryResponseDto. If HAPI FHIR is unavailable, the summary endpoint degrades gracefully (Prisma data returned, FHIR stats empty).
  • Tenant Provisioner — the only correct way to create a new organization; provisions FHIR partition + MinIO buckets
  • User Management — users are always scoped to an organization; org must exist before staff can be created
  • FHIR Partitions & Tenancy — FHIR Organization resource is created during provisioning and read by OrganizationsService