Fix tracking of RG with incomplete opening BC
This commit is contained in:
parent
85477bd8a0
commit
fae69cd353
|
@ -1,9 +1,9 @@
|
|||
use crate::{CommoditiesPriceOracle};
|
||||
use crate::books::TransactionReference;
|
||||
use crate::CommoditiesPriceOracle;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Neg;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use crate::books::TransactionReference;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AssetCommodity {
|
||||
|
@ -22,12 +22,19 @@ impl AssetCommodity {
|
|||
pub struct AssetAccount {
|
||||
name: String,
|
||||
commodities: HashMap<String, Vec<AssetCommodity>>,
|
||||
running_total: HashMap<String, Decimal>,
|
||||
running_total: HashMap<String, Decimal>,
|
||||
// txid, date, commodity, quantity, cost,
|
||||
pub(crate) transactions: Vec<(TransactionReference, String, String, Decimal, Decimal, String)>,
|
||||
opening: HashMap<String, Decimal>,
|
||||
pub(crate) transactions: Vec<(
|
||||
TransactionReference,
|
||||
String,
|
||||
String,
|
||||
Decimal,
|
||||
Decimal,
|
||||
String,
|
||||
)>,
|
||||
opening: HashMap<String, Decimal>,
|
||||
pub(crate) realized_gains: Decimal,
|
||||
opening_date: String
|
||||
opening_date: String,
|
||||
}
|
||||
|
||||
impl AssetAccount {
|
||||
|
@ -39,7 +46,7 @@ impl AssetAccount {
|
|||
running_total: HashMap::new(),
|
||||
transactions: vec![],
|
||||
opening: HashMap::new(),
|
||||
opening_date: opening_date.clone()
|
||||
opening_date: opening_date.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,13 +57,17 @@ impl AssetAccount {
|
|||
println!("[UG] Calculating Unrealized Gains for {}", self.name);
|
||||
let mut unrealized_gain = Decimal::new(0, 0);
|
||||
|
||||
let historical_cost = commodities.iter().fold(Decimal::new(0,0), |accum, x| {
|
||||
println!("[UG] Historial {} {}", x.cost_basis , x.quantity);
|
||||
accum + (x.cost_basis * x.quantity)
|
||||
let historical_cost = commodities.iter().fold(Decimal::new(0, 0), |accum, x| {
|
||||
println!("[UG] Historial {} {}", x.cost_basis, x.quantity);
|
||||
accum + (x.cost_basis * x.quantity)
|
||||
});
|
||||
|
||||
let current_cost = commodities.iter().fold(Decimal::new(0,0), |accum, x| {
|
||||
println!("[UG] Current {} {}", oracle.lookup(&x.name, date) , x.quantity);
|
||||
let current_cost = commodities.iter().fold(Decimal::new(0, 0), |accum, x| {
|
||||
println!(
|
||||
"[UG] Current {} {}",
|
||||
oracle.lookup(&x.name, date),
|
||||
x.quantity
|
||||
);
|
||||
accum + (oracle.lookup(&x.name, date) * x.quantity)
|
||||
});
|
||||
|
||||
|
@ -70,18 +81,20 @@ impl AssetAccount {
|
|||
pub fn total_nominal(&self, oracle: &CommoditiesPriceOracle, date: &String) -> f64 {
|
||||
let mut total = 0.0;
|
||||
for (commodity_name, quantity) in self.running_total.iter() {
|
||||
let mut value = if commodity_name != "CAD" && commodity_name != "$" && commodity_name != "StickerSheets" {
|
||||
(oracle.lookup(commodity_name, date) * quantity.abs()).to_f64().unwrap()
|
||||
} else if commodity_name == "CAD" || commodity_name == "$" {
|
||||
quantity.to_f64().unwrap()
|
||||
let mut value = if commodity_name != "CAD" && commodity_name != "$" && commodity_name != "StickerSheets" {
|
||||
(oracle.lookup(commodity_name, date) * quantity.abs())
|
||||
.to_f64()
|
||||
.unwrap()
|
||||
} else if commodity_name == "CAD" || commodity_name == "$" {
|
||||
quantity.to_f64().unwrap()
|
||||
} else {
|
||||
0.0
|
||||
0.0
|
||||
};
|
||||
if self.name.starts_with("Liability") {
|
||||
// flip sign
|
||||
value = value.neg()
|
||||
}
|
||||
|
||||
|
||||
total += value;
|
||||
}
|
||||
total
|
||||
|
@ -90,26 +103,27 @@ impl AssetAccount {
|
|||
pub fn opening_nominal(&self, oracle: &CommoditiesPriceOracle, date: &String) -> f64 {
|
||||
let mut total = 0.0;
|
||||
for (commodity_name, quantity) in self.opening.iter() {
|
||||
let mut value = if commodity_name != "CAD" && commodity_name!= "$" && commodity_name != "StickerSheets" {
|
||||
(oracle.lookup(commodity_name, date) * quantity.abs()).to_f64().unwrap()
|
||||
let mut value = if commodity_name != "CAD" && commodity_name != "$" && commodity_name != "StickerSheets" {
|
||||
(oracle.lookup(commodity_name, date) * quantity.abs())
|
||||
.to_f64()
|
||||
.unwrap()
|
||||
} else if commodity_name == "CAD" || commodity_name == "$" {
|
||||
quantity.to_f64().unwrap()
|
||||
quantity.to_f64().unwrap()
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
|
||||
if self.name.starts_with("Liability") {
|
||||
// flip sign
|
||||
value = value.neg()
|
||||
}
|
||||
|
||||
|
||||
total += value;
|
||||
}
|
||||
total
|
||||
}
|
||||
|
||||
|
||||
pub fn trade(&mut self, txid: TransactionReference, name: String, cost: Decimal, quantity: Decimal, date: &String, oracle: &mut CommoditiesPriceOracle) -> Vec<(String, Decimal, String, Decimal)>{
|
||||
pub fn trade(&mut self, txid: TransactionReference, name: String, cost: Decimal, quantity: Decimal, date: &String, oracle: &mut CommoditiesPriceOracle) -> Vec<(String, Decimal, String, Decimal)> {
|
||||
let cost_basis = cost;
|
||||
let asset_commodity = AssetCommodity {
|
||||
cost_basis,
|
||||
|
@ -120,55 +134,69 @@ impl AssetAccount {
|
|||
match self.commodities.contains_key(&name) {
|
||||
false => {
|
||||
self.commodities.insert(name.clone(), vec![]);
|
||||
self.running_total.insert(name.clone(), Decimal::new(0,0));
|
||||
if *date == self.opening_date {
|
||||
println!("Opening Total: {} {} {}", quantity, name, cost);
|
||||
self.opening.insert(name.clone(), quantity);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.running_total.insert(name.clone(), Decimal::new(0, 0));
|
||||
if *date == self.opening_date {
|
||||
println!("Opening Total: {} {} {}", quantity, name, cost);
|
||||
self.opening.insert(name.clone(), quantity);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
*self.running_total.get_mut(&name).unwrap() += quantity;
|
||||
println!("Running Total: {} {} {} {}", quantity, name, cost, *self.running_total.get_mut(&name).unwrap() );
|
||||
println!(
|
||||
"Running Total: {} {} {} {}",
|
||||
quantity,
|
||||
name,
|
||||
cost,
|
||||
*self.running_total.get_mut(&name).unwrap()
|
||||
);
|
||||
|
||||
if self.name.starts_with("Assets") == false {
|
||||
self.transactions.push((txid, date.clone(), name.clone(), cost, quantity, self.name.clone()));
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
name.clone(),
|
||||
cost,
|
||||
quantity,
|
||||
self.name.clone(),
|
||||
));
|
||||
let mut meta_transactions = vec![];
|
||||
meta_transactions.push((self.name.clone(), quantity, name, cost*quantity));
|
||||
meta_transactions.push((self.name.clone(), quantity, name, cost * quantity));
|
||||
// we don't track cost-basis for liabilities
|
||||
return meta_transactions;
|
||||
}
|
||||
|
||||
match self.commodities.get_mut(&name) {
|
||||
None => {
|
||||
panic!();
|
||||
panic!();
|
||||
}
|
||||
Some(current_commodities) => {
|
||||
|
||||
current_commodities.sort_by(|a,b| {
|
||||
a.cost_basis.cmp(&b.cost_basis)
|
||||
});
|
||||
current_commodities.sort_by(|a, b| a.cost_basis.cmp(&b.cost_basis));
|
||||
current_commodities.reverse();
|
||||
|
||||
|
||||
let mut quantity_needed = quantity.abs();
|
||||
let mut base_cost = Decimal::new(0,0);
|
||||
let mut base_cost = Decimal::new(0, 0);
|
||||
if name != "$" {
|
||||
for commodity in current_commodities.iter_mut() {
|
||||
if commodity.quantity.is_zero() {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
if commodity.quantity <= quantity_needed {
|
||||
// use all of this commodity....
|
||||
println!("Taking {} {} @@ {}", commodity.quantity, commodity.name, commodity.cost_basis);
|
||||
println!(
|
||||
"[REALIZED GAIN CALC] Taking {} {} @@ {}",
|
||||
commodity.quantity, commodity.name, commodity.cost_basis
|
||||
);
|
||||
base_cost += commodity.quantity * commodity.cost_basis;
|
||||
quantity_needed -= commodity.quantity;
|
||||
commodity.quantity = Decimal::new(0, 0);
|
||||
} else {
|
||||
// only use what we need from this...and then we are done...
|
||||
println!("Taking {} {} @@ {}", quantity_needed, commodity.name, commodity.cost_basis);
|
||||
println!(
|
||||
"Taking {} {} @@ {}",
|
||||
quantity_needed, commodity.name, commodity.cost_basis
|
||||
);
|
||||
base_cost += quantity_needed * commodity.cost_basis;
|
||||
commodity.quantity -= quantity_needed;
|
||||
break;
|
||||
|
@ -179,65 +207,149 @@ impl AssetAccount {
|
|||
}
|
||||
let mut oracle_price = oracle.lookup(&name, date);
|
||||
let mut buying = false;
|
||||
let arbitrage_difference = if oracle_price == Decimal::new(1,0) {
|
||||
let arbitrage_difference = if oracle_price == Decimal::new(1, 0) {
|
||||
buying = true;
|
||||
println!("[WARNING] NO Price found for {} {} {}", name, date, cost_basis);
|
||||
// oracle.insert(name.clone(), date.clone(), cost_basis);
|
||||
Decimal::new(0,0)
|
||||
println!(
|
||||
"[WARNING] NO Price found for {} {} {}",
|
||||
name, date, cost_basis
|
||||
);
|
||||
// oracle.insert(name.clone(), date.clone(), cost_basis);
|
||||
Decimal::new(0, 0)
|
||||
} else {
|
||||
oracle_price - cost
|
||||
};
|
||||
|
||||
if (base_cost - (cost * quantity.abs())).is_zero() == false {
|
||||
println!("Realized Gains: {} {} {} {} {} {} {} {} {}", self.name, (cost * quantity.abs()) - base_cost, base_cost, cost * quantity.abs(), date, base_cost/quantity, cost, oracle_price, arbitrage_difference);
|
||||
println!(
|
||||
"[DEBUG] Realized Gains: {} {} {} {} {} {} {} {} {}",
|
||||
self.name,
|
||||
(cost * quantity.abs()) - base_cost,
|
||||
base_cost,
|
||||
cost * quantity.abs(),
|
||||
date,
|
||||
base_cost / quantity,
|
||||
cost,
|
||||
oracle_price,
|
||||
arbitrage_difference
|
||||
);
|
||||
}
|
||||
|
||||
let arbitrage_expense = (arbitrage_difference * quantity).neg();
|
||||
let mut actual_gain = ((cost * quantity.abs()) - base_cost) + arbitrage_expense;
|
||||
let mut actual_gain = ((cost * quantity.abs()) - base_cost) + arbitrage_expense;
|
||||
if buying {
|
||||
actual_gain = Decimal::new(0,0);
|
||||
actual_gain = Decimal::new(0, 0);
|
||||
}
|
||||
self.realized_gains += actual_gain;
|
||||
|
||||
println!("[DEBUG] {} {} oracle price: {} cost: {} quantity: {} ae: {} {} ", name, base_cost, oracle_price, cost, quantity, arbitrage_expense, actual_gain);
|
||||
println!(
|
||||
"[REALIZED GAIN CALC] Realized {} base: {} oracle price: {} cost: {} quantity: {} ae: {} {} ",
|
||||
name, base_cost, oracle_price, cost, quantity, arbitrage_expense, actual_gain
|
||||
);
|
||||
|
||||
self.transactions.push((txid, date.clone(), name.clone(), cost, quantity, self.name.clone()));
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
name.clone(),
|
||||
cost,
|
||||
quantity,
|
||||
self.name.clone(),
|
||||
));
|
||||
let mut meta_transactions = vec![];
|
||||
meta_transactions.push((self.name.clone(),cost*quantity, name.clone(), base_cost.neg() ));
|
||||
meta_transactions.push((
|
||||
self.name.clone(),
|
||||
cost * quantity,
|
||||
name.clone(),
|
||||
base_cost.neg(),
|
||||
));
|
||||
if actual_gain.is_zero() == false {
|
||||
if actual_gain.is_sign_positive() {
|
||||
self.transactions.push((txid, date.clone(), format!("$"), Decimal::new(1, 0), actual_gain.neg(), format!("Income:RealizedGains")));
|
||||
meta_transactions.push((format!("Income:RealizedGains"), actual_gain.neg(), format!("$"), actual_gain.neg() ))
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
format!("$"),
|
||||
Decimal::new(1, 0),
|
||||
actual_gain.neg(),
|
||||
format!("Income:RealizedGains"),
|
||||
));
|
||||
meta_transactions.push((
|
||||
format!("Income:RealizedGains"),
|
||||
actual_gain.neg(),
|
||||
format!("$"),
|
||||
actual_gain.neg(),
|
||||
))
|
||||
} else if actual_gain.is_sign_negative() {
|
||||
self.transactions.push((txid, date.clone(), format!("$"), Decimal::new(1, 0), actual_gain.neg(), format!("Expenses:RealizedLoss")));
|
||||
meta_transactions.push((format!("Expenses:RealizedLoss"), actual_gain.neg(), format!("$"), actual_gain.neg() ))
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
format!("$"),
|
||||
Decimal::new(1, 0),
|
||||
actual_gain.neg(),
|
||||
format!("Expenses:RealizedLoss"),
|
||||
));
|
||||
meta_transactions.push((
|
||||
format!("Expenses:RealizedLoss"),
|
||||
actual_gain.neg(),
|
||||
format!("$"),
|
||||
actual_gain.neg(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
if arbitrage_expense.is_zero() == false {
|
||||
if arbitrage_expense.is_sign_negative() {
|
||||
self.transactions.push((txid, date.clone(), format!("$"), Decimal::new(1, 0), arbitrage_expense, format!("Income:ExchangeArbitrage")));
|
||||
meta_transactions.push((format!("Income:ExchangeArbitrage"), arbitrage_expense, format!("$") ,arbitrage_expense))
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
format!("$"),
|
||||
Decimal::new(1, 0),
|
||||
arbitrage_expense,
|
||||
format!("Income:ExchangeArbitrage"),
|
||||
));
|
||||
meta_transactions.push((
|
||||
format!("Income:ExchangeArbitrage"),
|
||||
arbitrage_expense,
|
||||
format!("$"),
|
||||
arbitrage_expense,
|
||||
))
|
||||
} else if arbitrage_expense.is_sign_positive() {
|
||||
self.transactions.push((txid, date.clone(), format!("$"), Decimal::new(1, 0), arbitrage_expense, format!("Expenses:BankingFees:ExchangeArbitrage")));
|
||||
meta_transactions.push((format!("Expenses:BankingFees:ExchangeArbitrage"), arbitrage_expense, format!("$"),arbitrage_expense ))
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
format!("$"),
|
||||
Decimal::new(1, 0),
|
||||
arbitrage_expense,
|
||||
format!("Expenses:BankingFees:ExchangeArbitrage"),
|
||||
));
|
||||
meta_transactions.push((
|
||||
format!("Expenses:BankingFees:ExchangeArbitrage"),
|
||||
arbitrage_expense,
|
||||
format!("$"),
|
||||
arbitrage_expense,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return meta_transactions;
|
||||
} else {
|
||||
println!("[UG] DEBUG ASSET TRADE {} {} {}", cost, quantity, name);
|
||||
current_commodities.push(AssetCommodity{
|
||||
current_commodities.push(AssetCommodity {
|
||||
cost_basis: cost_basis * quantity,
|
||||
quantity: quantity,
|
||||
name: name.clone()
|
||||
name: name.clone(),
|
||||
});
|
||||
let mut meta_transactions = vec![];
|
||||
self.transactions.push((txid, date.clone(), name.clone(), cost, quantity, self.name.clone()));
|
||||
meta_transactions.push((self.name.clone(), quantity, name.clone(), cost*quantity));
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
name.clone(),
|
||||
cost,
|
||||
quantity,
|
||||
self.name.clone(),
|
||||
));
|
||||
meta_transactions.push((self.name.clone(), quantity, name.clone(), cost * quantity));
|
||||
return meta_transactions;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,18 +363,34 @@ impl AssetAccount {
|
|||
match self.commodities.get_mut(&name) {
|
||||
None => {
|
||||
self.commodities.insert(name.clone(), vec![asset_commodity]);
|
||||
self.running_total.insert(name.clone(), Decimal::new(0,0));
|
||||
self.running_total.insert(name.clone(), Decimal::new(0, 0));
|
||||
if *date == self.opening_date {
|
||||
println!("Opening: {} {} {}", quantity, name, cost);
|
||||
self.opening.insert(name.clone(), quantity);
|
||||
}
|
||||
println!(
|
||||
"[REALIZED GAIN CALC] Opening: {} {} {}",
|
||||
quantity, name, cost
|
||||
);
|
||||
self.opening.insert(name.clone(), quantity);
|
||||
}
|
||||
}
|
||||
Some(account) => account.push(asset_commodity),
|
||||
}
|
||||
|
||||
self.transactions.push((txid, date.clone(), name.clone(), cost, quantity, self.name.clone()));
|
||||
self.transactions.push((
|
||||
txid,
|
||||
date.clone(),
|
||||
name.clone(),
|
||||
cost,
|
||||
quantity,
|
||||
self.name.clone(),
|
||||
));
|
||||
|
||||
*self.running_total.get_mut(&name).unwrap() += quantity;
|
||||
println!("Running Total: {} {} {} {}", quantity, name, cost, *self.running_total.get_mut(&name).unwrap() );
|
||||
println!(
|
||||
"Running Total: {} {} {} {}",
|
||||
quantity,
|
||||
name,
|
||||
cost,
|
||||
*self.running_total.get_mut(&name).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
203
src/books.rs
203
src/books.rs
|
@ -1,11 +1,11 @@
|
|||
use crate::{AssetAccount, CommoditiesPriceOracle, ExternalAccount};
|
||||
use chrono::NaiveDate;
|
||||
use ledger_parser::{LedgerItem, Posting, PostingAmount, Transaction};
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::read_to_string;
|
||||
use std::str::FromStr;
|
||||
use chrono::NaiveDate;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
|
||||
pub type TransactionReference = usize;
|
||||
|
||||
|
@ -13,9 +13,13 @@ pub struct Books {
|
|||
pub(crate) asset_accounts: HashMap<String, AssetAccount>,
|
||||
pub(crate) external_accounts: HashMap<String, ExternalAccount>,
|
||||
pub(crate) commodities_oracle: CommoditiesPriceOracle,
|
||||
pub general_ledger: Vec<(TransactionReference, String, Vec<(String, String, Decimal, Decimal)>)>,
|
||||
pub general_ledger: Vec<(
|
||||
TransactionReference,
|
||||
String,
|
||||
Vec<(String, String, Decimal, Decimal)>,
|
||||
)>,
|
||||
opening_date: String,
|
||||
closing_date: String
|
||||
closing_date: String,
|
||||
}
|
||||
|
||||
impl Books {
|
||||
|
@ -26,7 +30,7 @@ impl Books {
|
|||
commodities_oracle: CommoditiesPriceOracle::new(),
|
||||
general_ledger: vec![],
|
||||
opening_date: opening_date.clone(),
|
||||
closing_date: closing_date.clone()
|
||||
closing_date: closing_date.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,7 +45,6 @@ impl Books {
|
|||
let mut total = Decimal::new(0, 0);
|
||||
let mut commodities = HashSet::new();
|
||||
|
||||
|
||||
let mut general_ledger_entry = vec![];
|
||||
if tx.date > NaiveDate::from_str(&to_date).unwrap() {
|
||||
break;
|
||||
|
@ -53,12 +56,10 @@ impl Books {
|
|||
if a.amount.commodity != b.amount.commodity {
|
||||
// handle trading
|
||||
self.handle_commodities_exchange(id, &tx);
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,7 +76,8 @@ impl Books {
|
|||
total += amount.amount.quantity;
|
||||
commodities.insert(amount.amount.commodity.name.clone());
|
||||
|
||||
self.update_account(0,
|
||||
self.update_account(
|
||||
0,
|
||||
&mut general_ledger_entry,
|
||||
id as TransactionReference,
|
||||
&posting.account,
|
||||
|
@ -88,14 +90,14 @@ impl Books {
|
|||
}
|
||||
match final_line {
|
||||
Some(posting) => {
|
||||
|
||||
if commodities.len() != 1 {
|
||||
panic!("Trades should be handled separately")
|
||||
}
|
||||
|
||||
println!("{},{},{},{}", id, tx.date, posting.account, total);
|
||||
let commodity: Vec<&String> = commodities.iter().collect();
|
||||
self.update_account(0,
|
||||
self.update_account(
|
||||
0,
|
||||
&mut general_ledger_entry,
|
||||
id as TransactionReference,
|
||||
&posting.account,
|
||||
|
@ -124,7 +126,16 @@ impl Books {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update_account(&mut self, depth: usize, general_ledger_entry: &mut Vec<(String, String, Decimal, Decimal)>, tx_ref: TransactionReference, account_name: &String, tx_date: &String, commodity_name: &String, commodity_quantity: Decimal) {
|
||||
pub fn update_account(
|
||||
&mut self,
|
||||
depth: usize,
|
||||
general_ledger_entry: &mut Vec<(String, String, Decimal, Decimal)>,
|
||||
tx_ref: TransactionReference,
|
||||
account_name: &String,
|
||||
tx_date: &String,
|
||||
commodity_name: &String,
|
||||
commodity_quantity: Decimal,
|
||||
) {
|
||||
if account_name.starts_with("Assets:") || account_name.starts_with("Liability:") {
|
||||
match self.asset_accounts.contains_key(account_name) {
|
||||
false => {
|
||||
|
@ -136,29 +147,54 @@ impl Books {
|
|||
_ => {}
|
||||
}
|
||||
let asset_account = self.asset_accounts.get_mut(account_name).unwrap();
|
||||
let cost_basis = self.commodities_oracle.lookup(commodity_name, tx_date);
|
||||
let cost_basis = self.commodities_oracle.lookup(commodity_name, tx_date);
|
||||
|
||||
if commodity_quantity.is_sign_positive() {
|
||||
asset_account.deposit( tx_ref, commodity_name.clone(), cost_basis, commodity_quantity, tx_date, Decimal::new(1,0));
|
||||
general_ledger_entry.push((account_name.clone(), commodity_name.clone(), commodity_quantity, commodity_quantity*cost_basis));
|
||||
} else {
|
||||
let extra_transactions = asset_account.trade(
|
||||
tx_ref,
|
||||
commodity_name.clone(),
|
||||
cost_basis,
|
||||
commodity_quantity,
|
||||
tx_date,
|
||||
&mut self.commodities_oracle
|
||||
);
|
||||
if commodity_quantity.is_sign_positive() {
|
||||
asset_account.deposit(
|
||||
tx_ref,
|
||||
commodity_name.clone(),
|
||||
cost_basis,
|
||||
commodity_quantity,
|
||||
tx_date,
|
||||
cost_basis,
|
||||
);
|
||||
general_ledger_entry.push((
|
||||
account_name.clone(),
|
||||
commodity_name.clone(),
|
||||
commodity_quantity,
|
||||
commodity_quantity * cost_basis,
|
||||
));
|
||||
} else {
|
||||
let extra_transactions = asset_account.trade(
|
||||
tx_ref,
|
||||
commodity_name.clone(),
|
||||
cost_basis,
|
||||
commodity_quantity,
|
||||
tx_date,
|
||||
&mut self.commodities_oracle,
|
||||
);
|
||||
|
||||
for (eaccount_name, commodity_quantity, commodity, nominal_value) in extra_transactions {
|
||||
if *account_name != eaccount_name && depth == 0 {
|
||||
self.update_account(1, general_ledger_entry, tx_ref, &eaccount_name.clone(), tx_date, &commodity, nominal_value)
|
||||
} else {
|
||||
general_ledger_entry.push((eaccount_name.clone(), commodity, commodity_quantity, nominal_value));
|
||||
}
|
||||
for (eaccount_name, commodity_quantity, commodity, nominal_value) in extra_transactions {
|
||||
if *account_name != eaccount_name && depth == 0 {
|
||||
self.update_account(
|
||||
1,
|
||||
general_ledger_entry,
|
||||
tx_ref,
|
||||
&eaccount_name.clone(),
|
||||
tx_date,
|
||||
&commodity,
|
||||
nominal_value,
|
||||
)
|
||||
} else {
|
||||
general_ledger_entry.push((
|
||||
eaccount_name.clone(),
|
||||
commodity,
|
||||
commodity_quantity,
|
||||
nominal_value,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This is an external account
|
||||
match self.external_accounts.contains_key(account_name) {
|
||||
|
@ -168,8 +204,7 @@ impl Books {
|
|||
} else if account_name.starts_with("Expenses") {
|
||||
ExternalAccount::new(account_name.clone(), false)
|
||||
} else if account_name.starts_with("Correction") {
|
||||
ExternalAccount::new(account_name.clone(), true)
|
||||
|
||||
ExternalAccount::new(account_name.clone(), true)
|
||||
} else {
|
||||
ExternalAccount::new(account_name.clone(), false)
|
||||
};
|
||||
|
@ -184,50 +219,104 @@ impl Books {
|
|||
let cost_basis = self.commodities_oracle.lookup(commodity_name, tx_date);
|
||||
if !external_account.income_account {
|
||||
external_account.deposit(tx_ref, commodity_name.clone(), commodity_quantity, tx_date);
|
||||
general_ledger_entry.push((account_name.clone(), commodity_name.clone(), commodity_quantity, cost_basis*commodity_quantity));
|
||||
general_ledger_entry.push((
|
||||
account_name.clone(),
|
||||
commodity_name.clone(),
|
||||
commodity_quantity,
|
||||
cost_basis * commodity_quantity,
|
||||
));
|
||||
} else {
|
||||
external_account.withdraw(tx_ref, commodity_name.clone(), commodity_quantity, tx_date);
|
||||
general_ledger_entry.push((account_name.clone(), commodity_name.clone(), commodity_quantity, cost_basis*commodity_quantity));
|
||||
general_ledger_entry.push((
|
||||
account_name.clone(),
|
||||
commodity_name.clone(),
|
||||
commodity_quantity,
|
||||
cost_basis * commodity_quantity,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_commodities_exchange(&mut self, id: usize, tx: &Transaction) {
|
||||
|
||||
let mut general_ledger_entry = vec![];
|
||||
let a = tx.postings[0].amount.as_ref().unwrap();
|
||||
let a = tx.postings[0].amount.as_ref().unwrap();
|
||||
let b = tx.postings[1].amount.as_ref().unwrap();
|
||||
let a_account = tx.postings[0].account.clone();
|
||||
let b_account = tx.postings[1].account.clone();
|
||||
if a.amount.quantity.is_sign_negative() {
|
||||
// Trading a for b
|
||||
let cost_basis = b.amount.quantity / a.amount.quantity.abs() ;
|
||||
let cost_basis = b.amount.quantity / a.amount.quantity.abs();
|
||||
|
||||
// Insert this price into the Oracle as the Market Price
|
||||
if (a.amount.commodity.name == "CAD") {
|
||||
let trade_basis = a.amount.quantity.abs() / b.amount.quantity;
|
||||
self.commodities_oracle.insert(b.amount.commodity.name.clone(), tx.date.to_string(), trade_basis);
|
||||
let trade_basis = a.amount.quantity.abs() / b.amount.quantity;
|
||||
self.commodities_oracle.insert(
|
||||
b.amount.commodity.name.clone(),
|
||||
tx.date.to_string(),
|
||||
trade_basis,
|
||||
);
|
||||
}
|
||||
|
||||
println!("NOMVAL: {} {} {} {} {} ", cost_basis, a.amount.quantity, a.amount.commodity.name, b.amount.quantity, b.amount.commodity.name);
|
||||
let extra_transactions = self.asset_accounts.get_mut(&a_account).unwrap().trade(id as TransactionReference,a.amount.commodity.name.clone(), cost_basis, a.amount.quantity, &tx.date.to_string(), &mut self.commodities_oracle);
|
||||
for (eaccount_name, quantity, commodity, nominal_value ) in extra_transactions {
|
||||
if eaccount_name == a_account {
|
||||
general_ledger_entry.push((eaccount_name.clone(), commodity.clone(), quantity, nominal_value));
|
||||
println!(
|
||||
"NOMVAL: {} {} {} {} {} ",
|
||||
cost_basis, a.amount.quantity, a.amount.commodity.name, b.amount.quantity, b.amount.commodity.name
|
||||
);
|
||||
let extra_transactions = self.asset_accounts.get_mut(&a_account).unwrap().trade(
|
||||
id as TransactionReference,
|
||||
a.amount.commodity.name.clone(),
|
||||
cost_basis,
|
||||
a.amount.quantity,
|
||||
&tx.date.to_string(),
|
||||
&mut self.commodities_oracle,
|
||||
);
|
||||
for (eaccount_name, quantity, commodity, nominal_value) in extra_transactions {
|
||||
if eaccount_name == a_account {
|
||||
general_ledger_entry.push((
|
||||
eaccount_name.clone(),
|
||||
commodity.clone(),
|
||||
quantity,
|
||||
nominal_value,
|
||||
));
|
||||
} else {
|
||||
self.update_account(1, &mut general_ledger_entry, id as TransactionReference, &eaccount_name, &tx.date.to_string(), &format!("$"), nominal_value)
|
||||
self.update_account(
|
||||
1,
|
||||
&mut general_ledger_entry,
|
||||
id as TransactionReference,
|
||||
&eaccount_name,
|
||||
&tx.date.to_string(),
|
||||
&format!("$"),
|
||||
nominal_value,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (a.amount.commodity.name == "CAD") {
|
||||
self.asset_accounts.get_mut(&b_account).unwrap().deposit(id as TransactionReference, b.amount.commodity.name.clone(), Decimal::new(1,0), b.amount.quantity, &tx.date.to_string(), a.amount.quantity.abs() / b.amount.quantity);
|
||||
} else {
|
||||
self.asset_accounts.get_mut(&b_account).unwrap().deposit(id as TransactionReference, b.amount.commodity.name.clone(), Decimal::new(1,0), b.amount.quantity, &tx.date.to_string(), Decimal::new(1,0));
|
||||
}
|
||||
general_ledger_entry.push((b_account, b.amount.commodity.name.clone(), b.amount.quantity, Decimal::new(1,0) * b.amount.quantity,));
|
||||
|
||||
if (a.amount.commodity.name == "CAD") {
|
||||
self.asset_accounts.get_mut(&b_account).unwrap().deposit(
|
||||
id as TransactionReference,
|
||||
b.amount.commodity.name.clone(),
|
||||
Decimal::new(1, 0),
|
||||
b.amount.quantity,
|
||||
&tx.date.to_string(),
|
||||
a.amount.quantity.abs() / b.amount.quantity,
|
||||
);
|
||||
} else {
|
||||
self.asset_accounts.get_mut(&b_account).unwrap().deposit(
|
||||
id as TransactionReference,
|
||||
b.amount.commodity.name.clone(),
|
||||
Decimal::new(1, 0),
|
||||
b.amount.quantity,
|
||||
&tx.date.to_string(),
|
||||
Decimal::new(1, 0),
|
||||
);
|
||||
}
|
||||
general_ledger_entry.push((
|
||||
b_account,
|
||||
b.amount.commodity.name.clone(),
|
||||
b.amount.quantity,
|
||||
Decimal::new(1, 0) * b.amount.quantity,
|
||||
));
|
||||
} else {
|
||||
panic!("Trade order should have selling first {} {}",a,b);
|
||||
panic!("Trade order should have selling first {} {}", a, b);
|
||||
}
|
||||
|
||||
self.general_ledger.push((
|
||||
|
@ -265,6 +354,6 @@ impl Books {
|
|||
"Total Realized + Unrealized Gains: {}",
|
||||
unrealized_gains + realized_gains
|
||||
);
|
||||
return (unrealized_gains).to_f64().unwrap()
|
||||
return (unrealized_gains).to_f64().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::books::TransactionReference;
|
||||
use rust_decimal::Decimal;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use crate::CommoditiesPriceOracle;
|
||||
use rust_decimal::prelude::ToPrimitive;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
// For tracking income and expenses...
|
||||
// All Transactions are one way.
|
||||
|
@ -51,9 +51,11 @@ impl ExternalAccount {
|
|||
let mut total = 0.0;
|
||||
for (txid, date, commodity, quantity) in self.transactions.iter() {
|
||||
if commodity != "CAD" && commodity != "$" && commodity != "StickerSheets" {
|
||||
total += (oracle.lookup(commodity, date) * quantity.abs()).to_f64().unwrap();
|
||||
} else if commodity == "CAD" || commodity == "$" {
|
||||
total += quantity.abs().to_f64().unwrap()
|
||||
total += (oracle.lookup(commodity, date) * quantity.abs())
|
||||
.to_f64()
|
||||
.unwrap();
|
||||
} else if commodity == "CAD" || commodity == "$" {
|
||||
total += quantity.abs().to_f64().unwrap()
|
||||
}
|
||||
}
|
||||
total
|
||||
|
|
|
@ -10,7 +10,6 @@ pub mod external_account;
|
|||
pub mod oracle;
|
||||
pub mod spreadsheet;
|
||||
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
/// Search for a pattern in a file and display the lines that contain it.
|
||||
|
@ -19,8 +18,7 @@ struct CommandLineParser {
|
|||
/// The pattern to look for
|
||||
book_file: String,
|
||||
start_date: String,
|
||||
stop_date: String
|
||||
|
||||
stop_date: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
@ -31,12 +31,12 @@ impl CommoditiesPriceOracle {
|
|||
match self.commodities.get(commodity) {
|
||||
None => {
|
||||
println!("no price history data for commodity: {}", commodity);
|
||||
return Decimal::new(1,0)
|
||||
return Decimal::new(1, 0);
|
||||
}
|
||||
Some(price_history) => match price_history.get(date) {
|
||||
None => {
|
||||
println!("[WARNING] no date history for {} on {}", commodity, date);
|
||||
return Decimal::new(0,0)
|
||||
return Decimal::new(0, 0);
|
||||
}
|
||||
Some(price) => price.clone(),
|
||||
},
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::str::FromStr;
|
||||
use chrono::{NaiveDate, NaiveTime};
|
||||
use rust_decimal::prelude::{ToPrimitive, Zero};
|
||||
use spreadsheet_ods::{CellStyle, format, Length, Sheet, Value, WorkBook};
|
||||
use crate::{AssetAccount, Books, ExternalAccount};
|
||||
use chrono::{NaiveDate, NaiveTime};
|
||||
use icu_locid::locale;
|
||||
use rust_decimal::prelude::{ToPrimitive, Zero};
|
||||
use rust_decimal::Decimal;
|
||||
use spreadsheet_ods::style::units::TextAlign;
|
||||
use spreadsheet_ods::Value::Currency;
|
||||
use spreadsheet_ods::{format, CellStyle, Length, Sheet, Value, WorkBook};
|
||||
use std::cmp::Ordering;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
||||
pub fn export_to_spreadsheet(name: &str, books: &Books, opening_date: &String, close_date: &String) {
|
||||
let mut wb =WorkBook::new(locale!("en_CA"));
|
||||
pub fn export_to_spreadsheet(name: &str, books: &Books, opening_date: &String, close_date: &String) {
|
||||
let mut wb = WorkBook::new(locale!("en_CA"));
|
||||
|
||||
let mut header_style = CellStyle::new_empty();
|
||||
header_style.set_font_bold();
|
||||
|
@ -31,103 +30,179 @@ pub fn export_to_spreadsheet(name: &str, books: &Books, opening_date: &String,
|
|||
currency_pretty_print.set_value_format(¤cy_style);
|
||||
let currency_pretty_print_style = wb.add_cellstyle(currency_pretty_print);
|
||||
|
||||
|
||||
// Create a Balance Statement
|
||||
let mut balance_statement = Sheet::new(format!("BalanceSheet"));
|
||||
balance_statement.set_styled_value(0, 0, Value::Text(format!("Balance Sheet")), &header_style_ref);
|
||||
balance_statement.set_styled_value(
|
||||
0,
|
||||
0,
|
||||
Value::Text(format!("Balance Sheet")),
|
||||
&header_style_ref,
|
||||
);
|
||||
balance_statement.set_col_width(0, Length::Pc(25.0));
|
||||
balance_statement.set_col_width(1, Length::Pc(25.0));
|
||||
balance_statement.set_col_width(2, Length::Pc(25.0));
|
||||
balance_statement.set_col_width(3, Length::Pc(25.0));
|
||||
balance_statement.set_col_width(4, Length::Pc(25.0));
|
||||
// We first need a list of all of our assets and liabilities
|
||||
let mut asset_accounts : Vec<(String, AssetAccount)> = books.asset_accounts.iter().map(|(a,b)| (a.clone(), b.clone())).collect();
|
||||
asset_accounts.sort_by(|a,b| {
|
||||
let mut asset_accounts: Vec<(String, AssetAccount)> = books
|
||||
.asset_accounts
|
||||
.iter()
|
||||
.map(|(a, b)| (a.clone(), b.clone()))
|
||||
.collect();
|
||||
asset_accounts.sort_by(|a, b| {
|
||||
if a.0.starts_with("Asset") && b.0.starts_with("Liability") {
|
||||
return Ordering::Less
|
||||
} if a.0.starts_with("Liability") && b.0.starts_with("Asset") {
|
||||
return Ordering::Greater
|
||||
return Ordering::Less;
|
||||
}
|
||||
if a.0.starts_with("Liability") && b.0.starts_with("Asset") {
|
||||
return Ordering::Greater;
|
||||
} else {
|
||||
return b.1.total_nominal(&books.commodities_oracle, close_date).total_cmp(&a.1.total_nominal(&books.commodities_oracle, close_date))
|
||||
return b
|
||||
.1
|
||||
.total_nominal(&books.commodities_oracle, close_date)
|
||||
.total_cmp(&a.1.total_nominal(&books.commodities_oracle, close_date));
|
||||
}
|
||||
});
|
||||
|
||||
// Assets Section
|
||||
balance_statement.set_styled_value(2, 0, Value::Text(format!("Assets")), &header_style_ref);
|
||||
balance_statement.set_styled_value(2, 2, Value::Text(format!("Closing Value")), &header_style_ref);
|
||||
balance_statement.set_styled_value(2, 3, Value::Text(format!("Opening Value")), &header_style_ref);
|
||||
balance_statement.set_styled_value(
|
||||
2,
|
||||
2,
|
||||
Value::Text(format!("Closing Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
balance_statement.set_styled_value(
|
||||
2,
|
||||
3,
|
||||
Value::Text(format!("Opening Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
let mut row_index = 3;
|
||||
let start = row_index;
|
||||
for (account_name, account) in asset_accounts.iter() {
|
||||
if account_name.starts_with("Asset") {
|
||||
let total_nominal = account.total_nominal(&books.commodities_oracle, close_date);
|
||||
let total_opening = account.opening_nominal(&books.commodities_oracle, opening_date);
|
||||
balance_statement.set_styled_value(row_index, 1, Value::Text(account_name.clone().replace("Assets:", "")), &general_text_style);
|
||||
balance_statement.set_styled_value(row_index, 2, Value::Currency(total_nominal, format!("CAD")), ¤cy_pretty_print_style);
|
||||
balance_statement.set_styled_value(row_index, 3, Value::Currency(total_opening, format!("CAD")), ¤cy_pretty_print_style);
|
||||
balance_statement.set_styled_value(
|
||||
row_index,
|
||||
1,
|
||||
Value::Text(account_name.clone().replace("Assets:", "")),
|
||||
&general_text_style,
|
||||
);
|
||||
balance_statement.set_styled_value(
|
||||
row_index,
|
||||
2,
|
||||
Value::Currency(total_nominal, format!("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
balance_statement.set_styled_value(
|
||||
row_index,
|
||||
3,
|
||||
Value::Currency(total_opening, format!("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
|
||||
row_index+=1;
|
||||
row_index += 1;
|
||||
}
|
||||
}
|
||||
balance_statement.set_styled_value(row_index, 0, format!("Total"), &header_style_ref);
|
||||
balance_statement.set_formula(row_index, 2, format!("SUM(C{}:C{})", start+1, row_index));
|
||||
balance_statement.set_cellstyle(row_index,2, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(row_index, 3, format!("SUM(D{}:D{})", start+1, row_index));
|
||||
balance_statement.set_cellstyle(row_index,3, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(row_index, 2, format!("SUM(C{}:C{})", start + 1, row_index));
|
||||
balance_statement.set_cellstyle(row_index, 2, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(row_index, 3, format!("SUM(D{}:D{})", start + 1, row_index));
|
||||
balance_statement.set_cellstyle(row_index, 3, ¤cy_pretty_print_style);
|
||||
let mut assets_total_row = row_index;
|
||||
row_index+=2;
|
||||
row_index += 2;
|
||||
|
||||
balance_statement.set_styled_value(row_index, 0, Value::Text(format!("Liabilities")), &header_style_ref);
|
||||
balance_statement.set_styled_value(
|
||||
row_index,
|
||||
0,
|
||||
Value::Text(format!("Liabilities")),
|
||||
&header_style_ref,
|
||||
);
|
||||
let start = row_index;
|
||||
for (account_name, account) in asset_accounts.iter() {
|
||||
if account_name.starts_with("Liability") {
|
||||
let total_nominal = account.total_nominal(&books.commodities_oracle, close_date);
|
||||
let total_opening = account.opening_nominal(&books.commodities_oracle, opening_date);
|
||||
if total_opening.is_zero() && total_nominal.is_zero() {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
balance_statement.set_styled_value(row_index, 1, Value::Text(account_name.clone().replace("Liability:", "")), &general_text_style);
|
||||
balance_statement.set_styled_value(row_index, 2, Value::Currency(total_nominal, format!("CAD")), ¤cy_pretty_print_style);
|
||||
balance_statement.set_styled_value(row_index, 3, Value::Currency(total_opening, format!("CAD")), ¤cy_pretty_print_style);
|
||||
balance_statement.set_styled_value(
|
||||
row_index,
|
||||
1,
|
||||
Value::Text(account_name.clone().replace("Liability:", "")),
|
||||
&general_text_style,
|
||||
);
|
||||
balance_statement.set_styled_value(
|
||||
row_index,
|
||||
2,
|
||||
Value::Currency(total_nominal, format!("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
balance_statement.set_styled_value(
|
||||
row_index,
|
||||
3,
|
||||
Value::Currency(total_opening, format!("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
|
||||
row_index+=1;
|
||||
row_index += 1;
|
||||
}
|
||||
}
|
||||
balance_statement.set_styled_value(row_index, 0, format!("Total"), &header_style_ref);
|
||||
balance_statement.set_formula(row_index, 2, format!("SUM(C{}:C{})", start+1, row_index));
|
||||
balance_statement.set_cellstyle(row_index,2, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(row_index, 3, format!("SUM(D{}:D{})", start+1, row_index));
|
||||
balance_statement.set_cellstyle(row_index,3, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(row_index, 2, format!("SUM(C{}:C{})", start + 1, row_index));
|
||||
balance_statement.set_cellstyle(row_index, 2, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(row_index, 3, format!("SUM(D{}:D{})", start + 1, row_index));
|
||||
balance_statement.set_cellstyle(row_index, 3, ¤cy_pretty_print_style);
|
||||
let mut liability_total_row = row_index;
|
||||
row_index+=2;
|
||||
|
||||
row_index += 2;
|
||||
|
||||
balance_statement.set_styled_value(row_index, 0, format!("Net Assets"), &header_style_ref);
|
||||
balance_statement.set_formula(row_index, 2, format!("C{} - C{}", assets_total_row+1, liability_total_row+1));
|
||||
balance_statement.set_cellstyle(row_index,2, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(
|
||||
row_index,
|
||||
2,
|
||||
format!("C{} - C{}", assets_total_row + 1, liability_total_row + 1),
|
||||
);
|
||||
balance_statement.set_cellstyle(row_index, 2, ¤cy_pretty_print_style);
|
||||
|
||||
|
||||
balance_statement.set_formula(row_index, 3, format!("D{} - D{}", assets_total_row+1, liability_total_row+1));
|
||||
balance_statement.set_cellstyle(row_index,3, ¤cy_pretty_print_style);
|
||||
balance_statement.set_formula(
|
||||
row_index,
|
||||
3,
|
||||
format!("D{} - D{}", assets_total_row + 1, liability_total_row + 1),
|
||||
);
|
||||
balance_statement.set_cellstyle(row_index, 3, ¤cy_pretty_print_style);
|
||||
let balance_sheet_opening_assets_row = row_index;
|
||||
|
||||
let balance_sheet_total_net_assets_row = row_index;
|
||||
|
||||
// Create an Income Statement
|
||||
let mut income_statement = Sheet::new(format!("IncomeStatement"));
|
||||
income_statement.set_styled_value(0, 0, Value::Text(format!("Income Statement")), &header_style_ref);
|
||||
income_statement.set_styled_value(
|
||||
0,
|
||||
0,
|
||||
Value::Text(format!("Income Statement")),
|
||||
&header_style_ref,
|
||||
);
|
||||
income_statement.set_col_width(0, Length::Pc(25.0));
|
||||
income_statement.set_col_width(1, Length::Pc(25.0));
|
||||
|
||||
// We first need a list of all of our incomes and expenses, ordered by value...
|
||||
let mut external_accounts : Vec<(String, ExternalAccount)> = books.external_accounts.iter().map(|(a,b)| (a.clone(), b.clone())).collect();
|
||||
external_accounts.sort_by(|a,b| {
|
||||
let mut external_accounts: Vec<(String, ExternalAccount)> = books
|
||||
.external_accounts
|
||||
.iter()
|
||||
.map(|(a, b)| (a.clone(), b.clone()))
|
||||
.collect();
|
||||
external_accounts.sort_by(|a, b| {
|
||||
if a.0.starts_with("Income") && b.0.starts_with("Expense") {
|
||||
return Ordering::Less
|
||||
} if a.0.starts_with("Expense") && b.0.starts_with("Income") {
|
||||
return Ordering::Greater
|
||||
return Ordering::Less;
|
||||
}
|
||||
if a.0.starts_with("Expense") && b.0.starts_with("Income") {
|
||||
return Ordering::Greater;
|
||||
} else {
|
||||
return a.1.total_nominal(&books.commodities_oracle).total_cmp(&b.1.total_nominal(&books.commodities_oracle))
|
||||
return a
|
||||
.1
|
||||
.total_nominal(&books.commodities_oracle)
|
||||
.total_cmp(&b.1.total_nominal(&books.commodities_oracle));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -137,203 +212,460 @@ pub fn export_to_spreadsheet(name: &str, books: &Books, opening_date: &String,
|
|||
let start = row_index;
|
||||
for (account_name, account) in external_accounts.iter() {
|
||||
if account_name.starts_with("Income") {
|
||||
income_statement.set_styled_value(row_index, 0, Value::Text(account_name.clone().replace("Income:", "")), &general_text_style);
|
||||
income_statement.set_styled_value(row_index, 1, Value::Currency(account.total_nominal(&books.commodities_oracle), format!("CAD")), ¤cy_pretty_print_style);
|
||||
row_index+=1;
|
||||
income_statement.set_styled_value(
|
||||
row_index,
|
||||
0,
|
||||
Value::Text(account_name.clone().replace("Income:", "")),
|
||||
&general_text_style,
|
||||
);
|
||||
income_statement.set_styled_value(
|
||||
row_index,
|
||||
1,
|
||||
Value::Currency(
|
||||
account.total_nominal(&books.commodities_oracle),
|
||||
format!("CAD"),
|
||||
),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
row_index += 1;
|
||||
}
|
||||
}
|
||||
income_statement.set_styled_value(row_index, 0, format!("Total"), &header_style_ref);
|
||||
income_statement.set_formula(row_index, 1, format!("SUM(B{}:B{})", start+1, row_index));
|
||||
income_statement.set_cellstyle(row_index,1, ¤cy_pretty_print_style);
|
||||
income_statement.set_formula(row_index, 1, format!("SUM(B{}:B{})", start + 1, row_index));
|
||||
income_statement.set_cellstyle(row_index, 1, ¤cy_pretty_print_style);
|
||||
let mut revenue_total_row = row_index;
|
||||
row_index+=2;
|
||||
row_index += 2;
|
||||
|
||||
// Now to Expenses...
|
||||
income_statement.set_styled_value(row_index, 0, Value::Text(format!("Expenses")), &general_text_style);
|
||||
row_index+=1;
|
||||
income_statement.set_styled_value(
|
||||
row_index,
|
||||
0,
|
||||
Value::Text(format!("Expenses")),
|
||||
&general_text_style,
|
||||
);
|
||||
row_index += 1;
|
||||
let start = row_index;
|
||||
for (account_name, account) in external_accounts.iter() {
|
||||
if account_name.starts_with("Expense") {
|
||||
income_statement.set_styled_value(row_index, 0, Value::Text(account_name.clone().replace("Expenses:", "")), &general_text_style);
|
||||
income_statement.set_styled_value(row_index, 1, Value::Currency(account.total_nominal(&books.commodities_oracle), format!("CAD")), ¤cy_pretty_print_style);
|
||||
row_index+=1;
|
||||
income_statement.set_styled_value(
|
||||
row_index,
|
||||
0,
|
||||
Value::Text(account_name.clone().replace("Expenses:", "")),
|
||||
&general_text_style,
|
||||
);
|
||||
income_statement.set_styled_value(
|
||||
row_index,
|
||||
1,
|
||||
Value::Currency(
|
||||
account.total_nominal(&books.commodities_oracle),
|
||||
format!("CAD"),
|
||||
),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
row_index += 1;
|
||||
}
|
||||
}
|
||||
income_statement.set_styled_value(row_index, 0, format!("Total"), &header_style_ref);
|
||||
income_statement.set_formula(row_index, 1, format!("SUM(B{}:B{})", start+1, row_index));
|
||||
income_statement.set_cellstyle(row_index,1, ¤cy_pretty_print_style);
|
||||
income_statement.set_formula(row_index, 1, format!("SUM(B{}:B{})", start + 1, row_index));
|
||||
income_statement.set_cellstyle(row_index, 1, ¤cy_pretty_print_style);
|
||||
let mut expense_total_row = row_index;
|
||||
|
||||
row_index+=2;
|
||||
row_index += 2;
|
||||
income_statement.set_styled_value(row_index, 0, format!("Net Income"), &header_style_ref);
|
||||
income_statement.set_formula(row_index, 1, format!("B{} - B{}", revenue_total_row+1, expense_total_row+1));
|
||||
income_statement.set_cellstyle(row_index,1, ¤cy_pretty_print_style);
|
||||
|
||||
income_statement.set_formula(
|
||||
row_index,
|
||||
1,
|
||||
format!("B{} - B{}", revenue_total_row + 1, expense_total_row + 1),
|
||||
);
|
||||
income_statement.set_cellstyle(row_index, 1, ¤cy_pretty_print_style);
|
||||
|
||||
let balance_sheet_summary_row = balance_sheet_total_net_assets_row + 2;
|
||||
balance_statement.set_styled_value(balance_sheet_summary_row,0, format!("Income statement balance for the year"), &header_style_ref);
|
||||
balance_statement.set_formula(balance_sheet_summary_row,1, format!("IncomeStatement.B{}", row_index+1));
|
||||
balance_statement.set_styled_value(
|
||||
balance_sheet_summary_row,
|
||||
0,
|
||||
format!("Income statement balance for the year"),
|
||||
&header_style_ref,
|
||||
);
|
||||
balance_statement.set_formula(
|
||||
balance_sheet_summary_row,
|
||||
1,
|
||||
format!("IncomeStatement.B{}", row_index + 1),
|
||||
);
|
||||
|
||||
balance_statement.set_styled_value(balance_sheet_summary_row+1,0, format!("Opening Assets + Income"), &header_style_ref);
|
||||
balance_statement.set_formula(balance_sheet_summary_row+1,1, format!("B{}+D{}", balance_sheet_summary_row+1,balance_sheet_opening_assets_row+1));
|
||||
balance_statement.set_styled_value(
|
||||
balance_sheet_summary_row + 1,
|
||||
0,
|
||||
format!("Opening Assets + Income"),
|
||||
&header_style_ref,
|
||||
);
|
||||
balance_statement.set_formula(
|
||||
balance_sheet_summary_row + 1,
|
||||
1,
|
||||
format!(
|
||||
"B{}+D{}",
|
||||
balance_sheet_summary_row + 1,
|
||||
balance_sheet_opening_assets_row + 1
|
||||
),
|
||||
);
|
||||
|
||||
let exchange_gains = books.calculate_gains(close_date);
|
||||
balance_statement.set_styled_value(balance_sheet_summary_row+2,0, format!("Unrealized Gains/Losses (Assets)"), &header_style_ref);
|
||||
balance_statement.set_styled_value(balance_sheet_summary_row+2,1, Currency(exchange_gains, format!("CAD")), ¤cy_pretty_print_style);
|
||||
|
||||
balance_statement.set_styled_value(balance_sheet_summary_row+3,0, format!("Opening Assets + Income + Unrealized Gains/Losses (Assets)"), &header_style_ref);
|
||||
balance_statement.set_formula(balance_sheet_summary_row+3,1, format!("B{}+B{}", balance_sheet_summary_row+2,balance_sheet_summary_row+3));
|
||||
balance_statement.set_styled_value(
|
||||
balance_sheet_summary_row + 2,
|
||||
0,
|
||||
format!("Unrealized Gains/Losses (Assets)"),
|
||||
&header_style_ref,
|
||||
);
|
||||
balance_statement.set_styled_value(
|
||||
balance_sheet_summary_row + 2,
|
||||
1,
|
||||
Currency(exchange_gains, format!("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
|
||||
balance_statement.set_styled_value(
|
||||
balance_sheet_summary_row + 3,
|
||||
0,
|
||||
format!("Opening Assets + Income + Unrealized Gains/Losses (Assets)"),
|
||||
&header_style_ref,
|
||||
);
|
||||
balance_statement.set_formula(
|
||||
balance_sheet_summary_row + 3,
|
||||
1,
|
||||
format!(
|
||||
"B{}+B{}",
|
||||
balance_sheet_summary_row + 2,
|
||||
balance_sheet_summary_row + 3
|
||||
),
|
||||
);
|
||||
|
||||
wb.push_sheet(balance_statement);
|
||||
wb.push_sheet(income_statement);
|
||||
|
||||
// Create a General Ledger
|
||||
let mut general_ledger = Sheet::new(format!("GeneralLedger"));
|
||||
general_ledger.set_styled_value(0, 0, Value::Text(format!("General Ledger")), &header_style_ref);
|
||||
general_ledger.set_styled_value(
|
||||
0,
|
||||
0,
|
||||
Value::Text(format!("General Ledger")),
|
||||
&header_style_ref,
|
||||
);
|
||||
|
||||
let start_row = 1;
|
||||
general_ledger.set_styled_value(start_row, 0, Value::Text(String::from("Ref")), &header_style_ref);
|
||||
general_ledger.set_styled_value(
|
||||
start_row,
|
||||
0,
|
||||
Value::Text(String::from("Ref")),
|
||||
&header_style_ref,
|
||||
);
|
||||
general_ledger.set_col_width(0, Length::Pc(15.0));
|
||||
|
||||
general_ledger.set_styled_value(start_row, 1, Value::Text(String::from("Date")), &header_style_ref);
|
||||
general_ledger.set_styled_value(
|
||||
start_row,
|
||||
1,
|
||||
Value::Text(String::from("Date")),
|
||||
&header_style_ref,
|
||||
);
|
||||
general_ledger.set_col_width(1, Length::Pc(15.0));
|
||||
|
||||
general_ledger.set_styled_value(start_row, 2, Value::Text(String::from("Account")), &header_style_ref);
|
||||
general_ledger.set_styled_value(
|
||||
start_row,
|
||||
2,
|
||||
Value::Text(String::from("Account")),
|
||||
&header_style_ref,
|
||||
);
|
||||
general_ledger.set_col_width(2, Length::Pc(30.0));
|
||||
|
||||
general_ledger.set_styled_value(start_row, 3, Value::Text(String::from("Recorded CAD Value")), &header_style_ref);
|
||||
general_ledger.set_styled_value(
|
||||
start_row,
|
||||
3,
|
||||
Value::Text(String::from("Recorded CAD Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
general_ledger.set_col_width(3, Length::Pc(15.0));
|
||||
|
||||
general_ledger.set_styled_value(start_row, 4, Value::Text(String::from("Recorded Commodity Value")), &header_style_ref);
|
||||
general_ledger.set_styled_value(
|
||||
start_row,
|
||||
4,
|
||||
Value::Text(String::from("Recorded Commodity Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
general_ledger.set_col_width(4, Length::Pc(15.0));
|
||||
|
||||
general_ledger.set_styled_value(start_row, 5, Value::Text(String::from("Commodity")), &header_style_ref);
|
||||
general_ledger.set_styled_value(
|
||||
start_row,
|
||||
5,
|
||||
Value::Text(String::from("Commodity")),
|
||||
&header_style_ref,
|
||||
);
|
||||
general_ledger.set_col_width(5, Length::Pc(15.0));
|
||||
general_ledger.set_header_rows(start_row,1);
|
||||
general_ledger.set_header_rows(start_row, 1);
|
||||
|
||||
let mut row_index = 2;
|
||||
// Create a sheet for each tracked account
|
||||
let mut running_total = Decimal::new(0,0);
|
||||
let mut running_total = Decimal::new(0, 0);
|
||||
for (txid, date, postings) in books.general_ledger.iter() {
|
||||
general_ledger.set_styled_value(row_index, 0, Value::Number(*txid as f64), &header_style_ref);
|
||||
let recorded_date = NaiveDate::from_str(date).unwrap();
|
||||
general_ledger.set_styled_value(row_index, 1, Value::DateTime(recorded_date.and_time(NaiveTime::default())), &header_style_ref);
|
||||
row_index +=1;
|
||||
for(account, commodity, quantity, nominal_value) in postings.iter() {
|
||||
general_ledger.set_styled_value(
|
||||
row_index,
|
||||
1,
|
||||
Value::DateTime(recorded_date.and_time(NaiveTime::default())),
|
||||
&header_style_ref,
|
||||
);
|
||||
row_index += 1;
|
||||
for (account, commodity, quantity, nominal_value) in postings.iter() {
|
||||
running_total += nominal_value;
|
||||
general_ledger.set_styled_value(row_index, 2, Value::Text(account.clone()), &general_text_style);
|
||||
general_ledger.set_styled_value(row_index, 3, Value::Currency(nominal_value.to_f64().unwrap(), String::from("CAD")), ¤cy_pretty_print_style);
|
||||
general_ledger.set_styled_value(row_index, 4, Value::Currency(quantity.to_f64().unwrap(), commodity.clone()),&general_text_style);
|
||||
general_ledger.set_styled_value(row_index, 5, Value::Text(commodity.clone().replace("$", "CAD")), &general_text_style);
|
||||
row_index +=1;
|
||||
general_ledger.set_styled_value(
|
||||
row_index,
|
||||
2,
|
||||
Value::Text(account.clone()),
|
||||
&general_text_style,
|
||||
);
|
||||
general_ledger.set_styled_value(
|
||||
row_index,
|
||||
3,
|
||||
Value::Currency(nominal_value.to_f64().unwrap(), String::from("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
general_ledger.set_styled_value(
|
||||
row_index,
|
||||
4,
|
||||
Value::Currency(quantity.to_f64().unwrap(), commodity.clone()),
|
||||
&general_text_style,
|
||||
);
|
||||
general_ledger.set_styled_value(
|
||||
row_index,
|
||||
5,
|
||||
Value::Text(commodity.clone().replace("$", "CAD")),
|
||||
&general_text_style,
|
||||
);
|
||||
row_index += 1;
|
||||
}
|
||||
|
||||
general_ledger.set_styled_value(row_index, 3, Value::Currency(running_total.to_f64().unwrap(), String::from("CAD")), ¤cy_pretty_print_style);
|
||||
row_index+=2;
|
||||
general_ledger.set_styled_value(
|
||||
row_index,
|
||||
3,
|
||||
Value::Currency(running_total.to_f64().unwrap(), String::from("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
row_index += 2;
|
||||
}
|
||||
wb.push_sheet(general_ledger);
|
||||
|
||||
// Create a sheet for each asset account
|
||||
// We first need a list of all of our incomes and expenses, ordered by value...
|
||||
let mut asset_accounts : Vec<(String, AssetAccount)> = books.asset_accounts.iter().map(|(a,b)| (a.clone(), b.clone())).collect();
|
||||
asset_accounts.sort_by(|a,b| {
|
||||
let mut asset_accounts: Vec<(String, AssetAccount)> = books
|
||||
.asset_accounts
|
||||
.iter()
|
||||
.map(|(a, b)| (a.clone(), b.clone()))
|
||||
.collect();
|
||||
asset_accounts.sort_by(|a, b| {
|
||||
if a.0.starts_with("Assets") && b.0.starts_with("Liability") {
|
||||
return Ordering::Less
|
||||
} if a.0.starts_with("Liability") && b.0.starts_with("Assets") {
|
||||
return Ordering::Greater
|
||||
return Ordering::Less;
|
||||
}
|
||||
if a.0.starts_with("Liability") && b.0.starts_with("Assets") {
|
||||
return Ordering::Greater;
|
||||
} else {
|
||||
return b.1.total_nominal(&books.commodities_oracle, close_date).total_cmp(&a.1.total_nominal(&books.commodities_oracle, close_date))
|
||||
return b
|
||||
.1
|
||||
.total_nominal(&books.commodities_oracle, close_date)
|
||||
.total_cmp(&a.1.total_nominal(&books.commodities_oracle, close_date));
|
||||
}
|
||||
});
|
||||
for (external_account, account) in asset_accounts.iter() {
|
||||
let mut sheet = Sheet::new(format!("{}", external_account.to_lowercase().replace(":", "")));
|
||||
|
||||
sheet.set_styled_value(0, 0, Value::Text(format!("Account: {}", external_account)), &header_style_ref);
|
||||
let mut sheet = Sheet::new(format!(
|
||||
"{}",
|
||||
external_account.to_lowercase().replace(":", "")
|
||||
));
|
||||
|
||||
sheet.set_styled_value(
|
||||
0,
|
||||
0,
|
||||
Value::Text(format!("Account: {}", external_account)),
|
||||
&header_style_ref,
|
||||
);
|
||||
|
||||
let start_row = 1;
|
||||
sheet.set_styled_value(start_row, 0, Value::Text(String::from("Ref")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
0,
|
||||
Value::Text(String::from("Ref")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(0, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 1, Value::Text(String::from("Date")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
1,
|
||||
Value::Text(String::from("Date")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(1, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 2, Value::Text(String::from("Recorded CAD Value")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
2,
|
||||
Value::Text(String::from("Recorded CAD Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(2, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 3, Value::Text(String::from("Recorded Commodity Value")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
3,
|
||||
Value::Text(String::from("Recorded Commodity Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(3, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 4, Value::Text(String::from("Commodity")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
4,
|
||||
Value::Text(String::from("Commodity")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(4, Length::Pc(15.0));
|
||||
|
||||
|
||||
sheet.set_header_rows(start_row,1);
|
||||
sheet.set_header_rows(start_row, 1);
|
||||
|
||||
let start_row = 2;
|
||||
for(row, (txref, date, commodity, cost, quantity, booked_account)) in account.transactions.iter().enumerate() {
|
||||
let index = start_row+(row as u32);
|
||||
for (row, (txref, date, commodity, cost, quantity, booked_account)) in account.transactions.iter().enumerate() {
|
||||
let index = start_row + (row as u32);
|
||||
let recorded_date = NaiveDate::from_str(date).unwrap();
|
||||
let nominal_value = if commodity != "CAD" && commodity != "$" && commodity != "StickerSheets" {
|
||||
books.commodities_oracle.lookup(commodity, date) * quantity
|
||||
} else {
|
||||
quantity * Decimal::new(1,0)
|
||||
quantity * Decimal::new(1, 0)
|
||||
};
|
||||
sheet.set_styled_value(index, 0, Value::Number(*txref as f64), &header_style_ref);
|
||||
sheet.set_styled_value(index, 1, Value::DateTime(recorded_date.and_time(NaiveTime::default())), &general_text_style);
|
||||
sheet.set_styled_value(index, 2, Value::Currency(nominal_value.to_f64().unwrap(), String::from("CAD")), ¤cy_pretty_print_style);
|
||||
sheet.set_styled_value(index, 3, Value::Currency(quantity.to_f64().unwrap(), commodity.clone()), &general_text_style);
|
||||
sheet.set_styled_value(index, 4, Value::Text(commodity.clone().replace("$", "CAD")), &general_text_style);
|
||||
sheet.set_styled_value(index, 5, Value::Text(booked_account.clone()), &general_text_style);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
1,
|
||||
Value::DateTime(recorded_date.and_time(NaiveTime::default())),
|
||||
&general_text_style,
|
||||
);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
2,
|
||||
Value::Currency(nominal_value.to_f64().unwrap(), String::from("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
3,
|
||||
Value::Currency(quantity.to_f64().unwrap(), commodity.clone()),
|
||||
&general_text_style,
|
||||
);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
4,
|
||||
Value::Text(commodity.clone().replace("$", "CAD")),
|
||||
&general_text_style,
|
||||
);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
5,
|
||||
Value::Text(booked_account.clone()),
|
||||
&general_text_style,
|
||||
);
|
||||
}
|
||||
wb.push_sheet(sheet);
|
||||
}
|
||||
|
||||
|
||||
// Create a sheet for each tracked account
|
||||
for (external_account, account) in external_accounts.iter() {
|
||||
let mut sheet = Sheet::new(format!("{}", external_account.to_lowercase().replace(":", "")));
|
||||
|
||||
sheet.set_styled_value(0, 0, Value::Text(format!("Account: {}", external_account)), &header_style_ref);
|
||||
let mut sheet = Sheet::new(format!(
|
||||
"{}",
|
||||
external_account.to_lowercase().replace(":", "")
|
||||
));
|
||||
|
||||
sheet.set_styled_value(
|
||||
0,
|
||||
0,
|
||||
Value::Text(format!("Account: {}", external_account)),
|
||||
&header_style_ref,
|
||||
);
|
||||
|
||||
let mut start_row = 1;
|
||||
sheet.set_styled_value(start_row, 0, Value::Text(String::from("Ref")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
0,
|
||||
Value::Text(String::from("Ref")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(0, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 1, Value::Text(String::from("Date")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
1,
|
||||
Value::Text(String::from("Date")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(1, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 2, Value::Text(String::from("Recorded CAD Value")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
2,
|
||||
Value::Text(String::from("Recorded CAD Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(2, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 3, Value::Text(String::from("Recorded Commodity Value")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
3,
|
||||
Value::Text(String::from("Recorded Commodity Value")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(3, Length::Pc(15.0));
|
||||
|
||||
sheet.set_styled_value(start_row, 4, Value::Text(String::from("Commodity")), &header_style_ref);
|
||||
sheet.set_styled_value(
|
||||
start_row,
|
||||
4,
|
||||
Value::Text(String::from("Commodity")),
|
||||
&header_style_ref,
|
||||
);
|
||||
sheet.set_col_width(4, Length::Pc(15.0));
|
||||
|
||||
|
||||
sheet.set_header_rows(start_row,1);
|
||||
sheet.set_header_rows(start_row, 1);
|
||||
|
||||
start_row += 1;
|
||||
for(row, (txref, date, commodity, quantity)) in account.transactions.iter().enumerate() {
|
||||
let index = start_row+(row as u32);
|
||||
for (row, (txref, date, commodity, quantity)) in account.transactions.iter().enumerate() {
|
||||
let index = start_row + (row as u32);
|
||||
let recorded_date = NaiveDate::from_str(date).unwrap();
|
||||
let nominal_value = if commodity != "CAD" && commodity != "$" && commodity != "StickerSheets" {
|
||||
let nominal_value = if commodity != "CAD" && commodity != "$" && commodity != "StickerSheets" {
|
||||
books.commodities_oracle.lookup(commodity, date) * quantity
|
||||
} else {
|
||||
Decimal::new(1,0 ) * quantity
|
||||
Decimal::new(1, 0) * quantity
|
||||
};
|
||||
sheet.set_styled_value(index, 0, Value::Number(*txref as f64), &header_style_ref);
|
||||
sheet.set_styled_value(index, 1, Value::DateTime(recorded_date.and_time(NaiveTime::default())), &general_text_style);
|
||||
sheet.set_styled_value(index, 2, Value::Currency(nominal_value.to_f64().unwrap(), String::from("CAD")), ¤cy_pretty_print_style);
|
||||
sheet.set_styled_value(index, 3, Value::Currency(quantity.to_f64().unwrap(), commodity.clone()), &general_text_style);
|
||||
sheet.set_styled_value(index, 4, Value::Text(commodity.clone().replace("$", "CAD")), &general_text_style);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
1,
|
||||
Value::DateTime(recorded_date.and_time(NaiveTime::default())),
|
||||
&general_text_style,
|
||||
);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
2,
|
||||
Value::Currency(nominal_value.to_f64().unwrap(), String::from("CAD")),
|
||||
¤cy_pretty_print_style,
|
||||
);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
3,
|
||||
Value::Currency(quantity.to_f64().unwrap(), commodity.clone()),
|
||||
&general_text_style,
|
||||
);
|
||||
sheet.set_styled_value(
|
||||
index,
|
||||
4,
|
||||
Value::Text(commodity.clone().replace("$", "CAD")),
|
||||
&general_text_style,
|
||||
);
|
||||
}
|
||||
wb.push_sheet(sheet);
|
||||
}
|
||||
|
||||
spreadsheet_ods::write_ods(&mut wb, name);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue