added support for interval reporting; changed some option flags
This commit is contained in:
parent
02be02011b
commit
7610aec86d
14 changed files with 403 additions and 195 deletions
99
NEWS
99
NEWS
|
|
@ -3,29 +3,38 @@
|
||||||
|
|
||||||
* 2.0
|
* 2.0
|
||||||
|
|
||||||
- The code base has been rewritten for clarity and consistency. As a
|
- The code base has been rewritten for clarity and consistency. The
|
||||||
result, the code is much simpler and more robust (and in most cases
|
code is now simpler, more robust, and a fair bit faster.
|
||||||
faster).
|
|
||||||
|
|
||||||
- Register reports now show the account being credited/debited. Use
|
- The most significant feature addition in this version is the use of
|
||||||
new -o option to see "other accounts" -- or the account the
|
"value expressions". These are now used in many places to indicate
|
||||||
credit/debit came from. (This was the old behavior in 1.x, but can
|
what to display, the sorting order, and even the output format.
|
||||||
lead to confusing reports when viewing accounts with subaccounts.)
|
|
||||||
The -o option also works for balance reports, where it will show all
|
|
||||||
the account totals related to your query.
|
|
||||||
|
|
||||||
- Regexps specified after the command are applied to account names
|
A value expression is a simple string that uses one letter codes to
|
||||||
only. To search on a payee name, use "--" to separate the two kinds
|
indicate transaction, entry and account details. Logic and math
|
||||||
of regexps. For example, to find payee's named John within all
|
operators are supported, as well as a few useful functions. See the
|
||||||
Expenses accounts:
|
README.
|
||||||
|
|
||||||
|
- If the environment variable LEDGER is used, a binary cache of that
|
||||||
|
current ledger will be kept, to speed up later queries of the same
|
||||||
|
data. The default location is in ~/.ledger, but this can be changed
|
||||||
|
with the environment variable LEDGER_CACHE. This only happens if no
|
||||||
|
"-f" flag was seen (i.e., if the LEDGER environment variable is
|
||||||
|
used).
|
||||||
|
|
||||||
|
- New "-y DATEFMT" options will change the date format used throughout
|
||||||
|
ledger. The default is "%Y/%m/%d".
|
||||||
|
|
||||||
|
- Regexps specified after the command name now apply to account names
|
||||||
|
only. To search on a payee, use "--" to separate the two kinds of
|
||||||
|
regexps. For example, to find a payee named "John" within all
|
||||||
|
Expenses accounts, use:
|
||||||
|
|
||||||
ledger register expenses -- john
|
ledger register expenses -- john
|
||||||
|
|
||||||
- The use of "+" and "-" in ledger files (to specify permanent
|
FYI: The above command is identical (and internally converted) to:
|
||||||
regexps) has been removed.
|
|
||||||
|
|
||||||
- The -G switch no longer generates gnuplot-safe data. It now reports
|
ledger -l "/expenses/|//john/" register
|
||||||
totals in terms of net gain/loss.
|
|
||||||
|
|
||||||
- To include entries from a file into a specific account, use:
|
- To include entries from a file into a specific account, use:
|
||||||
|
|
||||||
|
|
@ -36,10 +45,12 @@
|
||||||
All entries specified within the "@ ACCOUNT/@@" range will be added
|
All entries specified within the "@ ACCOUNT/@@" range will be added
|
||||||
under that account.
|
under that account.
|
||||||
|
|
||||||
- If the environment variable LEDGER_CACHE is set to a filename, a a
|
- Register reports now show the account being changed. Use the -r
|
||||||
binary dump of the current ledger will be written then, to speed up
|
option to see "other accounts" -- or the account the credit/debit
|
||||||
later queries of the same data. This only happens if no "-f" flag
|
came from. (This was the old behavior in 1.x, but can lead to
|
||||||
was seen (i.e., if the LEDGER environment variable is used).
|
confusing reports when viewing accounts with subaccounts.) The -r
|
||||||
|
option also works for balance reports, where it will show all the
|
||||||
|
account totals related to your query.
|
||||||
|
|
||||||
- There are several new default reporting styles, which work both in
|
- There are several new default reporting styles, which work both in
|
||||||
the balance and register reports:
|
the balance and register reports:
|
||||||
|
|
@ -54,6 +65,32 @@
|
||||||
-W Report the trend, with older values affecting the trend less
|
-W Report the trend, with older values affecting the trend less
|
||||||
-X Report expected amount for the next transaction
|
-X Report expected amount for the next transaction
|
||||||
|
|
||||||
|
- Automated transactions now use a single value expression as a
|
||||||
|
predicate. This means the new syntax is:
|
||||||
|
|
||||||
|
= VALUE-EXPR
|
||||||
|
TRANSACTIONS...
|
||||||
|
|
||||||
|
Only one VALUE-EXPR is supported, compared to the multiple account
|
||||||
|
regexps supported before. By using a VALUE-EXPR as a predicate,
|
||||||
|
matching may now be much more comprehensive and selective.
|
||||||
|
|
||||||
|
- The use of "+" and "-" in ledger files (to specify permanent
|
||||||
|
regexps) has been removed.
|
||||||
|
|
||||||
|
- The -G switch no longer generates gnuplot-safe data. It now reports
|
||||||
|
totals in terms of net gain/loss.
|
||||||
|
|
||||||
|
- The -l flag now takes an expression string as a "predicate".
|
||||||
|
Therefore, to equal the old behavior of "-l $100", you would use:
|
||||||
|
|
||||||
|
-l AT<{$100}
|
||||||
|
|
||||||
|
- The -S flag now takes an expression string, which yields the value
|
||||||
|
that will be sorted on.
|
||||||
|
|
||||||
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
- Value expressions are now supported, where the totals reported can
|
- Value expressions are now supported, where the totals reported can
|
||||||
be changed using -t and -T and an expression string composed of:
|
be changed using -t and -T and an expression string composed of:
|
||||||
|
|
||||||
|
|
@ -101,14 +138,6 @@
|
||||||
-W == -t a -T MD(MA*(d-b/e-b))
|
-W == -t a -T MD(MA*(d-b/e-b))
|
||||||
-X == -t a -T a+MD(MA*(d-b/e-b))
|
-X == -t a -T a+MD(MA*(d-b/e-b))
|
||||||
|
|
||||||
- The -l flag now takes an expression string as a "predicate".
|
|
||||||
Therefore, to equal the old behavior of "-l $100", you would use:
|
|
||||||
|
|
||||||
-l AT<{$100}
|
|
||||||
|
|
||||||
- The -S flag now takes an expression string, which yields the value
|
|
||||||
that will be sorted on.
|
|
||||||
|
|
||||||
- User-specified format strings are supported with a -F option. The
|
- User-specified format strings are supported with a -F option. The
|
||||||
strings are very much like printf format strings, with the following
|
strings are very much like printf format strings, with the following
|
||||||
syntax for each substitution:
|
syntax for each substitution:
|
||||||
|
|
@ -142,17 +171,7 @@
|
||||||
%?10d %?-.20p %-.22a %12.66t %12.80T
|
%?10d %?-.20p %-.22a %12.66t %12.80T
|
||||||
%20T %-a
|
%20T %-a
|
||||||
|
|
||||||
- Automated transactions now use a single value expression as a
|
* 1.7
|
||||||
predicate. This means the new syntax is:
|
|
||||||
|
|
||||||
= VALUE-EXPR
|
|
||||||
TRANSACTIONS...
|
|
||||||
|
|
||||||
Only one VALUE-EXPR is supported, compared to the multiple account
|
|
||||||
regexps supported before. By using a VALUE-EXPR as a predicate,
|
|
||||||
matching may now be much more comprehensive and selective.
|
|
||||||
|
|
||||||
* 1.7 (never released)
|
|
||||||
|
|
||||||
- Pricing histories are now supported, so that ledger remembers
|
- Pricing histories are now supported, so that ledger remembers
|
||||||
historical pricing of all commodities, and can give register reports
|
historical pricing of all commodities, and can give register reports
|
||||||
|
|
|
||||||
73
datetime.cc
73
datetime.cc
|
|
@ -31,8 +31,77 @@ static const char * formats[] = {
|
||||||
|
|
||||||
std::time_t interval_t::increment(const std::time_t moment)
|
std::time_t interval_t::increment(const std::time_t moment)
|
||||||
{
|
{
|
||||||
// jww (2004-08-11): NYI
|
std::time_t then = moment;
|
||||||
return moment + seconds;
|
|
||||||
|
if (years || months) {
|
||||||
|
struct std::tm * desc = std::gmtime(&then);
|
||||||
|
if (years)
|
||||||
|
desc->tm_year += years;
|
||||||
|
if (months) {
|
||||||
|
desc->tm_mon += months;
|
||||||
|
|
||||||
|
if (desc->tm_mon > 11) {
|
||||||
|
desc->tm_year++;
|
||||||
|
desc->tm_mon -= 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
then = std::mktime(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return then + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
interval_t * interval_t::parse(std::istream& in)
|
||||||
|
{
|
||||||
|
unsigned long years = 0;
|
||||||
|
unsigned long months = 0;
|
||||||
|
unsigned long seconds = 0;
|
||||||
|
|
||||||
|
std::string word;
|
||||||
|
in >> word;
|
||||||
|
if (word == "every") {
|
||||||
|
in >> word;
|
||||||
|
if (std::isdigit(word[0])) {
|
||||||
|
int quantity = std::atol(word.c_str());
|
||||||
|
in >> word;
|
||||||
|
if (word == "days")
|
||||||
|
seconds = 86400 * quantity;
|
||||||
|
else if (word == "weeks")
|
||||||
|
seconds = 7 * 86400 * quantity;
|
||||||
|
else if (word == "months")
|
||||||
|
months = quantity;
|
||||||
|
else if (word == "quarters")
|
||||||
|
months = 3 * quantity;
|
||||||
|
else if (word == "years")
|
||||||
|
years = quantity;
|
||||||
|
}
|
||||||
|
else if (word == "day")
|
||||||
|
seconds = 86400;
|
||||||
|
else if (word == "week")
|
||||||
|
seconds = 7 * 86400;
|
||||||
|
else if (word == "monthly")
|
||||||
|
months = 1;
|
||||||
|
else if (word == "quarter")
|
||||||
|
months = 3;
|
||||||
|
else if (word == "year")
|
||||||
|
years = 1;
|
||||||
|
}
|
||||||
|
else if (word == "daily")
|
||||||
|
seconds = 86400;
|
||||||
|
else if (word == "weekly")
|
||||||
|
seconds = 7 * 86400;
|
||||||
|
else if (word == "biweekly")
|
||||||
|
seconds = 14 * 86400;
|
||||||
|
else if (word == "monthly")
|
||||||
|
months = 1;
|
||||||
|
else if (word == "bimonthly")
|
||||||
|
months = 2;
|
||||||
|
else if (word == "quarterly")
|
||||||
|
months = 3;
|
||||||
|
else if (word == "yearly")
|
||||||
|
years = 1;
|
||||||
|
|
||||||
|
return new interval_t(seconds, months, years);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parse_date_mask(const char * date_str, struct std::tm * result)
|
bool parse_date_mask(const char * date_str, struct std::tm * result)
|
||||||
|
|
|
||||||
20
datetime.h
20
datetime.h
|
|
@ -11,22 +11,24 @@ struct interval_t
|
||||||
unsigned long months;
|
unsigned long months;
|
||||||
unsigned long seconds;
|
unsigned long seconds;
|
||||||
|
|
||||||
interval_t(unsigned long _seconds, unsigned long _months,
|
interval_t(unsigned long _seconds, unsigned long _months = 0,
|
||||||
unsigned long _years)
|
unsigned long _years = 0)
|
||||||
: years(_years), months(_months), seconds(_seconds) {}
|
: years(_years), months(_months), seconds(_seconds) {}
|
||||||
|
|
||||||
std::time_t increment(const std::time_t);
|
std::time_t increment(const std::time_t);
|
||||||
|
|
||||||
|
static interval_t * parse(std::istream& in);
|
||||||
};
|
};
|
||||||
|
|
||||||
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 bool quick_parse_date(char * date_str, std::time_t * result);
|
|
||||||
|
|
||||||
extern struct std::tm * now_tm;
|
extern struct std::tm * now_tm;
|
||||||
|
|
||||||
|
bool parse_date_mask(const char * date_str, struct std::tm * result);
|
||||||
|
|
||||||
|
bool parse_date(const char * date_str, std::time_t * result,
|
||||||
|
const int year = -1);
|
||||||
|
|
||||||
|
bool quick_parse_date(char * date_str, std::time_t * result);
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _DATETIME_H
|
#endif // _DATETIME_H
|
||||||
|
|
|
||||||
8
debug.h
8
debug.h
|
|
@ -81,6 +81,14 @@ inline bool _debug_active(const char * const cls) {
|
||||||
}
|
}
|
||||||
#define DEBUG_PRINT_(x) DEBUG_PRINT(_debug_cls, x)
|
#define DEBUG_PRINT_(x) DEBUG_PRINT(_debug_cls, x)
|
||||||
|
|
||||||
|
#define DEBUG_PRINT_TIME(cls, x) { \
|
||||||
|
char buf[256]; \
|
||||||
|
std::strftime(buf, 255, "%Y/%m/%d", std::gmtime(&x)); \
|
||||||
|
DEBUG_PRINT(cls, #x << " is " << buf); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEBUG_PRINT_TIME_(x) DEBUG_PRINT_TIME(_debug_cls, x)
|
||||||
|
|
||||||
#define CONFIRM(x) assert(x)
|
#define CONFIRM(x) assert(x)
|
||||||
|
|
||||||
#if RELEASE_LEVEL == DEVELOPER
|
#if RELEASE_LEVEL == DEVELOPER
|
||||||
|
|
|
||||||
13
error.h
13
error.h
|
|
@ -27,11 +27,18 @@ class compute_error : public error
|
||||||
virtual ~compute_error() throw() {}
|
virtual ~compute_error() throw() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class expr_error : public error
|
class value_expr_error : public error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
expr_error(const std::string& reason) throw() : error(reason) {}
|
value_expr_error(const std::string& reason) throw() : error(reason) {}
|
||||||
virtual ~expr_error() throw() {}
|
virtual ~value_expr_error() throw() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class interval_expr_error : public error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
interval_expr_error(const std::string& reason) throw() : error(reason) {}
|
||||||
|
virtual ~interval_expr_error() throw() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class format_error : public error
|
class format_error : public error
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ std::string partial_account_name(const account_t * account)
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string format_t::date_format = "%Y/%m/%d";
|
||||||
|
|
||||||
std::auto_ptr<value_expr_t> format_t::value_expr;
|
std::auto_ptr<value_expr_t> format_t::value_expr;
|
||||||
std::auto_ptr<value_expr_t> format_t::total_expr;
|
std::auto_ptr<value_expr_t> format_t::total_expr;
|
||||||
|
|
||||||
|
|
@ -114,15 +116,14 @@ element_t * format_t::parse_elements(const std::string& fmt)
|
||||||
current->chars = num;
|
current->chars = num;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'd':
|
case 'D':
|
||||||
current->type = element_t::DATE_STRING;
|
current->type = element_t::DATE_STRING;
|
||||||
// jww (2004-08-10): allow this to be changed
|
current->chars = format_t::date_format;
|
||||||
current->chars = "%Y/%m/%d";
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'X': current->type = element_t::CLEARED; break;
|
case 'X': current->type = element_t::CLEARED; break;
|
||||||
case 'C': current->type = element_t::CODE; 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 'o': current->type = element_t::OPT_AMOUNT; break;
|
||||||
|
|
|
||||||
17
format.h
17
format.h
|
|
@ -52,6 +52,8 @@ struct format_t
|
||||||
{
|
{
|
||||||
element_t * elements;
|
element_t * elements;
|
||||||
|
|
||||||
|
static std::string date_format;
|
||||||
|
|
||||||
static std::auto_ptr<value_expr_t> value_expr;
|
static std::auto_ptr<value_expr_t> value_expr;
|
||||||
static std::auto_ptr<value_expr_t> total_expr;
|
static std::auto_ptr<value_expr_t> total_expr;
|
||||||
|
|
||||||
|
|
@ -107,14 +109,15 @@ class format_transactions : public item_handler<transaction_t>
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void operator()(transaction_t * xact) {
|
virtual void operator()(transaction_t * xact) {
|
||||||
if (last_entry != xact->entry) {
|
if (! (xact->dflags & TRANSACTION_DISPLAYED)) {
|
||||||
first_line_format.format_elements(output_stream, details_t(xact));
|
if (last_entry != xact->entry) {
|
||||||
last_entry = xact->entry;
|
first_line_format.format_elements(output_stream, details_t(xact));
|
||||||
} else {
|
last_entry = xact->entry;
|
||||||
next_lines_format.format_elements(output_stream, details_t(xact));
|
} else {
|
||||||
|
next_lines_format.format_elements(output_stream, details_t(xact));
|
||||||
|
}
|
||||||
|
xact->dflags |= TRANSACTION_DISPLAYED;
|
||||||
}
|
}
|
||||||
|
|
||||||
xact->dflags |= TRANSACTION_DISPLAYED;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
177
main.cc
177
main.cc
|
|
@ -12,39 +12,13 @@
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
static const std::string bal_fmt = "%20T %2_%-n\n";
|
static const std::string bal_fmt = "%20T %2_%-n\n";
|
||||||
|
|
||||||
static const std::string reg_fmt
|
static const std::string reg_fmt
|
||||||
= "%10d %-.20p %-.22N %12.66t %12.80T\n\
|
= "%D %-.20P %-.22N %12.66t %12.80T\n\
|
||||||
%/ %-.22N %12.66t %12.80T\n";
|
%/ %-.22N %12.66t %12.80T\n";
|
||||||
|
|
||||||
static const std::string print_fmt
|
static const std::string print_fmt
|
||||||
= "\n%10d %X%C%p\n %-34N %12o\n%/ %-34N %12o\n";
|
= "\n%D %X%C%P\n %-34N %12o\n%/ %-34N %12o\n";
|
||||||
|
|
||||||
static const std::string equity_fmt
|
static const std::string equity_fmt
|
||||||
= "\n%10d %X%C%p\n%/ %-34N %12t\n";
|
= "\n%D %X%C%P\n%/ %-34N %12t\n";
|
||||||
|
|
||||||
|
|
||||||
void set_price_conversion(const std::string& setting)
|
|
||||||
{
|
|
||||||
char buf[128];
|
|
||||||
std::strcpy(buf, setting.c_str());
|
|
||||||
|
|
||||||
assert(setting.length() < 128);
|
|
||||||
|
|
||||||
char * c = buf;
|
|
||||||
char * p = std::strchr(buf, '=');
|
|
||||||
if (! p) {
|
|
||||||
std::cerr << "Warning: Invalid price setting: " << setting << std::endl;
|
|
||||||
} else {
|
|
||||||
*p++ = '\0';
|
|
||||||
|
|
||||||
amount_t price;
|
|
||||||
price.parse(p);
|
|
||||||
|
|
||||||
commodity_t * commodity = commodity_t::find_commodity(c, true);
|
|
||||||
commodity->set_conversion(price);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static long pricing_leeway = 24 * 3600;
|
static long pricing_leeway = 24 * 3600;
|
||||||
|
|
@ -102,6 +76,20 @@ void download_price_quote(commodity_t * commodity,
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
|
|
||||||
|
static std::string ledger_cache_file()
|
||||||
|
{
|
||||||
|
std::string cache_file;
|
||||||
|
|
||||||
|
if (const char * p = std::getenv("LEDGER_CACHE")) {
|
||||||
|
cache_file = p;
|
||||||
|
}
|
||||||
|
else if (const char * p = std::getenv("HOME")) {
|
||||||
|
cache_file = p;
|
||||||
|
cache_file += "/.ledger";
|
||||||
|
}
|
||||||
|
return cache_file;
|
||||||
|
}
|
||||||
|
|
||||||
static void show_version(std::ostream& out)
|
static void show_version(std::ostream& out)
|
||||||
{
|
{
|
||||||
out
|
out
|
||||||
|
|
@ -157,9 +145,13 @@ int main(int argc, char * argv[])
|
||||||
{
|
{
|
||||||
using namespace ledger;
|
using namespace ledger;
|
||||||
|
|
||||||
std::auto_ptr<journal_t> journal(new journal_t);
|
std::auto_ptr<journal_t> journal(new journal_t);
|
||||||
std::list<std::string> files;
|
std::list<std::string> files;
|
||||||
std::auto_ptr<value_expr_t> sort_order;
|
std::auto_ptr<value_expr_t> sort_order;
|
||||||
|
std::auto_ptr<std::ostream> output_stream;
|
||||||
|
std::auto_ptr<interval_t> report_interval;
|
||||||
|
|
||||||
|
#define OUT() (output_stream.get() ? *output_stream.get() : std::cout)
|
||||||
|
|
||||||
std::string predicate;
|
std::string predicate;
|
||||||
std::string display_predicate;
|
std::string display_predicate;
|
||||||
|
|
@ -167,6 +159,7 @@ int main(int argc, char * argv[])
|
||||||
std::string sort_string;
|
std::string sort_string;
|
||||||
std::string value_expr = "a";
|
std::string value_expr = "a";
|
||||||
std::string total_expr = "T";
|
std::string total_expr = "T";
|
||||||
|
std::time_t interval_begin = 0;
|
||||||
|
|
||||||
bool show_subtotals = true;
|
bool show_subtotals = true;
|
||||||
bool show_expanded = false;
|
bool show_expanded = false;
|
||||||
|
|
@ -205,18 +198,20 @@ int main(int argc, char * argv[])
|
||||||
|
|
||||||
cache_dirty = true;
|
cache_dirty = true;
|
||||||
|
|
||||||
if (use_cache)
|
if (use_cache) {
|
||||||
if (const char * p = std::getenv("LEDGER_CACHE"))
|
std::string cache_file = ledger_cache_file();
|
||||||
if (access(p, R_OK) != -1) {
|
if (! cache_file.empty() &&
|
||||||
std::ifstream instr(p);
|
access(cache_file.c_str(), R_OK) != -1) {
|
||||||
if (! read_binary_journal(instr, std::getenv("LEDGER"),
|
std::ifstream stream(cache_file.c_str());
|
||||||
journal.get())) {
|
if (! read_binary_journal(stream, std::getenv("LEDGER"),
|
||||||
// Throw away what's been read, and create a new journal
|
journal.get())) {
|
||||||
journal.reset(new journal_t);
|
// Throw away what's been read, and create a new journal
|
||||||
} else {
|
journal.reset(new journal_t);
|
||||||
cache_dirty = false;
|
} else {
|
||||||
}
|
cache_dirty = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the command-line options
|
// Parse the command-line options
|
||||||
|
|
@ -224,7 +219,7 @@ int main(int argc, char * argv[])
|
||||||
int c, index;
|
int c, index;
|
||||||
while (-1 !=
|
while (-1 !=
|
||||||
(c = getopt(argc, argv,
|
(c = getopt(argc, argv,
|
||||||
"+ABb:Ccd:DEe:F:f:Ghi:L:l:MnoOP:p:QRS:st:T:UVvWXZ"))) {
|
"+ABb:Ccd:DEe:F:f:Ghi:L:l:MnOo:P:p:QRrS:sT:t:UVvWXYy:Zz:"))) {
|
||||||
switch (char(c)) {
|
switch (char(c)) {
|
||||||
// Basic options
|
// Basic options
|
||||||
case 'h':
|
case 'h':
|
||||||
|
|
@ -240,8 +235,19 @@ int main(int argc, char * argv[])
|
||||||
use_cache = false;
|
use_cache = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'o':
|
||||||
|
if (std::string(optarg) != "-")
|
||||||
|
output_stream.reset(new std::ofstream(optarg));
|
||||||
|
break;
|
||||||
|
|
||||||
case 'p':
|
case 'p':
|
||||||
set_price_conversion(optarg);
|
if (char * p = std::strchr(optarg, '=')) {
|
||||||
|
*p = ' ';
|
||||||
|
std::string conversion = "C ";
|
||||||
|
conversion += p;
|
||||||
|
std::istringstream stream(conversion);
|
||||||
|
parse_textual_journal(stream, journal.get(), journal->master);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'b':
|
case 'b':
|
||||||
|
|
@ -294,6 +300,10 @@ int main(int argc, char * argv[])
|
||||||
format_string = optarg;
|
format_string = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'y':
|
||||||
|
format_t::date_format = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'E':
|
case 'E':
|
||||||
show_empty = true;
|
show_empty = true;
|
||||||
break;
|
break;
|
||||||
|
|
@ -310,10 +320,39 @@ int main(int argc, char * argv[])
|
||||||
sort_string = optarg;
|
sort_string = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'r':
|
||||||
show_related = true;
|
show_related = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'z': {
|
||||||
|
std::string str(optarg);
|
||||||
|
std::istringstream stream(str);
|
||||||
|
report_interval.reset(interval_t::parse(stream));
|
||||||
|
|
||||||
|
if (! stream.eof()) {
|
||||||
|
std::string word;
|
||||||
|
stream >> word;
|
||||||
|
if (word == "from") {
|
||||||
|
stream >> word;
|
||||||
|
if (! parse_date(word.c_str(), &interval_begin))
|
||||||
|
throw interval_expr_error("Could not parse 'from' date");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'W':
|
||||||
|
report_interval.reset(new interval_t(604800, 0, 0));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'M':
|
||||||
|
report_interval.reset(new interval_t(0, 1, 0));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Y':
|
||||||
|
report_interval.reset(new interval_t(0, 0, 1));
|
||||||
|
break;
|
||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
if (! predicate.empty())
|
if (! predicate.empty())
|
||||||
predicate += "&";
|
predicate += "&";
|
||||||
|
|
@ -386,23 +425,16 @@ int main(int argc, char * argv[])
|
||||||
total_expr = "DMT";
|
total_expr = "DMT";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'Z':
|
case 'X':
|
||||||
value_expr = "a";
|
value_expr = "a";
|
||||||
total_expr = "MDMT";
|
total_expr = "MDMT";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if 0
|
case 'Z':
|
||||||
case 'W':
|
|
||||||
value_expr = "a";
|
value_expr = "a";
|
||||||
total_expr = "MD(MT*(d-b/e-b))";
|
total_expr = "MD(MT/(1+(((t-d)/(30*86400))<0?0:((t-d)/(30*86400)))))";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'X':
|
|
||||||
value_expr = "a";
|
|
||||||
total_expr = "a+MD(MT*(d-b/e-b))";
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
|
|
@ -444,7 +476,7 @@ int main(int argc, char * argv[])
|
||||||
std::ifstream db(path);
|
std::ifstream db(path);
|
||||||
journal->sources.push_back(path);
|
journal->sources.push_back(path);
|
||||||
entry_count += parse_textual_journal(db, journal.get(),
|
entry_count += parse_textual_journal(db, journal.get(),
|
||||||
journal->master);
|
journal->master);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (error& err) {
|
catch (error& err) {
|
||||||
|
|
@ -597,7 +629,7 @@ int main(int argc, char * argv[])
|
||||||
formatter.reset(new filter_transactions(formatter.release(), predicate));
|
formatter.reset(new filter_transactions(formatter.release(), predicate));
|
||||||
walk_entries(journal->entries, *formatter.get());
|
walk_entries(journal->entries, *formatter.get());
|
||||||
|
|
||||||
format_account acct_formatter(std::cout, format, display_predicate);
|
format_account acct_formatter(OUT(), format, display_predicate);
|
||||||
if (show_subtotals)
|
if (show_subtotals)
|
||||||
sum_accounts(journal->master);
|
sum_accounts(journal->master);
|
||||||
walk_accounts(journal->master, acct_formatter, sort_order.get());
|
walk_accounts(journal->master, acct_formatter, sort_order.get());
|
||||||
|
|
@ -605,7 +637,7 @@ int main(int argc, char * argv[])
|
||||||
if (format_account::disp_subaccounts_p(journal->master)) {
|
if (format_account::disp_subaccounts_p(journal->master)) {
|
||||||
std::string end_format = "--------------------\n";
|
std::string end_format = "--------------------\n";
|
||||||
format.reset(end_format + f);
|
format.reset(end_format + f);
|
||||||
format.format_elements(std::cout, details_t(journal->master));
|
format.format_elements(OUT(), details_t(journal->master));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (command == "E") {
|
else if (command == "E") {
|
||||||
|
|
@ -614,12 +646,12 @@ int main(int argc, char * argv[])
|
||||||
formatter.reset(new filter_transactions(formatter.release(), predicate));
|
formatter.reset(new filter_transactions(formatter.release(), predicate));
|
||||||
walk_entries(journal->entries, *formatter.get());
|
walk_entries(journal->entries, *formatter.get());
|
||||||
|
|
||||||
format_equity acct_formatter(std::cout, format, nformat, display_predicate);
|
format_equity acct_formatter(OUT(), format, nformat, display_predicate);
|
||||||
sum_accounts(journal->master);
|
sum_accounts(journal->master);
|
||||||
walk_accounts(journal->master, acct_formatter, sort_order.get());
|
walk_accounts(journal->master, acct_formatter, sort_order.get());
|
||||||
}
|
}
|
||||||
else if (command == "e") {
|
else if (command == "e") {
|
||||||
format_transactions formatter(std::cout, format, nformat);
|
format_transactions formatter(OUT(), format, nformat);
|
||||||
walk_transactions(new_entry->transactions, formatter);
|
walk_transactions(new_entry->transactions, formatter);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -631,7 +663,7 @@ int main(int argc, char * argv[])
|
||||||
|
|
||||||
// format_transactions write each transaction received to the
|
// format_transactions write each transaction received to the
|
||||||
// output stream.
|
// output stream.
|
||||||
formatter.reset(new format_transactions(std::cout, format, nformat));
|
formatter.reset(new format_transactions(OUT(), format, nformat));
|
||||||
|
|
||||||
// sort_transactions will sort all the transactions it sees, based
|
// sort_transactions will sort all the transactions it sees, based
|
||||||
// on the `sort_order' value expression.
|
// on the `sort_order' value expression.
|
||||||
|
|
@ -653,8 +685,8 @@ int main(int argc, char * argv[])
|
||||||
// list to account for changes in market value of commodities,
|
// list to account for changes in market value of commodities,
|
||||||
// which otherwise would affect the running total unpredictably.
|
// which otherwise would affect the running total unpredictably.
|
||||||
if (show_revalued)
|
if (show_revalued)
|
||||||
formatter.reset(new changed_value_transactions(formatter.release() /*,
|
formatter.reset(new changed_value_transactions(formatter.release(),
|
||||||
show_revalued_only*/));
|
show_revalued_only));
|
||||||
|
|
||||||
// collapse_transactions causes entries with multiple transactions
|
// collapse_transactions causes entries with multiple transactions
|
||||||
// to appear as entries with a subtotaled transaction for each
|
// to appear as entries with a subtotaled transaction for each
|
||||||
|
|
@ -671,9 +703,10 @@ int main(int argc, char * argv[])
|
||||||
// everything.
|
// everything.
|
||||||
if (show_expanded)
|
if (show_expanded)
|
||||||
formatter.reset(new subtotal_transactions(formatter.release()));
|
formatter.reset(new subtotal_transactions(formatter.release()));
|
||||||
else if (0)
|
else if (report_interval.get())
|
||||||
formatter.reset(new interval_transactions(formatter.release(), 0,
|
formatter.reset(new interval_transactions(formatter.release(),
|
||||||
interval_t(9676800, 0, 0)));
|
*report_interval.get(),
|
||||||
|
interval_begin));
|
||||||
|
|
||||||
// related_transactions will pass along all transactions related
|
// related_transactions will pass along all transactions related
|
||||||
// to the transaction received. If `show_all_related' is true,
|
// to the transaction received. If `show_all_related' is true,
|
||||||
|
|
@ -703,12 +736,14 @@ int main(int argc, char * argv[])
|
||||||
|
|
||||||
// Save the cache, if need be
|
// Save the cache, if need be
|
||||||
|
|
||||||
if (use_cache && cache_dirty)
|
if (use_cache && cache_dirty) {
|
||||||
if (const char * p = std::getenv("LEDGER_CACHE")) {
|
std::string cache_file = ledger_cache_file();
|
||||||
std::ofstream outstr(p);
|
if (! cache_file.empty()) {
|
||||||
assert(std::getenv("LEDGER"));
|
assert(std::getenv("LEDGER"));
|
||||||
write_binary_journal(outstr, journal.get(), std::getenv("LEDGER"));
|
std::ofstream stream(cache_file.c_str());
|
||||||
|
write_binary_journal(stream, journal.get(), std::getenv("LEDGER"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ running_total = 0.0
|
||||||
index = 1
|
index = 1
|
||||||
last_line = ""
|
last_line = ""
|
||||||
|
|
||||||
for line in os.popen("./ledger %s reg %s" % (sys.argv[1], sys.argv[2])):
|
for line in os.popen("../ledger %s reg %s" % (sys.argv[1], sys.argv[2])):
|
||||||
value = clean(line[55:67])
|
value = clean(line[55:67])
|
||||||
total = clean(line[68:80])
|
total = clean(line[68:80])
|
||||||
|
|
||||||
|
|
@ -29,7 +29,7 @@ for line in os.popen("./ledger %s reg %s" % (sys.argv[1], sys.argv[2])):
|
||||||
|
|
||||||
balance_total = 0.0
|
balance_total = 0.0
|
||||||
|
|
||||||
for line in os.popen("./ledger %s bal %s" % (sys.argv[1], sys.argv[2])):
|
for line in os.popen("../ledger %s bal %s" % (sys.argv[1], sys.argv[2])):
|
||||||
balance_total = clean(line[:20])
|
balance_total = clean(line[:20])
|
||||||
|
|
||||||
if abs(balance_total - running_total) > 0.001:
|
if abs(balance_total - running_total) > 0.001:
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
for test in \
|
for test in \
|
||||||
"-O nrl:checking" \
|
"-O nrl:checking" \
|
||||||
"-B 401" \
|
"-B 401" \
|
||||||
"-V 401" \
|
"-V 401" \
|
||||||
"-G 401" \
|
"-G 401" \
|
||||||
"-B retire" \
|
"-B retire" \
|
||||||
"-V retire" \
|
"-V retire" \
|
||||||
"-G retire"
|
"-G retire"
|
||||||
|
|
|
||||||
60
valexpr.cc
60
valexpr.cc
|
|
@ -125,6 +125,10 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
|
||||||
result = (unsigned int) std::time(NULL);
|
result = (unsigned int) std::time(NULL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TODAY:
|
||||||
|
result = (unsigned int) std::time(NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
case CLEARED:
|
case CLEARED:
|
||||||
if (details.entry) {
|
if (details.entry) {
|
||||||
result = details.entry->state == entry_t::CLEARED;
|
result = details.entry->state == entry_t::CLEARED;
|
||||||
|
|
@ -188,6 +192,15 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
|
||||||
result = abs(result);
|
result = abs(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case F_STRIP: {
|
||||||
|
assert(left);
|
||||||
|
left->compute(result, details);
|
||||||
|
amount_t amt = result.amount();
|
||||||
|
amt.commodity = commodity_t::null_commodity;
|
||||||
|
result = amt;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case F_PAYEE_MASK:
|
case F_PAYEE_MASK:
|
||||||
assert(mask);
|
assert(mask);
|
||||||
if (details.entry)
|
if (details.entry)
|
||||||
|
|
@ -214,6 +227,10 @@ void value_expr_t::compute(balance_t& result, const details_t& details) const
|
||||||
moment = std::time(NULL);
|
moment = std::time(NULL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TODAY:
|
||||||
|
moment = std::time(NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw compute_error("Invalid date passed to P(value,date)");
|
throw compute_error("Invalid date passed to P(value,date)");
|
||||||
}
|
}
|
||||||
|
|
@ -341,7 +358,7 @@ value_expr_t * parse_value_term(std::istream& in)
|
||||||
if (c == '}')
|
if (c == '}')
|
||||||
in.get(c);
|
in.get(c);
|
||||||
else
|
else
|
||||||
throw expr_error("Missing '}'");
|
throw value_expr_error("Missing '}'");
|
||||||
} else {
|
} else {
|
||||||
while (! in.eof() && std::isdigit(c) || c == '.') {
|
while (! in.eof() && std::isdigit(c) || c == '.') {
|
||||||
in.get(c);
|
in.get(c);
|
||||||
|
|
@ -363,6 +380,7 @@ value_expr_t * parse_value_term(std::istream& in)
|
||||||
case 'a': node = new value_expr_t(value_expr_t::AMOUNT); break;
|
case 'a': node = new value_expr_t(value_expr_t::AMOUNT); break;
|
||||||
case 'c': node = new value_expr_t(value_expr_t::COST); break;
|
case 'c': node = new value_expr_t(value_expr_t::COST); break;
|
||||||
case 'd': node = new value_expr_t(value_expr_t::DATE); break;
|
case 'd': node = new value_expr_t(value_expr_t::DATE); break;
|
||||||
|
case 't': node = new value_expr_t(value_expr_t::TODAY); break;
|
||||||
case 'X': node = new value_expr_t(value_expr_t::CLEARED); break;
|
case 'X': node = new value_expr_t(value_expr_t::CLEARED); break;
|
||||||
case 'R': node = new value_expr_t(value_expr_t::REAL); break;
|
case 'R': node = new value_expr_t(value_expr_t::REAL); break;
|
||||||
case 'n': node = new value_expr_t(value_expr_t::INDEX); break;
|
case 'n': node = new value_expr_t(value_expr_t::INDEX); break;
|
||||||
|
|
@ -389,6 +407,11 @@ value_expr_t * parse_value_term(std::istream& in)
|
||||||
node->left = parse_value_term(in);
|
node->left = parse_value_term(in);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'S':
|
||||||
|
node = new value_expr_t(value_expr_t::F_STRIP);
|
||||||
|
node->left = parse_value_term(in);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
node = new value_expr_t(value_expr_t::F_ARITH_MEAN);
|
node = new value_expr_t(value_expr_t::F_ARITH_MEAN);
|
||||||
node->left = parse_value_term(in);
|
node->left = parse_value_term(in);
|
||||||
|
|
@ -413,7 +436,7 @@ value_expr_t * parse_value_term(std::istream& in)
|
||||||
if (peek_next_nonws(in) == ')')
|
if (peek_next_nonws(in) == ')')
|
||||||
in.get(c);
|
in.get(c);
|
||||||
else
|
else
|
||||||
throw expr_error("Missing ')'");
|
throw value_expr_error("Missing ')'");
|
||||||
} else {
|
} else {
|
||||||
node->left = parse_value_term(in);
|
node->left = parse_value_term(in);
|
||||||
}
|
}
|
||||||
|
|
@ -445,7 +468,7 @@ value_expr_t * parse_value_term(std::istream& in)
|
||||||
value_expr_t::F_PAYEE_MASK : value_expr_t::F_ACCOUNT_MASK);
|
value_expr_t::F_PAYEE_MASK : value_expr_t::F_ACCOUNT_MASK);
|
||||||
node->mask = new mask_t(ident);
|
node->mask = new mask_t(ident);
|
||||||
} else {
|
} else {
|
||||||
throw expr_error("Missing closing '/'");
|
throw value_expr_error("Missing closing '/'");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -455,7 +478,7 @@ value_expr_t * parse_value_term(std::istream& in)
|
||||||
if (peek_next_nonws(in) == ')')
|
if (peek_next_nonws(in) == ')')
|
||||||
in.get(c);
|
in.get(c);
|
||||||
else
|
else
|
||||||
throw expr_error("Missing ')'");
|
throw value_expr_error("Missing ')'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '[': {
|
case '[': {
|
||||||
|
|
@ -471,9 +494,9 @@ value_expr_t * parse_value_term(std::istream& in)
|
||||||
in.get(c);
|
in.get(c);
|
||||||
node = new value_expr_t(value_expr_t::CONSTANT_T);
|
node = new value_expr_t(value_expr_t::CONSTANT_T);
|
||||||
if (! parse_date(ident.c_str(), &node->constant_t))
|
if (! parse_date(ident.c_str(), &node->constant_t))
|
||||||
throw expr_error("Failed to parse date");
|
throw value_expr_error("Failed to parse date");
|
||||||
} else {
|
} else {
|
||||||
throw expr_error("Missing ']'");
|
throw value_expr_error("Missing ']'");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -609,7 +632,7 @@ value_expr_t * parse_logic_expr(std::istream& in)
|
||||||
if (! in.eof()) {
|
if (! in.eof()) {
|
||||||
std::ostringstream err;
|
std::ostringstream err;
|
||||||
err << "Unexpected character '" << c << "'";
|
err << "Unexpected character '" << c << "'";
|
||||||
throw expr_error(err.str());
|
throw value_expr_error(err.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -656,7 +679,7 @@ value_expr_t * parse_value_expr(std::istream& in)
|
||||||
if (c != ':') {
|
if (c != ':') {
|
||||||
std::ostringstream err;
|
std::ostringstream err;
|
||||||
err << "Unexpected character '" << c << "'";
|
err << "Unexpected character '" << c << "'";
|
||||||
throw expr_error(err.str());
|
throw value_expr_error(err.str());
|
||||||
}
|
}
|
||||||
in.get(c);
|
in.get(c);
|
||||||
choices->right = parse_logic_expr(in);
|
choices->right = parse_logic_expr(in);
|
||||||
|
|
@ -667,7 +690,7 @@ value_expr_t * parse_value_expr(std::istream& in)
|
||||||
if (! in.eof()) {
|
if (! in.eof()) {
|
||||||
std::ostringstream err;
|
std::ostringstream err;
|
||||||
err << "Unexpected character '" << c << "'";
|
err << "Unexpected character '" << c << "'";
|
||||||
throw expr_error(err.str());
|
throw value_expr_error(err.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c = peek_next_nonws(in);
|
c = peek_next_nonws(in);
|
||||||
|
|
@ -730,12 +753,13 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
|
||||||
out << "DATE/TIME[" << node->constant_t << "]";
|
out << "DATE/TIME[" << node->constant_t << "]";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case value_expr_t::AMOUNT: out << "AMOUNT"; break;
|
case value_expr_t::AMOUNT: out << "AMOUNT"; break;
|
||||||
case value_expr_t::COST: out << "COST"; break;
|
case value_expr_t::COST: out << "COST"; break;
|
||||||
case value_expr_t::DATE: out << "DATE"; break;
|
case value_expr_t::DATE: out << "DATE"; break;
|
||||||
case value_expr_t::CLEARED: out << "CLEARED"; break;
|
case value_expr_t::TODAY: out << "TODAY"; break;
|
||||||
case value_expr_t::REAL: out << "REAL"; break;
|
case value_expr_t::CLEARED: out << "CLEARED"; break;
|
||||||
case value_expr_t::INDEX: out << "INDEX"; break;
|
case value_expr_t::REAL: out << "REAL"; break;
|
||||||
|
case value_expr_t::INDEX: out << "INDEX"; break;
|
||||||
case value_expr_t::BALANCE: out << "BALANCE"; break;
|
case value_expr_t::BALANCE: out << "BALANCE"; break;
|
||||||
case value_expr_t::COST_BALANCE: out << "COST_BALANCE"; break;
|
case value_expr_t::COST_BALANCE: out << "COST_BALANCE"; break;
|
||||||
case value_expr_t::TOTAL: out << "TOTAL"; break;
|
case value_expr_t::TOTAL: out << "TOTAL"; break;
|
||||||
|
|
@ -759,6 +783,12 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
|
||||||
out << ")";
|
out << ")";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case value_expr_t::F_STRIP:
|
||||||
|
out << "STRIP(";
|
||||||
|
dump_value_expr(out, node->left);
|
||||||
|
out << ")";
|
||||||
|
break;
|
||||||
|
|
||||||
case value_expr_t::F_PAYEE_MASK:
|
case value_expr_t::F_PAYEE_MASK:
|
||||||
assert(node->mask);
|
assert(node->mask);
|
||||||
out << "P_MASK(" << node->mask->pattern << ")";
|
out << "P_MASK(" << node->mask->pattern << ")";
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ struct value_expr_t
|
||||||
AMOUNT,
|
AMOUNT,
|
||||||
COST,
|
COST,
|
||||||
DATE,
|
DATE,
|
||||||
|
TODAY,
|
||||||
CLEARED,
|
CLEARED,
|
||||||
REAL,
|
REAL,
|
||||||
INDEX, // for accounts, this is the DEPTH
|
INDEX, // for accounts, this is the DEPTH
|
||||||
|
|
@ -63,6 +64,7 @@ struct value_expr_t
|
||||||
F_VALUE,
|
F_VALUE,
|
||||||
F_NEG,
|
F_NEG,
|
||||||
F_ABS,
|
F_ABS,
|
||||||
|
F_STRIP,
|
||||||
F_PAYEE_MASK,
|
F_PAYEE_MASK,
|
||||||
F_ACCOUNT_MASK,
|
F_ACCOUNT_MASK,
|
||||||
|
|
||||||
|
|
|
||||||
64
walk.cc
64
walk.cc
|
|
@ -3,6 +3,21 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
void sort_transactions::flush()
|
||||||
|
{
|
||||||
|
std::stable_sort(transactions.begin(), transactions.end(),
|
||||||
|
compare_items<transaction_t>(sort_order));
|
||||||
|
|
||||||
|
for (transactions_deque::iterator i = transactions.begin();
|
||||||
|
i != transactions.end();
|
||||||
|
i++)
|
||||||
|
(*handler)(*i);
|
||||||
|
|
||||||
|
transactions.clear();
|
||||||
|
|
||||||
|
handler->flush();
|
||||||
|
}
|
||||||
|
|
||||||
void calc_transactions::operator()(transaction_t * xact)
|
void calc_transactions::operator()(transaction_t * xact)
|
||||||
{
|
{
|
||||||
if (last_xact)
|
if (last_xact)
|
||||||
|
|
@ -92,8 +107,12 @@ void changed_value_transactions::operator()(transaction_t * xact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xact)
|
if (xact) {
|
||||||
|
if (changed_values_only)
|
||||||
|
xact->dflags |= TRANSACTION_DISPLAYED;
|
||||||
|
|
||||||
(*handler)(xact);
|
(*handler)(xact);
|
||||||
|
}
|
||||||
|
|
||||||
last_xact = xact;
|
last_xact = xact;
|
||||||
}
|
}
|
||||||
|
|
@ -103,8 +122,14 @@ void subtotal_transactions::flush()
|
||||||
entry_t * entry = new entry_t;
|
entry_t * entry = new entry_t;
|
||||||
|
|
||||||
char buf[256];
|
char buf[256];
|
||||||
// jww (2004-08-10): allow for a format string here
|
std::string fmt = "- ";
|
||||||
std::strftime(buf, 255, "- %Y/%m/%d", std::gmtime(&finish));
|
fmt += format_t::date_format;
|
||||||
|
|
||||||
|
// Make sure the end date is inclusive
|
||||||
|
if (start != finish)
|
||||||
|
finish -= 86400;
|
||||||
|
|
||||||
|
std::strftime(buf, 255, fmt.c_str(), std::gmtime(&finish));
|
||||||
entry->payee = buf;
|
entry->payee = buf;
|
||||||
|
|
||||||
entry_temps.push_back(entry);
|
entry_temps.push_back(entry);
|
||||||
|
|
@ -153,4 +178,37 @@ void subtotal_transactions::operator()(transaction_t * xact)
|
||||||
(*i).second += *xact;
|
(*i).second += *xact;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void interval_transactions::operator()(transaction_t * xact)
|
||||||
|
{
|
||||||
|
std::time_t quant = interval.increment(begin);
|
||||||
|
if (std::difftime(xact->entry->date, quant) > 0) {
|
||||||
|
if (last_xact) {
|
||||||
|
start = begin;
|
||||||
|
finish = quant;
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! interval.seconds) {
|
||||||
|
struct std::tm * desc = std::gmtime(&xact->entry->date);
|
||||||
|
if (interval.years)
|
||||||
|
desc->tm_mon = 0;
|
||||||
|
desc->tm_mday = 1;
|
||||||
|
desc->tm_hour = 0;
|
||||||
|
desc->tm_min = 0;
|
||||||
|
desc->tm_sec = 0;
|
||||||
|
quant = std::mktime(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::time_t temp;
|
||||||
|
while (std::difftime(xact->entry->date,
|
||||||
|
temp = interval.increment(quant)) > 0)
|
||||||
|
quant = temp;
|
||||||
|
begin = quant;
|
||||||
|
}
|
||||||
|
|
||||||
|
subtotal_transactions::operator()(xact);
|
||||||
|
|
||||||
|
last_xact = xact;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
44
walk.h
44
walk.h
|
|
@ -87,19 +87,7 @@ class sort_transactions : public item_handler<transaction_t>
|
||||||
delete handler;
|
delete handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void flush() {
|
virtual void flush();
|
||||||
std::stable_sort(transactions.begin(), transactions.end(),
|
|
||||||
compare_items<transaction_t>(sort_order));
|
|
||||||
|
|
||||||
for (transactions_deque::iterator i = transactions.begin();
|
|
||||||
i != transactions.end();
|
|
||||||
i++)
|
|
||||||
(*handler)(*i);
|
|
||||||
|
|
||||||
transactions.clear();
|
|
||||||
|
|
||||||
handler->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void operator()(transaction_t * xact) {
|
virtual void operator()(transaction_t * xact) {
|
||||||
transactions.push_back(xact);
|
transactions.push_back(xact);
|
||||||
|
|
@ -207,6 +195,7 @@ class changed_value_transactions : public item_handler<transaction_t>
|
||||||
// This filter requires that calc_transactions be used at some point
|
// This filter requires that calc_transactions be used at some point
|
||||||
// later in the chain.
|
// later in the chain.
|
||||||
|
|
||||||
|
bool changed_values_only;
|
||||||
transaction_t * last_xact;
|
transaction_t * last_xact;
|
||||||
|
|
||||||
item_handler<transaction_t> * handler;
|
item_handler<transaction_t> * handler;
|
||||||
|
|
@ -215,8 +204,10 @@ class changed_value_transactions : public item_handler<transaction_t>
|
||||||
transactions_deque xact_temps;
|
transactions_deque xact_temps;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
changed_value_transactions(item_handler<transaction_t> * _handler)
|
changed_value_transactions(item_handler<transaction_t> * _handler,
|
||||||
: last_xact(NULL), handler(_handler) {}
|
bool _changed_values_only)
|
||||||
|
: changed_values_only(_changed_values_only), last_xact(NULL),
|
||||||
|
handler(_handler) {}
|
||||||
|
|
||||||
virtual ~changed_value_transactions() {
|
virtual ~changed_value_transactions() {
|
||||||
flush();
|
flush();
|
||||||
|
|
@ -288,7 +279,8 @@ class interval_transactions : public subtotal_transactions
|
||||||
|
|
||||||
public:
|
public:
|
||||||
interval_transactions(item_handler<transaction_t> * _handler,
|
interval_transactions(item_handler<transaction_t> * _handler,
|
||||||
std::time_t _begin, const interval_t& _interval)
|
const interval_t& _interval,
|
||||||
|
const std::time_t _begin = 0)
|
||||||
: subtotal_transactions(_handler),
|
: subtotal_transactions(_handler),
|
||||||
begin(_begin), interval(_interval), last_xact(NULL) {}
|
begin(_begin), interval(_interval), last_xact(NULL) {}
|
||||||
|
|
||||||
|
|
@ -297,25 +289,7 @@ class interval_transactions : public subtotal_transactions
|
||||||
finish = interval.increment(begin);
|
finish = interval.increment(begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void operator()(transaction_t * xact) {
|
virtual void operator()(transaction_t * xact);
|
||||||
if (std::difftime(xact->entry->date, interval.increment(begin)) > 0) {
|
|
||||||
if (last_xact) {
|
|
||||||
start = begin;
|
|
||||||
finish = interval.increment(begin);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
begin = interval.increment(begin);
|
|
||||||
std::time_t temp;
|
|
||||||
while (std::difftime(xact->entry->date,
|
|
||||||
temp = interval.increment(begin)) > 0)
|
|
||||||
begin = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
subtotal_transactions::operator()(xact);
|
|
||||||
|
|
||||||
last_xact = xact;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class related_transactions : public item_handler<transaction_t>
|
class related_transactions : public item_handler<transaction_t>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue