---
id: WF-ACC-PRD-003
title: Delete a product and clear its confirmation template
trigger: A permitted caller deletes a product, with console confirmation before sending the request.
product_area: accounting
module: products
revision: 3
status: draft
design_intent: proposed
implementation_evidence: observed
inference: []
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
- CAP-ACC-PRD-006
moves:
- STATE-ACC-PRD-001
- STATE-ACC-PRD-003
- STATE-ACC-PRD-004
- STATE-ACC-PRD-005
crosses:
- SVC-ACC-PRD-products
idempotency: A repeat after removal returns not found. Remote clearing is documented as repeat-safe, but Accounting
  records no durable retry.
concurrency: Delete locks the product, then its setup, and checks current protection. Foreign keys arbitrate
  simultaneous component references and removal.
hops:
- n: 1
  actor_or_service: Console row actions
  action: Confirm in the row dialog and send deletion without a read-version condition.
  anchor: react-console:components/accounting-config/products/product-row-actions.tsx:ProductRowActions
  seam: sync
  on_failure: Keep the dialog open with the request failure.
  left_inconsistent: No product mutation has committed.
  observer: The person sees the dialog error.
  provenance: observed
  source: react-console:components/accounting-config/products/product-row-actions.tsx:ProductRowActions
- n: 2
  actor_or_service: Products persistence (one transaction)
  action: Lock the product and setup, check owner and delete protection, and remove the local product aggregate
    by cascade.
  anchor: services-accounting:modules/products/services/persistence/products.ts:deleteProduct
  seam: sync
  on_failure: Abort deletion for absent records, protection or recognized dependency refusals.
  left_inconsistent: The product and owned local records remain as committed.
  observer: The caller receives the refusal.
  provenance: test
  source: services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
    a product whose accounting setup is locked against deletes, leaving the product and its setup (DEF-ACT-0101)
- n: 3
  actor_or_service: Database references owned by other modules
  action: Allow dependent foreign keys to refuse deletion for ordinary product, discount-product and installment
    references.
  anchor: services-accounting:modules/products/services/persistence/product-refusals.ts:STILL_REFERENCED
  seam: sync
  on_failure: Translate ordinary invoice restrictions; unmapped discount and installment constraints may yield
    a server error.
  left_inconsistent: The rejected delete preserves all local records.
  observer: The caller sees the dependency explanation or generic failure.
  provenance: observed
  source: services-accounting:modules/products/services/persistence/product-refusals.ts:STILL_REFERENCED
- n: 4
  actor_or_service: Confirmation template workflow, then Platform communications
  action: After local deletion commits, request clearing of any owned Platform confirmation template.
  anchor: services-accounting:modules/products/services/confirmation-emails/confirmation-email-templates.ts:clearConfirmationTemplateOfDeletedProduct
  seam: sync
  on_failure: Catch and log cleanup failure and still return successful deletion.
  left_inconsistent: The product is removed but remote owned content may remain active.
  observer: Operators can find the product identity and failure in the cleanup log.
  provenance: test
  source: services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#leaves
    the delete standing when Platform cannot clear the template
branches: []
waits: []
evidence:
- 'services-accounting at f47cb263: modules/products/services/persistence/products.ts (deleteProduct), product-refusals.ts;
  services/confirmation-emails/confirmation-email-templates.ts; db/*.table.ts (cascades); docs/accounting/products.md
  (Lifecycle).'
- 'react-console at baf6dae3: components/accounting-config/products/product-row-actions.tsx.'
controls: [TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-DAT-001, TC-DAT-004, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-AVL-001, TC-AVL-004, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PRV-001, TC-PRV-002]
---

# Delete a product and clear its confirmation template

## What It Does

Deletion checks the current product, its setup protection and dependent references before removing the local aggregate. After commit, Accounting attempts cleanup of the product's owned confirmation template.

## The Flow

```mermaid
sequenceDiagram
  participant C as Client
  participant P as Products service
  participant D as Database
  participant T as Platform templates
  C->>P: Delete after confirmation
  P->>D: Lock product and setup
  P->>P: Check ownership and protection
  P->>D: Delete with dependency checks and cascades
  P->>D: Commit deletion
  opt Product owned a template
    P->>T: Clear template
    T-->>P: Success or cleanup failure
  end
  P-->>C: Deletion succeeded
```

## When Something Fails

Local refusals leave the product and owned records intact. Some dependency constraints lack a matching refusal translation and can surface as generic server errors. A cleanup failure is logged and does not reverse deletion or schedule a retry. Recreating the identity while stale owned content remains requires care.

## Open Design Questions

Template cleanup needs a recovery policy if operators cannot rely on logs alone. Dependency failures also need consistent explanations and direct proof.
