---
id: CAP-ACC-PRD-016
title: Allocate product revenue across dates and accounts
feature: FEAT-ACC-PRD-04
product_area: accounting
module: products
revision: 4
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - 'react-console acceptance inventory CAP-ACC-PRD-014 (deferral, splits and inter-entity accounts)'
  - react-console acceptance inventory RUL-ACC-PRD-033
  - react-console acceptance inventory RUL-ACC-PRD-034
  - react-console acceptance inventory RUL-ACC-PRD-035
  - react-console acceptance inventory RUL-ACC-PRD-036
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md#D3'
purpose: Finance chooses when revenue should be recognized and whether parts belong to other accounts.
outcomes:
  - 'Revenue allocation retains valid dates, shares and account references.'
concepts:
  - CON-ACC-PRD-004
actors:
  - ACT-ACC-006
  - ACT-ACC-008
surfaces:
  - console
  - api
permissions:
  - key: accounting.product-accounting.update
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-ACC-PRD-016-01
    proved_by: [ATS-ACC-PRD-015-17, ATS-ACC-PRD-016-01, TST-ACC-PRD-026, TST-ACC-PRD-036]
    statement: >-
      Revenue treatment selects immediate, automatic, relative-date or fixed-date recognition, with deferred
      treatment requiring a capable kind and deferred revenue account.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/e2e/product-accounting.e2e.spec.js#answers 400 to deferral
      without a deferred revenue GL account
    origin: 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md#D3'
  - id: BR-CAP-ACC-PRD-016-02
    proved_by: [ATS-ACC-PRD-016-02, TST-ACC-PRD-010, TST-ACC-PRD-026, TST-ACC-PRD-036]
    statement: >-
      Fixed-date treatment requires up to one hundred twenty distinct dates whose percentages sum to exactly
      one hundred.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/product-accounting-setups.integration.spec.js#refuses
      percentages that add up to less than 100, in Rhythm 1.0’s words
    origin: 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md#D3'
    examples:
      positive: Allocations of 40.5 percent and 59.5 percent complete the required hundred percent.
      negative: Three allocations of 33.33 percent fall short at 99.99 percent and fail validation.
  - id: BR-CAP-ACC-PRD-016-03
    proved_by: [ATS-ACC-PRD-016-03, TST-ACC-PRD-026, TST-ACC-PRD-036]
    statement: >-
      Relative-date treatment requires up to twelve inclusive transaction-month ranges with no shared month,
      permits ranges crossing December and stores them by starting month.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/product-accounting-setups.integration.spec.js#REGRESSION
      — refuses 1-3 beside 11-2, the wrapping last rule overlapping the first, which Rhythm 1.0 accepted
    origin: 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md#D3'
  - id: BR-CAP-ACC-PRD-016-04
    proved_by: []
    statement: >-
      Relative-date fallback configuration selects immediate recognition or default deferral for transactions
      outside configured ranges.
    kind: policy
    enforced_at:
      - database
    must_prove: false
    provenance: observed
    source: >-
      services-accounting:modules/products/db/product-accounting-setups.table.ts:DEFERRAL_RELATIVE_DATE_FALLBACKS
    origin: none
  - id: BR-CAP-ACC-PRD-016-05
    proved_by: [ATS-ACC-PRD-016-04, TST-ACC-PRD-036, TST-ACC-PRD-066]
    statement: >-
      Each revenue split names an income account in the product’s unit and supplies either a percentage or
      fixed amount.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/product-accounting-setups.integration.spec.js#refuses
      a percentage split that also states an amount
    origin: 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md#D3'
  - id: BR-CAP-ACC-PRD-016-06
    proved_by: [ATS-ACC-PRD-008-11, ATS-ACC-PRD-011-09, ATS-ACC-PRD-016-05, ATS-ACC-PRD-016-09, ATS-ACC-PRD-017-07, TST-ACC-PRD-026, TST-ACC-PRD-036, TST-ACC-PRD-066]
    statement: >-
      Percentage shares total at most one hundred; fixed shares fit the standard set base price from the
      always period when available, preferring undated then first matching dated rows.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-accounting:modules/products/services/rules/product-rules.ts:listPrice;
      services-accounting:modules/products/services/rules/product-rules.ts:revenueSplitsProblem
    origin: 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md#D3'
  - id: BR-CAP-ACC-PRD-016-07
    proved_by: [ATS-ACC-PRD-016-06, TST-ACC-PRD-026, TST-ACC-PRD-036]
    statement: >-
      Revenue split identities are retained when supplied and generated when absent, with repeated identities
      rejected.
    kind: invariant
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/product-accounting-setups.integration.spec.js#keeps
      the id of a split sent back
    origin: none
  - id: BR-CAP-ACC-PRD-016-08
    proved_by: [ATS-ACC-PRD-016-07, TST-ACC-PRD-026, TST-ACC-PRD-036]
    statement: >-
      Inter-entity entries appear once per other unit, exclude the product’s unit and name accounts belonging
      to that other unit.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/product-accounting-setups.integration.spec.js#refuses
      a liability account of a third unit
    origin: none
  - id: BR-CAP-ACC-PRD-016-09
    proved_by: [ATS-ACC-PRD-016-08]
    statement: Accounting setup accepts no more than fifty revenue splits and two hundred inter-entity entries.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: observed
    source: >-
      services-accounting:modules/products/services/persistence/product-accounting-setups.ts:replaceProductAccounting
    origin: none
  - id: BR-CAP-ACC-PRD-016-10
    proved_by: []
    statement: >-
      The console rejects transaction-month ranges crossing December and omits split identities from saves,
      causing identities to be generated again.
    kind: policy
    enforced_at:
      - console
    must_prove: false
    provenance: observed
    source: 'react-console:lib/accounting-config/product-accounting-rules.ts:accountingForSave'
    origin: none
scenarios:
  - id: SC-CAP-ACC-PRD-016-01
    given: A priced course awaits accounting setup.
    when: 'Finance configures three dates totaling one hundred percent, a split and another unit’s accounts.'
    then: All three collections read back from the saved setup.
    provenance: test
    source: 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0116.spec.ts#ACT-ACC-0116'
edge_cases:
  - id: SC-CAP-ACC-PRD-016-02
    given: Relative coverage includes January–March and November–February.
    when: Finance attempts to save those ranges.
    then: Shared January and February coverage rejects the setup.
    provenance: test
    source: >-
      services-accounting:modules/products/tests/integration/services/persistence/product-accounting-setups.integration.spec.js#REGRESSION
      — refuses 1-3 beside 11-2, the wrapping last rule overlapping the first, which Rhythm 1.0 accepted
constraints: []
depends_on:
  - CAP-ACC-PRD-015
related:
  - CAP-ACC-PRD-008
  - CAP-ACC-PRD-011
journeys:
  - JNY-ACC-PRD-005
open_questions: []
evidence:
  - 'react-console:acceptance/accounting/playwright/journeys/products/ACT-ACC-0116.spec.ts'
  - 'react-console:components/accounting-config/products/inter-entity-accounts-editor.tsx'
  - 'react-console:components/accounting-config/products/revenue-recognition-editor.tsx'
  - 'react-console:components/accounting-config/products/revenue-splits-editor.tsx'
  - 'react-console:lib/accounting-config/product-accounting-rules.ts'
  - 'services-accounting:docs/accounting/products.md'
  - 'services-accounting:docs/milestones/ACCT-M1-books-and-invoices.md'
  - 'services-accounting:modules/products/db/product-accounting-children.table.ts'
  - 'services-accounting:modules/products/db/product-accounting-setups.table.ts'
  - 'services-accounting:modules/products/services/persistence/product-accounting-setups.ts'
  - 'services-accounting:modules/products/services/rules/product-rules.ts'
  - 'services-accounting:modules/products/tests/e2e/product-accounting.e2e.spec.js'
  - >-
    services-accounting:modules/products/tests/integration/services/persistence/product-accounting-setups.integration.spec.js
  - 'services-accounting:modules/products/tests/unit/rules/product-rules.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-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-PI-001, TC-PI-002, TC-PI-003]
exercised_by: [FAS-ACC-PRD-04-01, FAS-ACC-PRD-04-03]
suites: [TST-ACC-PRD-010, TST-ACC-PRD-026, TST-ACC-PRD-036, TST-ACC-PRD-066]
---

# Allocate product revenue across dates and accounts

## Purpose

Finance chooses when revenue should be recognized and whether parts belong to other accounts.

## How It Works

Finance chooses when revenue should be recognized and whether parts belong to other accounts. Deferred choices need a deferred revenue account and suitable product kind. Fixed dates allocate the whole amount across percentages. Relative rules group transaction months and must avoid overlapping coverage. Revenue splits allocate percentages or amounts within the product’s price limits. Inter-entity entries identify accounts for sales in other business units. The service keeps supplied split identities across replacements. The console currently regenerates them and refuses month ranges crossing December. Those editor differences need resolution before design treats the two paths as interchangeable.

## Relationships

This outcome uses assign a product’s accounting treatment.
