brought back the "print" and "equity" reports; this time much better!
This commit is contained in:
parent
2a9e778b7d
commit
f570e6644f
14 changed files with 637 additions and 571 deletions
37
amount.cc
37
amount.cc
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
40
balance.h
40
balance.h
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
|
|
||||||
|
|
|
||||||
8
expr.cc
8
expr.cc
|
|
@ -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
6
expr.h
|
|
@ -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;
|
||||||
|
|
|
||||||
84
format.cc
84
format.cc
|
|
@ -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);
|
||||||
|
|
|
||||||
11
format.h
11
format.h
|
|
@ -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
169
item.cc
|
|
@ -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
64
item.h
|
|
@ -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
128
ledger.cc
|
|
@ -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, '=');
|
||||||
|
|
|
||||||
18
ledger.h
18
ledger.h
|
|
@ -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
508
main.cc
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
127
textual.cc
127
textual.cc
|
|
@ -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
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue