Added a --strict session option
When enabled, if any accounts or commodities are seen in an uncleared transaction, which were not seen previously in a cleared or pending transaction or a textual directive dealing with accounts or commodities, a warning is generated about the unknown item.
This commit is contained in:
parent
2694335e54
commit
f2f52066d2
7 changed files with 54 additions and 14 deletions
|
|
@ -69,6 +69,7 @@ class account_t : public scope_t
|
||||||
optional<string> note;
|
optional<string> note;
|
||||||
unsigned short depth;
|
unsigned short depth;
|
||||||
accounts_map accounts;
|
accounts_map accounts;
|
||||||
|
bool known;
|
||||||
|
|
||||||
mutable void * data;
|
mutable void * data;
|
||||||
mutable string _fullname;
|
mutable string _fullname;
|
||||||
|
|
@ -77,7 +78,8 @@ class account_t : public scope_t
|
||||||
const string& _name = "",
|
const string& _name = "",
|
||||||
const optional<string>& _note = none)
|
const optional<string>& _note = none)
|
||||||
: scope_t(), parent(_parent), name(_name), note(_note),
|
: scope_t(), parent(_parent), name(_name), note(_note),
|
||||||
depth(parent ? parent->depth + 1 : 0), data(NULL) {
|
depth(parent ? parent->depth + 1 : 0),
|
||||||
|
known(false), data(NULL) {
|
||||||
TRACE_CTOR(account_t, "account_t *, const string&, const string&");
|
TRACE_CTOR(account_t, "account_t *, const string&, const string&");
|
||||||
}
|
}
|
||||||
account_t(const account_t& other)
|
account_t(const account_t& other)
|
||||||
|
|
@ -87,6 +89,7 @@ class account_t : public scope_t
|
||||||
note(other.note),
|
note(other.note),
|
||||||
depth(other.depth),
|
depth(other.depth),
|
||||||
accounts(other.accounts),
|
accounts(other.accounts),
|
||||||
|
known(other.known),
|
||||||
data(NULL) {
|
data(NULL) {
|
||||||
TRACE_CTOR(account_t, "copy");
|
TRACE_CTOR(account_t, "copy");
|
||||||
assert(other.data == NULL);
|
assert(other.data == NULL);
|
||||||
|
|
|
||||||
|
|
@ -150,6 +150,7 @@ public:
|
||||||
#define COMMODITY_NOMARKET 0x10
|
#define COMMODITY_NOMARKET 0x10
|
||||||
#define COMMODITY_BUILTIN 0x20
|
#define COMMODITY_BUILTIN 0x20
|
||||||
#define COMMODITY_WALKED 0x40
|
#define COMMODITY_WALKED 0x40
|
||||||
|
#define COMMODITY_KNOWN 0x80
|
||||||
|
|
||||||
string symbol;
|
string symbol;
|
||||||
amount_t::precision_t precision;
|
amount_t::precision_t precision;
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,9 @@ public:
|
||||||
|
|
||||||
std::size_t parse(std::istream& in,
|
std::size_t parse(std::istream& in,
|
||||||
scope_t& session_scope,
|
scope_t& session_scope,
|
||||||
account_t * master,
|
account_t * master = NULL,
|
||||||
const path * original_file);
|
const path * original_file = NULL,
|
||||||
|
bool strict = false);
|
||||||
|
|
||||||
bool valid() const;
|
bool valid() const;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace {
|
||||||
<< std::endl << str << std::endl;
|
<< std::endl << str << std::endl;
|
||||||
{
|
{
|
||||||
std::istringstream in(str);
|
std::istringstream in(str);
|
||||||
report.session.journal->parse(in, report.session, NULL, NULL);
|
report.session.journal->parse(in, report.session);
|
||||||
report.session.clean_accounts();
|
report.session.clean_accounts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,8 @@ std::size_t session_t::read_journal(std::istream& in,
|
||||||
{
|
{
|
||||||
if (! master)
|
if (! master)
|
||||||
master = journal->master;
|
master = journal->master;
|
||||||
std::size_t count = journal->parse(in, *this, master, &pathname);
|
std::size_t count = journal->parse(in, *this, master, &pathname,
|
||||||
|
HANDLED(strict));
|
||||||
clean_accounts(); // remove calculated totals
|
clean_accounts(); // remove calculated totals
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +221,9 @@ option_t<session_t> * session_t::lookup_option(const char * p)
|
||||||
case 'p':
|
case 'p':
|
||||||
OPT(price_db_);
|
OPT(price_db_);
|
||||||
break;
|
break;
|
||||||
|
case 's':
|
||||||
|
OPT(strict);
|
||||||
|
break;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
OPT_CH(download); // -Q
|
OPT_CH(download); // -Q
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ public:
|
||||||
|
|
||||||
OPTION(session_t, input_date_format_);
|
OPTION(session_t, input_date_format_);
|
||||||
OPTION(session_t, price_db_);
|
OPTION(session_t, price_db_);
|
||||||
|
OPTION(session_t, strict);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ namespace {
|
||||||
const path * original_file;
|
const path * original_file;
|
||||||
accounts_map account_aliases;
|
accounts_map account_aliases;
|
||||||
int current_year;
|
int current_year;
|
||||||
|
bool strict;
|
||||||
|
|
||||||
path pathname;
|
path pathname;
|
||||||
char linebuf[MAX_LINE + 1];
|
char linebuf[MAX_LINE + 1];
|
||||||
|
|
@ -78,6 +79,7 @@ namespace {
|
||||||
journal_t& _journal,
|
journal_t& _journal,
|
||||||
account_t * _master = NULL,
|
account_t * _master = NULL,
|
||||||
const path * _original_file = NULL,
|
const path * _original_file = NULL,
|
||||||
|
bool _strict = false,
|
||||||
instance_t * _parent = NULL);
|
instance_t * _parent = NULL);
|
||||||
|
|
||||||
~instance_t();
|
~instance_t();
|
||||||
|
|
@ -115,7 +117,8 @@ namespace {
|
||||||
xact_t * parse_xact(char * line,
|
xact_t * parse_xact(char * line,
|
||||||
std::streamsize len,
|
std::streamsize len,
|
||||||
account_t * account,
|
account_t * account,
|
||||||
entry_t * entry);
|
entry_t * entry,
|
||||||
|
bool honor_strict = true);
|
||||||
|
|
||||||
bool parse_xacts(account_t * account,
|
bool parse_xacts(account_t * account,
|
||||||
entry_base_t& entry);
|
entry_base_t& entry);
|
||||||
|
|
@ -168,13 +171,15 @@ instance_t::instance_t(std::list<account_t *>& _account_stack,
|
||||||
journal_t& _journal,
|
journal_t& _journal,
|
||||||
account_t * _master,
|
account_t * _master,
|
||||||
const path * _original_file,
|
const path * _original_file,
|
||||||
|
bool _strict,
|
||||||
instance_t * _parent)
|
instance_t * _parent)
|
||||||
: account_stack(_account_stack),
|
: account_stack(_account_stack),
|
||||||
#if defined(TIMELOG_SUPPORT)
|
#if defined(TIMELOG_SUPPORT)
|
||||||
timelog(_timelog),
|
timelog(_timelog),
|
||||||
#endif
|
#endif
|
||||||
parent(_parent), in(_in), session_scope(_session_scope),
|
parent(_parent), in(_in), session_scope(_session_scope),
|
||||||
journal(_journal), master(_master), original_file(_original_file)
|
journal(_journal), master(_master),
|
||||||
|
original_file(_original_file), strict(_strict)
|
||||||
{
|
{
|
||||||
TRACE_CTOR(instance_t, "...");
|
TRACE_CTOR(instance_t, "...");
|
||||||
|
|
||||||
|
|
@ -418,11 +423,13 @@ void instance_t::default_commodity_directive(char * line)
|
||||||
amount_t amt(skip_ws(line + 1));
|
amount_t amt(skip_ws(line + 1));
|
||||||
assert(amt.valid());
|
assert(amt.valid());
|
||||||
amount_t::current_pool->default_commodity = &amt.commodity();
|
amount_t::current_pool->default_commodity = &amt.commodity();
|
||||||
|
amt.commodity().add_flags(COMMODITY_KNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::default_account_directive(char * line)
|
void instance_t::default_account_directive(char * line)
|
||||||
{
|
{
|
||||||
journal.basket = account_stack.front()->find_account(skip_ws(line + 1));
|
journal.basket = account_stack.front()->find_account(skip_ws(line + 1));
|
||||||
|
journal.basket->known = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::price_conversion_directive(char * line)
|
void instance_t::price_conversion_directive(char * line)
|
||||||
|
|
@ -484,8 +491,12 @@ void instance_t::price_entry_directive(char * line)
|
||||||
assert(price.valid());
|
assert(price.valid());
|
||||||
|
|
||||||
if (commodity_t * commodity =
|
if (commodity_t * commodity =
|
||||||
amount_t::current_pool->find_or_create(symbol))
|
amount_t::current_pool->find_or_create(symbol)) {
|
||||||
commodity->add_price(datetime, price);
|
commodity->add_price(datetime, price);
|
||||||
|
commodity->add_flags(COMMODITY_KNOWN);
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::nomarket_directive(char * line)
|
void instance_t::nomarket_directive(char * line)
|
||||||
|
|
@ -496,7 +507,7 @@ void instance_t::nomarket_directive(char * line)
|
||||||
|
|
||||||
if (commodity_t * commodity =
|
if (commodity_t * commodity =
|
||||||
amount_t::current_pool->find_or_create(symbol))
|
amount_t::current_pool->find_or_create(symbol))
|
||||||
commodity->add_flags(COMMODITY_NOMARKET);
|
commodity->add_flags(COMMODITY_NOMARKET | COMMODITY_KNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void instance_t::year_directive(char * line)
|
void instance_t::year_directive(char * line)
|
||||||
|
|
@ -616,7 +627,7 @@ void instance_t::include_directive(char * line)
|
||||||
timelog,
|
timelog,
|
||||||
#endif
|
#endif
|
||||||
stream, session_scope, journal, master,
|
stream, session_scope, journal, master,
|
||||||
&filename, this);
|
&filename, strict, this);
|
||||||
instance.parse();
|
instance.parse();
|
||||||
|
|
||||||
errors += instance.errors;
|
errors += instance.errors;
|
||||||
|
|
@ -722,7 +733,8 @@ void instance_t::general_directive(char * line)
|
||||||
xact_t * instance_t::parse_xact(char * line,
|
xact_t * instance_t::parse_xact(char * line,
|
||||||
std::streamsize len,
|
std::streamsize len,
|
||||||
account_t * account,
|
account_t * account,
|
||||||
entry_t * entry)
|
entry_t * entry,
|
||||||
|
bool honor_strict)
|
||||||
{
|
{
|
||||||
TRACE_START(xact_details, 1, "Time spent parsing transactions:");
|
TRACE_START(xact_details, 1, "Time spent parsing transactions:");
|
||||||
|
|
||||||
|
|
@ -800,6 +812,14 @@ xact_t * instance_t::parse_xact(char * line,
|
||||||
if (! xact->account)
|
if (! xact->account)
|
||||||
xact->account = account->find_account(name);
|
xact->account = account->find_account(name);
|
||||||
|
|
||||||
|
if (honor_strict && strict && ! xact->account->known) {
|
||||||
|
if (xact->_state == item_t::UNCLEARED)
|
||||||
|
std::cerr << "Warning: \"" << pathname << "\", line " << linenum
|
||||||
|
<< ": Unknown account '" << xact->account->fullname()
|
||||||
|
<< "'" << std::endl;
|
||||||
|
xact->account->known = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the optional amount
|
// Parse the optional amount
|
||||||
|
|
||||||
bool saw_amount = false;
|
bool saw_amount = false;
|
||||||
|
|
@ -817,6 +837,15 @@ xact_t * instance_t::parse_xact(char * line,
|
||||||
static_cast<uint_least8_t>(expr_t::PARSE_NO_REDUCE) |
|
static_cast<uint_least8_t>(expr_t::PARSE_NO_REDUCE) |
|
||||||
static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN));
|
static_cast<uint_least8_t>(expr_t::PARSE_NO_ASSIGN));
|
||||||
|
|
||||||
|
if (! xact->amount.is_null() && honor_strict && strict &&
|
||||||
|
xact->amount.has_commodity() &&
|
||||||
|
! xact->amount.commodity().has_flags(COMMODITY_KNOWN)) {
|
||||||
|
if (xact->_state == item_t::UNCLEARED)
|
||||||
|
std::cerr << "Warning: \"" << pathname << "\", line " << linenum
|
||||||
|
<< ": Unknown commodity '" << xact->amount.commodity()
|
||||||
|
<< "'" << std::endl;
|
||||||
|
xact->amount.commodity().add_flags(COMMODITY_KNOWN);
|
||||||
|
}
|
||||||
#if 0
|
#if 0
|
||||||
// jww (2009-02-12): This isn't quite working yet; it causes cost computes
|
// jww (2009-02-12): This isn't quite working yet; it causes cost computes
|
||||||
// to skyrocket, since the per-unit price isn't also being reduced by the
|
// to skyrocket, since the per-unit price isn't also being reduced by the
|
||||||
|
|
@ -1022,7 +1051,7 @@ bool instance_t::parse_xacts(account_t * account,
|
||||||
std::streamsize len = read_line(line);
|
std::streamsize len = read_line(line);
|
||||||
assert(len > 0);
|
assert(len > 0);
|
||||||
|
|
||||||
if (xact_t * xact = parse_xact(line, len, account, NULL)) {
|
if (xact_t * xact = parse_xact(line, len, account, NULL, false)) {
|
||||||
entry.add_xact(xact);
|
entry.add_xact(xact);
|
||||||
added = true;
|
added = true;
|
||||||
}
|
}
|
||||||
|
|
@ -1144,7 +1173,8 @@ expr_t::ptr_op_t instance_t::lookup(const string& name)
|
||||||
std::size_t journal_t::parse(std::istream& in,
|
std::size_t journal_t::parse(std::istream& in,
|
||||||
scope_t& session_scope,
|
scope_t& session_scope,
|
||||||
account_t * master,
|
account_t * master,
|
||||||
const path * original_file)
|
const path * original_file,
|
||||||
|
bool strict)
|
||||||
{
|
{
|
||||||
TRACE_START(parsing_total, 1, "Total time spent parsing text:");
|
TRACE_START(parsing_total, 1, "Total time spent parsing text:");
|
||||||
|
|
||||||
|
|
@ -1158,7 +1188,7 @@ std::size_t journal_t::parse(std::istream& in,
|
||||||
timelog,
|
timelog,
|
||||||
#endif
|
#endif
|
||||||
in, session_scope, *this, master,
|
in, session_scope, *this, master,
|
||||||
original_file);
|
original_file, strict);
|
||||||
parsing_instance.parse();
|
parsing_instance.parse();
|
||||||
|
|
||||||
TRACE_STOP(parsing_total, 1);
|
TRACE_STOP(parsing_total, 1);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue