r23485 - gnucash/trunk/src - Bug 715123 - Post invoice problem, cannot unpost

Geert Janssens gjanssens at code.gnucash.org
Wed Dec 4 11:56:56 EST 2013


Author: gjanssens
Date: 2013-12-04 11:56:55 -0500 (Wed, 04 Dec 2013)
New Revision: 23485
Trac: http://svn.gnucash.org/trac/changeset/23485

Modified:
   gnucash/trunk/src/business/business-gnome/dialog-invoice.c
   gnucash/trunk/src/engine/gncInvoice.c
   gnucash/trunk/src/engine/gncInvoice.h
Log:
Bug 715123 - Post invoice problem, cannot unpost

Look for missing exchange rates in tax table entries
as well as in invoice entries

Modified: gnucash/trunk/src/business/business-gnome/dialog-invoice.c
===================================================================
--- gnucash/trunk/src/business/business-gnome/dialog-invoice.c	2013-12-04 07:30:54 UTC (rev 23484)
+++ gnucash/trunk/src/business/business-gnome/dialog-invoice.c	2013-12-04 16:56:55 UTC (rev 23485)
@@ -765,9 +765,10 @@
     KvpFrame *kvpf;
     KvpValue *kvp_val;
     const char *text;
-    EntryList *entries, *entries_iter;
-    GncEntry* entry;
-    gboolean is_cust_doc, is_cn, auto_pay;
+    GHashTable *foreign_currs;
+    GHashTableIter foreign_currs_iter;
+    gpointer key,value;
+    gboolean is_cust_doc, auto_pay;
     gboolean show_dialog = TRUE;
     gboolean post_ok = TRUE;
 
@@ -788,7 +789,6 @@
     }
 
     is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
-    is_cn = gncInvoiceGetIsCreditNote (invoice);
 
     /* Ok, we can post this invoice.  Ask for verification, set the due date,
      * post date, and posted account
@@ -821,105 +821,88 @@
     /* 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.");
 
-    /* Get the invoice entries */
-    entries = gncInvoiceGetEntries (invoice);
-
-    for (entries_iter = entries; entries_iter != NULL; entries_iter = g_list_next(entries_iter))
+    /* Ask the user for conversion rates for all foreign currencies
+     * (relative to the invoice currency) */
+    foreign_currs = gncInvoiceGetForeignCurrencies (invoice);
+    g_hash_table_iter_init (&foreign_currs_iter, foreign_currs);
+    while (g_hash_table_iter_next (&foreign_currs_iter, &key, &value))
     {
-        Account *this_acc;
-        gnc_commodity *account_currency;
+        GNCPrice *convprice;
+        gnc_commodity *account_currency = (gnc_commodity*)key;
+        gnc_numeric *amount = (gnc_numeric*)value;
 
-        entry = (GncEntry*)entries_iter->data;
-        this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
-                    gncEntryGetBillAccount (entry));
-        account_currency = xaccAccountGetCommodity (this_acc);
+        if (show_dialog)
+        {
+            gnc_info_dialog(iw_get_window(iw), "%s", text);
+            show_dialog = FALSE;
+        }
 
