PR #273 · pramaan-io/pramaan-functions · merged 2026-06-08
Deleting a client shouldn't orphan a matter.
What breaks without a guard
Hard delete orphans every matter that referenced this client.
The fix — Check the loans before pulling the book; mark withdrawn, never shred.
sequenceDiagram
autonumber
participant partner as Partner
participant service as Client Service
participant matters as Matters Library
participant catalog as Client Catalog
partner->>service: DELETE /clients/{id}
service->>matters: any open loans?
matters->>service: zero loans
service->>catalog: stamp withdrawn
catalog->>catalog: status=deleted, row retained
| Method | in → out | what it does |
|---|---|---|
_list_item | (client, usage_count) → ClientListItem | |
_detail_view | (client, usage_count) → ClientDetailView | |
_in_use_references | (client_id, firm_id) → tuple[InUseReference, ...] |
Service layer orchestrates soft-delete: delete_client guards the write, _in_use_references and _usage_count compute the matter signal, list views add canDelete.
| new method | signature |
|---|---|
_list_item | (client, usage_count) → ClientListItem |
_detail_view | (client, usage_count) → ClientDetailView |
_in_use_references | (client_id, firm_id) → tuple[InUseReference, ...] |
_usage_count | (db, client_id, firm_id) → int |
delete_client | (db, client_id) → None |
Alembic migration adding client delete guard schema for the deleted lifecycle state and in-use indexing.
| new method | signature |
|---|---|
upgrade | () → None |
downgrade | () → None |
revision : strdown_revision : str | Sequence[str] | Nonebranch_labels : str | Sequence[str] | Nonedepends_on : str | Sequence[str] | NoneREST router gains guarded DELETE /app/clients/{client_id} that surfaces pramaan.client.in_use when matters block the delete.
| new method | signature |
|---|---|
delete_client | (client_id, db, _) → Response |
Repository adds include_deleted flag so list queries hide soft-deleted rows from status=all and archived views.
include_deleted : boolClient DTOs add usage_count, is_in_use, and can_delete fields so the UI renders guard-aware delete affordances.
usage_count : intis_in_use : boolcan_delete : bool