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 #[arg(short, long)]
37 pretty: bool,
38 },
39 Show {
41 account: String,
43 },
44 Edit {
46 account: String,
48 #[arg(short, long)]
50 name: Option<String>,
51 },
52 Archive {
54 account: String,
56 },
57 Unarchive {
59 account: String,
61 },
62}
63
64pub fn handle_account_command(storage: &Storage, cmd: AccountCommands) -> EnvelopeResult<()> {
66 let service = AccountService::new(storage);
67
68 match cmd {
69 AccountCommands::Create {
70 name,
71 account_type,
72 balance,
73 off_budget,
74 } => {
75 let account_type = AccountType::parse(&account_type).ok_or_else(|| {
76 crate::error::EnvelopeError::Validation(format!(
77 "Invalid account type: '{}'. Valid types: checking, savings, credit, cash, investment, line_of_credit, other",
78 account_type
79 ))
80 })?;
81
82 let mut starting_balance = Money::parse(&balance).map_err(|e| {
83 crate::error::EnvelopeError::Validation(format!(
84 "Invalid balance format: '{}'. Use format like '1000.00' or '1000'. Error: {}",
85 balance, e
86 ))
87 })?;
88
89 if account_type.is_liability() && starting_balance.cents() > 0 {
93 starting_balance = Money::from_cents(-starting_balance.cents());
94 }
95
96 let account = service.create(&name, account_type, starting_balance, !off_budget)?;
97
98 println!("Created account: {}", account.name);
99 println!(" Type: {}", account.account_type);
100 println!(" Starting Balance: {}", account.starting_balance);
101 println!(
102 " On Budget: {}",
103 if account.on_budget { "Yes" } else { "No" }
104 );
105 println!(" ID: {}", account.id);
106 }
107
108 AccountCommands::List { all, pretty } => {
109 let summaries = service.list_with_balances(all)?;
110 print!("{}", format_account_list(&summaries, pretty));
111 }
112
113 AccountCommands::Show { account } => {
114 let found = service
115 .find(&account)?
116 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
117
118 let summary = service.get_summary(&found)?;
119 print!("{}", format_account_details(&summary));
120 }
121
122 AccountCommands::Edit { account, name } => {
123 let found = service
124 .find(&account)?
125 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
126
127 if name.is_none() {
128 println!("No changes specified. Use --name to change the account name.");
129 return Ok(());
130 }
131
132 let updated = service.update(found.id, name.as_deref())?;
133 println!("Updated account: {}", updated.name);
134 }
135
136 AccountCommands::Archive { account } => {
137 let found = service
138 .find(&account)?
139 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
140
141 let archived = service.archive(found.id)?;
142 println!("Archived account: {}", archived.name);
143 }
144
145 AccountCommands::Unarchive { account } => {
146 let found = service
147 .find(&account)?
148 .ok_or_else(|| crate::error::EnvelopeError::account_not_found(&account))?;
149
150 let unarchived = service.unarchive(found.id)?;
151 println!("Unarchived account: {}", unarchived.name);
152 }
153 }
154
155 Ok(())
156}