The first slice of the smart-archive project. Replaces the dumb DELETE client flow with a deliberate "look before you leap" pattern: - New columns on clients: archived_by, archive_reason, archive_metadata (jsonb capturing every decision made during archive, so restore can attempt reversal). Migration 0043. - client-archive-dossier.service builds a structured snapshot of "what's at stake" for a given client: pipeline interests, berths under offer (with next-in-line interests for the notification), yachts owned, active reservations, outstanding invoices, signed/in-flight Documenso envelopes, portal user, company memberships. Classifies the client as low-stakes or high-stakes based on pipeline stage (HIGH_STAKES_STAGES = deposit_10pct + later) so the bulk wizard knows which clients to prompt individually. - client-archive.service.archiveClientWithDecisions takes the operator's decisions and applies them in a single transaction. Persists the decision log into archive_metadata for restore. Auto-handles portal user revocation + company membership end-dating; everything else is caller-driven. Surfaces external cleanups (Documenso void) for the caller to queue. - client-restore.service.getRestoreDossier classifies each persisted decision as autoReversible / reversibleWithPrompt / locked based on the current state of the world (berth still available? new owner has active interests on the yacht? etc). restoreClientWithSelections applies reversals + un-archives the client. - 4 API routes wire the services to HTTP. The existing /restore endpoint is upgraded to use the smart restore but stays backwards-compatible: clients archived before this feature have no archive_metadata so the dossier returns empty, and a POST with no body just un-archives them — same as before. UI work + bulk variant + hard-delete + Documenso cleanup queueing land in follow-on commits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
31 lines
1.2 KiB
SQL
31 lines
1.2 KiB
SQL
-- Smart-archive feature — add columns that capture WHO archived a client,
|
|
-- WHY, and WHAT decisions they made along the way (for the restore
|
|
-- wizard to attempt reversal).
|
|
--
|
|
-- archived_at already exists (since the original schema). The three new
|
|
-- columns are all nullable and default-friendly so existing archived
|
|
-- rows backfill cleanly: their reason/metadata stays NULL, which the
|
|
-- service treats as "legacy archive (no smart-archive metadata)".
|
|
|
|
ALTER TABLE clients ADD COLUMN IF NOT EXISTS archived_by text;
|
|
ALTER TABLE clients ADD COLUMN IF NOT EXISTS archive_reason text;
|
|
ALTER TABLE clients ADD COLUMN IF NOT EXISTS archive_metadata jsonb;
|
|
|
|
-- archived_by FK is SET NULL on user delete so the audit trail outlives
|
|
-- staff turnover. Not VALID immediately to keep the lock window short;
|
|
-- VALIDATE after the column is in place.
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_constraint WHERE conname = 'clients_archived_by_fk'
|
|
) THEN
|
|
ALTER TABLE clients
|
|
ADD CONSTRAINT clients_archived_by_fk
|
|
FOREIGN KEY (archived_by) REFERENCES "user"(id)
|
|
ON DELETE SET NULL
|
|
NOT VALID;
|
|
END IF;
|
|
END$$;
|
|
|
|
ALTER TABLE clients VALIDATE CONSTRAINT clients_archived_by_fk;
|