summaryrefslogtreecommitdiff
path: root/reports.cc
diff options
context:
space:
mode:
Diffstat (limited to 'reports.cc')
-rw-r--r--reports.cc199
1 files changed, 125 insertions, 74 deletions
diff --git a/reports.cc b/reports.cc
index 47f6a650..97156e74 100644
--- a/reports.cc
+++ b/reports.cc
@@ -9,7 +9,6 @@ namespace ledger {
static bool cleared_only = false;
static bool uncleared_only = false;
-static bool cost_basis = false;
static bool show_virtual = true;
static bool show_children = false;
static bool show_sorted = false;
@@ -19,7 +18,10 @@ static bool full_names = false;
static bool print_monthly = false;
static bool gnuplot_safe = false;
-static bool get_quotes = false;
+static bool cost_basis = false;
+static bool use_history = false;
+static bool read_prices = false;
+static bool get_quotes = false;
long pricing_leeway = 24 * 3600;
std::string price_db;
@@ -71,6 +73,58 @@ static bool matches_date_range(entry * ent)
return true;
}
+static amount * resolve_amount(amount * amt,
+ std::time_t * when = NULL,
+ totals * balance = NULL,
+ bool add_base_value = false,
+ bool free_memory = false)
+{
+ amount * value;
+ bool alloced = true;
+
+ if (! use_history) {
+ value = amt;
+ alloced = false;
+ }
+ else if (cost_basis) {
+ value = amt->value();
+ }
+ else {
+ value = amt->street(when ? when : (have_ending ? &end_date : NULL),
+ use_history, get_quotes);
+ }
+
+ if (balance) {
+ if (add_base_value)
+ balance->credit(cost_basis ? value : amt);
+ else
+ balance->credit(value);
+ }
+
+ if (free_memory && alloced) {
+ delete value;
+ value = NULL;
+ }
+ else if (! free_memory && ! alloced) {
+ value = value->copy();
+ }
+
+ return value;
+}
+
+static inline void print_resolved_balance(std::ostream& out,
+ std::time_t * when,
+ totals& balance,
+ bool added_base_value = false)
+{
+ if (! added_base_value || ! use_history || cost_basis)
+ balance.print(out, 12);
+ else
+ balance.print_street(out, 12,
+ when ? when : (have_ending ? &end_date : NULL),
+ use_history, get_quotes);
+}
+
//////////////////////////////////////////////////////////////////////
//
// Balance reporting code
@@ -274,16 +328,7 @@ void report_balances(std::ostream& out, regexps_list& regexps)
}
if (acct->checked == 1) {
- amount * street = (*x)->cost->street(have_ending ? &end_date : NULL,
- cost_basis || get_quotes,
- get_quotes);
- if (cost_basis &&
- street->commdty() == (*x)->cost->commdty() &&
- (*x)->cost->has_price()) {
- street = (*x)->cost->value();
- }
- acct->balance.credit(street);
- delete street;
+ resolve_amount((*x)->cost, NULL, &acct->balance, false, true);
}
else if (show_subtotals) {
if (! regexps.empty() && ! match) {
@@ -364,9 +409,7 @@ void print_register_transaction(std::ostream& out, entry *ent,
// Always display the street value, if prices have been
// specified
- amount * street = xact->cost->street(&ent->date, cost_basis || get_quotes,
- get_quotes);
- balance.credit(street);
+ amount * street = resolve_amount(xact->cost, &ent->date, &balance, true);
// If there are two transactions, use the one which does not
// refer to this account. If there are more than two, print
@@ -395,7 +438,7 @@ void print_register_transaction(std::ostream& out, entry *ent,
out << std::right << street->as_str(true);
delete street;
- balance.print(out, 12);
+ print_resolved_balance(out, &ent->date, balance, true);
out << std::endl;
@@ -412,17 +455,16 @@ void print_register_transaction(std::ostream& out, entry *ent,
out.width(22);
out << std::left << truncated((*y)->acct_as_str(), 22) << " ";
-
out.width(12);
- street = (*y)->cost->street(&ent->date, cost_basis || get_quotes,
- get_quotes);
+
+ street = resolve_amount((*y)->cost, &ent->date);
out << std::right << street->as_str(true) << std::endl;
delete street;
}
}
void print_register_period(std::ostream& out, std::time_t date,
- account *acct, amount& sum, totals& balance)
+ account * acct, amount& sum, totals& balance)
{
char buf[32];
std::strftime(buf, 31, "%Y/%m/%d ", std::localtime(&date));
@@ -448,7 +490,7 @@ void print_register_period(std::ostream& out, std::time_t date,
out << std::right << sum.as_str();
if (! gnuplot_safe)
- balance.print(out, 12);
+ print_resolved_balance(out, &date, balance, true);
out << std::endl;
}
@@ -496,11 +538,8 @@ void print_register(std::ostream& out, const std::string& acct_name,
if (period == PERIOD_NONE) {
print_register_transaction(out, *i, *x, balance);
} else {
- amount * street = (*x)->cost->street(&(*i)->date,
- cost_basis || get_quotes,
- get_quotes);
- balance.credit(street);
-
+ amount * street = resolve_amount((*x)->cost, &(*i)->date,
+ &balance, true);
if (period_sum) {
period_sum->credit(street);
delete street;
@@ -550,16 +589,12 @@ static void equity_entry(account * acct, regexps_list& regexps,
transaction * xact = new transaction();
xact->acct = const_cast<account *>(acct);
- xact->cost = (*i).second->street(have_ending ? &end_date : NULL,
- cost_basis || get_quotes,
- get_quotes);
+ xact->cost = (*i).second->copy();
opening.xacts.push_back(xact);
xact = new transaction();
xact->acct = main_ledger->find_account("Equity:Opening Balances");
- xact->cost = (*i).second->street(have_ending ? &end_date : NULL,
- cost_basis || get_quotes,
- get_quotes);
+ xact->cost = (*i).second->copy();
xact->cost->negate();
opening.xacts.push_back(xact);
}
@@ -605,8 +640,7 @@ void price_report(std::ostream& out, regexps_list& regexps)
i++)
if (regexps.empty() || matches(regexps, (*i).first)) {
amount * price = (*i).second->price(have_ending ? &end_date : NULL,
- cost_basis || get_quotes,
- get_quotes);
+ use_history, get_quotes);
if (price && ! price->is_zero()) {
out.width(20);
out << std::right << price->as_str() << " " << (*i).first
@@ -787,28 +821,33 @@ static void show_help(std::ostream& out)
<< "usage: ledger [options] COMMAND [options] [REGEXPS]" << std::endl
<< std::endl
<< "ledger options:" << std::endl
+ << " -B report commodities in terms of their basis cost" << std::endl
<< " -b DATE specify a beginning date" << std::endl
- << " -e DATE specify an ending date" << std::endl
- << " -c do not show future entries (same as -e TODAY)" << std::endl
<< " -C show only cleared transactions and balances" << std::endl
+ << " -c do not show future entries (same as -e TODAY)" << std::endl
<< " -d DATE specify a date mask ('-d mon', for all mondays)" << std::endl
<< " -E also show accounts with zero totals" << std::endl
- << " -f FILE specify pathname of ledger data file" << std::endl
+ << " -e DATE specify an ending date" << std::endl
<< " -F print each account's full name" << std::endl
+ << " -f FILE specify pathname of ledger data file" << std::endl
+ << " -G use with -M to produce gnuplot-friendly output" << std::endl
<< " -h display this help text" << std::endl
<< " -i FILE read the list of inclusion regexps from FILE" << std::endl
+ << " -L MINS fetch price quotes if info older than MINS" << std::endl
<< " -l AMT don't print balance totals whose abs value is <AMT" << std::endl
<< " -M print register using monthly sub-totals" << std::endl
- << " -G use with -M to produce gnuplot-friendly output" << std::endl
- << " -n do not calculate parent account totals" << std::endl
<< " -N REGEX accounts matching REGEXP only display if negative" << std::endl
- << " -p ARG set a price, or read prices from a file" << std::endl
+ << " -n do not calculate parent account totals" << std::endl
<< " -P download price quotes from the Internet" << std::endl
<< " (works by running the command \"getquote SYMBOL\")" << std::endl
+ << " -p ARG set a direct price conversion: COMM=PRICE" << std::endl
+ << " -Q FILE keep price histories in FILE (implies -P)" << std::endl
<< " -R do not factor in virtual transactions" << std::endl
- << " -s show sub-accounts in balance totals" << std::endl
<< " -S sort the output of \"print\" by date" << std::endl
+ << " -s show sub-accounts in balance totals" << std::endl
+ << " -T report only commodities totals, not their value" << std::endl
<< " -U show only uncleared transactions and balances" << std::endl
+ << " -V report commodity values, but don't download quotes" << std::endl
<< " -v display version information" << std::endl << std::endl
<< "commands:" << std::endl
<< " balance show balance totals" << std::endl
@@ -830,17 +869,24 @@ int main(int argc, char * argv[])
std::string prices;
std::string limit;
regexps_list regexps;
- bool no_history = false;
std::vector<std::string> files;
main_ledger = new book;
+ // Initialize some variables based on environment variable settings
+
+ if (char * p = std::getenv("PRICE_HIST"))
+ price_db = p;
+
+ if (char * p = std::getenv("PRICE_EXP"))
+ pricing_leeway = std::atol(p) * 60;
+
// Parse the command-line options
int c;
while (-1 != (c = getopt(argc, argv,
- "+b:e:d:cCUhBRV:f:i:p:PL:Q:TvsSEnFMGl:N:"))) {
+ "+Bb:Ccd:Ee:Ff:Ghi:L:l:MN:nPp:Q:RSsTUVv"))) {
switch (char(c)) {
case 'b':
have_beginning = true;
@@ -900,24 +946,43 @@ int main(int argc, char * argv[])
break;
case 'P':
- get_quotes = true;
+ cost_basis = false;
+ use_history = true;
+ get_quotes = true;
+ read_prices = true;
break;
- case 'L':
- pricing_leeway = std::atol(optarg) * 60;
+ case 'Q':
+ cost_basis = false;
+ use_history = true;
+ get_quotes = true;
+ read_prices = true;
+ price_db = optarg;
break;
- case 'Q':
- get_quotes = true;
- price_db = optarg;
+ case 'V':
+ cost_basis = false;
+ use_history = true;
+ get_quotes = false;
+ read_prices = true;
break;
case 'B':
- cost_basis = true;
- // fall through...
+ cost_basis = true;
+ use_history = true;
+ get_quotes = false;
+ read_prices = false;
+ break;
+
case 'T':
- no_history = true;
- get_quotes = false;
+ cost_basis = false;
+ use_history = false;
+ get_quotes = false;
+ read_prices = false;
+ break;
+
+ case 'L':
+ pricing_leeway = std::atol(optarg) * 60;
break;
case 'l':
@@ -966,20 +1031,6 @@ int main(int argc, char * argv[])
for (; index < argc; index++)
regexps.push_back(mask(argv[index]));
- // If a price history file is specified with the environment
- // variable PRICE_HIST, add it to the list of ledger files to read.
-
- if (! no_history) {
- if (price_db.empty())
- if (char * p = std::getenv("PRICE_HIST")) {
- get_quotes = true;
- price_db = p;
- }
-
- if (char * p = std::getenv("PRICE_EXP"))
- pricing_leeway = std::atol(p) * 60;
- }
-
// A ledger data file must be specified
int entry_count = 0;
@@ -989,8 +1040,8 @@ int main(int argc, char * argv[])
for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":")) {
char * sep = std::strrchr(p, '=');
if (sep) *sep++ = '\0';
- entry_count += parse_ledger_file(main_ledger, std::string(p),
- regexps, command == "equity", sep);
+ entry_count += parse_ledger_file(main_ledger, std::string(p), regexps,
+ command == "equity", sep);
}
}
} else {
@@ -1001,14 +1052,14 @@ int main(int argc, char * argv[])
std::strcpy(p, (*i).c_str());
char * sep = std::strrchr(p, '=');
if (sep) *sep++ = '\0';
- entry_count += parse_ledger_file(main_ledger, std::string(p),
- regexps, command == "equity", sep);
+ entry_count += parse_ledger_file(main_ledger, std::string(p), regexps,
+ command == "equity", sep);
}
}
- if (! no_history && ! price_db.empty())
- entry_count += parse_ledger_file(main_ledger, price_db,
- regexps, command == "equity");
+ if (read_prices && ! price_db.empty())
+ entry_count += parse_ledger_file(main_ledger, price_db, regexps,
+ command == "equity");
if (entry_count == 0) {
std::cerr << ("Please specify ledger file(s) using -f option "