From 12786d344c10c1177b8d6157d5cad58b7de46758 Mon Sep 17 00:00:00 2001 From: Jacek Czaja Date: Wed, 3 Apr 2024 10:04:32 +0200 Subject: [PATCH] - Added support for interest adjustment --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 26 ++++++++++++++++++++++++++ src/pdfparser.rs | 36 +++++++++++++++++++++++++++++++++++- 4 files changed, 63 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7633023..9925bd4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -571,7 +571,7 @@ checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c" [[package]] name = "etradeTaxReturnHelper" -version = "0.5.0" +version = "0.5.1" dependencies = [ "calamine", "chrono", diff --git a/Cargo.toml b/Cargo.toml index e6b47c1..b41f752 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "etradeTaxReturnHelper" -version = "0.5.0" +version = "0.5.1" edition = "2021" 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)" license = "BSD-3-Clause" diff --git a/src/main.rs b/src/main.rs index 3ba0bfd..d52b73d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ use logging::ResultExt; // TODO: Make a parsing of incomplete date // TODO: When I sold on Dec there was EST cost (0.04). Make sure it is included in your results // TODO: async to get currency +// TODO: make UT using rounded vlaues of f32 // TODO: parse_gain_and_losses expect -> ? // TODO: GUI : choosing residency // TODO: Drag&Drop to work on MultiBrowser field @@ -438,4 +439,29 @@ mod tests { Err(x) => panic!("Error in taxation process"), } } + + #[test] + #[ignore] + fn test_interest_adjustment_taxation() -> Result<(), clap::Error> { + // Get all brokerage with dividends only + let myapp = App::new("E-trade tax helper").setting(AppSettings::ArgRequiredElseHelp); + let rd: Box = Box::new(pl::PL {}); + let matches = create_cmd_line_pattern(myapp) + .get_matches_from_safe(vec!["mytest", "data/example-interest-adj.pdf"])?; + let pdfnames = matches + .values_of("financial documents") + .expect_and_log("error getting brokarage statements pdfs names"); + let pdfnames: Vec = pdfnames.map(|x| x.to_string()).collect(); + + match etradeTaxReturnHelper::run_taxation(&rd, pdfnames) { + Ok((gross_div, tax_div, gross_sold, cost_sold, _, _, _, _)) => { + assert_eq!( + (gross_div, tax_div, gross_sold, cost_sold), + (0.66164804, 0.0, 0.0, 0.0), + ); + Ok(()) + } + Err(x) => panic!("Error in taxation process"), + } + } } diff --git a/src/pdfparser.rs b/src/pdfparser.rs index 873b81e..a49c34a 100644 --- a/src/pdfparser.rs +++ b/src/pdfparser.rs @@ -129,8 +129,10 @@ impl Entry for StringEntry { fn getstring(&self) -> Option { Some(self.val.clone()) } + // Either match parsed token against any of patterns or in case no patterns are there + // just return match (true) fn is_pattern(&self) -> bool { - self.patterns.iter().find(|&x| self.val == *x).is_some() + self.patterns.len() == 0 || self.patterns.iter().find(|&x| self.val == *x).is_some() } } @@ -165,6 +167,24 @@ fn create_interests_fund_parsing_sequence( sequence.push_back(Box::new(F32Entry { val: 0.0 })); // Income Entry } +fn create_interest_adjustment_parsing_sequence( + sequence: &mut std::collections::VecDeque>, +) { + sequence.push_back(Box::new(StringEntry { + val: String::new(), + patterns: vec![], + })); + sequence.push_back(Box::new(StringEntry { + val: String::new(), + patterns: vec![], + })); + sequence.push_back(Box::new(StringEntry { + val: String::new(), + patterns: vec![], + })); + sequence.push_back(Box::new(F32Entry { val: 0.0 })); // Income Entry +} + fn create_qualified_dividend_parsing_sequence( sequence: &mut std::collections::VecDeque>, ) { @@ -685,6 +705,10 @@ fn check_if_transaction( create_interests_fund_parsing_sequence(sequence); state = ParserState::ProcessingTransaction(TransactionType::Interests); log::info!("Starting to parse Interests Fund transaction"); + } else if candidate_string == "INTEREST INCOME-ADJ" { + create_interest_adjustment_parsing_sequence(sequence); + state = ParserState::ProcessingTransaction(TransactionType::Interests); + log::info!("Starting to parse Interest adjustment transaction"); } else if candidate_string == "QUALIFIED DIVIDEND" { create_qualified_dividend_parsing_sequence(sequence); state = ParserState::ProcessingTransaction(TransactionType::Dividends); @@ -940,6 +964,16 @@ mod tests { patterns: vec!["INTC".to_owned(), "DLB".to_owned()], }; s.parse(&pdf::primitive::PdfString::new(data)); + assert_eq!(s.is_pattern(), true); + + // unimportant string + let data: Vec = vec!['K' as u8, 'L' as u8, 'M' as u8]; + let mut s = StringEntry { + val: String::new(), + patterns: vec![], + }; + s.parse(&pdf::primitive::PdfString::new(data)); + assert_eq!(s.is_pattern(), true); Ok(()) }