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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
//! Pension models — IAS 19 / ASC 715.
//!
//! This module provides data models for defined benefit pension plans,
//! including actuarial assumptions, defined benefit obligation (DBO)
//! roll-forwards, plan asset roll-forwards, and pension disclosures.
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
// ---------------------------------------------------------------------------
// Enums
// ---------------------------------------------------------------------------
/// Type of pension plan.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum PensionPlanType {
/// Traditional defined benefit — benefit formula-driven obligation.
#[default]
DefinedBenefit,
/// Hybrid cash balance — lump sum account with interest credits.
HybridCashBalance,
}
// ---------------------------------------------------------------------------
// Actuarial Assumptions
// ---------------------------------------------------------------------------
/// Actuarial assumptions used to measure the defined benefit obligation.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActuarialAssumptions {
/// Discount rate used to present-value future benefit payments (e.g. 0.04 = 4%).
#[serde(with = "crate::serde_decimal")]
pub discount_rate: Decimal,
/// Expected annual salary growth rate (e.g. 0.03 = 3%).
#[serde(with = "crate::serde_decimal")]
pub salary_growth_rate: Decimal,
/// Expected annual pension increase rate post-retirement (e.g. 0.02 = 2%).
#[serde(with = "crate::serde_decimal")]
pub pension_increase_rate: Decimal,
/// Long-term expected return on plan assets (e.g. 0.06 = 6%).
#[serde(with = "crate::serde_decimal")]
pub expected_return_on_plan_assets: Decimal,
}
// ---------------------------------------------------------------------------
// Defined Benefit Plan
// ---------------------------------------------------------------------------
/// A defined benefit pension plan sponsored by an entity.
///
/// One plan is generated per reporting entity. The plan references its
/// obligation and asset roll-forwards via `plan_id`.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DefinedBenefitPlan {
/// Unique plan identifier (e.g. "PLAN-1000-DB").
pub id: String,
/// Company / entity code that sponsors this plan.
pub entity_code: String,
/// Human-readable plan name (e.g. "Acme Corp Retirement Plan").
pub plan_name: String,
/// Plan type — defined benefit or hybrid cash balance.
pub plan_type: PensionPlanType,
/// Number of active plan participants (employees enrolled).
pub participant_count: u32,
/// Actuarial assumptions used for valuation.
pub assumptions: ActuarialAssumptions,
/// Reporting currency code (e.g. "USD").
pub currency: String,
}
// ---------------------------------------------------------------------------
// DBO Roll-forward
// ---------------------------------------------------------------------------
/// Defined Benefit Obligation (DBO) roll-forward for one reporting period.
///
/// Reconciles opening to closing DBO per IAS 19.140 / ASC 715-20-50.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PensionObligation {
/// Reference to the parent `DefinedBenefitPlan.id`.
pub plan_id: String,
/// Period label (e.g. "2024-01" or "FY2024").
pub period: String,
/// DBO at start of period.
#[serde(with = "crate::serde_decimal")]
pub dbo_opening: Decimal,
/// Current service cost — present value of benefits earned by employees
/// during the current period.
#[serde(with = "crate::serde_decimal")]
pub service_cost: Decimal,
/// Interest cost — unwinding of the discount on the obligation
/// (`dbo_opening × discount_rate`).
#[serde(with = "crate::serde_decimal")]
pub interest_cost: Decimal,
/// Actuarial gains (negative) or losses (positive) arising from
/// changes in assumptions or experience adjustments.
#[serde(with = "crate::serde_decimal")]
pub actuarial_gains_losses: Decimal,
/// Benefits paid to retirees during the period (reduces DBO).
#[serde(with = "crate::serde_decimal")]
pub benefits_paid: Decimal,
/// DBO at end of period.
/// Identity: `dbo_opening + service_cost + interest_cost + actuarial_gains_losses − benefits_paid`
#[serde(with = "crate::serde_decimal")]
pub dbo_closing: Decimal,
}
// ---------------------------------------------------------------------------
// Plan Assets Roll-forward
// ---------------------------------------------------------------------------
/// Plan assets roll-forward for one reporting period.
///
/// Tracks the fair value of assets held in trust to fund pension obligations.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlanAssets {
/// Reference to the parent `DefinedBenefitPlan.id`.
pub plan_id: String,
/// Period label.
pub period: String,
/// Fair value of plan assets at start of period.
#[serde(with = "crate::serde_decimal")]
pub fair_value_opening: Decimal,
/// Expected return on plan assets
/// (`fair_value_opening × expected_return_on_plan_assets`).
#[serde(with = "crate::serde_decimal")]
pub expected_return: Decimal,
/// Actuarial gain (positive) or loss (negative) on plan assets
/// (actual return vs. expected return).
#[serde(with = "crate::serde_decimal")]
pub actuarial_gain_loss: Decimal,
/// Employer contributions paid into the plan trust.
#[serde(with = "crate::serde_decimal")]
pub employer_contributions: Decimal,
/// Benefits paid out of the plan trust during the period.
#[serde(with = "crate::serde_decimal")]
pub benefits_paid: Decimal,
/// Fair value of plan assets at end of period.
/// Identity: `fair_value_opening + expected_return + actuarial_gain_loss + employer_contributions − benefits_paid`
#[serde(with = "crate::serde_decimal")]
pub fair_value_closing: Decimal,
}
// ---------------------------------------------------------------------------
// Pension Disclosure
// ---------------------------------------------------------------------------
/// Summary pension disclosure amounts for a reporting period.
///
/// Provides the key balance-sheet and income-statement figures required by
/// IAS 19 / ASC 715 disclosures.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PensionDisclosure {
/// Reference to the parent `DefinedBenefitPlan.id`.
pub plan_id: String,
/// Period label.
pub period: String,
/// Net pension liability recognised on the balance sheet
/// (`dbo_closing − fair_value_closing`).
/// Positive = under-funded (liability); negative = over-funded (asset).
#[serde(with = "crate::serde_decimal")]
pub net_pension_liability: Decimal,
/// Total pension expense recognised in profit or loss
/// (`service_cost + interest_cost − expected_return`).
#[serde(with = "crate::serde_decimal")]
pub pension_expense: Decimal,
/// Remeasurements recognised in Other Comprehensive Income (OCI).
/// Combines obligation actuarial gains/losses and plan asset actuarial gains/losses.
/// Negative = gain in OCI; positive = loss recognised in OCI.
#[serde(with = "crate::serde_decimal")]
pub oci_remeasurements: Decimal,
/// Funding ratio: `fair_value_closing / dbo_closing` (expressed as a decimal, e.g. 0.95).
/// Zero when DBO is zero.
#[serde(with = "crate::serde_decimal")]
pub funding_ratio: Decimal,
}