Skip to content

Financial Statements Preparation

1. Document Control

Document TitleSingapore Financial Statements Preparation
Document IDSOP-ACC-003
Version1.0 (Draft for Automation Build)
Effective Date[Insert Date]
Review Date[Insert Date + 12 months]
Document OwnerHead of Reporting & Assurance
ApproverManaging Director
StatusDraft

Version History

VersionDateAuthorChangesApproved By
1.0[Insert Date][Insert Author]Initial SOP for the AI-assisted financial-statements module (BreezyCorp Phase 1)[Insert Approver]

Distribution

Role TitleAccess Rights
Client ApproverRead-only
FS PreparerEdit
FS ReviewerEdit
Senior AccountantEdit
Platform AdminEdit
Quality ReviewerRead-only
AI Workflow DeveloperRead-only

2. Purpose and Scope

2A. Process Overview for New Staff

What is this process?

This SOP defines the controlled annual / interim workflow by which BreezyCorp prepares Singapore-compliant financial statements for a private limited entity, using the AI-assisted Financial Statements Engagement Engine (FSEE). The FSEE orchestrates: trial-balance ingestion, SFRS taxonomy mapping (with AI auto-suggest and mandatory human confirmation), deterministic statement assembly (Balance Sheet, P&L, SOCE, Cash Flow), disclosure interrogation, AI-generated notes and Directors Report, and DOCX export — with human gates at mapping, interrogation, and final review.

When does this process occur?

This process is triggered when a client requires:

  • Annual financial statements for an accounting period (typically 12 months ending on a fixed year-end date)
  • Interim financial statements for a sub-annual period (less common)

A new FsEngagement is created per client per financial year. Multiple engagements for the same client (e.g. FY2023 and FY2024) coexist; each is independent.

What are the deliverables?

  • Validated trial-balance pack stored against the engagement
  • Confirmed SFRS taxonomy mapping for every TB row
  • Computed BS / P&L / SOCE / Cash Flow with balancing checks and anomaly flags
  • Complete disclosure responses for every triggered rule
  • AI-generated notes + Directors Report (Section 201 Companies Act)
  • Publication-ready DOCX export
  • Optional client portal sign-off
  • Archived engagement record with immutable audit trail

Who is involved?

  • FS Preparer (FS_PREPARER)
  • FS Reviewer (FS_REVIEWER)
  • Senior Accountant (SENIOR_ACCOUNTANT)
  • Client Approver (CLIENT_APPROVER)
  • FSEE (orchestration, deterministic statement generator, disclosure rule engine, AI mapping + notes)
  • Anthropic Claude Sonnet (taxonomy auto-suggest + notes/Directors Report generation)

Primary Compliance Areas (Singapore)

  • Singapore Financial Reporting Standards for Small Entities (SFRS for Small Entities) — default standard.
  • Full SFRS — for entities that do not meet the small-entity thresholds.
  • Companies Act 1967 — particularly Section 201 (Directors Report disclosures) and the requirement that financial statements give a true and fair view.
  • ACRA filing requirements (informs disclosure rule set, even when XBRL filing is downstream).
  • PDPA — directors' personal data appears in financial statements.

Compliance Horizon

This SOP is designed so that no financial statement leaves the system without: a mapped trial balance, a balanced statement set, a complete answer to every triggered disclosure rule, and the verbatim mandatory accounting-policy text (Income Tax, Employee Benefits, Provisions) inserted unaltered into the notes.


3. Definitions and Abbreviations

Term/AbbreviationDefinition
FSEEFinancial Statements Engagement Engine — the automation layer that orchestrates TB upload, mapping, statement generation, disclosure interrogation, notes generation, export
SFRSSingapore Financial Reporting Standards (Full SFRS)
SFRS_SESingapore Financial Reporting Standards for Small Entities
SFRS(I)Singapore Financial Reporting Standards (International) — IFRS-aligned
TBTrial Balance — the input list of GL account balances at year-end
SOFPStatement of Financial Position (Balance Sheet)
SPLOCIStatement of Profit or Loss and Other Comprehensive Income (P&L)
SOCEStatement of Changes in Equity
SOCFStatement of Cash Flows
ConstitutionImmutable JSON files in packages/financial-statements/constitution/: sfrs_taxonomy.json, disclosure_rules.json, mandatory_policies/*.md
Disclosure RuleOne of ~25 entries in disclosure_rules.json with a trigger_condition (always or tb_contains_any([...])) and a set of prompts
Mandatory PolicyVerbatim regulatory wording for Income Tax, Employee Benefits, Provisions; inserted unaltered into notes
ALAudit Logger — AuditEvent rows with actor, action, payload, timestamp
SoDSegregation of duties — the preparer is not the reviewer is not the client approver

4. Roles and Responsibilities

RoleResponsibilitiesCompetency Requirements
FS PreparerUpload trial balance, run auto-suggest mapping, override low-confidence rows, generate statements, complete disclosure interrogation, generate notesSFRS for Small Entities fluency, COA familiarity
FS ReviewerAll preparer rights + final review and DOCX export to the clientSenior SFRS judgment, disclosure completeness check
Senior AccountantAll FS actions (and all XBRL actions short of MARK_XBRL_FILED) — typically the firm partner-equivalent for cross-product engagementsFull SFRS + Companies Act + ACRA filing experience
Client ApproverReview the DOCX preview via the portal magic link; Approve or Request RevisionFamiliarity with the entity's operations and prior-year statements
FSEE (system)Trial-balance parsing, AI mapping suggestion, deterministic statement assembly, disclosure rule evaluation, AI notes generation, DOCX renderingConstitution loaders + status machine + worker handlers + AI plumbing

Segregation of Duties (SoD) Controls

  • The preparer who uploads the TB and overrides mappings must not be the same person who marks the engagement reviewed and exports the final DOCX. The EXPORT_FS_DOCUMENT action is reserved to FS_REVIEWER, SENIOR_ACCOUNTANT, PLATFORM_ADMIN.
  • AI auto-suggested mappings carry mappingConfidence and mappingRationale. A reviewer who accepts a LOW-confidence mapping without override is making a judgment that is audited and surfaced in the activity log.
  • Mandatory policy text is inserted verbatim from the constitution — the LLM is not permitted to rewrite it. Any edit to policy text is a constitution change, requires a PR + qualified-accountant sign-off, and is out of band.
  • The Client Approver path is one-way: Approve seals the engagement at NOTES_COMPLETE; Request Revision routes back to the preparer.

5. Prerequisites and Entry Conditions

Before commencing this process, verify the following conditions are met:

  1. Client master record exists with FINANCIAL_STATEMENTS in enabledProducts.
  2. Client.fsConfig is populated with defaultReportingStandard (SFRS_SE or FULL_SFRS), baseCurrency (typically SGD), fyEndMonth, roundingConvention, and the company's uen, registeredAddress, and principalActivities.
  3. Staff users hold the appropriate role: FS_PREPARER to drive the engagement; FS_REVIEWER (or SENIOR_ACCOUNTANT / PLATFORM_ADMIN) to export.
  4. Prior-year comparatives — if hasPriorYear will be true, the TB upload must carry prior-year balances or a separate prior-year TB must be available.

Entry Checklist

  • [ ] Engagement ID convention confirmed (auto-generated UUID; surfaced in URL as /dashboard/financial-statements/<engagementId>)
  • [ ] Financial year label confirmed (e.g. FY2024)
  • [ ] Year-end date confirmed (YYYY-MM-DD, must agree with fyConfig.fyEndMonth)
  • [ ] Reporting standard chosen (SFRS_SE or FULL_SFRS)
  • [ ] Functional currency and rounding convention confirmed
  • [ ] Audit logging enabled (default — every state transition emits AuditEvent)

6. Detailed Process Flow

Process Map (high-level phases)

  1. PREPARE — Engagement creation and configuration
  2. INGEST — Trial-balance upload and parsing
  3. MAP — Account → SFRS taxonomy key (AI-suggested, human-confirmed)
  4. GENERATE — Deterministic BS / P&L / SOCE / Cash Flow
  5. INTERROGATE — Disclosure rule evaluation and response capture
  6. AUTHOR — AI-generated notes + Directors Report
  7. EXPORT — Publication-ready DOCX (and later PDF / XLSX)
  8. REVIEW — Client portal sign-off (optional)
  9. ARCHIVE — Sealed engagement, retained per policy

Controlled States (State Machine)

The engagement walks through seven states. See packages/financial-statements/src/status-transitions.ts for the canonical transition matrix.

DRAFT → TB_UPLOADED → TB_MAPPED → STATEMENTS_GENERATED → NOTES_COMPLETE → ARCHIVED
                                ↘ NOTES_ERROR ↺ STATEMENTS_GENERATED (retry)

General State Transition Rules

  • Re-uploading a trial balance returns the engagement to DRAFT and invalidates all downstream artifacts. The status badge shows the regression so a reviewer knows everything below TB needs re-running.
  • Regenerating statements bumps FsGeneratedStatement.versionNo and stays at STATEMENTS_GENERATED. Notes generated against the prior version do not automatically invalidate, but a reviewer must confirm they still match the new figures.
  • NOTES_ERROR is a recoverable state. Statements remain valid; only the notes Claude call failed (timeout, JSON parse, schema mismatch).
  • Every transition is audit-logged with actor, timestamp, and a typed action.

Step 1.0: Create FS Engagement (S0 → DRAFT)

Process Phase: PREPARE

Actor: FS Preparer

Action Description

Create a new FsEngagement for the client + financial year. Header inputs: financial year label, year-end date, reporting standard, functional currency, prior-year flag.

Statutory Basis

N/A (internal control step — creates the container for the engagement)

Inputs

Field NameFormatSourceValidation Rule
clientIdUUIDClient masterMust have FINANCIAL_STATEMENTS enabled
financialYearStringUser inputFree-form (e.g. FY2024)
yearEndDateYYYY-MM-DDUser inputMust agree with Client.fsConfig.fyEndMonth
reportingStandardSFRS_SE | FULL_SFRSUser input or fsConfig defaultOne of the two values
functionalCurrencyISO 4217fsConfig defaultTypically SGD
hasPriorYearBooleanUser inputDefault false

Micro-Steps

  1. Open /dashboard/financial-statements/new (or POST /ops/financial-statements/engagements).
  2. Fill in header. The reporting-standard dropdown defaults from Client.fsConfig.defaultReportingStandard.
  3. Submit. Engagement created in DRAFT.

Validation and Control Checks

  • Check 1: Caller holds MANAGE_FS_ENGAGEMENT for the FINANCIAL_STATEMENTS product.
  • Check 2: No duplicate (clientId, financialYear) engagement exists (idempotency).
  • Check 3: Year-end date is consistent with fyEndMonth.

Decision Points and Branching Logic

  • IF enabledProducts does not include FINANCIAL_STATEMENTS, THEN request rejected with 400.
  • ELSE engagement created at DRAFT.

System Interaction

  • System: API (/ops/financial-statements/engagements)
  • Action: Insert FsEngagement row, emit AuditEvent (fs.engagement.created)

Data Storage and Evidence

  • FsEngagement row with versionNo = 1, status = DRAFT, timestamps.

Acceptance Criteria

  • [ ] Engagement created in DRAFT
  • [ ] Audit log shows fs.engagement.created

Step 2.0: Upload Trial Balance (DRAFT → TB_UPLOADED)

Process Phase: INGEST

Actor: FS Preparer

Action Description

Upload the client's trial balance as CSV or XLSX. The parse-trial-balance worker handler runs smart column detection, sign normalization (debit-positive in storage), year detection (4-digit headers, finds closest prior year), and append-replaces all TB rows for the engagement.

Statutory Basis

N/A (data capture)

Inputs

InputFormatNotes
TB fileCSV / XLSX / XLSMax 10 MB
Column headersCase-insensitiveRecognised: account_code, description, debit, credit, OR current_year/prior_year
CurrencyImplicitAssumed to equal engagement.functionalCurrency

Micro-Steps

  1. Open the engagement; click Upload Trial Balance.
  2. Drag-drop the file. The drag-drop surface accepts a single file per upload.
  3. The handler:
    • Reads the file from S3 (financial-statements/<clientId>/<engagementId>/tb-<timestamp>.{csv,xlsx})
    • Detects columns (case-insensitive, underscore/space-agnostic)
    • Normalizes signs (debit-positive)
    • Detects year columns (finds the closest 4-digit prior year — sets hasPriorYear = true)
    • Replaces all FsTrialBalanceItem rows for this engagement
  4. Engagement transitions DRAFT → TB_UPLOADED.
  5. The UI shows a preview table with row count, balance check (Σ current-year balances must equal zero ±1.0 SGD), and a "Replace upload" button.

Validation and Control Checks

  • Check 1: File size ≤ 10 MB; file extension is .csv, .xls, or .xlsx.
  • Check 2: Σ of all currentYearBalance values equals zero ±1.0 SGD (TB is balanced).
  • Check 3: No row has both null account code and null description.
  • Check 4: If hasPriorYear flips to true, prior-year column was successfully detected.

Decision Points and Branching Logic

  • IF the TB is not balanced, THEN flag the engagement with a warning (does not block but appears on the mapping screen).
  • IF column detection fails (no recognisable headers), THEN the handler emits an error and the engagement stays at DRAFT. The reviewer must reformat the file and re-upload.

System Interaction

  • System: API (POST /ops/financial-statements/engagements/:id/trial-balance), worker (parse-trial-balance), S3.

Data Storage and Evidence

  • Raw TB file persisted in S3 with the engagement-scoped key prefix.
  • One FsTrialBalanceItem row per GL account line.
  • AuditEvent fs.tb.uploaded with row count and balance-check result.

Common Mistakes to Avoid

  • Do not upload a TB with mixed sign conventions (some columns debit-positive, others credit-positive). The detector flags the second column but cannot infer the intent.
  • Do not include subtotal rows. The parser sums every row; subtotals will double-count.

Acceptance Criteria

  • [ ] Engagement reaches TB_UPLOADED
  • [ ] FsTrialBalanceItem count > 0 and balance check passed (or warned)

Step 3.0: Map Trial Balance to SFRS Taxonomy (TB_UPLOADED → TB_MAPPED)

Process Phase: MAP

Actor: FS Preparer (with Claude assist)

Action Description

Map every TB row to an SFRS taxonomy key from sfrs_taxonomy.json. Use the AI auto-suggest path for the bulk, then manually override low-confidence rows.

Statutory Basis

The taxonomy aligns to SFRS for Small Entities primary mapping and Full SFRS alternate references. Misclassification at this step propagates into the BS / P&L sections — this is the most important human-judgment gate in the workflow.

Micro-Steps

  1. Open the Mapping tab. The table shows every TB row with its current taxonomy key (empty initially).
  2. Click Auto Suggest. The handler enqueues auto-suggest-mapping. Claude Sonnet receives the TB rows + the full flattened SFRS taxonomy and returns per-row {taxonomyKey, taxonomyLabel, confidence, rationale}.
  3. The table refreshes with confidence badges (HIGH / MEDIUM / LOW). A tooltip on each badge shows the AI rationale.
  4. Walk every LOW and MEDIUM row. Decide: accept, or open the dropdown and pick a different taxonomy key.
  5. Manual overrides set isOverride = true and mappingConfidence = MANUAL. The override is audited.
  6. When every row has a taxonomyKey, the engagement transitions TB_UPLOADED → TB_MAPPED (handler-side when auto-suggest leaves nothing unmapped; otherwise on the final manual confirmation).

Validation and Control Checks

  • Check 1: Every row has a non-null taxonomyKey before transitioning.
  • Check 2: Every assigned key exists in the taxonomy constitution (findTaxonomyItem(key) != undefined).
  • Check 3: Sign consistency — credit-side keys (revenue, equity, liabilities) should pair with credit balances in the TB. The auto-suggest prompt is sign-aware but a manual override could mis-sign. The statement generator surfaces a sign-flip anomaly downstream.

Decision Points and Branching Logic

  • IF auto-suggest returns nothing for a row (Claude responded unmapped), the row stays unmapped and is highlighted with UNCLASSIFIED. Preparer must override manually.
  • IF the preparer re-uploads a fresh TB at any time, mappings are dropped and the engagement returns to TB_UPLOADED.

System Interaction

  • System: API (POST /ops/financial-statements/engagements/:id/mapping/auto-suggest, PATCH /ops/financial-statements/engagements/:id/mapping/:tbItemId), worker (auto-suggest-mapping), Claude Sonnet, constitution loaders.

Data Storage and Evidence

  • Each FsTrialBalanceItem carries taxonomyKey, taxonomyLabel, mappingConfidence, mappingRationale, isOverride.
  • AuditEvent fs.mapping.auto_suggested (one event per run; payload includes input + output token counts).
  • AuditEvent fs.mapping.overridden (one event per manual override).

Common Mistakes to Avoid

  • Do not auto-accept a LOW-confidence row without reading the rationale. The cost of misclassification is a mis-stated note disclosure later.
  • Do not invent a taxonomy key. The dropdown is constrained to the constitution.

Acceptance Criteria

  • [ ] Every TB row carries a valid taxonomyKey
  • [ ] Engagement reaches TB_MAPPED

Step 4.0: Generate Statements (TB_MAPPED → STATEMENTS_GENERATED)

Process Phase: GENERATE

Actor: FS Preparer

Action Description

Run the deterministic statement generator. There is no AI in this step — every figure is a pure function of the mapped TB.

Statutory Basis

The statement assembly follows SFRS for Small Entities presentation requirements:

  • SOFP: Non-current assets → Current assets → Total assets ‖ Equity → Non-current liabilities → Current liabilities → Total equity & liabilities. Balance check ±1.0 SGD.
  • SPLOCI: Revenue (sign-flipped from credit) − COGS = Gross Profit; + Other Income − Selling/Admin/Finance/Other = Profit Before Tax; − Tax = Profit After Tax.
  • SOCE: opening equity + PAT + share movements = closing equity; columns with no movement are omitted.
  • SOCF (indirect): Operating = PBT + D&A + working-capital movements − tax paid; Investing = PPE / ROU / intangibles / investment movements; Financing = share / loan / lease movements. Reconciles to opening + closing cash.

Micro-Steps

  1. Open the Statements tab; click Generate.
  2. The generate-statements handler runs synchronously (no LLM). Inputs: every FsTrialBalanceItem for the engagement; the SFRS taxonomy; engagement metadata.
  3. Output: a new FsGeneratedStatement row with versionNo bumped, statementsJson populated, isBalanced set, anomalies populated.
  4. The UI renders BS / P&L / SOCE / Cash Flow as four tabs. A red banner appears if isBalanced = false.
  5. Engagement transitions TB_MAPPED → STATEMENTS_GENERATED.

Anomaly Detection

The generator flags:

  • Unbalancedtotal_assets != total_equity_and_liabilities (±1.0 SGD).
  • Negative working capital — current liabilities exceed current assets.
  • Zero revenue — revenue side is empty.
  • Profit but no tax — PBT > 0 but no income-tax expense.
  • Sign flip — a credit-side taxonomy key resolved to a debit balance after sign normalisation (or vice versa). Surfaces a mapping question the reviewer must answer.

Acceptance Criteria

  • [ ] FsGeneratedStatement row created with isBalanced = true (or a knowingly-tolerated anomaly noted in the activity log)
  • [ ] Engagement reaches STATEMENTS_GENERATED

Step 5.0: Disclosure Interrogation (STATEMENTS_GENERATED)

Process Phase: INTERROGATE

Actor: FS Preparer

Action Description

Walk every triggered disclosure rule and provide responses. The set of triggered rules is computed live by evaluateDisclosureGaps(presentKeys) against the engagement's mapped TB keys.

Statutory Basis

The rule library encodes mandatory and conditional disclosures from SFRS for Small Entities and the Companies Act. Mandatory rules (always trigger) fire for every engagement; conditional rules fire when their tb_contains_any([...]) clause matches the mapped TB.

Rule Inventory (excerpt)

PriorityRule IDNameTrigger
mandatoryDISC-001Company General Informationalways
mandatoryDISC-002Reporting Standard & Functional Currencyalways
mandatoryDISC-003Going Concernalways
mandatoryDISC-017Income Tax (mandatory verbatim policy)always
mandatoryDISC-020Directors Remuneration / Auditorsalways
mandatoryDISC-021Employee Benefits (mandatory verbatim policy)always
mandatoryDISC-024Events After Reporting Periodalways
mandatoryDISC-025Dividendsalways
conditionalDISC-010PPEtb_contains_any(['ppe'])
conditionalDISC-011Leases & ROUtb_contains_any(['rou_assets','lease_liabilities'])
conditionalDISC-012Inventoriestb_contains_any(['inventory'])
conditionalDISC-013Trade Receivablestb_contains_any(['trade_receivables'])
conditionalDISC-014Cash and Banktb_contains_any(['cash'])
conditionalDISC-015Share Capitaltb_contains_any(['share_capital'])
conditionalDISC-016Borrowingstb_contains_any(['borrowings'])
conditionalDISC-018Revenuetb_contains_any(['revenue'])
conditionalDISC-022Finance Coststb_contains_any(['finance_costs'])
recommendedDISC-019Related Party Transactionsalways
recommendedDISC-023Commitments & Contingenciesalways

The full library lives in packages/financial-statements/constitution/disclosure_rules.json. The loader caches after first read; tests clear via clearDisclosureCache().

Prompt input types

input_typeRenders as
textSingle-line text
dateDate picker (YYYY-MM-DD)
selectDropdown (uses options[])
booleanYes/No
multi_textRepeating text rows
tableEditable table (e.g. directors list)
numberNumber with decimal control

Prompts may carry visible_when predicates referencing a sibling prompt (e.g. DISC-003-01 == 'no'). The UI shows or hides accordingly.

Micro-Steps

  1. Open Interrogation. The accordion lists triggered rules sorted by priority (mandatory → conditional → recommended), then by rule_id.
  2. For each rule, expand and answer every required prompt. Conditional prompts appear as triggered.
  3. Click Save. The handler upserts one FsDisclosureResponse row per (ruleId, promptId).
  4. The progress bar tracks complete_rules / triggered_rules. When 100% the engagement is interrogation-complete (no separate state — this is gating for the notes step).

Validation and Control Checks

  • Mandatory prompts must be answered before notes generation. The handler refuses to enqueue generate-notes if any mandatory prompt is empty.
  • Standard references (e.g. SFRS-SE Section 29) are surfaced as inline regulatory callouts in the accordion.

Data Storage and Evidence

  • FsDisclosureResponse rows keyed (engagementId, ruleId, promptId). responseData is JSONB {value: any}.
  • AuditEvent fs.disclosure.responded per save.

Acceptance Criteria

  • [ ] Every triggered mandatory rule has every required prompt answered
  • [ ] Conditional rules whose triggers fire are answered or knowingly skipped (the activity log captures the skip)

Step 6.0: Generate Notes & Directors Report (STATEMENTS_GENERATED → NOTES_COMPLETE)

Process Phase: AUTHOR

Actor: FS Preparer (with Claude)

Action Description

Run the AI notes generator. A single Claude Sonnet 16K-token call combines: the generated statements JSON; every disclosure response; the verbatim mandatory policies from packages/financial-statements/constitution/mandatory_policies/; engagement and client metadata. The model returns structured note blocks; deterministic post-processing replaces any financial_results_placeholder block with a computed table.

Statutory Basis

The mandatory policy texts (Income Tax — Section 29 SFRS_SE; Employee Benefits; Provisions) are verbatim regulated content. They are inserted into the notes from the constitution and the LLM is instructed not to rewrite them. The Directors Report follows Companies Act Section 201.

Micro-Steps

  1. Open Notes. Click Generate notes.
  2. The handler enqueues generate-notes (background; UI shows a polling indicator).
  3. Background job:
    • Loads disclosure responses, generated statements, engagement + client metadata, mandatory policies
    • Builds one consolidated 16K-token prompt
    • Calls Claude Sonnet (single call — cost-optimised vs per-note loops)
    • Receives {notes: [{note_key, title, blocks}], directors_report: {blocks}}
    • Replaces financial_results_placeholder blocks with Python-built financial-results tables (port: TS equivalent)
    • Upserts FsNote rows keyed (engagementId, noteKey)
  4. On success, engagement transitions STATEMENTS_GENERATED → NOTES_COMPLETE.
  5. On failure (timeout, JSON parse, schema mismatch), engagement transitions to NOTES_ERROR. A reviewer reads the failure record and either retries (NOTES_ERROR → STATEMENTS_GENERATED) or adjusts upstream inputs first.

Block Types

typeRenders as
paragraphBody text
subheadingInline subheading inside the note
table{headers: string[], rows: string[][]} right-aligned amounts
financial_results_placeholderReplaced post-LLM by the computed financial-results table

Mandatory Notes

The following notes are always generated:

  • directors_report — Section 201 Companies Act
  • note_general — name, UEN, registered office, principal activities
  • note_basis — basis of preparation
  • note_policies — significant accounting policies (uses the verbatim mandatory policies)

Dynamic notes per TB content: note_ppe, note_trade_receivables, note_revenue, note_rpt (related party transactions), etc.

Validation and Control Checks

  • Mandatory policy text must appear verbatim in the rendered note. A reviewer can verify by string-matching against the constitution.
  • Every note_key returned by the LLM must exist as a note_trigger flag in the taxonomy (or be a mandatory always-present note). Unknown note_keys are rejected at parse time.

Data Storage and Evidence

  • FsNote rows keyed (engagementId, noteKey). content is JSONB array of block objects.
  • AuditEvent fs.notes.generated with input/output token counts, model id, Claude request id.

Common Mistakes to Avoid

  • Do not edit the mandatory policy text in the note. Edit the constitution file via a PR with qualified-accountant sign-off.
  • Do not regenerate notes after the client has signed off. Once NOTES_COMPLETE has been promoted into a client review, regeneration invalidates the signed artifact.

Acceptance Criteria

  • [ ] FsNote row exists for every mandatory note + every dynamic note triggered by the TB
  • [ ] Engagement reaches NOTES_COMPLETE

Step 7.0: Export DOCX (NOTES_COMPLETE)

Process Phase: EXPORT

Actor: FS Reviewer / Senior Accountant / Platform Admin

Action Description

Render the publication-ready DOCX. Action gated to EXPORT_FS_DOCUMENT permission (FS_REVIEWER, SENIOR_ACCOUNTANT, PLATFORM_ADMIN).

Statutory Basis

The DOCX layout follows Singapore market convention for SFRS for Small Entities:

  • Cover page (company name, title, year-end date, UEN)
  • Running header (company left, year-end right, thin bottom border) and centred 8 pt page numbers
  • Directors Report (Section 201) — with the Financial Results table injected at the placeholder position
  • SOFP (4-col: Description / Note / CY / PY)
  • SPLOCI (2-col)
  • SOCE (zero-movement columns auto-omitted)
  • SOCF (Operating → Investing → Financing)
  • Notes (rendered from JSON blocks, paragraphs / subheadings / right-aligned amount tables; auto-numbered "1. General Information", "2. Basis", …)

Micro-Steps

  1. Open the engagement; click Export Word.
  2. The handler enqueues export-statements-docx. The renderer reads the latest FsGeneratedStatement and all FsNote rows.
  3. The DOCX lands in S3 at financial-statements/<clientId>/<engagementId>/<filename>.docx and a signed URL is returned.
  4. Engagement stays at NOTES_COMPLETE — export does not advance status. Multiple export versions are allowed; each is audit-logged.

Validation and Control Checks

  • Statement must be isBalanced = true (or the export emits a banner warning on the cover).
  • All mandatory disclosure rules must be answered. The handler refuses to render otherwise.
  • Mandatory policy verbatim text is asserted by the renderer against the constitution before write.

Acceptance Criteria

  • [ ] DOCX persisted to S3
  • [ ] Signed URL returned to the staff user
  • [ ] AuditEvent fs.export.generated

Step 8.0: Client Review via Portal (Optional, NOTES_COMPLETE)

Process Phase: REVIEW

Actor: Client Approver

Action Description

Optionally send the engagement to the client for sign-off via the magic-link portal. The portal renders a read-only preview and offers Approve / Request Revision.

Micro-Steps

  1. Staff clicks Send to client for review. The handler creates a PortalInvitation with resourceType = FS_ENGAGEMENT, token.product = FINANCIAL_STATEMENTS, expiry 30 days.
  2. Email sent to the client approver: subject "Financial statements ready for review", body links to /portal/<token>.
  3. The portal verifies the token, loads the engagement summary + statements preview, and shows two CTAs:
    • Approve — captures decision, audits, notifies staff.
    • Request revision — captures a comment, audits, notifies staff. The engagement stays at NOTES_COMPLETE; the preparer addresses the comment and re-runs upstream steps as needed.

Acceptance Criteria

  • [ ] Portal token verified
  • [ ] Client decision captured with timestamp and (if any) revision comment
  • [ ] AuditEvent fs.client.approved or fs.client.revision_requested

Step 9.0: Archive (NOTES_COMPLETE → ARCHIVED)

Process Phase: ARCHIVE

Actor: FS Reviewer / Senior Accountant / Platform Admin

Action Description

Seal the engagement at the end of the audit cycle. No further edits.

Micro-Steps

  1. From the engagement detail page, click Archive.
  2. Engagement transitions NOTES_COMPLETE → ARCHIVED.
  3. Retention policy applies (default 7 years per accountancy norms; configurable).

Acceptance Criteria

  • [ ] Engagement is ARCHIVED and read-only
  • [ ] AuditEvent fs.engagement.archived

7. System Architecture for AI Implementation

Process Input Schema (High Level)

Field NameData TypeValidation RulesRequired/OptionalSource System
clientIdUUIDFINANCIAL_STATEMENTS in enabledProductsRequiredClient master
financialYearStringFree-form; usually FY<year>RequiredEngagement create
yearEndDateYYYY-MM-DDAgrees with fsConfig.fyEndMonthRequiredEngagement create
reportingStandardEnumSFRS_SE or FULL_SFRSRequiredEngagement create
tbFileCSV / XLSXMax 10 MB; balanced ±1.0 SGDRequiredTB upload
taxonomyKey (per row)StringMember of sfrs_taxonomy.jsonRequired after mappingMapping step
disclosureResponsesJSONBOne per (ruleId, promptId)Required for mandatoryInterrogation step

Process Output Schema (High Level)

OutputData TypeFormat SpecificationDestinationRetention
FsGeneratedStatementJSONBNested BS / P&L / SOCE / CFDB + versioned in DBPer policy
FsNoteJSONB blocks{type, ...} block arrayDBPer policy
DOCX exportFileWord documentS3 + signed URLPer policy
Audit eventsAppend-onlyAuditEvent rowsDBImmutable

Integration Points

IntegrationProtocolDirectionError Handling
Claude Sonnet (mapping)Anthropic SDKOutboundRetry with backoff; persist on first success
Claude Sonnet (notes)Anthropic SDKOutboundOn failure → NOTES_ERROR; manual retry path
S3 (TB / DOCX)S3 clientIn/OutboundStandard SDK retry; checksum on download
Email (client portal)SMTP adapterOutboundOutbox-poller with retry; manual reissue path

Constitution & Loader Contract

  • loadSfrsTaxonomy() — reads sfrs_taxonomy.json. Cached on first call. Clear via clearTaxonomyCache() in tests.
  • loadDisclosureRules() — reads disclosure_rules.json. Cached. Clear via clearDisclosureCache().
  • evaluateTriggerCondition(condition, presentKeys) — evaluates always or tb_contains_any([...]) joined by " OR ".
  • evaluateDisclosureGaps(presentKeys) — returns triggered rules sorted by priority then rule_id.
  • findTaxonomyItem(key) — returns the SFRS taxonomy item or undefined. Use this instead of dipping into the JSON.

8. Quality Controls and Compliance Checkpoints

CheckpointWhat to CheckWho ChecksFrequencyRecord
TB balanceΣ currentYearBalance = 0 ±1.0 SGDFSEE, PreparerPer uploadAuditEvent
Mapping completenessEvery TB row has a taxonomyKeyFSEE, PreparerPre-statement-genFsTrialBalanceItem
Statement balancingtotal_assets = total_equity_and_liabilities ±1.0 SGDFSEEPer generationFsGeneratedStatement.isBalanced
Mandatory disclosuresEvery mandatory rule has every required prompt answeredFSEE, ReviewerPre-notesFsDisclosureResponse
Verbatim policy assertionMandatory policy text appears unchanged in note_policiesDOCX rendererPer exportRenderer-side assert
Reviewer sign-offReviewer ran the export — EXPORT_FS_DOCUMENT action is in the audit trailReviewerPer engagementAuditEvent
Client approvalClient decision captured before archiving (when portal review used)StaffPer engagementAuditEvent

  • SFRS for Small Entities (Section 29 — Income Taxes; Section 28 — Employee Benefits; Section 21 — Provisions and Contingencies)
  • Full SFRS (FRS 16 — PPE; FRS 116 — Leases; FRS 115 — Revenue; FRS 12 — Income Taxes; FRS 19 — Employee Benefits)
  • Companies Act 1967 (Section 201 — Directors Report)
  • ACRA filing requirements (informs disclosure set; XBRL filing is downstream)
  • Personal Data Protection Act (PDPA) — directors' personal data appears in financial statements

10. Records Management and Retention

Records to store for each engagement

  • Raw trial-balance file (CSV/XLSX) — S3
  • FsTrialBalanceItem rows (every TB row + mapping history)
  • FsGeneratedStatement rows (every version)
  • FsNote rows
  • FsDisclosureResponse rows
  • DOCX exports (every version)
  • Client portal sign-off evidence (if used)
  • AuditEvent rows — append-only

Retention

Minimum 7 years from the engagement year-end, consistent with Singapore accountancy norms. Configurable per client jurisdiction.


11. Risk Assessment and Mitigation

Risk DescriptionLikelihood (1-5)Impact (1-5)Risk ScoreCategory
Misclassification of TB row to wrong SFRS taxonomy key3412Reporting
Mandatory disclosure missed (rule trigger fired but not answered)2510Compliance
Mandatory policy text edited inadvertently155Compliance
Claude notes-generation hallucination contradicting disclosure responses248AI / Reporting
Statement unbalanced shipped to client155Reporting
Stale notes against regenerated statements339Operational

Preventive Controls

  • AI mapping suggestions are advisory; every row carries mappingConfidence and the reviewer is prompted to confirm LOW / MEDIUM rows.
  • Disclosure rule engine fires deterministically against the constitution; mandatory rules cannot be silenced.
  • Mandatory policy texts live in immutable Markdown files; the LLM is instructed not to paraphrase, and the DOCX renderer asserts the text against the constitution before write.
  • Notes generator runs as a single 16K-token consolidated call with the statement JSON in-prompt, so the LLM cannot drift from the figures.
  • Statement balancer flags any unbalance with a red banner and prevents silent export.

Escalation

  • Mapping override on a mandatory-rule-triggering taxonomy key escalates to Senior Accountant for review.
  • NOTES_ERROR recovery: if the same engagement re-fails three times, escalate to Senior Accountant; the failure record contains the model id, request id, and prompt fingerprint.

12. Process Re-engineering and Continual Improvement

  • Quarterly review of mappingConfidence distribution and override rate. A drift toward LOW confidence indicates a taxonomy gap.
  • After every ACRA / SFRS update, the constitution files are versioned by PR and the disclosure rule library is re-checked against the changes.
  • Track Claude token consumption per engagement; the notes prompt is the largest contributor and is a prompt-caching candidate.
  • Track DOCX visual-regression test outcomes against financial_statements_final3.docx (port baseline from the finstatement source repo).

13. Training and Competency Requirements

Minimum Competency Standards

  • Working knowledge of SFRS for Small Entities (or Full SFRS for engagements that require it).
  • Familiarity with the disclosure rule library and the Companies Act Section 201 Directors Report.
  • Understanding of the export gate: a FS_PREPARER cannot export — only FS_REVIEWER / SENIOR_ACCOUNTANT / PLATFORM_ADMIN.

Training Program

  • New preparers shadow two complete engagements end-to-end before independent ownership.
  • Reviewers spend one cycle pairing with the platform team to walk every audit event for a sample engagement.
  • Annual refresher on SFRS / Companies Act updates that affect the taxonomy or disclosure rules.


15. Appendices

Appendix A: Process Flow (Swimlane Description)

  • FS Preparer creates the engagement and uploads the trial balance.
  • FSEE parses TB, normalises signs, persists rows.
  • FS Preparer runs auto-suggest mapping; reviews and overrides low-confidence rows.
  • FSEE runs deterministic statement assembly; flags anomalies if any.
  • FS Preparer walks the disclosure interrogation accordion.
  • FSEE runs the Claude notes call; on success persists notes; on failure transitions to NOTES_ERROR for recovery.
  • FS Reviewer exports DOCX.
  • Optional: staff dispatches the portal magic link; client approves or requests revision.
  • FS Reviewer archives the engagement at engagement close.

Appendix B: Worked Example (Fictional Data — AURORA)

ClientCode: AURORA (seeded as a financial-statements-only client)

Engagement: AURORA / FY2024 (year-end 2024-12-31, SFRS for Small Entities, SGD)

  • 13 TB rows uploaded, every row mapped to an SFRS taxonomy key (10 HIGH, 2 MEDIUM, 1 LOW — accumulated_depreciation mapped to ppe).
  • Statements generated v1, is_balanced = true. Profit after tax: SGD 73,900 (PY: SGD 55,400).
  • Mandatory disclosures DISC-001 / DISC-002 / DISC-003 pre-filled in seed. Conditional rules DISC-010 (PPE), DISC-013 (Trade Receivables), DISC-014 (Cash), DISC-018 (Revenue) fire automatically.
  • Notes generation pending (service layer ships in Phase 1).
  • DOCX export pending.

Appendix C: Junior Staff Decision Guide

If You See This...Then Do This...Escalate?
TB upload returns "not balanced" warningOpen the TB file, find the unbalanced rows, fix at source, re-uploadNo
Auto-suggest returns LOW confidence on > 30% of rowsThe TB column structure may have confused the parser; check sign normalisation and re-runYes, to SA
Mandatory rule has no answer after interrogationRefuse to enqueue notes generation; revisit interrogationNo
Statement says total_assets != total_equity_and_liabilitiesOpen the mapping table, look for sign-flip flags, correct the affected row, regenerateYes if recurs
NOTES_ERROR after a generate attemptOpen the failure record; check the model id and request id; retry if the error was transientYes if 2+
DOCX export refuses with "mandatory policy mismatch"A note row has been edited away from the constitution text; regenerate notesYes, to SA

Appendix D: Escalation Matrix

  • Preparer to Reviewer: any LOW or UNCLASSIFIED taxonomy mapping; sign-flip anomaly on the statement.
  • Reviewer to Senior Accountant: mandatory disclosure rule answered with non-trivial assumptions; mandatory policy text dispute.
  • Senior Accountant to Managing Director: SFRS / Companies Act interpretation that affects multiple clients.

Appendix E: Completion Checklist

  • [ ] Engagement created in DRAFT
  • [ ] Trial balance uploaded; engagement reached TB_UPLOADED
  • [ ] Every TB row mapped to an SFRS taxonomy key; engagement reached TB_MAPPED
  • [ ] Statements generated; is_balanced = true
  • [ ] Every triggered mandatory disclosure rule answered
  • [ ] Notes generated; engagement reached NOTES_COMPLETE
  • [ ] DOCX exported
  • [ ] Optional: client portal approval captured
  • [ ] Engagement archived; retention applied

End of SOP

Internal use only — BreezyCorp