Add support for mixed currency for invoice payment

This patch extends the payment dialog to allow paying foreign-currency
AP with local currency, or local-currency AP with foreign currency (I
don't know if there's a use-case for the latter, but it would have been
harder to code to NOT support it).

Patch by Jamie Campbell.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@17710 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Christian Stimming 2008-11-20 17:00:53 +00:00
parent 1de131ed1b
commit 294176f40d
5 changed files with 112 additions and 31 deletions

View File

@ -1282,7 +1282,7 @@ gnc_lot_sort_func (GNCLot *a, GNCLot *b)
Transaction * Transaction *
gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice, gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice,
Account *posted_acc, Account *xfer_acc, Account *posted_acc, Account *xfer_acc,
gnc_numeric amount, Timespec date, gnc_numeric amount, gnc_numeric exch, Timespec date,
const char *memo, const char *num) const char *memo, const char *num)
{ {
QofBook *book; QofBook *book;
@ -1296,6 +1296,7 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice,
gnc_commodity *commodity; gnc_commodity *commodity;
gnc_numeric split_amt; gnc_numeric split_amt;
gboolean reverse, inv_passed = TRUE; gboolean reverse, inv_passed = TRUE;
gnc_numeric payment_value=amount;
/* Verify our arguments */ /* Verify our arguments */
if (!owner || !posted_acc || !xfer_acc) return NULL; if (!owner || !posted_acc || !xfer_acc) return NULL;
@ -1318,6 +1319,7 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice,
xaccTransSetDatePostedTS (txn, &date); xaccTransSetDatePostedTS (txn, &date);
xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT); xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
/* The split for the transfer account */ /* The split for the transfer account */
split = xaccMallocSplit (book); split = xaccMallocSplit (book);
xaccSplitSetMemo (split, memo); xaccSplitSetMemo (split, memo);
@ -1326,8 +1328,20 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice,
xaccAccountInsertSplit (xfer_acc, split); xaccAccountInsertSplit (xfer_acc, split);
xaccAccountCommitEdit (xfer_acc); xaccAccountCommitEdit (xfer_acc);
xaccTransAppendSplit (txn, split); xaccTransAppendSplit (txn, split);
if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity))
{
xaccSplitSetBaseValue (split, reverse ? amount : xaccSplitSetBaseValue (split, reverse ? amount :
gnc_numeric_neg (amount), commodity); gnc_numeric_neg (amount), commodity);
}
else
{
/* Need to value the payment in terms of the owner commodity */
xaccSplitSetAmount(split, reverse ? amount : gnc_numeric_neg (amount));
payment_value = gnc_numeric_mul(amount, exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
xaccSplitSetValue(split, reverse ? payment_value : gnc_numeric_neg(payment_value));
}
/* Now, find all "open" lots in the posting account for this /* Now, find all "open" lots in the posting account for this
* company and apply the payment on a FIFO basis. Create * company and apply the payment on a FIFO basis. Create
@ -1397,20 +1411,20 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice,
} }
/* /*
* If the amount <= the balance; we're done -- apply the amount. * If the payment_value <= the balance; we're done -- apply the payment_value.
* Otherwise, apply the balance, subtract that from the amount, * Otherwise, apply the balance, subtract that from the payment_value,
* and move on to the next one. * and move on to the next one.
*/ */
if (gnc_numeric_compare (amount, balance) <= 0) { if (gnc_numeric_compare (payment_value, balance) <= 0) {
/* amount <= balance */ /* payment_value <= balance */
split_amt = amount; split_amt = payment_value;
} else { } else {
/* amount > balance */ /* payment_value > balance */
split_amt = balance; split_amt = balance;
} }
/* reduce the amount by split_amt */ /* reduce the payment_value by split_amt */
amount = gnc_numeric_sub (amount, split_amt, GNC_DENOM_AUTO, GNC_DENOM_LCD); payment_value = gnc_numeric_sub (payment_value, split_amt, GNC_DENOM_AUTO, GNC_DENOM_LCD);
/* Create the split for this lot in the post account */ /* Create the split for this lot in the post account */
split = xaccMallocSplit (book); split = xaccMallocSplit (book);
@ -1427,14 +1441,14 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice,
if (this_invoice) if (this_invoice)
qof_event_gen (&this_invoice->inst, QOF_EVENT_MODIFY, NULL); qof_event_gen (&this_invoice->inst, QOF_EVENT_MODIFY, NULL);
if (gnc_numeric_zero_p (amount)) if (gnc_numeric_zero_p (payment_value))
break; break;
} }
g_list_free (fifo); g_list_free (fifo);
/* If there is still money left here, then create a pre-payment lot */ /* If there is still money left here, then create a pre-payment lot */
if (gnc_numeric_positive_p (amount)) { if (gnc_numeric_positive_p (payment_value)) {
if (prepay_lot == NULL) { if (prepay_lot == NULL) {
prepay_lot = gnc_lot_new (book); prepay_lot = gnc_lot_new (book);
gncOwnerAttachToLot (owner, prepay_lot); gncOwnerAttachToLot (owner, prepay_lot);
@ -1445,8 +1459,8 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice,
xaccSplitSetAction (split, _("Pre-Payment")); xaccSplitSetAction (split, _("Pre-Payment"));
xaccAccountInsertSplit (posted_acc, split); xaccAccountInsertSplit (posted_acc, split);
xaccTransAppendSplit (txn, split); xaccTransAppendSplit (txn, split);
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) : xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (payment_value) :
amount, commodity); payment_value, commodity);
gnc_lot_add_split (prepay_lot, split); gnc_lot_add_split (prepay_lot, split);
} }

