Skip to main content

datasynth_core/
framework_accounts.rs

1//! Centralized framework-aware account mapping.
2//!
3//! `FrameworkAccounts` maps ~45 semantic account purposes to framework-specific
4//! GL account codes. Adding a new accounting framework requires writing one
5//! constructor and one constants module — all downstream consumers use the
6//! same struct.
7
8use std::sync::Arc;
9
10use crate::accounts::AccountCategory;
11
12/// Audit export configuration flags.
13#[derive(Debug, Clone, Default)]
14pub struct AuditExportConfig {
15    /// French FEC (Fichier des Écritures Comptables), Art. A47 A-1 LPF.
16    pub fec_enabled: bool,
17    /// German GoBD (Grundsätze zur ordnungsmäßigen Führung und Aufbewahrung
18    /// von Büchern, Aufzeichnungen und Unterlagen in elektronischer Form).
19    pub gobd_enabled: bool,
20}
21
22/// Maps semantic account purposes to framework-specific GL codes.
23///
24/// Downstream generators use field names (`ar_control`, `cogs`, …) instead of
25/// hard-coded account numbers, making them framework-agnostic.
26#[derive(Clone)]
27pub struct FrameworkAccounts {
28    // ── Control ──────────────────────────────────────────────────────
29    pub ar_control: String,
30    pub ap_control: String,
31    pub inventory: String,
32    pub fixed_assets: String,
33    pub accumulated_depreciation: String,
34    pub gr_ir_clearing: String,
35    pub ic_ar_clearing: String,
36    pub ic_ap_clearing: String,
37
38    // ── Cash ─────────────────────────────────────────────────────────
39    pub operating_cash: String,
40    pub bank_account: String,
41    pub petty_cash: String,
42
43    // ── Revenue ──────────────────────────────────────────────────────
44    pub product_revenue: String,
45    pub service_revenue: String,
46    pub ic_revenue: String,
47    pub purchase_discount_income: String,
48    pub other_revenue: String,
49    pub sales_discounts: String,
50    pub sales_returns: String,
51
52    // ── Expense ──────────────────────────────────────────────────────
53    pub cogs: String,
54    pub raw_materials: String,
55    pub depreciation_expense: String,
56    pub salaries_wages: String,
57    pub rent: String,
58    pub interest_expense: String,
59    pub purchase_discounts: String,
60    pub fx_gain_loss: String,
61    pub bad_debt: String,
62
63    // ── Tax ──────────────────────────────────────────────────────────
64    pub sales_tax_payable: String,
65    pub vat_payable: String,
66    pub input_vat: String,
67    pub tax_receivable: String,
68    pub tax_expense: String,
69    pub deferred_tax_liability: String,
70    pub deferred_tax_asset: String,
71
72    // ── Liability ────────────────────────────────────────────────────
73    pub accrued_expenses: String,
74    pub accrued_salaries: String,
75    pub unearned_revenue: String,
76    pub short_term_debt: String,
77    pub long_term_debt: String,
78    pub ic_payable: String,
79
80    // ── Equity ───────────────────────────────────────────────────────
81    pub common_stock: String,
82    pub retained_earnings: String,
83    pub current_year_earnings: String,
84    pub cta: String,
85    pub income_summary: String,
86    pub dividends_paid: String,
87
88    // ── Suspense / Clearing ──────────────────────────────────────────
89    pub general_suspense: String,
90    pub payroll_clearing: String,
91    pub bank_reconciliation_suspense: String,
92
93    // ── HGB-specific ─────────────────────────────────────────────────
94    pub provisions: String,
95
96    // ── Audit export flags ───────────────────────────────────────────
97    pub audit_export: AuditExportConfig,
98
99    // ── Classification function ──────────────────────────────────────
100    classifier: Arc<dyn Fn(&str) -> AccountCategory + Send + Sync>,
101}
102
103impl std::fmt::Debug for FrameworkAccounts {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        f.debug_struct("FrameworkAccounts")
106            .field("ar_control", &self.ar_control)
107            .field("ap_control", &self.ap_control)
108            .field("audit_export", &self.audit_export)
109            .finish_non_exhaustive()
110    }
111}
112
113impl FrameworkAccounts {
114    /// Classify an account number using this framework's rules.
115    pub fn classify(&self, account: &str) -> AccountCategory {
116        (self.classifier)(account)
117    }
118
119    /// US GAAP (default) — 4-digit accounts from `crate::accounts`.
120    pub fn us_gaap() -> Self {
121        use crate::accounts::*;
122        Self {
123            ar_control: control_accounts::AR_CONTROL.into(),
124            ap_control: control_accounts::AP_CONTROL.into(),
125            inventory: control_accounts::INVENTORY.into(),
126            fixed_assets: control_accounts::FIXED_ASSETS.into(),
127            accumulated_depreciation: control_accounts::ACCUMULATED_DEPRECIATION.into(),
128            gr_ir_clearing: control_accounts::GR_IR_CLEARING.into(),
129            ic_ar_clearing: control_accounts::IC_AR_CLEARING.into(),
130            ic_ap_clearing: control_accounts::IC_AP_CLEARING.into(),
131
132            operating_cash: cash_accounts::OPERATING_CASH.into(),
133            bank_account: cash_accounts::BANK_ACCOUNT.into(),
134            petty_cash: cash_accounts::PETTY_CASH.into(),
135
136            product_revenue: revenue_accounts::PRODUCT_REVENUE.into(),
137            service_revenue: revenue_accounts::SERVICE_REVENUE.into(),
138            ic_revenue: revenue_accounts::IC_REVENUE.into(),
139            purchase_discount_income: revenue_accounts::PURCHASE_DISCOUNT_INCOME.into(),
140            other_revenue: revenue_accounts::OTHER_REVENUE.into(),
141            sales_discounts: revenue_accounts::SALES_DISCOUNTS.into(),
142            sales_returns: revenue_accounts::SALES_RETURNS.into(),
143
144            cogs: expense_accounts::COGS.into(),
145            raw_materials: expense_accounts::RAW_MATERIALS.into(),
146            depreciation_expense: expense_accounts::DEPRECIATION.into(),
147            salaries_wages: expense_accounts::SALARIES_WAGES.into(),
148            rent: expense_accounts::RENT.into(),
149            interest_expense: expense_accounts::INTEREST_EXPENSE.into(),
150            purchase_discounts: expense_accounts::PURCHASE_DISCOUNTS.into(),
151            fx_gain_loss: expense_accounts::FX_GAIN_LOSS.into(),
152            bad_debt: expense_accounts::BAD_DEBT.into(),
153
154            sales_tax_payable: tax_accounts::SALES_TAX_PAYABLE.into(),
155            vat_payable: tax_accounts::VAT_PAYABLE.into(),
156            input_vat: tax_accounts::INPUT_VAT.into(),
157            tax_receivable: tax_accounts::TAX_RECEIVABLE.into(),
158            tax_expense: tax_accounts::TAX_EXPENSE.into(),
159            deferred_tax_liability: tax_accounts::DEFERRED_TAX_LIABILITY.into(),
160            deferred_tax_asset: tax_accounts::DEFERRED_TAX_ASSET.into(),
161
162            accrued_expenses: liability_accounts::ACCRUED_EXPENSES.into(),
163            accrued_salaries: liability_accounts::ACCRUED_SALARIES.into(),
164            unearned_revenue: liability_accounts::UNEARNED_REVENUE.into(),
165            short_term_debt: liability_accounts::SHORT_TERM_DEBT.into(),
166            long_term_debt: liability_accounts::LONG_TERM_DEBT.into(),
167            ic_payable: liability_accounts::IC_PAYABLE.into(),
168
169            common_stock: equity_accounts::COMMON_STOCK.into(),
170            retained_earnings: equity_accounts::RETAINED_EARNINGS.into(),
171            current_year_earnings: equity_accounts::CURRENT_YEAR_EARNINGS.into(),
172            cta: equity_accounts::CTA.into(),
173            income_summary: equity_accounts::INCOME_SUMMARY.into(),
174            dividends_paid: equity_accounts::DIVIDENDS_PAID.into(),
175
176            general_suspense: suspense_accounts::GENERAL_SUSPENSE.into(),
177            payroll_clearing: suspense_accounts::PAYROLL_CLEARING.into(),
178            bank_reconciliation_suspense: suspense_accounts::BANK_RECONCILIATION_SUSPENSE.into(),
179
180            provisions: liability_accounts::ACCRUED_EXPENSES.into(), // US GAAP: no separate provisions class
181
182            audit_export: AuditExportConfig::default(),
183            classifier: Arc::new(us_gaap_classify),
184        }
185    }
186
187    /// French GAAP (PCG) — 6-digit accounts from `crate::pcg`.
188    pub fn french_gaap() -> Self {
189        use crate::pcg::*;
190        Self {
191            ar_control: control_accounts::AR_CONTROL.into(),
192            ap_control: control_accounts::AP_CONTROL.into(),
193            inventory: control_accounts::INVENTORY.into(),
194            fixed_assets: control_accounts::FIXED_ASSETS.into(),
195            accumulated_depreciation: control_accounts::ACCUMULATED_DEPRECIATION.into(),
196            gr_ir_clearing: control_accounts::GR_IR_CLEARING.into(),
197            ic_ar_clearing: control_accounts::IC_AR_CLEARING.into(),
198            ic_ap_clearing: control_accounts::IC_AP_CLEARING.into(),
199
200            operating_cash: cash_accounts::OPERATING_CASH.into(),
201            bank_account: cash_accounts::BANK_ACCOUNT.into(),
202            petty_cash: cash_accounts::PETTY_CASH.into(),
203
204            product_revenue: revenue_accounts::PRODUCT_REVENUE.into(),
205            service_revenue: revenue_accounts::SERVICE_REVENUE.into(),
206            ic_revenue: additional_revenue::IC_REVENUE.into(),
207            purchase_discount_income: additional_revenue::PURCHASE_DISCOUNT_INCOME.into(),
208            other_revenue: revenue_accounts::OTHER_REVENUE.into(),
209            sales_discounts: revenue_accounts::SALES_DISCOUNTS.into(),
210            sales_returns: additional_revenue::SALES_RETURNS.into(),
211
212            cogs: expense_accounts::COGS.into(),
213            raw_materials: additional_expense::RAW_MATERIALS.into(),
214            depreciation_expense: expense_accounts::DEPRECIATION.into(),
215            salaries_wages: expense_accounts::SALARIES_WAGES.into(),
216            rent: expense_accounts::RENT.into(),
217            interest_expense: expense_accounts::INTEREST_EXPENSE.into(),
218            purchase_discounts: additional_expense::PURCHASE_DISCOUNTS.into(),
219            fx_gain_loss: additional_expense::FX_GAIN_LOSS.into(),
220            bad_debt: additional_expense::BAD_DEBT.into(),
221
222            sales_tax_payable: tax_accounts::OUTPUT_VAT.into(),
223            vat_payable: tax_accounts::OUTPUT_VAT.into(),
224            input_vat: tax_accounts::INPUT_VAT.into(),
225            tax_receivable: tax_accounts::TAX_RECEIVABLE.into(),
226            tax_expense: tax_accounts::TAX_EXPENSE.into(),
227            deferred_tax_liability: tax_accounts::DEFERRED_TAX_LIABILITY.into(),
228            deferred_tax_asset: tax_accounts::DEFERRED_TAX_ASSET.into(),
229
230            accrued_expenses: liability_accounts::ACCRUED_EXPENSES.into(),
231            accrued_salaries: liability_accounts::ACCRUED_SALARIES.into(),
232            unearned_revenue: liability_accounts::UNEARNED_REVENUE.into(),
233            short_term_debt: equity_liability_accounts::SHORT_TERM_DEBT.into(),
234            long_term_debt: equity_liability_accounts::LONG_TERM_DEBT.into(),
235            ic_payable: liability_accounts::IC_PAYABLE.into(),
236
237            common_stock: equity_liability_accounts::COMMON_STOCK.into(),
238            retained_earnings: equity_liability_accounts::RETAINED_EARNINGS.into(),
239            current_year_earnings: equity_accounts::CURRENT_YEAR_EARNINGS.into(),
240            cta: equity_accounts::CTA.into(),
241            income_summary: equity_accounts::INCOME_SUMMARY.into(),
242            dividends_paid: equity_accounts::DIVIDENDS_PAID.into(),
243
244            general_suspense: suspense_accounts::GENERAL_SUSPENSE.into(),
245            payroll_clearing: suspense_accounts::PAYROLL_CLEARING.into(),
246            bank_reconciliation_suspense: suspense_accounts::GENERAL_SUSPENSE.into(), // PCG uses 471000 for general suspense
247
248            provisions: equity_liability_accounts::PROVISIONS.into(),
249
250            audit_export: AuditExportConfig {
251                fec_enabled: true,
252                gobd_enabled: false,
253            },
254            classifier: Arc::new(pcg_classify),
255        }
256    }
257
258    /// German GAAP (HGB) — 4-digit SKR04 accounts from `crate::skr`.
259    pub fn german_gaap() -> Self {
260        use crate::skr::*;
261        Self {
262            ar_control: control_accounts::AR_CONTROL.into(),
263            ap_control: control_accounts::AP_CONTROL.into(),
264            inventory: control_accounts::INVENTORY.into(),
265            fixed_assets: control_accounts::FIXED_ASSETS.into(),
266            accumulated_depreciation: control_accounts::ACCUMULATED_DEPRECIATION.into(),
267            gr_ir_clearing: control_accounts::GR_IR_CLEARING.into(),
268            ic_ar_clearing: control_accounts::IC_AR_CLEARING.into(),
269            ic_ap_clearing: control_accounts::IC_AP_CLEARING.into(),
270
271            operating_cash: cash_accounts::OPERATING_CASH.into(),
272            bank_account: cash_accounts::BANK_ACCOUNT.into(),
273            petty_cash: cash_accounts::PETTY_CASH.into(),
274
275            product_revenue: revenue_accounts::PRODUCT_REVENUE.into(),
276            service_revenue: revenue_accounts::SERVICE_REVENUE.into(),
277            ic_revenue: revenue_accounts::IC_REVENUE.into(),
278            purchase_discount_income: revenue_accounts::PURCHASE_DISCOUNT_INCOME.into(),
279            other_revenue: revenue_accounts::OTHER_REVENUE.into(),
280            sales_discounts: revenue_accounts::SALES_DISCOUNTS.into(),
281            sales_returns: revenue_accounts::SALES_RETURNS.into(),
282
283            cogs: expense_accounts::COGS.into(),
284            raw_materials: expense_accounts::RAW_MATERIALS.into(),
285            depreciation_expense: expense_accounts::DEPRECIATION.into(),
286            salaries_wages: expense_accounts::SALARIES_WAGES.into(),
287            rent: expense_accounts::RENT.into(),
288            interest_expense: expense_accounts::INTEREST_EXPENSE.into(),
289            purchase_discounts: expense_accounts::PURCHASE_DISCOUNTS.into(),
290            fx_gain_loss: expense_accounts::FX_GAIN_LOSS.into(),
291            bad_debt: expense_accounts::BAD_DEBT.into(),
292
293            sales_tax_payable: tax_accounts::OUTPUT_VAT.into(),
294            vat_payable: tax_accounts::OUTPUT_VAT.into(),
295            input_vat: tax_accounts::INPUT_VAT.into(),
296            tax_receivable: tax_accounts::TAX_RECEIVABLE.into(),
297            tax_expense: tax_accounts::TAX_EXPENSE.into(),
298            deferred_tax_liability: tax_accounts::DEFERRED_TAX_LIABILITY.into(),
299            deferred_tax_asset: tax_accounts::DEFERRED_TAX_ASSET.into(),
300
301            accrued_expenses: liability_accounts::ACCRUED_EXPENSES.into(),
302            accrued_salaries: liability_accounts::ACCRUED_SALARIES.into(),
303            unearned_revenue: liability_accounts::UNEARNED_REVENUE.into(),
304            short_term_debt: liability_accounts::SHORT_TERM_DEBT.into(),
305            long_term_debt: liability_accounts::LONG_TERM_DEBT.into(),
306            ic_payable: liability_accounts::IC_PAYABLE.into(),
307
308            common_stock: equity_accounts::COMMON_STOCK.into(),
309            retained_earnings: equity_accounts::RETAINED_EARNINGS.into(),
310            current_year_earnings: equity_accounts::CURRENT_YEAR_EARNINGS.into(),
311            cta: equity_accounts::CTA.into(),
312            income_summary: equity_accounts::INCOME_SUMMARY.into(),
313            dividends_paid: equity_accounts::DIVIDENDS_PAID.into(),
314
315            general_suspense: suspense_accounts::GENERAL_SUSPENSE.into(),
316            payroll_clearing: suspense_accounts::PAYROLL_CLEARING.into(),
317            bank_reconciliation_suspense: suspense_accounts::BANK_RECONCILIATION_SUSPENSE.into(),
318
319            provisions: equity_accounts::PROVISIONS.into(),
320
321            audit_export: AuditExportConfig {
322                fec_enabled: false,
323                gobd_enabled: true,
324            },
325            classifier: Arc::new(skr04_classify),
326        }
327    }
328
329    /// Select accounts for a given `AccountingFramework` from `datasynth-standards`.
330    ///
331    /// This is the primary entry point — other code should not hard-code
332    /// framework detection logic.
333    pub fn for_framework(framework: &str) -> Self {
334        match framework {
335            "french_gaap" | "FrenchGaap" => Self::french_gaap(),
336            "german_gaap" | "GermanGaap" | "hgb" => Self::german_gaap(),
337            _ => Self::us_gaap(),
338        }
339    }
340}
341
342// ── Classification functions ─────────────────────────────────────────────
343
344/// US GAAP: first digit maps directly to category.
345///   1=Asset, 2=Liability, 3=Equity, 4=Revenue, 5=COGS,
346///   6=OpEx, 7=Other, 8=Tax, 9=Suspense
347fn us_gaap_classify(account: &str) -> AccountCategory {
348    AccountCategory::from_account(account)
349}
350
351/// French PCG: classes 1-9 have different semantics.
352///   1=Equity/Liabilities, 2=Fixed Assets, 3=Inventory,
353///   4=Mixed (AP/AR/Personnel), 5=Cash, 6=Expense, 7=Revenue,
354///   8=Special, 9=Analytical
355fn pcg_classify(account: &str) -> AccountCategory {
356    match account.chars().next().and_then(|c| c.to_digit(10)) {
357        Some(1) => {
358            // Class 1: Equity & liabilities — check subclass
359            let sub = account.get(1..2).and_then(|s| s.parse::<u8>().ok());
360            match sub {
361                Some(0..=4) => AccountCategory::Equity, // 10x-14x: capital & reserves
362                Some(5) => AccountCategory::Liability,  // 15x: provisions
363                Some(6..=7) => AccountCategory::Liability, // 16x-17x: debts
364                _ => AccountCategory::Liability,
365            }
366        }
367        Some(2) => AccountCategory::Asset, // Fixed assets
368        Some(3) => AccountCategory::Asset, // Inventory
369        Some(4) => {
370            // Class 4: third parties — check subclass
371            let sub = account.get(1..2).and_then(|s| s.parse::<u8>().ok());
372            match sub {
373                Some(0) => AccountCategory::Liability,     // 40x: suppliers
374                Some(1) => AccountCategory::Asset,         // 41x: customers
375                Some(2) => AccountCategory::Liability,     // 42x: personnel
376                Some(3..=4) => AccountCategory::Liability, // 43-44x: social/tax
377                Some(5) => AccountCategory::Liability,     // 45x: group companies
378                _ => AccountCategory::Liability,
379            }
380        }
381        Some(5) => AccountCategory::Asset, // Cash / financial
382        Some(6) => AccountCategory::OperatingExpense, // Expenses
383        Some(7) => AccountCategory::Revenue, // Revenue
384        Some(8) => AccountCategory::Suspense, // Special
385        Some(9) => AccountCategory::Suspense, // Analytical
386        _ => AccountCategory::Unknown,
387    }
388}
389
390/// German SKR04: classes 0-9 follow the Abschlussgliederungsprinzip.
391///   0=Fixed Assets, 1=Current Assets, 2=Equity, 3=Liabilities,
392///   4=Revenue, 5=COGS/Material, 6=OpEx, 7=Financial, 8=Tax/Extra,
393///   9=Statistical
394fn skr04_classify(account: &str) -> AccountCategory {
395    match account.chars().next().and_then(|c| c.to_digit(10)) {
396        Some(0) => AccountCategory::Asset,              // Fixed assets
397        Some(1) => AccountCategory::Asset,              // Current assets
398        Some(2) => AccountCategory::Equity,             // Equity
399        Some(3) => AccountCategory::Liability,          // Liabilities
400        Some(4) => AccountCategory::Revenue,            // Revenue
401        Some(5) => AccountCategory::Cogs,               // Material / COGS
402        Some(6) => AccountCategory::OperatingExpense,   // Personnel & other OpEx
403        Some(7) => AccountCategory::OtherIncomeExpense, // Financial income/expense
404        Some(8) => AccountCategory::Tax,                // Extraordinary / Tax
405        Some(9) => AccountCategory::Suspense,           // Statistical
406        _ => AccountCategory::Unknown,
407    }
408}
409
410#[cfg(test)]
411#[allow(clippy::unwrap_used)]
412mod tests {
413    use super::*;
414
415    #[test]
416    fn test_framework_accounts_us_gaap_defaults() {
417        use crate::accounts::*;
418        let fa = FrameworkAccounts::us_gaap();
419        assert_eq!(fa.ar_control, control_accounts::AR_CONTROL);
420        assert_eq!(fa.ap_control, control_accounts::AP_CONTROL);
421        assert_eq!(fa.inventory, control_accounts::INVENTORY);
422        assert_eq!(fa.cogs, expense_accounts::COGS);
423        assert_eq!(fa.product_revenue, revenue_accounts::PRODUCT_REVENUE);
424        assert_eq!(fa.retained_earnings, equity_accounts::RETAINED_EARNINGS);
425        assert_eq!(fa.general_suspense, suspense_accounts::GENERAL_SUSPENSE);
426        assert!(!fa.audit_export.fec_enabled);
427        assert!(!fa.audit_export.gobd_enabled);
428    }
429
430    #[test]
431    fn test_framework_accounts_french_gaap() {
432        use crate::pcg;
433        let fa = FrameworkAccounts::french_gaap();
434        assert_eq!(fa.ar_control, pcg::control_accounts::AR_CONTROL);
435        assert_eq!(fa.ap_control, pcg::control_accounts::AP_CONTROL);
436        assert_eq!(fa.inventory, pcg::control_accounts::INVENTORY);
437        assert_eq!(fa.cogs, pcg::expense_accounts::COGS);
438        assert_eq!(fa.product_revenue, pcg::revenue_accounts::PRODUCT_REVENUE);
439        assert!(fa.audit_export.fec_enabled);
440        assert!(!fa.audit_export.gobd_enabled);
441    }
442
443    #[test]
444    fn test_framework_accounts_german_gaap() {
445        use crate::skr;
446        let fa = FrameworkAccounts::german_gaap();
447        assert_eq!(fa.ar_control, skr::control_accounts::AR_CONTROL);
448        assert_eq!(fa.ap_control, skr::control_accounts::AP_CONTROL);
449        assert_eq!(fa.cogs, skr::expense_accounts::COGS);
450        assert!(!fa.audit_export.fec_enabled);
451        assert!(fa.audit_export.gobd_enabled);
452    }
453
454    #[test]
455    fn test_classify_us_gaap() {
456        let fa = FrameworkAccounts::us_gaap();
457        assert_eq!(fa.classify("1100"), AccountCategory::Asset);
458        assert_eq!(fa.classify("2000"), AccountCategory::Liability);
459        assert_eq!(fa.classify("3200"), AccountCategory::Equity);
460        assert_eq!(fa.classify("4000"), AccountCategory::Revenue);
461        assert_eq!(fa.classify("5000"), AccountCategory::Cogs);
462        assert_eq!(fa.classify("6100"), AccountCategory::OperatingExpense);
463        assert_eq!(fa.classify("9000"), AccountCategory::Suspense);
464    }
465
466    #[test]
467    fn test_classify_pcg() {
468        let fa = FrameworkAccounts::french_gaap();
469        assert_eq!(fa.classify("101000"), AccountCategory::Equity);
470        assert_eq!(fa.classify("151000"), AccountCategory::Liability);
471        assert_eq!(fa.classify("210000"), AccountCategory::Asset);
472        assert_eq!(fa.classify("310000"), AccountCategory::Asset);
473        assert_eq!(fa.classify("401000"), AccountCategory::Liability);
474        assert_eq!(fa.classify("411000"), AccountCategory::Asset);
475        assert_eq!(fa.classify("512000"), AccountCategory::Asset);
476        assert_eq!(fa.classify("603000"), AccountCategory::OperatingExpense);
477        assert_eq!(fa.classify("701000"), AccountCategory::Revenue);
478    }
479
480    #[test]
481    fn test_classify_skr04() {
482        let fa = FrameworkAccounts::german_gaap();
483        assert_eq!(fa.classify("0200"), AccountCategory::Asset);
484        assert_eq!(fa.classify("1200"), AccountCategory::Asset);
485        assert_eq!(fa.classify("2000"), AccountCategory::Equity);
486        assert_eq!(fa.classify("3300"), AccountCategory::Liability);
487        assert_eq!(fa.classify("4000"), AccountCategory::Revenue);
488        assert_eq!(fa.classify("5000"), AccountCategory::Cogs);
489        assert_eq!(fa.classify("6000"), AccountCategory::OperatingExpense);
490        assert_eq!(fa.classify("7300"), AccountCategory::OtherIncomeExpense);
491    }
492
493    #[test]
494    fn test_for_framework_dispatch() {
495        let us = FrameworkAccounts::for_framework("us_gaap");
496        assert_eq!(us.ar_control, "1100");
497
498        let fr = FrameworkAccounts::for_framework("french_gaap");
499        assert_eq!(fr.ar_control, "411000");
500
501        let de = FrameworkAccounts::for_framework("german_gaap");
502        assert_eq!(de.ar_control, "1200");
503
504        let hgb = FrameworkAccounts::for_framework("hgb");
505        assert_eq!(hgb.ar_control, "1200");
506    }
507}