-        if (this_acc &&
-                !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), account_currency))
+        convprice = gncInvoiceGetPrice(invoice, account_currency);
+        if (convprice == NULL)
         {
-            GNCPrice *convprice;
+            XferDialog *xfer;
+            gnc_numeric exch_rate;
+            Timespec date;
 
-            if (show_dialog)
-            {
-                gnc_info_dialog(iw_get_window(iw), "%s", text);
-                show_dialog = FALSE;
-            }
+            /* Note some twisted logic here:
+             * We ask the exchange rate
+             *  FROM invoice currency
+             *  TO other account currency
+             *  Because that's what happens logically.
+             *  But the internal posting logic works backwards:
+             *  It searches for an exchange rate
+             *  FROM other account currency
+             *  TO invoice currency
+             *  So we will store the inverted exchange rate
+             */
 
-            convprice = gncInvoiceGetPrice(invoice, account_currency);
-            if (convprice == NULL)
+            /* create the exchange-rate dialog */
+            xfer = gnc_xfer_dialog (iw_get_window(iw), acc);
+            gnc_xfer_dialog_is_exchange_dialog(xfer, &exch_rate);
+            gnc_xfer_dialog_select_to_currency(xfer, account_currency);
+            /* Even if amount is 0 ask for an exchange rate. It's required
+             * for the transaction generating code. Use an amount of 1 in
+             * that case as the dialog won't allow to specify an exchange
+             * rate for 0. */
+            gnc_xfer_dialog_set_amount(xfer, gnc_numeric_zero_p (*amount) ?
+                                             (gnc_numeric){1, 1} : *amount);
+
+            /* All we want is the exchange rate so prevent the user from thinking
+               it makes sense to mess with other stuff */
+            gnc_xfer_dialog_set_from_show_button_active(xfer, FALSE);
+            gnc_xfer_dialog_set_to_show_button_active(xfer, FALSE);
+            gnc_xfer_dialog_hide_from_account_tree(xfer);
+            gnc_xfer_dialog_hide_to_account_tree(xfer);
+            if (gnc_xfer_dialog_run_until_done(xfer))
             {
-                XferDialog *xfer;
-                gnc_numeric exch_rate;
-                Timespec date;
-                gnc_numeric amount = gnc_numeric_create(1, 1);
-                gnc_numeric value;
-                gnc_numeric tax;
+                /* User finished the transfer dialog successfully */
 
-                /* Note some twisted logic here:
-                 * We ask the exchange rate
-                 *  FROM invoice currency
-                 *  TO other account currency
-                 *  Because that's what happens logically.
-                 *  But the internal posting logic works backwards:
-                 *  It searches for an exchange rate
-                 *  FROM other account currency
-                 *  TO invoice currency
-                 *  So we will store the inverted exchange rate
-                 */
+                /* Invert the exchange rate as explained above */
+                if (!gnc_numeric_zero_p (exch_rate))
+                    exch_rate = gnc_numeric_div ((gnc_numeric){1, 1}, exch_rate,
+                GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
+                convprice = gnc_price_create(iw->book);
+                gnc_price_begin_edit (convprice);
+                gnc_price_set_commodity (convprice, account_currency);
+                gnc_price_set_currency (convprice, gncInvoiceGetCurrency (invoice));
+                date.tv_sec = gnc_time (NULL);
+                date.tv_nsec = 0;
+                gnc_price_set_time (convprice, date);
+                gnc_price_set_source (convprice, "user:invoice-post");
 
-                /* Obtain the Entry's total value (net + tax) */
-                value = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn);
-                tax   = gncEntryGetDocTaxValue (entry, FALSE, is_cust_doc, is_cn);
-                amount = gnc_numeric_add (value, tax,
-                                          gnc_commodity_get_fraction (account_currency),
-                                          GNC_HOW_RND_ROUND_HALF_UP );
-
-                /* create the exchange-rate dialog */
-                xfer = gnc_xfer_dialog (iw_get_window(iw), acc);
-                gnc_xfer_dialog_select_to_account(xfer, this_acc);
-                gnc_xfer_dialog_set_amount(xfer, amount);
-
-                /* All we want is the exchange rate so prevent the user from thinking
-                   it makes sense to mess with other stuff */
-                gnc_xfer_dialog_set_from_show_button_active(xfer, FALSE);
-                gnc_xfer_dialog_set_to_show_button_active(xfer, FALSE);
-                gnc_xfer_dialog_hide_from_account_tree(xfer);
-                gnc_xfer_dialog_hide_to_account_tree(xfer);
-                gnc_xfer_dialog_is_exchange_dialog(xfer, &exch_rate);
-                if (gnc_xfer_dialog_run_until_done(xfer))
-                {
-                    /* User finished the transfer dialog successfully */
-
-                    /* Invert the exchange rate as explained above */
-                    if (!gnc_numeric_zero_p (exch_rate))
-                        exch_rate = gnc_numeric_div ((gnc_numeric)
-                    {
-                        1, 1
-                    }, exch_rate,
-                    GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
-                    convprice = gnc_price_create(iw->book);
-                    gnc_price_begin_edit (convprice);
-                    gnc_price_set_commodity (convprice, account_currency);
-                    gnc_price_set_currency (convprice, gncInvoiceGetCurrency (invoice));
-                    date.tv_sec = gnc_time (NULL);
-                    date.tv_nsec = 0;
-                    gnc_price_set_time (convprice, date);
-                    gnc_price_set_source (convprice, "user:invoice-post");
-
-                    /* Yes, magic strings are evil but I can't find any defined constants
-                       for this..*/
-                    gnc_price_set_typestr (convprice, "last");
-                    gnc_price_set_value (convprice, exch_rate);
-                    gncInvoiceAddPrice(invoice, convprice);
-                    gnc_price_commit_edit (convprice);
-                }
-                else
-                {
-                    /* User canceled the transfer dialog, abort posting */
-                    post_ok = FALSE;
-                    goto cleanup;
-                }
+                /* Yes, magic strings are evil but I can't find any defined constants
+                   for this..*/
+                gnc_price_set_typestr (convprice, "last");
+                gnc_price_set_value (convprice, exch_rate);
+                gncInvoiceAddPrice(invoice, convprice);
+                gnc_price_commit_edit (convprice);
             }
+            else
+            {
+                /* User canceled the transfer dialog, abort posting */
+                post_ok = FALSE;
+                goto cleanup;
+            }
         }
     }
 
@@ -943,6 +926,7 @@
 
 cleanup:
     gncInvoiceCommitEdit (invoice);
+    g_hash_table_unref (foreign_currs);
     gnc_resume_gui_refresh ();
 
     if (memo)

Modified: gnucash/trunk/src/engine/gncInvoice.c
===================================================================
--- gnucash/trunk/src/engine/gncInvoice.c	2013-12-04 07:30:54 UTC (rev 23484)
+++ gnucash/trunk/src/engine/gncInvoice.c	2013-12-04 16:56:55 UTC (rev 23485)
@@ -1226,6 +1226,66 @@
     }
 }
 
+GHashTable *gncInvoiceGetForeignCurrencies (const GncInvoice *invoice)
+{
+    EntryList *entries_iter;
+    gboolean is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
+    gboolean is_cn = gncInvoiceGetIsCreditNote (invoice);
+    GHashTable *amt_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                                  NULL, g_free);
+
+    for (entries_iter = invoice->entries; entries_iter != NULL; entries_iter = g_list_next(entries_iter))
+    {
+        GncEntry *entry = (GncEntry*)entries_iter->data;
+        Account *this_acc;
+        gnc_commodity *account_currency;
+        AccountValueList *tt_amts = NULL, *tt_iter;
+
+        /* Check entry's account currency */
+        this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) :
+                    gncEntryGetBillAccount (entry));
+        account_currency = xaccAccountGetCommodity (this_acc);
+
+        if (this_acc &&
+                !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), account_currency))
+        {
+            gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, account_currency);
+            gnc_numeric *entry_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
+            *entry_amt = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn);
+            if (curr_amt)
+                *entry_amt = gnc_numeric_add (*entry_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
+            g_hash_table_insert (amt_hash, account_currency, entry_amt);
+        }
+
+        /* Check currencies of each account in the tax table linked
+         * to the current entry */
+        tt_amts = gncEntryGetDocTaxValues (entry, is_cust_doc, is_cn);
+
+        if (!tt_amts)
+            continue;
+
+        for (tt_iter = tt_amts; tt_iter != NULL; tt_iter = g_list_next(tt_iter))
+        {
+            GncAccountValue *tt_amt_val = (GncAccountValue*)tt_iter->data;
+            Account *tt_acc = tt_amt_val->account;
+            gnc_commodity *tt_acc_currency = xaccAccountGetCommodity (tt_acc);
+
+            if (tt_acc &&
+                    !gnc_commodity_equal (gncInvoiceGetCurrency (invoice), tt_acc_currency))
+            {
+                gnc_numeric *curr_amt = (gnc_numeric*) g_hash_table_lookup (amt_hash, tt_acc_currency);
+                gnc_numeric *tt_acc_amt = (gnc_numeric*) g_new0 (gnc_numeric, 1);
+                *tt_acc_amt = tt_amt_val->value;
+                if (curr_amt)
+                    *tt_acc_amt = gnc_numeric_add (*tt_acc_amt, *curr_amt, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
+                g_hash_table_insert (amt_hash, tt_acc_currency, tt_acc_amt);
+            }
+        }
+        gncAccountValueDestroy (tt_amts);
+    }
+    return amt_hash;
+}
+
 static gboolean gncInvoicePostAddSplit (QofBook *book,
                                         Account *acc,
                                         Transaction *txn,

Modified: gnucash/trunk/src/engine/gncInvoice.h
===================================================================
--- gnucash/trunk/src/engine/gncInvoice.h	2013-12-04 07:30:54 UTC (rev 23484)
+++ gnucash/trunk/src/engine/gncInvoice.h	2013-12-04 16:56:55 UTC (rev 23485)
@@ -41,6 +41,7 @@
 typedef struct _gncInvoiceClass GncInvoiceClass;
 typedef GList GncInvoiceList;
 
+#include <glib.h>
 #include "gncBillTerm.h"
 #include "gncEntry.h"
 #include "gncOwner.h"
@@ -178,6 +179,17 @@
  */
 gboolean gncInvoiceAmountPositive (const GncInvoice *invoice);
 
+/** Return an overview of amounts on this invoice that will be posted to
+ *  accounts in currencies that are different from the invoice currency.
+ *  These accounts can be the accounts referred to in invoice entries
+ *  or tax tables. This information is returned in the from of a hash
+ *  table. The keys in the hash table are the foreign currencies, the
+ *  values are the accumulated amounts in that currency.
+ *  Drop the reference to the hash table with g_hash_table_unref when
+ *  no longer needed.
+ */
+GHashTable *gncInvoiceGetForeignCurrencies (const GncInvoice *invoice);
+
 /** Post this invoice to an account.  Returns the new Transaction
  * that is tied to this invoice.   The transaction is set with
  * the supplied posted date, due date, and memo.  The Transaction



More information about the gnucash-changes mailing list