View File

@ -160,7 +160,7 @@ gncInvoiceUnpost (GncInvoice *invoice, gboolean reset_tax_tables);
Transaction * Transaction *
gncOwnerApplyPayment (GncOwner *owner, GncInvoice *invoice, gncOwnerApplyPayment (GncOwner *owner, GncInvoice *invoice,
Account *posted_acc, Account *xfer_acc, Account *posted_acc, Account *xfer_acc,
gnc_numeric amount, Timespec date, gnc_numeric amount, gnc_numeric exch, Timespec date,
const char *memo, const char *num); const char *memo, const char *num);

View File

@ -49,6 +49,8 @@
#include "dialog-employee.h" #include "dialog-employee.h"
#include "dialog-invoice.h" #include "dialog-invoice.h"
#include "gnc-commodity.h"
typedef enum { typedef enum {
GNCSEARCH_TYPE_SELECT, GNCSEARCH_TYPE_SELECT,
GNCSEARCH_TYPE_EDIT GNCSEARCH_TYPE_EDIT
@ -303,7 +305,7 @@ void gnc_invoice_set_owner (GtkWidget *widget, GncOwner *owner)
void void
gnc_fill_account_select_combo (GtkWidget *combo, GNCBook *book, gnc_fill_account_select_combo (GtkWidget *combo, GNCBook *book,
GList *acct_types) GList *acct_types, GList *acct_commodities)
{ {
GtkListStore *store; GtkListStore *store;
GtkEntry *entry; GtkEntry *entry;
@ -335,6 +337,17 @@ gnc_fill_account_select_combo (GtkWidget *combo, GNCBook *book,
== -1) == -1)
continue; continue;
/* Only present accounts with the right commodity, if that's a
restriction */
if (acct_commodities)
{
if ( g_list_find_custom( acct_commodities,
GINT_TO_POINTER(xaccAccountGetCommodity(account)),
gnc_commodity_compare) == NULL ) {
continue;
}
}
name = xaccAccountGetFullName (account); name = xaccAccountGetFullName (account);
gtk_combo_box_append_text(GTK_COMBO_BOX(combo), name); gtk_combo_box_append_text(GTK_COMBO_BOX(combo), name);
g_free(name); g_free(name);
@ -370,6 +383,7 @@ GList *
gnc_business_commodities (GncOwner *owner) gnc_business_commodities (GncOwner *owner)
{ {
g_return_val_if_fail (owner, NULL); g_return_val_if_fail (owner, NULL);
g_return_val_if_fail (gncOwnerGetCurrency(owner), NULL);
return (g_list_prepend (NULL, gncOwnerGetCurrency(owner))); return (g_list_prepend (NULL, gncOwnerGetCurrency(owner)));
} }

View File

@ -67,7 +67,8 @@ GList * gnc_business_commodities (GncOwner *owner);
/* Fill in a combo box with the appropriate list of accounts */ /* Fill in a combo box with the appropriate list of accounts */
void gnc_fill_account_select_combo (GtkWidget *combo, GNCBook *book, void gnc_fill_account_select_combo (GtkWidget *combo, GNCBook *book,
GList *acct_types); GList *acct_types,
GList *acct_commodities);
/* Create an optionmenu of available billing terms and attach it to /* Create an optionmenu of available billing terms and attach it to

View File

@ -45,6 +45,8 @@
#include "dialog-payment.h" #include "dialog-payment.h"
#include "business-gnome-utils.h" #include "business-gnome-utils.h"
#include "dialog-transfer.h"
#define DIALOG_PAYMENT_CUSTOMER_CM_CLASS "customer-payment-dialog" #define DIALOG_PAYMENT_CUSTOMER_CM_CLASS "customer-payment-dialog"
#define DIALOG_PAYMENT_VENDOR_CM_CLASS "vendor-payment-dialog" #define DIALOG_PAYMENT_VENDOR_CM_CLASS "vendor-payment-dialog"
@ -65,6 +67,7 @@ struct _payment_window {
GncOwner owner; GncOwner owner;
GncInvoice * invoice; GncInvoice * invoice;
GList * acct_types; GList * acct_types;
GList * acct_commodities;
}; };
@ -78,7 +81,7 @@ gnc_payment_window_refresh_handler (GHashTable *changes, gpointer data)
{ {
PaymentWindow *pw = data; PaymentWindow *pw = data;
gnc_fill_account_select_combo (pw->post_combo, pw->book, pw->acct_types); gnc_fill_account_select_combo (pw->post_combo, pw->book, pw->acct_types, pw->acct_commodities);
} }
static void static void
@ -110,8 +113,8 @@ gnc_payment_dialog_invoice_changed(PaymentWindow *pw)
static void static void
gnc_payment_dialog_owner_changed(PaymentWindow *pw) gnc_payment_dialog_owner_changed(PaymentWindow *pw)
{ {
Account *last_acct; Account *last_acct=NULL;
GUID *guid; GUID *guid=NULL;
KvpValue* value; KvpValue* value;
KvpFrame* slots; KvpFrame* slots;
@ -129,15 +132,36 @@ gnc_payment_dialog_owner_changed(PaymentWindow *pw)
/* Now handle the account tree */ /* Now handle the account tree */
slots = gncOwnerGetSlots(&pw->owner); slots = gncOwnerGetSlots(&pw->owner);
if (!slots) return; if (slots)
{
value = kvp_frame_get_slot_path(slots, "payment", "last_acct", NULL); value = kvp_frame_get_slot_path(slots, "payment", "last_acct", NULL);
if (!value) return; if (value)
{
guid = kvp_value_get_guid(value); guid = kvp_value_get_guid(value);
if (!guid) return; }
}
/* refresh the post and acc available accounts, but cleanup first */
if (pw->acct_types)
{
g_list_free(pw->acct_types);
pw->acct_types = NULL;
}
if (pw->acct_commodities)
{
g_list_free(pw->acct_commodities);
pw->acct_commodities = NULL;
}
pw->acct_types = gnc_business_account_types(&pw->owner);
pw->acct_commodities = gnc_business_commodities (&pw->owner);
gnc_fill_account_select_combo (pw->post_combo, pw->book, pw->acct_types, pw->acct_commodities);
if (guid)
{
last_acct = xaccAccountLookup(guid, pw->book); last_acct = xaccAccountLookup(guid, pw->book);
}
/* Set the last-used transfer account */ /* Set the last-used transfer account */
if (last_acct) { if (last_acct) {
@ -271,15 +295,41 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
{ {
const char *memo, *num; const char *memo, *num;
Timespec date; Timespec date;
gnc_numeric exch = gnc_numeric_create(1,1); //default to "one to one" rate
/* Obtain all our ancillary information */ /* Obtain all our ancillary information */
memo = gtk_entry_get_text (GTK_ENTRY (pw->memo_entry)); memo = gtk_entry_get_text (GTK_ENTRY (pw->memo_entry));
num = gtk_entry_get_text (GTK_ENTRY (pw->num_entry)); num = gtk_entry_get_text (GTK_ENTRY (pw->num_entry));
date = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (pw->date_edit)); date = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (pw->date_edit));
/* If the 'acc' account and the post account don't have the same
currency, we need to get the user to specify the exchange rate */
if (!gnc_commodity_equal(xaccAccountGetCommodity(acc), xaccAccountGetCommodity(post)))
{
XferDialog* xfer;
text = _("The transfer and post accounts are associated with different currencies. Please specify the conversion rate.");
xfer = gnc_xfer_dialog(pw->dialog, acc);
gnc_info_dialog(pw->dialog, "%s", text);
gnc_xfer_dialog_select_to_account(xfer,post);
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);
gnc_xfer_dialog_run_until_done(xfer);
}
/* Now apply the payment */ /* Now apply the payment */
gncOwnerApplyPayment (&pw->owner, pw->invoice, gncOwnerApplyPayment (&pw->owner, pw->invoice,
post, acc, amount, date, memo, num); post, acc, amount, exch, date, memo, num);
} }
gnc_resume_gui_refresh (); gnc_resume_gui_refresh ();
@ -306,6 +356,7 @@ gnc_payment_window_destroy_cb (GtkWidget *widget, gpointer data)
gnc_unregister_gui_component (pw->component_id); gnc_unregister_gui_component (pw->component_id);
g_list_free (pw->acct_types); g_list_free (pw->acct_types);
g_list_free (pw->acct_commodities);
g_free (pw); g_free (pw);
} }
@ -377,6 +428,8 @@ new_payment_window (GncOwner *owner, GNCBook *book, GncInvoice *invoice)
/* Compute the post-to account types */ /* Compute the post-to account types */
pw->acct_types = gnc_business_account_types (owner); pw->acct_types = gnc_business_account_types (owner);
pw->acct_commodities = gnc_business_commodities (owner);
/* Open and read the XML */ /* Open and read the XML */
xml = gnc_glade_xml_new ("payment.glade", "Payment Dialog"); xml = gnc_glade_xml_new ("payment.glade", "Payment Dialog");
pw->dialog = glade_xml_get_widget (xml, "Payment Dialog"); pw->dialog = glade_xml_get_widget (xml, "Payment Dialog");
@ -444,8 +497,7 @@ new_payment_window (GncOwner *owner, GNCBook *book, GncInvoice *invoice)
QOF_EVENT_DESTROY); QOF_EVENT_DESTROY);
/* Fill in the post_combo and account_tree widgets */ /* Fill in the post_combo and account_tree widgets */
gnc_fill_account_select_combo (pw->post_combo, pw->book, pw->acct_types); gnc_fill_account_select_combo (pw->post_combo, pw->book, pw->acct_types, pw->acct_commodities);
/* Show it all */ /* Show it all */
gtk_widget_show_all (pw->dialog); gtk_widget_show_all (pw->dialog);