mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
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:
parent
1de131ed1b
commit
294176f40d
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user