brought back the "print" and "equity" reports; this time much better!

This commit is contained in:
John Wiegley 2004-07-31 07:42:34 -04:00
parent 2a9e778b7d
commit f570e6644f
14 changed files with 637 additions and 571 deletions

View file

@ -10,7 +10,7 @@
namespace ledger { namespace ledger {
static void mpz_round(mpz_t value, int precision) static void mpz_round(mpz_t out, mpz_t value, int precision)
{ {
mpz_t divisor; mpz_t divisor;
@ -33,17 +33,17 @@ static void mpz_round(mpz_t value, int precision)
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision); mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision);
mpz_add(remainder, divisor, remainder); mpz_add(remainder, divisor, remainder);
mpz_ui_sub(remainder, 0, remainder); mpz_ui_sub(remainder, 0, remainder);
mpz_add(value, value, remainder); mpz_add(out, value, remainder);
} else { } else {
mpz_sub(value, value, remainder); mpz_sub(out, value, remainder);
} }
} else { } else {
if (mpz_cmp(remainder, divisor) >= 0) { if (mpz_cmp(remainder, divisor) >= 0) {
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision); mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision);
mpz_sub(remainder, divisor, remainder); mpz_sub(remainder, divisor, remainder);
mpz_add(value, value, remainder); mpz_add(out, value, remainder);
} else { } else {
mpz_sub(value, value, remainder); mpz_sub(out, value, remainder);
} }
} }
@ -357,7 +357,7 @@ amount_t amount_t::round(int precision) const
return *this; return *this;
} else { } else {
amount_t temp = *this; amount_t temp = *this;
mpz_round(MPZ(temp.quantity), mpz_round(MPZ(temp.quantity), MPZ(temp.quantity),
precision == -1 ? commodity->precision : precision); precision == -1 ? commodity->precision : precision);
return temp; return temp;
} }
@ -380,8 +380,14 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
bool negative = false; bool negative = false;
// Ensure the value is rounded to the commodity's precision before
// outputting it. NOTE: `rquotient' is used here as a temp variable!
if (amt.commodity->precision != MAX_PRECISION)
mpz_round(rquotient, MPZ(amt.quantity), amt.commodity->precision);
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION); mpz_ui_pow_ui(divisor, 10, MAX_PRECISION);
mpz_tdiv_qr(quotient, remainder, MPZ(amt.quantity), divisor); mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0) if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0)
negative = true; negative = true;
@ -392,10 +398,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
if (amt.commodity->precision == MAX_PRECISION) { if (amt.commodity->precision == MAX_PRECISION) {
mpz_set(rquotient, remainder); mpz_set(rquotient, remainder);
} else { } else {
// Ensure the value is rounded to the commodity's precision before
// outputting it
mpz_round(MPZ(amt.quantity), amt.commodity->precision);
assert(MAX_PRECISION - amt.commodity->precision > 0); assert(MAX_PRECISION - amt.commodity->precision > 0);
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - amt.commodity->precision); mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - amt.commodity->precision);
mpz_tdiv_qr(rquotient, remainder, remainder, divisor); mpz_tdiv_qr(rquotient, remainder, remainder, divisor);
@ -681,6 +683,19 @@ void (*commodity_t::updater)(commodity_t * commodity,
commodities_map commodity_t::commodities; commodities_map commodity_t::commodities;
struct cleanup_commodities
{
~cleanup_commodities() {
for (commodities_map::iterator i
= commodity_t::commodities.begin();
i != commodity_t::commodities.end();
i++)
delete (*i).second;
}
};
static cleanup_commodities cleanup;
commodity_t * commodity_t::find_commodity(const std::string& symbol, commodity_t * commodity_t::find_commodity(const std::string& symbol,
bool auto_create) bool auto_create)
{ {

View file

@ -274,6 +274,27 @@ class balance_t
return amount(amt.commodity) >= amt; return amount(amt.commodity) >= amt;
} }
bool operator==(const balance_t& bal) const {
amounts_map::const_iterator i, j;
for (i = amounts.begin(), j = bal.amounts.begin();
i != amounts.end() && j != bal.amounts.end();
i++, j++) {
if (! ((*i).first == (*j).first &&
(*i).second == (*j).second))
return false;
}
return i == amounts.end() && j == bal.amounts.end();
}
bool operator==(const amount_t& amt) const {
return amounts.size() == 1 && (*amounts.begin()).second == amt;
}
bool operator!=(const balance_t& bal) const {
return ! (*this == bal);
}
bool operator!=(const amount_t& amt) const {
return ! (*this == amt);
}
// unary negation // unary negation
balance_t& negate() { balance_t& negate() {
for (amounts_map::iterator i = amounts.begin(); for (amounts_map::iterator i = amounts.begin();
@ -561,6 +582,25 @@ class balance_pair_t
return quantity >= amt; return quantity >= amt;
} }
bool operator==(const balance_pair_t& bal_pair) const {
return quantity == bal_pair.quantity;
}
bool operator==(const balance_t& bal) const {
return quantity == bal;
}
bool operator==(const amount_t& amt) const {
return quantity == amt;
}
bool operator!=(const balance_pair_t& bal_pair) const {
return ! (*this == bal_pair);
}
bool operator!=(const balance_t& bal) const {
return ! (*this == bal);
}
bool operator!=(const amount_t& amt) const {
return ! (*this == amt);
}
// unary negation // unary negation
balance_pair_t& negate() { balance_pair_t& negate() {
quantity.negate(); quantity.negate();

View file

@ -15,7 +15,7 @@
namespace ledger { namespace ledger {
unsigned long magic_number = 0xFFEED765; unsigned long magic_number = 0xFFEED765;
static unsigned long format_version = 0x00020008; static unsigned long format_version = 0x00020009;
static char buf[4096]; static char buf[4096];

View file

@ -129,15 +129,11 @@ balance_t node_t::compute(const item_t * item) const
break; break;
case CLEARED: case CLEARED:
#if 0 temp = amount_t(item->state == entry_t::CLEARED ? 1 : 0);
temp = amount_t(item->state == CLEARED ? 1 : 0);
#endif
break; break;
case REAL: case REAL:
#if 0
temp = amount_t(item->flags & TRANSACTION_VIRTUAL ? 0 : 1); temp = amount_t(item->flags & TRANSACTION_VIRTUAL ? 0 : 1);
#endif
break; break;
case INDEX: case INDEX:
@ -162,7 +158,7 @@ balance_t node_t::compute(const item_t * item) const
case F_PAYEE_MASK: case F_PAYEE_MASK:
assert(mask); assert(mask);
temp = mask->match(item->payee); temp = (mask->match(item->payee) || mask->match(item->note)) ? 1 : 0;
break; break;
case F_ACCOUNT_MASK: case F_ACCOUNT_MASK:

6
expr.h
View file

@ -136,7 +136,10 @@ class value_predicate
} else { } else {
item_t temp; item_t temp;
temp.date = xact->entry->date; temp.date = xact->entry->date;
temp.state = xact->entry->state;
temp.code = xact->entry->code;
temp.payee = xact->entry->payee; temp.payee = xact->entry->payee;
temp.flags = xact->flags;
temp.account = xact->account; temp.account = xact->account;
return predicate->compute(&temp); return predicate->compute(&temp);
} }
@ -149,6 +152,8 @@ class value_predicate
item_t temp; item_t temp;
temp.date = entry->date; temp.date = entry->date;
temp.payee = entry->payee; temp.payee = entry->payee;
temp.state = entry->state;
temp.code = entry->code;
// Although there may be conflicting account masks for the whole // Although there may be conflicting account masks for the whole
// set of transactions -- for example, /rent/&!/expenses/, which // set of transactions -- for example, /rent/&!/expenses/, which
@ -159,6 +164,7 @@ class value_predicate
for (transactions_list::const_iterator i = entry->transactions.begin(); for (transactions_list::const_iterator i = entry->transactions.begin();
i != entry->transactions.end(); i != entry->transactions.end();
i++) { i++) {
temp.flags = (*i)->flags;
temp.account = (*i)->account; temp.account = (*i)->account;
if (predicate->compute(&temp)) if (predicate->compute(&temp))
return true; return true;

View file

@ -24,8 +24,8 @@ std::string maximal_account_name(const item_t * item, const item_t * parent)
return name; return name;
} }
node_t * format_t::value_expr = NULL; std::auto_ptr<node_t> format_t::value_expr;
node_t * format_t::total_expr = NULL; std::auto_ptr<node_t> format_t::total_expr;
element_t * format_t::parse_elements(const std::string& fmt) element_t * format_t::parse_elements(const std::string& fmt)
{ {
@ -110,9 +110,12 @@ element_t * format_t::parse_elements(const std::string& fmt)
current->chars = "%Y/%m/%d"; current->chars = "%Y/%m/%d";
break; break;
case 'X': current->type = element_t::CLEARED; break;
case 'C': current->type = element_t::CODE; break;
case 'p': current->type = element_t::PAYEE; break; case 'p': current->type = element_t::PAYEE; break;
case 'n': current->type = element_t::ACCOUNT_NAME; break; case 'n': current->type = element_t::ACCOUNT_NAME; break;
case 'N': current->type = element_t::ACCOUNT_FULLNAME; break; case 'N': current->type = element_t::ACCOUNT_FULLNAME; break;
case 'o': current->type = element_t::OPT_AMOUNT; break;
case 't': current->type = element_t::VALUE; break; case 't': current->type = element_t::VALUE; break;
case 'T': current->type = element_t::TOTAL; break; case 'T': current->type = element_t::TOTAL; break;
case '_': current->type = element_t::SPACER; break; case '_': current->type = element_t::SPACER; break;
@ -174,28 +177,89 @@ void format_t::format_elements(std::ostream& out, const item_t * item,
} }
break; break;
case element_t::CLEARED:
if (item->state == entry_t::CLEARED)
out << "* ";
else
out << "";
break;
case element_t::CODE:
if (! item->code.empty())
out << "(" << item->code << ") ";
else
out << "";
break;
case element_t::PAYEE: case element_t::PAYEE:
out << (elem->max_width == 0 ? out << (elem->max_width == 0 ?
item->payee : truncated(item->payee, elem->max_width)); item->payee : truncated(item->payee, elem->max_width));
break; break;
case element_t::ACCOUNT_NAME: case element_t::ACCOUNT_NAME:
case element_t::ACCOUNT_FULLNAME:
if (item->account) { if (item->account) {
std::string name = maximal_account_name(item, displayed_parent); std::string name = (elem->type == element_t::ACCOUNT_FULLNAME ?
out << (elem->max_width == 0 ? name : truncated(name, elem->max_width)); item->account->fullname() :
maximal_account_name(item, displayed_parent));
if (elem->max_width > 0)
name = truncated(name, elem->max_width);
if (item->flags & TRANSACTION_VIRTUAL) {
if (item->flags & TRANSACTION_BALANCE)
name = "[" + name + "]";
else
name = "(" + name + ")";
}
out << name;
} else { } else {
out << " "; out << " ";
} }
break; break;
case element_t::ACCOUNT_FULLNAME: case element_t::OPT_AMOUNT: {
if (item->account) std::string disp;
out << (elem->max_width == 0 ? bool use_disp = false;
item->account->fullname() :
truncated(item->account->fullname(), elem->max_width)); if (std::find(displayed_parent->subitems.begin(),
displayed_parent->subitems.end(), item) !=
displayed_parent->subitems.end()) {
if (displayed_parent->subitems.size() == 2 &&
item == displayed_parent->subitems.back() &&
(displayed_parent->subitems.front()->value.quantity ==
displayed_parent->subitems.front()->value.cost) &&
(displayed_parent->subitems.front()->value ==
- displayed_parent->subitems.back()->value)) {
use_disp = true;
}
else if (displayed_parent->subitems.size() != 2 &&
item->value.quantity != item->value.cost &&
item->value.quantity.amounts.size() == 1 &&
item->value.cost.amounts.size() == 1 &&
((*item->value.quantity.amounts.begin()).first !=
(*item->value.cost.amounts.begin()).first)) {
amount_t unit_cost
= ((*item->value.cost.amounts.begin()).second /
(*item->value.quantity.amounts.begin()).second);
std::ostringstream stream;
stream << item->value.quantity << " @ " << unit_cost;
disp = stream.str();
use_disp = true;
}
}
if (use_disp)
out << disp;
else else
out << " "; item->value.quantity.write(out, elem->min_width,
elem->max_width > 0 ?
elem->max_width : elem->min_width);
// jww (2004-07-31): this should be handled differently
if (! item->note.empty())
out << " ; " << item->note;
break; break;
}
case element_t::VALUE: { case element_t::VALUE: {
balance_t value = compute_value(item); balance_t value = compute_value(item);

View file

@ -16,9 +16,12 @@ struct element_t
STRING, STRING,
VALUE_EXPR, VALUE_EXPR,
DATE_STRING, DATE_STRING,
CLEARED,
CODE,
PAYEE, PAYEE,
ACCOUNT_NAME, ACCOUNT_NAME,
ACCOUNT_FULLNAME, ACCOUNT_FULLNAME,
OPT_AMOUNT,
VALUE, VALUE,
TOTAL, TOTAL,
SPACER SPACER
@ -47,8 +50,8 @@ struct format_t
{ {
element_t * elements; element_t * elements;
static node_t * value_expr; static std::auto_ptr<node_t> value_expr;
static node_t * total_expr; static std::auto_ptr<node_t> total_expr;
format_t(const std::string& _format) { format_t(const std::string& _format) {
elements = parse_elements(_format); elements = parse_elements(_format);
@ -63,10 +66,10 @@ struct format_t
const item_t * displayed_parent = NULL) const; const item_t * displayed_parent = NULL) const;
static balance_t compute_value(const item_t * item) { static balance_t compute_value(const item_t * item) {
return value_expr ? value_expr->compute(item) : balance_t(); return value_expr.get() ? value_expr->compute(item) : balance_t();
} }
static balance_t compute_total(const item_t * item) { static balance_t compute_total(const item_t * item) {
return total_expr ? total_expr->compute(item) : balance_t(); return total_expr.get() ? total_expr->compute(item) : balance_t();
} }
}; };

169
item.cc
View file

@ -3,51 +3,6 @@
namespace ledger { namespace ledger {
// jww (2004-07-21): If format.show_empty is set, then include all
// subaccounts, empty, balanced or no
item_t * walk_accounts(const account_t * account,
const node_t * predicate,
const bool show_subtotals)
{
item_t * item = new item_t;
item->account = account;
std::time_t latest = 0;
for (transactions_list::const_iterator i
= std::find_if(account->transactions.begin(),
account->transactions.end(),
value_predicate(predicate));
i != account->transactions.end();
i = std::find_if(++i, account->transactions.end(),
value_predicate(predicate))) {
if (std::difftime(latest, (*i)->entry->date) < 0)
latest = (*i)->entry->date;
item->value += *(*i);
if (show_subtotals)
item->total += *(*i);
}
item->date = latest;
for (accounts_map::const_iterator i = account->accounts.begin();
i != account->accounts.end();
i++) {
item_t * subitem = walk_accounts((*i).second, predicate, show_subtotals);
subitem->parent = item;
if (std::difftime(item->date, subitem->date) < 0)
item->date = subitem->date;
if (show_subtotals)
item->total += subitem->total;
if (show_subtotals ? subitem->total : subitem->value)
item->subitems.push_back(subitem);
}
return item;
}
static inline void sum_items(const item_t * top, static inline void sum_items(const item_t * top,
const bool show_subtotals, const bool show_subtotals,
item_t * item) item_t * item)
@ -64,27 +19,88 @@ static inline void sum_items(const item_t * top,
sum_items(*i, show_subtotals, item); sum_items(*i, show_subtotals, item);
} }
item_t * walk_items(const item_t * top, item_t * walk_accounts(const item_t * top,
const account_t * account, account_t * account,
const node_t * predicate, const node_t * predicate,
const bool show_subtotals) const bool show_subtotals,
const bool show_flattened)
{ {
item_t * item = new item_t; item_t * item = new item_t;
item->account = account; item->account = account;
sum_items(top, show_subtotals, item); if (top) {
sum_items(top, show_subtotals, item);
} else {
std::time_t latest = 0;
for (transactions_list::iterator i
= std::find_if(account->transactions.begin(),
account->transactions.end(),
value_predicate(predicate));
i != account->transactions.end();
i = std::find_if(++i, account->transactions.end(),
value_predicate(predicate))) {
if (std::difftime(latest, (*i)->entry->date) < 0)
latest = (*i)->entry->date;
for (accounts_map::const_iterator i = account->accounts.begin(); item->value += *(*i);
if (show_subtotals)
item->total += *(*i);
}
item->date = latest;
}
for (accounts_map::iterator i = account->accounts.begin();
i != account->accounts.end(); i != account->accounts.end();
i++) { i++) {
item_t * subitem = walk_items(top, (*i).second, predicate, show_subtotals); std::auto_ptr<item_t>
subitem(walk_accounts(top, (*i).second, predicate, show_subtotals,
show_flattened));
subitem->parent = item; subitem->parent = item;
if (std::difftime(item->date, subitem->date) < 0)
item->date = subitem->date;
if (show_flattened) {
item_t * ptr = item;
balance_pair_t total;
for (items_deque::const_iterator i = subitem->subitems.begin();
i != subitem->subitems.end();
i++)
if (show_subtotals ? (*i)->total : (*i)->value) {
if (! account->parent) {
if (! total) {
item_t * temp = new item_t;
temp->date = top ? top->date : item->date;
temp->payee = "Opening balance";
item->subitems.push_back(temp);
ptr = temp;
}
total += show_subtotals ? (*i)->total : (*i)->value;
}
ptr->subitems.push_back(new item_t(*i));
ptr->subitems.back()->date = ptr->date;
ptr->subitems.back()->payee = ptr->payee;
}
if (total) {
item_t * temp = new item_t;
temp->date = ptr->date;
temp->payee = ptr->payee;
temp->account = account->find_account("Equity:Opening Balances");
temp->value = total;
temp->value.negate();
ptr->subitems.push_back(temp);
}
}
if (show_subtotals) if (show_subtotals)
item->total += subitem->total; item->total += subitem->total;
if (show_subtotals ? subitem->total : subitem->value) if ((! show_flattened || account->parent) &&
item->subitems.push_back(subitem); show_subtotals ? subitem->total : subitem->value)
item->subitems.push_back(subitem.release());
} }
return item; return item;
@ -103,6 +119,7 @@ item_t * walk_entries(entries_list::const_iterator begin,
for (entries_list::const_iterator i = std::find_if(begin, end, pred_obj); for (entries_list::const_iterator i = std::find_if(begin, end, pred_obj);
i != end; i != end;
i = std::find_if(++i, end, pred_obj)) { i = std::find_if(++i, end, pred_obj)) {
transactions_list reckoned;
item_t * item = NULL; item_t * item = NULL;
for (transactions_list::const_iterator j for (transactions_list::const_iterator j
@ -115,39 +132,32 @@ item_t * walk_entries(entries_list::const_iterator begin,
assert(*i == (*j)->entry); assert(*i == (*j)->entry);
if (! item) { if (! item) {
item = new item_t; item = new item_t(*i);
item->index = count++; item->index = count++;
item->date = (*i)->date;
item->payee = (*i)->payee;
} }
// If show_inverted is true, it implies show_related. // If show_inverted is true, it implies show_related.
if (! show_inverted) { if (! show_inverted &&
item_t * subitem = new item_t; std::find(reckoned.begin(),
subitem->parent = item; reckoned.end(), *j) == reckoned.end()) {
subitem->date = item->date; item->add_item(new item_t(*j));
subitem->payee = item->payee; reckoned.push_back(*j);
subitem->account = (*j)->account;
subitem->value = *(*j);
item->value += subitem->value;
item->subitems.push_back(subitem);
} }
if (show_related) if (show_related)
for (transactions_list::iterator k = (*i)->transactions.begin(); for (transactions_list::iterator k = (*i)->transactions.begin();
k != (*i)->transactions.end(); k != (*i)->transactions.end();
k++) k++) {
if (*k != *j && ! ((*k)->flags & TRANSACTION_VIRTUAL)) { if (*k == *j || ((*k)->flags & TRANSACTION_AUTO) ||
item_t * subitem = new item_t; std::find(reckoned.begin(),
subitem->parent = item; reckoned.end(), *k) != reckoned.end())
subitem->date = item->date; continue;
subitem->payee = item->payee;
subitem->account = (*k)->account; item->add_item(new item_t(*k));
subitem->value = *(*k); if (show_inverted)
if (show_inverted) item->subitems.back()->value.negate();
subitem->value.negate(); reckoned.push_back(*k);
item->subitems.push_back(subitem); }
}
} }
if (item) { if (item) {
@ -155,6 +165,9 @@ item_t * walk_entries(entries_list::const_iterator begin,
result = new item_t; result = new item_t;
item->parent = result; item->parent = result;
result->subitems.push_back(item); result->subitems.push_back(item);
if (std::difftime(result->date, item->date) < 0)
result->date = item->date;
} }
} }

64
item.h
View file

@ -14,17 +14,38 @@ typedef std::deque<item_t *> items_deque;
struct item_t struct item_t
{ {
struct item_t * parent; struct item_t * parent;
items_deque subitems; items_deque subitems;
unsigned int index; unsigned int index;
std::time_t date; std::time_t date;
std::string payee; entry_t::entry_state_t state;
const account_t * account; std::string code;
balance_pair_t value; std::string payee;
balance_pair_t total; unsigned int flags;
const account_t * account;
balance_pair_t value;
balance_pair_t total;
std::string note;
item_t() : parent(NULL), index(0), date(-1), account(NULL) {} item_t() : parent(NULL), index(0), date(-1),
state(entry_t::UNCLEARED), flags(0), account(NULL) {}
item_t(const item_t * item)
: parent(NULL), index(0), date(item->date), state(item->state),
code(item->code), payee(item->payee), flags(item->flags),
account(item->account), value(item->value), total(item->total),
note(item->note) {}
item_t(const entry_t * entry)
: parent(NULL), index(0), date(entry->date), state(entry->state),
code(entry->code), payee(entry->payee) {}
item_t(const transaction_t * xact)
: parent(NULL), index(0), date(xact->entry->date),
state(xact->entry->state), code(xact->entry->code),
payee(xact->entry->payee), flags(xact->flags),
account(xact->account), value(*xact), note(xact->note) {}
~item_t() { ~item_t() {
for (items_deque::iterator i = subitems.begin(); for (items_deque::iterator i = subitems.begin();
@ -33,25 +54,28 @@ struct item_t
delete *i; delete *i;
} }
void add_item(item_t * item) {
item->parent = this;
value += item->value;
subitems.push_back(item);
}
void sort(const node_t * sort_order); void sort(const node_t * sort_order);
}; };
struct node_t; struct node_t;
item_t * walk_accounts(const account_t * account, item_t * walk_accounts(const item_t * top,
const node_t * predicate, account_t * account,
const bool show_subtotals); const node_t * predicate = NULL,
const bool show_subtotals = true,
item_t * walk_items(const item_t * top, const bool show_flattened = false);
const account_t * account,
const node_t * predicate,
const bool show_subtotals);
item_t * walk_entries(entries_list::const_iterator begin, item_t * walk_entries(entries_list::const_iterator begin,
entries_list::const_iterator end, entries_list::const_iterator end,
const node_t * predicate, const node_t * predicate = NULL,
const bool show_related, const bool show_related = false,
const bool show_inverted); const bool show_inverted = false);
} // namespace report } // namespace report

128
ledger.cc
View file

@ -1,4 +1,5 @@
#include "ledger.h" #include "ledger.h"
#include "expr.h"
#include "textual.h" #include "textual.h"
#include "binary.h" #include "binary.h"
@ -52,6 +53,133 @@ bool ledger_t::remove_entry(entry_t * entry)
return true; return true;
} }
entry_t * ledger_t::derive_entry(int argc, char **argv) const
{
entry_t * added = new entry_t;
entry_t * matching = NULL;
int index = 0;
assert(index < argc);
if (! parse_date(argv[index++], &added->date)) {
std::cerr << "Error: Bad entry date: " << argv[index - 1]
<< std::endl;
return false;
}
if (index == argc) {
std::cerr << "Error: Too few arguments to 'entry'." << std::endl;
return false;
}
mask_t regexp(argv[index++]);
for (entries_list::const_reverse_iterator i = entries.rbegin();
i != entries.rend();
i++)
if (regexp.match((*i)->payee)) {
matching = *i;
break;
}
added->payee = matching ? matching->payee : regexp.pattern;
if (index == argc) {
std::cerr << "Error: Too few arguments to 'entry'." << std::endl;
return false;
}
if (argv[index][0] == '-' || std::isdigit(argv[index][0])) {
if (! matching) {
std::cerr << "Error: Missing account name for non-matching entry."
<< std::endl;
return false;
}
transaction_t * m_xact, * xact, * first;
m_xact = matching->transactions.front();
amount_t amt(argv[index++]);
first = xact = new transaction_t(added, m_xact->account, amt, amt);
if (xact->amount.commodity->symbol.empty()) {
xact->amount.commodity = m_xact->amount.commodity;
xact->cost.commodity = m_xact->amount.commodity;
}
added->add_transaction(xact);
m_xact = matching->transactions.back();
xact = new transaction_t(added, m_xact->account,
- first->amount, - first->amount);
added->add_transaction(xact);
if ((index + 1) < argc && std::string(argv[index]) == "-from")
if (account_t * acct = find_account(argv[++index]))
added->transactions.back()->account = acct;
} else {
while (index < argc && std::string(argv[index]) != "-from") {
mask_t acct_regex(argv[index++]);
account_t * acct = NULL;
commodity_t * cmdty = NULL;
if (matching) {
for (transactions_list::iterator x
= matching->transactions.begin();
x != matching->transactions.end();
x++) {
if (acct_regex.match((*x)->account->fullname())) {
acct = (*x)->account;
cmdty = (*x)->amount.commodity;
break;
}
}
}
if (! acct)
acct = find_account(acct_regex.pattern);
if (! acct) {
std::cerr << "Error: Could not find account name '"
<< acct_regex.pattern << "'." << std::endl;
return false;
}
if (index == argc) {
std::cerr << "Error: Too few arguments to 'entry'." << std::endl;
return false;
}
amount_t amt(argv[index]++);
transaction_t * xact = new transaction_t(added, acct, amt, amt);
if (! xact->amount.commodity)
xact->amount.commodity = cmdty;
added->add_transaction(xact);
}
if ((index + 1) < argc && std::string(argv[index]) == "-from") {
if (account_t * acct = find_account(argv[++index])) {
transaction_t * xact = new transaction_t(NULL, acct);
added->add_transaction(xact);
}
} else {
if (! matching) {
std::cerr << "Error: Could not figure out the account to draw from."
<< std::endl;
std::exit(1);
}
transaction_t * xact
= new transaction_t(added, matching->transactions.back()->account);
added->add_transaction(xact);
}
}
return added;
}
int parse_ledger_file(char * p, ledger_t * journal) int parse_ledger_file(char * p, ledger_t * journal)
{ {
char * sep = std::strrchr(p, '='); char * sep = std::strrchr(p, '=');

View file

@ -288,6 +288,7 @@ class commodity_t
#define TRANSACTION_NORMAL 0x0 #define TRANSACTION_NORMAL 0x0
#define TRANSACTION_VIRTUAL 0x1 #define TRANSACTION_VIRTUAL 0x1
#define TRANSACTION_BALANCE 0x2 #define TRANSACTION_BALANCE 0x2
#define TRANSACTION_AUTO 0x4
class transaction_t class transaction_t
{ {
@ -322,11 +323,13 @@ class entry_t
UNCLEARED, CLEARED, PENDING UNCLEARED, CLEARED, PENDING
}; };
std::time_t date; std::time_t date;
enum entry_state_t state; entry_state_t state;
std::string code; std::string code;
std::string payee; std::string payee;
transactions_list transactions; transactions_list transactions;
entry_t() : date(-1), state(UNCLEARED) {}
~entry_t() { ~entry_t() {
for (transactions_list::iterator i = transactions.begin(); for (transactions_list::iterator i = transactions.begin();
@ -429,9 +432,14 @@ class ledger_t
account_t * find_account(const std::string& name, bool auto_create = true) { account_t * find_account(const std::string& name, bool auto_create = true) {
return master->find_account(name, auto_create); return master->find_account(name, auto_create);
} }
account_t * find_account(const std::string& name) const {
return master->find_account(name, false);
}
bool add_entry(entry_t * entry); bool add_entry(entry_t * entry);
bool remove_entry(entry_t * entry); bool remove_entry(entry_t * entry);
entry_t * derive_entry(int argc, char **argv) const;
}; };
int parse_ledger_file(char * p, ledger_t * journal); int parse_ledger_file(char * p, ledger_t * journal);

508
main.cc
View file

@ -83,11 +83,15 @@ void balance_report(std::ostream& out,
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// //
// The command-line register report // The command-line register and print report
// //
static const std::string reg_fmt static const std::string reg_fmt
= "%10d %-.20p %-.22N %12.66t %12.80T\n%/%22_ %-.22N %12.66t %12.80T\n"; = "%10d %-.20p %-.22N %12.66t %12.80T\n\
%/ %-.22N %12.66t %12.80T\n";
static const std::string print_fmt
= "\n%10d %X%C%p\n %-34N %12o\n%/ %-34N %12o\n";
static bool show_commodities_revalued = false; static bool show_commodities_revalued = false;
static bool show_commodities_revalued_only = false; static bool show_commodities_revalued_only = false;
@ -151,11 +155,11 @@ void register_report(std::ostream& out,
bool first = true; bool first = true;
if ((*i)->subitems.size() > 1 && ! show_expanded) { if ((*i)->subitems.size() > 1 && ! show_expanded) {
item_t summary; item_t summary(*i);
summary.date = (*i)->date;
summary.parent = *i; summary.parent = *i;
summary.account = &splits; summary.account = &splits;
summary.value = 0;
for (items_deque::const_iterator j = (*i)->subitems.begin(); for (items_deque::const_iterator j = (*i)->subitems.begin();
j != (*i)->subitems.end(); j != (*i)->subitems.end();
j++) j++)
@ -171,7 +175,7 @@ void register_report(std::ostream& out,
if (show) { if (show) {
if (! show_commodities_revalued_only) if (! show_commodities_revalued_only)
first_line_format.format_elements(out, *i, top); first_line_format.format_elements(out, &summary, top);
if (show_commodities_revalued) if (show_commodities_revalued)
last_reported = balance; last_reported = balance;
@ -215,139 +219,6 @@ void register_report(std::ostream& out,
} }
bool add_new_entry(int index, int argc, char **argv, ledger_t * ledger)
{
masks_list regexps;
entry_t added;
entry_t * matching = NULL;
added.state = entry_t::UNCLEARED;
assert(index < argc);
if (! parse_date(argv[index++], &added.date)) {
std::cerr << "Error: Bad entry date: " << argv[index - 1]
<< std::endl;
return false;
}
if (index == argc) {
std::cerr << "Error: Too few arguments to 'entry'." << std::endl;
return false;
}
regexps.push_back(mask_t(argv[index++]));
for (entries_list::reverse_iterator i = ledger->entries.rbegin();
i != ledger->entries.rend();
i++)
if (matches(regexps, (*i)->payee)) {
matching = *i;
break;
}
added.payee = matching ? matching->payee : regexps.front().pattern;
if (index == argc) {
std::cerr << "Error: Too few arguments to 'entry'." << std::endl;
return false;
}
if (argv[index][0] == '-' || std::isdigit(argv[index][0])) {
if (! matching) {
std::cerr << "Error: Missing account name for non-matching entry."
<< std::endl;
return false;
}
transaction_t * m_xact, * xact, * first;
m_xact = matching->transactions.front();
amount_t amt(argv[index++]);
first = xact = new transaction_t(&added, m_xact->account, amt, amt);
if (xact->amount.commodity->symbol.empty()) {
xact->amount.commodity = m_xact->amount.commodity;
xact->cost.commodity = m_xact->amount.commodity;
}
added.add_transaction(xact);
m_xact = matching->transactions.back();
xact = new transaction_t(&added, m_xact->account,
- first->amount, - first->amount);
added.add_transaction(xact);
if ((index + 1) < argc && std::string(argv[index]) == "-from")
if (account_t * acct = ledger->find_account(argv[++index]))
added.transactions.back()->account = acct;
} else {
while (index < argc && std::string(argv[index]) != "-from") {
mask_t acct_regex(argv[index++]);
account_t * acct = NULL;
commodity_t * cmdty = NULL;
if (matching) {
for (transactions_list::iterator x
= matching->transactions.begin();
x != matching->transactions.end();
x++) {
if (acct_regex.match((*x)->account->fullname())) {
acct = (*x)->account;
cmdty = (*x)->amount.commodity;
break;
}
}
}
if (! acct)
acct = ledger->find_account(acct_regex.pattern);
if (! acct) {
std::cerr << "Error: Could not find account name '"
<< acct_regex.pattern << "'." << std::endl;
return false;
}
if (index == argc) {
std::cerr << "Error: Too few arguments to 'entry'." << std::endl;
return false;
}
amount_t amt(argv[index]++);
transaction_t * xact = new transaction_t(&added, acct, amt, amt);
if (! xact->amount.commodity)
xact->amount.commodity = cmdty;
added.add_transaction(xact);
}
if ((index + 1) < argc && std::string(argv[index]) == "-from") {
if (account_t * acct = ledger->find_account(argv[++index])) {
transaction_t * xact = new transaction_t(NULL, acct);
added.add_transaction(xact);
}
} else {
if (! matching) {
std::cerr << "Error: Could not figure out the account to draw from."
<< std::endl;
std::exit(1);
}
transaction_t * xact
= new transaction_t(&added, matching->transactions.back()->account);
added.add_transaction(xact);
}
}
// if (added.finalize())
print_textual_entry(std::cout, &added);
return true;
}
void set_price_conversion(const std::string& setting) void set_price_conversion(const std::string& setting)
{ {
char buf[128]; char buf[128];
@ -468,23 +339,24 @@ static void show_help(std::ostream& out)
<< " balance show balance totals" << std::endl << " balance show balance totals" << std::endl
<< " register display a register for ACCOUNT" << std::endl << " register display a register for ACCOUNT" << std::endl
<< " print print all ledger entries" << std::endl << " print print all ledger entries" << std::endl
<< " equity generate equity ledger for all entries" << std::endl
<< " entry output a newly formed entry, based on arguments" << std::endl << " entry output a newly formed entry, based on arguments" << std::endl
<< " price show the last known price for matching commodities" << std::endl; << " equity output equity entries for specified accounts" << std::endl;
} }
int main(int argc, char * argv[]) int main(int argc, char * argv[])
{ {
std::list<std::string> files; std::auto_ptr<ledger::ledger_t> journal(new ledger::ledger_t);
std::list<std::string> files;
std::auto_ptr<ledger::node_t> predicate;
std::auto_ptr<ledger::node_t> display_predicate;
std::auto_ptr<ledger::node_t> sort_order;
std::string predicate_string; std::string predicate_string;
ledger::node_t * predicate = NULL; std::string display_predicate_string;
std::string format_string; std::string format_string;
std::string sort_string; std::string sort_string;
ledger::node_t * sort_order = NULL; std::string value_expr = "a";
std::string value_expr = "a"; std::string total_expr = "T";
std::string total_expr = "T";
ledger::ledger_t * journal = new ledger::ledger_t;
bool show_subtotals = true; bool show_subtotals = true;
bool show_expanded = false; bool show_expanded = false;
@ -521,11 +393,9 @@ int main(int argc, char * argv[])
if (access(p, R_OK) != -1) { if (access(p, R_OK) != -1) {
std::ifstream instr(p); std::ifstream instr(p);
if (! ledger::read_binary_ledger(instr, std::getenv("LEDGER"), if (! ledger::read_binary_ledger(instr, std::getenv("LEDGER"),
journal)) { journal.get())) {
// We need to throw away what we've read, and create a new // Throw away what's been read, and create a new journal
// ledger journal.reset(new ledger::ledger_t);
delete journal;
journal = new ledger::ledger_t;
} else { } else {
ledger::cache_dirty = false; ledger::cache_dirty = false;
} }
@ -652,6 +522,14 @@ int main(int argc, char * argv[])
predicate_string += ")"; predicate_string += ")";
break; break;
case 'd':
if (! display_predicate_string.empty())
display_predicate_string += "&";
display_predicate_string += "(";
display_predicate_string += optarg;
display_predicate_string += ")";
break;
// Commodity reporting // Commodity reporting
case 'P': case 'P':
ledger::price_db = optarg; ledger::price_db = optarg;
@ -747,14 +625,14 @@ int main(int argc, char * argv[])
if (files.empty()) { if (files.empty()) {
if (char * p = std::getenv("LEDGER")) if (char * p = std::getenv("LEDGER"))
for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":")) for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":"))
entry_count += parse_ledger_file(p, journal); entry_count += parse_ledger_file(p, journal.get());
} else { } else {
for (std::list<std::string>::iterator i = files.begin(); for (std::list<std::string>::iterator i = files.begin();
i != files.end(); i++) { i != files.end(); i++) {
char buf[4096]; char buf[4096];
char * p = buf; char * p = buf;
std::strcpy(p, (*i).c_str()); std::strcpy(p, (*i).c_str());
entry_count += parse_ledger_file(p, journal); entry_count += parse_ledger_file(p, journal.get());
} }
} }
@ -765,7 +643,8 @@ int main(int argc, char * argv[])
const char * path = ledger::price_db.c_str(); const char * path = ledger::price_db.c_str();
std::ifstream db(path); std::ifstream db(path);
journal->sources.push_back(path); journal->sources.push_back(path);
entry_count += ledger::parse_textual_ledger(db, journal, journal->master); entry_count += ledger::parse_textual_ledger(db, journal.get(),
journal->master);
} }
} }
catch (ledger::error& err) { catch (ledger::error& err) {
@ -780,52 +659,98 @@ int main(int argc, char * argv[])
} }
} }
// Read the command word, and handle the "entry" command specially, // Read the command word, and then check and simplify it
// without any other processing.
const std::string command = argv[index++]; std::string command = argv[index++];
if (command == "entry") if (command == "balance" || command == "bal" || command == "b")
return add_new_entry(index, argc, argv, journal) ? 0 : 1; command = "b";
else if (command == "register" || command == "reg" || command == "r")
// Interpret the remaining arguments as regular expressions, used command = "r";
// for refining report results. else if (command == "print" || command == "p")
command = "p";
for (; index < argc; index++) { else if (command == "entry")
if (std::strcmp(argv[index], "--") == 0) { command = "e";
index++; else if (command == "equity")
break; command = "E";
} else {
std::cerr << "Error: Unrecognized command '" << command << "'."
show_expanded = true; << std::endl;
return 1;
if (! predicate_string.empty())
predicate_string += "&";
if (argv[index][0] == '-') {
predicate_string += "(!/";
predicate_string += argv[index] + 1;
} else {
predicate_string += "(/";
predicate_string += argv[index];
}
predicate_string += "/)";
} }
for (; index < argc; index++) { // Process the remaining command-line arguments
show_expanded = true;
if (! predicate_string.empty()) std::auto_ptr<ledger::entry_t> new_entry;
predicate_string += "&"; if (command == "entry") {
new_entry.reset(journal->derive_entry(argc - index, &argv[index]));
} else {
// Treat the remaining command-line arguments as regular
// expressions, used for refining report results.
if (argv[index][0] == '-') { bool have_regexps = index < argc;
predicate_string += "(!//"; bool first = true;
predicate_string += argv[index] + 1;
} else { for (; index < argc; index++) {
predicate_string += "(//"; if (std::strcmp(argv[index], "--") == 0) {
predicate_string += argv[index]; index++;
if (! first && index < argc)
predicate_string += ")";
break;
}
if (! show_expanded && command == "b")
show_expanded = true;
if (first) {
if (! predicate_string.empty())
predicate_string += "&(";
else
predicate_string += "(";
first = false;
} else {
predicate_string += "|";
}
if (argv[index][0] == '-') {
predicate_string += "!/";
predicate_string += argv[index] + 1;
} else {
predicate_string += "/";
predicate_string += argv[index];
}
predicate_string += "/";
} }
predicate_string += "/)";
if (index < argc) {
if (! predicate_string.empty())
predicate_string += "&(";
else
predicate_string += "(";
}
first = true;
for (; index < argc; index++) {
if (! show_expanded && command == "b")
show_expanded = true;
if (first)
first = false;
else
predicate_string += "|";
if (argv[index][0] == '-') {
predicate_string += "!//";
predicate_string += argv[index] + 1;
} else {
predicate_string += "//";
predicate_string += argv[index];
}
predicate_string += "/";
}
if (have_regexps)
predicate_string += ")";
} }
// Compile the predicate // Compile the predicate
@ -835,122 +760,110 @@ int main(int argc, char * argv[])
if (debug) if (debug)
std::cerr << "predicate = " << predicate_string << std::endl; std::cerr << "predicate = " << predicate_string << std::endl;
#endif #endif
predicate = ledger::parse_expr(predicate_string); predicate.reset(ledger::parse_expr(predicate_string));
}
if (! display_predicate_string.empty()) {
#ifdef DEBUG
if (debug)
std::cerr << "display predicate = " << display_predicate_string
<< std::endl;
#endif
display_predicate.reset(ledger::parse_expr(display_predicate_string));
} }
// Compile the sorting string // Compile the sorting string
if (! sort_string.empty()) if (! sort_string.empty())
sort_order = ledger::parse_expr(sort_string); sort_order.reset(ledger::parse_expr(sort_string));
// Setup the meaning of %t and %T encountered in format strings // Setup the meaning of %t and %T encountered in format strings
ledger::format_t::value_expr = ledger::parse_expr(value_expr); ledger::format_t::value_expr.reset(ledger::parse_expr(value_expr));
ledger::format_t::total_expr = ledger::parse_expr(total_expr); ledger::format_t::total_expr.reset(ledger::parse_expr(total_expr));
// Now handle the command that was identified above. // Now handle the command that was identified above.
if (command == "print") { if (command == "p" || command == "e") {
#if 0 show_related = true;
if (ledger::item_t * top show_expanded = true;
= ledger::walk_entries(journal->entries.begin(),
journal->entries.end(), predicate,
show_related, show_inverted)) {
ledger::format_t * format = new ledger::format_t(format_string);
ledger::entry_report(std::cout, top, *format);
#ifdef DEBUG
delete top;
delete format;
#endif
}
#endif
} }
else if (command == "equity") { else if (command == "E") {
#if 0 show_expanded = true;
if (ledger::item_t * top
= ledger::walk_accounts(journal->master, predicate, show_subtotals)) {
ledger::format_t * format = new ledger::format_t(format_string);
ledger::entry_report(std::cout, top, predicate, *format);
#ifdef DEBUG
delete top;
delete format;
#endif
}
#endif
} }
else if (! sort_order && ! show_related && else if (show_related && command == "r") {
(command == "balance" || command == "bal")) { show_inverted = true;
if (ledger::item_t * top
= ledger::walk_accounts(journal->master, predicate, show_subtotals)) {
ledger::format_t * format
= new ledger::format_t(format_string.empty() ?
ledger::bal_fmt : format_string);
ledger::balance_report(std::cout, top, predicate, sort_order, *format,
show_expanded, show_subtotals);
#ifdef DEBUG
delete format;
delete top;
#endif
}
} }
else if (command == "balance" || command == "bal") {
if (ledger::item_t * list
= ledger::walk_entries(journal->entries.begin(),
journal->entries.end(), predicate,
show_related, show_inverted))
if (ledger::item_t * top
= ledger::walk_items(list, journal->master, predicate,
show_subtotals)) {
ledger::format_t * format
= new ledger::format_t(format_string.empty() ?
ledger::bal_fmt : format_string);
ledger::balance_report(std::cout, top, predicate, sort_order, *format,
show_expanded, show_subtotals);
#ifdef DEBUG
delete format;
delete top;
delete list;
#endif
}
}
else if (command == "register" || command == "reg") {
if (show_related)
show_inverted = true;
if (ledger::item_t * top std::auto_ptr<ledger::item_t> top;
= ledger::walk_entries(journal->entries.begin(), std::auto_ptr<ledger::item_t> list;
journal->entries.end(), predicate,
show_related, show_inverted)) { if (command == "e") {
top.reset(new ledger::item_t);
ledger::item_t * item = new ledger::item_t(new_entry.get());
for (ledger::transactions_list::const_iterator i
= new_entry->transactions.begin();
i != new_entry->transactions.end();
i++)
item->add_item(new ledger::item_t(*i));
top->add_item(item);
}
else if ((! show_related || ! predicate.get()) &&
(command == "b" || command == "E")) {
top.reset(ledger::walk_accounts(NULL, journal->master, predicate.get(),
command != "E" && show_subtotals,
command == "E"));
}
else {
top.reset(ledger::walk_entries(journal->entries.begin(),
journal->entries.end(), predicate.get(),
show_related, show_inverted));
if (top.get() && command == "b" || command == "E") {
list.reset(top.release());
top.reset(ledger::walk_accounts(list.get(), journal->master,
predicate.get(),
command != "E" && show_subtotals,
command == "E"));
}
}
if (top.get()) {
const char * f;
if (! format_string.empty())
f = format_string.c_str();
else if (command == "b")
f = ledger::bal_fmt.c_str();
else if (command == "r")
f = ledger::reg_fmt.c_str();
else
f = ledger::print_fmt.c_str();
if (command == "b") {
std::auto_ptr<ledger::format_t> format(new ledger::format_t(f));
ledger::balance_report(std::cout, top.get(), display_predicate.get(),
sort_order.get(), *format, show_expanded,
show_subtotals);
} else {
std::string first_line_format; std::string first_line_format;
std::string next_lines_format; std::string next_lines_format;
const char * f = (format_string.empty() ?
ledger::reg_fmt.c_str() : format_string.c_str());
if (const char * p = std::strstr(f, "%/")) { if (const char * p = std::strstr(f, "%/")) {
first_line_format = std::string(f, 0, p - f); first_line_format = std::string(f, 0, p - f);
next_lines_format = std::string(p + 2); next_lines_format = std::string(p + 2);
} else { } else {
first_line_format = format_string; first_line_format = next_lines_format = f;
next_lines_format = format_string;
} }
ledger::format_t * format = new ledger::format_t(first_line_format); std::auto_ptr<ledger::format_t>
ledger::format_t * nformat = new ledger::format_t(next_lines_format); format(new ledger::format_t(first_line_format));
std::auto_ptr<ledger::format_t>
nformat(new ledger::format_t(next_lines_format));
ledger::register_report(std::cout, top, predicate, sort_order, ledger::register_report(std::cout, top.get(), display_predicate.get(),
*format, *nformat, show_expanded); sort_order.get(), *format, *nformat,
#ifdef DEBUG show_expanded);
delete nformat;
delete format;
delete top;
#endif
} }
} }
else {
std::cerr << "Error: Unrecognized command '" << command << "'."
<< std::endl;
return 1;
}
// Save the cache, if need be // Save the cache, if need be
@ -958,31 +871,10 @@ int main(int argc, char * argv[])
if (const char * p = std::getenv("LEDGER_CACHE")) { if (const char * p = std::getenv("LEDGER_CACHE")) {
std::ofstream outstr(p); std::ofstream outstr(p);
assert(std::getenv("LEDGER")); assert(std::getenv("LEDGER"));
ledger::write_binary_ledger(outstr, journal, std::getenv("LEDGER")); ledger::write_binary_ledger(outstr, journal.get(),
std::getenv("LEDGER"));
} }
#ifdef DEBUG
delete journal;
if (predicate)
delete predicate;
if (sort_order)
delete sort_order;
if (ledger::format_t::value_expr)
delete ledger::format_t::value_expr;
if (ledger::format_t::total_expr)
delete ledger::format_t::total_expr;
// jww (2004-07-30): This should be moved into some kind of
// "ledger::shutdown" function.
for (ledger::commodities_map::iterator i
= ledger::commodity_t::commodities.begin();
i != ledger::commodity_t::commodities.end();
i++)
delete (*i).second;
#endif
return 0; return 0;
} }

View file

@ -294,7 +294,8 @@ void automated_transaction_t::extend_entry(entry_t * entry)
amt = (*t)->amount; amt = (*t)->amount;
transaction_t * xact transaction_t * xact
= new transaction_t(entry, (*t)->account, amt, amt, (*t)->flags); = new transaction_t(entry, (*t)->account, amt, amt,
(*t)->flags | TRANSACTION_AUTO);
entry->add_transaction(xact); entry->add_transaction(xact);
} }
} }
@ -603,8 +604,9 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
amt.parse(buf); amt.parse(buf);
time_commodity = amt.commodity; time_commodity = amt.commodity;
transaction_t * xact = new transaction_t(curr, last_account, amt, amt, transaction_t * xact
TRANSACTION_VIRTUAL); = new transaction_t(curr, last_account, amt, amt,
TRANSACTION_VIRTUAL);
curr->add_transaction(xact); curr->add_transaction(xact);
if (! finalize_entry(curr) || ! journal->add_entry(curr)) if (! finalize_entry(curr) || ! journal->add_entry(curr))
@ -777,123 +779,4 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
return count; return count;
} }
//////////////////////////////////////////////////////////////////////
//
// Textual ledger printing code
//
void print_transaction(std::ostream& out, transaction_t * xact,
bool display_amount, bool display_cost)
{
std::ostringstream s;
s << *(xact->account);
std::string acct_name = s.str();
if (xact->flags & TRANSACTION_VIRTUAL) {
if (xact->flags & TRANSACTION_BALANCE)
acct_name = std::string("[") + acct_name + "]";
else
acct_name = std::string("(") + acct_name + ")";
}
out.width(30);
out.fill(' ');
out << std::left << acct_name;
if (xact->amount && display_amount) {
out << " ";
out.width(12);
out.fill(' ');
std::ostringstream s;
s << xact->amount;
out << std::right << s.str();
}
if (xact->amount && display_cost &&
xact->amount != xact->cost) {
out << " @ ";
out << xact->cost / xact->amount;
}
if (! xact->note.empty())
out << " ; " << xact->note;
out << std::endl;
}
void print_textual_entry(std::ostream& out, entry_t * entry, bool shortcut)
{
char buf[32];
std::strftime(buf, 31, "%Y/%m/%d ", std::gmtime(&entry->date));
out << buf;
if (entry->state == entry_t::CLEARED)
out << "* ";
if (! entry->code.empty())
out << '(' << entry->code << ") ";
if (! entry->payee.empty())
out << entry->payee;
out << std::endl;
const commodity_t * comm = NULL;
int size = 0;
for (transactions_list::const_iterator x
= entry->transactions.begin();
x != entry->transactions.end();
x++) {
if ((*x)->flags & TRANSACTION_VIRTUAL &&
! ((*x)->flags & TRANSACTION_BALANCE))
continue;
if (! comm)
comm = (*x)->amount.commodity;
else if (comm != (*x)->amount.commodity)
shortcut = false;
size++;
}
if (shortcut && size != 2)
shortcut = false;
for (transactions_list::const_iterator x
= entry->transactions.begin();
x != entry->transactions.end();
x++) {
out << " ";
print_transaction(out, *x,
(! shortcut || x == entry->transactions.begin() ||
((*x)->flags & TRANSACTION_VIRTUAL &&
! ((*x)->flags & TRANSACTION_BALANCE))),
size != 2);
}
out << std::endl;
}
void print_textual_ledger(std::ostream& out, ledger_t * journal,
bool shortcut)
{
for (entries_list::const_iterator i = journal->entries.begin();
i != journal->entries.end();
i++)
print_textual_entry(out, *i, shortcut);
}
} // namespace ledger } // namespace ledger
#ifdef PARSE_TEST
int main(int argc, char *argv[])
{
journal.sources.push_back(argv[1]);
std::ifstream stream(argv[1]);
ledger::ledger_t journal;
int count = parse_textual_ledger(stream, &journal, journal.master);
std::cout << "Read " << count << " entries." << std::endl;
print_textual_ledger(std::cout, &journal, true);
}
#endif // PARSE_TEST

View file

@ -13,12 +13,6 @@ extern bool parse_date_mask(const char * date_str, struct std::tm * result);
extern bool parse_date(const char * date_str, std::time_t * result, extern bool parse_date(const char * date_str, std::time_t * result,
const int year = -1); const int year = -1);
extern void print_textual_ledger(std::ostream& out, ledger_t * ledger,
bool shortcut = true);
extern void print_textual_entry(std::ostream& out, entry_t * entry,
bool shortcut = true);
} // namespace ledger } // namespace ledger
#endif // _TEXTUAL_H #endif // _TEXTUAL_H