envelope_cli/cli/
account.rs

1//! Account CLI commands
2//!
3//! Implements CLI commands for account management.
4
5use clap::Subcommand;
6
7use crate::display::account::{format_account_details, format_account_list};
8use crate::error::EnvelopeResult;
9use crate::models::{AccountType, Money};
10use crate::services::AccountService;
11use crate::storage::Storage;
12
13/// Account subcommands
14#[derive(Subcommand)]
15pub enum AccountCommands {
16    /// Create a new account
17    Create {
18        /// Account name
19        name: String,
20        /// Account type (checking, savings, credit, cash, investment)
21        #[arg(short = 't', long, default_value = "checking")]
22        account_type: String,
23        /// Starting balance (e.g., "1000.00" or "1000")
24        #[arg(short, long, default_value = "0")]
25        balance: String,
26        /// Mark as off-budget
27        #[arg(long)]
28        off_budget: bool,
29    },
30    /// List all accounts
31    List {
32        /// Show archived accounts
33        #[arg(short, long)]
34        all: bool,
35    },
36    /// Show account details
37    Show {
38        /// Account name or ID
39        account: String,
40    },
41    /// Edit an account
42    Edit {
43        /// Account name or ID
44        account: String,
45        /// New name
46        #[arg(short, long)]
47        name: Option<String>,
48    },
49    /// Archive an account
50    Archive {
51        /// Account name or ID
52        account: String,
53    },
54    /// Unarchive an account
55    Unarchive {
56        /// Account name or ID
57        account: String,
58    },
59}
60
61/// Handle an account command
62pub fn handle_account_command(storage: &Storage, cmd: AccountCommands) -> EnvelopeResult<()> {
63    let service = AccountService::new(storage);
64
65    match cmd {
66        AccountCommands::Create {
67            name,
68            account_type,
69            balance,
70            off_budget,
71        } => {
72            let account_type = AccountType::parse(&account_type).ok_or_else(|| {
73                crate::error::EnvelopeError::Validation(format!(
74                    "Invalid account type: '{}'. Valid types: checking, savings, credit, cash, investment, line_of_credit, other",
75                    account_type
76                ))
77            })?;
78
79            let starting_balance = Money::parse(&balance).map_err(|e| {
80                crate::error::EnvelopeError::Validation(format!(
81                    "Invalid balance format: '{}'. Use format like '1000.00' or '1000'. Error: {}",
82                    balance, e
83                ))
84            })?;
85
86            let account = service.create(&name, account_type, starting_balance, !off_budget)?;
87
88            println!("Created account: {}", account.name);
89            println!("  Type: {}", account.account_type);
90            println!("  Starting Balance: {}", account.starting_balance);
91            println!(
92                "  On Budget: {}",
93                if account.on_budget { "Yes" } else { "No" }
94            );
95            println!("  ID: {}", account.id);
96        }
97
98        AccountCommands::List { all } => {
99            let summaries = service.list_with_balances(all)?;
100            print!("{}", format_account_list(&summaries));
101        }
102
103        AccountCommands::Show { account } => {
104            let found = service
105                .find(&account)?
106                .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
107
108            let summary = service.get_summary(&found)?;
109            print!("{}", format_account_details(&summary));
110        }
111
112        AccountCommands::Edit { account, name } => {
113            let found = service
114                .find(&account)?
115                .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
116
117            if name.is_none() {
118                println!("No changes specified. Use --name to change the account name.");
119                return Ok(());
120            }
121
122            let updated = service.update(found.id, name.as_deref())?;
123            println!("Updated account: {}", updated.name);
124        }
125
126        AccountCommands::Archive { account } => {
127            let found = service
128                .find(&account)?
129                .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
130
131            let archived = service.archive(found.id)?;
132            println!("Archived account: {}", archived.name);
133        }
134
135        AccountCommands::Unarchive { account } => {
136            let found = service
137                .find(&account)?
138                .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
139
140            let unarchived = service.unarchive(found.id)?;
141            println!("Unarchived account: {}", unarchived.name);
142        }
143    }
144
145    Ok(())
146}