1use std::sync::Arc;
9
10use crate::accounts::AccountCategory;
11
12#[derive(Debug, Clone, Default)]
14pub struct AuditExportConfig {
15 pub fec_enabled: bool,
17 pub gobd_enabled: bool,
20}
21
22#[derive(Clone)]
27pub struct FrameworkAccounts {
28 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 pub operating_cash: String,
40 pub bank_account: String,
41 pub petty_cash: String,
42
43 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 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 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 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 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 pub general_suspense: String,
90 pub payroll_clearing: String,
91 pub bank_reconciliation_suspense: String,
92
93 pub provisions: String,
95
96 pub audit_export: AuditExportConfig,
98
99 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 pub fn classify(&self, account: &str) -> AccountCategory {
116 (self.classifier)(account)
117 }
118
119 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(), audit_export: AuditExportConfig::default(),
183 classifier: Arc::new(us_gaap_classify),
184 }
185 }
186
187 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(), 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 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 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
342fn us_gaap_classify(account: &str) -> AccountCategory {
348 AccountCategory::from_account(account)
349}
350
351fn pcg_classify(account: &str) -> AccountCategory {
356 match account.chars().next().and_then(|c| c.to_digit(10)) {
357 Some(1) => {
358 let sub = account.get(1..2).and_then(|s| s.parse::<u8>().ok());
360 match sub {
361 Some(0..=4) => AccountCategory::Equity, Some(5) => AccountCategory::Liability, Some(6..=7) => AccountCategory::Liability, _ => AccountCategory::Liability,
365 }
366 }
367 Some(2) => AccountCategory::Asset, Some(3) => AccountCategory::Asset, Some(4) => {
370 let sub = account.get(1..2).and_then(|s| s.parse::<u8>().ok());
372 match sub {
373 Some(0) => AccountCategory::Liability, Some(1) => AccountCategory::Asset, Some(2) => AccountCategory::Liability, Some(3..=4) => AccountCategory::Liability, Some(5) => AccountCategory::Liability, _ => AccountCategory::Liability,
379 }
380 }
381 Some(5) => AccountCategory::Asset, Some(6) => AccountCategory::OperatingExpense, Some(7) => AccountCategory::Revenue, Some(8) => AccountCategory::Suspense, Some(9) => AccountCategory::Suspense, _ => AccountCategory::Unknown,
387 }
388}
389
390fn skr04_classify(account: &str) -> AccountCategory {
395 match account.chars().next().and_then(|c| c.to_digit(10)) {
396 Some(0) => AccountCategory::Asset, Some(1) => AccountCategory::Asset, Some(2) => AccountCategory::Equity, Some(3) => AccountCategory::Liability, Some(4) => AccountCategory::Revenue, Some(5) => AccountCategory::Cogs, Some(6) => AccountCategory::OperatingExpense, Some(7) => AccountCategory::OtherIncomeExpense, Some(8) => AccountCategory::Tax, Some(9) => AccountCategory::Suspense, _ => 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}