FS-01 · Create FS engagement
SOP:
SOP_Financial_Statements.md§6 / Step 1.0 (S0 → DRAFT)Actors: FS Preparer — manually triggered asfs-preparer@spade.local(FS_PREPARER) orsenior@spade.local(SENIOR_ACCOUNTANT). Pre-state: Client exists withFINANCIAL_STATEMENTSinenabledProductsand a populatedfsConfig. Post-state:FsEngagementrow created inDRAFT. 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.
- Existing seed: AURORA already has one FY2024 engagement in
- Logged in as
fs-preparer@spade.local(which holdsMANAGE_FS_ENGAGEMENT).
1. Steps
1.1 Create the engagement (Option A — Web UI)
- Sidebar → Financial Statements → New engagement.
- Pick AURORA from the client dropdown.
- Fill in:
- Financial year:
FY2025 - Year-end date:
2025-12-31 - Reporting standard:
SFRS_SE(defaults fromClient.fsConfig.defaultReportingStandard) - Functional currency:
SGD - Has prior year:
true
- Financial year:
- 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:
- Asserts
hasProductPermission(role, FINANCIAL_STATEMENTS, MANAGE_FS_ENGAGEMENT). - Asserts the client has
FINANCIAL_STATEMENTSinenabledProducts. - Asserts no duplicate
(clientId, financialYear)engagement exists. - Inserts the
FsEngagementrow withstatus = DRAFT,versionNo = 1. - Emits
AuditEventfs.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 = trueAudit log
/dashboard/financial-statements/<engagementId>/audit should list:
| Event type | Notes |
|---|---|
fs.engagement.created | actor = 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)returns409 Conflictwith the existing engagement id. - Client without FS product enabled —
400 Bad Requestwith"Client does not have FINANCIAL_STATEMENTS enabled." - Caller without
MANAGE_FS_ENGAGEMENT—403 Forbidden.XBRL_PREPARERfor example canVIEW_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.fyEndMonthis 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.