envelope_cli/cli/
account.rs1use 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#[derive(Subcommand)]
15pub enum AccountCommands {
16 Create {
18 name: String,
20 #[arg(short = 't', long, default_value = "checking")]
22 account_type: String,
23 #[arg(short, long, default_value = "0")]
25 balance: String,
26 #[arg(long)]
28 off_budget: bool,
29 },
30 List {
32 #[arg(short, long)]
34 all: bool,
35 },
36 Show {
38 account: String,
40 },
41 Edit {
43 account: String,
45 #[arg(short, long)]
47 name: Option<String>,
48 },
49 Archive {
51 account: String,
53 },
54 Unarchive {
56 account: String,
58 },
59}
60
61pub 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 mut 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 if account_type.is_liability() && starting_balance.cents() > 0 {
90 starting_balance = Money::from_cents(-starting_balance.cents());
91 }
92
93 let account = service.create(&name, account_type, starting_balance, !off_budget)?;
94
95 println!("Created account: {}", account.name);
96 println!(" Type: {}", account.account_type);
97 println!(" Starting Balance: {}", account.starting_balance);
98 println!(
99 " On Budget: {}",
100 if account.on_budget { "Yes" } else { "No" }
101 );
102 println!(" ID: {}", account.id);
103 }
104
105 AccountCommands::List { all } => {
106 let summaries = service.list_with_balances(all)?;
107 print!("{}", format_account_list(&summaries));
108 }
109
110 AccountCommands::Show { account } => {
111 let found = service
112 .find(&account)?
113 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
114
115 let summary = service.get_summary(&found)?;
116 print!("{}", format_account_details(&summary));
117 }
118
119 AccountCommands::Edit { account, name } => {
120 let found = service
121 .find(&account)?
122 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
123
124 if name.is_none() {
125 println!("No changes specified. Use --name to change the account name.");
126 return Ok(());
127 }
128
129 let updated = service.update(found.id, name.as_deref())?;
130 println!("Updated account: {}", updated.name);
131 }
132
133 AccountCommands::Archive { account } => {
134 let found = service
135 .find(&account)?
136 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
137
138 let archived = service.archive(found.id)?;
139 println!("Archived account: {}", archived.name);
140 }
141
142 AccountCommands::Unarchive { account } => {
143 let found = service
144 .find(&account)?
145 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
146
147 let unarchived = service.unarchive(found.id)?;
148 println!("Unarchived account: {}", unarchived.name);
149 }
150 }
151
152 Ok(())
153}