Merge branch 'next'

This commit is contained in:
John Wiegley 2009-03-06 02:52:14 -04:00
commit 4a0f5f9034
17 changed files with 324 additions and 166 deletions

View file

@ -406,10 +406,14 @@ cppunittests: check
endif endif
fullcheck: cppunittests fullcheck: cppunittests
@$(top_builddir)/RegressTests --verify --gmalloc @$(top_builddir)/RegressTests --verify
@$(top_builddir)/BaselineTests --verify --gmalloc @$(top_builddir)/BaselineTests --verify
@$(top_builddir)/ConfirmTests --verify --gmalloc @$(top_builddir)/ConfirmTests --verify
@$(top_builddir)/GenerateTests --verify --gmalloc @$(top_builddir)/GenerateTests --verify
@$(top_builddir)/RegressTests --gmalloc
@$(top_builddir)/BaselineTests --gmalloc
@$(top_builddir)/ConfirmTests --gmalloc
@$(top_builddir)/GenerateTests --gmalloc
###################################################################### ######################################################################

View file

@ -1,4 +1,4 @@
.Dd March 3, 2009 .Dd March 5, 2009
.Dt ledger 1 .Dt ledger 1
.Sh NAME .Sh NAME
.Nm ledger .Nm ledger
@ -214,7 +214,6 @@ appeared in the original journal file.
.It Fl \-total Ar EXPR .It Fl \-total Ar EXPR
.It Fl \-total-data Pq Fl J .It Fl \-total-data Pq Fl J
.It Fl \-total-width Ar INT .It Fl \-total-width Ar INT
.It Fl \-totals
.It Fl \-trace Ar INT .It Fl \-trace Ar INT
.It Fl \-truncate .It Fl \-truncate
.It Fl \-unbudgeted .It Fl \-unbudgeted

View file

