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/gnucash/gnome/dialog-invoice.c b/gnucash/gnome/dialog-invoice.c index bc4e22855e..09ad5ac584 100644 --- a/gnucash/gnome/dialog-invoice.c +++ b/gnucash/gnome/dialog-invoice.c @@ -990,7 +990,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) */ diff --git a/gnucash/gnome/dialog-payment.c b/gnucash/gnome/dialog-payment.c index 3447debb1c..fa9a403ac4 100644 --- a/gnucash/gnome/dialog-payment.c +++ b/gnucash/gnome/dialog-payment.c @@ -1568,23 +1568,34 @@ 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); 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, @@ -1683,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) @@ -1695,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) { @@ -1707,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; } @@ -1808,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/gnucash/report/report-utilities.scm b/gnucash/report/report-utilities.scm index 8b00ea0557..5ecb9917a1 100644 --- a/gnucash/report/report-utilities.scm +++ b/gnucash/report/report-utilities.scm @@ -666,7 +666,7 @@ ;; 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)) @@ -912,7 +912,7 @@ (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?) @@ -920,44 +920,49 @@ ;; amount does not match the transaction amount ;; overpayment: a number indicating overpayment amount (define (gnc:payment-txn->payment-info txn) - (let lp ((splits (xaccTransGetSplitList txn)) - (invoices '()) - (overpayment 0) - (opposing-splits '())) - (match splits - (() (vector invoices opposing-splits overpayment)) - (((? not-APAR? split) . rest) - (lp rest invoices (+ overpayment (xaccSplitGetAmount split)) - 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 (xaccSplitGetAmount s)) - 0 lot-bal)) - (derived? (not (zero? lot-bal))) - (partial-amount - (fold - (lambda (a b) - (if (equal? s a) b (+ b (xaccSplitGetAmount a)))) - (- lot-bal) lot-all-splits))) - (lp1 tail (+ overpayment partial-amount) - (cons (list s partial-amount derived?) - opposing-splits))))))) - (inv - (lp rest - (cons (cons inv split) invoices) - (+ overpayment (xaccSplitGetAmount split)) - 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 @@ -971,7 +976,7 @@ (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) @@ -989,10 +994,11 @@ (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) - (+ (xaccSplitGetAmount a) b) + (+ (xaccSplitConvertAmount a apar-acct) b) b)) 0 lot-splits)) (bal (if receivable? bal (- bal))) diff --git a/gnucash/report/test/test-report-utilities.scm b/gnucash/report/test/test-report-utilities.scm index 61731f5e70..5aae0c80c5 100644 --- a/gnucash/report/test/test-report-utilities.scm +++ b/gnucash/report/test/test-report-utilities.scm @@ -453,6 +453,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) diff --git a/libgnucash/app-utils/calculation/fin.c b/libgnucash/app-utils/calculation/fin.c index 2966a2fb83..410ccc22f3 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 @@ -2035,7 +2035,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; @@ -2138,7 +2138,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/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/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)) + xaccTransBeginEdit (txn); + while (i < xaccTransCountSplits(txn)) { - 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 - { - 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 */ diff --git a/libgnucash/engine/qofinstance.cpp b/libgnucash/engine/qofinstance.cpp index 6b7d3393f9..111e082c96 100644 --- a/libgnucash/engine/qofinstance.cpp +++ b/libgnucash/engine/qofinstance.cpp @@ -173,7 +173,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));