Admin Retirement vs Hard Delete - hmislk/hmis GitHub Wiki

Admin — Retirement vs Hard Delete

When the UI says "Delete", HMIS performs a soft retire — the record is flagged as retired, hidden from active lists, but kept in the database. This is intentional and protects historical data. Hard delete (physically removing the row) is not exposed in the UI; when it is needed, it must be done at the database level. This article explains when each is appropriate and the implications of crossing the line.

The end-user view is Practitioner — Deactivate and Bulk Delete.

What soft retire actually does

When a user clicks Delete on a doctor, consultant, or staff record:

Field on the record New value
retired true
retirer The WebUser who clicked Delete
retiredAt Current timestamp

The record is otherwise unchanged. Every linked entity (bills, sessions, working times, signatures, payroll, etc.) keeps pointing at the same primary key. Lookups that filter retired = false skip the record, which is most active dropdowns and lists.

Soft retire is reversible by setting retired back to false at the database level. There is no in-app reactivate button.

When soft retire is the right choice

Almost always. Soft retire is appropriate for:

  • A doctor or consultant who has left the institution.
  • A staff member whose role no longer exists.
  • A duplicate record that arose from a typo, provided no bills, sessions, or other linked data exist against the duplicate.
  • Test records you created during installation that have no linked transactions.

The decision rule: if any business data points at this record, soft retire only. The historic linkage matters more than the disk space saved.

When hard delete might be considered

Hard delete is appropriate only when all of the following are true:

  1. The record has no linked bills (Bill.staff, Bill.toStaff, fee lines, etc.).
  2. No working times exist against the record.
  3. No payroll entries (StaffEmployment, StaffBasic, etc.) reference it.
  4. No SMS history references it.
  5. No WebUser links to it.
  6. No channel sessions or bookings reference it.
  7. The record was clearly created in error (e.g., typo during an upload, accidentally clicked Add twice).

If any of these is not met, do not hard delete — use soft retire and accept the inactive row.

How to hard delete safely

There is no menu option for this. The process is:

  1. Take a database backup. Mandatory.
  2. Confirm orphaned status. Run JPQL or SQL against the relevant linked tables to confirm there are zero references. Sample queries:
    SELECT COUNT(*) FROM bill WHERE staff_id = :id OR to_staff_id = :id;
    SELECT COUNT(*) FROM bill_fee WHERE staff_id = :id;
    SELECT COUNT(*) FROM working_time WHERE staff_id = :id OR ended_by_id = :id;
    SELECT COUNT(*) FROM web_user WHERE staff_id = :id;
    SELECT COUNT(*) FROM person_institution WHERE staff_id = :id;
    
    All must return 0.
  3. Delete the linked Person row only if it is not referenced by another Staff (one Person can be reused by another Staff record in some edge cases).
  4. Delete the row. DELETE FROM staff WHERE id = :id;
  5. Run smoke tests. Open the lists that should have included the record; confirm everything still renders.

Use the database administrator's normal change-control process. Hard-deleting from a Production database mid-day without a transaction window is not safe.

What never to hard delete

Record class Why not
Anyone who has issued or signed a bill Bills include the staff name resolved live from the staff record. Hard-deleting orphans the bill display.
Anyone who has had a payroll entry Payroll history loses its anchor.
Anyone with a working time link The shift would still exist but with no staff.
Retired staff older than six months By now they are part of the historical record; let them stay as soft-retired.

Re-instating a retired record

There is no in-app reactivation:

  1. Backup first.
  2. Run UPDATE staff SET retired = false, retirer_id = NULL, retired_at = NULL WHERE id = :id;.
  3. Open the staff list and confirm the record reappears.

If the record had been retired for a long period and other people have created new records with similar names, decide whether to keep the older record or the newer one as canonical. There is no merge — pick one and retire the other again.

Bulk hard-delete is not supported

The Bulk Delete Staff screen performs soft retire only on every selected row, despite the name. If you need to remove many records physically, the only option is a scripted database operation following the per-record process above.

Why not just expose a hard-delete button?

HMIS chooses soft delete for the practitioner module for three reasons:

  1. Historical bills, sessions, and reports must continue to display correct names long after a doctor leaves.
  2. Audit and compliance often require that records be retained, not erased.
  3. The soft-delete pattern allows reactivation without restoring from backup, which is operationally safer.

A future version of HMIS may expose a database administration tool with a guarded hard-delete; for now, the responsibility is operational.

Related articles

Back to Admin — Practitioner Module Configuration Overview