accounting_core/
traits.rs

1//! Traits for storage abstraction and extensibility
2
3use async_trait::async_trait;
4use bigdecimal::BigDecimal;
5use chrono::NaiveDate;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9use crate::types::*;
10
11/// Storage abstraction for the ledger system
12///
13/// This trait allows the accounting core to work with any storage backend
14/// (PostgreSQL, MySQL, SQLite, in-memory, etc.) by implementing these methods.
15#[async_trait]
16pub trait LedgerStorage: Send + Sync {
17    /// Save an account to storage
18    async fn save_account(&mut self, account: &Account) -> LedgerResult<()>;
19
20    /// Get an account by ID
21    async fn get_account(&self, account_id: &str) -> LedgerResult<Option<Account>>;
22
23    /// List accounts with optional pagination and filtering
24    async fn list_accounts(
25        &self,
26        account_type: Option<AccountType>,
27        pagination: PaginationOption,
28    ) -> LedgerResult<ListResponse<Account>>;
29
30    /// Update an account
31    async fn update_account(&mut self, account: &Account) -> LedgerResult<()>;
32
33    /// Delete an account (if no transactions reference it)
34    async fn delete_account(&mut self, account_id: &str) -> LedgerResult<()>;
35
36    /// Save a transaction to storage
37    async fn save_transaction(&mut self, transaction: &Transaction) -> LedgerResult<()>;
38
39    /// Get a transaction by ID
40    async fn get_transaction(&self, transaction_id: &str) -> LedgerResult<Option<Transaction>>;
41
42    /// List transactions for a specific account with optional pagination
43    async fn get_account_transactions(
44        &self,
45        account_id: &str,
46        start_date: Option<NaiveDate>,
47        end_date: Option<NaiveDate>,
48        pagination: PaginationOption,
49    ) -> LedgerResult<ListResponse<Transaction>>;
50
51    /// List all transactions within a date range with optional pagination
52    async fn get_transactions(
53        &self,
54        start_date: Option<NaiveDate>,
55        end_date: Option<NaiveDate>,
56        pagination: PaginationOption,
57    ) -> LedgerResult<ListResponse<Transaction>>;
58
59    /// Update a transaction
60    async fn update_transaction(&mut self, transaction: &Transaction) -> LedgerResult<()>;
61
62    /// Delete a transaction and reverse its effects on account balances
63    async fn delete_transaction(&mut self, transaction_id: &str) -> LedgerResult<()>;
64
65    /// Get account balance as of a specific date
66    async fn get_account_balance(
67        &self,
68        account_id: &str,
69        as_of_date: Option<NaiveDate>,
70    ) -> LedgerResult<BigDecimal>;
71
72    /// Get trial balance as of a specific date
73    async fn get_trial_balance(&self, as_of_date: NaiveDate) -> LedgerResult<TrialBalance>;
74
75    /// Get all account balances grouped by account type
76    async fn get_account_balances_by_type(
77        &self,
78        as_of_date: NaiveDate,
79    ) -> LedgerResult<HashMap<AccountType, Vec<AccountBalance>>>;
80}
81
82/// Trait for implementing custom account validation rules
83pub trait AccountValidator: Send + Sync {
84    /// Validate an account before saving
85    fn validate_account(&self, account: &Account) -> LedgerResult<()>;
86
87    /// Validate account deletion (e.g., check for existing transactions)
88    fn validate_account_deletion(&self, account_id: &str) -> LedgerResult<()>;
89}
90
91/// Trait for implementing custom transaction validation rules
92pub trait TransactionValidator: Send + Sync {
93    /// Validate a transaction before saving
94    fn validate_transaction(&self, transaction: &Transaction) -> LedgerResult<()>;
95
96    /// Validate that all referenced accounts exist
97    fn validate_account_references(&self, transaction: &Transaction) -> LedgerResult<()>;
98}
99
100/// Default account validator with basic rules
101pub struct DefaultAccountValidator;
102
103impl AccountValidator for DefaultAccountValidator {
104    fn validate_account(&self, account: &Account) -> LedgerResult<()> {
105        if account.id.trim().is_empty() {
106            return Err(LedgerError::Validation(
107                "Account ID cannot be empty".to_string(),
108            ));
109        }
110
111        if account.name.trim().is_empty() {
112            return Err(LedgerError::Validation(
113                "Account name cannot be empty".to_string(),
114            ));
115        }
116
117        Ok(())
118    }
119
120    fn validate_account_deletion(&self, _account_id: &str) -> LedgerResult<()> {
121        // Basic implementation - in a real system you'd check for existing transactions
122        Ok(())
123    }
124}
125
126/// Default transaction validator with basic double-entry rules
127pub struct DefaultTransactionValidator;
128
129impl TransactionValidator for DefaultTransactionValidator {
130    fn validate_transaction(&self, transaction: &Transaction) -> LedgerResult<()> {
131        transaction.validate()
132    }
133
134    fn validate_account_references(&self, _transaction: &Transaction) -> LedgerResult<()> {
135        // Basic implementation - in a real system you'd verify accounts exist in storage
136        Ok(())
137    }
138}
139
140/// Trait for implementing custom chart of accounts structures
141#[async_trait]
142pub trait ChartOfAccounts: Send + Sync {
143    /// Get the full chart of accounts as a hierarchical structure
144    async fn get_chart(&self) -> LedgerResult<Vec<Account>>;
145
146    /// Add a new account to the chart
147    async fn add_account(&mut self, account: Account) -> LedgerResult<()>;
148
149    /// Get all child accounts of a parent account
150    async fn get_child_accounts(&self, parent_id: &str) -> LedgerResult<Vec<Account>>;
151
152    /// Get the full path to an account (for hierarchical display)
153    async fn get_account_path(&self, account_id: &str) -> LedgerResult<Vec<Account>>;
154}
155
156/// Trait for report generation
157#[async_trait]
158pub trait ReportGenerator: Send + Sync {
159    /// Generate a balance sheet as of a specific date
160    async fn generate_balance_sheet(&self, as_of_date: NaiveDate) -> LedgerResult<BalanceSheet>;
161
162    /// Generate an income statement for a date range
163    async fn generate_income_statement(
164        &self,
165        start_date: NaiveDate,
166        end_date: NaiveDate,
167    ) -> LedgerResult<IncomeStatement>;
168
169    /// Generate a cash flow statement for a date range
170    async fn generate_cash_flow(
171        &self,
172        start_date: NaiveDate,
173        end_date: NaiveDate,
174    ) -> LedgerResult<CashFlowStatement>;
175}
176
177/// Balance Sheet structure
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct BalanceSheet {
180    pub as_of_date: NaiveDate,
181    pub assets: Vec<AccountBalance>,
182    pub liabilities: Vec<AccountBalance>,
183    pub equity: Vec<AccountBalance>,
184    pub total_assets: BigDecimal,
185    pub total_liabilities: BigDecimal,
186    pub total_equity: BigDecimal,
187    pub is_balanced: bool,
188}
189
190/// Income Statement structure
191#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
192pub struct IncomeStatement {
193    pub start_date: NaiveDate,
194    pub end_date: NaiveDate,
195    pub revenue: Vec<AccountBalance>,
196    pub expenses: Vec<AccountBalance>,
197    pub total_revenue: BigDecimal,
198    pub total_expenses: BigDecimal,
199    pub net_income: BigDecimal,
200}
201
202/// Cash Flow Statement structure
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
204pub struct CashFlowStatement {
205    pub start_date: NaiveDate,
206    pub end_date: NaiveDate,
207    pub operating_activities: Vec<CashFlowItem>,
208    pub investing_activities: Vec<CashFlowItem>,
209    pub financing_activities: Vec<CashFlowItem>,
210    pub net_operating_cash_flow: BigDecimal,
211    pub net_investing_cash_flow: BigDecimal,
212    pub net_financing_cash_flow: BigDecimal,
213    pub net_cash_flow: BigDecimal,
214}
215
216/// Cash Flow Item
217#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218pub struct CashFlowItem {
219    pub description: String,
220    pub amount: BigDecimal,
221}