672 lines
23 KiB
Rust
672 lines
23 KiB
Rust
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"));
|
|
|
|
let mut header_style = CellStyle::new_empty();
|
|
header_style.set_font_bold();
|
|
header_style.set_text_align(TextAlign::Center);
|
|
let header_style_ref = wb.add_cellstyle(header_style);
|
|
|
|
let currency_format = format::create_currency_prefix("cad", locale!("en_CA"), format!("$"));
|
|
let currency_style = wb.add_currency_format(currency_format);
|
|
|
|
let mut general_text_style = CellStyle::new_empty();
|
|
general_text_style.set_text_align(TextAlign::Right);
|
|
let general_text_style = wb.add_cellstyle(general_text_style);
|
|
|
|
let mut currency_pretty_print = CellStyle::new_empty();
|
|
currency_pretty_print.set_text_align(TextAlign::Right);
|
|
currency_pretty_print.set_decimal_places(2);
|
|
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_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| {
|
|
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;
|
|
} else {
|
|
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,
|
|
);
|
|
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,
|
|
);
|
|
|
|
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);
|
|
let mut assets_total_row = row_index;
|
|
row_index += 2;
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
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);
|
|
let mut liability_total_row = row_index;
|
|
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,
|
|
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_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| {
|
|
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;
|
|
} else {
|
|
return a
|
|
.1
|
|
.total_nominal(&books.commodities_oracle)
|
|
.total_cmp(&b.1.total_nominal(&books.commodities_oracle));
|
|
}
|
|
});
|
|
|
|
// Revenue Section
|
|
income_statement.set_styled_value(2, 0, Value::Text(format!("Revenue")), &header_style_ref);
|
|
let mut row_index = 3;
|
|
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, 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);
|
|
let mut revenue_total_row = row_index;
|
|
row_index += 2;
|
|
|
|
// Now to Expenses...
|
|
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, 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);
|
|
let mut expense_total_row = row_index;
|
|
|
|
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);
|
|
|
|
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 + 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
|
|
),
|
|
);
|
|
|
|
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,
|
|
);
|
|
|
|
let start_row = 1;
|
|
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_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_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_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_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_col_width(5, Length::Pc(15.0));
|
|
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);
|
|
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() {
|
|
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,
|
|
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| {
|
|
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;
|
|
} else {
|
|
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 start_row = 1;
|
|
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_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_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_col_width(3, Length::Pc(15.0));
|
|
|
|
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);
|
|
|
|
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);
|
|
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)
|
|
};
|
|
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,
|
|
);
|
|
}
|
|
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 start_row = 1;
|
|
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_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_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_col_width(3, Length::Pc(15.0));
|
|
|
|
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);
|
|
|
|
start_row += 1;
|
|
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" {
|
|
books.commodities_oracle.lookup(commodity, date) * quantity
|
|
} else {
|
|
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,
|
|
);
|
|
}
|
|
wb.push_sheet(sheet);
|
|
}
|
|
|
|
spreadsheet_ods::write_ods(&mut wb, name);
|
|
}
|