1use chrono::NaiveDate;
8use rust_decimal::Decimal;
9use serde::{Deserialize, Serialize};
10use uuid::Uuid;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct FrameworkDifferenceRecord {
15 pub difference_id: Uuid,
17
18 pub company_code: String,
20
21 pub period_date: NaiveDate,
23
24 pub difference_area: DifferenceArea,
26
27 pub source_reference: String,
29
30 pub description: String,
32
33 #[serde(with = "datasynth_core::serde_decimal")]
35 pub us_gaap_amount: Decimal,
36
37 #[serde(with = "datasynth_core::serde_decimal")]
39 pub ifrs_amount: Decimal,
40
41 #[serde(with = "datasynth_core::serde_decimal")]
43 pub difference_amount: Decimal,
44
45 pub us_gaap_classification: String,
47
48 pub ifrs_classification: String,
50
51 pub explanation: String,
53
54 pub difference_type: DifferenceType,
56
57 pub financial_statement_impact: FinancialStatementImpact,
59}
60
61impl FrameworkDifferenceRecord {
62 pub fn new(
64 company_code: impl Into<String>,
65 period_date: NaiveDate,
66 difference_area: DifferenceArea,
67 source_reference: impl Into<String>,
68 description: impl Into<String>,
69 us_gaap_amount: Decimal,
70 ifrs_amount: Decimal,
71 ) -> Self {
72 let difference_amount = ifrs_amount - us_gaap_amount;
73 Self {
74 difference_id: Uuid::now_v7(),
75 company_code: company_code.into(),
76 period_date,
77 difference_area,
78 source_reference: source_reference.into(),
79 description: description.into(),
80 us_gaap_amount,
81 ifrs_amount,
82 difference_amount,
83 us_gaap_classification: String::new(),
84 ifrs_classification: String::new(),
85 explanation: String::new(),
86 difference_type: DifferenceType::Temporary,
87 financial_statement_impact: FinancialStatementImpact::default(),
88 }
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
94#[serde(rename_all = "snake_case")]
95pub enum DifferenceArea {
96 RevenueRecognition,
98 LeaseAccounting,
100 InventoryCosting,
102 DevelopmentCosts,
104 PropertyRevaluation,
106 Impairment,
108 ContingentLiabilities,
110 ShareBasedPayment,
112 FinancialInstruments,
114 Consolidation,
116 JointArrangements,
118 IncomeTaxes,
120 PresentationDisclosure,
122 Other,
124}
125
126impl std::fmt::Display for DifferenceArea {
127 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128 match self {
129 Self::RevenueRecognition => write!(f, "Revenue Recognition"),
130 Self::LeaseAccounting => write!(f, "Lease Accounting"),
131 Self::InventoryCosting => write!(f, "Inventory Costing"),
132 Self::DevelopmentCosts => write!(f, "Development Costs"),
133 Self::PropertyRevaluation => write!(f, "Property Revaluation"),
134 Self::Impairment => write!(f, "Impairment"),
135 Self::ContingentLiabilities => write!(f, "Contingent Liabilities"),
136 Self::ShareBasedPayment => write!(f, "Share-Based Payment"),
137 Self::FinancialInstruments => write!(f, "Financial Instruments"),
138 Self::Consolidation => write!(f, "Consolidation"),
139 Self::JointArrangements => write!(f, "Joint Arrangements"),
140 Self::IncomeTaxes => write!(f, "Income Taxes"),
141 Self::PresentationDisclosure => write!(f, "Presentation & Disclosure"),
142 Self::Other => write!(f, "Other"),
143 }
144 }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
149#[serde(rename_all = "snake_case")]
150pub enum DifferenceType {
151 #[default]
153 Temporary,
154 Permanent,
156 Classification,
158 Measurement,
160 Timing,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, Default)]
166pub struct FinancialStatementImpact {
167 #[serde(with = "datasynth_core::serde_decimal")]
169 pub assets_impact: Decimal,
170 #[serde(with = "datasynth_core::serde_decimal")]
172 pub liabilities_impact: Decimal,
173 #[serde(with = "datasynth_core::serde_decimal")]
175 pub equity_impact: Decimal,
176 #[serde(with = "datasynth_core::serde_decimal")]
178 pub revenue_impact: Decimal,
179 #[serde(with = "datasynth_core::serde_decimal")]
181 pub expense_impact: Decimal,
182 #[serde(with = "datasynth_core::serde_decimal")]
184 pub net_income_impact: Decimal,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct FrameworkReconciliation {
190 pub company_code: String,
192
193 pub period_date: NaiveDate,
195
196 #[serde(with = "datasynth_core::serde_decimal")]
198 pub us_gaap_net_income: Decimal,
199
200 #[serde(with = "datasynth_core::serde_decimal")]
202 pub ifrs_net_income: Decimal,
203
204 #[serde(with = "datasynth_core::serde_decimal")]
206 pub us_gaap_equity: Decimal,
207
208 #[serde(with = "datasynth_core::serde_decimal")]
210 pub ifrs_equity: Decimal,
211
212 #[serde(with = "datasynth_core::serde_decimal")]
214 pub us_gaap_assets: Decimal,
215
216 #[serde(with = "datasynth_core::serde_decimal")]
218 pub ifrs_assets: Decimal,
219
220 pub reconciling_items: Vec<ReconcilingItem>,
222}
223
224impl FrameworkReconciliation {
225 pub fn new(company_code: impl Into<String>, period_date: NaiveDate) -> Self {
227 Self {
228 company_code: company_code.into(),
229 period_date,
230 us_gaap_net_income: Decimal::ZERO,
231 ifrs_net_income: Decimal::ZERO,
232 us_gaap_equity: Decimal::ZERO,
233 ifrs_equity: Decimal::ZERO,
234 us_gaap_assets: Decimal::ZERO,
235 ifrs_assets: Decimal::ZERO,
236 reconciling_items: Vec::new(),
237 }
238 }
239
240 pub fn calculate_totals(&mut self) {
242 let mut income_adjustment = Decimal::ZERO;
243 let mut equity_adjustment = Decimal::ZERO;
244 let mut asset_adjustment = Decimal::ZERO;
245
246 for item in &self.reconciling_items {
247 income_adjustment += item.net_income_impact;
248 equity_adjustment += item.equity_impact;
249 asset_adjustment += item.asset_impact;
250 }
251
252 self.ifrs_net_income = self.us_gaap_net_income + income_adjustment;
253 self.ifrs_equity = self.us_gaap_equity + equity_adjustment;
254 self.ifrs_assets = self.us_gaap_assets + asset_adjustment;
255 }
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize)]
260pub struct ReconcilingItem {
261 pub description: String,
263
264 pub difference_area: DifferenceArea,
266
267 #[serde(with = "datasynth_core::serde_decimal")]
269 pub net_income_impact: Decimal,
270
271 #[serde(with = "datasynth_core::serde_decimal")]
273 pub equity_impact: Decimal,
274
275 #[serde(with = "datasynth_core::serde_decimal")]
277 pub asset_impact: Decimal,
278
279 #[serde(with = "datasynth_core::serde_decimal")]
281 pub liability_impact: Decimal,
282
283 pub explanation: String,
285}
286
287impl ReconcilingItem {
288 pub fn new(
312 description: impl Into<String>,
313 difference_area: DifferenceArea,
314 net_income_impact: Decimal,
315 ) -> Self {
316 Self {
317 description: description.into(),
318 difference_area,
319 net_income_impact,
320 equity_impact: net_income_impact,
323 asset_impact: Decimal::ZERO,
324 liability_impact: Decimal::ZERO,
325 explanation: String::new(),
326 }
327 }
328}
329
330#[cfg(test)]
331#[allow(clippy::unwrap_used)]
332mod tests {
333 use super::*;
334 use rust_decimal_macros::dec;
335
336 #[test]
337 fn test_framework_difference_record() {
338 let record = FrameworkDifferenceRecord::new(
339 "1000",
340 NaiveDate::from_ymd_opt(2024, 12, 31).unwrap(),
341 DifferenceArea::DevelopmentCosts,
342 "RD001",
343 "Software development costs",
344 dec!(0), dec!(100000), );
347
348 assert_eq!(record.difference_amount, dec!(100000));
349 assert_eq!(record.difference_area, DifferenceArea::DevelopmentCosts);
350 }
351
352 #[test]
353 fn test_framework_reconciliation() {
354 let mut recon =
355 FrameworkReconciliation::new("1000", NaiveDate::from_ymd_opt(2024, 12, 31).unwrap());
356
357 recon.us_gaap_net_income = dec!(1000000);
358 recon.us_gaap_equity = dec!(5000000);
359 recon.us_gaap_assets = dec!(10000000);
360
361 recon.reconciling_items.push(ReconcilingItem::new(
363 "Development cost capitalization",
364 DifferenceArea::DevelopmentCosts,
365 dec!(100000), ));
367
368 recon.reconciling_items.push(ReconcilingItem::new(
369 "Impairment reversal",
370 DifferenceArea::Impairment,
371 dec!(50000), ));
373
374 recon.calculate_totals();
375
376 assert_eq!(recon.ifrs_net_income, dec!(1150000));
377 assert_eq!(recon.ifrs_equity, dec!(5150000));
378 }
379}