This commit is contained in:
parent
dd7ac99cd0
commit
8fd35b0d94
|
@ -11,7 +11,7 @@ use chrono::NaiveDate;
|
||||||
|
|
||||||
use crate::account::{Account, RawAccount, Lot};
|
use crate::account::{Account, RawAccount, Lot};
|
||||||
use crate::transaction::{Transaction, ActionRecord};
|
use crate::transaction::{Transaction, ActionRecord};
|
||||||
use crate::user_choices::{LotProcessingChoices, LikeKindSettings};
|
use crate::cli_user_choices::{LotProcessingChoices, LikeKindSettings};
|
||||||
use crate::import_accts_txns;
|
use crate::import_accts_txns;
|
||||||
use crate::import_cost_proceeds_etc;
|
use crate::import_cost_proceeds_etc;
|
||||||
use crate::create_lots_mvmts;
|
use crate::create_lots_mvmts;
|
||||||
|
|
|
@ -10,7 +10,7 @@ use chrono::NaiveDate;
|
||||||
|
|
||||||
use crate::transaction::{Transaction, ActionRecord, TxType, Polarity, TxHasMargin};
|
use crate::transaction::{Transaction, ActionRecord, TxType, Polarity, TxHasMargin};
|
||||||
use crate::account::{Account, RawAccount, Lot, Movement};
|
use crate::account::{Account, RawAccount, Lot, Movement};
|
||||||
use crate::user_choices::{LotProcessingChoices, InventoryCostingMethod, LikeKindSettings};
|
use crate::cli_user_choices::{LotProcessingChoices, InventoryCostingMethod, LikeKindSettings};
|
||||||
use crate::utils::{round_d128_1e8};
|
use crate::utils::{round_d128_1e8};
|
||||||
|
|
||||||
pub fn create_lots_and_movements(
|
pub fn create_lots_and_movements(
|
||||||
|
|
433
src/export.rs
433
src/export.rs
|
@ -1,433 +0,0 @@
|
||||||
// Copyright (c) 2017-2019, scoobybejesus
|
|
||||||
// Redistributions must include the license: https://github.com/scoobybejesus/cryptools-rs/LEGAL.txt
|
|
||||||
|
|
||||||
use std::rc::{Rc};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::collections::{HashMap};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use decimal::d128;
|
|
||||||
|
|
||||||
use crate::transaction::{Transaction, ActionRecord, Polarity, TxType};
|
|
||||||
use crate::account::{Account, RawAccount, Term};
|
|
||||||
use crate::user_choices::{LotProcessingChoices};
|
|
||||||
|
|
||||||
|
|
||||||
pub fn _1_account_sums_to_csv(
|
|
||||||
settings: &LotProcessingChoices,
|
|
||||||
raw_acct_map: &HashMap<u16, RawAccount>,
|
|
||||||
acct_map: &HashMap<u16, Account>
|
|
||||||
) {
|
|
||||||
|
|
||||||
let mut rows: Vec<Vec<String>> = [].to_vec();
|
|
||||||
let mut header: Vec<String> = [].to_vec();
|
|
||||||
|
|
||||||
header.extend_from_slice(&[
|
|
||||||
"Account".to_string(),
|
|
||||||
"Balance".to_string(),
|
|
||||||
"Ticker".to_string(),
|
|
||||||
"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<String> = [].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 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() }
|
|
||||||
}
|
|
||||||
|
|
||||||
row.push(raw_acct.name.to_string());
|
|
||||||
row.push(balance);
|
|
||||||
row.push(raw_acct.ticker.to_string());
|
|
||||||
row.push(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 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 _2_account_sums_nonzero_to_csv(
|
|
||||||
acct_map: &HashMap<u16, Account>,
|
|
||||||
settings: &LotProcessingChoices,
|
|
||||||
raw_acct_map: &HashMap<u16, RawAccount>
|
|
||||||
) {
|
|
||||||
|
|
||||||
let mut rows: Vec<Vec<String>> = [].to_vec();
|
|
||||||
let mut header: Vec<String> = [].to_vec();
|
|
||||||
|
|
||||||
header.extend_from_slice(&[
|
|
||||||
"Account".to_string(),
|
|
||||||
"Balance".to_string(),
|
|
||||||
"Ticker".to_string(),
|
|
||||||
"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<String> = [].to_vec();
|
|
||||||
|
|
||||||
let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap();
|
|
||||||
let name = raw_acct.name.to_string();
|
|
||||||
|
|
||||||
let balance: String;
|
|
||||||
let mut balance_d128 = d128!(0);
|
|
||||||
let tentative_balance = acct.get_sum_of_amts_in_lots();
|
|
||||||
|
|
||||||
if tentative_balance == d128!(0) {
|
|
||||||
balance = "0.00".to_string()
|
|
||||||
} else { balance_d128 += tentative_balance; balance = tentative_balance.to_string() }
|
|
||||||
|
|
||||||
let 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 balance_d128 != d128!(0) {
|
|
||||||
row.push(name);
|
|
||||||
row.push(balance);
|
|
||||||
row.push(raw_acct.ticker.to_string());
|
|
||||||
row.push(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 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 transactions_to_csv(
|
|
||||||
// transactions: &[Rc<Transaction>],
|
|
||||||
// ars: &HashMap<u32, ActionRecord>,
|
|
||||||
// raw_acct_map: &HashMap<u16, RawAccount>,
|
|
||||||
// acct_map: &HashMap<u16, Account>,
|
|
||||||
// txns_map: &HashMap<u32, Transaction>,) {
|
|
||||||
|
|
||||||
// let mut rows: Vec<Vec<String>> = [].to_vec();
|
|
||||||
// let mut header: Vec<String> = [].to_vec();
|
|
||||||
// header.extend_from_slice(&[
|
|
||||||
// "Date".to_string(),
|
|
||||||
// "Txn#".to_string(),
|
|
||||||
// "Type".to_string(),
|
|
||||||
// "Memo".to_string(),
|
|
||||||
// "Amount".to_string(),
|
|
||||||
// "Ticker".to_string(),
|
|
||||||
// "Proceeds".to_string(),
|
|
||||||
// "Cost basis".to_string(),
|
|
||||||
// "Gain/loss".to_string(),
|
|
||||||
// "Term".to_string(),
|
|
||||||
// "Income".to_string(),
|
|
||||||
// "Expense".to_string(),
|
|
||||||
// ]);
|
|
||||||
// rows.push(header);
|
|
||||||
// for txn in transactions {
|
|
||||||
// for mvmt in txn.flow_or_outgoing_exchange_movements.borrow().iter() {
|
|
||||||
// let lot = mvmt.borrow().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 mut row: Vec<String> = [].to_vec();
|
|
||||||
// row.push(txn.date.format("%Y/%m/%d").to_string());
|
|
||||||
// row.push(txn.tx_number.to_string());
|
|
||||||
// row.push(txn.transaction_type(&ars, &raw_acct_map, &acct_map).to_string());
|
|
||||||
// row.push(txn.memo.to_string());
|
|
||||||
// row.push(mvmt.borrow().amount.to_string());
|
|
||||||
// row.push(raw_acct.ticker.to_string());
|
|
||||||
// row.push(mvmt.borrow().proceeds.to_string());
|
|
||||||
// row.push(mvmt.borrow().cost_basis.to_string());
|
|
||||||
// row.push(mvmt.borrow().get_gain_or_loss().to_string());
|
|
||||||
// row.push(mvmt.borrow().get_term(acct_map, ars).to_string());
|
|
||||||
// row.push(mvmt.borrow().get_income(ars, &raw_acct_map, &acct_map, &txns_map).to_string());
|
|
||||||
// row.push(mvmt.borrow().get_expense(ars, &raw_acct_map, &acct_map, &txns_map).to_string());
|
|
||||||
// rows.push(row);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// let buffer = File::create("/Users/scoob/Documents/Testing/rust_exports/test/txns-3rd-try.csv").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 _5_transaction_mvmt_summaries_to_csv(
|
|
||||||
settings: &LotProcessingChoices,
|
|
||||||
ars: &HashMap<u32, ActionRecord>,
|
|
||||||
raw_acct_map: &HashMap<u16, RawAccount>,
|
|
||||||
acct_map: &HashMap<u16, Account>,
|
|
||||||
txns_map: &HashMap<u32, Transaction>,
|
|
||||||
) {
|
|
||||||
|
|
||||||
let mut rows: Vec<Vec<String>> = [].to_vec();
|
|
||||||
let mut header: Vec<String> = [].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(),
|
|
||||||
]);
|
|
||||||
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 txn_date_string = txn.date.format("%Y/%m/%d").to_string();
|
|
||||||
let tx_num_string = txn.tx_number.to_string();
|
|
||||||
let tx_type_string = txn.transaction_type(ars, &raw_acct_map, &acct_map).to_string();
|
|
||||||
let tx_memo_string = txn.memo.to_string();
|
|
||||||
let mut term_st: Option<Term> = None;
|
|
||||||
let mut term_lt: Option<Term> = None;
|
|
||||||
let mut ticker: Option<String> = None;
|
|
||||||
let mut polarity: Option<Polarity> = None;
|
|
||||||
|
|
||||||
let mut amount_st = d128!(0);
|
|
||||||
let mut proceeds_st = d128!(0);
|
|
||||||
let mut cost_basis_st = d128!(0);
|
|
||||||
|
|
||||||
let mut income_st = d128!(0);
|
|
||||||
let mut expense_st = d128!(0);
|
|
||||||
|
|
||||||
let mut amount_lt = d128!(0);
|
|
||||||
let mut proceeds_lt = d128!(0);
|
|
||||||
let mut cost_basis_lt = d128!(0);
|
|
||||||
|
|
||||||
let mut income_lt = d128!(0);
|
|
||||||
let mut expense_lt = d128!(0);
|
|
||||||
|
|
||||||
let flow_or_outgoing_exchange_movements = txn.get_outgoing_exchange_and_flow_mvmts(
|
|
||||||
settings,
|
|
||||||
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();
|
|
||||||
|
|
||||||
if let None = ticker { ticker = Some(raw_acct.ticker.clone()) };
|
|
||||||
|
|
||||||
if let None = polarity {
|
|
||||||
polarity = if mvmt.amount > d128!(0) {
|
|
||||||
Some(Polarity::Incoming)
|
|
||||||
} else { Some(Polarity::Outgoing)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let term = mvmt.get_term(acct_map, ars);
|
|
||||||
|
|
||||||
if term == Term::LT {
|
|
||||||
amount_lt += mvmt.amount;
|
|
||||||
proceeds_lt += mvmt.proceeds.get();
|
|
||||||
cost_basis_lt += mvmt.cost_basis.get();
|
|
||||||
match term_lt {
|
|
||||||
None => { term_lt = Some(term)}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assert_eq!(term, Term::ST);
|
|
||||||
amount_st += mvmt.amount;
|
|
||||||
proceeds_st += mvmt.proceeds.get();
|
|
||||||
cost_basis_st += mvmt.cost_basis.get();
|
|
||||||
if term_st == None {
|
|
||||||
term_st = Some(term);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (txn.transaction_type(ars, &raw_acct_map, &acct_map) == TxType::Flow) & (polarity == Some(Polarity::Incoming)) {
|
|
||||||
// println!("Incoming flow {}", txn.tx_number);
|
|
||||||
income_st = proceeds_st;
|
|
||||||
proceeds_st = d128!(0);
|
|
||||||
cost_basis_st = d128!(0);
|
|
||||||
income_lt = proceeds_lt;
|
|
||||||
proceeds_lt = d128!(0);
|
|
||||||
cost_basis_lt = d128!(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (txn.transaction_type(ars, &raw_acct_map, &acct_map) == TxType::Flow) & (polarity == Some(Polarity::Outgoing)) {
|
|
||||||
// println!("Outgoing flow {}, proceeds_st {}, proceeds_lt {}", txn.tx_number, proceeds_st, proceeds_lt);
|
|
||||||
expense_st -= proceeds_st;
|
|
||||||
expense_lt -= proceeds_lt;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(term) = term_st {
|
|
||||||
|
|
||||||
let mut row: Vec<String> = [].to_vec();
|
|
||||||
|
|
||||||
row.push(txn_date_string.clone());
|
|
||||||
row.push(tx_num_string.clone());
|
|
||||||
row.push(tx_type_string.clone());
|
|
||||||
row.push(tx_memo_string.clone());
|
|
||||||
row.push(amount_st.to_string());
|
|
||||||
row.push(ticker.clone().unwrap());
|
|
||||||
row.push(term.abbr_string());
|
|
||||||
row.push(proceeds_st.to_string());
|
|
||||||
row.push(cost_basis_st.to_string());
|
|
||||||
row.push((proceeds_st + cost_basis_st).to_string());
|
|
||||||
row.push(income_st.to_string());
|
|
||||||
row.push(expense_st.to_string());
|
|
||||||
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
if let Some(term) = term_lt {
|
|
||||||
|
|
||||||
let mut row: Vec<String> = [].to_vec();
|
|
||||||
|
|
||||||
row.push(txn_date_string);
|
|
||||||
row.push(tx_num_string);
|
|
||||||
row.push(tx_type_string);
|
|
||||||
row.push(tx_memo_string);
|
|
||||||
row.push(amount_lt.to_string());
|
|
||||||
row.push(ticker.unwrap());
|
|
||||||
row.push(term.abbr_string());
|
|
||||||
row.push(proceeds_lt.to_string());
|
|
||||||
row.push(cost_basis_lt.to_string());
|
|
||||||
row.push((proceeds_lt + cost_basis_lt).to_string());
|
|
||||||
row.push(income_lt.to_string());
|
|
||||||
row.push(expense_lt.to_string());
|
|
||||||
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let file_name = PathBuf::from("5_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");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn accounts_to_csv(
|
|
||||||
accounts: &[Rc<Account>],
|
|
||||||
ars: &HashMap<u32, ActionRecord>,
|
|
||||||
raw_acct_map: &HashMap<u16, RawAccount>,
|
|
||||||
acct_map: &HashMap<u16, Account>,
|
|
||||||
txns_map: &HashMap<u32, Transaction>,
|
|
||||||
) {
|
|
||||||
|
|
||||||
let mut rows: Vec<Vec<String>> = [].to_vec();
|
|
||||||
let mut header: Vec<String> = [].to_vec();
|
|
||||||
|
|
||||||
header.extend_from_slice(&[
|
|
||||||
"#".to_string(),
|
|
||||||
"Account".to_string(),
|
|
||||||
"Ticker".to_string(),
|
|
||||||
"Margin".to_string(),
|
|
||||||
"Date".to_string(),
|
|
||||||
"Txn#".to_string(),
|
|
||||||
"Type".to_string(),
|
|
||||||
"Memo".to_string(),
|
|
||||||
"Amount".to_string(),
|
|
||||||
"Proceeds".to_string(),
|
|
||||||
"Cost basis\n".to_string(),
|
|
||||||
"Gain/loss".to_string(),
|
|
||||||
"Term".to_string(),
|
|
||||||
"Income".to_string(),
|
|
||||||
"Expense".to_string(),
|
|
||||||
]);
|
|
||||||
rows.push(header);
|
|
||||||
|
|
||||||
for acct in accounts {
|
|
||||||
for lot in acct.list_of_lots.borrow().iter() {
|
|
||||||
for mvmt in lot.movements.borrow().iter() {
|
|
||||||
|
|
||||||
let raw_acct = raw_acct_map.get(&acct.raw_key).unwrap();
|
|
||||||
let txn = txns_map.get(&mvmt.transaction_key).unwrap();
|
|
||||||
let mut row: Vec<String> = [].to_vec();
|
|
||||||
|
|
||||||
row.push(raw_acct.account_num.to_string());
|
|
||||||
row.push(raw_acct.name.to_string());
|
|
||||||
row.push(raw_acct.ticker.to_string());
|
|
||||||
row.push(raw_acct.is_margin.to_string());
|
|
||||||
row.push(mvmt.date.format("%Y/%m/%d").to_string());
|
|
||||||
row.push(txn.tx_number.to_string());
|
|
||||||
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.cost_basis.get().to_string());
|
|
||||||
row.push(mvmt.get_gain_or_loss().to_string());
|
|
||||||
row.push(mvmt.get_term(acct_map, ars).to_string());
|
|
||||||
row.push(mvmt.get_income(ars, &raw_acct_map, &acct_map, &txns_map).to_string());
|
|
||||||
row.push(mvmt.get_expense(ars, &raw_acct_map, &acct_map, &txns_map).to_string());
|
|
||||||
|
|
||||||
rows.push(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let buffer = File::create("/Users/scoob/Documents/Testing/rust_exports/test/accts-1st-try.csv").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");
|
|
||||||
}
|
|
|
@ -9,7 +9,7 @@ use decimal::d128;
|
||||||
use crate::transaction::{Transaction, TxType, ActionRecord, Polarity};
|
use crate::transaction::{Transaction, TxType, ActionRecord, Polarity};
|
||||||
use crate::account::{Account, RawAccount};
|
use crate::account::{Account, RawAccount};
|
||||||
use crate::utils::{round_d128_1e2};
|
use crate::utils::{round_d128_1e2};
|
||||||
use crate::user_choices::{LotProcessingChoices};
|
use crate::cli_user_choices::{LotProcessingChoices};
|
||||||
|
|
||||||
pub fn add_cost_basis_to_movements(
|
pub fn add_cost_basis_to_movements(
|
||||||
settings: &LotProcessingChoices,
|
settings: &LotProcessingChoices,
|
||||||
|
|
20
src/main.rs
20
src/main.rs
|
@ -21,12 +21,12 @@ mod core_functions;
|
||||||
mod import_accts_txns;
|
mod import_accts_txns;
|
||||||
mod create_lots_mvmts;
|
mod create_lots_mvmts;
|
||||||
mod import_cost_proceeds_etc;
|
mod import_cost_proceeds_etc;
|
||||||
mod user_choices;
|
mod cli_user_choices;
|
||||||
mod export;
|
mod csv_export;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use crate::user_choices::LotProcessingChoices;
|
use crate::cli_user_choices::LotProcessingChoices;
|
||||||
|
|
||||||
|
|
||||||
#[derive(StructOpt, Debug)]
|
#[derive(StructOpt, Debug)]
|
||||||
|
@ -129,7 +129,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if let Some(file) = args.file_to_import {
|
if let Some(file) = args.file_to_import {
|
||||||
input_file_path = file
|
input_file_path = file
|
||||||
} else {
|
} else {
|
||||||
input_file_path = user_choices::choose_file_for_import();
|
input_file_path = cli_user_choices::choose_file_for_import();
|
||||||
}
|
}
|
||||||
|
|
||||||
costing_method_choice = LotProcessingChoices::choose_inventory_costing_method();
|
costing_method_choice = LotProcessingChoices::choose_inventory_costing_method();
|
||||||
|
@ -193,7 +193,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
"y" | "ye" | "yes" | "" => { println!("Creating reports now."); Ok(true) },
|
"y" | "ye" | "yes" | "" => { println!("Creating reports now."); Ok(true) },
|
||||||
"n" | "no" => { println!("Okay, no reports were created."); Ok(false) },
|
"n" | "no" => { println!("Okay, no reports were created."); Ok(false) },
|
||||||
"c" | "change" => {
|
"c" | "change" => {
|
||||||
let new_dir = user_choices::choose_export_dir();
|
let new_dir = cli_user_choices::choose_export_dir();
|
||||||
settings.export_path = PathBuf::from(new_dir);
|
settings.export_path = PathBuf::from(new_dir);
|
||||||
println!("Creating reports now in newly chosen path.");
|
println!("Creating reports now in newly chosen path.");
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
@ -261,19 +261,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
if should_export {
|
if should_export {
|
||||||
|
|
||||||
export::_1_account_sums_to_csv(
|
csv_export::_1_account_sums_to_csv(
|
||||||
&settings,
|
&settings,
|
||||||
&raw_acct_map,
|
&raw_acct_map,
|
||||||
&account_map
|
&account_map
|
||||||
);
|
);
|
||||||
|
|
||||||
export::_2_account_sums_nonzero_to_csv(
|
csv_export::_2_account_sums_nonzero_to_csv(
|
||||||
&account_map,
|
&account_map,
|
||||||
&settings,
|
&settings,
|
||||||
&raw_acct_map
|
&raw_acct_map
|
||||||
);
|
);
|
||||||
|
|
||||||
export::_5_transaction_mvmt_summaries_to_csv(
|
csv_export::_5_transaction_mvmt_summaries_to_csv(
|
||||||
&settings,
|
&settings,
|
||||||
&action_records_map,
|
&action_records_map,
|
||||||
&raw_acct_map,
|
&raw_acct_map,
|
||||||
|
@ -292,9 +292,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
// // export::transactions_to_csv(&transactions);
|
// // csv_export::transactions_to_csv(&transactions);
|
||||||
// // println!("\nReturned from `fn transactions_to_csv`. It worked!! Right?");
|
// // println!("\nReturned from `fn transactions_to_csv`. It worked!! Right?");
|
||||||
|
|
||||||
// export::accounts_to_csv(&accounts);
|
// csv_export::accounts_to_csv(&accounts);
|
||||||
// println!("\nReturned from `fn accounts_to_csv`. It worked!! Right?");
|
// println!("\nReturned from `fn accounts_to_csv`. It worked!! Right?");
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use decimal::d128;
|
||||||
use chrono::NaiveDate;
|
use chrono::NaiveDate;
|
||||||
use serde_derive::{Serialize, Deserialize};
|
use serde_derive::{Serialize, Deserialize};
|
||||||
|
|
||||||
use crate::user_choices::LotProcessingChoices;
|
use crate::cli_user_choices::LotProcessingChoices;
|
||||||
use crate::account::{Account, Movement, RawAccount};
|
use crate::account::{Account, Movement, RawAccount};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -1,292 +0,0 @@
|
||||||
// Copyright (c) 2017-2019, scoobybejesus
|
|
||||||
// Redistributions must include the license: https://github.com/scoobybejesus/cryptools-rs/LEGAL.txt
|
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
use std::io::{self, BufRead};
|
|
||||||
use std::process;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use chrono::NaiveDate;
|
|
||||||
use rustyline::completion::{Completer, FilenameCompleter, Pair};
|
|
||||||
use rustyline::{CompletionType, Config, Context, EditMode, Editor, Helper};
|
|
||||||
use rustyline::config::OutputStreamType;
|
|
||||||
use rustyline::hint::{Hinter};
|
|
||||||
use rustyline::error::ReadlineError;
|
|
||||||
use rustyline::highlight::{Highlighter};
|
|
||||||
use structopt::StructOpt;
|
|
||||||
|
|
||||||
use crate::utils;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn choose_file_for_import() -> PathBuf {
|
|
||||||
|
|
||||||
println!("Please input a file (absolute or relative path) to import: ");
|
|
||||||
|
|
||||||
// PathBuf::from("/Users/scoob/Documents/Repos/cryptools-rs/private/RawTxForImport-pycleaned.csv")
|
|
||||||
|
|
||||||
let file_str = _get_path();
|
|
||||||
PathBuf::from(file_str.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn choose_export_dir() -> PathBuf {
|
|
||||||
|
|
||||||
println!("Please input a file path for exports: ");
|
|
||||||
|
|
||||||
// PathBuf::from("/Users/scoob/Documents/Testing/rust_exports/")
|
|
||||||
|
|
||||||
let file_str = _get_path();
|
|
||||||
PathBuf::from(file_str.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _get_path() -> Result<(String), Box<Error>> {
|
|
||||||
|
|
||||||
struct MyHelper {
|
|
||||||
completer: FilenameCompleter,
|
|
||||||
colored_prompt: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Completer for MyHelper {
|
|
||||||
|
|
||||||
type Candidate = Pair;
|
|
||||||
|
|
||||||
fn complete(
|
|
||||||
&self,
|
|
||||||
line: &str,
|
|
||||||
pos: usize,
|
|
||||||
ctx: &Context<'_>,
|
|
||||||
) -> Result<(usize, Vec<Pair>), ReadlineError> {
|
|
||||||
self.completer.complete(line, pos, ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Hinter for MyHelper {}
|
|
||||||
impl Highlighter for MyHelper {}
|
|
||||||
impl Helper for MyHelper {}
|
|
||||||
|
|
||||||
let h = MyHelper {
|
|
||||||
completer: FilenameCompleter::new(),
|
|
||||||
colored_prompt: "".to_owned(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let config = Config::builder()
|
|
||||||
.history_ignore_space(true)
|
|
||||||
.completion_type(CompletionType::Circular)
|
|
||||||
.edit_mode(EditMode::Vi)
|
|
||||||
.output_stream(OutputStreamType::Stdout)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let count = 1;
|
|
||||||
let mut rl = Editor::with_config(config);
|
|
||||||
let p = format!("{}> ", count);
|
|
||||||
rl.set_helper(Some(h));
|
|
||||||
rl.helper_mut().unwrap().colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
|
|
||||||
let readline = rl.readline(">> ");
|
|
||||||
|
|
||||||
match readline {
|
|
||||||
Ok(line) => {
|
|
||||||
rl.add_history_entry(line.as_str());
|
|
||||||
println!("");
|
|
||||||
Ok(line)
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
println!("Error during Rustyline: {:?}", err);
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, StructOpt)]
|
|
||||||
pub enum InventoryCostingMethod {
|
|
||||||
/// 1. LIFO according to the order the lot was created.
|
|
||||||
LIFObyLotCreationDate,
|
|
||||||
/// 2. LIFO according to the basis date of the lot.
|
|
||||||
LIFObyLotBasisDate,
|
|
||||||
/// 3. FIFO according to the order the lot was created.
|
|
||||||
FIFObyLotCreationDate,
|
|
||||||
/// 4. FIFO according to the basis date of the lot.
|
|
||||||
FIFObyLotBasisDate,
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl std::convert::From<OsStr> for InventoryCostingMethod {
|
|
||||||
// fn from(osstr: OsStr) -> InventoryCostingMethod {
|
|
||||||
// let osstring1 = OsString::from(Box::<osstr>);
|
|
||||||
// let new_string = osstr.into_string().expect("Invalid input. Could not convert to string.");
|
|
||||||
// let method = match new_string.trim() {
|
|
||||||
// "1" => InventoryCostingMethod::LIFObyLotCreationDate,
|
|
||||||
// "2" => InventoryCostingMethod::LIFObyLotBasisDate,
|
|
||||||
// "3" => InventoryCostingMethod::FIFObyLotCreationDate,
|
|
||||||
// "4" => InventoryCostingMethod::FIFObyLotBasisDate,
|
|
||||||
// _ => { println!("Invalid choice. Could not convert."); process::exit(1)
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// method
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub struct LotProcessingChoices {
|
|
||||||
pub export_path: PathBuf,
|
|
||||||
pub home_currency: String,
|
|
||||||
pub enable_like_kind_treatment: bool,
|
|
||||||
pub costing_method: InventoryCostingMethod,
|
|
||||||
pub lk_cutoff_date_string: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LotProcessingChoices {
|
|
||||||
|
|
||||||
pub fn choose_inventory_costing_method() -> InventoryCostingMethod {
|
|
||||||
|
|
||||||
println!("Choose the lot inventory costing method. [Default: 1]");
|
|
||||||
println!("1. LIFO according to the order the lot was created.");
|
|
||||||
println!("2. LIFO according to the basis date of the lot.");
|
|
||||||
println!("3. FIFO according to the order the lot was created.");
|
|
||||||
println!("4. FIFO according to the basis date of the lot.");
|
|
||||||
|
|
||||||
let method = match _costing_method() {
|
|
||||||
Ok(x) => {x},
|
|
||||||
Err(err) => { process::exit(1) }
|
|
||||||
};
|
|
||||||
|
|
||||||
fn _costing_method() -> Result<(InventoryCostingMethod), Box<Error>> {
|
|
||||||
|
|
||||||
let mut input = String::new();
|
|
||||||
let stdin = io::stdin();
|
|
||||||
stdin.lock().read_line(&mut input).expect("Failed to read stdin");
|
|
||||||
|
|
||||||
match input.trim() { // Without .trim(), there's a hidden \n or something preventing the match
|
|
||||||
"1" | "" => Ok(InventoryCostingMethod::LIFObyLotCreationDate),
|
|
||||||
"2" => Ok(InventoryCostingMethod::LIFObyLotBasisDate),
|
|
||||||
"3" => Ok(InventoryCostingMethod::FIFObyLotCreationDate),
|
|
||||||
"4" => Ok(InventoryCostingMethod::FIFObyLotBasisDate),
|
|
||||||
_ => { println!("Invalid choice. Please enter a valid number."); _costing_method() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
method
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn inv_costing_from_cmd_arg(arg: String) -> InventoryCostingMethod {
|
|
||||||
|
|
||||||
let method = match _costing_method(arg) {
|
|
||||||
Ok(x) => {x},
|
|
||||||
Err(err) => { process::exit(1) }
|
|
||||||
};
|
|
||||||
|
|
||||||
fn _costing_method(input: String) -> Result<(InventoryCostingMethod), Box<Error>> {
|
|
||||||
|
|
||||||
match input.trim() { // Without .trim(), there's a hidden \n or something preventing the match
|
|
||||||
"1" => Ok(InventoryCostingMethod::LIFObyLotCreationDate),
|
|
||||||
"2" => Ok(InventoryCostingMethod::LIFObyLotBasisDate),
|
|
||||||
"3" => Ok(InventoryCostingMethod::FIFObyLotCreationDate),
|
|
||||||
"4" => Ok(InventoryCostingMethod::FIFObyLotBasisDate),
|
|
||||||
_ => { println!("Invalid choice. Please enter a valid number."); _costing_method(input) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
method
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn elect_like_kind_treatment(cutoff_date_arg: &Option<String>) -> (bool, String) {
|
|
||||||
|
|
||||||
let election: bool;
|
|
||||||
let date: String;
|
|
||||||
|
|
||||||
if cutoff_date_arg.is_some() {
|
|
||||||
|
|
||||||
let provided_date = NaiveDate::parse_from_str(&cutoff_date_arg.clone().unwrap(), "%y-%m-%d")
|
|
||||||
.unwrap_or(NaiveDate::parse_from_str(&cutoff_date_arg.clone().unwrap(), "%Y-%m-%d")
|
|
||||||
.expect("Date entered as -c command line arg has an incorrect format."));
|
|
||||||
|
|
||||||
println!("\nUse like-kind exchange treatment through {}? [Y/n/c] ('c' to 'change') ", provided_date);
|
|
||||||
|
|
||||||
let (election, date) = match _elect_like_kind_arg(&cutoff_date_arg, provided_date) {
|
|
||||||
Ok(x) => {x},
|
|
||||||
Err(err) => { println!("Fatal error in fn elect_like_kind_treatment()."); process::exit(1) }
|
|
||||||
};
|
|
||||||
|
|
||||||
fn _elect_like_kind_arg(cutoff_date_arg: &Option<String>, provided_date: NaiveDate) -> Result<(bool, String), Box<Error>> {
|
|
||||||
|
|
||||||
let mut input = String::new();
|
|
||||||
let stdin = io::stdin();
|
|
||||||
stdin.lock().read_line(&mut input)?;
|
|
||||||
|
|
||||||
|
|
||||||
match input.trim().to_ascii_lowercase().as_str() {
|
|
||||||
"y" | "ye" | "yes" | "" => {
|
|
||||||
println!(" Using like-kind treatment through {}.\n", provided_date);
|
|
||||||
Ok( (true, cutoff_date_arg.clone().unwrap()) )
|
|
||||||
},
|
|
||||||
"n" | "no" => { println!(" Proceeding without like-kind treatment.\n");
|
|
||||||
Ok( (false, "1-1-1".to_string()) )
|
|
||||||
},
|
|
||||||
"c" | "change" => {
|
|
||||||
println!("Please enter your desired like-kind exchange treatment cutoff date.");
|
|
||||||
println!(" You must use the format %y-%m-%d (e.g., 2017-12-31, 17-12-31, and 9-6-1 are all acceptable).\n");
|
|
||||||
let mut input = String::new();
|
|
||||||
let stdin = io::stdin();
|
|
||||||
stdin.lock().read_line(&mut input)?;
|
|
||||||
utils::trim_newline(&mut input);
|
|
||||||
let newly_chosen_date = NaiveDate::parse_from_str(&input, "%y-%m-%d")
|
|
||||||
.unwrap_or(NaiveDate::parse_from_str(&input, "%Y-%m-%d")
|
|
||||||
.expect("Date entered has an incorrect format. Program must abort."));
|
|
||||||
// TODO: figure out how to make this fail gracefully and let the user input the date again
|
|
||||||
println!(" Using like-kind treatment through {}.\n", newly_chosen_date);
|
|
||||||
Ok( (true, input) )
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
println!("Please respond with 'y', 'n', or 'c' (or 'yes' or 'no' or 'change').");
|
|
||||||
_elect_like_kind_arg(&cutoff_date_arg, provided_date)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (election, date)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
println!("\nContinue without like-kind exchange treatment? [Y/n] ");
|
|
||||||
|
|
||||||
let (election, date) = match _no_elect_like_kind_arg() {
|
|
||||||
Ok(x) => {x},
|
|
||||||
Err(err) => { println!("Fatal error in like_kind selection. Perhaps incorrect date format."); process::exit(1) }
|
|
||||||
};
|
|
||||||
|
|
||||||
fn _no_elect_like_kind_arg() -> Result<(bool, String), Box<Error>> {
|
|
||||||
|
|
||||||
let mut input = String::new();
|
|
||||||
let stdin = io::stdin();
|
|
||||||
stdin.lock().read_line(&mut input)?;
|
|
||||||
|
|
||||||
match input.trim().to_ascii_lowercase().as_str() {
|
|
||||||
"y" | "ye" | "yes" | "" => { println!(" Proceeding without like-kind treatment.\n");
|
|
||||||
Ok( (false, "1-1-1".to_string()) )
|
|
||||||
},
|
|
||||||
"n" | "no" => {
|
|
||||||
println!("Please enter your desired like-kind exchange treatment cutoff date.");
|
|
||||||
println!(" You must use the format %y-%m-%d (e.g., 2017-12-31, 17-12-31, and 9-6-1 are all acceptable).\n");
|
|
||||||
let mut input = String::new();
|
|
||||||
let stdin = io::stdin();
|
|
||||||
stdin.lock().read_line(&mut input)?;
|
|
||||||
utils::trim_newline(&mut input);
|
|
||||||
|
|
||||||
let newly_chosen_date = NaiveDate::parse_from_str(&input, "%y-%m-%d")
|
|
||||||
.unwrap_or(NaiveDate::parse_from_str(&input, "%Y-%m-%d")
|
|
||||||
.expect("Date entered has an incorrect format. Program must abort."));
|
|
||||||
// TODO: figure out how to make this fail gracefully and let the user input the date again
|
|
||||||
println!(" Using like-kind treatment through {}.\n", newly_chosen_date);
|
|
||||||
|
|
||||||
Ok( (true, input) )
|
|
||||||
},
|
|
||||||
_ => { println!("Please respond with 'y' or 'n' (or 'yes' or 'no')."); _no_elect_like_kind_arg() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (election, date)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LikeKindSettings {
|
|
||||||
pub like_kind_cutoff_date: NaiveDate,
|
|
||||||
pub like_kind_basis_date_preserved: bool,
|
|
||||||
}
|
|
Loading…
Reference in New Issue