diff --git a/src/core_functions.rs b/src/core_functions.rs index 81be4ff..d32d672 100644 --- a/src/core_functions.rs +++ b/src/core_functions.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use std::error::Error; use std::fs::File; use std::collections::{HashMap}; +use std::fmt; use chrono::NaiveDate; use structopt::StructOpt; @@ -27,6 +28,18 @@ pub enum InventoryCostingMethod { FIFObyLotBasisDate, } +impl fmt::Display for InventoryCostingMethod { + + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + InventoryCostingMethod::LIFObyLotCreationDate => write!(f, "LIFO by lot creation date"), + InventoryCostingMethod::LIFObyLotBasisDate => write!(f, "LIFO by lot basis date"), + InventoryCostingMethod::FIFObyLotCreationDate => write!(f, "FIFO by lot creation date"), + InventoryCostingMethod::FIFObyLotBasisDate => write!(f, "FIFO by lot basis date"), + } + } +} + #[derive(Clone)] pub struct LikeKindSettings { pub like_kind_cutoff_date: NaiveDate, diff --git a/src/main.rs b/src/main.rs index e1b37fb..afd002c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -23,6 +23,7 @@ mod create_lots_mvmts; mod import_cost_proceeds_etc; mod cli_user_choices; mod csv_export; +mod txt_export; mod string_utils; mod decimal_utils; mod tests; @@ -290,6 +291,14 @@ fn main() -> Result<(), Box> { &transactions_map )?; + txt_export::_1_account_lot_detail_to_txt( + &settings, + &raw_acct_map, + &account_map, + &transactions_map, + &action_records_map + )?; + } // use tests::test; diff --git a/src/txt_export.rs b/src/txt_export.rs new file mode 100644 index 0000000..a1d4545 --- /dev/null +++ b/src/txt_export.rs @@ -0,0 +1,185 @@ +// Copyright (c) 2017-2019, scoobybejesus +// Redistributions must include the license: https://github.com/scoobybejesus/cryptools-rs/blob/master/LEGAL.txt + +use std::fs::{OpenOptions}; +use std::collections::{HashMap}; +use std::path::PathBuf; +use std::error::Error; +use std::io::prelude::Write; + +use decimal::d128; + +use crate::transaction::{Transaction, ActionRecord, TxType}; +use crate::account::{Account, RawAccount}; +use crate::core_functions::{ImportProcessParameters}; + + +pub fn _1_account_lot_detail_to_txt( + settings: &ImportProcessParameters, + raw_acct_map: &HashMap, + acct_map: &HashMap, + txns_map: &HashMap, + ars: &HashMap, +) -> Result<(), Box> { + + // ===================================== + // Exchange BTC + // Account balance: 0.5000000 BTC; Total cost basis: 450.0000000 + // ------------------------- + // Lot 1 + // • Σ: 0E-7, with remaining cost basis of 0E-7 and basis date of 2016-01-01 + // Movements: + // 1. 0.2500000 BTC (Txn #1) Exchange txn on 1/1/16. - FIRST + // Proceeds: 0.0; Cost basis: 220.0000000; for Gain/loss: LT 0; Inc.: 0; Exp.: 0. + // 2. -0.2500000 BTC (Txn #2) Exchange txn on 2/1/16. - SECOND + // Proceeds: 250.0; Cost basis: -220.0; for Gain/loss: ST 30.0; Inc.: 0; Exp.: 0. + // ------------------------- + // Lot 2 + // • Σ: 0E-7, with remaining cost basis of 0.0 and basis date of 2016-03-01 + // Movements: + // 1. 0.3000000 BTC (Txn #3) Exchange txn on 3/1/16. - THIRD + // Proceeds: 0.0; Cost basis: 160.0; for Gain/loss: LT 0; Inc.: 0; Exp.: 0. + // 2. -0.3 BTC (Txn #5) Exchange txn on 7/1/16. - FIFTH + // Proceeds: 100.0; Cost basis: -160.0; for Gain/loss: ST -60.0; Inc.: 0; Exp.: 0. + // ------------------------- + // Lot 3 + // • Σ: 0E-7, with remaining cost basis of 0.0 and basis date of 2016-04-01 + // Movements: + // 1. 0.3000000 BTC (Txn #4) Exchange txn on 4/1/16. - FOURTH + // Proceeds: 0.0; Cost basis: 210.0; for Gain/loss: LT 0; Inc.: 0; Exp.: 0. + // 2. -0.3 BTC (Txn #5) Exchange txn on 7/1/16. - FIFTH + // Proceeds: 100.0; Cost basis: -210.0; for Gain/loss: ST -110.0; Inc.: 0; Exp.: 0. + // ------------------------- + // Lot 4 + // • Σ: 0.5000000, with remaining cost basis of 450.0 and basis date of 2016-10-01 + // Movements: + // 1. 1.0000000 BTC (Txn #6) Exchange txn on 10/1/16. - SIXTH + // Proceeds: 0.0; Cost basis: 900.0; for Gain/loss: LT 0; Inc.: 0; Exp.: 0. + // 2. -0.5000000 BTC (Txn #7) ToSelf txn on 1/1/18. - SEVENTH + // Proceeds: 0.0; Cost basis: -450.0; for Gain/loss: LT 0; Inc.: 0; Exp.: 0. + + + + let file_name = PathBuf::from("1_Acct_lot_detail.txt"); + let path = PathBuf::from(&settings.export_path.clone()); + let full_path: PathBuf = [path, file_name].iter().collect(); + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(full_path)?; + + let length = acct_map.len(); + + writeln!(file, "Account Listing - All Lots - All Movements - with high level of detail. +\nCosting method used: {}. +Home currency: {} +Enable like-kind treatment: {}", + settings.costing_method, + settings.home_currency, + settings.enable_like_kind_treatment + )?; + + if settings.enable_like_kind_treatment { + writeln!(file, "Like-kind cut-off date: {}.", + settings.lk_cutoff_date_string + )?; + } + + for j in 1..=length { + + let acct = acct_map.get(&(j as u16)).unwrap(); + let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap(); + + if acct.list_of_lots.borrow().len() > 0 { + + writeln!(file, "\n\n=====================================")?; + writeln!(file, "{} {}", raw_acct.name, raw_acct.ticker)?; + writeln!(file, "Account balance: {} {}; Total cost basis: {}", + acct.get_sum_of_amts_in_lots(), + raw_acct.ticker, + acct.get_sum_of_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 movements_sum = lot.get_sum_of_amts_in_lot(); + + if acct.list_of_lots.borrow().len() > 0 { + + writeln!(file, "-------------------------")?; + writeln!(file, " Lot {}", (lot_idx+1))?; + writeln!(file, "\t• Σ: {}, with remaining cost basis of {} and basis date of {}", + movements_sum, + lot_basis, + lot.date_for_basis_purposes + )?; + writeln!(file, "\t Movements:")?; + + for (m_idx, mvmt) in lot.movements.borrow().iter().enumerate() { + + let txn = txns_map.get(&mvmt.transaction_key).unwrap(); + let tx_type = txn.transaction_type(ars, raw_acct_map, acct_map)?; + + let description_str = format!("\t\t{}.\t{} {} (Txn #{}) {} txn on {}. - {}", + (m_idx+1), + mvmt.amount, + raw_acct.ticker, + mvmt.transaction_key, + tx_type, + mvmt.date_as_string, + txn.memo + ); + + writeln!(file, "{}", description_str)?; + + let proceeds = mvmt.proceeds.get(); + let cost_basis = mvmt.cost_basis.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 + gain_loss = d128!(0) + } else { + gain_loss = proceeds + 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, + mvmt.get_term(acct_map, ars), + gain_loss, + income, + expense, + ); + + 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 activity_str = format!("\t\t\tGain deferred in this txn: {}; Accumulated in prior txns: {}", + // dg_curr, + // dg_prev, + // ); + + // writeln!(file, "{}", activity_str)? + // } + } + } + } + } + + Ok(()) +}