@ -30,6 +30,8 @@
*/ */
#include "account.h" #include "account.h"
#include "post.h"
#include "xact.h"
#include "interactive.h" #include "interactive.h"
namespace ledger { namespace ledger {
@ -166,33 +168,20 @@ namespace {
return string_value(account.name); return string_value(account.name);
} }
value_t get_total(account_t& account) { value_t get_amount(account_t& account) {
if (! account.xdata_ || account.xdata_->family_details.total.is_null()) return VALUE_OR_ZERO(account.self_total());
return 0L;
else
return account.xdata_->family_details.total;
} }
value_t get_count(account_t& account) { value_t get_total(account_t& account) {
if (account.xdata_) return VALUE_OR_ZERO(account.family_total());
return long(account.xdata_->family_details.posts_count);
else
return 0L;
} }
value_t get_subcount(account_t& account) { value_t get_subcount(account_t& account) {
if (account.xdata_) return long(account.self_details().posts_count);
return long(account.xdata_->self_details.posts_count);
else
return 0L;
} }
value_t get_amount(account_t& account) { value_t get_count(account_t& account) {
if (! account.xdata_ || return long(account.family_details().posts_count);
account.xdata_->self_details.total.is_null())
return 0L;
else
return account.xdata_->self_details.total;
} }
value_t get_depth(account_t& account) { value_t get_depth(account_t& account) {
@ -317,4 +306,132 @@ std::size_t account_t::children_with_flags(xdata_t::flags_t flags) const
return count; return count;
} }
account_t::xdata_t::details_t&
account_t::xdata_t::details_t::operator+=(const details_t& other)
{
// jww (2009-03-05): NYI
return *this;
}
value_t account_t::self_total(const optional<expr_t&>& expr) const
{
if (xdata_ && xdata_->has_flags(ACCOUNT_EXT_VISITED)) {
if (! xdata_) xdata_ = xdata_t();
posts_deque::const_iterator i =
posts.begin() + xdata_->self_details.last_size;
for (; i != posts.end(); i++) {
if ((*i)->xdata().has_flags(POST_EXT_VISITED) &&
! (*i)->xdata().has_flags(POST_EXT_CONSIDERED)) {
(*i)->add_to_value(xdata_->self_details.total, expr);
(*i)->xdata().add_flags(POST_EXT_CONSIDERED);
}
}
xdata_->self_details.last_size = posts.size();
return xdata_->self_details.total;
} else {
return NULL_VALUE;
}
}
value_t account_t::family_total(const optional<expr_t&>& expr) const
{
if (! (xdata_ && xdata_->family_details.calculated)) {
const_cast<account_t&>(*this).xdata().family_details.calculated = true;
value_t temp;
foreach (const accounts_map::value_type& pair, accounts) {
temp = pair.second->family_total(expr);
if (! temp.is_null())
add_or_set_value(xdata_->family_details.total, temp);
}
temp = self_total(expr);
if (! temp.is_null())
add_or_set_value(xdata_->family_details.total, temp);
}
return xdata_->family_details.total;
}
const account_t::xdata_t::details_t&
account_t::self_details(bool gather_all) const
{
if (! (xdata_ && xdata_->self_details.gathered)) {
const_cast<account_t&>(*this).xdata().self_details.gathered = true;
foreach (const post_t * post, posts)
xdata_->self_details.update(const_cast<post_t&>(*post), gather_all);
}
return xdata_->self_details;
}
const account_t::xdata_t::details_t&
account_t::family_details(bool gather_all) const
{
if (! (xdata_ && xdata_->family_details.gathered)) {
const_cast<account_t&>(*this).xdata().family_details.gathered = true;
foreach (const accounts_map::value_type& pair, accounts)
xdata_->family_details += pair.second->family_details(gather_all);
xdata_->self_details += self_details(gather_all);
}
return xdata_->family_details;
}
void account_t::xdata_t::details_t::update(post_t& post,
bool gather_all)
{
if (last_xact != post.xact) {
xacts_count++;
last_xact = post.xact;
}
if (last_post == &post)
return;
last_post = &post;
posts_count++;
if (post.has_flags(POST_VIRTUAL))
posts_virtuals_count++;
if (gather_all)
filenames.insert(post.pathname);
date_t date = post.date();
if (date.year() == CURRENT_DATE().year() &&
date.month() == CURRENT_DATE().month())
posts_this_month_count++;
if ((CURRENT_DATE() - date).days() <= 30)
posts_last_30_count++;
if ((CURRENT_DATE() - date).days() <= 7)
posts_last_7_count++;
if (! is_valid(earliest_post) || post.date() < earliest_post)
earliest_post = post.date();
if (! is_valid(latest_post) || post.date() > latest_post)
latest_post = post.date();
if (post.state() == item_t::CLEARED) {
posts_cleared_count++;
if (! is_valid(earliest_cleared_post) ||
post.date() < earliest_cleared_post)
earliest_cleared_post = post.date();
if (! is_valid(latest_cleared_post) ||
post.date() > latest_cleared_post)
latest_cleared_post = post.date();
}
if (gather_all) {
accounts_referenced.insert(post.account->fullname());
payees_referenced.insert(post.xact->payee);
}
}
} // namespace ledger } // namespace ledger

View file

