---
id: CAP-ACC-PRD-018
title: Maintain the tax default for a product kind
feature: FEAT-ACC-PRD-04
product_area: accounting
module: products
revision: 5
status: draft
product_intent: out-of-scope
implementation_evidence: observed
supersedes:
  - react-console acceptance inventory CAP-ACC-PRD-016
  - react-console acceptance inventory RUL-ACC-PRD-047
  - react-console acceptance inventory RUL-ACC-PRD-053
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#D-7'
  - 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md#D4'
  - 'services-accounting:docs/milestones/plans/ACCT-realignment-2026-10-22.md#D-T2'
purpose: A tax administrator chooses a default class for a product kind.
outcomes:
  - Tax defaults provide fallback classification during the ownership transfer.
concepts:
  - CON-ACC-PRD-004
actors:
  - ACT-ACC-009
  - ACT-ACC-007
  - ACT-ACC-011
surfaces:
  - console
  - api
permissions:
  - key: accounting.tax-classes.read
    where_checked: api
    scope: tenant
  - key: accounting.tax-classes.update
    where_checked: api
    scope: tenant
  - key: accounting.tax-classes.delete
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-ACC-PRD-018-01
    proved_by: [ATS-ACC-PRD-003-05, ATS-ACC-PRD-015-17, ATS-ACC-PRD-018-01, ATS-ACC-PRD-018-09, TST-ACC-PRD-029, TST-ACC-PRD-042]
    statement: >-
      Tax-class resolution uses the setup assignment first, the workspace’s default for the kind second and no
      class otherwise.
    kind: calculation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products-shared-base.e2e.spec.js#lets the product’s own
      accounting setup class win over the type default
    origin: 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#D-7'
  - id: BR-CAP-ACC-PRD-018-02
    proved_by: [ATS-ACC-PRD-018-02, TST-ACC-PRD-029, TST-ACC-PRD-042]
    statement: >-
      A workspace holds at most one default per registered kind, including inactive kinds, and cannot change
      the default’s kind.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.shared-base.integration.spec.js#refuses
      an unknown type, an unknown class and an inactive class newly chosen, and keeps an inactive class once
      held
    origin: 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#D-7'
  - id: BR-CAP-ACC-PRD-018-03
    proved_by: [ATS-ACC-PRD-018-02, TST-ACC-PRD-029, TST-ACC-PRD-042]
    statement: >-
      A newly selected default class must be an active workspace class, while an already held retired class
      remains acceptable.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.shared-base.integration.spec.js#refuses
      an unknown type, an unknown class and an inactive class newly chosen, and keeps an inactive class once
      held
    origin: 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#D-7'
  - id: BR-CAP-ACC-PRD-018-04
    proved_by: [ATS-ACC-PRD-018-03, TST-ACC-PRD-029, TST-ACC-PRD-042]
    statement: >-
      First-time default assignment omits a version and subsequent replacement requires the current read
      version.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products-shared-base.e2e.spec.js#replaces the default at
      the version read, lists it, and refuses a stale version 409
    origin: none
  - id: BR-CAP-ACC-PRD-018-05
    proved_by: [ATS-ACC-PRD-018-04, ATS-ACC-PRD-018-09, TST-ACC-PRD-029, TST-ACC-PRD-042]
    statement: >-
      Removing a default leaves products without setup assignments with no resolved class, and a repeat
      removal returns not found.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products-shared-base.e2e.spec.js#removes the default, 200
      then 404, after which a package without a setup class is taxed under none
    origin: 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#D-7'
  - id: BR-CAP-ACC-PRD-018-06
    proved_by: [ATS-ACC-PRD-018-05, TST-ACC-PRD-029, TST-ACC-PRD-042]
    statement: A tax class referenced by a product-kind default cannot be deleted.
    kind: invariant
    enforced_at:
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products-shared-base.e2e.spec.js#refuses deleting a tax
      class only a default names, 409 with the sentence, and allows it once the default is gone
    origin: none
  - id: BR-CAP-ACC-PRD-018-07
    proved_by: [ATS-ACC-PRD-018-06, ATS-ACC-PRD-018-08, TST-ACC-PRD-042]
    statement: >-
      A combined update-and-delete lock blocks both operations on a default, while a delete-only lock permits
      replacement.
    kind: permission
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/products.shared-base.integration.spec.js#refuses
      replacing or deleting a locked default
    origin: none
  - id: BR-CAP-ACC-PRD-018-08
    proved_by: [ATS-ACC-PRD-018-07, TST-ACC-PRD-029]
    statement: Tax defaults require tax-class permissions rather than product permissions.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products-shared-base.e2e.spec.js#refuses a caller with
      product rights alone (tax configuration needs the tax-classes keys)
    origin: none
  - id: BR-CAP-ACC-PRD-018-09
    proved_by: []
    statement: >-
      Documented line behavior freezes the resolved class when no explicit invoice or memo line class is
      supplied.
    kind: policy
    enforced_at: []
    must_prove: false
    provenance: documented
    source: 'services-accounting:docs/accounting/products.md#Tax class (2026-10-18)'
    origin: 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md#D-7'
  - id: BR-CAP-ACC-PRD-018-10
    proved_by: []
    statement: >-
      The product should move tax-class ownership, product-kind defaults and product assignments to Commerce
      and remove tax classification from Accounting’s editor.
    kind: policy
    enforced_at: []
    must_prove: false
    provenance: documented
    source: 'services-accounting:docs/milestones/plans/ACCT-realignment-2026-10-22.md#D-T2'
    origin: 'services-accounting:docs/milestones/plans/ACCT-realignment-2026-10-22.md#D-T2'
