Revert "Added the concept of "balance setting transactions"."
This reverts commit c93175183e.
The feature needs more thought and is not ready for 2.6.1.
This commit is contained in:
parent
0db8d69796
commit
0005e2887d
5 changed files with 6 additions and 209 deletions
116
NEWS
116
NEWS
|
|
@ -2,121 +2,7 @@
|
||||||
|
|
||||||
* 2.6.1
|
* 2.6.1
|
||||||
|
|
||||||
- Added the concept of "balance setting transactions":
|
- This version has no new features, it's all bug fixes.
|
||||||
|
|
||||||
# Setting an account's balance
|
|
||||||
|
|
||||||
You can now manually set an account's balance to whatever you want, at
|
|
||||||
any time. Here's how it might look at the beginning of your Ledger
|
|
||||||
file:
|
|
||||||
|
|
||||||
2008/07/27 Starting fresh
|
|
||||||
Assets:Checking = $1,000.00
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
If Assets:Checking is empty, this is no different from omitting the
|
|
||||||
"=". However, if Assets:Checking did have a prior balance, the amount
|
|
||||||
of the transaction will be auto-calculated so that the final balance
|
|
||||||
of Assets:Checking is now $1,000.00.
|
|
||||||
|
|
||||||
Let me give an example of this. Say you have this:
|
|
||||||
|
|
||||||
2008/07/27 Starting fresh
|
|
||||||
Assets:Checking $750.00
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
2008/07/27 Starting fresh
|
|
||||||
Assets:Checking = $1,000.00
|
|
||||||
Equity:Adjustments
|
|
||||||
|
|
||||||
These two entries are exactly equivalent to these two:
|
|
||||||
|
|
||||||
2008/07/27 Starting fresh
|
|
||||||
Assets:Checking $750.00
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
2008/07/27 Starting fresh
|
|
||||||
Assets:Checking $250.00
|
|
||||||
Equity:Adjustments
|
|
||||||
|
|
||||||
The use of the "=" sign here is that it sets the transaction's amount
|
|
||||||
to whatever is required to satisfy the assignment. This is the
|
|
||||||
behavior if the transaction's amount is left empty.
|
|
||||||
|
|
||||||
# Multiple commodities
|
|
||||||
|
|
||||||
As far as commodities go, the = sign only works if the account
|
|
||||||
balance's commodity matches the commodity of the amount after the
|
|
||||||
equals sign. However, if the account has multiple commodities, only
|
|
||||||
the matching commodity is affected. Here's what I mean:
|
|
||||||
|
|
||||||
2008/07/24 Opening Balance
|
|
||||||
Assets:Checking = $250.00 ; we force set it
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
2008/07/24 Opening Balance
|
|
||||||
Assets:Checking = EC 250.00 ; we force set it again
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
This is an error, because $250.00 cannot be auto-balanced to match EC
|
|
||||||
250.00. However:
|
|
||||||
|
|
||||||
2008/07/24 Opening Balance
|
|
||||||
Assets:Checking = $250.00 ; we force set it again
|
|
||||||
Assets:Checking EC 100.00 ; and add some EC's
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
2008/07/24 Opening Balance
|
|
||||||
Assets:Checking = EC 250.00 ; we force set the EC's
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
This is *not* an error, because the latter auto-balancing transaction
|
|
||||||
only affects the EC 100.00 part of the account's balance; the $250.00
|
|
||||||
part is left alone.
|
|
||||||
|
|
||||||
# Checking statement balances
|
|
||||||
|
|
||||||
When you reconcile a statement, there are typically one or more
|
|
||||||
transactions which result in a known balance. Here's how you specify
|
|
||||||
that in your Ledger data:
|
|
||||||
|
|
||||||
2008/07/24 Opening Balance
|
|
||||||
Assets:Checking = $100.00
|
|
||||||
Equity:Opening Balances
|
|
||||||
|
|
||||||
2008/07/30 We spend money, with a known balance afterward
|
|
||||||
Expenses:Food $20.00
|
|
||||||
Assets:Checking = $80.00
|
|
||||||
|
|
||||||
2008/07/30 Again we spend money, but this time with all the info
|
|
||||||
Expenses:Food $20.00
|
|
||||||
Assets:Checking $-20.00 = $60.00
|
|
||||||
|
|
||||||
2008/07/30 This entry yield an 'unbalanced' error
|
|
||||||
Expenses:Food $20.00
|
|
||||||
Assets:Checking $-20.00 = $30.00
|
|
||||||
|
|
||||||
The last entry in this set fails to balance with an unbalanced
|
|
||||||
remainder of $-10.00. Either the entry must be corrected, or you can
|
|
||||||
have Ledger deal with the remainder automatically:
|
|
||||||
|
|
||||||
2008/07/30 The fixed entry
|
|
||||||
Expenses:Food $20.00
|
|
||||||
Assets:Checking $-20.00 = $30.00
|
|
||||||
Equity:Adjustments
|
|
||||||
|
|
||||||
# Conclusion
|
|
||||||
|
|
||||||
This simple feature has all the utility of @check, plus auto-balancing
|
|
||||||
to match known target balances, plus the ability to guarantee that an
|
|
||||||
account which uses only one commodity does contain only that
|
|
||||||
commodity.
|
|
||||||
|
|
||||||
This feature slows down textual parsing slightly, does not affect
|
|
||||||
speed when loading from the binary cache.
|
|
||||||
|
|
||||||
- The rest of the changes in the version is all bug fixes (around 45 of
|
|
||||||
them).
|
|
||||||
|
|
||||||
* 2.6.0.90
|
* 2.6.0.90
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,10 +182,6 @@ unsigned int parse_ledger_data(config_t& config,
|
||||||
journal, acct);
|
journal, acct);
|
||||||
if (! journal->price_db.empty())
|
if (! journal->price_db.empty())
|
||||||
journal->sources.push_back(journal->price_db);
|
journal->sources.push_back(journal->price_db);
|
||||||
|
|
||||||
// Clear out what was set during the textual parsing phase
|
|
||||||
clear_account_xdata acct_cleaner;
|
|
||||||
walk_accounts(*journal->master, acct_cleaner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
81
textual.cc
81
textual.cc
|
|
@ -201,15 +201,13 @@ transaction_t * parse_transaction(char * line, account_t * account,
|
||||||
goto finished;
|
goto finished;
|
||||||
if (p == ';')
|
if (p == ';')
|
||||||
goto parse_note;
|
goto parse_note;
|
||||||
if (p == '=' && entry)
|
|
||||||
goto parse_assign;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
unsigned long beg = (long)in.tellg();
|
unsigned long beg = (long)in.tellg();
|
||||||
|
|
||||||
xact->amount_expr =
|
xact->amount_expr =
|
||||||
parse_amount_expr(in, xact->amount, xact.get(),
|
parse_amount_expr(in, xact->amount, xact.get(),
|
||||||
PARSE_VALEXPR_NO_REDUCE | PARSE_VALEXPR_NO_ASSIGN);
|
PARSE_VALEXPR_NO_REDUCE);
|
||||||
|
|
||||||
unsigned long end = (long)in.tellg();
|
unsigned long end = (long)in.tellg();
|
||||||
xact->amount_expr.expr = std::string(line, beg, end - beg);
|
xact->amount_expr.expr = std::string(line, beg, end - beg);
|
||||||
|
|
@ -243,8 +241,7 @@ transaction_t * parse_transaction(char * line, account_t * account,
|
||||||
unsigned long beg = (long)in.tellg();
|
unsigned long beg = (long)in.tellg();
|
||||||
|
|
||||||
if (parse_amount_expr(in, *xact->cost, xact.get(),
|
if (parse_amount_expr(in, *xact->cost, xact.get(),
|
||||||
PARSE_VALEXPR_NO_MIGRATE |
|
PARSE_VALEXPR_NO_MIGRATE))
|
||||||
PARSE_VALEXPR_NO_ASSIGN))
|
|
||||||
throw new parse_error
|
throw new parse_error
|
||||||
("A transaction's cost must evaluate to a constant value");
|
("A transaction's cost must evaluate to a constant value");
|
||||||
|
|
||||||
|
|
@ -291,80 +288,6 @@ transaction_t * parse_transaction(char * line, account_t * account,
|
||||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
||||||
"Reduced amount is " << xact->amount);
|
"Reduced amount is " << xact->amount);
|
||||||
|
|
||||||
parse_assign:
|
|
||||||
if (entry != NULL) {
|
|
||||||
// Add this amount to the related account now
|
|
||||||
|
|
||||||
account_xdata_t& xdata(account_xdata(*xact->account));
|
|
||||||
|
|
||||||
if (xact->amount) {
|
|
||||||
xdata.value += xact->amount;
|
|
||||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
|
||||||
"XACT assign: account total = " << xdata.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the optional assigned (= AMOUNT)
|
|
||||||
|
|
||||||
if (in.good() && ! in.eof()) {
|
|
||||||
p = peek_next_nonws(in);
|
|
||||||
if (p == '=') {
|
|
||||||
in.get(p);
|
|
||||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
|
||||||
"Found a balance assignment indicator");
|
|
||||||
if (in.good() && ! in.eof()) {
|
|
||||||
amount_t amt;
|
|
||||||
|
|
||||||
try {
|
|
||||||
unsigned long beg = (long)in.tellg();
|
|
||||||
|
|
||||||
if (parse_amount_expr(in, amt, xact.get(),
|
|
||||||
PARSE_VALEXPR_NO_MIGRATE))
|
|
||||||
throw new parse_error
|
|
||||||
("An assigned balance must evaluate to a constant value");
|
|
||||||
|
|
||||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
|
||||||
"XACT assign: parsed amt = " << amt);
|
|
||||||
|
|
||||||
unsigned long end = (long)in.tellg();
|
|
||||||
|
|
||||||
amount_t diff;
|
|
||||||
if (xdata.value.type == value_t::AMOUNT)
|
|
||||||
diff = amt - *((amount_t *) xdata.value.data);
|
|
||||||
else if (xdata.value.type == value_t::BALANCE)
|
|
||||||
diff = amt - ((balance_t *) xdata.value.data)->amount(amt.commodity());
|
|
||||||
else if (xdata.value.type == value_t::BALANCE_PAIR)
|
|
||||||
diff = amt - ((balance_pair_t *) xdata.value.data)->quantity.amount(amt.commodity());
|
|
||||||
else
|
|
||||||
diff = amt;
|
|
||||||
|
|
||||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
|
||||||
"XACT assign: diff = " << diff);
|
|
||||||
|
|
||||||
if (! diff.realzero()) {
|
|
||||||
if (xact->amount) {
|
|
||||||
transaction_t * temp
|
|
||||||
= new transaction_t(xact->account, diff, TRANSACTION_CALCULATED);
|
|
||||||
entry->add_transaction(temp);
|
|
||||||
|
|
||||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
|
||||||
"Created balancing transaction");
|
|
||||||
} else {
|
|
||||||
xact->amount = diff;
|
|
||||||
DEBUG_PRINT("ledger.textual.parse", "line " << linenum << ": " <<
|
|
||||||
"Overwrite null transaction");
|
|
||||||
}
|
|
||||||
xdata.value = amt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (error * err) {
|
|
||||||
err_desc = "While parsing assigned balance:";
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the optional note
|
// Parse the optional note
|
||||||
|
|
||||||
parse_note:
|
parse_note:
|
||||||
|
|
|
||||||
13
valexpr.cc
13
valexpr.cc
|
|
@ -833,8 +833,7 @@ value_expr_t * parse_value_term(std::istream& in, scope_t * scope,
|
||||||
bool definition = false;
|
bool definition = false;
|
||||||
if (c == '=') {
|
if (c == '=') {
|
||||||
in.get(c);
|
in.get(c);
|
||||||
if ((flags & PARSE_VALEXPR_NO_ASSIGN) ||
|
if (peek_next_nonws(in) == '=') {
|
||||||
peek_next_nonws(in) == '=') {
|
|
||||||
in.unget();
|
in.unget();
|
||||||
c = '\0';
|
c = '\0';
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1161,16 +1160,10 @@ value_expr_t * parse_logic_expr(std::istream& in, scope_t * scope,
|
||||||
case '!':
|
case '!':
|
||||||
case '=': {
|
case '=': {
|
||||||
bool negate = c == '!';
|
bool negate = c == '!';
|
||||||
if (! negate && (flags & PARSE_VALEXPR_NO_ASSIGN)) {
|
if ((c = peek_next_nonws(in)) == '=')
|
||||||
in.unget();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if ((c = peek_next_nonws(in)) == '=') {
|
|
||||||
in.get(c);
|
in.get(c);
|
||||||
}
|
else
|
||||||
else {
|
|
||||||
unexpected(c, '=');
|
unexpected(c, '=');
|
||||||
}
|
|
||||||
value_expr prev(node.release());
|
value_expr prev(node.release());
|
||||||
node.reset(new value_expr_t(negate ? value_expr_t::O_NEQ :
|
node.reset(new value_expr_t(negate ? value_expr_t::O_NEQ :
|
||||||
value_expr_t::O_EQ));
|
value_expr_t::O_EQ));
|
||||||
|
|
|
||||||
|
|
@ -280,7 +280,6 @@ bool compute_amount(value_expr_t * expr, amount_t& amt,
|
||||||
#define PARSE_VALEXPR_RELAXED 0x02
|
#define PARSE_VALEXPR_RELAXED 0x02
|
||||||
#define PARSE_VALEXPR_NO_MIGRATE 0x04
|
#define PARSE_VALEXPR_NO_MIGRATE 0x04
|
||||||
#define PARSE_VALEXPR_NO_REDUCE 0x08
|
#define PARSE_VALEXPR_NO_REDUCE 0x08
|
||||||
#define PARSE_VALEXPR_NO_ASSIGN 0x10
|
|
||||||
|
|
||||||
value_expr_t * parse_value_expr(std::istream& in,
|
value_expr_t * parse_value_expr(std::istream& in,
|
||||||
scope_t * scope = NULL,
|
scope_t * scope = NULL,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue