---
id: DOM-ACC-BKS-003
title: Fiscal Calendar
realises: CON-ACC-BKS-003
product_area: accounting
module: books
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-ACC-BKS-books
readable_by:
  - SVC-ACC-BKS-maintenance
lifecycle: STATE-ACC-BKS-002
custom_fields: none
identity: >-
  A year receives a generated workspace-scoped identity and is unique by unit
  and starting date. A period is addressed by its year and number from one to
  twelve; each unit also has a unique period starting date.
attributes:
  - name: year name
    kind: text
    required: true
    computed: false
    notes: >-
      Generation names the year from its calendar span; staff may later rename
      it.
  - name: year start and end dates
    kind: date
    required: true
    computed: false
    notes: >-
      Dates follow month boundaries and the unit’s fixed starting month;
      calendar maintenance cannot change them.
  - name: year standing
    kind: enum
    required: true
    computed: true
    notes: >-
      The database stores closed only when all twelve periods are closed;
      otherwise the year is open.
  - name: periods
    kind: collection
    required: true
    computed: false
    notes: 'Twelve numbered monthly records hold names, dates and close standing.'
  - name: period standing
    kind: enum
    required: true
    computed: false
    notes: 'Close maintains open, closing, closed or reopening standing.'
  - name: close details
    kind: collection
    required: false
    computed: false
    notes: >-
      Close records the closer and time, the holding run and the count of
      reopenings.
  - name: current period
    kind: computed
    required: false
    computed: true
    notes: >-
      The lookup selects the earliest open period in the earliest open year; it
      can find no period even in an open year.
  - name: version
    kind: computed
    required: true
    computed: true
    notes: Renaming requires the version loaded by the caller.
relationships:
  - to: DOM-ACC-BKS-001
    cardinality: Each fiscal year belongs to exactly one unit
    ownership: >-
      Years and periods belong to their unit and cascade during an eligible unit
      deletion.
    cross_service: false
invariants:
  - id: INV-DOM-ACC-BKS-003-01
    statement: >-
      Database checks preserve contiguous month-boundary years, with twelve
      matching monthly periods per year at commit.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-accounting:modules/books/tests/integration/db/fiscal-years.table.integration.spec.js#refuses
      a year leaving a gap after the latest, in Rhythm 1.0’s sentence
  - id: INV-DOM-ACC-BKS-003-02
    statement: >-
      The database derives a closed year from twelve closed periods and reopens
      the year when any period leaves closed standing.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-accounting:modules/books/tests/integration/db/fiscal-years.table.integration.spec.js#closes
      the year when its last open period is closed
  - id: INV-DOM-ACC-BKS-003-03
    statement: >-
      Calendar maintenance accepts a name change and refuses changes to the
      year’s dates, standing or period collection.
    enforced_at:
      - service
      - database
    provenance: test
    source: >-
      services-accounting:modules/books/tests/e2e/fiscal-years.e2e.spec.js#answers
      409 to a changed start date, naming the column
  - id: INV-DOM-ACC-BKS-003-04
    statement: >-
      Direct year deletion is permitted only for the latest year when all its
      periods are currently open.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-accounting:modules/books/tests/e2e/fiscal-years.e2e.spec.js#answers
      409 naming the latest year
  - id: INV-DOM-ACC-BKS-003-05
    statement: >-
      Generation refuses the entire request if any planned year would start
      beyond the workspace date’s five-year horizon.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-accounting:modules/books/tests/integration/services/persistence/fiscal-years.integration.spec.js#writes
      none of the years, not even the years within reach
  - id: INV-DOM-ACC-BKS-003-06
    statement: >-
      Period lookups add no calendar records and report uncovered dates as
      missing.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-accounting:modules/books/tests/e2e/fiscal-years.e2e.spec.js#answers
      404 to the day after the last year, generating nothing
physical:
  - accounting.fiscal_years
  - accounting.fiscal_periods
evidence:
  - >-
    services-accounting at f47cb263: modules/books/db/fiscal-years.table.ts,
    fiscal-periods.table.ts
  - >-
    services-accounting at f47cb263:
    modules/books/services/persistence/fiscal-years.ts, fiscal-periods.ts;
    services/rules/fiscal-year-rules.ts
  - >-
    react-console at baf6dae3:
    components/accounting-config/fiscal-years/fiscal-years-page.tsx
---

# Fiscal Calendar

## What It Models

The fiscal calendar assigns accounting dates to a unit’s years and monthly periods. Controllers extend its coverage explicitly; Close maintains period standing. This separates calendar setup from the work that closes or reopens financial activity.

## Identity and Attributes

Generated year identities stay fixed when staff rename a year. Periods retain their existing names after that rename. Each period has fixed dates and numbering, with close details maintained separately.

Year standing is stored but derived from period standing. The current-period lookup selects an open year first and then its earliest open period; if that year has no open period, the result is absent.

## Relationships

The unit supplies the fiscal starting month and owns the calendar. Unit creation writes two initial years atomically; eligible later year deletion can remove that coverage. Financial records and Close runs may prevent removal through their references.

## Invariants

Database constraints protect calendar structure and derived year standing. Service guards limit generation and direct year removal. Generation writes a complete requested extension or rolls it back; date lookups never extend coverage.

## How It Is Stored and Shared

Books owns calendar generation, naming and ordinary removal. Close writes period standing through its own transition rules. Consumers read the shared calendar rather than a separate published read operation. Workspace date and time zone drive generation; the rows have no custom-field capability and are protected by workspace row policies.

## Open Design Questions

There is no examined advance warning for schedules approaching the coverage boundary. Coordination between year deletion, generation and Close needs stronger concurrency proof before claiming a universal calendar guarantee. A nonmonthly fiscal structure requires a separate product decision.