@ -50,9 +50,12 @@
namespace ledger { namespace ledger {
class account_t;
class session_t; class session_t;
class account_t;
class xact_t;
class post_t;
typedef std::deque<post_t *> posts_deque;
typedef std::map<const string, account_t *> accounts_map; typedef std::map<const string, account_t *> accounts_map;
/** /**
@ -68,14 +71,15 @@ class account_t : public scope_t
optional<string> note; optional<string> note;
unsigned short depth; unsigned short depth;
accounts_map accounts; accounts_map accounts;
posts_deque posts;
bool known; bool known;
mutable void * data; mutable void * data;
mutable string _fullname; mutable string _fullname;
account_t(account_t * _parent = NULL, account_t(account_t * _parent = NULL,
const string& _name = "", const string& _name = "",
const optional<string>& _note = none) const optional<string>& _note = none)
: scope_t(), parent(_parent), name(_name), note(_note), : scope_t(), parent(_parent), name(_name), note(_note),
depth(static_cast<unsigned short>(parent ? parent->depth + 1 : 0)), depth(static_cast<unsigned short>(parent ? parent->depth + 1 : 0)),
known(false), data(NULL) { known(false), data(NULL) {
@ -112,6 +116,10 @@ class account_t : public scope_t
account_t * find_account(const string& name, bool auto_create = true); account_t * find_account(const string& name, bool auto_create = true);
account_t * find_account_re(const string& regexp); account_t * find_account_re(const string& regexp);
void add_post(post_t * post) {
posts.push_back(post);
}
virtual expr_t::ptr_op_t lookup(const string& name); virtual expr_t::ptr_op_t lookup(const string& name);
bool valid() const; bool valid() const;
@ -123,20 +131,68 @@ class account_t : public scope_t
#define ACCOUNT_EXT_SORT_CALC 0x01 #define ACCOUNT_EXT_SORT_CALC 0x01
#define ACCOUNT_EXT_HAS_NON_VIRTUALS 0x02 #define ACCOUNT_EXT_HAS_NON_VIRTUALS 0x02
#define ACCOUNT_EXT_HAS_UNB_VIRTUALS 0x04 #define ACCOUNT_EXT_HAS_UNB_VIRTUALS 0x04
#define ACCOUNT_EXT_VISITED 0x08 #define ACCOUNT_EXT_AUTO_VIRTUALIZE 0x08
#define ACCOUNT_EXT_MATCHING 0x10 #define ACCOUNT_EXT_VISITED 0x10
#define ACCOUNT_EXT_DISPLAYED 0x20 #define ACCOUNT_EXT_MATCHING 0x20
#define ACCOUNT_EXT_DISPLAYED 0x40
struct details_t struct details_t
{ {
value_t total; value_t total;
bool calculated;
bool gathered;
std::size_t posts_count; // The following are only calculated if --totals is enabled
std::size_t posts_virtuals_count; std::size_t xacts_count;
std::size_t posts_count;
std::size_t posts_virtuals_count;
std::size_t posts_cleared_count;
std::size_t posts_last_7_count;
std::size_t posts_last_30_count;
std::size_t posts_this_month_count;
date_t earliest_post;
date_t earliest_cleared_post;
date_t latest_post;
date_t latest_cleared_post;
xact_t * last_xact;
post_t * last_post;
std::size_t last_size;
// The following are only calculated if --gather is enabled
std::set<path> filenames;
std::set<string> accounts_referenced;
std::set<string> payees_referenced;
details_t() details_t()
: posts_count(0), : calculated(false),
posts_virtuals_count(0) {} gathered(false),
xacts_count(0),
posts_count(0),
posts_virtuals_count(0),
posts_cleared_count(0),
posts_last_7_count(0),
posts_last_30_count(0),
posts_this_month_count(0),
earliest_post(0),
earliest_cleared_post(0),
latest_post(0),
latest_cleared_post(0),
last_xact(NULL),
last_post(NULL),
last_size(0) {}
details_t& operator+=(const details_t& other);
void update(post_t& post, bool gather_all = false);
}; };
details_t self_details; details_t self_details;
@ -184,6 +240,12 @@ class account_t : public scope_t
return *xdata_; return *xdata_;
} }
value_t self_total(const optional<expr_t&>& expr = none) const;
value_t family_total(const optional<expr_t&>& expr = none) const;
const xdata_t::details_t& self_details(bool gather_all = true) const;
const xdata_t::details_t& family_details(bool gather_all = true) const;
bool has_flags(xdata_t::flags_t flags) const { bool has_flags(xdata_t::flags_t flags) const {
return xdata_ && xdata_->has_flags(flags); return xdata_ && xdata_->has_flags(flags);
} }

View file

@ -93,11 +93,7 @@ post_handler_ptr chain_post_handlers(report_t& report,
// calc_posts computes the running total. When this appears will determine, // calc_posts computes the running total. When this appears will determine,
// for example, whether filtered posts are included or excluded from the // for example, whether filtered posts are included or excluded from the
// running total. // running total.
handler.reset(new calc_posts(handler, expr, report.HANDLED(totals))); handler.reset(new calc_posts(handler, expr, only_preliminaries));
// unround_posts will unround the amounts in all postings
if (report.HANDLED(unround))
handler.reset(new unround_posts(handler));
// filter_posts will only pass through posts matching the // filter_posts will only pass through posts matching the
// `secondary_predicate'. // `secondary_predicate'.

View file

@ -194,43 +194,21 @@ void calc_posts::operator()(post_t& post)
if (last_post) { if (last_post) {
assert(last_post->has_xdata()); assert(last_post->has_xdata());
add_or_set_value(xdata.total, last_post->xdata().total); if (! account_wise)
xdata.total = last_post->xdata().total;
xdata.count = last_post->xdata().count + 1; xdata.count = last_post->xdata().count + 1;
} else { } else {
xdata.count = 1; xdata.count = 1;
} }
value_t amount; post.add_to_value(xdata.visited_value, amount_expr);
post.add_to_value(amount, amount_expr); xdata.add_flags(POST_EXT_VISITED);
add_or_set_value(xdata.total, amount); account_t * acct = post.reported_account();
acct->xdata().add_flags(ACCOUNT_EXT_VISITED);
if (calc_totals) { if (! account_wise)
account_t * acct = post.reported_account(); add_or_set_value(xdata.total, xdata.visited_value);
account_t::xdata_t * acct_xdata = &acct->xdata();
add_or_set_value(acct_xdata->self_details.total, amount);
acct_xdata->self_details.posts_count++;
acct_xdata->self_details.posts_virtuals_count++;
acct_xdata->add_flags(ACCOUNT_EXT_VISITED);
while (true) {
add_or_set_value(acct_xdata->family_details.total, amount);
acct_xdata->family_details.posts_count++;
if (post.has_flags(POST_VIRTUAL))
acct_xdata->family_details.posts_virtuals_count++;
acct = acct->parent;
if (acct)
acct_xdata = &acct->xdata();
else
break;
}
}
item_handler<post_t>::operator()(post); item_handler<post_t>::operator()(post);
@ -259,7 +237,8 @@ namespace {
// If the account for this post is all virtual, then report the post as // If the account for this post is all virtual, then report the post as
// such. This allows subtotal reports to show "(Account)" for accounts // such. This allows subtotal reports to show "(Account)" for accounts
// that contain only virtual posts. // that contain only virtual posts.
if (account && account->has_xdata()) { if (account && account->has_xdata() &&
account->xdata().has_flags(ACCOUNT_EXT_AUTO_VIRTUALIZE)) {
if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS)) { if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS)) {
post.add_flags(POST_VIRTUAL); post.add_flags(POST_VIRTUAL);
if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS)) if (! account->xdata().has_flags(ACCOUNT_EXT_HAS_UNB_VIRTUALS))
@ -286,7 +265,7 @@ namespace {
case value_t::BALANCE: case value_t::BALANCE:
case value_t::SEQUENCE: case value_t::SEQUENCE:
xdata.value = temp; xdata.compound_value = temp;
xdata.add_flags(POST_EXT_COMPOUND); xdata.add_flags(POST_EXT_COMPOUND);
break; break;
@ -567,6 +546,8 @@ void subtotal_posts::operator()(post_t& post)
// such, so that `handle_value' can show "(Account)" for accounts // such, so that `handle_value' can show "(Account)" for accounts
// that contain only virtual posts. // that contain only virtual posts.
post.reported_account()->xdata().add_flags(ACCOUNT_EXT_AUTO_VIRTUALIZE);
if (! post.has_flags(POST_VIRTUAL)) if (! post.has_flags(POST_VIRTUAL))
post.reported_account()->xdata().add_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS); post.reported_account()->xdata().add_flags(ACCOUNT_EXT_HAS_NON_VIRTUALS);
else if (! post.has_flags(POST_MUST_BALANCE)) else if (! post.has_flags(POST_MUST_BALANCE))

View file

@ -82,29 +82,6 @@ public:
} }
}; };
/**
* @brief Brief
*
* Long.
*/
class unround_posts : public item_handler<post_t>
{
public:
unround_posts(post_handler_ptr handler)
: item_handler<post_t>(handler) {
TRACE_CTOR(unround_posts, "posts_list&");
}
virtual ~unround_posts() {
TRACE_DTOR(unround_posts);
}
virtual void operator()(post_t& post) {
post.xdata().value = post.amount.unrounded();
post.xdata().add_flags(POST_EXT_COMPOUND);
item_handler<post_t>::operator()(post);
}
};
class posts_iterator; class posts_iterator;
/** /**
@ -342,16 +319,16 @@ class calc_posts : public item_handler<post_t>
{ {
post_t * last_post; post_t * last_post;
expr_t& amount_expr; expr_t& amount_expr;
bool calc_totals; bool account_wise;
calc_posts(); calc_posts();
public: public:
calc_posts(post_handler_ptr handler, calc_posts(post_handler_ptr handler,
expr_t& _amount_expr, expr_t& _amount_expr,
bool _calc_totals = false) bool _account_wise = false)
: item_handler<post_t>(handler), last_post(NULL), : item_handler<post_t>(handler), last_post(NULL),
amount_expr(_amount_expr), calc_totals(_calc_totals) { amount_expr(_amount_expr), account_wise(_account_wise) {
TRACE_CTOR(calc_posts, "post_handler_ptr, expr_t&, bool"); TRACE_CTOR(calc_posts, "post_handler_ptr, expr_t&, bool");
} }
virtual ~calc_posts() { virtual ~calc_posts() {

View file

@ -413,9 +413,6 @@ void global_scope_t::normalize_report_options(const string& verb)
if (verb[0] != 'b' && verb[0] != 'r') if (verb[0] != 'b' && verb[0] != 'r')
rep.HANDLER(base).on_only(); rep.HANDLER(base).on_only();
if (verb[0] == 'b' || verb == "equity")
rep.HANDLER(totals).on_only();
if (rep.HANDLED(period_) && ! rep.HANDLED(sort_all_)) if (rep.HANDLED(period_) && ! rep.HANDLED(sort_all_))
rep.HANDLER(sort_xacts_).on_only(); rep.HANDLER(sort_xacts_).on_only();

View file

@ -297,17 +297,11 @@ void format_accounts::flush()
} }
} }
if (report.session.master->has_xdata()) { if (! report.HANDLED(no_total) && top_displayed > 1 &&
account_t::xdata_t& xdata(report.session.master->xdata()); report.session.master->family_total()) {
bind_scope_t bound_scope(report, *report.session.master);
if (! report.HANDLED(no_total) && top_displayed > 1 && separator_format.format(out, bound_scope);
xdata.family_details.total) { total_line_format.format(out, bound_scope);
xdata.self_details.total = xdata.family_details.total;
bind_scope_t bound_scope(report, *report.session.master);
separator_format.format(out, bound_scope);
total_line_format.format(out, bound_scope);
}
} }
out.flush(); out.flush();

View file

@ -140,12 +140,10 @@ namespace {
} }
value_t get_amount(post_t& post) { value_t get_amount(post_t& post) {
if (post.has_xdata() && if (post.has_xdata() && post.xdata().has_flags(POST_EXT_COMPOUND))
post.xdata().has_flags(POST_EXT_COMPOUND)) { return post.xdata().compound_value;
return post.xdata().value; else
} else {
return post.amount; return post.amount;
}
} }
value_t get_use_direct_amount(post_t& post) { value_t get_use_direct_amount(post_t& post) {
@ -169,7 +167,7 @@ namespace {
return *post.cost; return *post.cost;
else if (post.has_xdata() && else if (post.has_xdata() &&
post.xdata().has_flags(POST_EXT_COMPOUND)) post.xdata().has_flags(POST_EXT_COMPOUND))
return post.xdata().value; return post.xdata().compound_value;
else else
return post.amount; return post.amount;
} }
@ -233,10 +231,11 @@ namespace {
DEBUG("post.account_amount", "Found account: " << account->fullname()); DEBUG("post.account_amount", "Found account: " << account->fullname());
if (account->xdata().self_details.total.is_null()) value_t total = account->self_total();
if (total.is_null())
return 0L; return 0L;
else else
return account->xdata().self_details.total.simplified(); return total.simplified();
} }
value_t get_account_depth(post_t& post) { value_t get_account_depth(post_t& post) {
@ -365,13 +364,31 @@ bool post_t::valid() const
return true; return true;
} }
void post_t::add_to_value(value_t& value, expr_t& expr) void post_t::add_to_value(value_t& value, const optional<expr_t&>& expr) const
{ {
if (xdata_ && xdata_->has_flags(POST_EXT_COMPOUND)) { if (xdata_ && xdata_->has_flags(POST_EXT_COMPOUND)) {
add_or_set_value(value, xdata_->compound_value);
}
else if (expr) {
bind_scope_t bound_scope(*expr->get_context(),
const_cast<post_t&>(*this));
#if 1
value_t temp(expr->calc(bound_scope));
add_or_set_value(value, temp);
#else
if (! xdata_) xdata_ = xdata_t();
xdata_->value = expr->calc(bound_scope);
xdata_->add_flags(POST_EXT_COMPOUND);
add_or_set_value(value, xdata_->value); add_or_set_value(value, xdata_->value);
} else { #endif
bind_scope_t bound_scope(*expr.get_context(), *this); }
add_or_set_value(value, expr.calc(bound_scope)); else if (xdata_ && xdata_->has_flags(POST_EXT_VISITED) &&
! xdata_->visited_value.is_null()) {
add_or_set_value(value, xdata_->visited_value);
}
else {
add_or_set_value(value, amount);
} }
} }

View file

@ -53,9 +53,6 @@ namespace ledger {
class xact_t; class xact_t;
class account_t; class account_t;
class post_t;
typedef std::list<post_t *> posts_list;
/** /**
* @brief Brief * @brief Brief
* *
@ -126,20 +123,22 @@ public:
bool valid() const; bool valid() const;
struct xdata_t : public supports_flags<> struct xdata_t : public supports_flags<uint_least16_t>
{ {
#define POST_EXT_RECEIVED 0x01 #define POST_EXT_RECEIVED 0x0001
#define POST_EXT_HANDLED 0x02 #define POST_EXT_HANDLED 0x0002
#define POST_EXT_TO_DISPLAY 0x04 #define POST_EXT_DISPLAYED 0x0004
#define POST_EXT_DISPLAYED 0x08 #define POST_EXT_DIRECT_AMT 0x0008
#define POST_EXT_DIRECT_AMT 0x10 #define POST_EXT_SORT_CALC 0x0010
#define POST_EXT_SORT_CALC 0x20 #define POST_EXT_COMPOUND 0x0020
#define POST_EXT_COMPOUND 0x40 #define POST_EXT_VISITED 0x0040
#define POST_EXT_MATCHES 0x80 #define POST_EXT_MATCHES 0x0080
#define POST_EXT_CONSIDERED 0x0100
value_t visited_value;
value_t compound_value;
value_t total; value_t total;
std::size_t count; std::size_t count;
value_t value;
date_t date; date_t date;
account_t * account; account_t * account;
void * ptr; void * ptr;
@ -147,14 +146,16 @@ public:
std::list<sort_value_t> sort_values; std::list<sort_value_t> sort_values;
xdata_t() xdata_t()
: supports_flags<>(), count(0), account(NULL), ptr(NULL) { : supports_flags<uint_least16_t>(), count(0),
account(NULL), ptr(NULL) {
TRACE_CTOR(post_t::xdata_t, ""); TRACE_CTOR(post_t::xdata_t, "");
} }
xdata_t(const xdata_t& other) xdata_t(const xdata_t& other)
: supports_flags<>(other.flags()), : supports_flags<uint_least16_t>(other.flags()),
visited_value(other.visited_value),
compound_value(other.compound_value),
total(other.total), total(other.total),
count(other.count), count(other.count),
value(other.value),
date(other.date), date(other.date),
account(other.account), account(other.account),
ptr(NULL), ptr(NULL),
@ -184,8 +185,12 @@ public:
xdata_ = xdata_t(); xdata_ = xdata_t();
return *xdata_; return *xdata_;
} }
const xdata_t& xdata() const {
return const_cast<post_t *>(this)->xdata();
}
void add_to_value(value_t& value, expr_t& expr); void add_to_value(value_t& value,
const optional<expr_t&>& expr = none) const;
account_t * reported_account() { account_t * reported_account() {
if (xdata_) if (xdata_)

View file

@ -196,6 +196,11 @@ value_t report_t::fn_rounded(call_scope_t& args)
return args.value().rounded(); return args.value().rounded();
} }
value_t report_t::fn_unrounded(call_scope_t& args)
{
return args.value().unrounded();
}
value_t report_t::fn_quantity(call_scope_t& scope) value_t report_t::fn_quantity(call_scope_t& scope)
{ {
interactive_t args(scope, "a"); interactive_t args(scope, "a");
@ -606,7 +611,6 @@ option_t<report_t> * report_t::lookup_option(const char * p)
OPT_CH(amount_); OPT_CH(amount_);
else OPT(tail_); else OPT(tail_);
else OPT(total_); else OPT(total_);
else OPT(totals);
else OPT(total_data); else OPT(total_data);
else OPT(truncate_); else OPT(truncate_);
else OPT(total_width_); else OPT(total_width_);
@ -859,6 +863,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
case 'u': case 'u':
if (is_eq(p, "underline")) if (is_eq(p, "underline"))
return WRAP_FUNCTOR(fn_underline); return WRAP_FUNCTOR(fn_underline);
else if (is_eq(p, "unrounded"))
return MAKE_FUNCTOR(report_t::fn_unrounded);
break; break;
case 'w': case 'w':

View file

@ -145,6 +145,7 @@ public:
value_t fn_scrub(call_scope_t& scope); value_t fn_scrub(call_scope_t& scope);
value_t fn_quantity(call_scope_t& scope); value_t fn_quantity(call_scope_t& scope);
value_t fn_rounded(call_scope_t& scope); value_t fn_rounded(call_scope_t& scope);
value_t fn_unrounded(call_scope_t& scope);
value_t fn_truncated(call_scope_t& scope); value_t fn_truncated(call_scope_t& scope);
value_t fn_abs(call_scope_t& scope); value_t fn_abs(call_scope_t& scope);
value_t fn_justify(call_scope_t& scope); value_t fn_justify(call_scope_t& scope);
@ -613,8 +614,6 @@ public:
set_expr(args[0].to_string()); set_expr(args[0].to_string());
}); });
OPTION(report_t, totals);
OPTION_(report_t, total_data, DO() { // -J OPTION_(report_t, total_data, DO() { // -J
parent->HANDLER(format_).on_with(parent->HANDLER(plot_total_format_).value); parent->HANDLER(format_).on_with(parent->HANDLER(plot_total_format_).value);
}); });
@ -641,7 +640,9 @@ public:
parent->HANDLER(limit_).on("uncleared|pending"); parent->HANDLER(limit_).on("uncleared|pending");
}); });
OPTION(report_t, unround); OPTION_(report_t, unround, DO() {
parent->HANDLER(amount_).set_expr("unrounded(amount)");
});
OPTION_(report_t, weekly, DO() { // -W OPTION_(report_t, weekly, DO() { // -W
parent->HANDLER(period_).on("weekly"); parent->HANDLER(period_).on("weekly");

View file

@ -87,9 +87,14 @@ std::size_t session_t::read_journal(std::istream& in,
{ {
if (! master) if (! master)
master = journal->master; master = journal->master;
std::size_t count = journal->parse(in, *this, master, &pathname, std::size_t count = journal->parse(in, *this, master, &pathname,
HANDLED(strict)); HANDLED(strict));
clean_accounts(); // remove calculated totals
// remove calculated totals and flags
clean_posts();
clean_accounts();
return count; return count;
} }

View file

@ -1013,25 +1013,24 @@ post_t * instance_t::parse_post(char * line,
DEBUG("textual.parse", "line " << linenum << ": " DEBUG("textual.parse", "line " << linenum << ": "
<< "POST assign: parsed amt = " << *post->assigned_amount); << "POST assign: parsed amt = " << *post->assigned_amount);
account_t::xdata_t& xdata(post->account->xdata()); amount_t& amt(*post->assigned_amount);
amount_t& amt(*post->assigned_amount); value_t account_total(post->account->self_total(false));
DEBUG("post.assign", "line " << linenum << ": " DEBUG("post.assign", "line " << linenum << ": "
"account balance = " << xdata.self_details.total); "account balance = " << account_total);
DEBUG("post.assign", "line " << linenum << ": " DEBUG("post.assign", "line " << linenum << ": "
"post amount = " << amt); "post amount = " << amt);
amount_t diff; amount_t diff;
switch (xdata.self_details.total.type()) { switch (account_total.type()) {
case value_t::AMOUNT: case value_t::AMOUNT:
diff = amt - xdata.self_details.total.as_amount(); diff = amt - account_total.as_amount();
break; break;
case value_t::BALANCE: case value_t::BALANCE:
if (optional<amount_t> comm_bal = if (optional<amount_t> comm_bal =
xdata.self_details.total.as_balance() account_total.as_balance().commodity_amount(amt.commodity()))
.commodity_amount(amt.commodity()))
diff = amt - *comm_bal; diff = amt - *comm_bal;
else else
diff = amt; diff = amt;

View file

@ -930,6 +930,8 @@ inline value_t string_value(const string& str) {
return value_t(str, true); return value_t(str, true);
} }
#define VALUE_OR_ZERO(val) ((val).is_null() ? value_t(0L) : (val))
inline value_t mask_value(const string& str) { inline value_t mask_value(const string& str) {
return value_t(mask_t(str)); return value_t(mask_t(str));
} }

View file

@ -306,7 +306,7 @@ bool xact_base_t::finalize()
throw_(balance_error, _("Transaction does not balance")); throw_(balance_error, _("Transaction does not balance"));
} }
// Add the final calculated totals each to their related account // Add a pointer to each posting to their related accounts
if (dynamic_cast<xact_t *>(this)) { if (dynamic_cast<xact_t *>(this)) {
bool all_null = true; bool all_null = true;
@ -314,19 +314,15 @@ bool xact_base_t::finalize()
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
if (! post->amount.is_null()) { if (! post->amount.is_null()) {
all_null = false; all_null = false;
post->amount.in_place_reduce(); post->amount.in_place_reduce();
add_or_set_value(post->account->xdata().self_details.total,
post->amount);
DEBUG("xact.finalize.totals",
"Total for " << post->account->fullname() << " + "
<< post->amount << ": "
<< post->account->xdata().self_details.total);
} else { } else {
some_null = true; some_null = true;
} }
post->account->add_post(post);
post->xdata().add_flags(POST_EXT_VISITED);
post->account->xdata().add_flags(ACCOUNT_EXT_VISITED);
} }
if (all_null) if (all_null)
return false; // ignore this xact completely return false; // ignore this xact completely