bus-accounts — chart of accounts and stable references (SDD)
bus-accounts — chart of accounts and stable references
Introduction and Overview
Bus Accounts maintains the chart of accounts as schema-validated repository data. It enforces uniqueness and allowed account types, provides stable account references for downstream modules, and owns the account-to-statement mapping dataset contract used by Finnish statutory statement layouts in bus-reports.
Requirements
FR-ACC-001 Account registry integrity. Bus Accounts MUST maintain a deterministic chart of accounts in accounts.csv with schema validation and stable identifiers. Acceptance criteria: the module refuses duplicate identifiers and invalid account types and writes only schema-valid rows and emits only schema-valid schemas.
FR-ACC-002 CLI surface for account lifecycle. Bus Accounts MUST provide commands to initialize, list, add, set, and validate accounts so workflows can manage the chart of accounts without manual file edits. Acceptance criteria: the command names init, list, add, set, and validate are available under bus accounts, and each command fails with deterministic diagnostics on invalid inputs.
FR-ACC-005 Table Schema correctness. The file accounts.schema.json MUST be a valid Frictionless Table Schema document and MUST be parseable by bus-data and all BusDK consumers. Acceptance criteria: (1) accounts.schema.json is valid Table Schema and passes bus-data schema and workspace validators. (2) Foreign key contract for the optional parent_code hierarchy field: if a foreignKeys entry is present for parent_code, the reference object MUST always include both resource and fields; for a self-referencing relationship reference.resource MUST be the empty string and reference.fields MUST be "code". (3) If foreignKeys is present it must never be partial: missing reference.resource, missing reference.fields, or malformed reference is an immediate validation error. (4) If the project chooses not to enforce the hierarchy via foreign keys, the schema MUST omit the foreignKeys entry entirely; it MUST never include an incomplete foreignKeys entry.
FR-ACC-004 Add fails when account exists. The add command MUST fail if an account with the same identifier (e.g. --code) already exists in the chart of accounts. Acceptance criteria: invoking bus accounts add with a code that is already present exits non-zero, emits a clear diagnostic to standard error, and does not modify the dataset. Modifying an existing account is done via bus accounts set, not add.
FR-ACC-003 Data-layer init via bus-data library. Bus Accounts MUST perform all initialization of the accounts dataset and schema through the bus-data Go library, not by invoking the bus-data CLI. Before creating or ensuring accounts.csv and accounts.schema.json, the module MUST use the bus-data library to ensure the workspace data package is initialized (i.e. an empty datapackage.json exists at the workspace root when missing, as defined by bus-data init). The module MUST create the accounts table and beside-the-table schema via the bus-data library (e.g. schema init or resource add). After a successful bus accounts init, datapackage.json MUST contain a resource entry for the accounts table with the path to accounts.csv and the associated schema so that workspace-level validation and discovery see the accounts data. Acceptance criteria: bus accounts init does not shell out to bus data; it calls bus-data library APIs only; after init, datapackage.json includes a resource for accounts (path and schema reference); re-running init when the data package already contains the accounts resource and files are consistent is idempotent.
NFR-ACC-001 Deterministic outputs. The module MUST produce deterministic listings and diagnostics so scripting and audits are stable across machines. Acceptance criteria: listings are ordered by stable account identifiers, and diagnostics refer to workspace-relative paths and stable identifiers.
NFR-ACC-002 Path exposure via Go library. The module MUST expose a Go library API that returns the workspace-relative path(s) to its owned data file(s) (e.g. accounts table and schema). Other modules that need read-only access to the chart of accounts raw file(s) MUST obtain the path(s) from this module’s library, not by hardcoding file names. The API MUST be designed so that future dynamic path configuration (e.g. from workspace or data package config) can be supported without breaking consumers; today paths may be conventional defaults (e.g. accounts.csv). Acceptance criteria: the library provides at least one path accessor (e.g. given workspace root, returns the path to the accounts CSV and/or schema); consumers that need to read accounts data for pure-data purposes use this accessor; no consumer hardcodes accounts.csv outside this module.
FR-ACC-006 Statutory statement mapping dataset contract. The module MUST define and own the schema contract for account-to-statement mapping data consumed by bus-reports for fi-* statutory layouts. Acceptance criteria: a dedicated mapping dataset (for example report-account-mapping.csv with beside-the-table schema) defines at least layout_id, account_code, statement_target, layout_line_id, and normal_side; each account mapping is deterministic per selected layout; schema validation catches duplicate or malformed mapping rows before downstream report generation.
Planned: optional helper (or documented recommended accounts) for sole-proprietor owner withdrawals and investments (yksityisotto, yksityissijoitus) that produces a balanced journal entry from configured equity/cash accounts. Until implemented, users post via bus-journal using accounts from this chart.
System Architecture
Bus Accounts is a module that owns the accounts datasets and schemas and exposes a CLI surface that reads and writes those datasets. It integrates with the bus-data Go library for all initialization: ensuring the workspace data package exists, creating accounts.csv and accounts.schema.json, and registering the accounts resource in datapackage.json. It integrates with other modules by providing stable account identifiers and types that become foreign keys in journal, invoice, budget, and reporting datasets.
Key Decisions
KD-ACC-001 Accounts are authoritative repository data. The chart of accounts is stored as workspace datasets with beside-the-table schemas so it remains reviewable and exportable.
KD-ACC-002 Init via bus-data library only. Account baseline creation is implemented on top of the bus-data Go library. The module ensures the data package descriptor exists (bus-data init semantics), then creates accounts.csv and accounts.schema.json and registers the accounts resource in datapackage.json through library calls. This keeps a single code path for descriptor and table creation and aligns with bus-data KD-DAT-004 and bus-config / bus-init practice.
KD-ACC-003 Path exposure for read-only consumption. The module exposes path accessors in its Go library so that other modules can resolve the location of the accounts dataset for read-only access without hardcoding file names. Write access and all account business logic remain in this module; path exposure does not grant write or domain-logic rights.
Component Design and Interfaces
Interface IF-ACC-001 (module CLI). The module exposes bus accounts with subcommands init, list, report, add, set, and validate and follows BusDK CLI conventions for deterministic output and diagnostics.
The report command produces a filing-grade chart-of-accounts (tililuettelo) with deterministic ordering. Supported output formats include text, tsv, csv, markdown, and pdf, and PDF output includes generation metadata and page numbering suitable for statutory archive packages.
The init command creates the baseline accounts dataset and schema when they are absent. It MUST use the bus-data Go library only (no CLI invocation). Init sequence: (1) Call the bus-data library to ensure the workspace data package is initialized — i.e. create an empty datapackage.json at the workspace root when the file is missing, matching bus-data init behavior. (2) Create accounts.csv and accounts.schema.json via the bus-data library (e.g. schema init or resource add). The emitted schema MUST conform to the foreign key contract in FR-ACC-005: if foreignKeys is present for parent_code, each entry’s reference MUST include both resource and fields (self-referencing: reference.resource empty string, reference.fields "code"); if hierarchy is not enforced via foreign keys, foreignKeys MUST be omitted entirely. (3) Ensure datapackage.json contains a resource entry for the accounts table (path to accounts.csv and schema reference) so that after init the data package describes the accounts dataset. If both accounts.csv and accounts.schema.json already exist and are consistent and the data package already contains the accounts resource, init prints a warning to standard error and exits 0 without modifying anything. If only one of the files exists, or the data is inconsistent, or the data package is missing when it should exist, init fails with a clear error to standard error, does not write any file, and exits non-zero (see bus-init FR-INIT-003).
The parent_code field is optional and empty is allowed; if non-empty it is intended to reference an existing account code, and the schema may enforce this via a self-referencing foreign key. Whatever bus accounts init emits MUST be accepted by bus-data’s schema and workspace validators and MUST be consumable by bus-journal without special cases.
The add command creates a new account. It accepts account identity and type parameters: --code <account-id>, --name <account-name>, and --type <asset|liability|equity|income|expense>. The command MUST fail if an account with the same identifier (e.g. the same --code) already exists in the chart of accounts: it MUST exit non-zero, emit a clear diagnostic to standard error, and MUST NOT modify the dataset (FR-ACC-004).
The set command modifies an existing account. It is used to update name, type, or other attributes of an account that already exists (identified by code). The command MUST fail if no account with the given identifier exists. Parameters and semantics are defined by the module implementation; the invariant is that creation is done only via add and updates only via set.
Interface IF-ACC-002 (path accessors, Go library). The module exposes Go library functions that return the workspace-relative path(s) to its owned data file(s). For example, given a workspace root path, the library returns the path to the accounts CSV (and optionally the beside-the-table schema path). Resolution MUST be designed so that default paths are conventional names today but can later be overridden from workspace or data package configuration without changing the consumer API. Consumers use these accessors for read-only access to the raw file(s); they must not write to the path or rely on account business logic except via this module’s own APIs.
Usage examples:
bus accounts init
bus accounts list
bus accounts report --format pdf --output ./reports/tililuettelo.pdf
bus accounts add --code 3000 --name "Consulting Income" --type income
bus accounts set --code 3000 --name "Consulting & Training Income"
bus accounts validate
Data Design
The module reads and writes accounts.csv with a beside-the-table JSON Table Schema. Account identifiers are stable keys used by other datasets. For Finnish statutory reporting, the module also owns the account-mapping dataset contract (for example report-account-mapping.csv with report-account-mapping.schema.json) that maps account ids to statement layout line ids per layout id. Master data owned by this module is stored in the workspace root only; the module does not create or use an accounts/ or other subdirectory for its datasets and schemas. After a successful bus accounts init, the workspace data package (datapackage.json) MUST contain a resource entry for the accounts table: the resource path points to accounts.csv and the resource reflects the beside-the-table schema so that bus data package discover, bus data package validate, and other data-package operations see the accounts dataset.
Other modules that need read-only access to the accounts dataset (e.g. to resolve account codes or read the chart for balance computation in another workspace) MUST obtain the path from this module’s Go library (IF-ACC-002), not by hardcoding accounts.csv. All writes and account-domain logic (validation, types, listing) remain in this module.
Assumptions and Dependencies
Bus Accounts depends on the workspace layout and schema conventions defined in the BusDK design spec and on the bus-data Go library for data package and table initialization. The module MUST integrate with bus-data via the library only (no invocation of the bus data CLI). If the bus-data library is unavailable or the workspace cannot be initialized (e.g. unwritable root), the module fails with deterministic diagnostics. If the accounts datasets or schemas are missing or invalid after an init attempt, the module fails with deterministic diagnostics.
Security Considerations
The chart of accounts is repository data and should be protected by the same access controls as the rest of the workspace. Corrections are recorded as new rows rather than destructive edits to preserve auditability.
Observability and Logging
Command results are written to standard output, and diagnostics are written to standard error with deterministic references to dataset paths and identifiers.
Error Handling and Resilience
Invalid usage exits with a non-zero status and a concise usage error. Schema and invariant violations exit non-zero without modifying datasets.
The validate command MUST validate both the CSV content and the schema document itself. If accounts.schema.json contains a foreignKeys entry where reference.resource is missing or reference is otherwise malformed, bus accounts validate MUST exit non-zero and print a clear error pointing to accounts.schema.json and the offending foreign key path, instead of allowing downstream modules (e.g. bus-journal) to fail later with “unsupported foreign key reference” or similar.
Testing Strategy
Unit tests cover account validation and deterministic listing behavior, and command-level tests exercise init, add, set, list, and validate against fixture workspaces. Tests MUST verify that add fails with non-zero exit and no dataset change when the account code already exists (FR-ACC-004), and that set can modify an existing account and fails when the account does not exist.
Required regression and compatibility coverage: (1) An e2e or command-level test runs bus accounts init in an empty workspace, then bus accounts validate, and asserts success. (2) A regression test proves downstream compatibility: after bus accounts init (and minimal required init for journal/period if needed), run a simple bus journal add that references accounts and assert it does not fail due to schema parsing or foreign key reference errors. (3) A negative test starts from an intentionally malformed accounts.schema.json (e.g. foreignKeys entry with missing reference.resource) and asserts bus accounts validate fails with the expected diagnostic pointing to the schema file and the offending foreign key. Implementers MUST keep these tests in place so the invalid-schema bug cannot recur.
Deployment and Operations
Not Applicable. The module ships as a BusDK CLI component and relies on the standard workspace layout.
Migration/Rollout
Not Applicable. Schema evolution is handled through the standard schema migration workflow for workspace datasets.
Risks
Not Applicable. Module-specific risks are not enumerated beyond the general need for deterministic and audit-friendly account data.
Glossary and Terminology
Chart of accounts: the authoritative list of account identifiers and types stored as workspace datasets.
Account identifier: a stable key used by other datasets to reference accounts.
Data package: the workspace datapackage.json descriptor maintained by bus-data; after accounts init it includes a resource for the accounts table.