From 8c62211b64d3a25003ebd41c5efe4518ac64e0fc Mon Sep 17 00:00:00 2001 From: scoobybejesus Date: Sat, 21 Sep 2019 13:57:16 -0400 Subject: [PATCH] Merged dg2 branch which tracked changed related to preserving historical cost basis and proceeds that are changed as a result of like-kind treatment. --- src/account.rs | 128 +++++++++++++--- src/create_lots_mvmts.rs | 36 ++++- src/csv_export.rs | 251 +++++++++++++++++++++++++++---- src/csv_import_accts_txns.rs | 4 +- src/import_cost_proceeds_etc.rs | 256 +++++++++++++++++++++++--------- src/main.rs | 14 ++ src/txt_export.rs | 53 +++---- 7 files changed, 594 insertions(+), 148 deletions(-) diff --git a/src/account.rs b/src/account.rs index bd663c9..ae5d44c 100644 --- a/src/account.rs +++ b/src/account.rs @@ -52,11 +52,21 @@ impl Account { total_amount } - pub fn get_sum_of_basis_in_lots(&self) -> d128 { + pub fn get_sum_of_lk_basis_in_lots(&self) -> d128 { let lots = self.list_of_lots.borrow(); let mut total_amount = d128!(0); for lot in lots.iter() { - let sum = lot.get_sum_of_basis_in_lot(); + let sum = lot.get_sum_of_lk_basis_in_lot(); + total_amount += sum; + } + total_amount + } + + pub fn get_sum_of_orig_basis_in_lots(&self) -> d128 { + let lots = self.list_of_lots.borrow(); + let mut total_amount = d128!(0); + for lot in lots.iter() { + let sum = lot.get_sum_of_orig_basis_in_lot(); total_amount += sum; } total_amount @@ -87,7 +97,13 @@ impl Lot { // d128!(0) == Self::get_sum_of_amts_in_lot(&self) // } - pub fn get_sum_of_basis_in_lot(&self) -> d128 { + pub fn get_sum_of_lk_basis_in_lot(&self) -> d128 { + let mut amts = d128!(0); + self.movements.borrow().iter().for_each(|movement| amts += movement.cost_basis_lk.get()); + amts + } + + pub fn get_sum_of_orig_basis_in_lot(&self) -> d128 { let mut amts = d128!(0); self.movements.borrow().iter().for_each(|movement| amts += movement.cost_basis.get()); amts @@ -106,6 +122,8 @@ pub struct Movement { pub ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell, // Set in wrap_mvmt_and_push() pub lot_num: u32, pub proceeds: Cell, // Initialized with 0. Set in add_proceeds_to_movements() + pub proceeds_lk: Cell, + pub cost_basis_lk: Cell, } impl Movement { @@ -126,17 +144,67 @@ impl Movement { acct_map: &HashMap, ar_map: &HashMap ) -> d128 { - // println!("Lot #: {}", self.lot.lot_number); - let ar = ar_map.get(&self.action_record_key).unwrap(); - let acct = acct_map.get(&ar.account_key).unwrap(); - let lot = &acct.list_of_lots.borrow()[self.lot_num as usize - 1]; // lots start at 1 and indexes at 0 - let borrowed_mvmt_list = lot.movements.borrow(); - let ratio = self.amount / borrowed_mvmt_list.first().unwrap().amount; - // println!("ratio_of_amt_to_lots_first_mvmt: {}", ratio.abs()); + // // println!("Lot #: {}", self.lot.lot_number); + // let ar = ar_map.get(&self.action_record_key).unwrap(); + // let acct = acct_map.get(&ar.account_key).unwrap(); + // let lot = &acct.list_of_lots.borrow()[self.lot_num as usize - 1]; // lots start at 1 and indexes at 0 + let lot = self.get_lot(acct_map, ar_map); + let list_of_lot_mvmts = lot.movements.borrow(); + let ratio = self.amount / list_of_lot_mvmts.first().unwrap().amount; + ratio.abs() } - pub fn get_gain_or_loss(&self) -> d128 { + pub fn get_lk_cost_basis_of_lots_first_mvmt( + &self, + acct_map: &HashMap, + ar_map: &HashMap + ) -> d128 { + // let ar = ar_map.get(&self.action_record_key).unwrap(); + // let acct = acct_map.get(&ar.account_key).unwrap(); + // let lot = &acct.list_of_lots.borrow()[self.lot_num as usize - 1]; // lots start at 1 and indexes at 0 + let lot = self.get_lot(acct_map, ar_map); + let list_of_lot_mvmts = lot.movements.borrow(); + let cost_basis_lk = list_of_lot_mvmts.first().unwrap().cost_basis_lk.get(); + + cost_basis_lk + } + + pub fn get_cost_basis_of_lots_first_mvmt( + &self, + acct_map: &HashMap, + ar_map: &HashMap + ) -> d128 { + // let ar = ar_map.get(&self.action_record_key).unwrap(); + // let acct = acct_map.get(&ar.account_key).unwrap(); + // let lot = &acct.list_of_lots.borrow()[self.lot_num as usize - 1]; // lots start at 1 and indexes at 0 + // let borrowed_mvmt_list = lot.movements.borrow(); + let lot = self.get_lot(acct_map, ar_map); + let list_of_lot_mvmts = lot.movements.borrow(); + let cost_basis = list_of_lot_mvmts.first().unwrap().cost_basis.get(); + + cost_basis + } + + // pub fn get_lk_proceeds_of_lots_first_mvmt( + // &self, + // acct_map: &HashMap, + // ar_map: &HashMap + // ) -> d128 { + // let ar = ar_map.get(&self.action_record_key).unwrap(); + // let acct = acct_map.get(&ar.account_key).unwrap(); + // let lot = &acct.list_of_lots.borrow()[self.lot_num as usize - 1]; // lots start at 1 and indexes at 0 + // let borrowed_mvmt_list = lot.movements.borrow(); + // let proceeds_lk = borrowed_mvmt_list.first().unwrap().proceeds_lk.get(); + + // proceeds_lk + // } + + pub fn get_lk_gain_or_loss(&self) -> d128 { // Returns proceeds*2 for Incoming+Flow txns + self.proceeds_lk.get() + self.cost_basis_lk.get() + } + + pub fn get_orig_gain_or_loss(&self) -> d128 { // Returns proceeds*2 for Incoming+Flow txns self.proceeds.get() + self.cost_basis.get() } @@ -147,20 +215,28 @@ impl Movement { let lot = Self::get_lot(&self, acct_map, ar_map); match ar.direction() { + Polarity::Incoming => { let today = Utc::now(); - let utc_lot_date = Self::create_date_time_from_atlantic(lot.date_for_basis_purposes, NaiveTime::from_hms_milli(12, 34, 56, 789)); + let utc_lot_date = Self::create_date_time_from_atlantic( + lot.date_for_basis_purposes, + NaiveTime::from_hms_milli(12, 34, 56, 789) + ); // if today.signed_duration_since(self.lot.date_for_basis_purposes) > 365 - if (today - utc_lot_date) > Duration::days(365) { // TODO: figure out how to instantiate today's date and convert it to compare to NaiveDate + if (today - utc_lot_date) > Duration::days(365) { + // TODO: figure out how to instantiate today's date and convert it to compare to NaiveDate Term::LT } else { Term::ST } } + Polarity::Outgoing => { + let lot_date_for_basis_purposes = lot.date_for_basis_purposes; - if self.date.signed_duration_since(lot_date_for_basis_purposes) > Duration::days(365) { + + if self.date.signed_duration_since(lot_date_for_basis_purposes) > Duration::days(365) { return Term::LT } Term::ST @@ -169,9 +245,11 @@ impl Movement { } pub fn create_date_time_from_atlantic(date: NaiveDate, time: NaiveTime) -> DateTime { + let naive_datetime = NaiveDateTime::new(date, time); let east_time = Eastern.from_local_datetime(&naive_datetime).unwrap(); - east_time.with_timezone(&Utc) + + east_time.with_timezone(&Utc) } pub fn get_income( @@ -184,11 +262,15 @@ impl Movement { )-> Result> { // Returns 0 or positive number let txn = txns_map.get(&self.transaction_key).expect("Couldn't get txn. Tx num invalid?"); + match txn.transaction_type(ar_map, raw_accts, acct_map)? { + TxType::Flow => { + let ar = ar_map.get(&self.action_record_key).unwrap(); - if ar.direction() == Polarity::Incoming { - Ok(self.proceeds.get()) + + if ar.direction() == Polarity::Incoming { + Ok(-self.proceeds_lk.get()) } else { Ok(d128!(0)) } } @@ -206,11 +288,15 @@ impl Movement { )-> Result> { // Returns 0 or negative number let txn = txns_map.get(&self.transaction_key).expect("Couldn't get txn. Tx num invalid?"); + match txn.transaction_type(ar_map, raw_accts, acct_map)? { + TxType::Flow => { + let ar = ar_map.get(&self.action_record_key).unwrap(); - if ar.direction() == Polarity::Outgoing { - let expense = -self.proceeds.get(); + + if ar.direction() == Polarity::Outgoing { + let expense = -self.proceeds_lk.get(); Ok(expense) } else { Ok(d128!(0)) } @@ -245,9 +331,9 @@ impl Term { impl fmt::Display for Term { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { + match *self { Term::LT => write!(f, "LT"), Term::ST => write!(f, "ST"), - } + } } } diff --git a/src/create_lots_mvmts.rs b/src/create_lots_mvmts.rs index e34471c..fb59428 100644 --- a/src/create_lots_mvmts.rs +++ b/src/create_lots_mvmts.rs @@ -136,6 +136,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: base_lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(base_mvmt, &base_ar, &base_lot, &settings, &raw_acct_map, &acct_map); @@ -150,6 +152,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: quote_lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(quote_mvmt, "e_ar, "e_lot, &settings, &raw_acct_map, &acct_map); @@ -191,6 +195,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(whole_mvmt, &ar, &lot, &settings, &raw_acct_map, &acct_map); acct.list_of_lots.borrow_mut().push(lot); @@ -210,6 +216,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(whole_mvmt, &ar, &lot, &settings, &raw_acct_map, &acct_map); continue @@ -239,6 +247,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(whole_mvmt, &ar, &lot, &settings, &raw_acct_map, &acct_map); continue @@ -326,6 +336,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot_to_use.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; fit_into_lots( @@ -365,6 +377,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(mvmt, &ar, &lot, &settings, &raw_acct_map, &acct_map); continue @@ -395,6 +409,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; } else { assert_eq!(txn.action_record_idx_vec.len(), 2); @@ -451,6 +467,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: inner_lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(inner_mvmt, &ar, &inner_lot, &settings, &raw_acct_map, &acct_map); acct.list_of_lots.borrow_mut().push(inner_lot); @@ -483,6 +501,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; } wrap_mvmt_and_push(mvmt, &ar, &lot, &settings, &raw_acct_map, &acct_map); @@ -539,6 +559,8 @@ pub fn create_lots_and_movements( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(whole_mvmt, &ar, &lot, &settings, &raw_acct_map, &acct_map); acct.list_of_lots.borrow_mut().push(lot); @@ -693,6 +715,8 @@ fn fit_into_lots( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: newly_chosen_lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; fit_into_lots( txn_num, @@ -710,8 +734,6 @@ fn fit_into_lots( } assert!(sum_of_mvmts_in_lot > d128!(0.0)); let remainder_amt = mvmt_to_fit.amount; - // println!("Sum of mvmts in lot: {}; Remainder amount: {}; Net: {}", - // sum_of_mvmts_in_lot, remainder_amt, sum_of_mvmts_in_lot + remainder_amt); let does_remainder_fit: bool = (sum_of_mvmts_in_lot + remainder_amt) >= d128!(0.0); @@ -727,6 +749,8 @@ fn fit_into_lots( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(remainder_that_fits, &spawning_ar, &lot, &settings, &raw_acct_map, &acct_map); return // And we're done @@ -746,6 +770,8 @@ fn fit_into_lots( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; wrap_mvmt_and_push(mvmt_that_fits_in_lot, &spawning_ar, &lot, &settings, &raw_acct_map, &acct_map); let remainder_amt_to_recurse = remainder_amt + sum_of_mvmts_in_lot; @@ -765,6 +791,8 @@ fn fit_into_lots( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), // This acts as a dummy value. lot_num: newly_chosen_lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; assert!(current_index_position < vec_of_ordered_index_values.len()); fit_into_lots( @@ -841,6 +869,8 @@ fn process_multiple_incoming_lots_and_mvmts( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; // println!("From first set of incoming movements, amount: {} {} to account: {}", // incoming_mvmt.amount, acct_incoming_ar.ticker, acct_incoming_ar.account_num); @@ -878,6 +908,8 @@ fn process_multiple_incoming_lots_and_mvmts( ratio_of_amt_to_outgoing_mvmts_in_a_r: Cell::new(d128!(1.0)), lot_num: lot.lot_number, proceeds: Cell::new(d128!(0.0)), + proceeds_lk: Cell::new(d128!(0.0)), + cost_basis_lk: Cell::new(d128!(0.0)), }; // println!("Final incoming mvmt for this actionrecord, amount: {} {} to account: {}", // incoming_mvmt.amount, acct_incoming_ar.ticker, acct_incoming_ar.account_num); diff --git a/src/csv_export.rs b/src/csv_export.rs index cd4d510..1941ebd 100644 --- a/src/csv_export.rs +++ b/src/csv_export.rs @@ -46,23 +46,23 @@ pub fn _1_account_sums_to_csv( } else { balance = tentative_balance.to_string() } let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); - let cost_basis: String; + let lk_cost_basis: String; - if raw_acct.is_margin { cost_basis = "0.00".to_string() } else { - let tentative_cost_basis = acct.get_sum_of_basis_in_lots(); - if tentative_cost_basis == d128!(0) { - cost_basis = "0.00".to_string() - } else { cost_basis = tentative_cost_basis.to_string() } + if raw_acct.is_margin { lk_cost_basis = "0.00".to_string() } else { + let tentative_lk_cost_basis = acct.get_sum_of_lk_basis_in_lots(); + if tentative_lk_cost_basis == d128!(0) { + lk_cost_basis = "0.00".to_string() + } else { lk_cost_basis = tentative_lk_cost_basis.to_string() } } row.push(raw_acct.name.to_string()); row.push(balance); row.push(raw_acct.ticker.to_string()); - row.push(cost_basis); + row.push(lk_cost_basis); row.push(acct.list_of_lots.borrow().len().to_string()); rows.push(row); } - let file_name = PathBuf::from("1_Acct_Sum_with_cost_basis.csv"); + let file_name = PathBuf::from("C1_Acct_Sum_with_cost_basis.csv"); let path = PathBuf::from(&settings.export_path.clone()); let full_path: PathBuf = [path, file_name].iter().collect(); @@ -111,26 +111,102 @@ pub fn _2_account_sums_nonzero_to_csv( balance = "0.00".to_string() } else { balance_d128 += tentative_balance; balance = tentative_balance.to_string() } - let cost_basis: String; + let lk_cost_basis: String; - if raw_acct.is_margin { cost_basis = "0.00".to_string() } else { - let tentative_cost_basis = acct.get_sum_of_basis_in_lots(); - if tentative_cost_basis == d128!(0) { - cost_basis = "0.00".to_string() - } else { cost_basis = tentative_cost_basis.to_string() } + if raw_acct.is_margin { lk_cost_basis = "0.00".to_string() } else { + let tentative_lk_cost_basis = acct.get_sum_of_lk_basis_in_lots(); + if tentative_lk_cost_basis == d128!(0) { + lk_cost_basis = "0.00".to_string() + } else { lk_cost_basis = tentative_lk_cost_basis.to_string() } } if balance_d128 != d128!(0) { row.push(name); row.push(balance); row.push(raw_acct.ticker.to_string()); - row.push(cost_basis); + row.push(lk_cost_basis); row.push(acct.list_of_lots.borrow().len().to_string()); rows.push(row); } } - let file_name = PathBuf::from("2_Acct_Sum_with_nonzero_cost_basis.csv"); + let file_name = PathBuf::from("C2_Acct_Sum_with_nonzero_cost_basis.csv"); + let path = PathBuf::from(&settings.export_path.clone()); + + let full_path: PathBuf = [path, file_name].iter().collect(); + let buffer = File::create(full_path).unwrap(); + let mut wtr = csv::Writer::from_writer(buffer); + + for row in rows.iter() { + wtr.write_record(row).expect("Could not write row to CSV file"); + } + wtr.flush().expect("Could not flush Writer, though file should exist and be complete"); +} + +pub fn _3_account_sums_to_csv_with_orig_basis( + settings: &ImportProcessParameters, + raw_acct_map: &HashMap, + acct_map: &HashMap +) { + + let mut rows: Vec> = [].to_vec(); + let mut header: Vec = [].to_vec(); + + header.extend_from_slice(&[ + "Account".to_string(), + "Balance".to_string(), + "Ticker".to_string(), + "Orig. Cost Basis".to_string(), + "LK Cost Basis".to_string(), + "Total lots".to_string(), + ]); + rows.push(header); + + let length = acct_map.len(); + + for j in 1..=length { + + let acct = acct_map.get(&(j as u16)).unwrap(); + let mut row: Vec = [].to_vec(); + + let balance: String; + let tentative_balance = acct.get_sum_of_amts_in_lots(); + + if tentative_balance == d128!(0) { + balance = "0.00".to_string() + } else { balance = tentative_balance.to_string() } + + let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); + let lk_cost_basis: String; + let orig_cost_basis: String; + + if raw_acct.is_margin { + + lk_cost_basis = "0.00".to_string(); + orig_cost_basis = "0.00".to_string(); + } else { + + let tentative_lk_cost_basis = acct.get_sum_of_lk_basis_in_lots(); + let tentative_orig_cost_basis = acct.get_sum_of_orig_basis_in_lots(); + + if tentative_lk_cost_basis == d128!(0) { + lk_cost_basis = "0.00".to_string() + } else { lk_cost_basis = tentative_lk_cost_basis.to_string() } + + if tentative_orig_cost_basis == d128!(0) { + orig_cost_basis = "0.00".to_string() + } else { orig_cost_basis = tentative_orig_cost_basis.to_string() } + } + + row.push(raw_acct.name.to_string()); + row.push(balance); + row.push(raw_acct.ticker.to_string()); + row.push(orig_cost_basis); + row.push(lk_cost_basis); + row.push(acct.list_of_lots.borrow().len().to_string()); + rows.push(row); + } + let file_name = PathBuf::from("C3_Acct_Sum_with_orig_and_lk_cost_basis.csv"); let path = PathBuf::from(&settings.export_path.clone()); let full_path: PathBuf = [path, file_name].iter().collect(); @@ -197,15 +273,15 @@ pub fn _4_transaction_mvmt_detail_to_csv( amount += mvmt.amount; // To prevent printing -5E+1 instead of 50, for example let ticker = raw_acct.ticker.to_string(); let term = mvmt.get_term(acct_map, ars).to_string(); - let mut proceeds = mvmt.proceeds.get(); - let mut cost_basis = mvmt.cost_basis.get(); - let mut gain_loss = mvmt.get_gain_or_loss(); + let mut proceeds_lk = mvmt.proceeds_lk.get(); + let mut cost_basis_lk = mvmt.cost_basis_lk.get(); + let mut gain_loss = mvmt.get_lk_gain_or_loss(); let income = mvmt.get_income(ars, &raw_acct_map, &acct_map, &txns_map)?; let expense = mvmt.get_expense(ars, &raw_acct_map, &acct_map, &txns_map)?; if tx_type == TxType::Flow && amount > d128!(0) { - proceeds = d128!(0); - cost_basis = d128!(0); + proceeds_lk = d128!(0); + cost_basis_lk = d128!(0); gain_loss = d128!(0); } @@ -218,8 +294,8 @@ pub fn _4_transaction_mvmt_detail_to_csv( row.push(amount.to_string()); row.push(ticker); row.push(term); - row.push(proceeds.to_string()); - row.push(cost_basis.to_string()); + row.push(proceeds_lk.to_string()); + row.push(cost_basis_lk.to_string()); row.push(gain_loss.to_string()); row.push(income.to_string()); row.push(expense.to_string()); @@ -227,7 +303,7 @@ pub fn _4_transaction_mvmt_detail_to_csv( } } - let file_name = PathBuf::from("4_Txns_mvmts_detail.csv"); + let file_name = PathBuf::from("C4_Txns_mvmts_detail.csv"); let path = PathBuf::from(&settings.export_path); let full_path: PathBuf = [path, file_name].iter().collect(); @@ -324,8 +400,8 @@ pub fn _5_transaction_mvmt_summaries_to_csv( if term == Term::LT { amount_lt += mvmt.amount; - proceeds_lt += mvmt.proceeds.get(); - cost_basis_lt += mvmt.cost_basis.get(); + proceeds_lt += mvmt.proceeds_lk.get(); + cost_basis_lt += mvmt.cost_basis_lk.get(); match term_lt { None => { term_lt = Some(term)} _ => {} @@ -333,8 +409,8 @@ pub fn _5_transaction_mvmt_summaries_to_csv( } else { assert_eq!(term, Term::ST); amount_st += mvmt.amount; - proceeds_st += mvmt.proceeds.get(); - cost_basis_st += mvmt.cost_basis.get(); + proceeds_st += mvmt.proceeds_lk.get(); + cost_basis_st += mvmt.cost_basis_lk.get(); if term_st == None { term_st = Some(term); } @@ -346,10 +422,10 @@ pub fn _5_transaction_mvmt_summaries_to_csv( &raw_acct_map, &acct_map)? == TxType::Flow ) & (polarity == Some(Polarity::Incoming)) { - income_st = proceeds_st; + income_st = -proceeds_st; // Proceeds are negative for incoming txns proceeds_st = d128!(0); cost_basis_st = d128!(0); - income_lt = proceeds_lt; + income_lt = -proceeds_lt; // Proceeds are negative for incoming txns proceeds_lt = d128!(0); cost_basis_lt = d128!(0); } @@ -403,7 +479,118 @@ pub fn _5_transaction_mvmt_summaries_to_csv( } } - let file_name = PathBuf::from("5_Txns_mvmts_summary.csv"); + let file_name = PathBuf::from("C5_Txns_mvmts_summary.csv"); + let path = PathBuf::from(&settings.export_path); + + let full_path: PathBuf = [path, file_name].iter().collect(); + let buffer = File::create(full_path).unwrap(); + let mut wtr = csv::Writer::from_writer(buffer); + + for row in rows.iter() { + wtr.write_record(row).expect("Could not write row to CSV file"); + } + wtr.flush().expect("Could not flush Writer, though file should exist and be complete"); + + Ok(()) +} + +pub fn _6_transaction_mvmt_detail_to_csv_w_orig( + settings: &ImportProcessParameters, + ars: &HashMap, + raw_acct_map: &HashMap, + acct_map: &HashMap, + txns_map: &HashMap, +) -> Result<(), Box> { + + let mut rows: Vec> = [].to_vec(); + let mut header: Vec = [].to_vec(); + header.extend_from_slice(&[ + "Date".to_string(), + "Txn#".to_string(), + "Type".to_string(), + "Memo".to_string(), + "Amount".to_string(), + "Ticker".to_string(), + "Term".to_string(), + "Proceeds".to_string(), + "Cost basis".to_string(), + "Gain/loss".to_string(), + "Income".to_string(), + "Expense".to_string(), + "Orig. Proceeds".to_string(), + "Orig. Cost basis".to_string(), + "Orig. Gain/loss".to_string(), + ]); + rows.push(header); + + let length = txns_map.len(); + + for txn_num in 1..=length { + + let txn_num = txn_num as u32; + let txn = txns_map.get(&(txn_num)).unwrap(); + + let flow_or_outgoing_exchange_movements = txn.get_outgoing_exchange_and_flow_mvmts( + &settings.home_currency, + ars, + raw_acct_map, + acct_map, + txns_map + )?; + + for mvmt in flow_or_outgoing_exchange_movements.iter() { + let lot = mvmt.get_lot(acct_map, ars); + let acct = acct_map.get(&lot.account_key).unwrap(); + let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); + + let date = txn.date.format("%Y/%m/%d").to_string(); + let tx_number = txn.tx_number.to_string(); + let tx_type = txn.transaction_type(&ars, &raw_acct_map, &acct_map)?; + let memo = txn.memo.to_string(); + let mut amount = d128!(0); + amount += mvmt.amount; // To prevent printing -5E+1 instead of 50, for example + let ticker = raw_acct.ticker.to_string(); + let term = mvmt.get_term(acct_map, ars).to_string(); + let mut proceeds_lk = mvmt.proceeds_lk.get(); + let mut cost_basis_lk = mvmt.cost_basis_lk.get(); + let mut gain_loss = mvmt.get_lk_gain_or_loss(); + let income = mvmt.get_income(ars, &raw_acct_map, &acct_map, &txns_map)?; + let expense = mvmt.get_expense(ars, &raw_acct_map, &acct_map, &txns_map)?; + let mut orig_proc = mvmt.proceeds.get(); + let mut orig_cost = mvmt.cost_basis.get(); + let mut orig_gain_loss = mvmt.get_orig_gain_or_loss(); + + if tx_type == TxType::Flow && amount > d128!(0) { + proceeds_lk = d128!(0); + cost_basis_lk = d128!(0); + gain_loss = d128!(0); + orig_proc = d128!(0); + orig_cost = d128!(0); + orig_gain_loss = d128!(0); + } + + let mut row: Vec = [].to_vec(); + + row.push(date); + row.push(tx_number); + row.push(tx_type.to_string()); + row.push(memo); + row.push(amount.to_string()); + row.push(ticker); + row.push(term); + row.push(proceeds_lk.to_string()); + row.push(cost_basis_lk.to_string()); + row.push(gain_loss.to_string()); + row.push(income.to_string()); + row.push(expense.to_string()); + row.push(orig_proc.to_string()); + row.push(orig_cost.to_string()); + row.push(orig_gain_loss.to_string()); + rows.push(row); + } + } + + let file_name = PathBuf::from("C6_Txns_mvmts_detail_w_orig.csv"); let path = PathBuf::from(&settings.export_path); let full_path: PathBuf = [path, file_name].iter().collect(); @@ -465,7 +652,7 @@ pub fn _5_transaction_mvmt_summaries_to_csv( // row.push(txn.transaction_type(ars, &raw_acct_map, &acct_map).to_string()); // row.push(txn.memo.to_string()); // row.push(mvmt.amount.to_string()); -// row.push(mvmt.proceeds.get().to_string()); +// row.push(mvmt.proceeds_lk.get().to_string()); // row.push(mvmt.cost_basis.get().to_string()); // row.push(mvmt.get_gain_or_loss().to_string()); // row.push(mvmt.get_term(acct_map, ars).to_string()); diff --git a/src/csv_import_accts_txns.rs b/src/csv_import_accts_txns.rs index 2ca2aed..8995431 100644 --- a/src/csv_import_accts_txns.rs +++ b/src/csv_import_accts_txns.rs @@ -137,6 +137,7 @@ pub fn import_transactions( let mut this_proceeds: &str = ""; let mut this_memo: &str = ""; let mut this: String; + let mut proceeds_parsed = 0f32; // Next, create action_records. let mut action_records_map_keys_vec: Vec = [].to_vec(); @@ -152,6 +153,7 @@ pub fn import_transactions( else if idx == 1 { this = field.replace(",", ""); this_proceeds = this.as_str(); + proceeds_parsed = this_proceeds.parse::()?; } else if idx == 2 { this_memo = field; } @@ -213,7 +215,7 @@ pub fn import_transactions( date_as_string: this_tx_date.to_string(), date: tx_date, memo: this_memo.to_string(), - proceeds: this_proceeds.parse::()?, + proceeds: proceeds_parsed, action_record_idx_vec: action_records_map_keys_vec, }; diff --git a/src/import_cost_proceeds_etc.rs b/src/import_cost_proceeds_etc.rs index cdebe6c..62e1f03 100644 --- a/src/import_cost_proceeds_etc.rs +++ b/src/import_cost_proceeds_etc.rs @@ -44,36 +44,52 @@ pub fn add_cost_basis_to_movements( // println!("Txn: {} on {} of type: {:?}",txn.tx_number,txn.date, txn.transaction_type()); if !raw_acct.is_margin { + match polarity { + Polarity::Outgoing => { + if is_home_curr { + let mvmts_amt = mvmt_copy.amount; + mvmt.cost_basis.set(mvmts_amt); + mvmt.cost_basis_lk.set(mvmts_amt); + } else { - let mvmt_lot = mvmt_copy.get_lot(acct_map, ars); - let borrowed_mvmt_list = mvmt_lot.movements.borrow().clone(); - let lots_first_mvmt = borrowed_mvmt_list.first().unwrap().clone(); - let cb_of_lots_first_mvmt = lots_first_mvmt.cost_basis.get(); + + // let mvmt_lot = mvmt_copy.get_lot(acct_map, ars); + // let borrowed_mvmt_list = mvmt_lot.movements.borrow().clone(); + // let lots_first_mvmt = borrowed_mvmt_list.first().unwrap().clone(); + // let cb_of_lots_first_mvmt = lots_first_mvmt.cost_basis.get(); + let cb_of_lots_first_mvmt = mvmt_copy.get_cost_basis_of_lots_first_mvmt(acct_map, ars); let ratio_of_amt_to_lots_first_mvmt = borrowed_mvmt.ratio_of_amt_to_lots_first_mvmt(acct_map, ars); let unrounded_basis = -(cb_of_lots_first_mvmt * ratio_of_amt_to_lots_first_mvmt); let rounded_basis = round_d128_1e2(&unrounded_basis); + mvmt.cost_basis.set(rounded_basis); - // if txn.tx_number == 5 { - // println!("Txn#: {}, ratio: {}, cb of lot's 1st mvmt: {}, cost basis: {}", txn.tx_number, ratio_of_amt_to_lots_first_mvmt, cb_of_lots_first_mvmt, cost_basis) - // }; + mvmt.cost_basis_lk.set(rounded_basis); } - // println!("Outgoing a_r from txn: {} of type: {:?}, with {} {} with cost basis: {}", - // txn.tx_number, txn.transaction_type(), borrowed_mvmt.amount, ar.account.ticker, cost_basis); assert!(mvmt.cost_basis.get() <= d128!(0)); + // assert!(mvmt.cost_basis_lk.get() <= d128!(0)); // Same as above assert. continue } + Polarity::Incoming => { + if is_home_curr { + let mvmts_amt = mvmt_copy.amount; + mvmt.cost_basis.set(mvmts_amt); + mvmt.cost_basis_lk.set(mvmts_amt); + } else { + match tx_type { + TxType::Exchange => { + let other_ar = ars.get(&txn.action_record_idx_vec[0]).unwrap(); let other_acct = acct_map.get(&other_ar.account_key).unwrap(); let raw_other_acct = raw_acct_map.get(&other_acct.raw_key).unwrap(); @@ -82,42 +98,66 @@ pub fn add_cost_basis_to_movements( if other_ar_is_home_curr { mvmt.cost_basis.set(-(other_ar.amount)); + mvmt.cost_basis_lk.set(-(other_ar.amount)); + } else { let ratio_of_amt_to_incoming_mvmts_in_a_r = borrowed_mvmt.ratio_of_amt_to_incoming_mvmts_in_a_r; - let txn_proceeds = txn.proceeds.to_string().parse::().unwrap(); + let txn_proceeds = txn.proceeds + .to_string() + .parse::() + .unwrap(); let unrounded_basis = txn_proceeds * ratio_of_amt_to_incoming_mvmts_in_a_r; let rounded_basis = round_d128_1e2(&unrounded_basis); + mvmt.cost_basis.set(rounded_basis); + mvmt.cost_basis_lk.set(rounded_basis); } } + TxType::ToSelf => { + + let ratio_of_amt_to_incoming_mvmts_in_a_r = + borrowed_mvmt.ratio_of_amt_to_incoming_mvmts_in_a_r; let cb_outgoing_ar = retrieve_cost_basis_from_corresponding_outgoing_toself( - txn_num, &ars, txns_map, acct_map); - let ratio_of_amt_to_incoming_mvmts_in_a_r = borrowed_mvmt.ratio_of_amt_to_incoming_mvmts_in_a_r; - let this_ratio = (ratio_of_amt_to_incoming_mvmts_in_a_r) - .to_string() - .parse::() - .unwrap(); - let unrounded_basis = cb_outgoing_ar * this_ratio; + txn_num, + &ars, + txns_map, + acct_map + ); + // let this_ratio = ratio_of_amt_to_incoming_mvmts_in_a_r + // .to_string() + // .parse::() + // .unwrap(); // I don't recall why this was once needed in the next line. + let unrounded_basis = cb_outgoing_ar * ratio_of_amt_to_incoming_mvmts_in_a_r; let rounded_basis = round_d128_1e2(&unrounded_basis); + mvmt.cost_basis.set(-rounded_basis); + mvmt.cost_basis_lk.set(-rounded_basis); } + TxType::Flow => { + let txn_proceeds = txn.proceeds.to_string().parse::().unwrap(); - let mvmt_proceeds = round_d128_1e2(&(txn_proceeds * - borrowed_mvmt.ratio_of_amt_to_incoming_mvmts_in_a_r)); + let mvmt_proceeds = round_d128_1e2( + &(txn_proceeds * + borrowed_mvmt.ratio_of_amt_to_incoming_mvmts_in_a_r) + ); // Ratio should always be 1.0, but we do the calc anyway, for future-proofing. + mvmt.cost_basis.set(mvmt_proceeds); + mvmt.cost_basis_lk.set(mvmt_proceeds); } } } - // println!("Incoming a_r from txn: {} of type: {:?}, with {} {} with cost basis: {}", - // txn.tx_number, txn.transaction_type(), borrowed_mvmt.amount, ar.account.ticker, cost_basis); assert!(mvmt.cost_basis.get() >= d128!(0)); + // assert!(mvmt.cost_basis_lk.get() >= d128!(0)); // Same as above assert. continue } } + } else { + // Do nothing. Future changes can add a code path where margin txns "settle" + // as they happen, though, if desired. Just need to write the code. } } } @@ -174,27 +214,46 @@ pub fn add_proceeds_to_movements( let borrowed_mvmt = mvmt_copy.clone(); match tx_type { - TxType::Exchange => { + + TxType::Exchange | TxType::Flow => { + match polarity { + Polarity::Outgoing => { + let ratio = borrowed_mvmt.amount / ar.amount; let proceeds_unrounded = txn.proceeds.to_string().parse::().unwrap() * ratio; let proceeds_rounded = round_d128_1e2(&proceeds_unrounded); + mvmt.proceeds.set(proceeds_rounded); + mvmt.proceeds_lk.set(proceeds_rounded); + } + + Polarity::Incoming => { + // For a time, this was blank. As part of the commit(s) to add cost_basis_lk + // and proceeds_lk, let's change this to reflect that incoming proceeds are now + // negative, which net against the positive cost_basis to result in a gain of $0. + // Additionally, we apply the same treatment to Flow txns. + mvmt.proceeds.set(-mvmt.cost_basis.get()); + mvmt.proceeds_lk.set(-mvmt.cost_basis_lk.get()); } - Polarity::Incoming => {} } } - TxType::Flow => { - let ratio = borrowed_mvmt.amount / ar.amount; - let proceeds_unrounded = txn.proceeds.to_string().parse::().unwrap() * ratio; - let proceeds_rounded = round_d128_1e2(&proceeds_unrounded); - mvmt.proceeds.set(proceeds_rounded); + + // TxType::Flow => { + // let ratio = borrowed_mvmt.amount / ar.amount; + // let proceeds_unrounded = txn.proceeds.to_string().parse::().unwrap() * ratio; + // let proceeds_rounded = round_d128_1e2(&proceeds_unrounded); + // mvmt.proceeds.set(proceeds_rounded); + // } + + TxType::ToSelf => { + // Originally did nothing. Now explicity creating a condition where a report containing + // ToSelf txns would reflect a $0 gain/loss. + mvmt.proceeds.set(-mvmt.cost_basis.get()); + mvmt.proceeds_lk.set(-mvmt.cost_basis_lk.get()); } - TxType::ToSelf => {} } - // println!("Txn: {}, type: {:?} of {} {} w/ proceeds: {} & basis: {}", - // txn.tx_number, txn.transaction_type(), borrowed_mvmt.amount, ar.account.ticker, proceeds, borrowed_mvmt.cost_basis); } } } @@ -222,6 +281,9 @@ pub fn apply_like_kind_treatment( if txn.date <= cutoff_date { perform_likekind_treatment_on_txn(txn_num, &settings, &ars, &raw_acct_map, &acct_map, &txns_map)?; } + // else { + // // carry_forward_deferred_gains(txn_num, &settings, &ars, &raw_acct_map, &acct_map, &txns_map)?; + // } } Ok(()) @@ -236,7 +298,7 @@ fn update_current_txn_for_prior_likekind_treatment( txns_map: &HashMap, ) -> Result<(), Box> { - let mut sum_of_outgoing_cost_basis_in_ar = d128!(0); + let mut sum_of_outgoing_lk_cost_basis_in_ar = d128!(0); let txn = txns_map.get(&txn_num).unwrap(); for ar_num in txn.action_record_idx_vec.iter() { @@ -256,31 +318,58 @@ fn update_current_txn_for_prior_likekind_treatment( let borrowed_mvmt = mvmt_copy.clone(); if !raw_acct.is_margin { + match polarity { + Polarity::Outgoing => { + if !is_home_curr { - let borrowed_mvmt_lot = borrowed_mvmt.get_lot(acct_map, ars); - let borrowed_mvmt_list = borrowed_mvmt_lot.movements.borrow(); - let cb_of_lots_first_mvmt = borrowed_mvmt_list.first().unwrap().cost_basis.get(); + + // let borrowed_mvmt_lot = borrowed_mvmt.get_lot(acct_map, ars); + // let borrowed_mvmt_list = borrowed_mvmt_lot.movements.borrow(); + // let lk_cb_of_lots_first_mvmt = borrowed_mvmt_list.first().unwrap().cost_basis_lk.get(); + let lk_cb_of_lots_first_mvmt = borrowed_mvmt.get_lk_cost_basis_of_lots_first_mvmt(acct_map, ars); let ratio_of_amt_to_lots_first_mvmt = borrowed_mvmt.ratio_of_amt_to_lots_first_mvmt(acct_map, ars); - let unrounded_basis = -(cb_of_lots_first_mvmt * ratio_of_amt_to_lots_first_mvmt); - let rounded_basis = round_d128_1e2(&unrounded_basis); - mvmt.cost_basis.set(rounded_basis); + let unrounded_lk_basis = -(lk_cb_of_lots_first_mvmt * ratio_of_amt_to_lots_first_mvmt); + let rounded_lk_basis = round_d128_1e2(&unrounded_lk_basis); + + mvmt.cost_basis_lk.set(rounded_lk_basis); + + if tx_type == TxType::ToSelf { + mvmt.proceeds_lk.set(-rounded_lk_basis) + } } - sum_of_outgoing_cost_basis_in_ar += mvmt.cost_basis.get() + sum_of_outgoing_lk_cost_basis_in_ar += mvmt.cost_basis_lk.get() } + Polarity::Incoming => { + match tx_type { - TxType::Exchange => {} - TxType::Flow => {} + + TxType::Exchange => { + // Do nothing. + // If txn.date is after the LK treatment date, the incoming mvmt goes untreated. + // If txn.date is before the LK date, incoming mvmt gets treatment in apply_lk_treatment next. + } + TxType::Flow => { + if txn.action_record_idx_vec.len() == 2 { + mvmt.cost_basis_lk.set(d128!(0)); + mvmt.proceeds_lk.set(d128!(0)); + } + // Do nothing for non-margin txns. + } TxType::ToSelf => { + if !is_home_curr { + let ratio_of_amt_to_incoming_mvmts_in_a_r = borrowed_mvmt.ratio_of_amt_to_incoming_mvmts_in_a_r; - let unrounded_basis = sum_of_outgoing_cost_basis_in_ar * + let unrounded_lk_basis = sum_of_outgoing_lk_cost_basis_in_ar * ratio_of_amt_to_incoming_mvmts_in_a_r; - let rounded_basis = round_d128_1e2(&unrounded_basis); - mvmt.cost_basis.set(-rounded_basis); + let rounded_lk_basis = round_d128_1e2(&unrounded_lk_basis); + + mvmt.cost_basis_lk.set(-rounded_lk_basis); + mvmt.proceeds_lk.set(rounded_lk_basis); } } } @@ -305,27 +394,26 @@ fn perform_likekind_treatment_on_txn( let txn = txns_map.get(&txn_num).unwrap(); let tx_type = txn.transaction_type(ars, raw_acct_map, acct_map)?; - let og_ar = ars.get(&txn.action_record_idx_vec.first().unwrap()).unwrap(); - let ic_ar = ars.get(&txn.action_record_idx_vec.last().unwrap()).unwrap(); - let og_acct = acct_map.get(&og_ar.account_key).unwrap(); - let ic_acct = acct_map.get(&ic_ar.account_key).unwrap(); - let raw_og_acct = raw_acct_map.get(&og_acct.raw_key).unwrap(); - let raw_ic_acct = raw_acct_map.get(&ic_acct.raw_key).unwrap(); - - fn both_are_non_home_curr(raw_og_acct: &RawAccount, raw_ic_acct: &RawAccount, settings: &ImportProcessParameters) -> bool { - let og_is_home_curr = raw_og_acct.is_home_currency(&settings.home_currency); - let ic_is_home_curr = raw_ic_acct.is_home_currency(&settings.home_currency); - let both_are_non_home_curr = !ic_is_home_curr && !og_is_home_curr; - both_are_non_home_curr - } - match tx_type { + TxType::Exchange => { - if both_are_non_home_curr(raw_og_acct, raw_ic_acct, settings) { - let mut sum_of_outgoing_cost_basis_in_ar = d128!(0); + + let og_ar = ars.get(&txn.action_record_idx_vec.first().unwrap()).unwrap(); + let ic_ar = ars.get(&txn.action_record_idx_vec.last().unwrap()).unwrap(); + let og_acct = acct_map.get(&og_ar.account_key).unwrap(); + let ic_acct = acct_map.get(&ic_ar.account_key).unwrap(); + let raw_og_acct = raw_acct_map.get(&og_acct.raw_key).unwrap(); + let raw_ic_acct = raw_acct_map.get(&ic_acct.raw_key).unwrap(); + + if _both_are_non_home_curr(raw_og_acct, raw_ic_acct, settings) { + + let mut sum_of_outgoing_lk_cost_basis_in_ar = d128!(0); + for ar_num in txn.action_record_idx_vec.iter() { + let ar = ars.get(ar_num).unwrap(); let movements = ar.get_mvmts_in_ar(acct_map, txns_map); + for mvmt in movements.iter() { let polarity = ar.direction(); @@ -334,26 +422,38 @@ fn perform_likekind_treatment_on_txn( let borrowed_mvmt = mvmt_copy.clone(); match polarity { + Polarity::Outgoing => { - let cb = borrowed_mvmt.cost_basis.get(); - sum_of_outgoing_cost_basis_in_ar += cb; - mvmt.proceeds.set(-cb); + + let cb = borrowed_mvmt.cost_basis_lk.get(); + sum_of_outgoing_lk_cost_basis_in_ar += cb; + + mvmt.proceeds_lk.set(-cb); } + Polarity::Incoming => { + let ratio_of_amt_to_incoming_mvmts_in_a_r = borrowed_mvmt.ratio_of_amt_to_incoming_mvmts_in_a_r; - let unrounded_basis = sum_of_outgoing_cost_basis_in_ar * + let unrounded_basis = sum_of_outgoing_lk_cost_basis_in_ar * ratio_of_amt_to_incoming_mvmts_in_a_r; let rounded_basis = round_d128_1e2(&unrounded_basis); - mvmt.cost_basis.set(-rounded_basis); + + mvmt.cost_basis_lk.set(-rounded_basis); + mvmt.proceeds_lk.set(rounded_basis); } } } } } } + TxType::Flow => { + if txn.action_record_idx_vec.len() == 2 { + + // Consider asserting TxHasMargin::OneAR + for ar_num in txn.action_record_idx_vec.iter() { let ar = ars.get(ar_num).unwrap(); @@ -364,7 +464,13 @@ fn perform_likekind_treatment_on_txn( for mvmt in movements.iter() { match polarity { - Polarity::Outgoing => {} + + Polarity::Outgoing => { + // Do nothing. + // If 'spot' acct outgoing, the loss nets to the basis in the spent coins. + // If margin acct outgoing, no gain, and the incoming gets no cost basis. + } + Polarity::Incoming => { // Reminder: May need extra logic here if margin exchange trades get cost_basis and proceeds mvmt.cost_basis.set(d128!(0)); @@ -375,7 +481,23 @@ fn perform_likekind_treatment_on_txn( } } } - TxType::ToSelf => {} + + TxType::ToSelf => { + // Like-kind "exchange," so do nothing. + } + } + + fn _both_are_non_home_curr( + raw_og_acct: &RawAccount, + raw_ic_acct: &RawAccount, + settings: &ImportProcessParameters + ) -> bool { + + let og_is_home_curr = raw_og_acct.is_home_currency(&settings.home_currency); + let ic_is_home_curr = raw_ic_acct.is_home_currency(&settings.home_currency); + let both_are_non_home_curr = !ic_is_home_curr && !og_is_home_curr; + + both_are_non_home_curr } Ok(()) diff --git a/src/main.rs b/src/main.rs index aa1155f..d77fe66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -275,6 +275,12 @@ fn main() -> Result<(), Box> { &raw_acct_map ); + csv_export::_3_account_sums_to_csv_with_orig_basis( + &settings, + &raw_acct_map, + &account_map + ); + csv_export::_4_transaction_mvmt_detail_to_csv( &settings, &action_records_map, @@ -291,6 +297,14 @@ fn main() -> Result<(), Box> { &transactions_map )?; + csv_export::_6_transaction_mvmt_detail_to_csv_w_orig( + &settings, + &action_records_map, + &raw_acct_map, + &account_map, + &transactions_map + )?; + txt_export::_1_account_lot_detail_to_txt( &settings, &raw_acct_map, diff --git a/src/txt_export.rs b/src/txt_export.rs index 58ff27e..84eebf4 100644 --- a/src/txt_export.rs +++ b/src/txt_export.rs @@ -9,7 +9,7 @@ use std::io::prelude::Write; use decimal::d128; -use crate::transaction::{Transaction, ActionRecord, TxType}; +use crate::transaction::{Transaction, ActionRecord}; use crate::account::{Account, RawAccount}; use crate::core_functions::{ImportProcessParameters}; @@ -60,7 +60,7 @@ pub fn _1_account_lot_detail_to_txt( - let file_name = PathBuf::from("1_Acct_lot_detail.txt"); + let file_name = PathBuf::from("T1_Acct_lot_detail.txt"); let path = PathBuf::from(&settings.export_path.clone()); let full_path: PathBuf = [path, file_name].iter().collect(); @@ -99,14 +99,16 @@ Enable like-kind treatment: {}", writeln!(file, "Account balance: {} {}; Total cost basis: {}", acct.get_sum_of_amts_in_lots(), raw_acct.ticker, - acct.get_sum_of_basis_in_lots() + acct.get_sum_of_lk_basis_in_lots() )?; + } else { + continue } if raw_acct.is_margin { writeln!(file, "Margin Account")?; } for (lot_idx, lot) in acct.list_of_lots.borrow().iter().enumerate() { - let lot_basis = lot.get_sum_of_basis_in_lot(); + let lk_lot_basis = lot.get_sum_of_lk_basis_in_lot(); let movements_sum = lot.get_sum_of_amts_in_lot(); if acct.list_of_lots.borrow().len() > 0 { @@ -115,7 +117,7 @@ Enable like-kind treatment: {}", writeln!(file, " Lot {}", (lot_idx+1))?; writeln!(file, "\t• Σ: {}, with remaining cost basis of {} and basis date of {}", movements_sum, - lot_basis, + lk_lot_basis, lot.date_for_basis_purposes )?; writeln!(file, "\t Movements:")?; @@ -137,26 +139,27 @@ Enable like-kind treatment: {}", writeln!(file, "{}", description_str)?; - let proceeds = mvmt.proceeds.get(); - let cost_basis = mvmt.cost_basis.get(); + let lk_proceeds = mvmt.proceeds_lk.get(); + let lk_cost_basis = mvmt.cost_basis_lk.get(); let gain_loss: d128; - if mvmt.amount > d128!(0) { // Can't have a gain on an incoming txn - gain_loss = d128!(0) - } else if raw_acct.is_home_currency(&settings.home_currency) { // Can't have a gain disposing home currency - gain_loss = d128!(0) - } else if tx_type == TxType::ToSelf { // Can't have a gain sending to yourself + // if mvmt.amount > d128!(0) { // Can't have a gain on an incoming txn + // gain_loss = d128!(0) + // } else + if raw_acct.is_home_currency(&settings.home_currency) { // Can't have a gain disposing home currency gain_loss = d128!(0) + // } else if tx_type == TxType::ToSelf { // Can't have a gain sending to yourself + // gain_loss = d128!(0) } else { - gain_loss = proceeds + cost_basis + gain_loss = lk_proceeds + lk_cost_basis; } let income = mvmt.get_income(ars, raw_acct_map, acct_map, txns_map)?; let expense = mvmt.get_expense(ars, raw_acct_map, acct_map, txns_map)?; let activity_str = format!("\t\t\tProceeds: {}; Cost basis: {}; for Gain/loss: {} {}; Inc.: {}; Exp.: {}.", - proceeds, - cost_basis, + lk_proceeds, + lk_cost_basis, mvmt.get_term(acct_map, ars), gain_loss, income, @@ -166,8 +169,8 @@ Enable like-kind treatment: {}", writeln!(file, "{}", activity_str)?; // if settings.enable_like_kind_treatment { - // let dg_prev = mvmt.deferred_gain_prev.get(); - // let dg_curr = mvmt.deferred_gain_curr.get(); + // let dg_prev = mvmt.proceeds_lk.get(); + // let dg_curr = mvmt.cost_basis_lk.get(); // let activity_str = format!("\t\t\tGain deferred in this txn: {}; Accumulated in prior txns: {}", // dg_curr, @@ -205,7 +208,7 @@ pub fn _2_account_lot_summary_to_txt( - let file_name = PathBuf::from("2_Acct_lot_summary.txt"); + let file_name = PathBuf::from("T2_Acct_lot_summary.txt"); let path = PathBuf::from(&settings.export_path.clone()); let full_path: PathBuf = [path, file_name].iter().collect(); @@ -244,14 +247,14 @@ Enable like-kind treatment: {}", writeln!(file, "Account balance: {} {}; Total cost basis: {}", acct.get_sum_of_amts_in_lots(), raw_acct.ticker, - acct.get_sum_of_basis_in_lots() + acct.get_sum_of_lk_basis_in_lots() )?; } if raw_acct.is_margin { writeln!(file, "Margin Account")?; } for (lot_idx, lot) in acct.list_of_lots.borrow().iter().enumerate() { - let lot_basis = lot.get_sum_of_basis_in_lot(); + let lk_lot_basis = lot.get_sum_of_lk_basis_in_lot(); let movements_sum = lot.get_sum_of_amts_in_lot(); if acct.list_of_lots.borrow().len() > 0 { @@ -259,7 +262,7 @@ Enable like-kind treatment: {}", writeln!(file, " Lot {} • Σ: {}, with remaining cost basis of {} and basis date of {}", (lot_idx+1), movements_sum, - lot_basis, + lk_lot_basis, lot.date_for_basis_purposes )?; } @@ -287,7 +290,7 @@ pub fn _3_account_lot_summary_non_zero_to_txt( - let file_name = PathBuf::from("3_Acct_lot_summary_non_zero.txt"); + let file_name = PathBuf::from("T3_Acct_lot_summary_non_zero.txt"); let path = PathBuf::from(&settings.export_path.clone()); let full_path: PathBuf = [path, file_name].iter().collect(); @@ -328,7 +331,7 @@ Enable like-kind treatment: {}", writeln!(file, "Account balance: {} {}; Total cost basis: {}", amt_in_acct, raw_acct.ticker, - acct.get_sum_of_basis_in_lots() + acct.get_sum_of_lk_basis_in_lots() )?; } else { continue @@ -338,7 +341,7 @@ Enable like-kind treatment: {}", for (lot_idx, lot) in acct.list_of_lots.borrow().iter().enumerate() { - let lot_basis = lot.get_sum_of_basis_in_lot(); + let lk_lot_basis = lot.get_sum_of_lk_basis_in_lot(); let movements_sum = lot.get_sum_of_amts_in_lot(); if acct.list_of_lots.borrow().len() > 0 { @@ -347,7 +350,7 @@ Enable like-kind treatment: {}", writeln!(file, " Lot {} • Σ: {}, with remaining cost basis of {} and basis date of {}", (lot_idx+1), movements_sum, - lot_basis, + lk_lot_basis, lot.date_for_basis_purposes )?; }