Skip to main content

datasynth_core/models/
financial_statement_notes.rs

1//! Notes to financial statements models.
2//!
3//! Provides structured data models for the disclosures that accompany the
4//! primary financial statements.  Notes are required by virtually every
5//! financial reporting framework (IFRS IAS 1, ASC 235, etc.) and include
6//! accounting policies, detail disclosures, contingencies, subsequent events,
7//! related parties, segment information, and standard-specific items.
8
9use chrono::NaiveDate;
10use rust_decimal::Decimal;
11use serde::{Deserialize, Serialize};
12
13// ---------------------------------------------------------------------------
14// Enums
15// ---------------------------------------------------------------------------
16
17/// High-level category of a note to the financial statements.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
19#[serde(rename_all = "snake_case")]
20pub enum NoteCategory {
21    /// Summary of significant accounting policies (IAS 1.117 / ASC 235-10).
22    #[default]
23    AccountingPolicy,
24    /// Detailed breakdown supporting a line item on the face of the statements.
25    DetailDisclosure,
26    /// Provisions, contingent liabilities, and contingent assets (IAS 37 / ASC 450).
27    Contingency,
28    /// Events after the reporting period (IAS 10 / ASC 855).
29    SubsequentEvent,
30    /// Related party transactions and balances (IAS 24 / ASC 850).
31    RelatedParty,
32    /// Operating segment disclosures (IFRS 8 / ASC 280).
33    SegmentInformation,
34    /// Disclosures specific to a particular accounting standard.
35    StandardSpecific,
36}
37
38/// A typed cell value inside a disclosure table.
39#[derive(Debug, Clone, Serialize, Deserialize)]
40#[serde(rename_all = "snake_case", tag = "type", content = "value")]
41pub enum NoteTableValue {
42    /// Plain text cell.
43    Text(String),
44    /// Monetary or numeric amount.
45    Amount(#[serde(with = "rust_decimal::serde::str")] Decimal),
46    /// Percentage value (stored as a fraction, e.g. 0.25 = 25 %).
47    Percentage(#[serde(with = "rust_decimal::serde::str")] Decimal),
48    /// Date cell.
49    Date(NaiveDate),
50    /// Empty / not applicable cell.
51    Empty,
52}
53
54// ---------------------------------------------------------------------------
55// Note building blocks
56// ---------------------------------------------------------------------------
57
58/// A two-dimensional disclosure table within a note section.
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct NoteTable {
61    /// Brief caption describing the table's content.
62    pub caption: String,
63    /// Column headers.
64    pub headers: Vec<String>,
65    /// Data rows — each row has the same length as `headers`.
66    pub rows: Vec<Vec<NoteTableValue>>,
67}
68
69/// A single section within a note (heading + narrative + optional tables).
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct NoteSection {
72    /// Section heading (e.g. "Basis of preparation").
73    pub heading: String,
74    /// Explanatory narrative text.
75    pub narrative: String,
76    /// Optional tabular data supporting the narrative.
77    pub tables: Vec<NoteTable>,
78}
79
80// ---------------------------------------------------------------------------
81// Top-level note
82// ---------------------------------------------------------------------------
83
84/// A complete note to the financial statements.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct FinancialStatementNote {
87    /// Sequential note number (1 = first note in the set).
88    pub note_number: u32,
89    /// Short descriptive title.
90    pub title: String,
91    /// Disclosure category.
92    pub category: NoteCategory,
93    /// One or more content sections within this note.
94    pub content_sections: Vec<NoteSection>,
95    /// Cross-references to other notes or financial statement line items.
96    pub cross_references: Vec<String>,
97}
98
99impl FinancialStatementNote {
100    /// Create a minimal note with a single section and no tables.
101    pub fn simple(
102        note_number: u32,
103        title: impl Into<String>,
104        category: NoteCategory,
105        heading: impl Into<String>,
106        narrative: impl Into<String>,
107    ) -> Self {
108        Self {
109            note_number,
110            title: title.into(),
111            category,
112            content_sections: vec![NoteSection {
113                heading: heading.into(),
114                narrative: narrative.into(),
115                tables: Vec::new(),
116            }],
117            cross_references: Vec::new(),
118        }
119    }
120
121    /// Attach a cross-reference to another note.
122    pub fn with_cross_reference(mut self, reference: impl Into<String>) -> Self {
123        self.cross_references.push(reference.into());
124        self
125    }
126}