Skip to content

Commit 03b590f

Browse files
authored
Reporting Errors to GUI (#85)
* - Reporting first error to notes works * - more errors to be reported to GUI * - Added reporting of errors to GUI from transactions
1 parent 790d293 commit 03b590f

7 files changed

Lines changed: 69 additions & 47 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "etradeTaxReturnHelper"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
edition = "2021"
55
description = "Parses etrade financial documents for transaction details (income, tax paid, cost basis) and compute total income and total tax paid according to chosen tax residency (currency)"
66
license = "BSD-3-Clause"

src/gui.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub mod gui {
7979
let mut file_names: Vec<String> = vec![];
8080
let list_names = browser.borrow();
8181
log::info!("Processing {} files", list_names.size());
82-
for i in 1..list_names.size() + 1 {
82+
for i in 1..=list_names.size() {
8383
let line_content = browser.borrow().text(i);
8484
match line_content {
8585
Some(text) => {
@@ -92,10 +92,16 @@ pub mod gui {
9292
}
9393
}
9494
}
95+
buffer.set_text("");
96+
tbuffer.set_text("");
97+
nbuffer.set_text("Running...");
9598
let rd: Box<dyn etradeTaxReturnHelper::Residency> = Box::new(PL {});
9699
let (gross_div, tax_div, gross_sold, cost_sold, div_transactions, sold_transactions) =
97100
match run_taxation(&rd, file_names) {
98-
Ok((gd, td, gs, cs, dts, sts)) => (gd, td, gs, cs, dts, sts),
101+
Ok((gd, td, gs, cs, dts, sts)) => {
102+
nbuffer.set_text("Finished.\n\n (Double check if generated tax data (Summary) makes sense and then copy it to your tax form)");
103+
(gd, td, gs, cs, dts, sts)
104+
}
99105
Err(err) => {
100106
nbuffer.set_text(&err);
101107
panic!("Error: unable to perform taxation");
@@ -217,7 +223,13 @@ pub mod gui {
217223

218224
let mut pack3 = Pack::new(0, 0, SUMMARY_COL_WIDTH, 300, "");
219225
pack3.set_type(fltk::group::PackType::Vertical);
220-
let mut frame3 = Frame::new(0, 0, SUMMARY_COL_WIDTH, 30, "Summary");
226+
let mut frame3 = Frame::new(
227+
0,
228+
0,
229+
SUMMARY_COL_WIDTH,
230+
30,
231+
"Summary (Data for your Tax form)",
232+
);
221233
frame3.set_frame(FrameType::EngravedFrame);
222234

223235
let buffer = TextBuffer::default();

src/lib.rs

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -109,39 +109,43 @@ pub trait Residency {
109109

110110
let base_exchange_rate_url = "https://www.exchange-rates.org/Rate/";
111111

112-
dates.iter_mut().for_each(|(date, val)| {
113-
let mut converted_date = chrono::NaiveDate::parse_from_str(&date, "%m/%d/%y").unwrap();
112+
dates.iter_mut().try_for_each(|(date, val)| {
113+
let mut converted_date = chrono::NaiveDate::parse_from_str(&date, "%m/%d/%y")
114+
.map_err(|x| format!("Unable to convert date {x}"))?;
114115

115116
converted_date = converted_date
116117
.checked_sub_signed(chrono::Duration::days(1))
117-
.expect_and_log("Error traversing date");
118+
.ok_or("Error traversing date")?;
118119

119120
let exchange_rate_url: String = base_exchange_rate_url.to_string()
120121
+ &format!("{}/{}/{}", from, to, converted_date.format("%m-%d-%Y"))
121122
+ "/?format=json";
122123

123124
let body = client.get(&(exchange_rate_url)).send();
124-
let actual_body = body.expect_and_log(&format!(
125-
"Getting Exchange Rate from Exchange-Rates.org ({}) failed",
126-
exchange_rate_url
127-
));
125+
let actual_body = body.map_err(|_| {
126+
format!(
127+
"Getting Exchange Rate from Exchange-Rates.org ({}) failed",
128+
exchange_rate_url
129+
)
130+
})?;
128131
if actual_body.status().is_success() {
129132
log::info!("RESPONSE {:#?}", actual_body);
130133

131134
let exchange_rates_response = actual_body
132135
.text()
133-
.expect_and_log("Error converting response to Text");
136+
.map_err(|_| "Error converting response to Text")?;
134137
log::info!("body of exchange_rate = {:#?}", &exchange_rates_response);
135138
// parsing text response
136139
if let Ok((exchange_rate, exchange_rate_date)) =
137140
self.parse_exchange_rates(&exchange_rates_response)
138141
{
139142
*val = Some((exchange_rate_date, exchange_rate));
140143
}
144+
Ok(())
141145
} else {
142-
panic!("Error getting exchange rate");
146+
return Err("Error getting exchange rate".to_string());
143147
}
144-
});
148+
})?;
145149

146150
Ok(())
147151
}
@@ -198,18 +202,12 @@ pub fn run_taxation(
198202
}
199203
});
200204
// 2. Verify Transactions
201-
match verify_dividends_transactions(&parsed_div_transactions) {
202-
Ok(()) => log::info!("Dividends transactions are consistent"),
203-
Err(msg) => {
204-
println!("{}", msg);
205-
log::warn!("{}", msg);
206-
}
207-
}
205+
verify_dividends_transactions(&parsed_div_transactions)?;
206+
log::info!("Dividends transactions are consistent");
208207

209208
// 3. Verify and create full sold transactions info needed for TAX purposes
210209
let detailed_sold_transactions =
211-
reconstruct_sold_transactions(&parsed_sold_transactions, &parsed_gain_and_losses)
212-
.expect_and_log("Error reconstructing detailed sold transactions.");
210+
reconstruct_sold_transactions(&parsed_sold_transactions, &parsed_gain_and_losses)?;
213211

214212
// 4. Get Exchange rates
215213
// Gather all trade , settlement and transaction dates into hash map to be passed to
@@ -238,8 +236,7 @@ pub fn run_taxation(
238236
},
239237
);
240238

241-
rd.get_exchange_rates(&mut dates)
242-
.expect_and_log("Error: unable to get exchange rates");
239+
rd.get_exchange_rates(&mut dates).map_err(|x| "Error: unable to get exchange rates. Please check your internet connection or proxy settings\n\nDetails:".to_string()+&x)?;
243240

244241
// Make a detailed_div_transactions
245242
let transactions = create_detailed_div_transactions(parsed_div_transactions, &dates);

src/main.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ fn main() {
7171

7272
let pdfnames: Vec<String> = pdfnames.map(|x| x.to_string()).collect();
7373

74-
let (gross_div, tax_div, gross_sold, cost_sold, _, _) = run_taxation(&rd, pdfnames).unwrap();
74+
let (gross_div, tax_div, gross_sold, cost_sold) = match run_taxation(&rd, pdfnames) {
75+
Ok((gross_div, tax_div, gross_sold, cost_sold, _, _)) => {
76+
(gross_div, tax_div, gross_sold, cost_sold)
77+
}
78+
Err(msg) => panic!("\nError: Unable to compute taxes. \n\nDetails: {msg}"),
79+
};
7580

7681
let presentation = rd.present_result(gross_div, tax_div, gross_sold, cost_sold);
7782
presentation.iter().for_each(|x| println!("{x}"));
@@ -242,7 +247,7 @@ mod tests {
242247
);
243248
Ok(())
244249
}
245-
Err(x) => panic!("Error in taxation process"),
250+
Err(x) => panic!("Error in taxation process: {x}"),
246251
}
247252
}
248253

src/pl.rs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,54 +41,61 @@ impl etradeTaxReturnHelper::Residency for PL {
4141
// If there is proxy then pick first URL
4242
let base_client = ReqwestClient::builder();
4343
let client = match &http_proxy {
44-
Ok(proxy) => base_client
45-
.proxy(reqwest::Proxy::http(proxy).expect_and_log("Error setting HTTP proxy")),
44+
Ok(proxy) => base_client.proxy(
45+
reqwest::Proxy::http(proxy)
46+
.map_err(|x| format!("Error setting HTTP proxy. \nDetails: {}", x))?,
47+
),
4648
Err(_) => base_client,
4749
};
4850
let client = match &https_proxy {
49-
Ok(proxy) => client
50-
.proxy(reqwest::Proxy::https(proxy).expect_and_log("Error setting HTTP proxy")),
51+
Ok(proxy) => client.proxy(
52+
reqwest::Proxy::https(proxy)
53+
.map_err(|x| format!("Error setting HTTPS proxy. \nDetails: {}", x))?,
54+
),
5155
Err(_) => client,
5256
};
5357
let client = client
5458
.build()
55-
.expect_and_log("Could not create REST API client");
59+
.map_err(|_| "Could not create REST API client")?;
5660

5761
let base_exchange_rate_url = "https://api.nbp.pl/api/exchangerates/rates/a/";
5862

59-
dates.iter_mut().for_each(|(date, val)| {
63+
dates.iter_mut().try_for_each(|(date, val)| {
6064
let mut converted_date = chrono::NaiveDate::parse_from_str(&date, "%m/%d/%y").unwrap();
6165

6266
// Try to get exchange rate going backwards with dates till success
6367
let mut is_success = false;
6468
while is_success == false {
6569
converted_date = converted_date
6670
.checked_sub_signed(chrono::Duration::days(1))
67-
.expect_and_log("Error traversing date");
71+
.ok_or("Error traversing date")?;
6872

6973
let exchange_rate_url: String = base_exchange_rate_url.to_string()
7074
+ &format!("usd/{}", converted_date.format("%Y-%m-%d"))
7175
+ "/?format=json";
7276

7377
let body = client.get(&(exchange_rate_url)).send();
74-
let actual_body = body.expect_and_log(&format!(
75-
"Getting Exchange Rate from NBP ({}) failed",
76-
exchange_rate_url
77-
));
78+
let actual_body = body.map_err(|_| {
79+
format!(
80+
"Getting Exchange Rate from NBP ({}) failed",
81+
exchange_rate_url
82+
)
83+
})?;
7884
is_success = actual_body.status().is_success();
7985
if is_success == true {
8086
log::info!("RESPONSE {:#?}", actual_body);
8187

8288
let nbp_response = actual_body
8389
.json::<NBPResponse<ExchangeRate>>()
84-
.expect_and_log("Error converting response to JSON");
90+
.map_err(|_| "Error: getting exchange rate from NBP")?;
8591
log::info!("body of exchange_rate = {:#?}", nbp_response);
8692
let exchange_rate = nbp_response.rates[0].mid;
8793
let exchange_rate_date = format!("{}", converted_date.format("%Y-%m-%d"));
8894
*val = Some((exchange_rate_date, exchange_rate));
8995
};
9096
}
91-
});
97+
Ok::<(), String>(())
98+
})?;
9299
Ok(())
93100
}
94101

src/transactions.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ pub fn verify_dividends_transactions(
2626
.unwrap()
2727
.year();
2828
if tr_year != transaction_year {
29-
let msg: &str =
30-
"WARNING! Brokerage statements are related to different years. Was it intentional?";
29+
let msg: &str = "Error: Brokerage statements are related to different years!";
3130
verification = Err(msg.to_owned());
3231
}
3332
});
@@ -51,8 +50,8 @@ pub fn reconstruct_sold_transactions(
5150
let mut detailed_sold_transactions: Vec<(String, String, String, f32, f32)> = vec![];
5251

5352
if sold_transactions.len() > 0 && gains_and_losses.is_empty() {
54-
panic!("\n\nERROR: Sold transaction detected, but corressponding Gain&Losses document is missing. Please download Gain&Losses XLSX document at:\n
55-
https://us.etrade.com/etx/sp/stockplan#/myAccount/gainsLosses\n\n");
53+
return Err("\n\nERROR: Sold transaction detected, but corressponding Gain&Losses document is missing. Please download Gain&Losses XLSX document at:\n
54+
https://us.etrade.com/etx/sp/stockplan#/myAccount/gainsLosses\n\n".to_string());
5655
}
5756

5857
// iterate through all sold transactions and update it with needed info
@@ -492,7 +491,6 @@ mod tests {
492491
}
493492

494493
#[test]
495-
#[should_panic]
496494
fn test_sold_transaction_reconstruction_no_gains_fail() {
497495
let parsed_sold_transactions: Vec<(String, String, i32, f32, f32)> = vec![
498496
(
@@ -513,6 +511,9 @@ mod tests {
513511

514512
let parsed_gains_and_losses: Vec<(String, String, f32, f32, f32)> = vec![];
515513

516-
let _ = reconstruct_sold_transactions(&parsed_sold_transactions, &parsed_gains_and_losses);
514+
let result =
515+
reconstruct_sold_transactions(&parsed_sold_transactions, &parsed_gains_and_losses);
516+
assert_eq!( result , Err("\n\nERROR: Sold transaction detected, but corressponding Gain&Losses document is missing. Please download Gain&Losses XLSX document at:\n
517+
https://us.etrade.com/etx/sp/stockplan#/myAccount/gainsLosses\n\n".to_string()));
517518
}
518519
}

0 commit comments

Comments
 (0)