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