---
id: STATE-ACC-BKS-002
title: Fiscal year and period standing
object: DOM-ACC-BKS-003
product_area: accounting
module: books
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
states:
  - name: ungenerated
    meaning: No generated year covers the months in question.
  - name: open
    meaning: >-
      A period is open; the year remains open whenever at least one period is
      not closed.
  - name: closing
    meaning: >-
      Close holds the period while completing a close run; the year is still
      open.
  - name: closed
    meaning: >-
      Close has finalized the period; the year is closed only when all twelve
      periods are closed.
  - name: reopening
    meaning: 'A reopen run is in progress, which makes the year open.'
  - name: removed
    meaning: >-
      The latest eligible year has been removed, or the unit’s calendar has
      cascaded with its deletion.
transitions:
  - from: ungenerated
    to: open
    trigger: Generate calendar coverage
    guard: >-
      Request one to five years within the five-year starting-date horizon,
      continuing after existing coverage or beginning with the fiscal year
      containing the workspace date
    side_effects:
      - Insert twelve open monthly periods for each new year
      - Check calendar shape at commit
    who: 'Calendar administrator, or unit creation'
    provenance: test
    source: >-
      services-accounting:modules/books/tests/e2e/fiscal-years.e2e.spec.js#answers
      the two years asked for, each following the one before
  - from: open
    to: closing
    trigger: Begin closing a period
    guard: Close evaluates readiness and its own transition guard
    side_effects:
      - Associate the close run with the period
    who: Month-end Close
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/rules/transitions.ts:PERIOD_STATUS_TRANSITIONS
  - from: closing
    to: closed
    trigger: Finalize the close
    guard: Close completes its guarded finalization
    side_effects:
      - Record when and by whom the period closed
      - Derive a closed year if no period remains outside closed standing
    who: Month-end Close
    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
  - from: closing
    to: open
    trigger: Return a failed or cancelled close to open
    guard: Close applies its failure or cancellation rules
    side_effects:
      - Release the period’s close-run association
    who: Month-end Close
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/rules/transitions.ts:PERIOD_STATUS_TRANSITIONS
  - from: closed
    to: reopening
    trigger: Begin reopening
    guard: Close applies its reopening rules
    side_effects:
      - Derive open year standing
    who: Month-end Close
    provenance: observed
    source: >-
      services-accounting:modules/books/db/fiscal-periods.table.ts:fiscal_periods_year_status_trigger
  - from: reopening
    to: open
    trigger: Complete reopening
    guard: Close accepts the reopening completion
    side_effects:
      - Increase the reopen count
    who: Month-end Close
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/persistence/fiscal-periods.ts
  - from: reopening
    to: closed
    trigger: Return a failed reopening to closed
    guard: Close applies its failure transition
    side_effects: []
    who: Month-end Close
    provenance: observed
    source: >-
      services-accounting:modules/fiscal-close/services/rules/transitions.ts:PERIOD_STATUS_TRANSITIONS
  - from: open
    to: removed
    trigger: Remove eligible calendar records
    guard: >-
      Direct year deletion requires the latest year, all periods currently open
      and no restricting reference; eligible unit deletion uses its own guards
    side_effects:
      - Remove the year’s periods by cascade
    who: Calendar administrator or unit deletion
    provenance: test
    source: >-
      services-accounting:modules/books/tests/e2e/fiscal-years.e2e.spec.js#answers
      409 naming the latest year
invalid:
  - from: closed
    to: removed
    why: >-
      A direct year delete refuses nonlatest years or any period currently
      outside open standing; unit deletion instead follows its cascade and
      reference guards.
  - from: open
    to: closed
    why: >-
      The Books calendar API cannot close periods; Close reaches closed through
      closing standing.
  - from: ungenerated
    to: closed
    why: >-
      Generation creates open periods only, and an uncovered-date lookup creates
      nothing.
  - from: closed
    to: open
    why: >-
      Close refuses a direct jump from closed to open and requires reopening
      standing.
concurrency: >-
  Generation locks the unit before planning and appends after the latest
  committed year. Renaming takes the read version. Close is the period-standing
  writer. A year deletion’s own checks do not prove full coordination with
  concurrent generation or Close.
invariants:
  - INV-DOM-ACC-BKS-003-01
  - INV-DOM-ACC-BKS-003-02
  - INV-DOM-ACC-BKS-003-03
  - INV-DOM-ACC-BKS-003-04
  - INV-DOM-ACC-BKS-003-06
evidence:
  - >-
    services-accounting at f47cb263: modules/books/db/fiscal-years.table.ts,
    fiscal-periods.table.ts; services/persistence/fiscal-years.ts
  - >-
    services-accounting at f47cb263:
    modules/fiscal-close/services/rules/transitions.ts,
    services/persistence/fiscal-periods.ts (cited writer of period standing)
---

# Fiscal year and period standing

## The Lifecycle

```mermaid
stateDiagram-v2
  state "ungenerated" as s0
  state "open" as s1
  state "closing" as s2
  state "closed" as s3
  state "reopening" as s4
  state "removed" as s5
  s0 --> s1: Generate calendar coverage
  s1 --> s2: Begin closing a period
  s2 --> s3: Finalize the close
  s2 --> s1: Return a failed or cancelled close to open
  s3 --> s4: Begin reopening
  s4 --> s1: Complete reopening
  s4 --> s3: Return a failed reopening to closed
  s1 --> s5: Remove eligible calendar records
```

## Transitions Explained

Generation supplies monthly coverage. Unit creation supplies two years automatically; later extensions are explicit administrator requests. All new periods begin open.

Close owns the moves between open, closing, closed and reopening. The database updates year standing from its periods. Books can rename a year or directly remove the latest year while every period is currently open.

## What Cannot Happen

A date lookup never generates missing coverage. Calendar maintenance cannot alter dates or period standing. The direct deletion guard checks current standing, not whether a period has ever participated in Close.

## Concurrency and Timing

A generation writes its whole extension in one transaction and serializes planning on the unit. It uses the workspace’s local date. Database structural checks apply at commit, but the draft’s broad concurrency guarantee needs separate proof for generation, deletion and Close together.
