---
id: WF-ACC-CLS-002
title: Reopen the latest closed month
trigger: >-
  A controller confirms correction of the latest closed month, or an authorized
  client submits a reopen with its reason.
product_area: accounting
module: close
revision: 1
status: draft
design_intent: confirmed
origin: >-
  services-accounting:docs/milestones/plans/ACCT-M4-plan.md#4a row 75 (class A:
  reopen with a reason and its own grant, the period counts its reopens; plan
  approved 2026-10-21); docs/milestones/walkthroughs/ACCT-M3-handoff.md
  (reversal with swapped debits and credits)
implementation_evidence: observed
inference: []
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-ACC-CLS-011
  - CAP-ACC-CLS-009
  - CAP-ACC-CLS-014
moves:
  - STATE-ACC-CLS-001
  - STATE-ACC-CLS-002
crosses:
  - SVC-ACC-CLS-fiscal-close
idempotency: >-
  The dialog reserves one key, and matching kind, period and reason return the
  saved reopen. Task outputs preserve lock and reversal results; completed
  finalization recognizes an already open month with a successful run. Requester
  identity is absent from command comparison.
concurrency: >-
  One queued or running run is admitted per target month. Changing reopen tasks
  lock that row, but calendar sequence reads do not lock later months. The code
  therefore does not establish race prevention against a following-month close.
hops:
  - 'n': 1
    actor_or_service: Console reopen dialog
    action: >-
      Reserve the dialog key and require the reason and exact month label before
      sending reopen.
    anchor: 'react-console:components/accounting/close/reopen-dialog.tsx:ReopenDialog'
    seam: sync
    on_failure: The dialog shows validation or service refusal.
    left_inconsistent: No reopen request is stored before acceptance.
    observer: The controller sees the confirmation result.
    provenance: observed
    source: 'react-console:components/accounting/close/reopen-dialog.tsx:ReopenDialog'
  - 'n': 2
    actor_or_service: Reopen route (configuration function)
    action: >-
      Check reopen authority and the trimmed reason and command-key limits at
      the route.
    anchor: >-
      services-accounting:modules/fiscal-close/aws/api/fiscal-period-close.routes.ts:fiscalPeriodCloseRoutes
    seam: sync
    on_failure: Invalid input or missing permission returns an error.
    left_inconsistent: Validation creates no run.
    observer: The client receives the refusal.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/e2e/fiscal-close-runs.e2e.spec.js#refuses
      a reason under ten characters with 400
  - 'n': 3
    actor_or_service: Reopen request (one transaction)
    action: >-
      Replay an existing command or validate the latest closed month, then save
      its reopen request and first job and start after commit.
    anchor: >-
      services-accounting:modules/fiscal-close/services/jobs/close-run-actions.ts:requestPeriodReopen
    seam: sync
    on_failure: >-
      Transactional refusal rolls back; a start failure is delegated to the
      reconciler.
    left_inconsistent: A committed request may remain queued while its month stays closed.
    observer: The response and run view report the accepted or refused request.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/reopen-job.integration.spec.js#refuses
      reopening August while September is closed, with 1.0’s sentence
  - 'n': 4
    actor_or_service: 'Reopen job, lock step'
    action: >-
      Lock the target, recheck later-month sequence and reason length, set
      reopening and retain the original closer and time on the running run.
    anchor: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-job-tasks.ts:lockForReopen
    seam: state_machine
    on_failure: A domain refusal leaves the month unchanged and settles the run failed.
    left_inconsistent: Rollback preserves the previous closed state.
    observer: The run’s failed lock step reports the reason.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/reopen-job.integration.spec.js#refuses
      a reason under ten characters in the job’s lock, the period left closed
  - 'n': 5
    actor_or_service: 'Reopen job, reversal step, through Batch management'
    action: >-
      Use the original close key to unrecognize entries and reverse its batch,
      or retain a skip result when that close recognized nothing.
    anchor: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-job-tasks.ts:reverseRevenue
    seam: state_machine
    on_failure: >-
      Domain refusal leaves reopening for recovery; transactional errors retry,
      and a skipped-result event can fail after commit.
    left_inconsistent: >-
      Committed reversal can remain in a protected reopening month until
      finalization.
    observer: The output links the reversal and step history reports errors.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/reopen-job.integration.spec.js#reverses
      what the close recognised, by the close’s event key
  - 'n': 6
    actor_or_service: 'Reopen job, finalization step'
    action: >-
      Commit open state, cleared attribution, incremented reopen count and run
      success; append requester and reason afterwards, retaining the old
      snapshot and requesting no aging.
    anchor: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-job-tasks.ts:finalizeReopen
    seam: state_machine
    on_failure: >-
      Domain failure leaves reopening for retry. Failure of the later history
      append leaves the already open month and successful run.
    left_inconsistent: The month can be open without its reopen completion event.
    observer: >-
      The run and original close record reflect domain completion; job history
      reflects events successfully appended.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/reopen-job.integration.spec.js#opens
      the period and counts the reopen
  - 'n': 7
    actor_or_service: 'Reopen job failure task, or the Platform reconciler'
    action: >-
      Record the reported job failure without automatically restoring a reopen’s
      previous state.
    anchor: >-
      services-accounting:modules/fiscal-close/services/jobs/fiscal-close-jobs.ts:applyRunFailure
    seam: state_machine
    on_failure: >-
      The declared failure task retries, while delegated reconciler behavior is
      not inspected here.
    left_inconsistent: >-
      A retained failure keeps the month reopening; a lock failure leaves its
      earlier state.
    observer: The run’s recorded failure is the customer-visible result.
    provenance: test
    source: >-
      services-accounting:modules/fiscal-close/tests/integration/services/jobs/reopen-job.integration.spec.js#leaves
      the period reopening
branches:
  - >-
    When the original close recognized no revenue, reversal retains a skip
    result.
evidence:
  - >-
    services-accounting at f47cb263: modules/fiscal-close/services/jobs/*.ts;
    services/ports/revenue-recognition-port.ts; docs/fiscal-close/README.md
    §6.2; docs/fiscal-close/PHASE-2-PLAN.md §4.1 (reopen requests nothing).
  - 'react-console at baf6dae3: components/accounting/close/reopen-dialog.tsx.'
controls: [TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, 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-LOG-005, TC-AVL-001, TC-AVL-004, TC-MON-001, TC-MON-002, TC-MON-004, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005]
---

# Reopen the latest closed month

## What It Does

Reopening restores the eligible latest closed month for correction. Execution protects it, reverses revenue recorded by its close and finally opens it again. The original snapshot remains available and later reads include the reopen request.

## The Flow

```mermaid
sequenceDiagram
  actor Staff
  participant API as Reopen service
  participant Job as Durable reopen
  participant Revenue as Batch management
  Staff->>API: submit reason and confirmation
  API->>API: commit reopen and job
  API->>Job: start after commit
  Job->>Job: take month and retain previous attribution
  Job->>Revenue: reverse recorded revenue or skip
  Job->>Job: open month and succeed run
  Job->>Job: append completion history separately
```

## When Something Fails

A refusal before locking leaves the month closed. Later failure retains reopening for recovery. Cancellation can restore prior close attribution before reversal completes; after completed reversal the remaining path is retry. Finalization appends its history event after opening the month, so a later event failure does not undo the domain result. Reopen requests no aging version.

## Open Design Questions

Product must resolve execution authority and the bounded correction-history view. Separate month locks do not prove that a reopen and a following-month close cannot race. The confirmation’s cost of goods sold claim also needs correction.
