From 67ab9e90d264979d46a3426100160fbcbcf5ae1f Mon Sep 17 00:00:00 2001 From: luzpaz Date: Wed, 25 Jan 2023 15:07:39 +0000 Subject: [PATCH 01/10] Fix some typos --- .../example_scripts/rest-api/gnucash_rest.py | 2 +- .../example_scripts/simple_invoice_insert.py | 2 +- doc/examples/customers_import.csv | 2 +- libgnucash/app-utils/calculation/fin.c | 16 ++++++++-------- libgnucash/app-utils/options.scm | 2 +- libgnucash/doc/constraints.txt | 2 +- libgnucash/engine/qofinstance.cpp | 2 +- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bindings/python/example_scripts/rest-api/gnucash_rest.py b/bindings/python/example_scripts/rest-api/gnucash_rest.py index e17e77d6c3..f28c0e8315 100644 --- a/bindings/python/example_scripts/rest-api/gnucash_rest.py +++ b/bindings/python/example_scripts/rest-api/gnucash_rest.py @@ -1826,7 +1826,7 @@ def gnc_numeric_from_decimal(decimal_value): TEN = int(Decimal(0).radix()) # this is always 10 numerator_place_value = 1 # add each digit to the final value multiplied by the place value - # from least significant to most sigificant + # from least significant to most significant for i in range(len(digits)-1,-1,-1): numerator += digits[i] * numerator_place_value numerator_place_value *= TEN diff --git a/bindings/python/example_scripts/simple_invoice_insert.py b/bindings/python/example_scripts/simple_invoice_insert.py index 391b05c6d9..89c9c93d7a 100644 --- a/bindings/python/example_scripts/simple_invoice_insert.py +++ b/bindings/python/example_scripts/simple_invoice_insert.py @@ -66,7 +66,7 @@ def gnc_numeric_from_decimal(decimal_value): TEN = int(Decimal(0).radix()) # this is always 10 numerator_place_value = 1 # add each digit to the final value multiplied by the place value - # from least significant to most sigificant + # from least significant to most significant for i in range(len(digits)-1,-1,-1): numerator += digits[i] * numerator_place_value numerator_place_value *= TEN diff --git a/doc/examples/customers_import.csv b/doc/examples/customers_import.csv index 599aadc6e3..6e9ae4baca 100644 --- a/doc/examples/customers_import.csv +++ b/doc/examples/customers_import.csv @@ -8,7 +8,7 @@ id,company,name,addr1,addr2,addr3,addr4,phone,fax,email,notes,shipname,shipaddr1 #company with the same ID will be UPDATED. This may not be what you want! 000099,Average Company,Accounts Dept,50 Poor Avenue,,,,,,,,,,,,,,, ,Academy,Academy,Some Street,,,,555-237-6959,,,,,,,,,,shipmail -,company,name,addr1,addr2,addr3,addr4,phone,fax,emai,lnotes,shipname,shipaddr1,shipaddr2,shipaddr3,shipaddr4,shipphone,shipfax,shipemail +,company,name,addr1,addr2,addr3,addr4,phone,fax,email,notes,shipname,shipaddr1,shipaddr2,shipaddr3,shipaddr4,shipphone,shipfax,shipemail ,No Address Company,Accounts,,,,,,,,,,,,,,,, #Just another example after a blank line diff --git a/libgnucash/app-utils/calculation/fin.c b/libgnucash/app-utils/calculation/fin.c index 3c04118fb8..bc011b20e3 100644 --- a/libgnucash/app-utils/calculation/fin.c +++ b/libgnucash/app-utils/calculation/fin.c @@ -103,7 +103,7 @@ * interest only loan), or large enough to fully repay both the interest and * principal during the term of the loan (a fully amoritized loan). Many loans * fall somewhere between, with payments that do not fully cover repayment of - * both the principal and interst. These loans require a larger final payment + * both the principal and interest. These loans require a larger final payment * (balloon) to complete their amortization. Payments may occur at the * beginning or end of a payment period. If you and your friend had agreed on * monthly repayment of the $800 loan at 12% NAR compounded monthly, twelve @@ -220,7 +220,7 @@ * compounding Frequency, CF, is simply the number of times per * year, the monies in the financial transaction are compounded. In * the U.S., monies are usually compounded daily on bank deposits, - * and monthly on loans. Somtimes Long term deposits are compounded + * and monthly on loans. Sometimes Long term deposits are compounded * quarterly or weekly. * * The Payment Frequency, PF, is simply how often during a year @@ -596,7 +596,7 @@ * T[n] = -i*n*(PV + C) - i*C*n(n+1)/2 * T[n] = -i*n*(PV + (C*(n - 1)/2)) * - * Note: substituing for C = -PV/N, in the equations for PV[n], I[n], + * Note: substituting for C = -PV/N, in the equations for PV[n], I[n], * P[n], and T[n] would give the following equations: * * PV[n] = PV*(1 - n/N) @@ -739,12 +739,12 @@ * 1. The payment *, interest paid, principal paid and remaining PV * for each payment period are computed and displayed. At the end of * each year a summary is computed and displayed and the total - * interest paid is diplayed at the end. + * interest paid is displayed at the end. * * 2. A summary is computed and displayed for each year. The * interest paid during the year is computed and displayed as well * as the remaining balance at years end. The total interest paid - * is diplayed at the end. + * is displayed at the end. * * 3. An amortization schedule is computed for a common method of * advanced payment of principal is computed and displayed. In this @@ -1016,7 +1016,7 @@ * Example 6: Balloon Payment * On long term loans, small changes in the periodic payments can generate * large changes in the future value. If the monthly payment in example 5 is - * rounded down to $1125, how much addtional (balloon) payment will be due + * rounded down to $1125, how much additional (balloon) payment will be due * with the final regular payment. * <>pmt=-1125 * -1,125 @@ -2034,7 +2034,7 @@ Amortization_Schedule (amort_sched_ptr amortsched) else { /* remaining pv less than advanced principal payment reduce - * advanced pricipal payment to remaining pv and set + * advanced principal payment to remaining pv and set * remaining pv to fv */ adv_pmt = -pv; pv = fv; @@ -2137,7 +2137,7 @@ Amortization_Schedule (amort_sched_ptr amortsched) case 'o': /* Constant payment to principal use constant payment equal to * original pv divided by number of periods. constant payment to - * pricipal could be amount specified by user. */ + * principal could be amount specified by user. */ amortsched->schedule.first_yr = amortyr = (amort_sched_yr_ptr) calloc (1, sizeof (amort_sched_yr)); amortsched->total_periods = n; diff --git a/libgnucash/app-utils/options.scm b/libgnucash/app-utils/options.scm index dc49f45ae0..8491933f34 100644 --- a/libgnucash/app-utils/options.scm +++ b/libgnucash/app-utils/options.scm @@ -455,7 +455,7 @@ the option '~a'.")) #f #f #f #f))) ;; budget option -;; TODO: need to double-check this proc (dates back to r11545 or eariler) +;; TODO: need to double-check this proc (dates back to r11545 or earlier) ;; ;; Always takes/returns a budget ;; Stores the GUID in the KVP diff --git a/libgnucash/doc/constraints.txt b/libgnucash/doc/constraints.txt index 6723259773..9d3a928408 100644 --- a/libgnucash/doc/constraints.txt +++ b/libgnucash/doc/constraints.txt @@ -71,7 +71,7 @@ returned. Simple/ad-hoc lazy evaluation works well when data dependencies are simple, but it breaks down when there are too many/circular -relationships. It becomes all too easy to get trapped in inifinite +relationships. It becomes all too easy to get trapped in infinite loops of corrections. The goal of moving to a formal constraint system is to introduce specific, well-defined sync points where constraint checking can be done, without incuring circular diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp index 12b7dcbfed..16b964b7ed 100644 --- a/libgnucash/engine/qofinstance.cpp +++ b/libgnucash/engine/qofinstance.cpp @@ -176,7 +176,7 @@ static void qof_instance_class_init(QofInstanceClass *klass) "Object Last Update", "A pointer to the last time this object was " "updated. This value is present for use by " - "backends and shouldnot be written by other " + "backends and shouldn't be written by other " "code.", G_PARAM_READWRITE)); From e615a5775aebc78e105e33c3dd9ef7b3a43c220e Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Sat, 28 Jan 2023 20:53:24 +0800 Subject: [PATCH 02/10] Bug 798734 - Aging Reports don't handle mixed currency payments and invoices without Trading Accounts --- gnucash/report/report-utilities.scm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gnucash/report/report-utilities.scm b/gnucash/report/report-utilities.scm index 1d29011580..36253fe31f 100644 --- a/gnucash/report/report-utilities.scm +++ b/gnucash/report/report-utilities.scm @@ -1002,7 +1002,7 @@ query instead.") (match splits (() (vector invoices opposing-splits overpayment)) (((? not-APAR? split) . rest) - (lp rest invoices (+ overpayment (xaccSplitGetAmount split)) + (lp rest invoices (+ overpayment (xaccSplitGetValue split)) opposing-splits)) ((split . rest) (let* ((lot (xaccSplitGetLot split)) @@ -1017,13 +1017,13 @@ query instead.") (((? split=?) . tail) (lp1 tail overpayment opposing-splits)) ((s . tail) (let* ((lot-bal (gnc-lot-get-balance lot)) - (lot-bal (if (sign-equal? lot-bal (xaccSplitGetAmount s)) + (lot-bal (if (sign-equal? lot-bal (xaccSplitGetValue s)) 0 lot-bal)) (derived? (not (zero? lot-bal))) (partial-amount (fold (lambda (a b) - (if (equal? s a) b (+ b (xaccSplitGetAmount a)))) + (if (equal? s a) b (+ b (xaccSplitGetValue a)))) (- lot-bal) lot-all-splits))) (lp1 tail (+ overpayment partial-amount) (cons (list s partial-amount derived?) @@ -1031,7 +1031,7 @@ query instead.") (inv (lp rest (cons (cons inv split) invoices) - (+ overpayment (xaccSplitGetAmount split)) + (+ overpayment (xaccSplitGetValue split)) opposing-splits)))))))) ;; create a stepped list, then add a date in the infinite future for @@ -1067,7 +1067,7 @@ query instead.") (bal (fold (lambda (a b) (if (<= (xaccTransGetDate (xaccSplitGetParent a)) to-date) - (+ (xaccSplitGetAmount a) b) + (+ (xaccSplitGetValue a) b) b)) 0 lot-splits)) (bal (if receivable? bal (- bal))) From 3a6d1ea6631a3c3ac90eedbad59c8c0ec89ed645 Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sun, 29 Jan 2023 10:14:38 +0100 Subject: [PATCH 03/10] =?UTF-8?q?Bug=C2=A0798737=20-=20Minor=20grammatical?= =?UTF-8?q?=20error=20-=20'for'=20missing=20in=20'You=20will=20be=20asked?= =?UTF-8?q?=20a=20conversion=20rate=20for=20each.'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gnucash/gnome/dialog-invoice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome/dialog-invoice.c b/gnucash/gnome/dialog-invoice.c index 8307cec9f3..c02e64fcde 100644 --- a/gnucash/gnome/dialog-invoice.c +++ b/gnucash/gnome/dialog-invoice.c @@ -999,7 +999,7 @@ gnc_invoice_post(InvoiceWindow *iw, struct post_invoice_params *post_params) gncInvoiceSetCurrency (invoice, gncOwnerGetCurrency (gncInvoiceGetOwner (invoice))); /* Fill in the conversion prices with feedback from the user */ - text = _("One or more of the entries are for accounts different from the invoice/bill currency. You will be asked a conversion rate for each."); + text = _("One or more of the entries are for accounts different from the invoice/bill currency. You will be asked to enter a conversion rate for each."); /* Ask the user for conversion rates for all foreign currencies * (relative to the invoice currency) */ From 34be3c8b14c97fd06a2a113d2a960f2089e3a279 Mon Sep 17 00:00:00 2001 From: Christopher Lam Date: Sun, 29 Jan 2023 14:25:41 +0800 Subject: [PATCH 04/10] [report-utilities] use fold for gnc:accounts-count-splits previous definition would build intermediate lists for each step. also handles the null account-list. --- gnucash/report/report-utilities.scm | 2 +- gnucash/report/test/test-report-utilities.scm | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/gnucash/report/report-utilities.scm b/gnucash/report/report-utilities.scm index 36253fe31f..79d3131f89 100644 --- a/gnucash/report/report-utilities.scm +++ b/gnucash/report/report-utilities.scm @@ -741,7 +741,7 @@ query instead.") ;; function to count the total number of splits to be iterated (define (gnc:accounts-count-splits accounts) - (apply + (map length (map xaccAccountGetSplitList accounts)))) + (fold (lambda (a b) (+ b (length (xaccAccountGetSplitList a)))) 0 accounts)) ;; Sums up any splits of a certain type affecting a set of accounts. ;; the type is an alist '((str "match me") (cased #f) (regexp #f)) diff --git a/gnucash/report/test/test-report-utilities.scm b/gnucash/report/test/test-report-utilities.scm index 1016e3999d..c198ec430e 100644 --- a/gnucash/report/test/test-report-utilities.scm +++ b/gnucash/report/test/test-report-utilities.scm @@ -477,6 +477,10 @@ 44 (gnc:accounts-count-splits (list expense income))) + (test-equal "gnc:accounts-count-splits null" + 0 + (gnc:accounts-count-splits '())) + (let ((account-balances (gnc:get-assoc-account-balances (list bank gbp-bank) (lambda (acct) From dd50c7af13ed3954492e997884fa962b76df6328 Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sun, 29 Jan 2023 11:31:24 +0100 Subject: [PATCH 05/10] Multi-currency - show split amount rather than transaction value This issue was found while evaluating bug 798734, but not the immediate topic of that bug. --- gnucash/gnome/dialog-payment.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnucash/gnome/dialog-payment.c b/gnucash/gnome/dialog-payment.c index 3447debb1c..54f3827065 100644 --- a/gnucash/gnome/dialog-payment.c +++ b/gnucash/gnome/dialog-payment.c @@ -1568,7 +1568,7 @@ gboolean gnc_ui_payment_is_customer_payment(const Transaction *txn) // /////////////// static char *gen_split_desc (Transaction *txn, Split *split) { - gnc_numeric value = xaccSplitGetValue(split); + gnc_numeric value = xaccSplitGetAmount(split); Account *xfer_acct = xaccSplitGetAccount(split); char *acct_name = gnc_account_get_full_name (xfer_acct); const char *action = gnc_get_action_num (txn, split); From 8e6fb15d44a497b11962772fb6ce3d064ca8a8b7 Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sun, 29 Jan 2023 11:32:58 +0100 Subject: [PATCH 06/10] Improve a few messages related to business payments --- gnucash/gnome/dialog-payment.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/gnucash/gnome/dialog-payment.c b/gnucash/gnome/dialog-payment.c index 54f3827065..4718942f45 100644 --- a/gnucash/gnome/dialog-payment.c +++ b/gnucash/gnome/dialog-payment.c @@ -1573,18 +1573,29 @@ static char *gen_split_desc (Transaction *txn, Split *split) char *acct_name = gnc_account_get_full_name (xfer_acct); const char *action = gnc_get_action_num (txn, split); const char *memo = xaccSplitGetMemo (split); + char rec_state = xaccSplitGetReconcile (split); const char *print_amt = xaccPrintAmount(value, gnc_account_print_info (xfer_acct, TRUE)); char *split_str = NULL; + char *rec_str = NULL; + + if (rec_state == CREC) + rec_str = g_strdup_printf("[%s] ", _("Cleared")); + else if (rec_state == YREC) + rec_str = g_strdup_printf("[%s] ", _("Reconciled")); + else + rec_str = g_strdup(""); if (action && *action && memo && *memo) - split_str = g_strdup_printf ("%s: %s (%s, %s)", acct_name, print_amt, + split_str = g_strdup_printf ("%s%s: %s (%s, %s)", rec_str, acct_name, print_amt, action, memo); else if((action && *action) || (memo && *memo)) - split_str = g_strdup_printf ("%s: %s (%s)", acct_name, print_amt, + split_str = g_strdup_printf ("%s%s: %s (%s)", rec_str, acct_name, print_amt, action ? action : memo); else - split_str = g_strdup_printf ("%s: %s", acct_name, print_amt); + split_str = g_strdup_printf ("%s%s: %s", rec_str, acct_name, print_amt); + g_free (acct_name); + g_free (rec_str); return split_str; } @@ -1621,8 +1632,8 @@ static Split *select_payment_split (GtkWindow *parent, Transaction *txn) GList *node; GtkWidget *first_rb = NULL; int answer = GTK_BUTTONS_OK; - const char *message = _("While this transaction has multiple splits that can be considered\nas 'the payment split', gnucash only knows how to handle one.\n" - "Please select one, the others will be ignored.\n\n"); + const char *message = _("While this transaction has multiple splits that can be considered\nas 'the payment split', GnuCash only knows how to handle one.\n" + "Please select one, the others will be discarded.\n\n"); GtkDialog *dialog = GTK_DIALOG( gtk_dialog_new_with_buttons (_("Warning"), parent, From 2d3d05068d5a0d8f1349324616e37dc6fbc7f551 Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sun, 29 Jan 2023 11:37:54 +0100 Subject: [PATCH 07/10] Edit/assign payment - be more prudent not to lose data We can't handle the case where a transaction has splits in more than one APAR account. Instead of only warning that some of them will be lost refuse to continue and leave it to the user to fix the transaction first. --- gnucash/gnome/dialog-payment.c | 82 ++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/gnucash/gnome/dialog-payment.c b/gnucash/gnome/dialog-payment.c index 4718942f45..75712bc581 100644 --- a/gnucash/gnome/dialog-payment.c +++ b/gnucash/gnome/dialog-payment.c @@ -1694,10 +1694,11 @@ static Split *select_payment_split (GtkWindow *parent, Transaction *txn) static GList *select_txn_lots (GtkWindow *parent, Transaction *txn, Account **post_acct, gboolean *abort) { - gboolean has_no_lot_apar_splits = FALSE; - SplitList *post_splits = NULL, *no_lot_post_splits = NULL; + SplitList *apar_splits = NULL; /* all spits in txn that are APAR type */ + SplitList *apar_splits_no_lot = NULL; /* all splits in txn that are APAR type, but not tied to a lot */ SplitList *iter; GList *txn_lots = NULL; + GList *unique_apar_accts = NULL; /* There's no use in continuing if I can't set the post_acct or abort variables */ if (!post_acct || !abort) @@ -1706,11 +1707,20 @@ static GList *select_txn_lots (GtkWindow *parent, Transaction *txn, Account **po *abort = FALSE; *post_acct = NULL; - post_splits = xaccTransGetAPARAcctSplitList (txn, FALSE); - for (iter = post_splits; iter; iter = iter->next) + /* Start by filtering out all APAR splits that have lots. Those are the ones we can + display as invoices and pre-payments in the payment window. */ + apar_splits = xaccTransGetAPARAcctSplitList (txn, FALSE); + for (iter = apar_splits; iter; iter = iter->next) { GNCLot *postlot = NULL; Split *post_split = iter->data; + Account *apar_acct = xaccSplitGetAccount (post_split); + + /* Store found apar_acct in our list of unique_apar_accts + * for later processing */ + if (!g_list_find (unique_apar_accts, apar_acct)) + unique_apar_accts = g_list_prepend (unique_apar_accts, apar_acct); + postlot = xaccSplitGetLot (post_split); if (postlot) { @@ -1718,59 +1728,55 @@ static GList *select_txn_lots (GtkWindow *parent, Transaction *txn, Account **po lot_info->lot = postlot; lot_info->amount = xaccSplitGetValue (post_split); txn_lots = g_list_prepend (txn_lots, lot_info); - *post_acct = xaccSplitGetAccount (post_split); + *post_acct = apar_acct; } else - { - /* Make sure not to override post_acct if it was set above from a lot split */ - if (!*post_acct) - *post_acct = xaccSplitGetAccount (post_split); - no_lot_post_splits = g_list_prepend (no_lot_post_splits, post_split); - has_no_lot_apar_splits = TRUE; - } + apar_splits_no_lot = g_list_prepend (apar_splits_no_lot, post_split); } + g_list_free (apar_splits); - g_list_free (post_splits); + /* If no post_acct was selected from the postlots, fall back to the first apar split's + * account if there is one. */ + if (!*post_acct && apar_splits_no_lot) + *post_acct = xaccSplitGetAccount (apar_splits_no_lot->data); + g_list_free (apar_splits_no_lot); - /* If the txn has both APAR splits linked to a business lot and - * splits that are not, issue a warning some will be discarded. + /* Abort if the txn has splits in more than one APAR account + * GnuCash can only handle one post account per payment transaction. */ - if (has_no_lot_apar_splits && gnc_list_length_cmp (txn_lots, 0)) + if (g_list_length (unique_apar_accts) > 1) { GtkWidget *dialog; char *split_str = g_strdup (""); - for (iter = no_lot_post_splits; iter; iter = iter->next) + + for (iter = unique_apar_accts; iter; iter = iter->next) { - Split *post_split = iter->data; - char *tmp_str = gen_split_desc (txn, post_split); - char *tmp_str2 = g_strconcat(split_str, "• ", tmp_str, "\n", NULL); - g_free (tmp_str); + Account *acct = iter->data; + char *acct_name = gnc_account_get_full_name (acct); + char *tmp_str = g_strconcat(split_str, "• ", acct_name, "\n", NULL); + g_free (acct_name); g_free (split_str); - split_str = tmp_str2; + split_str = tmp_str; } dialog = gtk_message_dialog_new (parent, GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_CANCEL, - _("The transaction has at least one split in a business account that is not part of a business transaction.\n" - "If you continue these splits will be ignored:\n\n%s\n" - "Do you wish to continue and ignore these splits?"), + GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, + _("This transaction has splits in multiple business accounts:\n\n%s\n" + "GnuCash can only handle transactions that post to a single account.\n\n" + "Please correct this manually by editing the transaction directly and then try again."), split_str); - gtk_dialog_add_buttons (GTK_DIALOG(dialog), - _("Continue"), GTK_BUTTONS_OK, NULL); - gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_BUTTONS_CANCEL); - if (gtk_dialog_run (GTK_DIALOG(dialog)) != GTK_BUTTONS_OK) - { - *abort = TRUE; - g_list_free_full (txn_lots, g_free); - txn_lots = NULL; - } + gtk_dialog_run (GTK_DIALOG(dialog)); gtk_widget_destroy (dialog); + PINFO("Multiple asset accounts in splits of txn \"%s\"; cannot use this for assigning a payment.", + xaccTransGetDescription(txn)); g_free (split_str); - } - g_list_free (no_lot_post_splits); + *abort = TRUE; + g_list_free_full (txn_lots, g_free); + txn_lots = NULL; + } return txn_lots; } From 894f8241e127ce180eb74ee280a49c4a95ba88cf Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sun, 29 Jan 2023 22:48:50 +0100 Subject: [PATCH 08/10] Revisit invoice payment in multi-currency context - Show proper amount in dialog when applying or editing an existing transaction as payment - Be more careful not to waste the existing payment split - If the user changed the payment amount while starting from an existing transaction unreconcile the changed payment split - Avoid needlessly changing transaction currency (only do so if the user chose a new transfer account and the old currency is neither the new transfer account's currency nor the post account's currency) --- gnucash/gnome/dialog-payment.c | 3 +- libgnucash/engine/gncOwner.c | 140 +++++++++++++++------------------ 2 files changed, 64 insertions(+), 79 deletions(-) diff --git a/gnucash/gnome/dialog-payment.c b/gnucash/gnome/dialog-payment.c index 75712bc581..fa9a403ac4 100644 --- a/gnucash/gnome/dialog-payment.c +++ b/gnucash/gnome/dialog-payment.c @@ -1825,7 +1825,8 @@ PaymentWindow * gnc_ui_payment_new_with_txn (GtkWindow* parent, GncOwner *owner, GDate txn_date = xaccTransGetDatePostedGDate (txn); gnc_ui_payment_window_set_date(pw, &txn_date); } - gnc_ui_payment_window_set_amount(pw, xaccSplitGetValue(payment_split)); + + gnc_ui_payment_window_set_amount(pw, xaccSplitConvertAmount (payment_split, post_acct)); if (payment_split) gnc_ui_payment_window_set_xferaccount(pw, xaccSplitGetAccount(payment_split)); return pw; diff --git a/libgnucash/engine/gncOwner.c b/libgnucash/engine/gncOwner.c index c3c8b73151..5fcc0b24f7 100644 --- a/libgnucash/engine/gncOwner.c +++ b/libgnucash/engine/gncOwner.c @@ -756,10 +756,12 @@ gncOwnerCreatePaymentLotSecs (const GncOwner *owner, Transaction **preset_txn, QofBook *book; Split *split; const char *name; - gnc_commodity *commodity; + gnc_commodity *post_comm, *xfer_comm; Split *xfer_split = NULL; Transaction *txn = NULL; GNCLot *payment_lot; + gnc_numeric xfer_amount = gnc_numeric_zero(); + gnc_numeric txn_value = gnc_numeric_zero(); /* Verify our arguments */ if (!owner || !posted_acc || !xfer_acc) return NULL; @@ -768,7 +770,9 @@ gncOwnerCreatePaymentLotSecs (const GncOwner *owner, Transaction **preset_txn, /* Compute the ancillary data */ book = gnc_account_get_book (posted_acc); name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner)); - commodity = gncOwnerGetCurrency (owner); + post_comm = xaccAccountGetCommodity (posted_acc); + xfer_comm = xaccAccountGetCommodity (xfer_acc); + // reverse = use_reversed_payment_amounts(owner); if (preset_txn && *preset_txn) @@ -776,110 +780,90 @@ gncOwnerCreatePaymentLotSecs (const GncOwner *owner, Transaction **preset_txn, if (txn) { - xaccTransSetDescription (txn, name ? name : ""); + int i = 0; /* Pre-existing transaction was specified. We completely clear it, - * except for the split in the transfer account, unless the - * transaction can't be reused (wrong currency, wrong transfer account). - * In that case, the transaction is simply removed and an new - * one created. */ - + * except for a pre-existing transfer split. We're very conservative + * in preserving that one as it may have been reconciled already. */ xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc); - - if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner)) - { - PINFO("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction."); - xfer_split = NULL; - } - - if (!xfer_split) - { - PINFO("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one."); - - xaccTransBeginEdit (txn); - xaccTransDestroy (txn); - xaccTransCommitEdit (txn); - - txn = NULL; - } - else + xaccTransBeginEdit (txn); + while (i < xaccTransCountSplits(txn)) { - int i = 0; - xaccTransBeginEdit (txn); - while (i < xaccTransCountSplits(txn)) - { - Split *split = xaccTransGetSplit (txn, i); - if (split == xfer_split) - { - gnc_set_num_action (NULL, split, num, _("Payment")); - ++i; - } - else - { - xaccSplitDestroy(split); - } - } - /* Note: don't commit transaction now - that would insert an imbalance split.*/ + Split *split = xaccTransGetSplit (txn, i); + if (split == xfer_split) + ++i; + else + xaccSplitDestroy(split); } + /* Note: don't commit transaction now - that would insert an imbalance split.*/ } - - /* Create the transaction if we don't have one yet */ - if (!txn) + else { txn = xaccMallocTransaction (book); xaccTransBeginEdit (txn); } + /* Complete transaction setup */ + xaccTransSetDescription (txn, name ? name : ""); + if (!gnc_commodity_equal(xaccTransGetCurrency (txn), post_comm) && + !gnc_commodity_equal (xaccTransGetCurrency (txn), xfer_comm)) + xaccTransSetCurrency (txn, xfer_comm); + + /* With all commodities involved known, define split amounts and txn value. + * - post amount (amount passed in as parameter) is always in post_acct commodity, + * - xfer amount requires conversion if the xfer account has a different + * commodity than the post account. + * - txn value requires conversion if the post account has a different + * commodity than the transaction */ + if (gnc_commodity_equal(post_comm, xfer_comm)) + xfer_amount = amount; + else + xfer_amount = gnc_numeric_mul (amount, exch, GNC_DENOM_AUTO, + GNC_HOW_RND_ROUND_HALF_UP); + + if (gnc_commodity_equal(post_comm, xaccTransGetCurrency (txn))) + txn_value = amount; + else + txn_value = gnc_numeric_mul (amount, exch, GNC_DENOM_AUTO, + GNC_HOW_RND_ROUND_HALF_UP); + /* Insert a split for the transfer account if we don't have one yet */ if (!xfer_split) { - - /* Set up the transaction */ - xaccTransSetDescription (txn, name ? name : ""); - /* set per book option */ - xaccTransSetCurrency (txn, commodity); - - /* The split for the transfer account */ - split = xaccMallocSplit (book); - xaccSplitSetMemo (split, memo); + xfer_split = xaccMallocSplit (book); + xaccSplitSetMemo (xfer_split, memo); /* set per book option */ - gnc_set_num_action (NULL, split, num, _("Payment")); + gnc_set_num_action (NULL, xfer_split, num, _("Payment")); xaccAccountBeginEdit (xfer_acc); - xaccAccountInsertSplit (xfer_acc, split); + xaccAccountInsertSplit (xfer_acc, xfer_split); xaccAccountCommitEdit (xfer_acc); - xaccTransAppendSplit (txn, split); + xaccTransAppendSplit (txn, xfer_split); - if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity)) - { - xaccSplitSetBaseValue (split, amount, commodity); - } - else - { - /* This will be a multi-currency transaction. The amount passed to this - * function is in the owner commodity (also used by the post account). - * For the xfer split we also need to value the payment in the xfer account's - * commodity. - * exch is from post account to xfer account so that can be used directly - * to calculate the equivalent amount in the xfer account's commodity. */ - gnc_numeric xfer_amount = gnc_numeric_mul (amount, exch, GNC_DENOM_AUTO, - GNC_HOW_RND_ROUND_HALF_UP); - - xaccSplitSetAmount(split, xfer_amount); /* Payment in xfer account currency */ - xaccSplitSetValue(split, amount); /* Payment in transaction currency */ - } + xaccSplitSetAmount(xfer_split, xfer_amount); /* Payment in xfer account currency */ + xaccSplitSetValue(xfer_split, txn_value); /* Payment in transaction currency */ + } + /* For a pre-existing xfer split, let's check if the amount and value + * have changed. If so, update them and unreconcile. */ + else if (!gnc_numeric_equal (xaccSplitGetAmount (xfer_split), xfer_amount) || + !gnc_numeric_equal (xaccSplitGetValue (xfer_split), txn_value)) + { + xaccSplitSetAmount (xfer_split, xfer_amount); + xaccSplitSetValue (xfer_split, txn_value); + xaccSplitSetReconcile (xfer_split, NREC); } /* Add a split in the post account */ split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set per book option */ - gnc_set_num_action (NULL, split, num, _("Payment")); + xaccSplitSetAction (split, _("Payment")); xaccAccountBeginEdit (posted_acc); xaccAccountInsertSplit (posted_acc, split); xaccAccountCommitEdit (posted_acc); xaccTransAppendSplit (txn, split); - xaccSplitSetBaseValue (split, gnc_numeric_neg (amount), commodity); + xaccSplitSetAmount (split, gnc_numeric_neg (amount)); + xaccSplitSetValue (split, gnc_numeric_neg (txn_value)); /* Create a new lot for the payment */ payment_lot = gnc_lot_new (book); @@ -887,7 +871,7 @@ gncOwnerCreatePaymentLotSecs (const GncOwner *owner, Transaction **preset_txn, gnc_lot_add_split (payment_lot, split); /* Mark the transaction as a payment */ - gnc_set_num_action (txn, NULL, num, _("Payment")); + xaccTransSetNum (txn, num); xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT); /* Set date for transaction */ From 34ed91eac969a3fb5cff8802a38293abeedc6ba6 Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sun, 29 Jan 2023 22:50:44 +0100 Subject: [PATCH 09/10] =?UTF-8?q?Bug=C2=A0798734=20-=20Aging=20Reports=20d?= =?UTF-8?q?on't=20handle=20mixed=20currency=20payments=20and=20invoices=20?= =?UTF-8?q?without=20Trading=20Accounts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revisit the fix. It still made assumptions about the way payment transactions were created. It can now handle payment transaction either in the payment account currency or in the post account currency. --- gnucash/report/report-utilities.scm | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/gnucash/report/report-utilities.scm b/gnucash/report/report-utilities.scm index 79d3131f89..eaf42e5eef 100644 --- a/gnucash/report/report-utilities.scm +++ b/gnucash/report/report-utilities.scm @@ -987,7 +987,7 @@ query instead.") (define (not-APAR? s) (not (xaccAccountIsAPARType (xaccAccountGetType (xaccSplitGetAccount s))))) ;; analyse a payment transaction and return a 3-element vector: -;; (vector invoices opposing-splits overpayment) +;; (vector invoices overpayment opposing-splits) ;; ;; invoices: a list of (cons invoice inv-APAR-split) ;; opposing-splits: a list of (list pmt-APAR-split partial-amount derived?) @@ -995,6 +995,8 @@ query instead.") ;; amount does not match the transaction amount ;; overpayment: a number indicating overpayment amount (define (gnc:payment-txn->payment-info txn) +(let* ((apar-split (xaccTransGetFirstAPARAcctSplit txn #t)) + (apar-acct (xaccSplitGetAccount apar-split))) (let lp ((splits (xaccTransGetSplitList txn)) (invoices '()) (overpayment 0) @@ -1002,7 +1004,8 @@ query instead.") (match splits (() (vector invoices opposing-splits overpayment)) (((? not-APAR? split) . rest) - (lp rest invoices (+ overpayment (xaccSplitGetValue split)) + (gnc:msg "next " (gnc:strify split) " overpayment " (+ overpayment (xaccSplitConvertAmount split apar-acct))) + (lp rest invoices (+ overpayment (xaccSplitConvertAmount split apar-acct)) opposing-splits)) ((split . rest) (let* ((lot (xaccSplitGetLot split)) @@ -1017,22 +1020,24 @@ query instead.") (((? split=?) . tail) (lp1 tail overpayment opposing-splits)) ((s . tail) (let* ((lot-bal (gnc-lot-get-balance lot)) - (lot-bal (if (sign-equal? lot-bal (xaccSplitGetValue s)) + (lot-bal (if (sign-equal? lot-bal (xaccSplitConvertAmount s apar-acct)) 0 lot-bal)) (derived? (not (zero? lot-bal))) (partial-amount (fold (lambda (a b) - (if (equal? s a) b (+ b (xaccSplitGetValue a)))) + (if (equal? s a) b (+ b (xaccSplitConvertAmount a apar-acct)))) (- lot-bal) lot-all-splits))) + (gnc:msg "next " (gnc:strify s) " overpayment " (+ overpayment partial-amount)) (lp1 tail (+ overpayment partial-amount) (cons (list s partial-amount derived?) opposing-splits))))))) (inv + (gnc:msg "next " (gnc:strify split) " overpayment " (+ overpayment (xaccSplitConvertAmount split apar-acct))) (lp rest (cons (cons inv split) invoices) - (+ overpayment (xaccSplitGetValue split)) - opposing-splits)))))))) + (+ overpayment (xaccSplitConvertAmount split apar-acct)) + opposing-splits))))))))) ;; create a stepped list, then add a date in the infinite future for ;; the "current" bucket @@ -1046,7 +1051,7 @@ query instead.") (define-public (gnc:owner-splits->aging-list splits num-buckets to-date date-type receivable?) (gnc:msg "processing " (qof-print-date to-date) " date-type " date-type - "receivable? " receivable?) + " receivable? " receivable?) (let ((bucket-dates (make-extended-interval-list to-date (- num-buckets 3))) (buckets (make-vector num-buckets 0))) (define (addbucket! idx amt) @@ -1064,10 +1069,11 @@ query instead.") (xaccSplitGetParent (car splits)))) (lot (gncInvoiceGetPostedLot invoice)) (lot-splits (gnc-lot-get-split-list lot)) + (apar-acct (gncInvoiceGetPostedAcc invoice)) (bal (fold (lambda (a b) (if (<= (xaccTransGetDate (xaccSplitGetParent a)) to-date) - (+ (xaccSplitGetValue a) b) + (+ (xaccSplitConvertAmount a apar-acct) b) b)) 0 lot-splits)) (bal (if receivable? bal (- bal))) From 5bbfb8efd49f171b0674e04046cd8689155452f5 Mon Sep 17 00:00:00 2001 From: Geert Janssens Date: Sun, 29 Jan 2023 23:11:49 +0100 Subject: [PATCH 10/10] Add proper indent to scheme changes from previous commit Committed seperately to make it easier to look at the actual changes. This commit only adds a 2 space indent to one function body. --- gnucash/report/report-utilities.scm | 86 ++++++++++++++--------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/gnucash/report/report-utilities.scm b/gnucash/report/report-utilities.scm index eaf42e5eef..9974e2d6d4 100644 --- a/gnucash/report/report-utilities.scm +++ b/gnucash/report/report-utilities.scm @@ -995,49 +995,49 @@ query instead.") ;; amount does not match the transaction amount ;; overpayment: a number indicating overpayment amount (define (gnc:payment-txn->payment-info txn) -(let* ((apar-split (xaccTransGetFirstAPARAcctSplit txn #t)) - (apar-acct (xaccSplitGetAccount apar-split))) - (let lp ((splits (xaccTransGetSplitList txn)) - (invoices '()) - (overpayment 0) - (opposing-splits '())) - (match splits - (() (vector invoices opposing-splits overpayment)) - (((? not-APAR? split) . rest) - (gnc:msg "next " (gnc:strify split) " overpayment " (+ overpayment (xaccSplitConvertAmount split apar-acct))) - (lp rest invoices (+ overpayment (xaccSplitConvertAmount split apar-acct)) - opposing-splits)) - ((split . rest) - (let* ((lot (xaccSplitGetLot split)) - (lot-all-splits (gnc-lot-get-split-list lot))) - (define split=? (cut equal? <> split)) - (match (gncInvoiceGetInvoiceFromLot lot) - (() (let lp1 ((lot-splits lot-all-splits) - (overpayment overpayment) - (opposing-splits opposing-splits)) - (match lot-splits - (() (lp rest invoices overpayment opposing-splits)) - (((? split=?) . tail) (lp1 tail overpayment opposing-splits)) - ((s . tail) - (let* ((lot-bal (gnc-lot-get-balance lot)) - (lot-bal (if (sign-equal? lot-bal (xaccSplitConvertAmount s apar-acct)) - 0 lot-bal)) - (derived? (not (zero? lot-bal))) - (partial-amount - (fold - (lambda (a b) - (if (equal? s a) b (+ b (xaccSplitConvertAmount a apar-acct)))) - (- lot-bal) lot-all-splits))) - (gnc:msg "next " (gnc:strify s) " overpayment " (+ overpayment partial-amount)) - (lp1 tail (+ overpayment partial-amount) - (cons (list s partial-amount derived?) - opposing-splits))))))) - (inv - (gnc:msg "next " (gnc:strify split) " overpayment " (+ overpayment (xaccSplitConvertAmount split apar-acct))) - (lp rest - (cons (cons inv split) invoices) - (+ overpayment (xaccSplitConvertAmount split apar-acct)) - opposing-splits))))))))) + (let* ((apar-split (xaccTransGetFirstAPARAcctSplit txn #t)) + (apar-acct (xaccSplitGetAccount apar-split))) + (let lp ((splits (xaccTransGetSplitList txn)) + (invoices '()) + (overpayment 0) + (opposing-splits '())) + (match splits + (() (vector invoices opposing-splits overpayment)) + (((? not-APAR? split) . rest) + (gnc:msg "next " (gnc:strify split) " overpayment " (+ overpayment (xaccSplitConvertAmount split apar-acct))) + (lp rest invoices (+ overpayment (xaccSplitConvertAmount split apar-acct)) + opposing-splits)) + ((split . rest) + (let* ((lot (xaccSplitGetLot split)) + (lot-all-splits (gnc-lot-get-split-list lot))) + (define split=? (cut equal? <> split)) + (match (gncInvoiceGetInvoiceFromLot lot) + (() (let lp1 ((lot-splits lot-all-splits) + (overpayment overpayment) + (opposing-splits opposing-splits)) + (match lot-splits + (() (lp rest invoices overpayment opposing-splits)) + (((? split=?) . tail) (lp1 tail overpayment opposing-splits)) + ((s . tail) + (let* ((lot-bal (gnc-lot-get-balance lot)) + (lot-bal (if (sign-equal? lot-bal (xaccSplitConvertAmount s apar-acct)) + 0 lot-bal)) + (derived? (not (zero? lot-bal))) + (partial-amount + (fold + (lambda (a b) + (if (equal? s a) b (+ b (xaccSplitConvertAmount a apar-acct)))) + (- lot-bal) lot-all-splits))) + (gnc:msg "next " (gnc:strify s) " overpayment " (+ overpayment partial-amount)) + (lp1 tail (+ overpayment partial-amount) + (cons (list s partial-amount derived?) + opposing-splits))))))) + (inv + (gnc:msg "next " (gnc:strify split) " overpayment " (+ overpayment (xaccSplitConvertAmount split apar-acct))) + (lp rest + (cons (cons inv split) invoices) + (+ overpayment (xaccSplitConvertAmount split apar-acct)) + opposing-splits))))))))) ;; create a stepped list, then add a date in the infinite future for ;; the "current" bucket