scenarios:
  - id: SC-CAP-ACC-PRD-018-01
    given: Membership packages have no kind default.
    when: A tax administrator assigns their standard class.
    then: Unassigned packages resolve the default while a package with its own class keeps that assignment.
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/products-shared-base.e2e.spec.js#sets a default for
      membership packages, after which a package without a setup class is taxed under it
edge_cases: []
constraints: []
depends_on:
  - CAP-ACC-PRD-015
related:
  - CAP-ACC-PRD-003
journeys: []
open_questions:
  - OQ-ACC-PRD-001
evidence:
  - 'react-console:components/accounting-config/products/product-tax-class-field.tsx'
  - 'react-console:components/configuration/tax/product-type-tax-classes-panel.tsx'
  - 'react-console:lib/configuration/areas.ts'
  - 'services-accounting:aws/accounting.service.ts'
  - 'services-accounting:docs/accounting/products.md'
  - 'services-accounting:docs/migrations/accounting-modernization/products-shared/README.md'
  - 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md'
  - 'services-accounting:docs/milestones/plans/ACCT-realignment-2026-10-22.md'
  - 'services-accounting:modules/products/aws/api/products.routes.ts'
  - 'services-accounting:modules/products/db/product-type-tax-classes.table.ts'
  - 'services-accounting:modules/products/services/persistence/product-type-tax-classes.ts'
  - 'services-accounting:modules/products/tests/e2e/products-shared-base.e2e.spec.js'
  - >-
    services-accounting:modules/products/tests/integration/services/persistence/products.shared-base.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-PI-001, TC-PI-003, TC-PI-005]
exercised_by: [FAS-ACC-PRD-04-02, FAS-ACC-PRD-04-04]
suites: [TST-ACC-PRD-029, TST-ACC-PRD-042]
---

# Maintain the tax default for a product kind

## Purpose

A tax administrator chooses a default class for a product kind.

## How It Works

A tax administrator chooses a default class for a product kind. Products with their own setup assignment continue to use that assignment. The default applies when the product has none. New classes must be active in the workspace, while a held class can survive retirement. Replacement uses the default’s own version. Locks restrict updates and deletion differently. Removing the default changes future resolution without changing stored product identities. This built Accounting surface remains recorded during the approved transfer to Commerce. The product should establish one tax owner and preserve existing assignments through that transfer.

## Relationships

This outcome uses assign a product’s accounting treatment.
