# ADR-045: Transaction Edit/Delete Uses Approval Queue for Non-Admins, Direct Apply for Admins

## Status

Accepted, 2026-06-22.

## Status History

```yaml
status_history:
  - date: 2026-06-22
    status: Proposed
    changed_by: hkl
    reason: Formalising two-path edit/delete pattern in Custodian PWA
    changed_via: adr-kit (360lm)
  - date: 2026-06-22
    status: Accepted
    changed_by: hkl
    reason: edit_requests table + RPCs live; admin direct-apply and non-admin queue both active
    changed_via: adr-kit (360lm)
```

## Context

Custodian transactions (transfers, receipts) record real financial events. Allowing anyone to edit or delete a posted transaction creates an audit and reconciliation risk. However, genuine errors occur (wrong amount, wrong payee) and must be correctable. The solution must balance correctability with auditability. Two different user types have different needs: non-admin custodians submitted the transaction and may need corrections reviewed; admins (finance supervisors) need to correct errors immediately without waiting for approval.

DB triggers handle wallet balance updates on INSERT but do NOT automatically reverse on UPDATE or DELETE — wallet impact must be explicitly reversed in the application.

## Decision

Non-admins submit edit/delete requests to `custodian.edit_requests` table via:
- `submit_edit_request(transaction_id, proposed_changes, reason)` → creates a pending request.

Admin reviews via:
- `approve_edit_request(request_id)` → applies the change, reverses wallet impact explicitly, marks approved.
- `reject_edit_request(request_id, rejection_reason)` → dismisses the request.

Admins can bypass the queue entirely via:
- `direct_edit_transaction(transaction_id, changes)` — applies immediately, reverses wallet impact, logs in audit trail.
- `direct_delete_transaction(transaction_id)` — deletes and reverses wallet impact, logs in audit trail.

**Critical:** wallet impact reversal (balance adjustment) must be done explicitly in the RPC — existing INSERT triggers do not fire on UPDATE/DELETE. Missing the explicit reversal leaves wallet balances incorrect after an edit.

**Decision Maker:** hkl

## Alternatives Considered

- **All edits go through approval queue (no admin bypass).** Rejected: admins waiting for self-approval is operationally nonsensical; for urgent corrections (e.g. wrong amount entered at end of day), the queue adds delay without audit benefit for admin actions (admin actions are logged directly).
- **All edits are direct (no approval queue).** Rejected: non-admin custodians could silently correct errors without supervisor oversight; the audit trail would show only the final state, not the original + reason for correction.
- **Soft-delete + versioning (keep all historical states).** Rejected: adds schema complexity (version table, current-pointer); the existing `edit_requests` + audit log achieves the same tracability without version traversal; PostgREST queries would require always filtering to the current version.
- **Allow UPDATE/DELETE on transactions via PostgREST directly (no RPCs).** Rejected: wallet balance would not be updated (triggers only fire on INSERT); every edit would create a balance discrepancy requiring manual correction.

## Consequences

**Positive:**
- Non-admin edits are auditable — request reason, proposed change, and approver all recorded.
- Admin edits are immediate (no self-approval queue) but still logged.
- Wallet balances remain correct because RPCs always reverse impact explicitly.
- Clear separation of concerns: queue for non-admins, direct apply for admins.

**Negative / Trade-offs:**
- Non-admins must wait for admin approval to see a correction applied — not suitable for high-urgency corrections.
- Two code paths (queue + direct) to maintain and test.
- Every new type of editable transaction must implement wallet reversal in the direct-apply RPCs.

**Risks and mitigations:**
- Wallet reversal omitted in a new RPC: mitigated by this ADR documenting the requirement; test suite should verify balance state before and after every edit/delete RPC.
- Admin uses direct-delete to hide a transaction (no audit): mitigated by `direct_delete_transaction` writing to an audit log that is append-only (no delete grants on audit log for web_anon).

## Related Decisions

- ADR-033 (atomic impress deduction) — same principle: financial operations must be atomic RPCs, not split calls.
- ADR-007 (SECURITY DEFINER) — edit RPCs that write wallet/balance tables require SECURITY DEFINER.

## References

- `memory/dbt_custodian.md` — edit_requests table, RPCs: submit/approve/reject/direct-edit/direct-delete
- `finance/custodian/index.html` — edit request submission and admin review flows
