Skip to content

FS-01 · Create FS engagement

SOP: SOP_Financial_Statements.md §6 / Step 1.0 (S0 → DRAFT)Actors: FS Preparer — manually triggered as fs-preparer@spade.local (FS_PREPARER) or senior@spade.local (SENIOR_ACCOUNTANT). Pre-state: Client exists with FINANCIAL_STATEMENTS in enabledProducts and a populated fsConfig. Post-state: FsEngagement row created in DRAFT. URL settles to /dashboard/financial-statements/<engagementId>.

0. Prerequisites

  • Environment up per _shared/00-environment-setup.md.
  • Anchor client: AURORA (clientCode: AURORA).
    • Existing seed: AURORA already has one FY2024 engagement in TB_MAPPED. To exercise this flow, target a year that does not yet exist (e.g. FY2025) or stand up a fresh client first.
  • Logged in as fs-preparer@spade.local (which holds MANAGE_FS_ENGAGEMENT).

1. Steps

1.1 Create the engagement (Option A — Web UI)

  1. Sidebar → Financial StatementsNew engagement.
  2. Pick AURORA from the client dropdown.
  3. Fill in:
    • Financial year: FY2025
    • Year-end date: 2025-12-31
    • Reporting standard: SFRS_SE (defaults from Client.fsConfig.defaultReportingStandard)
    • Functional currency: SGD
    • Has prior year: true
  4. Submit.

1.2 Create the engagement (Option B — API)

http
POST /ops/financial-statements/engagements
Content-Type: application/json
Authorization: Bearer <fs-preparer-jwt>

{
  "clientId": "<auroraClientId>",
  "financialYear": "FY2025",
  "yearEndDate": "2025-12-31",
  "reportingStandard": "SFRS_SE",
  "functionalCurrency": "SGD",
  "hasPriorYear": true
}

The handler:

  1. Asserts hasProductPermission(role, FINANCIAL_STATEMENTS, MANAGE_FS_ENGAGEMENT).
  2. Asserts the client has FINANCIAL_STATEMENTS in enabledProducts.
  3. Asserts no duplicate (clientId, financialYear) engagement exists.
  4. Inserts the FsEngagement row with status = DRAFT, versionNo = 1.
  5. Emits AuditEvent fs.engagement.created.

2. Verification

Database

sql
SELECT id, financial_year, year_end_date, reporting_standard, status
  FROM fs_engagements
  WHERE client_id = '<auroraClientId>' AND financial_year = 'FY2025';
-- expect status = 'DRAFT', hasPriorYear = true

Audit log

/dashboard/financial-statements/<engagementId>/audit should list:

Event typeNotes
fs.engagement.createdactor = fs-preparer@spade.local

Status badge

Engagement detail page shows DRAFT and the Upload Trial Balance CTA is enabled. Mapping / Statements / Notes / Export tabs are visible but disabled with tooltip "Upload a trial balance first".

3. Negative & edge cases

  • Duplicate engagement — second call with the same (clientId, financialYear) returns 409 Conflict with the existing engagement id.
  • Client without FS product enabled400 Bad Request with "Client does not have FINANCIAL_STATEMENTS enabled."
  • Caller without MANAGE_FS_ENGAGEMENT403 Forbidden. XBRL_PREPARER for example can VIEW_FS_ENGAGEMENT (read-only cross-product reference) but cannot create.
  • Year-end date inconsistent with fsConfig.fyEndMonth — accepted with a warning banner on the engagement detail page (the date is the source of truth for the engagement; fsConfig.fyEndMonth is the client-level default).
  • Idempotency — there is no idempotency key. Two concurrent POSTs with the same payload race; one succeeds, the other returns 409.

Next

Proceed to FS-02 · Upload trial balance.

Internal use only — BreezyCorp