---
id: WF-ACC-PRD-004
title: Save a domain product inside the domain's transaction
trigger: A domain caller begins a transaction for a common product and its specialized record.
product_area: accounting
module: products
revision: 3
status: draft
design_intent: confirmed
origin: 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#D-2 (Andrew,
  2026-10-18: A then B, the service-side transaction through accounting.save_product)'
implementation_evidence: observed
inference: []
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
- CAP-ACC-PRD-007
- CAP-ACC-PRD-005
moves:
- STATE-ACC-PRD-001
- STATE-ACC-PRD-002
- STATE-ACC-PRD-004
crosses:
- SVC-ACC-PRD-products
idempotency: Creating under an existing key fails. Replacement and retirement compare current versions. The
  caller owns retry policy; the product functions retain no replay records.
concurrency: The domain function locks its product inside the caller's transaction and refuses stale edits.
  That transaction must continue through extension writing.
hops:
- n: 1
  actor_or_service: Domain service
  action: Begin the domain transaction under an application or system role with workspace and acting-user session
    settings.
  anchor: "services-accounting:docs/accounting/products.md#The domain write path: `accounting.save_product` and `accounting.retire_product` (D-2 B, ADR §5)"
  seam: sync
  on_failure: Refuse missing workspace or unsupported role before product persistence.
  left_inconsistent: The function has written no product.
  observer: The domain receives the session-context refusal.
  provenance: documented
  source: services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#The write
    path is a database function the domain calls in its own transaction (D-2 B)
- n: 2
  actor_or_service: Accounting save function (security definer)
  action: Derive the application from the database session and match the kind owner, preserving ownership checks
    even for execution-capability members.
  anchor: services-accounting:db/migrations/20261019201047_product_lock_and_held_bundle_components.sql:accounting.save_product
  seam: sync
  on_failure: Refuse a mismatched owner without committing changes.
  left_inconsistent: No product change commits.
  observer: The domain receives the owner refusal.
  provenance: test
  source: 'services-accounting:modules/products/tests/integration/db/save-product-function.integration.spec.js#refuses
    Accounting’s own role writing a membership package: the owner rule names the owner, the type and the caller'
- n: 3
  actor_or_service: Accounting save function
  action: Call the save function with a supplied key and required replacement version; validate common data,
    lock the product, keep supported omitted scalars and replace lists.
  anchor: services-accounting:db/migrations/20261019201047_product_lock_and_held_bundle_components.sql:accounting.save_product
  seam: sync
  on_failure: Raise the appropriate SQL input, state, absence or access error; the caller must abort.
  left_inconsistent: The domain must roll back its transaction.
  observer: The caller receives the function error.
  provenance: test
  source: services-accounting:modules/products/tests/integration/db/save-product-function.integration.spec.js#replaces
    the row and every collection as a whole, keeping an omitted property and clearing a null one
- n: 4
  actor_or_service: Domain service
  action: Save the extension under the common key and commit both records as required by the documented domain
    boundary.
  anchor: services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#The write
    path is a database function the domain calls in its own transaction (D-2 B)
  seam: sync
  on_failure: The caller must abort both writes after extension failure; actual cross-service execution is
    unproved.
  left_inconsistent: The design intends no partial commit, but the real domain rollback test is missing.
  observer: The domain owns the outcome; execution through its consumer is unverified.
  provenance: unknown
  source: FND-ACC-PRD-027
branches:
- Domain allocation defaults absent prices to zero and sums without quantity; HTTP checks explicit weighted
  amounts.
- Domain saving accepts held audiences after the Commerce register exists, unlike HTTP saving.
- Domain replacement requires an active kind; a readable audience register admits active or held choices without
  a separate built-in exemption, and denied access bypasses the check.
- Domain functions expose neither reactivation nor deletion.
waits: []
evidence:
- 'services-accounting at f47cb263: db/migrations/20261018200018_products_shared_base.sql, 20261019201047_product_lock_and_held_bundle_components.sql
  (save_product, retire_product, calling_service); docs/accounting/products.md (The domain write path); docs/migrations/accounting-modernization/products-shared/README.md
  (D-2).'
- 'services-accounting at f47cb263: modules/products/tests/integration/db/save-product-function.integration.spec.js.'
controls: [TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, 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-PI-001, TC-PI-002, TC-PI-003, TC-PI-005]
---

# Save a domain product inside the domain's transaction

## What It Does

Accounting supplies database functions for saving and retiring a domain-owned common product. The domain supplies session context, the enclosing transaction and the extension write. The cited product decision requires common and specialized records to share that commit boundary.

## The Flow

```mermaid
sequenceDiagram
  participant H as Domain service
  participant F as Accounting database function
  participant D as Database
  H->>D: Begin with workspace, actor and role
  H->>F: Save common product
  F->>F: Check caller and configuration
  F->>D: Lock and replace local product data
  F-->>H: Common product result
  H->>D: Write specialized extension, expected caller step
  H->>D: Commit both, expected caller step
```

## When Something Fails

The functions raise errors for invalid context, ownership, input or state. The domain must abort its transaction if the later extension write fails. The available tests establish Accounting's function behavior but do not execute that complete failure through a real domain, so the caller hop remains unverified. Owned-template work and accounting setup use separate API operations.

## Open Design Questions

The HTTP and database paths differ in omitted values, kind activity, audience admission and bundle allocation. Product must choose the shared contract, and an executable domain test must prove rollback after an extension failure.
