Skip to main content

datasynth_generators/period_close/
cash_flow_enhancer.rs

1//! Cash flow enhancer — supplementary CashFlowItem entries from v2.3 data.
2//!
3//! The existing [`FinancialStatementGenerator`] produces a basic indirect-method
4//! cash flow statement.  This module generates **additional** items (from
5//! manufacturing, treasury, tax, and dividend modules) that Task 10 merges into
6//! the financial statement's `cash_flow_items` vector.
7
8use datasynth_core::models::{CashFlowCategory, CashFlowItem};
9use rust_decimal::Decimal;
10
11/// Source data collected from upstream v2.3 generators.
12#[derive(Debug, Clone)]
13pub struct CashFlowSourceData {
14    /// Total depreciation / amortisation for the period (from depreciation runs).
15    pub depreciation_total: Decimal,
16    /// Net movement in provisions for the period (from provision generator).
17    pub provision_movements_net: Decimal,
18    /// Change in accounts-receivable balance (positive = AR increased).
19    pub delta_ar: Decimal,
20    /// Change in accounts-payable balance (positive = AP increased).
21    pub delta_ap: Decimal,
22    /// Change in inventory balance (positive = inventory increased).
23    pub delta_inventory: Decimal,
24    /// Capital expenditure paid in the period (non-negative).
25    pub capex: Decimal,
26    /// Proceeds from new debt issued in the period.
27    pub debt_issuance: Decimal,
28    /// Principal repaid on debt in the period (non-negative).
29    pub debt_repayment: Decimal,
30    /// Interest paid in cash during the period (non-negative).
31    pub interest_paid: Decimal,
32    /// Income tax paid in cash during the period (non-negative).
33    pub tax_paid: Decimal,
34    /// Dividends paid to shareholders in the period (non-negative).
35    pub dividends_paid: Decimal,
36    /// Accounting framework: `"US_GAAP"` or `"IFRS"`.
37    ///
38    /// Under US GAAP, interest paid is an Operating cash flow.
39    /// Under IFRS, interest paid is a Financing cash flow.
40    pub framework: String,
41}
42
43/// Generates supplementary cash flow items from v2.3 source data.
44pub struct CashFlowEnhancer;
45
46impl CashFlowEnhancer {
47    /// Build the supplementary [`CashFlowItem`] list.
48    ///
49    /// Items whose computed amount is zero are silently omitted so that the
50    /// merged statement is not cluttered with empty lines.
51    pub fn generate(data: &CashFlowSourceData) -> Vec<CashFlowItem> {
52        let is_ifrs = data.framework.eq_ignore_ascii_case("IFRS");
53        let mut items: Vec<CashFlowItem> = Vec::new();
54        let mut sort = 100u32; // start above the values the FS generator uses
55
56        // ------------------------------------------------------------------
57        // Operating activities — adjustments to net income
58        // ------------------------------------------------------------------
59
60        // Depreciation add-back (non-cash charge reversed back)
61        if !data.depreciation_total.is_zero() {
62            items.push(CashFlowItem {
63                item_code: "CF-DEP".to_string(),
64                label: "Depreciation and Amortisation".to_string(),
65                category: CashFlowCategory::Operating,
66                amount: data.depreciation_total,
67                amount_prior: None,
68                sort_order: sort,
69                is_total: false,
70            });
71            sort += 10;
72        }
73
74        // Provision movements (e.g., warranties, bad-debt allowances)
75        if !data.provision_movements_net.is_zero() {
76            items.push(CashFlowItem {
77                item_code: "CF-PROV".to_string(),
78                label: "Movement in Provisions".to_string(),
79                category: CashFlowCategory::Operating,
80                amount: data.provision_movements_net,
81                amount_prior: None,
82                sort_order: sort,
83                is_total: false,
84            });
85            sort += 10;
86        }
87
88        // ΔAR: increase in AR → cash has been tied up → outflow (negative)
89        let dar = -data.delta_ar;
90        if !dar.is_zero() {
91            items.push(CashFlowItem {
92                item_code: "CF-DAR".to_string(),
93                label: "Change in Trade Receivables".to_string(),
94                category: CashFlowCategory::Operating,
95                amount: dar,
96                amount_prior: None,
97                sort_order: sort,
98                is_total: false,
99            });
100            sort += 10;
101        }
102
103        // ΔAP: increase in AP → supplier financing received → inflow (positive)
104        if !data.delta_ap.is_zero() {
105            items.push(CashFlowItem {
106                item_code: "CF-DAP".to_string(),
107                label: "Change in Trade Payables".to_string(),
108                category: CashFlowCategory::Operating,
109                amount: data.delta_ap,
110                amount_prior: None,
111                sort_order: sort,
112                is_total: false,
113            });
114            sort += 10;
115        }
116
117        // ΔInventory: increase in inventory → cash consumed → outflow (negative)
118        let dinv = -data.delta_inventory;
119        if !dinv.is_zero() {
120            items.push(CashFlowItem {
121                item_code: "CF-DINV".to_string(),
122                label: "Change in Inventories".to_string(),
123                category: CashFlowCategory::Operating,
124                amount: dinv,
125                amount_prior: None,
126                sort_order: sort,
127                is_total: false,
128            });
129            sort += 10;
130        }
131
132        // Interest paid — Operating under US GAAP only
133        if !is_ifrs && !data.interest_paid.is_zero() {
134            items.push(CashFlowItem {
135                item_code: "CF-INT".to_string(),
136                label: "Interest Paid".to_string(),
137                category: CashFlowCategory::Operating,
138                amount: -data.interest_paid.abs(),
139                amount_prior: None,
140                sort_order: sort,
141                is_total: false,
142            });
143            sort += 10;
144        }
145
146        // Tax paid (always Operating under both frameworks)
147        if !data.tax_paid.is_zero() {
148            items.push(CashFlowItem {
149                item_code: "CF-TAX".to_string(),
150                label: "Income Tax Paid".to_string(),
151                category: CashFlowCategory::Operating,
152                amount: -data.tax_paid.abs(),
153                amount_prior: None,
154                sort_order: sort,
155                is_total: false,
156            });
157            sort += 10;
158        }
159
160        // ------------------------------------------------------------------
161        // Investing activities
162        // ------------------------------------------------------------------
163
164        if !data.capex.is_zero() {
165            items.push(CashFlowItem {
166                item_code: "CF-CAPEX".to_string(),
167                label: "Purchase of Property, Plant and Equipment".to_string(),
168                category: CashFlowCategory::Investing,
169                amount: -data.capex.abs(),
170                amount_prior: None,
171                sort_order: sort,
172                is_total: false,
173            });
174            sort += 10;
175        }
176
177        // ------------------------------------------------------------------
178        // Financing activities
179        // ------------------------------------------------------------------
180
181        if !data.debt_issuance.is_zero() {
182            items.push(CashFlowItem {
183                item_code: "CF-DEBT-IN".to_string(),
184                label: "Proceeds from Borrowings".to_string(),
185                category: CashFlowCategory::Financing,
186                amount: data.debt_issuance,
187                amount_prior: None,
188                sort_order: sort,
189                is_total: false,
190            });
191            sort += 10;
192        }
193
194        if !data.debt_repayment.is_zero() {
195            items.push(CashFlowItem {
196                item_code: "CF-DEBT-OUT".to_string(),
197                label: "Repayment of Borrowings".to_string(),
198                category: CashFlowCategory::Financing,
199                amount: -data.debt_repayment.abs(),
200                amount_prior: None,
201                sort_order: sort,
202                is_total: false,
203            });
204            sort += 10;
205        }
206
207        // Interest paid — Financing under IFRS only
208        if is_ifrs && !data.interest_paid.is_zero() {
209            items.push(CashFlowItem {
210                item_code: "CF-INT-FIN".to_string(),
211                label: "Interest Paid".to_string(),
212                category: CashFlowCategory::Financing,
213                amount: -data.interest_paid.abs(),
214                amount_prior: None,
215                sort_order: sort,
216                is_total: false,
217            });
218            sort += 10;
219        }
220
221        if !data.dividends_paid.is_zero() {
222            items.push(CashFlowItem {
223                item_code: "CF-DIV".to_string(),
224                label: "Dividends Paid".to_string(),
225                category: CashFlowCategory::Financing,
226                amount: -data.dividends_paid.abs(),
227                amount_prior: None,
228                sort_order: sort,
229                is_total: false,
230            });
231        }
232
233        items
234    }
235}