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 {
|
||||
|
||||
static void mpz_round(mpz_t value, int precision)
|
||||
static void mpz_round(mpz_t out, mpz_t value, int precision)
|
||||
{
|
||||
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_add(remainder, divisor, remainder);
|
||||
mpz_ui_sub(remainder, 0, remainder);
|
||||
mpz_add(value, value, remainder);
|
||||
mpz_add(out, value, remainder);
|
||||
} else {
|
||||
mpz_sub(value, value, remainder);
|
||||
mpz_sub(out, value, remainder);
|
||||
}
|
||||
} else {
|
||||
if (mpz_cmp(remainder, divisor) >= 0) {
|
||||
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - precision);
|
||||
mpz_sub(remainder, divisor, remainder);
|
||||
mpz_add(value, value, remainder);
|
||||
mpz_add(out, value, remainder);
|
||||
} 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;
|
||||
} else {
|
||||
amount_t temp = *this;
|
||||
mpz_round(MPZ(temp.quantity),
|
||||
mpz_round(MPZ(temp.quantity), MPZ(temp.quantity),
|
||||
precision == -1 ? commodity->precision : precision);
|
||||
return temp;
|
||||
}
|
||||
|
|
@ -380,8 +380,14 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
|
|||
|
||||
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_tdiv_qr(quotient, remainder, MPZ(amt.quantity), divisor);
|
||||
mpz_tdiv_qr(quotient, remainder, rquotient, divisor);
|
||||
|
||||
if (mpz_sgn(quotient) < 0 || mpz_sgn(remainder) < 0)
|
||||
negative = true;
|
||||
|
|
@ -392,10 +398,6 @@ std::ostream& operator<<(std::ostream& out, const amount_t& amt)
|
|||
if (amt.commodity->precision == MAX_PRECISION) {
|
||||
mpz_set(rquotient, remainder);
|
||||
} 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);
|
||||
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - amt.commodity->precision);
|
||||
mpz_tdiv_qr(rquotient, remainder, remainder, divisor);
|
||||
|
|
@ -681,6 +683,19 @@ void (*commodity_t::updater)(commodity_t * commodity,
|
|||
|
||||
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,
|
||||
bool auto_create)
|
||||
{
|
||||
|
|
|
|||
40
balance.h
40
balance.h
|
|
@ -274,6 +274,27 @@ class balance_t
|
|||
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
|
||||
balance_t& negate() {
|
||||
for (amounts_map::iterator i = amounts.begin();
|
||||
|
|
@ -561,6 +582,25 @@ class balance_pair_t
|
|||
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
|
||||
balance_pair_t& negate() {
|
||||
quantity.negate();
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
namespace ledger {
|
||||
|
||||
unsigned long magic_number = 0xFFEED765;
|
||||
static unsigned long format_version = 0x00020008;
|
||||
static unsigned long format_version = 0x00020009;
|
||||
|
||||
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;
|
||||
|
||||
case CLEARED:
|
||||
#if 0
|
||||
temp = amount_t(item->state == CLEARED ? 1 : 0);
|
||||
#endif
|
||||
temp = amount_t(item->state == entry_t::CLEARED ? 1 : 0);
|
||||
break;
|
||||
|
||||
case REAL:
|
||||
#if 0
|
||||
temp = amount_t(item->flags & TRANSACTION_VIRTUAL ? 0 : 1);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case INDEX:
|
||||
|
|
@ -162,7 +158,7 @@ balance_t node_t::compute(const item_t * item) const
|
|||
|
||||
case F_PAYEE_MASK:
|
||||
assert(mask);
|
||||
temp = mask->match(item->payee);
|
||||
temp = (mask->match(item->payee) || mask->match(item->note)) ? 1 : 0;
|
||||
break;
|
||||
|
||||
case F_ACCOUNT_MASK:
|
||||
|
|
|
|||
6
expr.h
6
expr.h
|
|
@ -136,7 +136,10 @@ class value_predicate
|
|||
} else {
|
||||
item_t temp;
|
||||
temp.date = xact->entry->date;
|
||||
temp.state = xact->entry->state;
|
||||
temp.code = xact->entry->code;
|
||||
temp.payee = xact->entry->payee;
|
||||
temp.flags = xact->flags;
|
||||
temp.account = xact->account;
|
||||
return predicate->compute(&temp);
|
||||
}
|
||||
|
|
@ -149,6 +152,8 @@ class value_predicate
|
|||
item_t temp;
|
||||
temp.date = entry->date;
|
||||
temp.payee = entry->payee;
|
||||
temp.state = entry->state;
|
||||
temp.code = entry->code;
|
||||
|
||||
// Although there may be conflicting account masks for the whole
|
||||
// set of transactions -- for example, /rent/&!/expenses/, which
|
||||
|
|
@ -159,6 +164,7 @@ class value_predicate
|
|||
for (transactions_list::const_iterator i = entry->transactions.begin();
|
||||
i != entry->transactions.end();
|
||||
i++) {
|
||||
temp.flags = (*i)->flags;
|
||||
temp.account = (*i)->account;
|
||||
if (predicate->compute(&temp))
|
||||
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;
|
||||
}
|
||||
|
||||
node_t * format_t::value_expr = NULL;
|
||||
node_t * format_t::total_expr = NULL;
|
||||
std::auto_ptr<node_t> format_t::value_expr;
|
||||
std::auto_ptr<node_t> format_t::total_expr;
|
||||
|
||||
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";
|
||||
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 'n': current->type = element_t::ACCOUNT_NAME; 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::TOTAL; 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;
|
||||
|
||||
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:
|
||||
out << (elem->max_width == 0 ?
|
||||
item->payee : truncated(item->payee, elem->max_width));
|
||||
break;
|
||||
|
||||
case element_t::ACCOUNT_NAME:
|
||||
case element_t::ACCOUNT_FULLNAME:
|
||||
if (item->account) {
|
||||
std::string name = maximal_account_name(item, displayed_parent);
|
||||
out << (elem->max_width == 0 ? name : truncated(name, elem->max_width));
|
||||
std::string name = (elem->type == element_t::ACCOUNT_FULLNAME ?
|
||||
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 {
|
||||
out << " ";
|
||||
}
|
||||
break;
|
||||
|
||||
case element_t::ACCOUNT_FULLNAME:
|
||||
if (item->account)
|
||||
out << (elem->max_width == 0 ?
|
||||
item->account->fullname() :
|
||||
truncated(item->account->fullname(), elem->max_width));
|
||||
case element_t::OPT_AMOUNT: {
|
||||
std::string disp;
|
||||
bool use_disp = false;
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
case element_t::VALUE: {
|
||||
balance_t value = compute_value(item);
|
||||
|
|
|
|||
11
format.h
11
format.h
|
|
@ -16,9 +16,12 @@ struct element_t
|
|||
STRING,
|
||||
VALUE_EXPR,
|
||||
DATE_STRING,
|
||||
CLEARED,
|
||||
CODE,
|
||||
PAYEE,
|
||||
ACCOUNT_NAME,
|
||||
ACCOUNT_FULLNAME,
|
||||
OPT_AMOUNT,
|
||||
VALUE,
|
||||
TOTAL,
|
||||
SPACER
|
||||
|
|
@ -47,8 +50,8 @@ struct format_t
|
|||
{
|
||||
element_t * elements;
|
||||
|
||||
static node_t * value_expr;
|
||||
static node_t * total_expr;
|
||||
static std::auto_ptr<node_t> value_expr;
|
||||
static std::auto_ptr<node_t> total_expr;
|
||||
|
||||
format_t(const std::string& _format) {
|
||||
elements = parse_elements(_format);
|
||||
|
|
@ -63,10 +66,10 @@ struct format_t
|
|||
const item_t * displayed_parent = NULL) const;
|
||||
|
||||
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) {
|
||||
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 {
|
||||
|
||||
// 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,
|
||||
const bool show_subtotals,
|
||||
item_t * item)
|
||||
|
|
@ -64,27 +19,88 @@ static inline void sum_items(const item_t * top,
|
|||
sum_items(*i, show_subtotals, item);
|
||||
}
|
||||
|
||||
item_t * walk_items(const item_t * top,
|
||||
const account_t * account,
|
||||
const node_t * predicate,
|
||||
const bool show_subtotals)
|
||||
item_t * walk_accounts(const item_t * top,
|
||||
account_t * account,
|
||||
const node_t * predicate,
|
||||
const bool show_subtotals,
|
||||
const bool show_flattened)
|
||||
{
|
||||
item_t * item = new item_t;
|
||||
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++) {
|
||||
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;
|
||||
|
||||
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)
|
||||
item->total += subitem->total;
|
||||
|
||||
if (show_subtotals ? subitem->total : subitem->value)
|
||||
item->subitems.push_back(subitem);
|
||||
if ((! show_flattened || account->parent) &&
|
||||
show_subtotals ? subitem->total : subitem->value)
|
||||
item->subitems.push_back(subitem.release());
|
||||
}
|
||||
|
||||
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);
|
||||
i != end;
|
||||
i = std::find_if(++i, end, pred_obj)) {
|
||||
transactions_list reckoned;
|
||||
item_t * item = NULL;
|
||||
|
||||
for (transactions_list::const_iterator j
|
||||
|
|
@ -115,39 +132,32 @@ item_t * walk_entries(entries_list::const_iterator begin,
|
|||
assert(*i == (*j)->entry);
|
||||
|
||||
if (! item) {
|
||||
item = new item_t;
|
||||
item = new item_t(*i);
|
||||
item->index = count++;
|
||||
item->date = (*i)->date;
|
||||
item->payee = (*i)->payee;
|
||||
}
|
||||
|
||||
// If show_inverted is true, it implies show_related.
|
||||
if (! show_inverted) {
|
||||
item_t * subitem = new item_t;
|
||||
subitem->parent = item;
|
||||
subitem->date = item->date;
|
||||
subitem->payee = item->payee;
|
||||
subitem->account = (*j)->account;
|
||||
subitem->value = *(*j);
|
||||
item->value += subitem->value;
|
||||
item->subitems.push_back(subitem);
|
||||
if (! show_inverted &&
|
||||
std::find(reckoned.begin(),
|
||||
reckoned.end(), *j) == reckoned.end()) {
|
||||
item->add_item(new item_t(*j));
|
||||
reckoned.push_back(*j);
|
||||
}
|
||||
|
||||
if (show_related)
|
||||
for (transactions_list::iterator k = (*i)->transactions.begin();
|
||||
k != (*i)->transactions.end();
|
||||
k++)
|
||||
if (*k != *j && ! ((*k)->flags & TRANSACTION_VIRTUAL)) {
|
||||
item_t * subitem = new item_t;
|
||||
subitem->parent = item;
|
||||
subitem->date = item->date;
|
||||
subitem->payee = item->payee;
|
||||
subitem->account = (*k)->account;
|
||||
subitem->value = *(*k);
|
||||
if (show_inverted)
|
||||
subitem->value.negate();
|
||||
item->subitems.push_back(subitem);
|
||||
}
|
||||
k++) {
|
||||
if (*k == *j || ((*k)->flags & TRANSACTION_AUTO) ||
|
||||
std::find(reckoned.begin(),
|
||||
reckoned.end(), *k) != reckoned.end())
|
||||
continue;
|
||||
|
||||
item->add_item(new item_t(*k));
|
||||
if (show_inverted)
|
||||
item->subitems.back()->value.negate();
|
||||
reckoned.push_back(*k);
|
||||
}
|
||||
}
|
||||
|
||||
if (item) {
|
||||
|
|
@ -155,6 +165,9 @@ item_t * walk_entries(entries_list::const_iterator begin,
|
|||
result = new item_t;
|
||||
item->parent = result;
|
||||
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 * parent;
|
||||
items_deque subitems;
|
||||
struct item_t * parent;
|
||||
items_deque subitems;
|
||||
|
||||
unsigned int index;
|
||||
std::time_t date;
|
||||
std::string payee;
|
||||
const account_t * account;
|
||||
balance_pair_t value;
|
||||
balance_pair_t total;
|
||||
unsigned int index;
|
||||
std::time_t date;
|
||||
entry_t::entry_state_t state;
|
||||
std::string code;
|
||||
std::string payee;
|
||||
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() {
|
||||
for (items_deque::iterator i = subitems.begin();
|
||||
|
|
@ -33,25 +54,28 @@ struct item_t
|
|||
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);
|
||||
};
|
||||
|
||||
struct node_t;
|
||||
|
||||
item_t * walk_accounts(const account_t * account,
|
||||
const node_t * predicate,
|
||||
const bool show_subtotals);
|
||||
|
||||
item_t * walk_items(const item_t * top,
|
||||
const account_t * account,
|
||||
const node_t * predicate,
|
||||
const bool show_subtotals);
|
||||
item_t * walk_accounts(const item_t * top,
|
||||
account_t * account,
|
||||
const node_t * predicate = NULL,
|
||||
const bool show_subtotals = true,
|
||||
const bool show_flattened = false);
|
||||
|
||||
item_t * walk_entries(entries_list::const_iterator begin,
|
||||
entries_list::const_iterator end,
|
||||
const node_t * predicate,
|
||||
const bool show_related,
|
||||
const bool show_inverted);
|
||||
const node_t * predicate = NULL,
|
||||
const bool show_related = false,
|
||||
const bool show_inverted = false);
|
||||
|
||||
} // namespace report
|
||||
|
||||
|
|
|
|||
128
ledger.cc
128
ledger.cc
|
|
@ -1,4 +1,5 @@
|
|||
#include "ledger.h"
|
||||
#include "expr.h"
|
||||
#include "textual.h"
|
||||
#include "binary.h"
|
||||
|
||||
|
|
@ -52,6 +53,133 @@ bool ledger_t::remove_entry(entry_t * entry)
|
|||
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)
|
||||
{
|
||||
char * sep = std::strrchr(p, '=');
|
||||
|
|
|
|||
18
ledger.h
18
ledger.h
|
|
@ -288,6 +288,7 @@ class commodity_t
|
|||
#define TRANSACTION_NORMAL 0x0
|
||||
#define TRANSACTION_VIRTUAL 0x1
|
||||
#define TRANSACTION_BALANCE 0x2
|
||||
#define TRANSACTION_AUTO 0x4
|
||||
|
||||
class transaction_t
|
||||
{
|
||||
|
|
@ -322,11 +323,13 @@ class entry_t
|
|||
UNCLEARED, CLEARED, PENDING
|
||||
};
|
||||
|
||||
std::time_t date;
|
||||
enum entry_state_t state;
|
||||
std::string code;
|
||||
std::string payee;
|
||||
transactions_list transactions;
|
||||
std::time_t date;
|
||||
entry_state_t state;
|
||||
std::string code;
|
||||
std::string payee;
|
||||
transactions_list transactions;
|
||||
|
||||
entry_t() : date(-1), state(UNCLEARED) {}
|
||||
|
||||
~entry_t() {
|
||||
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) {
|
||||
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 remove_entry(entry_t * entry);
|
||||
|
||||
entry_t * derive_entry(int argc, char **argv) const;
|
||||
};
|
||||
|
||||
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
|
||||
= "%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_only = false;
|
||||
|
|
@ -151,11 +155,11 @@ void register_report(std::ostream& out,
|
|||
bool first = true;
|
||||
|
||||
if ((*i)->subitems.size() > 1 && ! show_expanded) {
|
||||
item_t summary;
|
||||
summary.date = (*i)->date;
|
||||
item_t summary(*i);
|
||||
summary.parent = *i;
|
||||
summary.account = &splits;
|
||||
|
||||
summary.value = 0;
|
||||
for (items_deque::const_iterator j = (*i)->subitems.begin();
|
||||
j != (*i)->subitems.end();
|
||||
j++)
|
||||
|
|
@ -171,7 +175,7 @@ void register_report(std::ostream& out,
|
|||
|
||||
if (show) {
|
||||
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)
|
||||
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)
|
||||
{
|
||||
char buf[128];
|
||||
|
|
@ -468,23 +339,24 @@ static void show_help(std::ostream& out)
|
|||
<< " balance show balance totals" << std::endl
|
||||
<< " register display a register for ACCOUNT" << 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
|
||||
<< " 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[])
|
||||
{
|
||||
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;
|
||||
ledger::node_t * predicate = NULL;
|
||||
std::string format_string;
|
||||
std::string sort_string;
|
||||
ledger::node_t * sort_order = NULL;
|
||||
std::string value_expr = "a";
|
||||
std::string total_expr = "T";
|
||||
ledger::ledger_t * journal = new ledger::ledger_t;
|
||||
std::string predicate_string;
|
||||
std::string display_predicate_string;
|
||||
std::string format_string;
|
||||
std::string sort_string;
|
||||
std::string value_expr = "a";
|
||||
std::string total_expr = "T";
|
||||
|
||||
bool show_subtotals = true;
|
||||
bool show_expanded = false;
|
||||
|
|
@ -521,11 +393,9 @@ int main(int argc, char * argv[])
|
|||
if (access(p, R_OK) != -1) {
|
||||
std::ifstream instr(p);
|
||||
if (! ledger::read_binary_ledger(instr, std::getenv("LEDGER"),
|
||||
journal)) {
|
||||
// We need to throw away what we've read, and create a new
|
||||
// ledger
|
||||
delete journal;
|
||||
journal = new ledger::ledger_t;
|
||||
journal.get())) {
|
||||
// Throw away what's been read, and create a new journal
|
||||
journal.reset(new ledger::ledger_t);
|
||||
} else {
|
||||
ledger::cache_dirty = false;
|
||||
}
|
||||
|
|
@ -652,6 +522,14 @@ int main(int argc, char * argv[])
|
|||
predicate_string += ")";
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
if (! display_predicate_string.empty())
|
||||
display_predicate_string += "&";
|
||||
display_predicate_string += "(";
|
||||
display_predicate_string += optarg;
|
||||
display_predicate_string += ")";
|
||||
break;
|
||||
|
||||
// Commodity reporting
|
||||
case 'P':
|
||||
ledger::price_db = optarg;
|
||||
|
|
@ -747,14 +625,14 @@ int main(int argc, char * argv[])
|
|||
if (files.empty()) {
|
||||
if (char * p = std::getenv("LEDGER"))
|
||||
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 {
|
||||
for (std::list<std::string>::iterator i = files.begin();
|
||||
i != files.end(); i++) {
|
||||
char buf[4096];
|
||||
char * p = buf;
|
||||
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();
|
||||
std::ifstream db(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) {
|
||||
|
|
@ -780,52 +659,98 @@ int main(int argc, char * argv[])
|
|||
}
|
||||
}
|
||||
|
||||
// Read the command word, and handle the "entry" command specially,
|
||||
// without any other processing.
|
||||
// Read the command word, and then check and simplify it
|
||||
|
||||
const std::string command = argv[index++];
|
||||
std::string command = argv[index++];
|
||||
|
||||
if (command == "entry")
|
||||
return add_new_entry(index, argc, argv, journal) ? 0 : 1;
|
||||
|
||||
// Interpret the remaining arguments as regular expressions, used
|
||||
// for refining report results.
|
||||
|
||||
for (; index < argc; index++) {
|
||||
if (std::strcmp(argv[index], "--") == 0) {
|
||||
index++;
|
||||
break;
|
||||
}
|
||||
|
||||
show_expanded = true;
|
||||
|
||||
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 += "/)";
|
||||
if (command == "balance" || command == "bal" || command == "b")
|
||||
command = "b";
|
||||
else if (command == "register" || command == "reg" || command == "r")
|
||||
command = "r";
|
||||
else if (command == "print" || command == "p")
|
||||
command = "p";
|
||||
else if (command == "entry")
|
||||
command = "e";
|
||||
else if (command == "equity")
|
||||
command = "E";
|
||||
else {
|
||||
std::cerr << "Error: Unrecognized command '" << command << "'."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (; index < argc; index++) {
|
||||
show_expanded = true;
|
||||
// Process the remaining command-line arguments
|
||||
|
||||
if (! predicate_string.empty())
|
||||
predicate_string += "&";
|
||||
std::auto_ptr<ledger::entry_t> new_entry;
|
||||
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] == '-') {
|
||||
predicate_string += "(!//";
|
||||
predicate_string += argv[index] + 1;
|
||||
} else {
|
||||
predicate_string += "(//";
|
||||
predicate_string += argv[index];
|
||||
bool have_regexps = index < argc;
|
||||
bool first = true;
|
||||
|
||||
for (; index < argc; index++) {
|
||||
if (std::strcmp(argv[index], "--") == 0) {
|
||||
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
|
||||
|
|
@ -835,122 +760,110 @@ int main(int argc, char * argv[])
|
|||
if (debug)
|
||||
std::cerr << "predicate = " << predicate_string << std::endl;
|
||||
#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
|
||||
|
||||
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
|
||||
|
||||
ledger::format_t::value_expr = ledger::parse_expr(value_expr);
|
||||
ledger::format_t::total_expr = ledger::parse_expr(total_expr);
|
||||
ledger::format_t::value_expr.reset(ledger::parse_expr(value_expr));
|
||||
ledger::format_t::total_expr.reset(ledger::parse_expr(total_expr));
|
||||
|
||||
// Now handle the command that was identified above.
|
||||
|
||||
if (command == "print") {
|
||||
#if 0
|
||||
if (ledger::item_t * top
|
||||
= 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
|
||||
if (command == "p" || command == "e") {
|
||||
show_related = true;
|
||||
show_expanded = true;
|
||||
}
|
||||
else if (command == "equity") {
|
||||
#if 0
|
||||
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 (command == "E") {
|
||||
show_expanded = true;
|
||||
}
|
||||
else if (! sort_order && ! show_related &&
|
||||
(command == "balance" || command == "bal")) {
|
||||
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 (show_related && command == "r") {
|
||||
show_inverted = true;
|
||||
}
|
||||
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
|
||||
= ledger::walk_entries(journal->entries.begin(),
|
||||
journal->entries.end(), predicate,
|
||||
show_related, show_inverted)) {
|
||||
std::auto_ptr<ledger::item_t> top;
|
||||
std::auto_ptr<ledger::item_t> list;
|
||||
|
||||
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 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, "%/")) {
|
||||
first_line_format = std::string(f, 0, p - f);
|
||||
next_lines_format = std::string(p + 2);
|
||||
} else {
|
||||
first_line_format = format_string;
|
||||
next_lines_format = format_string;
|
||||
first_line_format = next_lines_format = f;
|
||||
}
|
||||
|
||||
ledger::format_t * format = new ledger::format_t(first_line_format);
|
||||
ledger::format_t * nformat = new ledger::format_t(next_lines_format);
|
||||
std::auto_ptr<ledger::format_t>
|
||||
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,
|
||||
*format, *nformat, show_expanded);
|
||||
#ifdef DEBUG
|
||||
delete nformat;
|
||||
delete format;
|
||||
delete top;
|
||||
#endif
|
||||
ledger::register_report(std::cout, top.get(), display_predicate.get(),
|
||||
sort_order.get(), *format, *nformat,
|
||||
show_expanded);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "Error: Unrecognized command '" << command << "'."
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Save the cache, if need be
|
||||
|
||||
|
|
@ -958,31 +871,10 @@ int main(int argc, char * argv[])
|
|||
if (const char * p = std::getenv("LEDGER_CACHE")) {
|
||||
std::ofstream outstr(p);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
127
textual.cc
127
textual.cc
|
|
@ -294,7 +294,8 @@ void automated_transaction_t::extend_entry(entry_t * entry)
|
|||
amt = (*t)->amount;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -603,8 +604,9 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
|
|||
amt.parse(buf);
|
||||
time_commodity = amt.commodity;
|
||||
|
||||
transaction_t * xact = new transaction_t(curr, last_account, amt, amt,
|
||||
TRANSACTION_VIRTUAL);
|
||||
transaction_t * xact
|
||||
= new transaction_t(curr, last_account, amt, amt,
|
||||
TRANSACTION_VIRTUAL);
|
||||
curr->add_transaction(xact);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// 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
|
||||
|
||||
#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,
|
||||
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
|
||||
|
||||
#endif // _TEXTUAL_H
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue