Skip to content

WHO device lifecycle gap — implementation checklist ​

Ovahol's data model was compared against the WHO medical device lifecycle model described in Inventory and maintenance management information system for medical devices (WHO, 2025) — see the publication. WHO frames the device lifecycle as eight stages: procurement & selection, installation, initial inspection/commissioning, user training, performance monitoring, routine maintenance, post-market surveillance & QA, and decommissioning & disposal.

Ovahol already has strong coverage for training, routine maintenance (work orders + PM schedules), and decommissioning/disposal (decommissioning review → disposition decision → disposal record is more granular than WHO's own description). This checklist tracks the remaining gaps, ordered by priority.

Priority 1 — Performance monitoring (inspection & calibration records) — done ​

This was the clearest gap. WHO describes periodic inspection as checks run at a frequency driven by device risk classification, feeding into a next-due date. This checklist's boxes were still unchecked, but auditing the actual code (2026-08-31, on branch feat/inspection-calibration) found the feature fully built in an earlier session; the doc was just never updated.

  • [x] Entity shape: single PerformanceCheck entity with a check_type discriminator (INSPECTION | CALIBRATION) (internal/database/tenant_migrations/versions/041_performance_check.sql).
  • [x] Module ownership: maintenance, alongside PMSchedule/WorkOrder/ FaultReport.
  • [x] Schema: device_record_id FK, check_type, trigger, performed_at, performed_by, result, visual/functional/safety/software-validation check booleans, calibration fields (reference_standard, baseline_value, adjustment_notes, verification_value, certificate_number), next_due_date, resulting_work_order_id. Goes beyond this checklist's originally-scoped three check categories — see the "Refinements" note in docs/who-maintenance-cmms-gap-checklist.md, which is also already satisfied by this same schema.
  • [x] Repository: internal/modules/maintenance/repository/performance_check.repository.go + embedded sql/performance_check.sql, same QueryLoader/ TenantRepository pattern as the rest of the module.
  • [x] Service + DTO: full Create/Get/List/Update/Delete/GetOverdue (service/performance_check.go, dto/performance_check.go).
  • [x] Handler + routes, RBAC-gated (routes/performance_check.go: read roles include RoleFacilityAdmin/RoleEngineeringManager/RoleEngineeringTech/ RoleOrgAdmin, write narrower).
  • [x] Link failed checks to corrective action: GenerateWorkOrder service method, exposed at POST /performance-checks/:id/generate, mirroring FaultReportService.ConvertToWorkOrder.
  • [x] Frontend: list/detail/create views (frontend/src/features/tenant/performance-check/). Dashboard card was the one actually-missing piece — added PerformanceCheckCard.vue (overdue inspections/calibrations count + top overdue list) to the facility dashboard, fed by a new overdueChecks/fetchOverdueChecks state on performanceCheckStore.
  • [x] Export: ListPerformanceChecksForExport on the maintenance facade, wired into deviceexport/service/export.go's lifecycle export.
  • Note: check_type/trigger/result ended up as fixed CHECK constraint enums rather than lookup-driven tables — a deliberate simpler choice, not a gap.

Priority 2 — Installation record — done ​

WHO treats installation as its own stage: site-readiness checks (power, ventilation, network, structural requirements) before the device is physically installed and connected. Ovahol previously only had device_record.installation_date — a single timestamp, no structured record of what was checked or confirmed.

  • [x] Went with the lightweight option: site_readiness_confirmed, installed_by, connections_verified (plus installed_by's resolved name) added directly to device_record (internal/database/tenant_migrations/versions/045_installation_record.sql), not a full InstallationRecord entity — matches the doc's own "don't over-build this one" note, and stays distinct from the existing DeviceCommissioningEvent entity (which covers the later "goes into service" checklist, not physical installation). installed_by is a facility-member picker (same facilityService.listMembers pattern used elsewhere), not free text, matching the created_by/reviewed_by/submitted_by convention used throughout the rest of the schema.
  • [x] Extended for external installers: installed_by only fits facility staff (it's a core_public.users FK), but installation is often done by a vendor/supplier technician instead. Added installer_name (free text — external techs won't have a system account) and installer_vendor_id (FK core_public.vendor) to device_record (internal/database/tenant_migrations/versions/046_external_installer.sql), mirroring the exact vendor_id pattern RefurbishmentRecord already uses (SQL LEFT JOIN core_public.vendor, no new cross-module facade needed). Frontend vendor options are fetched via counterpartyApi.listVendors, the same call the refurbishment dialogs use.

Priority 3 — Post-market surveillance / regulatory reporting — done ​

Ovahol already had decent coverage here relative to WHO's description: IncidentReport.reportable_to_regulator and RecallNotice cover the internal tracking. What was missing is the outbound half WHO describes — tracking that a report was actually filed, not just that it should be.

  • [x] Added regulator-submission tracking to IncidentReport: regulator_name, report_reference, submitted_at, submitted_by (tenant migration 044). Implemented as a dedicated action — SubmitIncidentReportToRegulator (internal/modules/lifecycle/service/asset_lifecycle.go), exposed at POST /incidents/:id/submit-to-regulator — rather than folded into UpdateIncidentReportRequest, matching the existing acknowledge/close-recall convention for discrete, audited workflow actions. Submitting also forces reportable_to_regulator = TRUE for consistency. Surfaced on IncidentDetail.vue (a "Submit to Regulator" action + display fields) and added to the Excel lifecycle export's "Incident Reports" sheet.
  • [x] Manufacturer notification tracking. Added manufacturer_name, manufacturer_reference, manufacturer_notified_at, manufacturer_notified_by to IncidentReport (internal/database/tenant_migrations/versions/049_incident_manufacturer_notification.sql), mirroring the regulator-submission columns field-for-field — a device incident often needs the manufacturer notified separately from the regulator (e.g. to trigger a field safety corrective action). Same dedicated-action pattern: NotifyIncidentManufacturer (internal/modules/lifecycle/service/asset_lifecycle.go), exposed at POST /incidents/:id/notify-manufacturer, gated by the same management-only role restriction as the regulator-submission action. Surfaced on IncidentDetail.vue (a "Notify Manufacturer" action + display fields, independent of reportable_to_regulator since a manufacturer notification doesn't require regulator reportability) and added to the Excel lifecycle export's "Incident Reports" sheet.

Priority 4 (deferred) — Predictive maintenance ​

WHO calls out IoT/AI-driven predictive maintenance as an emerging practice layered on top of performance monitoring. Ovahol has no sensor/IoT integration and this is a large, separate capability — not worth scoping in detail until Priority 1 (performance monitoring) exists, since predictive maintenance is meaningless without the underlying inspection/calibration history to predict from.

  • [ ] Once Priority 1 ships and has real data, consider a lightweight first step: simple trend-based due-date suggestions from accumulated inspection/calibration results, short of any actual ML/sensor integration.

Explicitly out of scope ​

  • Procurement needs-assessment and evidence-based device selection (WHO stage 1's pre-purchase decision process) — this is a health-system planning/finance concern, not equipment-management data. Ovahol's procurement module already covers the acquisition transaction itself (vendor, receipt, acceptance approval); the selection decision upstream of that is a different kind of tool.

Ovahol developer documentation