summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/ledger3.texi39
-rw-r--r--src/textual.cc50
-rw-r--r--test/regress/1187_5.test36
3 files changed, 109 insertions, 16 deletions
diff --git a/doc/ledger3.texi b/doc/ledger3.texi
index a094d977..45afe1ae 100644
--- a/doc/ledger3.texi
+++ b/doc/ledger3.texi
@@ -3230,6 +3230,45 @@ A balance assertion has this general form:
This simply asserts that after subtracting $20.00 from Assets:Cash,
that the resulting total matches $500.00. If not, it is an error.
+The assertion has an effect only on the specified commodity. If an account has
+multiple commodities, then only the one asserted is verified:
+
+@smallexample
+2012-03-10 KFC New York
+ Expenses:Food $20.00
+ Assets:Cash $-20.00 = $500.00
+
+2012-03-11 KFC Montreal
+ Expenses:Food 15.00 CAD
+ Assets:Cash -15.00 CAD = $500.00
+@end smallexample
+
+In this case, the amount in USD of cash (which has not changed) is validated.
+Nothing is asserted about the current amount of Canadian dollars in @samp{Asset:Cash}.
+
+@subsubsection Special assertion value 0
+
+The only value that can be asserted without a commodity is @samp{0}.
+This results in a cross-commodities assertion, which makes it possible to
+assert that an account is totally empty.
+
+@smallexample
+2012-03-09 Fill Wallet
+ Revenue $20.00
+ Revenue 15.00 CAD
+ Assets:Cash
+
+2012-03-10 KFC New York
+ Expenses:Food $20.00
+ Assets:Cash $-20.00
+
+2012-03-11 KFC Montreal
+ Expenses:Food 15.00 CAD
+ Assets:Cash -15.00 CAD = 0
+@end smallexample
+
+The last transaction will assert that we are out of cash of any sort.
+
@node Balance assignments, Resetting a balance, Balance assertions, Balance verification
@subsection Balance assignments
diff --git a/src/textual.cc b/src/textual.cc
index 8fbc5c08..246db751 100644
--- a/src/textual.cc
+++ b/src/textual.cc
@@ -1644,29 +1644,30 @@ post_t * instance_t::parse_post(char * line,
}
DEBUG("textual.parse", "line " << context.linenum << ": "
- << "POST assign: parsed amt = " << *post->assigned_amount);
+ << "POST assign: parsed balance amount = " << *post->assigned_amount);
- amount_t& amt(*post->assigned_amount);
+ const amount_t& amt(*post->assigned_amount);
value_t account_total
(post->account->amount().strip_annotations(keep_details_t()));
DEBUG("post.assign", "line " << context.linenum << ": "
<< "account balance = " << account_total);
- DEBUG("post.assign",
- "line " << context.linenum << ": " << "post amount = " << amt);
+ DEBUG("post.assign", "line " << context.linenum << ": "
+ << "post amount = " << amt << " (is_zero = " << amt.is_zero() << ")");
- amount_t diff = amt;
+ balance_t diff = amt;
switch (account_total.type()) {
case value_t::AMOUNT:
- if (account_total.as_amount().commodity_ptr() == diff.commodity_ptr())
- diff -= account_total.as_amount();
+ diff -= account_total.as_amount();
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Subtracting amount " << account_total.as_amount() << " from diff, yielding " << diff);
break;
case value_t::BALANCE:
- if (optional<amount_t> comm_bal =
- account_total.as_balance().commodity_amount(amt.commodity()))
- diff -= *comm_bal;
+ diff -= account_total.as_balance();
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Subtracting balance " << account_total.as_balance() << " from diff, yielding " << diff);
break;
default:
@@ -1680,18 +1681,34 @@ post_t * instance_t::parse_post(char * line,
// Subtract amounts from previous posts to this account in the xact.
for (post_t* p : xact->posts) {
- if (p->account == post->account &&
- p->amount.commodity_ptr() == diff.commodity_ptr()) {
+ if (p->account == post->account) {
diff -= p->amount;
DEBUG("textual.parse", "line " << context.linenum << ": "
- << "Subtract " << p->amount << ", diff = " << diff);
+ << "Subtracting " << p->amount << ", diff = " << diff);
+ }
+ }
+
+ // If amt has a commodity, restrict balancing to that. Otherwise, it's the blanket '0' and
+ // check that all of them are zero.
+ if (amt.has_commodity()) {
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Finding commodity " << amt.commodity() << " (" << amt << ") in balance " << diff);
+ optional<amount_t> wanted_commodity = diff.commodity_amount(amt.commodity());
+ if (!wanted_commodity) {
+ diff = amt - amt; // this is '0' with the correct commodity.
+ } else {
+ diff = *wanted_commodity;
}
+ DEBUG("textual.parse", "line " << context.linenum << ": "
+ << "Diff is now " << diff);
}
if (post->amount.is_null()) {
// balance assignment
if (! diff.is_zero()) {
- post->amount = diff;
+ // This will fail if there are more than 1 commodity in diff, which is wanted,
+ // as amount cannot store more than 1 commodity.
+ post->amount = diff.to_amount();
DEBUG("textual.parse", "line " << context.linenum << ": "
<< "Overwrite null posting");
}
@@ -1699,10 +1716,11 @@ post_t * instance_t::parse_post(char * line,
// balance assertion
diff -= post->amount;
if (! no_assertions && ! diff.is_zero()) {
- amount_t tot = amt - diff;
+ balance_t tot = -diff + amt;
+ DEBUG("textual.parse", "Balance assertion: off by " << diff << " (expected to see " << tot << ")");
throw_(parse_error,
_f("Balance assertion off by %1% (expected to see %2%)")
- % diff % tot);
+ % diff.to_string() % tot.to_string());
}
}
diff --git a/test/regress/1187_5.test b/test/regress/1187_5.test
new file mode 100644
index 00000000..4aa5fdd8
--- /dev/null
+++ b/test/regress/1187_5.test
@@ -0,0 +1,36 @@
+2013/12/01 * Initial State
+ Crédit:Viseca:MasterCard P1 -618.50 CHF
+ Crédit:Viseca:MasterCard P2 -52.10 CHF
+ Equity:Opening Balances
+
+2013/12/15 * Buy Some Chocolate
+ Dépenses:Nourriture 19.00 EUR ; #1
+ Crédit:Viseca:MasterCard P1
+
+2013/12/15 * Buy Some Chocolate
+ Crédit:Viseca:MasterCard P1 18.00 EUR ; #2
+ Recettes:Erreurs
+
+2013/12/23 * Facture Viseca
+ Crédit:Viseca:MasterCard P2 52.10 CHF = 0 ; #3
+ Crédit:Viseca:MasterCard P1 618.50 CHF = 0 CHF ; #4
+ Dépenses:Frais:Gestion Comptes 1.50 CHF
+ Crédit:Viseca -672.10 CHF
+
+2014/01/03 * Facture Viseca
+ Crédit:Viseca 672.10 CHF = 0
+ Actif:Comptes:CP courant
+
+test bal
+ -672.10 CHF Actif:Comptes:CP courant
+ -1.00 EUR Crédit:Viseca
+ -1.00 EUR MasterCard P1
+ 1.50 CHF
+ 19.00 EUR Dépenses
+ 1.50 CHF Frais:Gestion Comptes
+ 19.00 EUR Nourriture
+ 670.60 CHF Equity:Opening Balances
+ -18.00 EUR Recettes:Erreurs
+--------------------
+ 0
+end test