use serde::{Deserialize, Serialize};
use datasynth_standards::framework::AccountingFramework;
use datasynth_core::accounts::{
cash_accounts, control_accounts, equity_accounts, expense_accounts, intangible_accounts,
liability_accounts, manufacturing_accounts, provision_accounts, revenue_accounts, tax_accounts,
treasury_accounts,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TranslationAccountType {
BsMonetary,
BsNonMonetary,
PlRevenue,
PlExpense,
Equity,
PlOci,
}
pub fn classify_account(
account_code: &str,
framework: AccountingFramework,
) -> TranslationAccountType {
let _ = framework;
if let Some(ty) = classify_named(account_code) {
return ty;
}
classify_by_range(account_code)
}
fn classify_named(code: &str) -> Option<TranslationAccountType> {
use TranslationAccountType::*;
if matches!(
code,
cash_accounts::OPERATING_CASH
| cash_accounts::BANK_ACCOUNT
| cash_accounts::PETTY_CASH
| cash_accounts::WIRE_CLEARING
| control_accounts::AR_CONTROL
| control_accounts::IC_AR_CLEARING
| control_accounts::GR_IR_CLEARING
| tax_accounts::INPUT_VAT
| tax_accounts::TAX_RECEIVABLE
| tax_accounts::DEFERRED_TAX_ASSET
| treasury_accounts::DERIVATIVE_ASSET
| treasury_accounts::CASH_POOL_IC_RECEIVABLE
) {
return Some(BsMonetary);
}
if matches!(
code,
control_accounts::INVENTORY
| manufacturing_accounts::WIP
| manufacturing_accounts::FINISHED_GOODS
) {
return Some(BsNonMonetary);
}
if matches!(
code,
control_accounts::FIXED_ASSETS | control_accounts::ACCUMULATED_DEPRECIATION
) {
return Some(BsNonMonetary);
}
if matches!(
code,
intangible_accounts::GOODWILL
| intangible_accounts::CUSTOMER_RELATIONSHIPS
| intangible_accounts::TRADE_NAME
| intangible_accounts::TECHNOLOGY
| intangible_accounts::ACCUMULATED_AMORTIZATION
) {
return Some(BsNonMonetary);
}
if matches!(
code,
control_accounts::AP_CONTROL
| control_accounts::IC_AP_CLEARING
| liability_accounts::ACCRUED_EXPENSES
| liability_accounts::ACCRUED_SALARIES
| liability_accounts::ACCRUED_BENEFITS
| liability_accounts::UNEARNED_REVENUE
| liability_accounts::SHORT_TERM_DEBT
| liability_accounts::LONG_TERM_DEBT
| liability_accounts::IC_PAYABLE
| tax_accounts::SALES_TAX_PAYABLE
| tax_accounts::VAT_PAYABLE
| tax_accounts::WITHHOLDING_TAX_PAYABLE
| tax_accounts::INCOME_TAX_PAYABLE
| tax_accounts::DEFERRED_TAX_LIABILITY
| manufacturing_accounts::LABOR_ACCRUAL
| manufacturing_accounts::WARRANTY_PROVISION
| provision_accounts::PROVISION_LIABILITY
| treasury_accounts::INTEREST_PAYABLE
| treasury_accounts::DEBT_PREMIUM
| treasury_accounts::DEBT_DISCOUNT
| treasury_accounts::DERIVATIVE_LIABILITY
| treasury_accounts::CASH_POOL_IC_PAYABLE
) {
return Some(BsMonetary);
}
if matches!(
code,
equity_accounts::COMMON_STOCK
| equity_accounts::APIC
| equity_accounts::RETAINED_EARNINGS
| equity_accounts::CURRENT_YEAR_EARNINGS
| equity_accounts::TREASURY_STOCK
| equity_accounts::CTA
| equity_accounts::INCOME_SUMMARY
| equity_accounts::DIVIDENDS_PAID
| treasury_accounts::OCI_CASH_FLOW_HEDGE
) {
return Some(Equity);
}
if matches!(
code,
revenue_accounts::PRODUCT_REVENUE
| revenue_accounts::SERVICE_REVENUE
| revenue_accounts::IC_REVENUE
| revenue_accounts::PURCHASE_DISCOUNT_INCOME
| revenue_accounts::OTHER_REVENUE
| revenue_accounts::SALES_DISCOUNTS
| revenue_accounts::SALES_RETURNS
| intangible_accounts::BARGAIN_PURCHASE_GAIN
) {
return Some(PlRevenue);
}
if matches!(
code,
expense_accounts::COGS
| expense_accounts::RAW_MATERIALS
| expense_accounts::DIRECT_LABOR
| expense_accounts::MANUFACTURING_OVERHEAD
| expense_accounts::DEPRECIATION
| expense_accounts::SALARIES_WAGES
| expense_accounts::BENEFITS
| expense_accounts::RENT
| expense_accounts::UTILITIES
| expense_accounts::OFFICE_SUPPLIES
| expense_accounts::TRAVEL_ENTERTAINMENT
| expense_accounts::PROFESSIONAL_FEES
| expense_accounts::INSURANCE
| expense_accounts::BAD_DEBT
| expense_accounts::INTEREST_EXPENSE
| expense_accounts::PURCHASE_DISCOUNTS
| expense_accounts::FX_GAIN_LOSS
| intangible_accounts::AMORTIZATION_EXPENSE
| provision_accounts::PROVISION_EXPENSE
| treasury_accounts::HEDGE_INEFFECTIVENESS
) {
return Some(PlExpense);
}
None
}
fn classify_by_range(code: &str) -> TranslationAccountType {
use TranslationAccountType::*;
let leading = match code.chars().next() {
Some(c) if c.is_ascii_digit() => c,
_ => return BsMonetary,
};
match leading {
'1' => classify_asset_range(code),
'2' => BsMonetary,
'3' => Equity,
'4' => PlRevenue,
'5' | '6' | '7' => PlExpense,
'8' => PlOci,
'9' => BsMonetary,
_ => BsMonetary,
}
}
fn classify_asset_range(code: &str) -> TranslationAccountType {
use TranslationAccountType::*;
let prefix4: String = code.chars().take(4).collect();
let n: u32 = match prefix4.parse() {
Ok(n) => n,
Err(_) => return BsMonetary,
};
match n {
1000..=1199 => BsMonetary, 1200..=1299 => BsNonMonetary, 1300..=1399 => BsNonMonetary, 1400..=1499 => BsMonetary, 1500..=1899 => BsNonMonetary, 1900..=1999 => BsNonMonetary, _ => BsMonetary,
}
}