rewrote the way registers are computed -- again
This commit is contained in:
parent
f570e6644f
commit
1741c80fe4
26 changed files with 1305 additions and 1154 deletions
46
Makefile
46
Makefile
|
|
@ -1,17 +1,34 @@
|
||||||
CODE = amount.cc balance.cc account.cc ledger.cc \
|
CODE = account.cc \
|
||||||
item.cc expr.cc format.cc textual.cc binary.cc
|
amount.cc \
|
||||||
|
autoxact.cc \
|
||||||
|
balance.cc \
|
||||||
|
binary.cc \
|
||||||
|
datetime.cc \
|
||||||
|
error.cc \
|
||||||
|
format.cc \
|
||||||
|
ledger.cc \
|
||||||
|
textual.cc \
|
||||||
|
valexpr.cc \
|
||||||
|
walk.cc
|
||||||
|
|
||||||
OBJS = $(patsubst %.cc,%.o,$(CODE))
|
OBJS = $(patsubst %.cc,%.o,$(CODE))
|
||||||
|
|
||||||
#CXX = cc
|
#CXX = cc
|
||||||
CXX = g++
|
CXX = g++
|
||||||
|
|
||||||
CFLAGS = -Wall -ansi -pedantic
|
CFLAGS = -Wall -ansi -pedantic
|
||||||
#DFLAGS = -O3 -fomit-frame-pointer
|
#DFLAGS = -O3 -fomit-frame-pointer
|
||||||
DFLAGS = -g -DDEBUG=1
|
DFLAGS = -g -DDEBUG=1
|
||||||
#DFLAGS = -g -pg
|
#DFLAGS = -g -pg
|
||||||
INCS = -I/sw/include -I/usr/include/gcc/darwin/3.3/c++ -I/usr/include/gcc/darwin/3.3/c++/ppc-darwin
|
|
||||||
LIBS = -L/sw/lib -lgmpxx -lgmp -lpcre
|
INCS = -I/sw/include \
|
||||||
|
-I/usr/include/gcc/darwin/3.3/c++ \
|
||||||
|
-I/usr/include/gcc/darwin/3.3/c++/ppc-darwin
|
||||||
|
LIBS = -L/sw/lib -lgmpxx -lgmp -lpcre
|
||||||
|
|
||||||
ifdef GNUCASH
|
ifdef GNUCASH
|
||||||
CODE := $(CODE) gnucash.cc
|
CODE := $(CODE) gnucash.cc
|
||||||
|
OBJS := $(OBJS) gnucash.o
|
||||||
CFLAGS := $(CFLAGS) -DREAD_GNUCASH=1
|
CFLAGS := $(CFLAGS) -DREAD_GNUCASH=1
|
||||||
INCS := $(INCS) -I/usr/include/httpd/xml
|
INCS := $(INCS) -I/usr/include/httpd/xml
|
||||||
LIBS := $(LIBS) -L/sw/lib -lxmlparse
|
LIBS := $(LIBS) -L/sw/lib -lxmlparse
|
||||||
|
|
@ -34,8 +51,8 @@ libledger.a: $(OBJS)
|
||||||
ledger: libledger.a main.o
|
ledger: libledger.a main.o
|
||||||
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -o $@ main.o -L. -lledger $(LIBS)
|
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -o $@ main.o -L. -lledger $(LIBS)
|
||||||
|
|
||||||
report: libledger.a report.cc
|
valexpr: libledger.a valexpr.cc
|
||||||
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -DTEST -o $@ report.cc \
|
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -DTEST -o $@ valexpr.cc \
|
||||||
-L. -lledger $(LIBS)
|
-L. -lledger $(LIBS)
|
||||||
|
|
||||||
ledger.info: ledger.texi
|
ledger.info: ledger.texi
|
||||||
|
|
@ -48,9 +65,9 @@ ledger.pdf: ledger.texi
|
||||||
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -c -o $@ $<
|
$(CXX) $(CFLAGS) $(INCS) $(DFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ledger report libledger.a *.o *.elc *~ .\#*
|
rm -f ledger valexpr libledger.a *.o *.elc *~ .\#*
|
||||||
rm -f *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr
|
rm -f *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr
|
||||||
rm -f .gdb_history
|
rm -f .gdb_history gmon.out out
|
||||||
|
|
||||||
distclean fullclean: clean
|
distclean fullclean: clean
|
||||||
rm -f *.texi *.info *.html *.pdf *.elc make.deps TAGS
|
rm -f *.texi *.info *.html *.pdf *.elc make.deps TAGS
|
||||||
|
|
@ -60,7 +77,7 @@ rebuild: clean deps all
|
||||||
deps: make.deps
|
deps: make.deps
|
||||||
|
|
||||||
make.deps: Makefile
|
make.deps: Makefile
|
||||||
cc -M $(INCS) $(CODE) > $@
|
cc -M $(INCS) $(CODE) main.cc > $@
|
||||||
|
|
||||||
include make.deps
|
include make.deps
|
||||||
|
|
||||||
|
|
@ -95,11 +112,6 @@ dist:
|
||||||
mv t Makefile && \
|
mv t Makefile && \
|
||||||
perl -ne 'print if 1 .. /^include make.deps/;' Makefile > t && \
|
perl -ne 'print if 1 .. /^include make.deps/;' Makefile > t && \
|
||||||
mv t Makefile && \
|
mv t Makefile && \
|
||||||
cd $(HOME)/Public && \
|
cd /tmp && \
|
||||||
tar cvzf ledger-$(VERSION).tar.gz /tmp/ledger-$(VERSION))
|
tar cvzf $(HOME)/Sites/ledger/ledger-$(VERSION).tar.gz \
|
||||||
|
ledger-$(VERSION))
|
||||||
publish: dist
|
|
||||||
(cd $(HOME)/Public && \
|
|
||||||
ln -sf ledger-$(VERSION).tar.gz ledger.tar.gz && \
|
|
||||||
rm -fr /tmp/ledger-$(VERSION) && \
|
|
||||||
upload)
|
|
||||||
|
|
|
||||||
2
README
2
README
|
|
@ -201,7 +201,7 @@ amount, if it is the same as the first line:
|
||||||
For this entry, Ledger will figure out that $-23.00 must come from
|
For this entry, Ledger will figure out that $-23.00 must come from
|
||||||
"Assets:Checking" in order to balance the entry.
|
"Assets:Checking" in order to balance the entry.
|
||||||
|
|
||||||
** Transactions: Additions and Subtractions
|
** Stating where money goes
|
||||||
|
|
||||||
Accountants will talk of `credits' and `debits', but their meaning is
|
Accountants will talk of `credits' and `debits', but their meaning is
|
||||||
often different from the layman's definitions. To avoid this semantic
|
often different from the layman's definitions. To avoid this semantic
|
||||||
|
|
|
||||||
40
account.cc
40
account.cc
|
|
@ -15,30 +15,26 @@ account_t::~account_t()
|
||||||
delete (*i).second;
|
delete (*i).second;
|
||||||
}
|
}
|
||||||
|
|
||||||
account_t * account_t::find_account(const std::string& ident,
|
account_t * account_t::find_account(const std::string& name,
|
||||||
const bool auto_create)
|
const bool auto_create)
|
||||||
{
|
{
|
||||||
accounts_map::const_iterator c = accounts_cache.find(ident);
|
accounts_map::const_iterator i = accounts.find(name);
|
||||||
if (c != accounts_cache.end())
|
|
||||||
return (*c).second;
|
|
||||||
|
|
||||||
accounts_map::const_iterator i = accounts.find(ident);
|
|
||||||
if (i != accounts.end())
|
if (i != accounts.end())
|
||||||
return (*i).second;
|
return (*i).second;
|
||||||
|
|
||||||
static char buf[256];
|
static char buf[256];
|
||||||
|
|
||||||
std::string::size_type sep = ident.find(':');
|
std::string::size_type sep = name.find(':');
|
||||||
const char * first, * rest;
|
const char * first, * rest;
|
||||||
if (sep == std::string::npos) {
|
if (sep == std::string::npos) {
|
||||||
first = ident.c_str();
|
first = name.c_str();
|
||||||
rest = NULL;
|
rest = NULL;
|
||||||
} else {
|
} else {
|
||||||
std::strncpy(buf, ident.c_str(), sep);
|
std::strncpy(buf, name.c_str(), sep);
|
||||||
buf[sep] = '\0';
|
buf[sep] = '\0';
|
||||||
|
|
||||||
first = buf;
|
first = buf;
|
||||||
rest = ident.c_str() + sep + 1;
|
rest = name.c_str() + sep + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
account_t * account;
|
account_t * account;
|
||||||
|
|
@ -56,8 +52,6 @@ account_t * account_t::find_account(const std::string& ident,
|
||||||
if (rest)
|
if (rest)
|
||||||
account = account->find_account(rest, auto_create);
|
account = account->find_account(rest, auto_create);
|
||||||
|
|
||||||
accounts_cache.insert(accounts_pair(ident, account));
|
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,16 +70,22 @@ bool account_t::remove_transaction(transaction_t * xact)
|
||||||
|
|
||||||
std::string account_t::fullname() const
|
std::string account_t::fullname() const
|
||||||
{
|
{
|
||||||
const account_t * first = this;
|
if (! _fullname.empty()) {
|
||||||
std::string fullname = name;
|
return _fullname;
|
||||||
|
} else {
|
||||||
|
const account_t * first = this;
|
||||||
|
std::string fullname = name;
|
||||||
|
|
||||||
while (first->parent) {
|
while (first->parent) {
|
||||||
first = first->parent;
|
first = first->parent;
|
||||||
if (! first->name.empty())
|
if (! first->name.empty())
|
||||||
fullname = first->name + ":" + fullname;
|
fullname = first->name + ":" + fullname;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fullname = fullname;
|
||||||
|
|
||||||
|
return fullname;
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
|
||||||
112
amount.cc
112
amount.cc
|
|
@ -10,6 +10,24 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
static mpz_t full_divisor;
|
||||||
|
static mpz_t true_value;
|
||||||
|
|
||||||
|
static class init_amounts
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
init_amounts() {
|
||||||
|
mpz_init(full_divisor);
|
||||||
|
mpz_init(true_value);
|
||||||
|
mpz_ui_pow_ui(full_divisor, 10, MAX_PRECISION);
|
||||||
|
mpz_mul_ui(true_value, full_divisor, 1);
|
||||||
|
}
|
||||||
|
~init_amounts() {
|
||||||
|
mpz_clear(full_divisor);
|
||||||
|
mpz_clear(true_value);
|
||||||
|
}
|
||||||
|
} initializer;
|
||||||
|
|
||||||
static void mpz_round(mpz_t out, mpz_t value, int precision)
|
static void mpz_round(mpz_t out, mpz_t value, int precision)
|
||||||
{
|
{
|
||||||
mpz_t divisor;
|
mpz_t divisor;
|
||||||
|
|
@ -52,7 +70,49 @@ static void mpz_round(mpz_t out, mpz_t value, int precision)
|
||||||
mpz_clear(remainder);
|
mpz_clear(remainder);
|
||||||
}
|
}
|
||||||
|
|
||||||
// destructor
|
amount_t::amount_t(const bool value)
|
||||||
|
: quantity(NULL), commodity(NULL)
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
commodity = commodity_t::null_commodity;
|
||||||
|
quantity = new MP_INT;
|
||||||
|
mpz_init_set(MPZ(quantity), true_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t::amount_t(const int value)
|
||||||
|
: quantity(NULL), commodity(NULL)
|
||||||
|
{
|
||||||
|
if (value != 0) {
|
||||||
|
_init();
|
||||||
|
commodity = commodity_t::null_commodity;
|
||||||
|
mpz_set_si(MPZ(quantity), value);
|
||||||
|
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t::amount_t(const unsigned int value)
|
||||||
|
: quantity(NULL), commodity(NULL)
|
||||||
|
{
|
||||||
|
if (value != 0) {
|
||||||
|
_init();
|
||||||
|
commodity = commodity_t::null_commodity;
|
||||||
|
mpz_set_ui(MPZ(quantity), value);
|
||||||
|
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t::amount_t(const double value)
|
||||||
|
: quantity(NULL), commodity(NULL)
|
||||||
|
{
|
||||||
|
if (value != 0.0) {
|
||||||
|
_init();
|
||||||
|
commodity = commodity_t::null_commodity;
|
||||||
|
mpz_set_d(MPZ(quantity), value);
|
||||||
|
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void amount_t::_clear()
|
void amount_t::_clear()
|
||||||
{
|
{
|
||||||
mpz_clear(MPZ(quantity));
|
mpz_clear(MPZ(quantity));
|
||||||
|
|
@ -92,6 +152,22 @@ amount_t& amount_t::operator=(const amount_t& amt)
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
amount_t& amount_t::operator=(const bool value)
|
||||||
|
{
|
||||||
|
if (! value) {
|
||||||
|
if (quantity) {
|
||||||
|
_clear();
|
||||||
|
quantity = NULL;
|
||||||
|
commodity = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
commodity = commodity_t::null_commodity;
|
||||||
|
quantity = new MP_INT;
|
||||||
|
mpz_init_set(MPZ(quantity), true_value);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
amount_t& amount_t::operator=(const int value)
|
amount_t& amount_t::operator=(const int value)
|
||||||
{
|
{
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
|
|
@ -101,10 +177,9 @@ amount_t& amount_t::operator=(const int value)
|
||||||
commodity = NULL;
|
commodity = NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::string str;
|
commodity = commodity_t::null_commodity;
|
||||||
std::ostringstream strstr(str);
|
mpz_set_si(MPZ(quantity), value);
|
||||||
strstr << value;
|
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
|
||||||
parse(strstr.str());
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -118,10 +193,9 @@ amount_t& amount_t::operator=(const unsigned int value)
|
||||||
commodity = NULL;
|
commodity = NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::string str;
|
commodity = commodity_t::null_commodity;
|
||||||
std::ostringstream strstr(str);
|
mpz_set_ui(MPZ(quantity), value);
|
||||||
strstr << value;
|
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
|
||||||
parse(strstr.str());
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -135,10 +209,9 @@ amount_t& amount_t::operator=(const double value)
|
||||||
commodity = NULL;
|
commodity = NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::string str;
|
commodity = commodity_t::null_commodity;
|
||||||
std::ostringstream strstr(str);
|
mpz_set_d(MPZ(quantity), value);
|
||||||
strstr << value;
|
mpz_mul(MPZ(quantity), MPZ(quantity), full_divisor);
|
||||||
parse(strstr.str());
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -279,7 +352,16 @@ amount_t::operator bool() const
|
||||||
{
|
{
|
||||||
if (quantity) {
|
if (quantity) {
|
||||||
assert(commodity);
|
assert(commodity);
|
||||||
return mpz_sgn(MPZ(round().quantity)) != 0;
|
mpz_t temp;
|
||||||
|
mpz_t divisor;
|
||||||
|
mpz_init_set(temp, MPZ(quantity));
|
||||||
|
mpz_init(divisor);
|
||||||
|
mpz_ui_pow_ui(divisor, 10, MAX_PRECISION - commodity->precision);
|
||||||
|
mpz_tdiv_q(temp, temp, divisor);
|
||||||
|
bool zero = mpz_sgn(temp) == 0;
|
||||||
|
mpz_clear(divisor);
|
||||||
|
mpz_clear(temp);
|
||||||
|
return ! zero;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -682,6 +764,8 @@ void (*commodity_t::updater)(commodity_t * commodity,
|
||||||
const std::time_t moment) = NULL;
|
const std::time_t moment) = NULL;
|
||||||
|
|
||||||
commodities_map commodity_t::commodities;
|
commodities_map commodity_t::commodities;
|
||||||
|
commodity_t * commodity_t::null_commodity =
|
||||||
|
commodity_t::find_commodity("", true);
|
||||||
|
|
||||||
struct cleanup_commodities
|
struct cleanup_commodities
|
||||||
{
|
{
|
||||||
|
|
|
||||||
244
amount.h
Normal file
244
amount.h
Normal file
|
|
@ -0,0 +1,244 @@
|
||||||
|
#ifndef _AMOUNT_H
|
||||||
|
#define _AMOUNT_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <ctime>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
class commodity_t;
|
||||||
|
|
||||||
|
class amount_t
|
||||||
|
{
|
||||||
|
typedef void * base_type;
|
||||||
|
|
||||||
|
void _init();
|
||||||
|
void _copy(const amount_t& amt);
|
||||||
|
void _clear();
|
||||||
|
|
||||||
|
public:
|
||||||
|
base_type quantity; // amount, to MAX_PRECISION
|
||||||
|
commodity_t * commodity;
|
||||||
|
|
||||||
|
bool valid() const {
|
||||||
|
if (quantity)
|
||||||
|
return commodity != NULL;
|
||||||
|
else
|
||||||
|
return commodity == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// constructors
|
||||||
|
amount_t(commodity_t * _commodity = NULL)
|
||||||
|
: quantity(NULL), commodity(_commodity) {}
|
||||||
|
|
||||||
|
amount_t(const amount_t& amt) : quantity(NULL) {
|
||||||
|
if (amt.quantity)
|
||||||
|
_copy(amt);
|
||||||
|
else
|
||||||
|
commodity = amt.commodity;
|
||||||
|
}
|
||||||
|
amount_t(const std::string& value) {
|
||||||
|
_init();
|
||||||
|
std::istringstream str(value);
|
||||||
|
str >> *this;
|
||||||
|
}
|
||||||
|
amount_t(const bool value);
|
||||||
|
amount_t(const int value);
|
||||||
|
amount_t(const unsigned int value);
|
||||||
|
amount_t(const double value);
|
||||||
|
|
||||||
|
// destructor
|
||||||
|
~amount_t() {
|
||||||
|
if (quantity)
|
||||||
|
_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// assignment operator
|
||||||
|
amount_t& operator=(const amount_t& amt);
|
||||||
|
amount_t& operator=(const std::string& value);
|
||||||
|
amount_t& operator=(const bool value);
|
||||||
|
amount_t& operator=(const int value);
|
||||||
|
amount_t& operator=(const unsigned int value);
|
||||||
|
amount_t& operator=(const double value);
|
||||||
|
|
||||||
|
// general methods
|
||||||
|
amount_t round(int precision = -1) const;
|
||||||
|
|
||||||
|
// in-place arithmetic
|
||||||
|
amount_t& operator*=(const amount_t& amt);
|
||||||
|
amount_t& operator/=(const amount_t& amt);
|
||||||
|
amount_t& operator%=(const amount_t& amt);
|
||||||
|
amount_t& operator+=(const amount_t& amt);
|
||||||
|
amount_t& operator-=(const amount_t& amt);
|
||||||
|
|
||||||
|
// simple arithmetic
|
||||||
|
amount_t operator*(const amount_t& amt) const {
|
||||||
|
amount_t temp = *this;
|
||||||
|
temp *= amt;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
amount_t operator/(const amount_t& amt) const {
|
||||||
|
amount_t temp = *this;
|
||||||
|
temp /= amt;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
amount_t operator%(const amount_t& amt) const {
|
||||||
|
amount_t temp = *this;
|
||||||
|
temp %= amt;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
amount_t operator+(const amount_t& amt) const {
|
||||||
|
amount_t temp = *this;
|
||||||
|
temp += amt;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
amount_t operator-(const amount_t& amt) const {
|
||||||
|
amount_t temp = *this;
|
||||||
|
temp -= amt;
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// unary negation
|
||||||
|
amount_t& negate();
|
||||||
|
amount_t negated() const {
|
||||||
|
amount_t temp = *this;
|
||||||
|
temp.negate();
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
amount_t operator-() const {
|
||||||
|
return negated();
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for non-zero (use ! for zero)
|
||||||
|
operator bool() const;
|
||||||
|
|
||||||
|
// comparisons to zero
|
||||||
|
bool operator<(const int num) const;
|
||||||
|
bool operator<=(const int num) const;
|
||||||
|
bool operator>(const int num) const;
|
||||||
|
bool operator>=(const int num) const;
|
||||||
|
|
||||||
|
// comparisons between amounts
|
||||||
|
bool operator<(const amount_t& amt) const;
|
||||||
|
bool operator<=(const amount_t& amt) const;
|
||||||
|
bool operator>(const amount_t& amt) const;
|
||||||
|
bool operator>=(const amount_t& amt) const;
|
||||||
|
bool operator==(const amount_t& amt) const;
|
||||||
|
bool operator!=(const amount_t& amt) const {
|
||||||
|
if (commodity != amt.commodity)
|
||||||
|
return true;
|
||||||
|
return ! (*this == amt);
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t value(const std::time_t moment) const;
|
||||||
|
|
||||||
|
operator std::string() const;
|
||||||
|
|
||||||
|
void parse(std::istream& in);
|
||||||
|
void parse(const std::string& str) {
|
||||||
|
std::istringstream stream(str);
|
||||||
|
parse(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_quantity(std::ostream& out) const;
|
||||||
|
void read_quantity(std::istream& in);
|
||||||
|
|
||||||
|
friend std::istream& operator>>(std::istream& in, amount_t& amt);
|
||||||
|
};
|
||||||
|
|
||||||
|
void parse_quantity(std::istream& in, std::string& value);
|
||||||
|
void parse_commodity(std::istream& in, std::string& symbol);
|
||||||
|
|
||||||
|
inline amount_t abs(const amount_t& amt) {
|
||||||
|
return amt < 0 ? amt.negated() : amt;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::istream& operator>>(std::istream& in, amount_t& amt) {
|
||||||
|
amt.parse(in);
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& out, const amount_t& amt);
|
||||||
|
|
||||||
|
|
||||||
|
#define COMMODITY_STYLE_DEFAULTS 0x00
|
||||||
|
#define COMMODITY_STYLE_SUFFIXED 0x01
|
||||||
|
#define COMMODITY_STYLE_SEPARATED 0x02
|
||||||
|
#define COMMODITY_STYLE_EUROPEAN 0x04
|
||||||
|
#define COMMODITY_STYLE_THOUSANDS 0x08
|
||||||
|
#define COMMODITY_STYLE_CONSULTED 0x10
|
||||||
|
#define COMMODITY_STYLE_NOMARKET 0x20
|
||||||
|
|
||||||
|
typedef std::map<const std::time_t, amount_t> history_map;
|
||||||
|
typedef std::pair<const std::time_t, amount_t> history_pair;
|
||||||
|
|
||||||
|
typedef std::map<const std::string, commodity_t *> commodities_map;
|
||||||
|
typedef std::pair<const std::string, commodity_t *> commodities_pair;
|
||||||
|
|
||||||
|
class commodity_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string symbol;
|
||||||
|
std::string name;
|
||||||
|
std::string note;
|
||||||
|
unsigned int precision;
|
||||||
|
unsigned int flags;
|
||||||
|
history_map history;
|
||||||
|
amount_t conversion;
|
||||||
|
unsigned long ident;
|
||||||
|
|
||||||
|
// If set, this global function pointer is called to determine
|
||||||
|
// whether prices have been updated in the meanwhile.
|
||||||
|
|
||||||
|
static void (*updater)(commodity_t * commodity,
|
||||||
|
const std::time_t date,
|
||||||
|
const amount_t& price,
|
||||||
|
const std::time_t moment);
|
||||||
|
|
||||||
|
// This map remembers all commodities that have been
|
||||||
|
// defined thus far.
|
||||||
|
|
||||||
|
static commodities_map commodities;
|
||||||
|
static commodity_t * null_commodity;
|
||||||
|
|
||||||
|
static void add_commodity(commodity_t * commodity,
|
||||||
|
const std::string symbol = "") {
|
||||||
|
commodities.insert(commodities_pair((symbol.empty() ?
|
||||||
|
commodity->symbol : symbol),
|
||||||
|
commodity));
|
||||||
|
}
|
||||||
|
static bool remove_commodity(commodity_t * commodity) {
|
||||||
|
commodities_map::size_type n = commodities.erase(commodity->symbol);
|
||||||
|
return n > 0;
|
||||||
|
}
|
||||||
|
static commodity_t * find_commodity(const std::string& symbol,
|
||||||
|
bool auto_create = false);
|
||||||
|
|
||||||
|
// Now the per-object constructor and methods
|
||||||
|
|
||||||
|
commodity_t(const std::string& _symbol = "",
|
||||||
|
unsigned int _precision = 2,
|
||||||
|
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
|
||||||
|
: symbol(_symbol), precision(_precision), flags(_flags) {}
|
||||||
|
|
||||||
|
void add_price(const std::time_t date, const amount_t& price) {
|
||||||
|
history.insert(history_pair(date, price));
|
||||||
|
}
|
||||||
|
bool remove_price(const std::time_t date) {
|
||||||
|
history_map::size_type n = history.erase(date);
|
||||||
|
return n > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_conversion(const amount_t& price) {
|
||||||
|
conversion = price;
|
||||||
|
}
|
||||||
|
|
||||||
|
amount_t value(const std::time_t moment = std::time(NULL));
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _AMOUNT_H
|
||||||
28
autoxact.cc
Normal file
28
autoxact.cc
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#include "autoxact.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
void automated_transaction_t::extend_entry(entry_t * entry)
|
||||||
|
{
|
||||||
|
for (transactions_list::iterator i = entry->transactions.begin();
|
||||||
|
i != entry->transactions.end();
|
||||||
|
i++)
|
||||||
|
if (matches(masks, *((*i)->account))) {
|
||||||
|
for (transactions_list::iterator t = transactions.begin();
|
||||||
|
t != transactions.end();
|
||||||
|
t++) {
|
||||||
|
amount_t amt;
|
||||||
|
if ((*t)->amount.commodity->symbol.empty())
|
||||||
|
amt = (*i)->amount * (*t)->amount;
|
||||||
|
else
|
||||||
|
amt = (*t)->amount;
|
||||||
|
|
||||||
|
transaction_t * xact
|
||||||
|
= new transaction_t(entry, (*t)->account, amt, amt,
|
||||||
|
(*t)->flags | TRANSACTION_AUTO);
|
||||||
|
entry->add_transaction(xact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
79
autoxact.h
Normal file
79
autoxact.h
Normal file
|
|
@ -0,0 +1,79 @@
|
||||||
|
#ifndef _AUTOXACT_H
|
||||||
|
#define _AUTOXACT_H
|
||||||
|
|
||||||
|
#include "ledger.h"
|
||||||
|
#include "valexpr.h"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
class automated_transaction_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
masks_list masks;
|
||||||
|
transactions_list transactions;
|
||||||
|
|
||||||
|
automated_transaction_t(masks_list& _masks,
|
||||||
|
transactions_list& _transactions) {
|
||||||
|
masks.insert(masks.begin(), _masks.begin(), _masks.end());
|
||||||
|
transactions.insert(transactions.begin(),
|
||||||
|
_transactions.begin(), _transactions.end());
|
||||||
|
// Take over ownership of the pointers
|
||||||
|
_transactions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
~automated_transaction_t() {
|
||||||
|
for (transactions_list::iterator i = transactions.begin();
|
||||||
|
i != transactions.end();
|
||||||
|
i++)
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void extend_entry(entry_t * entry);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
typedef std::deque<automated_transaction_t *> automated_transactions_deque;
|
||||||
|
|
||||||
|
class automated_transactions_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
automated_transactions_deque automated_transactions;
|
||||||
|
|
||||||
|
~automated_transactions_t() {
|
||||||
|
for (automated_transactions_deque::iterator i
|
||||||
|
= automated_transactions.begin();
|
||||||
|
i != automated_transactions.end();
|
||||||
|
i++)
|
||||||
|
delete *i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void extend_entry(entry_t * entry) {
|
||||||
|
for (automated_transactions_deque::iterator i
|
||||||
|
= automated_transactions.begin();
|
||||||
|
i != automated_transactions.end();
|
||||||
|
i++)
|
||||||
|
(*i)->extend_entry(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_automated_transaction(automated_transaction_t * auto_xact) {
|
||||||
|
automated_transactions.push_back(auto_xact);
|
||||||
|
}
|
||||||
|
bool remove_automated_transaction(automated_transaction_t * auto_xact) {
|
||||||
|
for (automated_transactions_deque::iterator i
|
||||||
|
= automated_transactions.begin();
|
||||||
|
i != automated_transactions.end();
|
||||||
|
i++) {
|
||||||
|
if (*i == auto_xact) {
|
||||||
|
automated_transactions.erase(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _AUTOXACT_H
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
#include "balance.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -86,7 +85,6 @@ void balance_t::write(std::ostream& out,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
balance_pair_t::balance_pair_t(const transaction_t& xact)
|
balance_pair_t::balance_pair_t(const transaction_t& xact)
|
||||||
: quantity(xact.amount), cost(xact.cost) {}
|
: quantity(xact.amount), cost(xact.cost) {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
#ifndef _BALANCE_H
|
#ifndef _BALANCE_H
|
||||||
#define _BALANCE_H
|
#define _BALANCE_H
|
||||||
|
|
||||||
#include "ledger.h"
|
#include <map>
|
||||||
|
#include <ctime>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "amount.h"
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -349,6 +353,7 @@ inline std::ostream& operator<<(std::ostream& out, const balance_t& bal) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class transaction_t;
|
||||||
|
|
||||||
class balance_pair_t
|
class balance_pair_t
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
unsigned long magic_number = 0xFFEED765;
|
unsigned long binary_magic_number = 0xFFEED765;
|
||||||
static unsigned long format_version = 0x00020009;
|
static unsigned long format_version = 0x00020009;
|
||||||
|
|
||||||
static char buf[4096];
|
static char buf[4096];
|
||||||
|
|
@ -292,7 +292,7 @@ unsigned int read_binary_ledger(std::istream& in,
|
||||||
|
|
||||||
unsigned long magic;
|
unsigned long magic;
|
||||||
in.read((char *)&magic, sizeof(magic));
|
in.read((char *)&magic, sizeof(magic));
|
||||||
if (magic != magic_number)
|
if (magic != binary_magic_number)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -560,7 +560,7 @@ void write_binary_account(std::ostream& out, account_t * account)
|
||||||
void write_binary_ledger(std::ostream& out, ledger_t * ledger,
|
void write_binary_ledger(std::ostream& out, ledger_t * ledger,
|
||||||
const std::string& leader)
|
const std::string& leader)
|
||||||
{
|
{
|
||||||
out.write((char *)&magic_number, sizeof(magic_number));
|
out.write((char *)&binary_magic_number, sizeof(binary_magic_number));
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
{
|
{
|
||||||
|
|
|
||||||
2
binary.h
2
binary.h
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
extern unsigned long magic_number;
|
extern unsigned long binary_magic_number;
|
||||||
|
|
||||||
extern unsigned int read_binary_ledger(std::istream& in,
|
extern unsigned int read_binary_ledger(std::istream& in,
|
||||||
const std::string& leader,
|
const std::string& leader,
|
||||||
|
|
|
||||||
125
datetime.cc
Normal file
125
datetime.cc
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
#include "datetime.h"
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
static std::time_t now = std::time(NULL);
|
||||||
|
struct std::tm * now_tm = std::localtime(&now);
|
||||||
|
|
||||||
|
static std::time_t base = -1;
|
||||||
|
static int base_year = -1;
|
||||||
|
|
||||||
|
static const int month_days[12] = {
|
||||||
|
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * formats[] = {
|
||||||
|
"%Y/%m/%d",
|
||||||
|
"%m/%d",
|
||||||
|
"%Y.%m.%d",
|
||||||
|
"%m.%d",
|
||||||
|
"%Y-%m-%d",
|
||||||
|
"%m-%d",
|
||||||
|
"%a",
|
||||||
|
"%A",
|
||||||
|
"%b",
|
||||||
|
"%B",
|
||||||
|
"%Y",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
bool parse_date_mask(const char * date_str, struct std::tm * result)
|
||||||
|
{
|
||||||
|
for (const char ** f = formats; *f; f++) {
|
||||||
|
memset(result, INT_MAX, sizeof(struct std::tm));
|
||||||
|
if (strptime(date_str, *f, result))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_date(const char * date_str, std::time_t * result, const int year)
|
||||||
|
{
|
||||||
|
struct std::tm when;
|
||||||
|
|
||||||
|
if (! parse_date_mask(date_str, &when))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
when.tm_hour = 0;
|
||||||
|
when.tm_min = 0;
|
||||||
|
when.tm_sec = 0;
|
||||||
|
|
||||||
|
if (when.tm_year == -1)
|
||||||
|
when.tm_year = ((year == -1) ? now_tm->tm_year : (year - 1900));
|
||||||
|
|
||||||
|
if (when.tm_mon == -1)
|
||||||
|
when.tm_mon = 0;
|
||||||
|
|
||||||
|
if (when.tm_mday == -1)
|
||||||
|
when.tm_mday = 1;
|
||||||
|
|
||||||
|
*result = std::mktime(&when);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool quick_parse_date(char * date_str, std::time_t * result)
|
||||||
|
{
|
||||||
|
int year = -1, month = -1, day, num = 0;
|
||||||
|
|
||||||
|
for (char * p = date_str; *p; p++) {
|
||||||
|
if (*p == '/' || *p == '-' || *p == '.') {
|
||||||
|
if (year == -1)
|
||||||
|
year = num;
|
||||||
|
else
|
||||||
|
month = num;
|
||||||
|
num = 0;
|
||||||
|
}
|
||||||
|
else if (*p < '0' || *p > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
num *= 10;
|
||||||
|
num += *p - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
day = num;
|
||||||
|
|
||||||
|
if (month == -1) {
|
||||||
|
month = year;
|
||||||
|
year = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (base == -1 || year != base_year) {
|
||||||
|
struct std::tm when;
|
||||||
|
|
||||||
|
when.tm_hour = 0;
|
||||||
|
when.tm_min = 0;
|
||||||
|
when.tm_sec = 0;
|
||||||
|
|
||||||
|
base_year = year == -1 ? now_tm->tm_year + 1900 : year;
|
||||||
|
when.tm_year = year == -1 ? now_tm->tm_year : year - 1900;
|
||||||
|
when.tm_mon = 0;
|
||||||
|
when.tm_mday = 1;
|
||||||
|
|
||||||
|
base = std::mktime(&when);
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = base;
|
||||||
|
|
||||||
|
--month;
|
||||||
|
while (--month >= 0) {
|
||||||
|
*result += month_days[month] * 24 * 60 * 60;
|
||||||
|
if (month == 1 && year % 4 == 0 && year != 2000) // february in leap years
|
||||||
|
*result += 24 * 60 * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--day)
|
||||||
|
*result += day * 24 * 60 * 60;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
19
datetime.h
Normal file
19
datetime.h
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef _DATETIME_H
|
||||||
|
#define _DATETIME_H
|
||||||
|
|
||||||
|
#include "ledger.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _DATETIME_H
|
||||||
21
error.h
21
error.h
|
|
@ -1,8 +1,19 @@
|
||||||
#ifndef _ERROR_H
|
#ifndef _ERROR_H
|
||||||
#define _ERROR_H
|
#define _ERROR_H
|
||||||
|
|
||||||
|
#include "ledger.h"
|
||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <sstream>
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#include <cassert>
|
||||||
|
#else
|
||||||
|
#ifdef assert
|
||||||
|
#undef assert
|
||||||
|
#endif
|
||||||
|
#define assert(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -49,13 +60,9 @@ class parse_error : public error
|
||||||
: error(reason), line(_line), file(_file) {}
|
: error(reason), line(_line), file(_file) {}
|
||||||
virtual ~parse_error() throw() {}
|
virtual ~parse_error() throw() {}
|
||||||
|
|
||||||
virtual const char* what() const throw() {
|
virtual const char* what() const throw();
|
||||||
static std::ostringstream msg;
|
|
||||||
msg << file << ", line " << line << ": " << error::what();
|
|
||||||
return msg.str().c_str();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _CONSTRAINT_H
|
#endif // _ERROR_H
|
||||||
|
|
|
||||||
138
format.cc
138
format.cc
|
|
@ -14,13 +14,18 @@ std::string truncated(const std::string& str, unsigned int width)
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string maximal_account_name(const item_t * item, const item_t * parent)
|
std::string partial_account_name(const account_t * account,
|
||||||
|
const unsigned int start_depth)
|
||||||
{
|
{
|
||||||
std::string name = item->account->name;
|
std::string name = account->name;
|
||||||
for (const item_t * i = item->parent;
|
const account_t * acct = account->parent;
|
||||||
i && i->account && i != parent;
|
|
||||||
i = i->parent)
|
for (int i = account->depth - start_depth - 1;
|
||||||
name = i->account->name + ":" + name;
|
--i >= 0 && acct->parent; ) {
|
||||||
|
assert(acct);
|
||||||
|
name = acct->name + ":" + name;
|
||||||
|
acct = acct->parent;
|
||||||
|
}
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,14 +144,10 @@ element_t * format_t::parse_elements(const std::string& fmt)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void format_t::format_elements(std::ostream& out, const item_t * item,
|
void format_t::format_elements(std::ostream& out,
|
||||||
const item_t * displayed_parent) const
|
const details_t& details) const
|
||||||
{
|
{
|
||||||
std::string result;
|
for (const element_t * elem = elements.get(); elem; elem = elem->next) {
|
||||||
|
|
||||||
for (const element_t * elem = elements;
|
|
||||||
elem;
|
|
||||||
elem = elem->next) {
|
|
||||||
if (elem->align_left)
|
if (elem->align_left)
|
||||||
out << std::left;
|
out << std::left;
|
||||||
else
|
else
|
||||||
|
|
@ -161,16 +162,18 @@ void format_t::format_elements(std::ostream& out, const item_t * item,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case element_t::VALUE_EXPR: {
|
case element_t::VALUE_EXPR: {
|
||||||
balance_t value = elem->val_expr->compute(item);
|
balance_t value;
|
||||||
value.write(out, elem->min_width,
|
elem->val_expr->compute(value, details);
|
||||||
elem->max_width > 0 ? elem->max_width : elem->min_width);
|
value.write(out, elem->min_width, (elem->max_width > 0 ?
|
||||||
|
elem->max_width : elem->min_width));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case element_t::DATE_STRING:
|
case element_t::DATE_STRING:
|
||||||
if (item->date != -1) {
|
if (details.entry && details.entry->date != -1) {
|
||||||
char buf[256];
|
char buf[256];
|
||||||
std::strftime(buf, 255, elem->chars.c_str(), std::gmtime(&item->date));
|
std::strftime(buf, 255, elem->chars.c_str(),
|
||||||
|
std::gmtime(&details.entry->date));
|
||||||
out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width));
|
out << (elem->max_width == 0 ? buf : truncated(buf, elem->max_width));
|
||||||
} else {
|
} else {
|
||||||
out << " ";
|
out << " ";
|
||||||
|
|
@ -178,35 +181,38 @@ void format_t::format_elements(std::ostream& out, const item_t * item,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case element_t::CLEARED:
|
case element_t::CLEARED:
|
||||||
if (item->state == entry_t::CLEARED)
|
if (details.entry && details.entry->state == entry_t::CLEARED)
|
||||||
out << "* ";
|
out << "* ";
|
||||||
else
|
else
|
||||||
out << "";
|
out << "";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case element_t::CODE:
|
case element_t::CODE:
|
||||||
if (! item->code.empty())
|
if (details.entry && ! details.entry->code.empty())
|
||||||
out << "(" << item->code << ") ";
|
out << "(" << details.entry->code << ") ";
|
||||||
else
|
else
|
||||||
out << "";
|
out << "";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case element_t::PAYEE:
|
case element_t::PAYEE:
|
||||||
out << (elem->max_width == 0 ?
|
if (details.entry)
|
||||||
item->payee : truncated(item->payee, elem->max_width));
|
out << (elem->max_width == 0 ?
|
||||||
|
details.entry->payee : truncated(details.entry->payee,
|
||||||
|
elem->max_width));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case element_t::ACCOUNT_NAME:
|
case element_t::ACCOUNT_NAME:
|
||||||
case element_t::ACCOUNT_FULLNAME:
|
case element_t::ACCOUNT_FULLNAME:
|
||||||
if (item->account) {
|
if (details.account) {
|
||||||
std::string name = (elem->type == element_t::ACCOUNT_FULLNAME ?
|
std::string name = (elem->type == element_t::ACCOUNT_FULLNAME ?
|
||||||
item->account->fullname() :
|
details.account->fullname() :
|
||||||
maximal_account_name(item, displayed_parent));
|
partial_account_name(details.account,
|
||||||
|
details.depth));
|
||||||
if (elem->max_width > 0)
|
if (elem->max_width > 0)
|
||||||
name = truncated(name, elem->max_width);
|
name = truncated(name, elem->max_width);
|
||||||
|
|
||||||
if (item->flags & TRANSACTION_VIRTUAL) {
|
if (details.xact && details.xact->flags & TRANSACTION_VIRTUAL) {
|
||||||
if (item->flags & TRANSACTION_BALANCE)
|
if (details.xact->flags & TRANSACTION_BALANCE)
|
||||||
name = "[" + name + "]";
|
name = "[" + name + "]";
|
||||||
else
|
else
|
||||||
name = "(" + name + ")";
|
name = "(" + name + ")";
|
||||||
|
|
@ -218,81 +224,67 @@ void format_t::format_elements(std::ostream& out, const item_t * item,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case element_t::OPT_AMOUNT: {
|
case element_t::OPT_AMOUNT: {
|
||||||
|
if (! details.entry || ! details.xact)
|
||||||
|
break;
|
||||||
|
|
||||||
std::string disp;
|
std::string disp;
|
||||||
bool use_disp = false;
|
bool use_disp = false;
|
||||||
|
|
||||||
if (std::find(displayed_parent->subitems.begin(),
|
if (std::find(details.entry->transactions.begin(),
|
||||||
displayed_parent->subitems.end(), item) !=
|
details.entry->transactions.end(), details.xact) !=
|
||||||
displayed_parent->subitems.end()) {
|
details.entry->transactions.end()) {
|
||||||
if (displayed_parent->subitems.size() == 2 &&
|
if (details.entry->transactions.size() == 2 &&
|
||||||
item == displayed_parent->subitems.back() &&
|
details.xact == details.entry->transactions.back() &&
|
||||||
(displayed_parent->subitems.front()->value.quantity ==
|
(details.entry->transactions.front()->amount ==
|
||||||
displayed_parent->subitems.front()->value.cost) &&
|
details.entry->transactions.front()->cost) &&
|
||||||
(displayed_parent->subitems.front()->value ==
|
(details.entry->transactions.front()->amount ==
|
||||||
- displayed_parent->subitems.back()->value)) {
|
- details.entry->transactions.back()->amount)) {
|
||||||
use_disp = true;
|
use_disp = true;
|
||||||
}
|
}
|
||||||
else if (displayed_parent->subitems.size() != 2 &&
|
else if (details.entry->transactions.size() != 2 &&
|
||||||
item->value.quantity != item->value.cost &&
|
details.xact->amount != details.xact->cost) {
|
||||||
item->value.quantity.amounts.size() == 1 &&
|
amount_t unit_cost = details.xact->cost / details.xact->amount;
|
||||||
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;
|
std::ostringstream stream;
|
||||||
stream << item->value.quantity << " @ " << unit_cost;
|
stream << details.xact->amount << " @ " << unit_cost;
|
||||||
disp = stream.str();
|
disp = stream.str();
|
||||||
use_disp = true;
|
use_disp = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_disp)
|
if (! use_disp)
|
||||||
out << disp;
|
disp = std::string(details.xact->amount);
|
||||||
else
|
out << disp;
|
||||||
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
|
// jww (2004-07-31): this should be handled differently
|
||||||
if (! item->note.empty())
|
if (! details.xact->note.empty())
|
||||||
out << " ; " << item->note;
|
out << " ; " << details.xact->note;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case element_t::VALUE: {
|
case element_t::VALUE: {
|
||||||
balance_t value = compute_value(item);
|
balance_t value;
|
||||||
value.write(out, elem->min_width,
|
compute_value(value, details);
|
||||||
elem->max_width > 0 ? elem->max_width : elem->min_width);
|
value.write(out, elem->min_width, (elem->max_width > 0 ?
|
||||||
|
elem->max_width : elem->min_width));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case element_t::TOTAL: {
|
case element_t::TOTAL: {
|
||||||
balance_t value = compute_total(item);
|
balance_t value;
|
||||||
value.write(out, elem->min_width,
|
compute_total(value, details);
|
||||||
elem->max_width > 0 ? elem->max_width : elem->min_width);
|
value.write(out, elem->min_width, (elem->max_width > 0 ?
|
||||||
|
elem->max_width : elem->min_width));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case element_t::SPACER: {
|
case element_t::SPACER:
|
||||||
int depth = 0;
|
for (unsigned int i = 0; i < details.depth; i++) {
|
||||||
for (const item_t * i = item; i->parent; i = i->parent)
|
|
||||||
depth++;
|
|
||||||
|
|
||||||
for (const item_t * i = item->parent;
|
|
||||||
i && i->account && i != displayed_parent;
|
|
||||||
i = i->parent)
|
|
||||||
depth--;
|
|
||||||
|
|
||||||
while (--depth >= 0) {
|
|
||||||
if (elem->min_width > 0 || elem->max_width > 0)
|
if (elem->min_width > 0 || elem->max_width > 0)
|
||||||
out.width(elem->min_width > elem->max_width ?
|
out.width(elem->min_width > elem->max_width ?
|
||||||
elem->min_width : elem->max_width);
|
elem->min_width : elem->max_width);
|
||||||
out << " ";
|
out << " ";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
|
||||||
32
format.h
32
format.h
|
|
@ -2,13 +2,14 @@
|
||||||
#define _FORMAT_H
|
#define _FORMAT_H
|
||||||
|
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
#include "balance.h"
|
#include "valexpr.h"
|
||||||
#include "expr.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
std::string truncated(const std::string& str, unsigned int width);
|
std::string truncated(const std::string& str, unsigned int width);
|
||||||
std::string maximal_account_name(const item_t * item, const item_t * parent);
|
|
||||||
|
std::string partial_account_name(const account_t * account,
|
||||||
|
const unsigned int start_depth);
|
||||||
|
|
||||||
struct element_t
|
struct element_t
|
||||||
{
|
{
|
||||||
|
|
@ -48,28 +49,27 @@ struct element_t
|
||||||
|
|
||||||
struct format_t
|
struct format_t
|
||||||
{
|
{
|
||||||
element_t * elements;
|
std::auto_ptr<element_t> elements;
|
||||||
|
|
||||||
static std::auto_ptr<node_t> value_expr;
|
static std::auto_ptr<node_t> value_expr;
|
||||||
static std::auto_ptr<node_t> total_expr;
|
static std::auto_ptr<node_t> total_expr;
|
||||||
|
|
||||||
format_t(const std::string& _format) {
|
format_t(const std::string& _format) {
|
||||||
elements = parse_elements(_format);
|
elements.reset(parse_elements(_format));
|
||||||
}
|
|
||||||
~format_t() {
|
|
||||||
if (elements) delete elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static element_t * parse_elements(const std::string& fmt);
|
static element_t * parse_elements(const std::string& fmt);
|
||||||
|
|
||||||
void format_elements(std::ostream& out, const item_t * item,
|
void format_elements(std::ostream& out, const details_t& details) const;
|
||||||
const item_t * displayed_parent = NULL) const;
|
|
||||||
|
|
||||||
static balance_t compute_value(const item_t * item) {
|
static void compute_value(balance_t& result, const details_t& details) {
|
||||||
return value_expr.get() ? value_expr->compute(item) : balance_t();
|
if (value_expr.get())
|
||||||
|
value_expr->compute(result, details);
|
||||||
}
|
}
|
||||||
static balance_t compute_total(const item_t * item) {
|
|
||||||
return total_expr.get() ? total_expr->compute(item) : balance_t();
|
static void compute_total(balance_t& result, const details_t& details) {
|
||||||
|
if (total_expr.get())
|
||||||
|
total_expr->compute(result, details);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
196
item.cc
196
item.cc
|
|
@ -1,196 +0,0 @@
|
||||||
#include "item.h"
|
|
||||||
#include "expr.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
|
||||||
|
|
||||||
static inline void sum_items(const item_t * top,
|
|
||||||
const bool show_subtotals,
|
|
||||||
item_t * item)
|
|
||||||
{
|
|
||||||
if (top->account == item->account) {
|
|
||||||
item->value += top->value;
|
|
||||||
if (show_subtotals)
|
|
||||||
item->total += top->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (items_deque::const_iterator i = top->subitems.begin();
|
|
||||||
i != top->subitems.end();
|
|
||||||
i++)
|
|
||||||
sum_items(*i, show_subtotals, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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++) {
|
|
||||||
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_flattened || account->parent) &&
|
|
||||||
show_subtotals ? subitem->total : subitem->value)
|
|
||||||
item->subitems.push_back(subitem.release());
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
unsigned int count = 0;
|
|
||||||
item_t * result = NULL;
|
|
||||||
value_predicate pred_obj(predicate);
|
|
||||||
|
|
||||||
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
|
|
||||||
= std::find_if((*i)->transactions.begin(),
|
|
||||||
(*i)->transactions.end(), pred_obj);
|
|
||||||
j != (*i)->transactions.end();
|
|
||||||
j = std::find_if(++j,
|
|
||||||
transactions_list::const_iterator((*i)->transactions.end()),
|
|
||||||
pred_obj)) {
|
|
||||||
assert(*i == (*j)->entry);
|
|
||||||
|
|
||||||
if (! item) {
|
|
||||||
item = new item_t(*i);
|
|
||||||
item->index = count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If show_inverted is true, it implies show_related.
|
|
||||||
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_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) {
|
|
||||||
if (! result)
|
|
||||||
result = new item_t;
|
|
||||||
item->parent = result;
|
|
||||||
result->subitems.push_back(item);
|
|
||||||
|
|
||||||
if (std::difftime(result->date, item->date) < 0)
|
|
||||||
result->date = item->date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cmp_items {
|
|
||||||
const node_t * sort_order;
|
|
||||||
|
|
||||||
cmp_items(const node_t * _sort_order) : sort_order(_sort_order) {
|
|
||||||
assert(sort_order);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(const item_t * left, const item_t * right) const {
|
|
||||||
assert(left);
|
|
||||||
assert(right);
|
|
||||||
return sort_order->compute(left) < sort_order->compute(right);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void item_t::sort(const node_t * sort_order)
|
|
||||||
{
|
|
||||||
std::stable_sort(subitems.begin(), subitems.end(), cmp_items(sort_order));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace ledger
|
|
||||||
82
item.h
82
item.h
|
|
@ -1,82 +0,0 @@
|
||||||
#ifndef _ITEM_H
|
|
||||||
#define _ITEM_H
|
|
||||||
|
|
||||||
#include "ledger.h"
|
|
||||||
#include "balance.h"
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
|
|
||||||
namespace ledger {
|
|
||||||
|
|
||||||
struct node_t;
|
|
||||||
struct item_t;
|
|
||||||
typedef std::deque<item_t *> items_deque;
|
|
||||||
|
|
||||||
struct item_t
|
|
||||||
{
|
|
||||||
struct item_t * parent;
|
|
||||||
items_deque subitems;
|
|
||||||
|
|
||||||
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),
|
|
||||||
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();
|
|
||||||
i != subitems.end();
|
|
||||||
i++)
|
|
||||||
delete *i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_item(item_t * item) {
|
|
||||||
item->parent = this;
|
|
||||||
value += item->value;
|
|
||||||
subitems.push_back(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sort(const node_t * sort_order);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct node_t;
|
|
||||||
|
|
||||||
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 = NULL,
|
|
||||||
const bool show_related = false,
|
|
||||||
const bool show_inverted = false);
|
|
||||||
|
|
||||||
} // namespace report
|
|
||||||
|
|
||||||
#endif // _REPORT_H
|
|
||||||
28
ledger.cc
28
ledger.cc
|
|
@ -1,5 +1,6 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
#include "expr.h"
|
#include "valexpr.h"
|
||||||
|
#include "datetime.h"
|
||||||
#include "textual.h"
|
#include "textual.h"
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
|
|
||||||
|
|
@ -9,6 +10,29 @@ namespace ledger {
|
||||||
|
|
||||||
const std::string version = "2.0b";
|
const std::string version = "2.0b";
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
struct cmp_items {
|
||||||
|
const node_t * sort_order;
|
||||||
|
|
||||||
|
cmp_items(const node_t * _sort_order) : sort_order(_sort_order) {
|
||||||
|
assert(sort_order);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const item_t * left, const item_t * right) const {
|
||||||
|
assert(left);
|
||||||
|
assert(right);
|
||||||
|
return sort_order->compute(left) < sort_order->compute(right);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void item_t::sort(const node_t * sort_order)
|
||||||
|
{
|
||||||
|
std::stable_sort(subitems.begin(), subitems.end(), cmp_items(sort_order));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
ledger_t::~ledger_t()
|
ledger_t::~ledger_t()
|
||||||
{
|
{
|
||||||
delete master;
|
delete master;
|
||||||
|
|
@ -200,7 +224,7 @@ int parse_ledger_file(char * p, ledger_t * journal)
|
||||||
stream.read((char *)&magic, sizeof(magic));
|
stream.read((char *)&magic, sizeof(magic));
|
||||||
stream.seekg(start);
|
stream.seekg(start);
|
||||||
|
|
||||||
if (magic == magic_number)
|
if (magic == binary_magic_number)
|
||||||
return read_binary_ledger(stream, "", journal, master);
|
return read_binary_ledger(stream, "", journal, master);
|
||||||
else
|
else
|
||||||
return parse_textual_ledger(stream, journal, master);
|
return parse_textual_ledger(stream, journal, master);
|
||||||
|
|
|
||||||
335
ledger.h
335
ledger.h
|
|
@ -14,291 +14,32 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cctype>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#include "amount.h"
|
||||||
#include <cassert>
|
#include "balance.h"
|
||||||
#else
|
|
||||||
#ifdef assert
|
|
||||||
#undef assert
|
|
||||||
#endif
|
|
||||||
#define assert(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
extern const std::string version;
|
#define TRANSACTION_NORMAL 0x00
|
||||||
|
#define TRANSACTION_VIRTUAL 0x01
|
||||||
|
#define TRANSACTION_BALANCE 0x02
|
||||||
|
#define TRANSACTION_AUTO 0x04
|
||||||
|
#define TRANSACTION_HANDLED 0x08
|
||||||
|
#define TRANSACTION_DISPLAYED 0x10
|
||||||
|
|
||||||
class commodity_t;
|
|
||||||
class amount_t;
|
|
||||||
class transaction_t;
|
|
||||||
class entry_t;
|
class entry_t;
|
||||||
class account_t;
|
class account_t;
|
||||||
class ledger_t;
|
|
||||||
|
|
||||||
class amount_t
|
|
||||||
{
|
|
||||||
typedef void * base_type;
|
|
||||||
|
|
||||||
void _init();
|
|
||||||
void _copy(const amount_t& amt);
|
|
||||||
void _clear();
|
|
||||||
|
|
||||||
public:
|
|
||||||
base_type quantity; // amount, to MAX_PRECISION
|
|
||||||
commodity_t * commodity;
|
|
||||||
|
|
||||||
bool valid() const {
|
|
||||||
if (quantity)
|
|
||||||
return commodity != NULL;
|
|
||||||
else
|
|
||||||
return commodity == NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// constructors
|
|
||||||
amount_t(commodity_t * _commodity = NULL)
|
|
||||||
: quantity(NULL), commodity(_commodity) {}
|
|
||||||
|
|
||||||
amount_t(const amount_t& amt) : quantity(NULL) {
|
|
||||||
if (amt.quantity)
|
|
||||||
_copy(amt);
|
|
||||||
else
|
|
||||||
commodity = amt.commodity;
|
|
||||||
}
|
|
||||||
amount_t(const std::string& value) {
|
|
||||||
_init();
|
|
||||||
std::istringstream str(value);
|
|
||||||
str >> *this;
|
|
||||||
}
|
|
||||||
amount_t(const int value) : quantity(NULL), commodity(NULL) {
|
|
||||||
if (value != 0) {
|
|
||||||
std::string str;
|
|
||||||
std::ostringstream strstr(str);
|
|
||||||
strstr << value;
|
|
||||||
parse(strstr.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
amount_t(const unsigned int value) : quantity(NULL), commodity(NULL) {
|
|
||||||
if (value != 0) {
|
|
||||||
std::string str;
|
|
||||||
std::ostringstream strstr(str);
|
|
||||||
strstr << value;
|
|
||||||
parse(strstr.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
amount_t(const double value) : quantity(NULL), commodity(NULL) {
|
|
||||||
if (value != 0.0) {
|
|
||||||
std::string str;
|
|
||||||
std::ostringstream strstr(str);
|
|
||||||
strstr << value;
|
|
||||||
parse(strstr.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
~amount_t() {
|
|
||||||
if (quantity)
|
|
||||||
_clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// assignment operator
|
|
||||||
amount_t& operator=(const amount_t& amt);
|
|
||||||
amount_t& operator=(const std::string& value);
|
|
||||||
amount_t& operator=(const int value);
|
|
||||||
amount_t& operator=(const unsigned int value);
|
|
||||||
amount_t& operator=(const double value);
|
|
||||||
|
|
||||||
// general methods
|
|
||||||
amount_t round(int precision = -1) const;
|
|
||||||
|
|
||||||
// in-place arithmetic
|
|
||||||
amount_t& operator*=(const amount_t& amt);
|
|
||||||
amount_t& operator/=(const amount_t& amt);
|
|
||||||
amount_t& operator%=(const amount_t& amt);
|
|
||||||
amount_t& operator+=(const amount_t& amt);
|
|
||||||
amount_t& operator-=(const amount_t& amt);
|
|
||||||
|
|
||||||
// simple arithmetic
|
|
||||||
amount_t operator*(const amount_t& amt) const {
|
|
||||||
amount_t temp = *this;
|
|
||||||
temp *= amt;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
amount_t operator/(const amount_t& amt) const {
|
|
||||||
amount_t temp = *this;
|
|
||||||
temp /= amt;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
amount_t operator%(const amount_t& amt) const {
|
|
||||||
amount_t temp = *this;
|
|
||||||
temp %= amt;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
amount_t operator+(const amount_t& amt) const {
|
|
||||||
amount_t temp = *this;
|
|
||||||
temp += amt;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
amount_t operator-(const amount_t& amt) const {
|
|
||||||
amount_t temp = *this;
|
|
||||||
temp -= amt;
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// unary negation
|
|
||||||
amount_t& negate();
|
|
||||||
amount_t negated() const {
|
|
||||||
amount_t temp = *this;
|
|
||||||
temp.negate();
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
amount_t operator-() const {
|
|
||||||
return negated();
|
|
||||||
}
|
|
||||||
|
|
||||||
// test for non-zero (use ! for zero)
|
|
||||||
operator bool() const;
|
|
||||||
|
|
||||||
// comparisons to zero
|
|
||||||
bool operator<(const int num) const;
|
|
||||||
bool operator<=(const int num) const;
|
|
||||||
bool operator>(const int num) const;
|
|
||||||
bool operator>=(const int num) const;
|
|
||||||
|
|
||||||
// comparisons between amounts
|
|
||||||
bool operator<(const amount_t& amt) const;
|
|
||||||
bool operator<=(const amount_t& amt) const;
|
|
||||||
bool operator>(const amount_t& amt) const;
|
|
||||||
bool operator>=(const amount_t& amt) const;
|
|
||||||
bool operator==(const amount_t& amt) const;
|
|
||||||
bool operator!=(const amount_t& amt) const {
|
|
||||||
if (commodity != amt.commodity)
|
|
||||||
return true;
|
|
||||||
return ! (*this == amt);
|
|
||||||
}
|
|
||||||
|
|
||||||
amount_t value(const std::time_t moment) const;
|
|
||||||
|
|
||||||
operator std::string() const;
|
|
||||||
|
|
||||||
void parse(std::istream& in);
|
|
||||||
void parse(const std::string& str) {
|
|
||||||
std::istringstream stream(str);
|
|
||||||
parse(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
void write_quantity(std::ostream& out) const;
|
|
||||||
void read_quantity(std::istream& in);
|
|
||||||
|
|
||||||
friend std::istream& operator>>(std::istream& in, amount_t& amt);
|
|
||||||
};
|
|
||||||
|
|
||||||
void parse_quantity(std::istream& in, std::string& value);
|
|
||||||
void parse_commodity(std::istream& in, std::string& symbol);
|
|
||||||
|
|
||||||
inline amount_t abs(const amount_t& amt) {
|
|
||||||
return amt < 0 ? amt.negated() : amt;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::istream& operator>>(std::istream& in, amount_t& amt) {
|
|
||||||
amt.parse(in);
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& out, const amount_t& amt);
|
|
||||||
|
|
||||||
|
|
||||||
#define COMMODITY_STYLE_DEFAULTS 0x00
|
|
||||||
#define COMMODITY_STYLE_SUFFIXED 0x01
|
|
||||||
#define COMMODITY_STYLE_SEPARATED 0x02
|
|
||||||
#define COMMODITY_STYLE_EUROPEAN 0x04
|
|
||||||
#define COMMODITY_STYLE_THOUSANDS 0x08
|
|
||||||
#define COMMODITY_STYLE_CONSULTED 0x10
|
|
||||||
#define COMMODITY_STYLE_NOMARKET 0x20
|
|
||||||
|
|
||||||
typedef std::map<const std::time_t, amount_t> history_map;
|
|
||||||
typedef std::pair<const std::time_t, amount_t> history_pair;
|
|
||||||
|
|
||||||
typedef std::map<const std::string, commodity_t *> commodities_map;
|
|
||||||
typedef std::pair<const std::string, commodity_t *> commodities_pair;
|
|
||||||
|
|
||||||
class commodity_t
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::string symbol;
|
|
||||||
std::string name;
|
|
||||||
std::string note;
|
|
||||||
unsigned int precision;
|
|
||||||
unsigned int flags;
|
|
||||||
history_map history;
|
|
||||||
amount_t conversion;
|
|
||||||
unsigned long ident;
|
|
||||||
|
|
||||||
// If set, this global function pointer is called to determine
|
|
||||||
// whether prices have been updated in the meanwhile.
|
|
||||||
|
|
||||||
static void (*updater)(commodity_t * commodity,
|
|
||||||
const std::time_t date,
|
|
||||||
const amount_t& price,
|
|
||||||
const std::time_t moment);
|
|
||||||
|
|
||||||
// This map remembers all commodities that have been
|
|
||||||
// defined thus far.
|
|
||||||
|
|
||||||
static commodities_map commodities;
|
|
||||||
|
|
||||||
static void add_commodity(commodity_t * commodity,
|
|
||||||
const std::string symbol = "") {
|
|
||||||
commodities.insert(commodities_pair((symbol.empty() ?
|
|
||||||
commodity->symbol : symbol),
|
|
||||||
commodity));
|
|
||||||
}
|
|
||||||
static bool remove_commodity(commodity_t * commodity) {
|
|
||||||
commodities_map::size_type n = commodities.erase(commodity->symbol);
|
|
||||||
return n > 0;
|
|
||||||
}
|
|
||||||
static commodity_t * find_commodity(const std::string& symbol,
|
|
||||||
bool auto_create = false);
|
|
||||||
|
|
||||||
// Now the per-object constructor and methods
|
|
||||||
|
|
||||||
commodity_t(const std::string& _symbol = "",
|
|
||||||
unsigned int _precision = 2,
|
|
||||||
unsigned int _flags = COMMODITY_STYLE_DEFAULTS)
|
|
||||||
: symbol(_symbol), precision(_precision), flags(_flags) {}
|
|
||||||
|
|
||||||
void add_price(const std::time_t date, const amount_t& price) {
|
|
||||||
history.insert(history_pair(date, price));
|
|
||||||
}
|
|
||||||
bool remove_price(const std::time_t date) {
|
|
||||||
history_map::size_type n = history.erase(date);
|
|
||||||
return n > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_conversion(const amount_t& price) {
|
|
||||||
conversion = price;
|
|
||||||
}
|
|
||||||
|
|
||||||
amount_t value(const std::time_t moment = std::time(NULL));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#define TRANSACTION_NORMAL 0x0
|
|
||||||
#define TRANSACTION_VIRTUAL 0x1
|
|
||||||
#define TRANSACTION_BALANCE 0x2
|
|
||||||
#define TRANSACTION_AUTO 0x4
|
|
||||||
|
|
||||||
class transaction_t
|
class transaction_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
entry_t * entry;
|
entry_t * entry;
|
||||||
account_t * account;
|
account_t * account;
|
||||||
amount_t amount;
|
amount_t amount;
|
||||||
amount_t cost;
|
amount_t cost;
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
std::string note;
|
std::string note;
|
||||||
|
|
||||||
transaction_t(entry_t * _entry, account_t * _account)
|
transaction_t(entry_t * _entry, account_t * _account)
|
||||||
: entry(_entry), account(_account), flags(TRANSACTION_NORMAL) {}
|
: entry(_entry), account(_account), flags(TRANSACTION_NORMAL) {}
|
||||||
|
|
@ -351,23 +92,27 @@ class entry_t
|
||||||
typedef std::map<const std::string, account_t *> accounts_map;
|
typedef std::map<const std::string, account_t *> accounts_map;
|
||||||
typedef std::pair<const std::string, account_t *> accounts_pair;
|
typedef std::pair<const std::string, account_t *> accounts_pair;
|
||||||
|
|
||||||
inline std::ostream& operator<<(std::ostream& out, const account_t& acct);
|
|
||||||
|
|
||||||
class account_t
|
class account_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const account_t * parent;
|
account_t * parent;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string note;
|
std::string note;
|
||||||
accounts_map accounts;
|
accounts_map accounts;
|
||||||
mutable accounts_map accounts_cache;
|
transactions_list transactions;
|
||||||
transactions_list transactions;
|
balance_pair_t value;
|
||||||
unsigned long ident;
|
balance_pair_t total;
|
||||||
static unsigned long next_ident;
|
unsigned long depth;
|
||||||
|
unsigned long ident;
|
||||||
|
|
||||||
account_t(const account_t * _parent, const std::string& _name = "",
|
mutable std::string _fullname;
|
||||||
|
static unsigned long next_ident;
|
||||||
|
|
||||||
|
account_t(account_t * _parent,
|
||||||
|
const std::string& _name = "",
|
||||||
const std::string& _note = "")
|
const std::string& _note = "")
|
||||||
: parent(_parent), name(_name), note(_note) {}
|
: parent(_parent), name(_name), note(_note),
|
||||||
|
depth(parent ? parent->depth + 1 : 0) {}
|
||||||
|
|
||||||
~account_t();
|
~account_t();
|
||||||
|
|
||||||
|
|
@ -409,9 +154,9 @@ typedef std::list<entry_t *> entries_list;
|
||||||
class ledger_t
|
class ledger_t
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
account_t * master;
|
account_t * master;
|
||||||
entries_list entries;
|
entries_list entries;
|
||||||
|
mutable accounts_map accounts_cache;
|
||||||
std::list<std::string> sources;
|
std::list<std::string> sources;
|
||||||
|
|
||||||
ledger_t() {
|
ledger_t() {
|
||||||
|
|
@ -430,10 +175,18 @@ class ledger_t
|
||||||
}
|
}
|
||||||
|
|
||||||
account_t * find_account(const std::string& name, bool auto_create = true) {
|
account_t * find_account(const std::string& name, bool auto_create = true) {
|
||||||
return master->find_account(name, auto_create);
|
accounts_map::iterator c = accounts_cache.find(name);
|
||||||
|
if (c != accounts_cache.end())
|
||||||
|
return (*c).second;
|
||||||
|
|
||||||
|
account_t * account = master->find_account(name, auto_create);
|
||||||
|
accounts_cache.insert(accounts_pair(name, account));
|
||||||
|
return account;
|
||||||
}
|
}
|
||||||
account_t * find_account(const std::string& name) const {
|
account_t * find_account(const std::string& name) const {
|
||||||
return master->find_account(name, false);
|
// With auto_create false, the other `find_account' will not
|
||||||
|
// change the object.
|
||||||
|
return const_cast<ledger_t *>(this)->find_account(name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool add_entry(entry_t * entry);
|
bool add_entry(entry_t * entry);
|
||||||
|
|
@ -444,6 +197,8 @@ class ledger_t
|
||||||
|
|
||||||
int parse_ledger_file(char * p, ledger_t * journal);
|
int parse_ledger_file(char * p, ledger_t * journal);
|
||||||
|
|
||||||
|
extern const std::string version;
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _LEDGER_H
|
#endif // _LEDGER_H
|
||||||
|
|
|
||||||
134
main.cc
134
main.cc
|
|
@ -1,11 +1,10 @@
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
#include "balance.h"
|
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "textual.h"
|
#include "textual.h"
|
||||||
#include "binary.h"
|
#include "binary.h"
|
||||||
#include "item.h"
|
#include "valexpr.h"
|
||||||
#include "expr.h"
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
#include "walk.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
@ -20,7 +19,9 @@ namespace ledger {
|
||||||
// The command-line balance report
|
// The command-line balance report
|
||||||
//
|
//
|
||||||
|
|
||||||
static const std::string bal_fmt = "%20T%2_%-n\n";
|
static const std::string bal_fmt = "%20T %2_%-n\n";
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
unsigned int show_balances(std::ostream& out,
|
unsigned int show_balances(std::ostream& out,
|
||||||
items_deque& items,
|
items_deque& items,
|
||||||
|
|
@ -80,6 +81,7 @@ void balance_report(std::ostream& out,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
|
@ -96,6 +98,8 @@ static const std::string print_fmt
|
||||||
static bool show_commodities_revalued = false;
|
static bool show_commodities_revalued = false;
|
||||||
static bool show_commodities_revalued_only = false;
|
static bool show_commodities_revalued_only = false;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
static void report_value_change(std::ostream& out,
|
static void report_value_change(std::ostream& out,
|
||||||
const std::time_t date,
|
const std::time_t date,
|
||||||
const balance_pair_t& balance,
|
const balance_pair_t& balance,
|
||||||
|
|
@ -218,6 +222,7 @@ void register_report(std::ostream& out,
|
||||||
first_line_format, next_lines_format);
|
first_line_format, next_lines_format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void set_price_conversion(const std::string& setting)
|
void set_price_conversion(const std::string& setting)
|
||||||
{
|
{
|
||||||
|
|
@ -362,6 +367,7 @@ int main(int argc, char * argv[])
|
||||||
bool show_expanded = false;
|
bool show_expanded = false;
|
||||||
bool show_related = false;
|
bool show_related = false;
|
||||||
bool show_inverted = false;
|
bool show_inverted = false;
|
||||||
|
bool show_empty = false;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
bool debug = false;
|
bool debug = false;
|
||||||
|
|
@ -492,11 +498,9 @@ int main(int argc, char * argv[])
|
||||||
format_string = optarg;
|
format_string = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if 0
|
|
||||||
case 'E':
|
case 'E':
|
||||||
show_empty = true;
|
show_empty = true;
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
|
|
||||||
case 'n':
|
case 'n':
|
||||||
show_subtotals = false;
|
show_subtotals = false;
|
||||||
|
|
@ -708,7 +712,11 @@ int main(int argc, char * argv[])
|
||||||
else
|
else
|
||||||
predicate_string += "(";
|
predicate_string += "(";
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
}
|
||||||
|
else if (argv[index][0] == '-') {
|
||||||
|
predicate_string += "&";
|
||||||
|
}
|
||||||
|
else {
|
||||||
predicate_string += "|";
|
predicate_string += "|";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -736,6 +744,8 @@ int main(int argc, char * argv[])
|
||||||
|
|
||||||
if (first)
|
if (first)
|
||||||
first = false;
|
first = false;
|
||||||
|
else if (argv[index][0] == '-')
|
||||||
|
predicate_string += "&";
|
||||||
else
|
else
|
||||||
predicate_string += "|";
|
predicate_string += "|";
|
||||||
|
|
||||||
|
|
@ -753,7 +763,7 @@ int main(int argc, char * argv[])
|
||||||
predicate_string += ")";
|
predicate_string += ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the predicate
|
// Compile the predicates
|
||||||
|
|
||||||
if (! predicate_string.empty()) {
|
if (! predicate_string.empty()) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -763,6 +773,9 @@ int main(int argc, char * argv[])
|
||||||
predicate.reset(ledger::parse_expr(predicate_string));
|
predicate.reset(ledger::parse_expr(predicate_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (display_predicate_string.empty() && command == "b" && ! show_empty)
|
||||||
|
display_predicate_string = "T";
|
||||||
|
|
||||||
if (! display_predicate_string.empty()) {
|
if (! display_predicate_string.empty()) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (debug)
|
if (debug)
|
||||||
|
|
@ -795,74 +808,53 @@ int main(int argc, char * argv[])
|
||||||
show_inverted = true;
|
show_inverted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::auto_ptr<ledger::item_t> top;
|
const char * f;
|
||||||
std::auto_ptr<ledger::item_t> list;
|
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 == "e") {
|
if (command == "b") {
|
||||||
top.reset(new ledger::item_t);
|
std::auto_ptr<ledger::format_t> format(new ledger::format_t(f));
|
||||||
ledger::item_t * item = new ledger::item_t(new_entry.get());
|
|
||||||
for (ledger::transactions_list::const_iterator i
|
ledger::walk_accounts(journal->master,
|
||||||
= new_entry->transactions.begin();
|
ledger::format_account(std::cout, *format.get()),
|
||||||
i != new_entry->transactions.end();
|
predicate.get(), show_related, show_inverted,
|
||||||
i++)
|
show_subtotals, display_predicate.get());
|
||||||
item->add_item(new ledger::item_t(*i));
|
|
||||||
top->add_item(item);
|
if (! display_predicate.get() ||
|
||||||
}
|
ledger::item_predicate(display_predicate.get())(journal->master)) {
|
||||||
else if ((! show_related || ! predicate.get()) &&
|
std::string end_format = "--------------------\n";
|
||||||
(command == "b" || command == "E")) {
|
end_format += f;
|
||||||
top.reset(ledger::walk_accounts(NULL, journal->master, predicate.get(),
|
format.get()->elements.reset(ledger::format_t::parse_elements(end_format));
|
||||||
command != "E" && show_subtotals,
|
ledger::format_account(std::cout, *format.get())(journal->master, true);
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
std::string first_line_format;
|
||||||
|
std::string next_lines_format;
|
||||||
|
|
||||||
if (top.get()) {
|
if (const char * p = std::strstr(f, "%/")) {
|
||||||
const char * f;
|
first_line_format = std::string(f, 0, p - f);
|
||||||
if (! format_string.empty())
|
next_lines_format = std::string(p + 2);
|
||||||
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 {
|
} else {
|
||||||
std::string first_line_format;
|
first_line_format = next_lines_format = f;
|
||||||
std::string next_lines_format;
|
|
||||||
|
|
||||||
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 = next_lines_format = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.get(), display_predicate.get(),
|
|
||||||
sort_order.get(), *format, *nformat,
|
|
||||||
show_expanded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::walk_entries(journal->entries.begin(), journal->entries.end(),
|
||||||
|
ledger::format_transaction(std::cout,
|
||||||
|
first_line_format,
|
||||||
|
next_lines_format),
|
||||||
|
predicate.get(), show_related, show_inverted,
|
||||||
|
display_predicate.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the cache, if need be
|
// Save the cache, if need be
|
||||||
|
|
|
||||||
252
textual.cc
252
textual.cc
|
|
@ -1,8 +1,9 @@
|
||||||
#include "textual.h"
|
#include "textual.h"
|
||||||
|
#include "datetime.h"
|
||||||
|
#include "autoxact.h"
|
||||||
|
#include "valexpr.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
#include "expr.h"
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
@ -13,11 +14,6 @@
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
#if 0
|
|
||||||
static const std::string entry1_fmt = "%10d %p";
|
|
||||||
static const std::string entryn_fmt = " %-30a %15t";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_LINE 1024
|
#define MAX_LINE 1024
|
||||||
|
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
@ -29,31 +25,6 @@ static account_t * last_account;
|
||||||
static std::string last_desc;
|
static std::string last_desc;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static std::time_t now = std::time(NULL);
|
|
||||||
static struct std::tm * now_tm = std::localtime(&now);
|
|
||||||
|
|
||||||
static std::time_t base = -1;
|
|
||||||
static int base_year = -1;
|
|
||||||
|
|
||||||
static const int month_days[12] = {
|
|
||||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char * formats[] = {
|
|
||||||
"%Y/%m/%d",
|
|
||||||
"%m/%d",
|
|
||||||
"%Y.%m.%d",
|
|
||||||
"%m.%d",
|
|
||||||
"%Y-%m-%d",
|
|
||||||
"%m-%d",
|
|
||||||
"%a",
|
|
||||||
"%A",
|
|
||||||
"%b",
|
|
||||||
"%B",
|
|
||||||
"%Y",
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
inline char * skip_ws(char * ptr)
|
inline char * skip_ws(char * ptr)
|
||||||
{
|
{
|
||||||
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
|
while (*ptr == ' ' || *ptr == '\t' || *ptr == '\n')
|
||||||
|
|
@ -61,6 +32,16 @@ inline char * skip_ws(char * ptr)
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline char peek_next_nonws(std::istream& in)
|
||||||
|
{
|
||||||
|
char c = in.peek();
|
||||||
|
while (! in.eof() && std::isspace(c) && c != '\n') {
|
||||||
|
in.get(c);
|
||||||
|
c = in.peek();
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
inline char * next_element(char * buf, bool variable = false)
|
inline char * next_element(char * buf, bool variable = false)
|
||||||
{
|
{
|
||||||
for (char * p = buf; *p; p++) {
|
for (char * p = buf; *p; p++) {
|
||||||
|
|
@ -83,109 +64,6 @@ inline char * next_element(char * buf, bool variable = false)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool parse_date_mask(const char * date_str, struct std::tm * result)
|
|
||||||
{
|
|
||||||
for (const char ** f = formats; *f; f++) {
|
|
||||||
memset(result, INT_MAX, sizeof(struct std::tm));
|
|
||||||
if (strptime(date_str, *f, result))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool parse_date(const char * date_str, std::time_t * result, const int year)
|
|
||||||
{
|
|
||||||
struct std::tm when;
|
|
||||||
|
|
||||||
if (! parse_date_mask(date_str, &when))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
when.tm_hour = 0;
|
|
||||||
when.tm_min = 0;
|
|
||||||
when.tm_sec = 0;
|
|
||||||
|
|
||||||
if (when.tm_year == -1)
|
|
||||||
when.tm_year = ((year == -1) ? now_tm->tm_year : (year - 1900));
|
|
||||||
|
|
||||||
if (when.tm_mon == -1)
|
|
||||||
when.tm_mon = 0;
|
|
||||||
|
|
||||||
if (when.tm_mday == -1)
|
|
||||||
when.tm_mday = 1;
|
|
||||||
|
|
||||||
*result = std::mktime(&when);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool quick_parse_date(char * date_str, std::time_t * result)
|
|
||||||
{
|
|
||||||
int year = -1, month = -1, day, num = 0;
|
|
||||||
|
|
||||||
for (char * p = date_str; *p; p++) {
|
|
||||||
if (*p == '/' || *p == '-' || *p == '.') {
|
|
||||||
if (year == -1)
|
|
||||||
year = num;
|
|
||||||
else
|
|
||||||
month = num;
|
|
||||||
num = 0;
|
|
||||||
}
|
|
||||||
else if (*p < '0' || *p > '9') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
num *= 10;
|
|
||||||
num += *p - '0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
day = num;
|
|
||||||
|
|
||||||
if (month == -1) {
|
|
||||||
month = year;
|
|
||||||
year = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base == -1 || year != base_year) {
|
|
||||||
struct std::tm when;
|
|
||||||
|
|
||||||
when.tm_hour = 0;
|
|
||||||
when.tm_min = 0;
|
|
||||||
when.tm_sec = 0;
|
|
||||||
|
|
||||||
base_year = year == -1 ? now_tm->tm_year + 1900 : year;
|
|
||||||
when.tm_year = year == -1 ? now_tm->tm_year : year - 1900;
|
|
||||||
when.tm_mon = 0;
|
|
||||||
when.tm_mday = 1;
|
|
||||||
|
|
||||||
base = std::mktime(&when);
|
|
||||||
}
|
|
||||||
|
|
||||||
*result = base;
|
|
||||||
|
|
||||||
--month;
|
|
||||||
while (--month >= 0) {
|
|
||||||
*result += month_days[month] * 24 * 60 * 60;
|
|
||||||
if (month == 1 && year % 4 == 0 && year != 2000) // february in leap years
|
|
||||||
*result += 24 * 60 * 60;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (--day)
|
|
||||||
*result += day * 24 * 60 * 60;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline char peek_next_nonws(std::istream& in)
|
|
||||||
{
|
|
||||||
char c = in.peek();
|
|
||||||
while (! in.eof() && std::isspace(c) && c != '\n') {
|
|
||||||
in.get(c);
|
|
||||||
c = in.peek();
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
transaction_t * parse_transaction_text(char * line, account_t * account,
|
transaction_t * parse_transaction_text(char * line, account_t * account,
|
||||||
entry_t * entry)
|
entry_t * entry)
|
||||||
{
|
{
|
||||||
|
|
@ -233,9 +111,9 @@ transaction_t * parse_transaction_text(char * line, account_t * account,
|
||||||
xact->account = account->find_account(p);
|
xact->account = account->find_account(p);
|
||||||
|
|
||||||
if (! xact->amount.commodity)
|
if (! xact->amount.commodity)
|
||||||
xact->amount.commodity = commodity_t::find_commodity("", true);
|
xact->amount.commodity = commodity_t::null_commodity;
|
||||||
if (! xact->cost.commodity)
|
if (! xact->cost.commodity)
|
||||||
xact->cost.commodity = commodity_t::find_commodity("", true);
|
xact->cost.commodity = commodity_t::null_commodity;
|
||||||
|
|
||||||
return xact;
|
return xact;
|
||||||
}
|
}
|
||||||
|
|
@ -250,95 +128,6 @@ transaction_t * parse_transaction(std::istream& in, account_t * account,
|
||||||
return parse_transaction_text(line, account, entry);
|
return parse_transaction_text(line, account, entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
class automated_transaction_t
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
masks_list masks;
|
|
||||||
transactions_list transactions;
|
|
||||||
|
|
||||||
automated_transaction_t(masks_list& _masks,
|
|
||||||
transactions_list& _transactions) {
|
|
||||||
masks.insert(masks.begin(), _masks.begin(), _masks.end());
|
|
||||||
transactions.insert(transactions.begin(),
|
|
||||||
_transactions.begin(), _transactions.end());
|
|
||||||
// Take over ownership of the pointers
|
|
||||||
_transactions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
~automated_transaction_t() {
|
|
||||||
for (transactions_list::iterator i = transactions.begin();
|
|
||||||
i != transactions.end();
|
|
||||||
i++)
|
|
||||||
delete *i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void extend_entry(entry_t * entry);
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::vector<automated_transaction_t *>
|
|
||||||
automated_transactions_vector;
|
|
||||||
|
|
||||||
void automated_transaction_t::extend_entry(entry_t * entry)
|
|
||||||
{
|
|
||||||
for (transactions_list::iterator i = entry->transactions.begin();
|
|
||||||
i != entry->transactions.end();
|
|
||||||
i++)
|
|
||||||
if (matches(masks, *((*i)->account))) {
|
|
||||||
for (transactions_list::iterator t = transactions.begin();
|
|
||||||
t != transactions.end();
|
|
||||||
t++) {
|
|
||||||
amount_t amt;
|
|
||||||
if ((*t)->amount.commodity->symbol.empty())
|
|
||||||
amt = (*i)->amount * (*t)->amount;
|
|
||||||
else
|
|
||||||
amt = (*t)->amount;
|
|
||||||
|
|
||||||
transaction_t * xact
|
|
||||||
= new transaction_t(entry, (*t)->account, amt, amt,
|
|
||||||
(*t)->flags | TRANSACTION_AUTO);
|
|
||||||
entry->add_transaction(xact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class automated_transactions_t
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
automated_transactions_vector automated_transactions;
|
|
||||||
|
|
||||||
~automated_transactions_t() {
|
|
||||||
for (automated_transactions_vector::iterator i
|
|
||||||
= automated_transactions.begin();
|
|
||||||
i != automated_transactions.end();
|
|
||||||
i++)
|
|
||||||
delete *i;
|
|
||||||
}
|
|
||||||
|
|
||||||
void extend_entry(entry_t * entry) {
|
|
||||||
for (automated_transactions_vector::iterator i
|
|
||||||
= automated_transactions.begin();
|
|
||||||
i != automated_transactions.end();
|
|
||||||
i++)
|
|
||||||
(*i)->extend_entry(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_automated_transaction(automated_transaction_t * auto_xact) {
|
|
||||||
automated_transactions.push_back(auto_xact);
|
|
||||||
}
|
|
||||||
bool remove_automated_transaction(automated_transaction_t * auto_xact) {
|
|
||||||
for (automated_transactions_vector::iterator i
|
|
||||||
= automated_transactions.begin();
|
|
||||||
i != automated_transactions.end();
|
|
||||||
i++) {
|
|
||||||
if (*i == auto_xact) {
|
|
||||||
automated_transactions.erase(i);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void parse_automated_transactions(std::istream& in, account_t * account,
|
void parse_automated_transactions(std::istream& in, account_t * account,
|
||||||
automated_transactions_t& auto_xacts)
|
automated_transactions_t& auto_xacts)
|
||||||
{
|
{
|
||||||
|
|
@ -498,11 +287,6 @@ entry_t * parse_entry(std::istream& in, account_t * master)
|
||||||
return curr;
|
return curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Textual ledger parser
|
|
||||||
//
|
|
||||||
|
|
||||||
unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
|
unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
|
||||||
account_t * master)
|
account_t * master)
|
||||||
{
|
{
|
||||||
|
|
@ -676,7 +460,8 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
|
||||||
amount_t price;
|
amount_t price;
|
||||||
|
|
||||||
parse_commodity(in, symbol);
|
parse_commodity(in, symbol);
|
||||||
in >> line; // the price
|
in.getline(line, MAX_LINE);
|
||||||
|
linenum++;
|
||||||
price.parse(line);
|
price.parse(line);
|
||||||
|
|
||||||
commodity_t * commodity = commodity_t::find_commodity(symbol, true);
|
commodity_t * commodity = commodity_t::find_commodity(symbol, true);
|
||||||
|
|
@ -733,7 +518,8 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
|
||||||
unsigned int curr_linenum = linenum;
|
unsigned int curr_linenum = linenum;
|
||||||
std::string curr_path = path;
|
std::string curr_path = path;
|
||||||
|
|
||||||
count += parse_textual_ledger(stream, journal, account_stack.front());
|
count += parse_textual_ledger(stream, journal,
|
||||||
|
account_stack.front());
|
||||||
|
|
||||||
linenum = curr_linenum;
|
linenum = curr_linenum;
|
||||||
path = curr_path;
|
path = curr_path;
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,6 @@ namespace ledger {
|
||||||
extern unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
|
extern unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
|
||||||
account_t * master = NULL);
|
account_t * master = NULL);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
} // namespace ledger
|
} // namespace ledger
|
||||||
|
|
||||||
#endif // _TEXTUAL_H
|
#endif // _TEXTUAL_H
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "expr.h"
|
#include "valexpr.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
#include "datetime.h"
|
||||||
#include "textual.h"
|
#include "textual.h"
|
||||||
|
|
||||||
#include <pcre.h>
|
#include <pcre.h>
|
||||||
|
|
@ -90,138 +91,199 @@ bool matches(const masks_list& regexps, const std::string& str,
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
balance_t node_t::compute(const item_t * item) const
|
void node_t::compute(balance_t& result, const details_t& details) const
|
||||||
{
|
{
|
||||||
balance_t temp;
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CONSTANT_A:
|
case CONSTANT_A:
|
||||||
temp = constant_a;
|
result = constant_a;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONSTANT_T:
|
case CONSTANT_T:
|
||||||
temp = amount_t((unsigned int) constant_t);
|
result = (unsigned int) constant_t;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AMOUNT:
|
case AMOUNT:
|
||||||
temp = item->value.quantity;
|
if (details.xact)
|
||||||
|
result = details.xact->amount;
|
||||||
|
else if (details.account)
|
||||||
|
result = details.account->value.quantity;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COST:
|
case COST:
|
||||||
temp = item->value.cost;
|
if (details.xact)
|
||||||
|
result = details.xact->cost;
|
||||||
|
else if (details.account)
|
||||||
|
result = details.account->value.cost;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BALANCE:
|
case BALANCE:
|
||||||
temp = item->total.quantity - item->value.quantity;
|
if (details.balance) {
|
||||||
|
result = details.balance->quantity;
|
||||||
|
if (details.xact)
|
||||||
|
result -= details.xact->amount;
|
||||||
|
else if (details.account)
|
||||||
|
result -= details.account->value.quantity;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COST_BALANCE:
|
case COST_BALANCE:
|
||||||
temp = item->total.cost - item->value.cost;
|
if (details.balance) {
|
||||||
|
result = details.balance->cost;
|
||||||
|
if (details.xact)
|
||||||
|
result -= details.xact->cost;
|
||||||
|
else if (details.account)
|
||||||
|
result -= details.account->value.cost;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOTAL:
|
case TOTAL:
|
||||||
temp = item->total.quantity;
|
if (details.balance)
|
||||||
|
result = details.balance->quantity;
|
||||||
|
else if (details.account)
|
||||||
|
result = details.account->total.quantity;
|
||||||
break;
|
break;
|
||||||
case COST_TOTAL:
|
case COST_TOTAL:
|
||||||
temp = item->total.cost;
|
if (details.balance)
|
||||||
|
result = details.balance->cost;
|
||||||
|
else if (details.account)
|
||||||
|
result = details.account->total.cost;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DATE:
|
case DATE:
|
||||||
temp = amount_t((unsigned int) item->date);
|
if (details.entry)
|
||||||
|
result = (unsigned int) details.entry->date;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CLEARED:
|
case CLEARED:
|
||||||
temp = amount_t(item->state == entry_t::CLEARED ? 1 : 0);
|
if (details.entry)
|
||||||
|
result = details.entry->state == entry_t::CLEARED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REAL:
|
case REAL:
|
||||||
temp = amount_t(item->flags & TRANSACTION_VIRTUAL ? 0 : 1);
|
if (details.xact)
|
||||||
|
result = bool(details.xact->flags & TRANSACTION_VIRTUAL);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INDEX:
|
case INDEX:
|
||||||
temp = amount_t(item->index + 1);
|
if (details.index)
|
||||||
|
result = *details.index + 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_ARITH_MEAN:
|
case F_ARITH_MEAN:
|
||||||
assert(left);
|
if (details.index) {
|
||||||
temp = left->compute(item);
|
assert(left);
|
||||||
temp /= amount_t(item->index + 1);
|
left->compute(result, details);
|
||||||
|
result /= amount_t(*details.index + 1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_NEG:
|
case F_NEG:
|
||||||
assert(left);
|
assert(left);
|
||||||
temp = left->compute(item).negated();
|
left->compute(result, details);
|
||||||
|
result.negate();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_ABS:
|
case F_ABS:
|
||||||
assert(left);
|
assert(left);
|
||||||
temp = abs(left->compute(item));
|
left->compute(result, details);
|
||||||
|
result = abs(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_PAYEE_MASK:
|
case F_PAYEE_MASK:
|
||||||
assert(mask);
|
assert(mask);
|
||||||
temp = (mask->match(item->payee) || mask->match(item->note)) ? 1 : 0;
|
if (details.entry)
|
||||||
|
result = mask->match(details.entry->payee);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_ACCOUNT_MASK:
|
case F_ACCOUNT_MASK:
|
||||||
assert(mask);
|
assert(mask);
|
||||||
temp = (item->account &&
|
if (details.account)
|
||||||
mask->match(item->account->fullname())) ? 1 : 0;
|
result = mask->match(details.account->fullname());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case F_VALUE: {
|
case F_VALUE: {
|
||||||
assert(left);
|
assert(left);
|
||||||
temp = left->compute(item);
|
left->compute(result, details);
|
||||||
|
|
||||||
std::time_t moment = -1;
|
std::time_t moment = -1;
|
||||||
if (right) {
|
if (right && details.entry) {
|
||||||
switch (right->type) {
|
switch (right->type) {
|
||||||
case DATE: moment = item->date; break;
|
case DATE: moment = details.entry->date; break;
|
||||||
default:
|
default:
|
||||||
throw compute_error("Invalid date passed to P(v,d)");
|
throw compute_error("Invalid date passed to P(v,d)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
temp = temp.value(moment);
|
result = result.value(moment);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case O_NOT:
|
case O_NOT:
|
||||||
temp = left->compute(item) ? 0 : 1;
|
left->compute(result, details);
|
||||||
|
result = result ? false : true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_QUES:
|
case O_QUES:
|
||||||
temp = left->compute(item);
|
assert(left);
|
||||||
if (temp)
|
assert(right);
|
||||||
temp = right->left->compute(item);
|
assert(right->type == O_COL);
|
||||||
|
left->compute(result, details);
|
||||||
|
if (result)
|
||||||
|
right->left->compute(result, details);
|
||||||
else
|
else
|
||||||
temp = right->right->compute(item);
|
right->right->compute(result, details);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case O_AND:
|
case O_AND:
|
||||||
|
assert(left);
|
||||||
|
assert(right);
|
||||||
|
left->compute(result, details);
|
||||||
|
if (result)
|
||||||
|
right->compute(result, details);
|
||||||
|
break;
|
||||||
|
|
||||||
case O_OR:
|
case O_OR:
|
||||||
|
assert(left);
|
||||||
|
assert(right);
|
||||||
|
left->compute(result, details);
|
||||||
|
if (! result)
|
||||||
|
right->compute(result, details);
|
||||||
|
break;
|
||||||
|
|
||||||
case O_EQ:
|
case O_EQ:
|
||||||
case O_LT:
|
case O_LT:
|
||||||
case O_LTE:
|
case O_LTE:
|
||||||
case O_GT:
|
case O_GT:
|
||||||
case O_GTE:
|
case O_GTE: {
|
||||||
|
assert(left);
|
||||||
|
assert(right);
|
||||||
|
left->compute(result, details);
|
||||||
|
balance_t temp = result;
|
||||||
|
right->compute(result, details);
|
||||||
|
switch (type) {
|
||||||
|
case O_EQ: result = temp == result; break;
|
||||||
|
case O_LT: result = temp < result; break;
|
||||||
|
case O_LTE: result = temp <= result; break;
|
||||||
|
case O_GT: result = temp > result; break;
|
||||||
|
case O_GTE: result = temp >= result; break;
|
||||||
|
default: assert(0); break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case O_ADD:
|
case O_ADD:
|
||||||
case O_SUB:
|
case O_SUB:
|
||||||
case O_MUL:
|
case O_MUL:
|
||||||
case O_DIV: {
|
case O_DIV: {
|
||||||
assert(left);
|
assert(left);
|
||||||
assert(right);
|
assert(right);
|
||||||
balance_t left_bal = left->compute(item);
|
right->compute(result, details);
|
||||||
balance_t right_bal = right->compute(item);
|
balance_t temp = result;
|
||||||
|
left->compute(result, details);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case O_AND: temp = (left_bal && right_bal) ? 1 : 0; break;
|
case O_ADD: result += temp; break;
|
||||||
case O_OR: temp = (left_bal || right_bal) ? 1 : 0; break;
|
case O_SUB: result -= temp; break;
|
||||||
case O_EQ: temp = (left_bal == right_bal) ? 1 : 0; break;
|
case O_MUL: result *= temp; break;
|
||||||
case O_LT: temp = (left_bal < right_bal) ? 1 : 0; break;
|
case O_DIV: result /= temp; break;
|
||||||
case O_LTE: temp = (left_bal <= right_bal) ? 1 : 0; break;
|
|
||||||
case O_GT: temp = (left_bal > right_bal) ? 1 : 0; break;
|
|
||||||
case O_GTE: temp = (left_bal >= right_bal) ? 1 : 0; break;
|
|
||||||
case O_ADD: temp = left_bal + right_bal; break;
|
|
||||||
case O_SUB: temp = left_bal - right_bal; break;
|
|
||||||
case O_MUL: temp = left_bal * right_bal; break;
|
|
||||||
case O_DIV: temp = left_bal / right_bal; break;
|
|
||||||
default: assert(0); break;
|
default: assert(0); break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -232,8 +294,6 @@ balance_t node_t::compute(const item_t * item) const
|
||||||
assert(0);
|
assert(0);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return temp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node_t * parse_term(std::istream& in);
|
node_t * parse_term(std::istream& in);
|
||||||
|
|
@ -608,8 +668,13 @@ namespace ledger {
|
||||||
static void dump_tree(std::ostream& out, node_t * node)
|
static void dump_tree(std::ostream& out, node_t * node)
|
||||||
{
|
{
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case node_t::CONSTANT_A: out << "CONST[" << node->constant_a << "]"; break;
|
case node_t::CONSTANT_A:
|
||||||
case node_t::CONSTANT_T: out << "DATE/TIME[" << node->constant_t << "]"; break;
|
out << "CONST[" << node->constant_a << "]";
|
||||||
|
break;
|
||||||
|
case node_t::CONSTANT_T:
|
||||||
|
out << "DATE/TIME[" << node->constant_t << "]";
|
||||||
|
break;
|
||||||
|
|
||||||
case node_t::AMOUNT: out << "AMOUNT"; break;
|
case node_t::AMOUNT: out << "AMOUNT"; break;
|
||||||
case node_t::COST: out << "COST"; break;
|
case node_t::COST: out << "COST"; break;
|
||||||
case node_t::DATE: out << "DATE"; break;
|
case node_t::DATE: out << "DATE"; break;
|
||||||
|
|
@ -674,11 +739,36 @@ static void dump_tree(std::ostream& out, node_t * node)
|
||||||
|
|
||||||
case node_t::O_AND:
|
case node_t::O_AND:
|
||||||
case node_t::O_OR:
|
case node_t::O_OR:
|
||||||
|
out << "(";
|
||||||
|
dump_tree(out, node->left);
|
||||||
|
switch (node->type) {
|
||||||
|
case node_t::O_AND: out << " & "; break;
|
||||||
|
case node_t::O_OR: out << " | "; break;
|
||||||
|
default: assert(0); break;
|
||||||
|
}
|
||||||
|
dump_tree(out, node->right);
|
||||||
|
out << ")";
|
||||||
|
break;
|
||||||
|
|
||||||
case node_t::O_EQ:
|
case node_t::O_EQ:
|
||||||
case node_t::O_LT:
|
case node_t::O_LT:
|
||||||
case node_t::O_LTE:
|
case node_t::O_LTE:
|
||||||
case node_t::O_GT:
|
case node_t::O_GT:
|
||||||
case node_t::O_GTE:
|
case node_t::O_GTE:
|
||||||
|
out << "(";
|
||||||
|
dump_tree(out, node->left);
|
||||||
|
switch (node->type) {
|
||||||
|
case node_t::O_EQ: out << "="; break;
|
||||||
|
case node_t::O_LT: out << "<"; break;
|
||||||
|
case node_t::O_LTE: out << "<="; break;
|
||||||
|
case node_t::O_GT: out << ">"; break;
|
||||||
|
case node_t::O_GTE: out << ">="; break;
|
||||||
|
default: assert(0); break;
|
||||||
|
}
|
||||||
|
dump_tree(out, node->right);
|
||||||
|
out << ")";
|
||||||
|
break;
|
||||||
|
|
||||||
case node_t::O_ADD:
|
case node_t::O_ADD:
|
||||||
case node_t::O_SUB:
|
case node_t::O_SUB:
|
||||||
case node_t::O_MUL:
|
case node_t::O_MUL:
|
||||||
|
|
@ -686,13 +776,6 @@ static void dump_tree(std::ostream& out, node_t * node)
|
||||||
out << "(";
|
out << "(";
|
||||||
dump_tree(out, node->left);
|
dump_tree(out, node->left);
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case node_t::O_AND: out << " & "; break;
|
|
||||||
case node_t::O_OR: out << " | "; break;
|
|
||||||
case node_t::O_EQ: out << "="; break;
|
|
||||||
case node_t::O_LT: out << "<"; break;
|
|
||||||
case node_t::O_LTE: out << "<="; break;
|
|
||||||
case node_t::O_GT: out << ">"; break;
|
|
||||||
case node_t::O_GTE: out << ">="; break;
|
|
||||||
case node_t::O_ADD: out << "+"; break;
|
case node_t::O_ADD: out << "+"; break;
|
||||||
case node_t::O_SUB: out << "-"; break;
|
case node_t::O_SUB: out << "-"; break;
|
||||||
case node_t::O_MUL: out << "*"; break;
|
case node_t::O_MUL: out << "*"; break;
|
||||||
|
|
@ -714,7 +797,7 @@ static void dump_tree(std::ostream& out, node_t * node)
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
ledger::dump_tree(std::cout, ledger::parse_expr(argv[1], NULL));
|
ledger::dump_tree(std::cout, ledger::parse_expr(argv[1]));
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
#define _EXPR_H
|
#define _EXPR_H
|
||||||
|
|
||||||
#include "ledger.h"
|
#include "ledger.h"
|
||||||
#include "balance.h"
|
|
||||||
#include "item.h"
|
|
||||||
|
|
||||||
namespace ledger {
|
namespace ledger {
|
||||||
|
|
||||||
|
|
@ -30,6 +28,33 @@ bool matches(const masks_list& regexps, const std::string& str,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
struct details_t
|
||||||
|
{
|
||||||
|
const entry_t * entry;
|
||||||
|
const transaction_t * xact;
|
||||||
|
const account_t * account;
|
||||||
|
const balance_pair_t * balance;
|
||||||
|
const unsigned int * index;
|
||||||
|
const unsigned int depth;
|
||||||
|
|
||||||
|
details_t(const entry_t * _entry,
|
||||||
|
const balance_pair_t * _balance = NULL,
|
||||||
|
const unsigned int * _index = NULL)
|
||||||
|
: entry(_entry), xact(NULL), account(NULL),
|
||||||
|
balance(_balance), index(_index), depth(0) {}
|
||||||
|
|
||||||
|
details_t(const transaction_t * _xact,
|
||||||
|
const balance_pair_t * _balance = NULL,
|
||||||
|
const unsigned int * _index = NULL)
|
||||||
|
: entry(_xact->entry), xact(_xact), account(_xact->account),
|
||||||
|
balance(_balance), index(_index), depth(0) {}
|
||||||
|
|
||||||
|
details_t(const account_t * _account,
|
||||||
|
const unsigned int _depth = 0)
|
||||||
|
: entry(NULL), xact(NULL), account(_account),
|
||||||
|
balance(NULL), index(NULL), depth(_depth) {}
|
||||||
|
};
|
||||||
|
|
||||||
struct node_t
|
struct node_t
|
||||||
{
|
{
|
||||||
enum kind_t {
|
enum kind_t {
|
||||||
|
|
@ -95,7 +120,7 @@ struct node_t
|
||||||
if (right) delete right;
|
if (right) delete right;
|
||||||
}
|
}
|
||||||
|
|
||||||
balance_t compute(const item_t * item) const;
|
void compute(balance_t& result, const details_t& details) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
node_t * parse_expr(std::istream& in);
|
node_t * parse_expr(std::istream& in);
|
||||||
|
|
@ -120,64 +145,6 @@ inline node_t * find_node(node_t * node, node_t::kind_t type) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_tree(std::ostream& out, node_t * node);
|
|
||||||
|
|
||||||
class value_predicate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const node_t * predicate;
|
|
||||||
|
|
||||||
explicit value_predicate(const node_t * _predicate)
|
|
||||||
: predicate(_predicate) {}
|
|
||||||
|
|
||||||
bool operator ()(const transaction_t * xact) const {
|
|
||||||
if (! predicate) {
|
|
||||||
return true;
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator ()(const entry_t * entry) const {
|
|
||||||
if (! predicate) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
// might match one by not another transactions -- we let the
|
|
||||||
// entry through if at least one of the transactions meets the
|
|
||||||
// criterion
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator ()(const item_t * item) const {
|
|
||||||
return ! predicate || predicate->compute(item);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace report
|
} // namespace report
|
||||||
|
|
||||||
#endif // _REPORT_H
|
#endif // _REPORT_H
|
||||||
234
walk.h
Normal file
234
walk.h
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
#ifndef _WALK_H
|
||||||
|
#define _WALK_H
|
||||||
|
|
||||||
|
#include "ledger.h"
|
||||||
|
#include "balance.h"
|
||||||
|
#include "format.h"
|
||||||
|
#include "valexpr.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
class item_predicate
|
||||||
|
{
|
||||||
|
const node_t * predicate;
|
||||||
|
balance_pair_t * balance;
|
||||||
|
unsigned int * index;
|
||||||
|
|
||||||
|
public:
|
||||||
|
item_predicate(const node_t * _predicate,
|
||||||
|
balance_pair_t * _balance = NULL,
|
||||||
|
unsigned int * _index = NULL)
|
||||||
|
: predicate(_predicate), balance(_balance), index(_index) {}
|
||||||
|
|
||||||
|
bool operator()(const entry_t * entry) const {
|
||||||
|
if (predicate) {
|
||||||
|
balance_t result;
|
||||||
|
predicate->compute(result, details_t(entry, balance, index));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const transaction_t * xact) const {
|
||||||
|
if (predicate) {
|
||||||
|
balance_t result;
|
||||||
|
predicate->compute(result, details_t(xact, balance, index));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(const account_t * account) const {
|
||||||
|
if (predicate) {
|
||||||
|
balance_t result;
|
||||||
|
predicate->compute(result, details_t(account));
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void add_to_balance_pair(balance_pair_t& balance,
|
||||||
|
transaction_t * xact,
|
||||||
|
const bool inverted = false)
|
||||||
|
{
|
||||||
|
if (inverted) {
|
||||||
|
balance.quantity += - xact->amount;
|
||||||
|
balance.cost += - xact->cost;
|
||||||
|
} else {
|
||||||
|
balance += *xact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class format_transaction
|
||||||
|
{
|
||||||
|
std::ostream& output_stream;
|
||||||
|
const format_t& first_line_format;
|
||||||
|
const format_t& next_lines_format;
|
||||||
|
mutable entry_t * last_entry;
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_transaction(std::ostream& _output_stream,
|
||||||
|
const format_t& _first_line_format,
|
||||||
|
const format_t& _next_lines_format)
|
||||||
|
: output_stream(_output_stream),
|
||||||
|
first_line_format(_first_line_format),
|
||||||
|
next_lines_format(_next_lines_format),
|
||||||
|
last_entry(NULL) {}
|
||||||
|
|
||||||
|
void operator()(transaction_t * xact,
|
||||||
|
balance_pair_t * balance,
|
||||||
|
unsigned int * index,
|
||||||
|
const bool inverted) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ignore_transaction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void operator()(transaction_t * xact,
|
||||||
|
balance_pair_t * balance,
|
||||||
|
unsigned int * index,
|
||||||
|
const bool inverted) const {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
void handle_transaction(transaction_t * xact,
|
||||||
|
Function functor,
|
||||||
|
item_predicate& pred_functor,
|
||||||
|
const bool related,
|
||||||
|
const bool inverted,
|
||||||
|
balance_pair_t * balance = NULL,
|
||||||
|
unsigned int * index = NULL)
|
||||||
|
{
|
||||||
|
// If inverted is true, it implies related.
|
||||||
|
if (! inverted && ! (xact->flags & TRANSACTION_HANDLED)) {
|
||||||
|
xact->flags |= TRANSACTION_HANDLED;
|
||||||
|
if (pred_functor(xact)) {
|
||||||
|
xact->flags |= TRANSACTION_DISPLAYED;
|
||||||
|
functor(xact, balance, index, inverted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (related)
|
||||||
|
for (transactions_list::iterator i = xact->entry->transactions.begin();
|
||||||
|
i != xact->entry->transactions.end();
|
||||||
|
i++) {
|
||||||
|
if (*i == xact || ((*i)->flags & (TRANSACTION_AUTO |
|
||||||
|
TRANSACTION_HANDLED)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
(*i)->flags |= TRANSACTION_HANDLED;
|
||||||
|
if (pred_functor(xact)) {
|
||||||
|
xact->flags |= TRANSACTION_DISPLAYED;
|
||||||
|
functor(*i, balance, index, inverted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
void walk_entries(entries_list::iterator begin,
|
||||||
|
entries_list::iterator end,
|
||||||
|
Function functor,
|
||||||
|
const node_t * predicate,
|
||||||
|
const bool related,
|
||||||
|
const bool inverted,
|
||||||
|
const node_t * display_predicate = NULL)
|
||||||
|
{
|
||||||
|
balance_pair_t balance;
|
||||||
|
unsigned int index;
|
||||||
|
item_predicate pred_functor(predicate, &balance, &index);
|
||||||
|
item_predicate disp_pred_functor(display_predicate, &balance, &index);
|
||||||
|
|
||||||
|
for (entries_list::iterator i = begin; i != end; i++)
|
||||||
|
for (transactions_list::iterator j = (*i)->transactions.begin();
|
||||||
|
j != (*i)->transactions.end();
|
||||||
|
j++)
|
||||||
|
if (pred_functor(*j))
|
||||||
|
handle_transaction(*j, functor, disp_pred_functor,
|
||||||
|
related, inverted, &balance, &index);
|
||||||
|
}
|
||||||
|
|
||||||
|
class format_account
|
||||||
|
{
|
||||||
|
std::ostream& output_stream;
|
||||||
|
const format_t& format;
|
||||||
|
|
||||||
|
mutable const account_t * last_account;
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_account(std::ostream& _output_stream, const format_t& _format)
|
||||||
|
: output_stream(_output_stream), format(_format) {}
|
||||||
|
|
||||||
|
void operator()(const account_t * account, bool report_top = false);
|
||||||
|
};
|
||||||
|
|
||||||
|
void calc__accounts(account_t * account,
|
||||||
|
const node_t * predicate,
|
||||||
|
const bool related,
|
||||||
|
const bool inverted,
|
||||||
|
const bool calc_subtotals);
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
void walk__accounts(const account_t * account,
|
||||||
|
Function functor,
|
||||||
|
const node_t * display_predicate)
|
||||||
|
{
|
||||||
|
if (! display_predicate || item_predicate(display_predicate)(account))
|
||||||
|
functor(account);
|
||||||
|
|
||||||
|
for (accounts_map::const_iterator i = account->accounts.begin();
|
||||||
|
i != account->accounts.end();
|
||||||
|
i++)
|
||||||
|
walk__accounts((*i).second, functor, display_predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
void walk_accounts(account_t * account,
|
||||||
|
Function functor,
|
||||||
|
const node_t * predicate,
|
||||||
|
const bool related,
|
||||||
|
const bool inverted,
|
||||||
|
const bool calc_subtotals,
|
||||||
|
const node_t * display_predicate = NULL)
|
||||||
|
{
|
||||||
|
calc__accounts(account, predicate, related, inverted, calc_subtotals);
|
||||||
|
walk__accounts<Function>(account, functor, display_predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
void sum_entries(entries_list& entries,
|
||||||
|
account_t * account,
|
||||||
|
const bool show_subtotals)
|
||||||
|
{
|
||||||
|
for (entries_list::const_iterator i = entries.begin();
|
||||||
|
i != entries.end();
|
||||||
|
i++)
|
||||||
|
for (transactions_list::const_iterator j = (*i)->transactions.begin();
|
||||||
|
j != (*i)->transactions.end();
|
||||||
|
j++)
|
||||||
|
if ((*j)->account == account) {
|
||||||
|
account->value += *(*j);
|
||||||
|
if (show_subtotals)
|
||||||
|
for (account_t * a = account;
|
||||||
|
a;
|
||||||
|
a = a->parent)
|
||||||
|
a->total += *(*j);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (accounts_map::iterator i = account->accounts.begin();
|
||||||
|
i != account->accounts.end();
|
||||||
|
i++)
|
||||||
|
sum_items(entries, *i, show_subtotals);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _WALK_H
|
||||||
Loading…
Add table
Reference in a new issue