datasynth_core/models/audit/accounting_estimates.rs
1//! Accounting estimates models — ISA 540.
2//!
3//! Accounting estimates are approximations of monetary amounts in the
4//! absence of a precise means of measurement. ISA 540 (Revised 2019)
5//! requires auditors to evaluate the reasonableness of management's
6//! estimates and identify risks of material misstatement.
7//!
8//! Key estimate types covered:
9//! - Deferred tax provisions (IAS 12 / ASC 740)
10//! - Expected credit losses (IFRS 9 / ASC 326)
11//! - Pension obligations (IAS 19 / ASC 715)
12//! - Fair value measurements (IFRS 13 / ASC 820)
13//! - Impairment tests (IAS 36 / ASC 350–360)
14//! - Provisions for liabilities (IAS 37 / ASC 450)
15//! - Share-based payments (IFRS 2 / ASC 718)
16//! - Depreciation useful-life assessments
17
18use rust_decimal::Decimal;
19use serde::{Deserialize, Serialize};
20
21// ---------------------------------------------------------------------------
22// Enums
23// ---------------------------------------------------------------------------
24
25/// Category of accounting estimate.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27#[serde(rename_all = "snake_case")]
28pub enum EstimateType {
29 /// Deferred tax asset/liability recognition (IAS 12 / ASC 740).
30 DeferredTaxProvision,
31 /// Expected credit loss allowance (IFRS 9 / ASC 326).
32 ExpectedCreditLoss,
33 /// Defined benefit pension obligation (IAS 19 / ASC 715).
34 PensionObligation,
35 /// Level 2/3 fair value measurement (IFRS 13 / ASC 820).
36 FairValueMeasurement,
37 /// Goodwill or long-lived asset impairment test (IAS 36 / ASC 350–360).
38 ImpairmentTest,
39 /// Provision for legal/environmental/warranty liabilities (IAS 37 / ASC 450).
40 ProvisionForLiabilities,
41 /// Share-based payment expense (IFRS 2 / ASC 718).
42 ShareBasedPayment,
43 /// Useful-life revision for property, plant & equipment (IAS 16 / ASC 360).
44 DepreciationUsefulLife,
45}
46
47impl std::fmt::Display for EstimateType {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 let s = match self {
50 Self::DeferredTaxProvision => "Deferred Tax Provision",
51 Self::ExpectedCreditLoss => "Expected Credit Loss",
52 Self::PensionObligation => "Pension Obligation",
53 Self::FairValueMeasurement => "Fair Value Measurement",
54 Self::ImpairmentTest => "Impairment Test",
55 Self::ProvisionForLiabilities => "Provision for Liabilities",
56 Self::ShareBasedPayment => "Share-Based Payment",
57 Self::DepreciationUsefulLife => "Depreciation Useful Life",
58 };
59 write!(f, "{s}")
60 }
61}
62
63/// Degree of uncertainty inherent in an accounting estimate.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
65#[serde(rename_all = "snake_case")]
66pub enum UncertaintyLevel {
67 /// Outcome is predictable with reasonable confidence.
68 Low,
69 /// Outcome involves some uncertainty; range of outcomes is limited.
70 Medium,
71 /// Outcome is highly sensitive to assumptions; wide range of outcomes possible.
72 High,
73}
74
75/// Complexity of the estimation process.
76#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
77#[serde(rename_all = "snake_case")]
78pub enum EstimateComplexity {
79 /// Standard actuarial or formulaic calculation; limited judgment required.
80 Simple,
81 /// Multiple interdependent inputs; moderate management judgment required.
82 Moderate,
83 /// Sophisticated models, specialist input, or significant management judgment.
84 Complex,
85}
86
87/// Auditor's assessment of an individual key assumption.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
89#[serde(rename_all = "snake_case")]
90pub enum AssumptionAssessment {
91 /// Assumption falls within an acceptable range given the entity's circumstances.
92 Reasonable,
93 /// Assumption is at the favourable end of an acceptable range.
94 Optimistic,
95 /// Assumption is outside or at the extreme end of an acceptable range.
96 Aggressive,
97}
98
99/// Degree of subjectivity in a key assumption.
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
101#[serde(rename_all = "snake_case")]
102pub enum SubjectivityLevel {
103 /// Observable market data or contractual terms determine the assumption.
104 Low,
105 /// Assumptions are internally derived but corroborated by external evidence.
106 Medium,
107 /// Assumptions rely primarily on management intent or unobservable inputs.
108 High,
109}
110
111// ---------------------------------------------------------------------------
112// Supporting structs
113// ---------------------------------------------------------------------------
114
115/// A key assumption underlying an accounting estimate.
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct EstimateAssumption {
118 /// Plain-language description of the assumption (e.g. "discount rate 4.5 %").
119 pub description: String,
120 /// Sensitivity of the estimate to a 1-unit change in this assumption
121 /// (expressed as absolute monetary impact).
122 #[serde(with = "crate::serde_decimal")]
123 pub sensitivity: Decimal,
124 /// Auditor's assessment of the assumption's reasonableness.
125 pub reasonableness: AssumptionAssessment,
126}
127
128/// ISA 540 risk factor indicators for the estimate.
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct Isa540RiskFactors {
131 /// Estimation uncertainty indicator per ISA 540.
132 pub estimation_uncertainty: UncertaintyLevel,
133 /// Complexity of the estimation process.
134 pub complexity: EstimateComplexity,
135 /// Degree of management subjectivity in key assumptions.
136 pub subjectivity: SubjectivityLevel,
137}
138
139/// Retrospective review comparing the prior-period estimate to the actual outcome.
140///
141/// ISA 540 requires auditors to review management's prior-period estimates
142/// to detect indications of possible management bias.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct RetrospectiveReview {
145 /// Management's estimate at the prior period-end.
146 #[serde(with = "crate::serde_decimal")]
147 pub prior_period_estimate: Decimal,
148 /// Actual outcome observed in the current period.
149 #[serde(with = "crate::serde_decimal")]
150 pub actual_outcome: Decimal,
151 /// Monetary variance (actual − estimate).
152 #[serde(with = "crate::serde_decimal")]
153 pub variance: Decimal,
154 /// Variance expressed as a percentage of the prior-period estimate.
155 #[serde(with = "crate::serde_decimal")]
156 pub variance_percentage: Decimal,
157 /// `true` if the direction of variance suggests consistent management bias
158 /// (e.g. estimate consistently overstated vs actual).
159 pub management_bias_indicator: bool,
160}
161
162// ---------------------------------------------------------------------------
163// Primary model
164// ---------------------------------------------------------------------------
165
166/// A single accounting estimate reviewed under ISA 540.
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct AccountingEstimate {
169 /// Unique identifier for this estimate.
170 pub id: String,
171 /// Entity code of the reporting entity.
172 pub entity_code: String,
173 /// Type of accounting estimate.
174 pub estimate_type: EstimateType,
175 /// Human-readable description (e.g. "ECL allowance — trade receivables").
176 pub description: String,
177 /// Management's point estimate (the amount recognised in the financial statements).
178 #[serde(with = "crate::serde_decimal")]
179 pub management_point_estimate: Decimal,
180 /// Auditor's independent point estimate (when developed as an audit procedure).
181 #[serde(
182 default,
183 skip_serializing_if = "Option::is_none",
184 with = "crate::serde_decimal::option"
185 )]
186 pub auditor_point_estimate: Option<Decimal>,
187 /// Auditor's assessment of estimation uncertainty per ISA 540.
188 pub estimation_uncertainty: UncertaintyLevel,
189 /// Auditor's assessment of the complexity of the estimation process.
190 pub complexity: EstimateComplexity,
191 /// Key assumptions identified and assessed during the audit.
192 pub assumptions: Vec<EstimateAssumption>,
193 /// Retrospective review of the prior-period estimate (when applicable).
194 pub retrospective_review: Option<RetrospectiveReview>,
195 /// Consolidated ISA 540 risk factor indicators for the estimate.
196 pub isa540_risk_factors: Isa540RiskFactors,
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use rust_decimal_macros::dec;
203
204 #[test]
205 fn test_estimate_type_display() {
206 assert_eq!(
207 EstimateType::ExpectedCreditLoss.to_string(),
208 "Expected Credit Loss"
209 );
210 assert_eq!(
211 EstimateType::PensionObligation.to_string(),
212 "Pension Obligation"
213 );
214 }
215
216 #[test]
217 fn test_accounting_estimate_roundtrip() {
218 let estimate = AccountingEstimate {
219 id: "EST-001".to_string(),
220 entity_code: "C001".to_string(),
221 estimate_type: EstimateType::ExpectedCreditLoss,
222 description: "ECL allowance — trade receivables".to_string(),
223 management_point_estimate: dec!(125000.00),
224 auditor_point_estimate: Some(dec!(130000.00)),
225 estimation_uncertainty: UncertaintyLevel::High,
226 complexity: EstimateComplexity::Moderate,
227 assumptions: vec![EstimateAssumption {
228 description: "12-month default rate 2.5%".to_string(),
229 sensitivity: dec!(50000.00),
230 reasonableness: AssumptionAssessment::Reasonable,
231 }],
232 retrospective_review: Some(RetrospectiveReview {
233 prior_period_estimate: dec!(115000.00),
234 actual_outcome: dec!(118000.00),
235 variance: dec!(3000.00),
236 variance_percentage: dec!(2.61),
237 management_bias_indicator: false,
238 }),
239 isa540_risk_factors: Isa540RiskFactors {
240 estimation_uncertainty: UncertaintyLevel::High,
241 complexity: EstimateComplexity::Moderate,
242 subjectivity: SubjectivityLevel::Medium,
243 },
244 };
245
246 let json = serde_json::to_string(&estimate).unwrap();
247 let parsed: AccountingEstimate = serde_json::from_str(&json).unwrap();
248 assert_eq!(parsed.entity_code, "C001");
249 assert_eq!(parsed.estimate_type, EstimateType::ExpectedCreditLoss);
250 assert!(parsed.auditor_point_estimate.is_some());
251 }
252}