Skip to content

Project quotations — expose delete affordance matching invoice delete

Why

Owner reported that a quotation could be created but not deleted like an invoice, and asked whether this was an RBAC issue or whether the web app did not currently allow quotation removal.

Diagnosis

  • The backend route already supports deletion: DELETE /api/projects/by-id/[projectId]/quotations.
  • The server-side helper already supports both paths:
  • pending quotations are hard-deleted;
  • converted quotations are soft-deleted when the quotation record carries quotationStatus: "Invoiced" and linkedInvoiceNumber.
  • This was not primarily an RBAC failure. The quotation route gates POST/PATCH/DELETE behind projects:detail:write@epl, while invoices use the dedicated projects:invoice:delete@epl delete permission.
  • The actual missing piece was UI exposure. ProjectShowApp had an invoice delete button in manage mode, but no equivalent quotation-row delete button or client handler.

What Changed

  • Added handleDeleteQuotation() to components/projects/ProjectShowApp.tsx.
  • Added a quotation-row delete button in manage mode, mirroring the invoice row behavior.
  • Pending unsaved quotation rows use the close icon to cancel creation.
  • Saved quotations call the existing quotation DELETE API and refresh the quotation list from the response.

Verification

  • NODE_OPTIONS=--max-old-space-size=8192 ./node_modules/.bin/tsc --noEmit passes.

Attestation

  • Read AGENTS.md.
  • Checked task board by scope (quotation, project quotation, delete/remove); no existing task covered this missing UI affordance.
  • Source: Codex local session · /Users/gutchumi/dev/ArtifactoftheEstablisher-codex-invoice-hotfix.