From df187e70b765f26e7d6d3aebe96ef7f3db5743f9 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 23 Oct 2019 00:11:30 +0800 Subject: [PATCH 1/5] [balsheet-eg] remove dead code and 2-arg record-constructor is deprecated in new-guile --- gnucash/report/business-reports/balsheet-eg.scm | 9 --------- 1 file changed, 9 deletions(-) diff --git a/gnucash/report/business-reports/balsheet-eg.scm b/gnucash/report/business-reports/balsheet-eg.scm index cad204b95b..ed7d6c8026 100644 --- a/gnucash/report/business-reports/balsheet-eg.scm +++ b/gnucash/report/business-reports/balsheet-eg.scm @@ -125,15 +125,6 @@ sublist) accrec-printer)) (define newaccrec-full (record-constructor accrectype)) ; requires all the fields -(define newaccrec-empty (record-constructor accrectype '())) ; all fields default to #f -(define newaccrec (record-constructor accrectype '(account ; most-likely-to-be-needed fields - code - placeholder? - namelink - commodity - balance-num - depth - treedepth))) (define (newaccrec-clean) ;; Create a new accrec with 'clean' empty values, e.g. strings are "", not #f (newaccrec-full #f ; account From d881130a194a4d14bc1036a23408e51161720ec9 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 23 Oct 2019 19:27:20 +0800 Subject: [PATCH 2/5] [standard-reports] convert to srfi-9 because 2-arg record-constructor is being deprecated. http://git.savannah.gnu.org/cgit/guile.git/commit/?id=f7b4055b --- .../standard-reports/standard-reports.scm | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/gnucash/report/standard-reports/standard-reports.scm b/gnucash/report/standard-reports/standard-reports.scm index 235f763c83..b656e300d3 100644 --- a/gnucash/report/standard-reports/standard-reports.scm +++ b/gnucash/report/standard-reports/standard-reports.scm @@ -25,6 +25,7 @@ (define-module (gnucash report standard-reports)) +(use-modules (srfi srfi-9)) (use-modules (srfi srfi-13)) (use-modules (gnucash utilities)) (use-modules (gnucash core-utils)) @@ -39,26 +40,15 @@ ;; or without split. If no function is found, then run the 'default' ;; function -(define acct-type-info (make-record-type "AcctTypeInfo" '(split non-split))) - -(define make-acct-type-private - (record-constructor acct-type-info '(split non-split))) +(define-record-type :acct-type-info + (make-acct-type-private split non-split) + acct-type? + (split get-split set-split) + (non-split get-non-split set-non-split)) (define (make-acct-type) (make-acct-type-private #f #f)) -(define get-split - (record-accessor acct-type-info 'split)) - -(define set-split - (record-modifier acct-type-info 'split)) - -(define get-non-split - (record-accessor acct-type-info 'non-split)) - -(define set-non-split - (record-modifier acct-type-info 'non-split)) - (define (gnc:register-report-hook acct-type split? create-fcn) (let ((type-info (hash-ref gnc:*register-report-hash* acct-type (make-acct-type)))) (if split? From 198570d8c8f770d215cdc304709fba3a93dd9b06 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 4 Sep 2019 21:11:10 +0800 Subject: [PATCH 3/5] Bug 797390 - xaccAccountRecomputeBalance also tallies no-closing balances Add a few API, enough for fixing bug 797326 easily. --- libgnucash/engine/Account.cpp | 127 ++++++++++++++++++++++++++++++++-- libgnucash/engine/Account.h | 8 +++ libgnucash/engine/AccountP.h | 2 + libgnucash/engine/Split.c | 13 ++++ libgnucash/engine/Split.h | 9 +++ libgnucash/engine/SplitP.h | 1 + 6 files changed, 156 insertions(+), 4 deletions(-) diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp index 6c9f27daeb..fcacf18623 100644 --- a/libgnucash/engine/Account.cpp +++ b/libgnucash/engine/Account.cpp @@ -63,6 +63,8 @@ static const std::string AB_ACCOUNT_UID("account-uid"); static const std::string AB_BANK_CODE("bank-code"); static const std::string AB_TRANS_RETRIEVAL("trans-retrieval"); +static gnc_numeric GetBalanceAsOfDate (Account *acc, time64 date, gboolean ignclosing); + using FinalProbabilityVec=std::vector>; using ProbabilityVec=std::vector>; using FlatKvpEntry=std::pair; @@ -88,6 +90,7 @@ enum PROP_COMMODITY_SCU, /* Table */ PROP_NON_STD_SCU, /* Table */ PROP_END_BALANCE, /* Constructed */ + PROP_END_NOCLOSING_BALANCE, /* Constructed */ PROP_END_CLEARED_BALANCE, /* Constructed */ PROP_END_RECONCILED_BALANCE, /* Constructed */ @@ -116,6 +119,7 @@ enum PROP_SORT_DIRTY, /* Runtime Value */ PROP_BALANCE_DIRTY, /* Runtime Value */ PROP_START_BALANCE, /* Runtime Value */ + PROP_START_NOCLOSING_BALANCE, /* Runtime Value */ PROP_START_CLEARED_BALANCE, /* Runtime Value */ PROP_START_RECONCILED_BALANCE, /* Runtime Value */ }; @@ -279,9 +283,11 @@ gnc_account_init(Account* acc) priv->non_standard_scu = FALSE; priv->balance = gnc_numeric_zero(); + priv->noclosing_balance = gnc_numeric_zero(); priv->cleared_balance = gnc_numeric_zero(); priv->reconciled_balance = gnc_numeric_zero(); priv->starting_balance = gnc_numeric_zero(); + priv->starting_noclosing_balance = gnc_numeric_zero(); priv->starting_cleared_balance = gnc_numeric_zero(); priv->starting_reconciled_balance = gnc_numeric_zero(); priv->balance_dirty = FALSE; @@ -364,6 +370,9 @@ gnc_account_get_property (GObject *object, case PROP_START_BALANCE: g_value_set_boxed(value, &priv->starting_balance); break; + case PROP_START_NOCLOSING_BALANCE: + g_value_set_boxed(value, &priv->starting_noclosing_balance); + break; case PROP_START_CLEARED_BALANCE: g_value_set_boxed(value, &priv->starting_cleared_balance); break; @@ -373,6 +382,9 @@ gnc_account_get_property (GObject *object, case PROP_END_BALANCE: g_value_set_boxed(value, &priv->balance); break; + case PROP_END_NOCLOSING_BALANCE: + g_value_set_boxed(value, &priv->noclosing_balance); + break; case PROP_END_CLEARED_BALANCE: g_value_set_boxed(value, &priv->cleared_balance); break; @@ -746,6 +758,23 @@ gnc_account_class_init (AccountClass *klass) GNC_TYPE_NUMERIC, static_cast(G_PARAM_READWRITE))); + g_object_class_install_property + (gobject_class, + PROP_START_NOCLOSING_BALANCE, + g_param_spec_boxed("start-noclosing-balance", + "Starting No-closing Balance", + "The starting balance for the account, ignoring closing." + "This parameter is intended for use with backends " + "that do not return the complete list of splits " + "for an account, but rather return a partial " + "list. In such a case, the backend will " + "typically return all of the splits after " + "some certain date, and the 'starting noclosing " + "balance' will represent the summation of the " + "splits up to that date, ignoring closing splits.", + GNC_TYPE_NUMERIC, + static_cast(G_PARAM_READWRITE))); + g_object_class_install_property (gobject_class, PROP_START_CLEARED_BALANCE, @@ -791,6 +820,18 @@ gnc_account_class_init (AccountClass *klass) GNC_TYPE_NUMERIC, G_PARAM_READABLE)); + g_object_class_install_property + (gobject_class, + PROP_END_NOCLOSING_BALANCE, + g_param_spec_boxed("end-noclosing-balance", + "Ending Account Noclosing Balance", + "This is the current ending no-closing balance for " + "the account. It is computed from the sum of the " + "starting balance and all cleared splits in the " + "account.", + GNC_TYPE_NUMERIC, + G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, PROP_END_CLEARED_BALANCE, @@ -1269,6 +1310,7 @@ xaccFreeAccount (Account *acc) priv->children = nullptr; priv->balance = gnc_numeric_zero(); + priv->noclosing_balance = gnc_numeric_zero(); priv->cleared_balance = gnc_numeric_zero(); priv->reconciled_balance = gnc_numeric_zero(); @@ -1565,6 +1607,22 @@ xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids) return FALSE; } + if (!gnc_numeric_equal(priv_aa->starting_noclosing_balance, + priv_ab->starting_noclosing_balance)) + { + char *str_a; + char *str_b; + + str_a = gnc_numeric_to_string(priv_aa->starting_noclosing_balance); + str_b = gnc_numeric_to_string(priv_ab->starting_noclosing_balance); + + PWARN ("starting noclosing balances differ: %s vs %s", str_a, str_b); + + g_free (str_a); + g_free (str_b); + + return FALSE; + } if (!gnc_numeric_equal(priv_aa->starting_cleared_balance, priv_ab->starting_cleared_balance)) { @@ -1615,6 +1673,21 @@ xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids) return FALSE; } + if (!gnc_numeric_equal(priv_aa->noclosing_balance, priv_ab->noclosing_balance)) + { + char *str_a; + char *str_b; + + str_a = gnc_numeric_to_string(priv_aa->noclosing_balance); + str_b = gnc_numeric_to_string(priv_ab->noclosing_balance); + + PWARN ("noclosing balances differ: %s vs %s", str_a, str_b); + + g_free (str_a); + g_free (str_b); + + return FALSE; + } if (!gnc_numeric_equal(priv_aa->cleared_balance, priv_ab->cleared_balance)) { char *str_a; @@ -2070,6 +2143,7 @@ xaccAccountRecomputeBalance (Account * acc) { AccountPrivate *priv; gnc_numeric balance; + gnc_numeric noclosing_balance; gnc_numeric cleared_balance; gnc_numeric reconciled_balance; GList *lp; @@ -2083,6 +2157,7 @@ xaccAccountRecomputeBalance (Account * acc) if (qof_book_shutting_down(qof_instance_get_book(acc))) return; balance = priv->starting_balance; + noclosing_balance = priv->starting_noclosing_balance; cleared_balance = priv->starting_cleared_balance; reconciled_balance = priv->starting_reconciled_balance; @@ -2107,13 +2182,18 @@ xaccAccountRecomputeBalance (Account * acc) gnc_numeric_add_fixed(reconciled_balance, amt); } + if (!(xaccTransGetIsClosingTxn (split->parent))) + noclosing_balance = gnc_numeric_add_fixed(noclosing_balance, amt); + split->balance = balance; + split->noclosing_balance = noclosing_balance; split->cleared_balance = cleared_balance; split->reconciled_balance = reconciled_balance; } priv->balance = balance; + priv->noclosing_balance = noclosing_balance; priv->cleared_balance = cleared_balance; priv->reconciled_balance = reconciled_balance; priv->balance_dirty = FALSE; @@ -3287,8 +3367,8 @@ xaccAccountGetProjectedMinimumBalance (const Account *acc) /********************************************************************\ \********************************************************************/ -gnc_numeric -xaccAccountGetBalanceAsOfDate (Account *acc, time64 date) +static gnc_numeric +GetBalanceAsOfDate (Account *acc, time64 date, gboolean ignclosing) { /* Ideally this could use xaccAccountForEachSplit, but * it doesn't exist yet and I'm uncertain of exactly how @@ -3307,7 +3387,10 @@ xaccAccountGetBalanceAsOfDate (Account *acc, time64 date) xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */ priv = GET_PRIVATE(acc); - balance = priv->balance; + if (ignclosing) + balance = priv->noclosing_balance; + else + balance = priv->balance; lp = priv->splits; while ( lp && !found ) @@ -3326,7 +3409,10 @@ xaccAccountGetBalanceAsOfDate (Account *acc, time64 date) /* Since lp is now pointing to a split which was past the reconcile * date, get the running balance of the previous split. */ - balance = xaccSplitGetBalance( (Split *)lp->prev->data ); + if (ignclosing) + balance = xaccSplitGetNoclosingBalance( (Split *)lp->prev->data ); + else + balance = xaccSplitGetBalance( (Split *)lp->prev->data ); } else { @@ -3342,6 +3428,18 @@ xaccAccountGetBalanceAsOfDate (Account *acc, time64 date) return( balance ); } +gnc_numeric +xaccAccountGetBalanceAsOfDate (Account *acc, time64 date) +{ + return GetBalanceAsOfDate (acc, date, FALSE); +} + +static gnc_numeric +xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date) +{ + return GetBalanceAsOfDate (acc, date, TRUE); +} + /* * Originally gsr_account_present_balance in gnc-split-reg.c * @@ -3669,6 +3767,16 @@ xaccAccountGetBalanceAsOfDateInCurrency( include_children); } +gnc_numeric +xaccAccountGetNoclosingBalanceAsOfDateInCurrency( + Account *acc, time64 date, gnc_commodity *report_commodity, + gboolean include_children) +{ + return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive + (acc, date, xaccAccountGetNoclosingBalanceAsOfDate, + report_commodity, include_children); +} + gnc_numeric xaccAccountGetBalanceChangeForPeriod (Account *acc, time64 t1, time64 t2, gboolean recurse) @@ -3680,6 +3788,17 @@ xaccAccountGetBalanceChangeForPeriod (Account *acc, time64 t1, time64 t2, return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED); } +gnc_numeric +xaccAccountGetNoclosingBalanceChangeForPeriod (Account *acc, time64 t1, + time64 t2, gboolean recurse) +{ + gnc_numeric b1, b2; + + b1 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t1, NULL, recurse); + b2 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t2, NULL, recurse); + return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED); +} + /********************************************************************\ \********************************************************************/ diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h index 758543eb57..92ee17ab53 100644 --- a/libgnucash/engine/Account.h +++ b/libgnucash/engine/Account.h @@ -595,12 +595,19 @@ gnc_numeric xaccAccountGetProjectedMinimumBalanceInCurrency ( const Account *account, const gnc_commodity *report_commodity, gboolean include_children); +/* This function gets the balance as of the given date, ignoring + closing entries, in the desired commodity. */ +gnc_numeric xaccAccountGetNoclosingBalanceAsOfDateInCurrency( + Account *acc, time64 date, gnc_commodity *report_commodity, + gboolean include_children); /* This function gets the balance as of the given date in the desired commodity. */ gnc_numeric xaccAccountGetBalanceAsOfDateInCurrency( Account *account, time64 date, gnc_commodity *report_commodity, gboolean include_children); +gnc_numeric xaccAccountGetNoclosingBalanceChangeForPeriod ( + Account *acc, time64 date1, time64 date2, gboolean recurse); gnc_numeric xaccAccountGetBalanceChangeForPeriod ( Account *acc, time64 date1, time64 date2, gboolean recurse); @@ -1504,6 +1511,7 @@ const char * dxaccAccountGetQuoteTZ (const Account *account); #define ACCOUNT_SORT_REVERSED_ "sort-reversed" #define ACCOUNT_NOTES_ "notes" #define ACCOUNT_BALANCE_ "balance" +#define ACCOUNT_NOCLOSING_ "noclosing" #define ACCOUNT_CLEARED_ "cleared" #define ACCOUNT_RECONCILED_ "reconciled" #define ACCOUNT_PRESENT_ "present" diff --git a/libgnucash/engine/AccountP.h b/libgnucash/engine/AccountP.h index a1ab6a0747..995bc4cdc3 100644 --- a/libgnucash/engine/AccountP.h +++ b/libgnucash/engine/AccountP.h @@ -104,11 +104,13 @@ typedef struct AccountPrivate /* protected data - should only be set by backends */ gnc_numeric starting_balance; + gnc_numeric starting_noclosing_balance; gnc_numeric starting_cleared_balance; gnc_numeric starting_reconciled_balance; /* cached parameters */ gnc_numeric balance; + gnc_numeric noclosing_balance; gnc_numeric cleared_balance; gnc_numeric reconciled_balance; diff --git a/libgnucash/engine/Split.c b/libgnucash/engine/Split.c index 335db848cc..4f74b37094 100644 --- a/libgnucash/engine/Split.c +++ b/libgnucash/engine/Split.c @@ -120,6 +120,7 @@ gnc_split_init(Split* split) split->balance = gnc_numeric_zero(); split->cleared_balance = gnc_numeric_zero(); split->reconciled_balance = gnc_numeric_zero(); + split->noclosing_balance = gnc_numeric_zero(); split->gains = GAINS_STATUS_UNKNOWN; split->gains_split = NULL; @@ -513,6 +514,7 @@ xaccSplitReinit(Split * split) split->balance = gnc_numeric_zero(); split->cleared_balance = gnc_numeric_zero(); split->reconciled_balance = gnc_numeric_zero(); + split->noclosing_balance = gnc_numeric_zero(); qof_instance_set_idata(split, 0); @@ -597,6 +599,7 @@ xaccSplitCloneNoKvp (const Split *s) split->balance = s->balance; split->cleared_balance = s->cleared_balance; split->reconciled_balance = s->reconciled_balance; + split->noclosing_balance = s->noclosing_balance; split->gains = GAINS_STATUS_UNKNOWN; split->gains_split = NULL; @@ -679,6 +682,7 @@ xaccSplitDump (const Split *split, const char *tag) printf(" CBalance: %s\n", gnc_numeric_to_string(split->cleared_balance)); printf(" RBalance: %s\n", gnc_numeric_to_string(split->reconciled_balance)); + printf(" NoClose: %s\n", gnc_numeric_to_string(split->noclosing_balance)); printf(" idata: %x\n", qof_instance_get_idata(split)); } #endif @@ -868,6 +872,9 @@ xaccSplitEqual(const Split *sa, const Split *sb, if (!xaccSplitEqualCheckBal ("reconciled ", sa->reconciled_balance, sb->reconciled_balance)) return FALSE; + if (!xaccSplitEqualCheckBal ("noclosing ", sa->noclosing_balance, + sb->noclosing_balance)) + return FALSE; } if (!xaccTransEqual(sa->parent, sb->parent, check_guids, check_txn_splits, @@ -1272,6 +1279,12 @@ xaccSplitGetBalance (const Split *s) return s ? s->balance : gnc_numeric_zero(); } +gnc_numeric +xaccSplitGetNoclosingBalance (const Split *s) +{ + return s ? s->noclosing_balance : gnc_numeric_zero(); +} + gnc_numeric xaccSplitGetClearedBalance (const Split *s) { diff --git a/libgnucash/engine/Split.h b/libgnucash/engine/Split.h index 963413e420..7f2e2025f8 100644 --- a/libgnucash/engine/Split.h +++ b/libgnucash/engine/Split.h @@ -296,6 +296,15 @@ gnc_numeric xaccSplitGetBaseValue (const Split *split, */ gnc_numeric xaccSplitGetBalance (const Split *split); +/** + * The noclosing-balance is the currency-denominated balance of all + * transactions except 'closing' transactions. It is correctly + * adjusted for price fluctuations. + * + * Returns the running balance up to & including the indicated split. + */ +gnc_numeric xaccSplitGetNoclosingBalance (const Split *split); + /** * The cleared-balance is the currency-denominated balance * of all transactions that have been marked as cleared or reconciled. diff --git a/libgnucash/engine/SplitP.h b/libgnucash/engine/SplitP.h index 9815d4128b..8e6f41bb6f 100644 --- a/libgnucash/engine/SplitP.h +++ b/libgnucash/engine/SplitP.h @@ -123,6 +123,7 @@ struct split_s * These balances apply to a sorting order by date posted * (not by date entered). */ gnc_numeric balance; + gnc_numeric noclosing_balance; gnc_numeric cleared_balance; gnc_numeric reconciled_balance; }; From 8b8c957ed2c6e37380416215468f48e11d8c0803 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Wed, 4 Sep 2019 21:40:23 +0800 Subject: [PATCH 4/5] Bug 797326 - Enhancement: budget's Estimate tool should ignore Closing Entries addition to estimate-budget tool to optionally ignore closing entries using brand new API --- gnucash/gnome/gnc-plugin-page-budget.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c index 3dba5d39b0..ca77b48937 100644 --- a/gnucash/gnome/gnc-plugin-page-budget.c +++ b/gnucash/gnome/gnc-plugin-page-budget.c @@ -892,9 +892,10 @@ estimate_budget_helper(GtkTreeModel *model, GtkTreePath *path, if (priv->useAvg && num_periods) { - num = xaccAccountGetBalanceChangeForPeriod(acct, - recurrenceGetPeriodTime(&priv->r, 0, FALSE), - recurrenceGetPeriodTime(&priv->r, num_periods - 1, TRUE), TRUE); + num = xaccAccountGetNoclosingBalanceChangeForPeriod + (acct, recurrenceGetPeriodTime(&priv->r, 0, FALSE), + recurrenceGetPeriodTime(&priv->r, num_periods - 1, TRUE), TRUE); + num = gnc_numeric_div(num, gnc_numeric_create(num_periods, 1), GNC_DENOM_AUTO, @@ -913,7 +914,10 @@ estimate_budget_helper(GtkTreeModel *model, GtkTreePath *path, { for (i = 0; i < num_periods; i++) { - num = recurrenceGetAccountPeriodValue(&priv->r, acct, i); + num = xaccAccountGetNoclosingBalanceChangeForPeriod + (acct, recurrenceGetPeriodTime(&priv->r, i, FALSE), + recurrenceGetPeriodTime(&priv->r, i, TRUE), TRUE); + if (!gnc_numeric_check(num)) { if (gnc_reverse_balance(acct)) From 1dc22e53c53108f02bc6d1c9e41d32e1dce78617 Mon Sep 17 00:00:00 2001 From: John Ralls Date: Tue, 22 Oct 2019 15:00:23 -0700 Subject: [PATCH 5/5] Fix test_xaccTransEqual. Needed to make the noclosing_balances equal to get the balance-equality test to pass. --- libgnucash/engine/test/utest-Transaction.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libgnucash/engine/test/utest-Transaction.cpp b/libgnucash/engine/test/utest-Transaction.cpp index 21d830e5a5..21b512282f 100644 --- a/libgnucash/engine/test/utest-Transaction.cpp +++ b/libgnucash/engine/test/utest-Transaction.cpp @@ -936,6 +936,8 @@ test_xaccTransEqual (Fixture *fixture, gconstpointer pData) split10->balance = split00->balance; split11->balance = split01->balance; + split10->noclosing_balance = split00->noclosing_balance; + split11->noclosing_balance = split01->noclosing_balance; g_assert (xaccTransEqual (txn1, txn0, TRUE, TRUE, TRUE, TRUE)); } g_free (check3->msg);