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