---
id: WF-ACC-PRD-001
title: Create or replace a product with its owned settings
trigger: A console caller submits a new product or a replacement carrying its read version.
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-002
- CAP-ACC-PRD-004
- CAP-ACC-PRD-005
- CAP-ACC-PRD-008
- CAP-ACC-PRD-010
- CAP-ACC-PRD-011
- CAP-ACC-PRD-012
- CAP-ACC-PRD-013
- CAP-ACC-PRD-014
moves:
- STATE-ACC-PRD-001
- STATE-ACC-PRD-002
- STATE-ACC-PRD-004
crosses:
- SVC-ACC-PRD-products
idempotency: Creation has no replay token and a repeated key collides; rereading that key can resolve a lost
  response. Successful replacement makes the old version stale. Unchanged template content is documented to
  avoid another publication.
concurrency: The replacement holds its product lock through template work and response assembly. Setup operations
  use the same lock and their own version. Concurrent creates rely on key uniqueness rather than locking an
  absent row.
hops:
- n: 1
  actor_or_service: API route and request mapper
  action: Check console authority and product permission, validate the body, refuse workspace or path-key mismatches,
    discard derived values and retain explicit lock input.
  anchor: services-accounting:modules/products/aws/api/products-api-mapper.ts:mapReplaceProductRequest
  seam: sync
  on_failure: Refuse the request before persistence if access or boundary checks fail.
  left_inconsistent: No local write has started.
  observer: The client receives the boundary error.
  provenance: observed
  source: services-accounting:modules/products/aws/api/products.routes.ts:productsRoutes
- n: 2
  actor_or_service: Products persistence (one transaction)
  action: Choose a new key on creation; on replacement lock the product, reject retirement and unreduced update
    protection, and compare immutable values.
  anchor: services-accounting:modules/products/services/persistence/products.ts:updateProduct
  seam: sync
  on_failure: Abort locally and report validation, absence or conflict.
  left_inconsistent: The previous local record remains committed.
  observer: The client receives the persistence error.
  provenance: test
  source: services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#refuses
    a locked product replaced without stating sys_locked, or with the same lock
- n: 3
  actor_or_service: Products persistence and rules
  action: Read Books, Commerce, CRM and Platform references and run ownership, product and feature checks until
    the first service refusal.
  anchor: services-accounting:modules/products/services/persistence/products.ts:assertProductRules
  seam: sync
  on_failure: Stop validation and roll back the local transaction.
  left_inconsistent: Previously committed configuration is unchanged.
  observer: The client receives the first rule error.
  provenance: test
  source: services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
    the old prices and components when a rule refuses the replacement
- n: 4
  actor_or_service: Products persistence
  action: Write the versioned product and complete price, component, task and discount lists, removing previous
    lists on replacement.
  anchor: services-accounting:modules/products/services/persistence/products.ts:writeCollections
  seam: sync
  on_failure: Roll back failed statements and translate recognized database refusals.
  left_inconsistent: Previously committed product and lists remain intact.
  observer: The client receives a mapped refusal or generic failure.
  provenance: test
  source: services-accounting:modules/products/tests/integration/services/persistence/products.integration.spec.js#leaves
    the old prices and components when a new component is refused by the database after the old ones were removed
- n: 5
  actor_or_service: Confirmation template workflow, then Platform communications
  action: For an explicit owned-template change, ask Platform independently to merge or clear content before
    local commit; omission makes no call.
  anchor: services-accounting:modules/products/services/confirmation-emails/confirmation-email-templates.ts:applyConfirmationTemplateChange
  seam: sync
  on_failure: Translate content validation errors and propagate other remote failures, aborting Accounting
    without proving Platform rollback.
  left_inconsistent: A successful remote write with a lost answer may coexist with unchanged local state.
  observer: The client sees failure; the port supplies actor and correlation context.
  provenance: test
  source: services-accounting:modules/products/tests/integration/services/persistence/products.features.integration.spec.js#rolls
    the product back when Platform cannot save the template
- n: 6
  actor_or_service: Database commit
  action: Read the saved collections, setup summary and tax answer within the transaction, then commit and
    return the assembled product.
  anchor: services-accounting:modules/products/services/persistence/products.ts:toProducts
  seam: sync
  on_failure: Abort Accounting if response reads or commit fail, without undoing a completed remote change.
  left_inconsistent: Platform may have changed content for a product write that did not commit.
  observer: The client sees failure; no dedicated reconciliation alert is declared.
  provenance: observed
  source: services-accounting:modules/products/services/persistence/products.ts:createProduct
branches:
- HTTP omission keeps confirmation but disables other omitted selling settings.
- Kinds that prohibit prices or selling settings refuse those inputs.
waits: []
evidence:
- 'services-accounting at f47cb263: modules/products/aws/api/products.routes.ts, products-api-mapper.ts; services/persistence/products.ts
  (createProduct, updateProduct, assertProductRules, assertFeatureRules, writeCollections, writeFeatureCollections,
  templateChangeOf); services/confirmation-emails/confirmation-email-templates.ts; services/persistence/product-refusals.ts;
  docs/accounting/products.md (Errors, Features).'
- 'services-accounting at f47cb263: tests/integration/services/persistence/products.integration.spec.js, products.features.integration.spec.js.'
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-002, 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-PI-005, TC-PRV-001, TC-PRV-002]
---

# Create or replace a product with its owned settings

## What It Does

The products API writes the local catalogue record and its owned lists in one transaction. When confirmation content changes, it calls Platform independently before reading the saved response and committing Accounting.

## The Flow

```mermaid
sequenceDiagram
  participant C as Console client
  participant P as Products service
  participant D as Database
  participant T as Platform templates
  C->>P: Submit product and replacement version
  P->>D: Lock existing product and read references
  P->>P: Validate common configuration
  P->>D: Write product and owned collections
  opt Explicit owned-template change
    P->>T: Merge or clear independent template
    T-->>P: Result
  end
  P->>D: Read response data inside transaction
  P->>D: Commit local changes
  P-->>C: Return saved product
```

## When Something Fails

Validation and database failures preserve the preceding local state. Remote handling and response-read failures also abort Accounting, but cannot roll back a Platform change already committed. No workflow retry or compensation closes that gap. The product lock stays held while the remote call completes.

## Open Design Questions

Product must assign recovery ownership for mismatched templates and records and choose the intended omission contract across HTTP and domain writes.
