---
id: WF-ACC-BAT-006
title: Submit and run an unposting job
trigger: A direct unposting submission or permitted Unpost action requests recovery.
product_area: accounting
module: batches
revision: 1
status: draft
design_intent: confirmed
origin: >-
  Reporting D7 (Andrew, 2026-10-21, unposting deletes the entries and keeps the
  history), recorded at services-accounting:docs/accounting/batches.md#Unposting
  deletes the entries; A-4 (Andrew, 2026-10-18, unposting controlled by
  permissions, closed periods and dependents)
implementation_evidence: observed
inference: []
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
implements:
  - CAP-ACC-BAT-016
  - CAP-ACC-BAT-004
moves:
  - STATE-ACC-BAT-001
  - STATE-ACC-BAT-003
  - STATE-ACC-BAT-004
  - STATE-ACC-BAT-005
crosses:
  - SVC-ACC-BAT-batches
idempotency: >-
  The recovery job uses its own kind in the submission identity. Repeated or
  equivalent in-flight requests reuse that job; a missing current generation
  leaves no ledger generation to remove.
concurrency: >-
  Requested collections are locked in order at submission and execution, with
  shared period protection. Dependent rows outside the request are not
  explicitly locked, and rejecting reads are omitted from admission.
hops:
  - 'n': 1
    actor_or_service: Batches API (or the Unpost action)
    action: >-
      Validate request shape and lock collections before finding or creating the
      recovery job; the named action checks admission first.
    anchor: >-
      services-accounting:modules/batches/services/jobs/batch-posting-jobs.ts:createBatchUnpostingJob
    seam: sync
    on_failure: Rejected submission retains no new job.
    left_inconsistent: The prior accounting remains unchanged.
    observer: The submitting caller
    provenance: test
    source: >-
      services-accounting:modules/batches/tests/e2e/batch-actions.e2e.spec.js#refuses
      unposting a batch that is not posted 409
  - 'n': 2
    actor_or_service: Unposting state machine
    action: >-
      After submission commit, start the recovery workflow, publish its phases
      and invoke its task.
    anchor: >-
      services-accounting:modules/batches/services/jobs/batch-posting-jobs.ts:batchUnpostingWorkflow
    seam: state_machine
    on_failure: >-
      Failed start can leave a Pending job without reconciliation; execution
      uses the configured refusal and retry paths.
    left_inconsistent: The committed submission may never execute.
    observer: Platform job monitoring
    provenance: observed
    source: >-
      services-accounting:modules/batches/services/jobs/batch-posting-jobs.ts:startBatchJob
  - 'n': 3
    actor_or_service: 'Unposting task, posting engine'
    action: >-
      Within the accounting transaction, check ordinary recovery admission and
      write Open outcomes with generation statistics removed.
    anchor: >-
      services-accounting:modules/batches/services/engine/unpost-batches.ts:unpostBatches
    seam: sync
    on_failure: >-
      Admission refusal fails the checking phase and rolls back accounting
      changes.
    left_inconsistent: The previous collection and ledger state remains.
    observer: Job admission failure
    provenance: observed
    source: >-
      services-accounting:modules/batches/services/rules/batch-posting-rules.ts:ensureBatchesCanBeUnposted
  - 'n': 4
    actor_or_service: Posting engine with the ledger's definer function
    action: >-
      Delete current ordinary ledger rows through the guarded function, retire
      Open schedules and mark generation history reversed before commit.
    anchor: >-
      services-accounting:modules/batches/services/persistence/subledger-entries.ts:deleteBatchPostingEntries
    seam: sync
    on_failure: Deletion refusal rolls back recovery and fails the job.
    left_inconsistent: >-
      Successful recovery still retains previously recognised releases and their
      fiscal ledger results after removing originating deferral entries.
    observer: The recovery result and fiscal recognition records
    provenance: test
    source: >-
      services-accounting:modules/batches/tests/integration/services/posting/unpost-refusal.integration.spec.js#keeps
      the posting, its entries and the batch as they were, and fails the
      unposting step with the refusal
  - 'n': 5
    actor_or_service: Unposting task
    action: >-
      After commit, settle the phases and report recovered collections and
      deleted-entry count.
    anchor: >-
      services-accounting:modules/batches/services/jobs/batch-posting-job-tasks.ts:unpostBatchesOperation
    seam: sync
    on_failure: Settlement failure can propagate without undoing committed recovery.
    left_inconsistent: A restored Open collection can coexist with a failed job record.
    observer: The durable job panel
    provenance: inferred
    source: >-
      services-accounting:modules/batches/services/jobs/batch-posting-job-tasks.ts:unpostBatchesOperation;
      FND-ACC-BAT-039
branches:
  - >-
    A Posting row unchanged for more than an hour is admitted by the carried
    stale-state recovery rule.
waits: []
evidence:
  - >-
    services-accounting at f47cb263:
    modules/batches/services/engine/unpost-batches.ts;
    services/persistence/subledger-entries.ts, batch-postings.ts,
    revenue-recognition-schedules.ts; services/jobs/**;
    docs/accounting/batches.md
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-CRY-004, TC-DAT-001, TC-DAT-004, 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]
---

# Submit and run an unposting job

## What It Does

Ordinary recovery removes the current accounting generation so intake can be corrected and posted again. Posting history survives removal of ledger entries. Recovery of the source generation does not automatically reverse fiscal recognition.

## The Flow

```mermaid
flowchart LR
  R[Recovery submission] --> J[Commit job]
  J --> W[Start recovery workflow]
  W --> C[Check admission]
  C --> D[Delete current ledger and retire plans]
  D --> A[Commit recovery]
  A --> S[Settle job phases]
```

## When Something Fails

Transactional refusal keeps the prior accounting intact. The dependency check has unproved read-failure and concurrency protection. Successful unposting can also leave previously recognised revenue behind, and a later settlement failure can misrepresent completed recovery.

## Open Design Questions

Reliable dependent admission, recognised-revenue recovery and missing job reconciliation require decisions or proof before implementation preparation.
