mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Multicurrency business features: Handle invoice/bill line items
This patch is the last in the multicurrency set : it deals with conversions where individual invoice/bill line item accounts are different from the "owner" currency (aka, the invoice/bill currency). The original plan was to also have a checkbox to allow the user to use an account that isn't usually in the owner currency, in the owner currency, but that's more ornate and requires more time and code than I have time for right now, so, it's a feature for future :) I tested out "invoice currency matches the default books currency and has some accounts that use other currencies" and "invoice currency does not match default currency and has some stuff that does and some stuff that doesn't".. it could probably stand to also be tested by some other folks, but what I tried worked :) Patch by Jamie Campbell. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@17719 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
e82a3a4b41
commit
6346583aaf
@ -54,6 +54,7 @@ struct _gncInvoice
|
||||
char *printname;
|
||||
GncBillTerm *terms;
|
||||
GList *entries;
|
||||
GList *prices;
|
||||
GncOwner owner;
|
||||
GncOwner billto;
|
||||
GncJob *job;
|
||||
@ -164,6 +165,7 @@ static void gncInvoiceFree (GncInvoice *invoice)
|
||||
CACHE_REMOVE (invoice->notes);
|
||||
CACHE_REMOVE (invoice->billing_id);
|
||||
g_list_free (invoice->entries);
|
||||
g_list_free (invoice->prices);
|
||||
|
||||
if (invoice->printname) g_free (invoice->printname);
|
||||
|
||||
@ -212,6 +214,14 @@ gncCloneInvoice (GncInvoice *from, QofBook *book)
|
||||
invoice->entries = g_list_prepend (invoice->entries, entry);
|
||||
}
|
||||
|
||||
invoice->prices = NULL;
|
||||
for (node = g_list_last(from->prices); node; node=node->next)
|
||||
{
|
||||
GNCPrice *price = node->data;
|
||||
price = gnc_price_clone(price, book);
|
||||
invoice->prices = g_list_prepend (invoice->prices, price);
|
||||
}
|
||||
|
||||
/* XXX should probably be obtain-twin not lookup-twin */
|
||||
invoice->posted_acc =
|
||||
GNC_ACCOUNT(qof_instance_lookup_twin(QOF_INSTANCE(from->posted_acc), book));
|
||||
@ -433,6 +443,22 @@ void gncInvoiceRemoveEntry (GncInvoice *invoice, GncEntry *entry)
|
||||
mark_invoice (invoice);
|
||||
}
|
||||
|
||||
void gncInvoiceAddPrice (GncInvoice *invoice, GNCPrice *price)
|
||||
{
|
||||
if (!invoice || !price) return;
|
||||
|
||||
invoice->prices = g_list_prepend(invoice->prices, price);
|
||||
mark_invoice (invoice);
|
||||
}
|
||||
|
||||
void gncInvoiceRemovePrice (GncInvoice *invoice, GNCPrice *price)
|
||||
{
|
||||
if (!invoice || !price) return;
|
||||
|
||||
invoice->prices = g_list_remove (invoice->prices, price);
|
||||
mark_invoice (invoice);
|
||||
}
|
||||
|
||||
void gncBillAddEntry (GncInvoice *bill, GncEntry *entry)
|
||||
{
|
||||
GncInvoice *old;
|
||||
@ -458,6 +484,23 @@ void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry)
|
||||
mark_invoice (bill);
|
||||
}
|
||||
|
||||
void gncBillAddPrice (GncInvoice *bill, GNCPrice *price)
|
||||
{
|
||||
if (!bill || !price) return;
|
||||
|
||||
bill->prices = g_list_prepend(bill->prices, price);
|
||||
mark_invoice (bill);
|
||||
}
|
||||
|
||||
void gncBillRemovePrice (GncInvoice *bill, GNCPrice *price)
|
||||
{
|
||||
if (!bill || !price) return;
|
||||
|
||||
bill->prices = g_list_remove (bill->prices, price);
|
||||
mark_invoice (bill);
|
||||
}
|
||||
|
||||
|
||||
void gncInvoiceSortEntries (GncInvoice *invoice)
|
||||
{
|
||||
if (!invoice) return;
|
||||
@ -543,7 +586,7 @@ const char * gncInvoiceGetNotes (const GncInvoice *invoice)
|
||||
return invoice->notes;
|
||||
}
|
||||
|
||||
static GncOwnerType gncInvoiceGetOwnerType (GncInvoice *invoice)
|
||||
GncOwnerType gncInvoiceGetOwnerType (GncInvoice *invoice)
|
||||
{
|
||||
GncOwner *owner;
|
||||
g_return_val_if_fail (invoice, GNC_OWNER_NONE);
|
||||
@ -678,6 +721,29 @@ EntryList * gncInvoiceGetEntries (GncInvoice *invoice)
|
||||
return invoice->entries;
|
||||
}
|
||||
|
||||
GList * gncInvoiceGetPrices(GncInvoice *invoice)
|
||||
{
|
||||
if (!invoice) return NULL;
|
||||
return invoice->prices;
|
||||
}
|
||||
|
||||
GNCPrice * gncInvoiceGetPrice(GncInvoice *invoice, gnc_commodity *commodity)
|
||||
{
|
||||
GList *node=g_list_first(invoice->prices);
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
GNCPrice *curr = (GNCPrice*)node->data;
|
||||
|
||||
if (gnc_commodity_equal(commodity, gnc_price_get_commodity(curr)))
|
||||
return curr;
|
||||
|
||||
node = g_list_next(node);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static QofCollection*
|
||||
qofInvoiceGetEntries (GncInvoice *invoice)
|
||||
{
|
||||
@ -988,9 +1054,35 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
|
||||
xaccAccountInsertSplit (this_acc, split);
|
||||
xaccAccountCommitEdit (this_acc);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
xaccSplitSetBaseValue (split, (reverse ? gnc_numeric_neg (value)
|
||||
|
||||
if (gnc_commodity_equal(xaccAccountGetCommodity(this_acc), invoice->currency))
|
||||
{
|
||||
xaccSplitSetBaseValue (split, (reverse ? gnc_numeric_neg (value)
|
||||
: value),
|
||||
invoice->currency);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*need to do conversion */
|
||||
GNCPrice *price = gncInvoiceGetPrice(invoice, xaccAccountGetCommodity(this_acc));
|
||||
|
||||
if (price == NULL)
|
||||
{
|
||||
/*This is an error, which shouldn't even be able to happen.
|
||||
We can't really do anything sensible about it, and this is
|
||||
a user-interface free zone so we can't try asking the user
|
||||
again either, have to return NULL*/
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
gnc_numeric converted_amount;
|
||||
xaccSplitSetValue(split, (reverse ? gnc_numeric_neg(value): value));
|
||||
converted_amount = gnc_numeric_div(value, gnc_price_get_value(price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
|
||||
printf("converting from %f to %f\n", gnc_numeric_to_double(value), gnc_numeric_to_double(converted_amount));
|
||||
xaccSplitSetAmount(split, reverse ? gnc_numeric_neg(converted_amount): converted_amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is a credit-card account, and this is a CCard
|
||||
@ -1049,9 +1141,36 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
|
||||
xaccAccountInsertSplit (acc_val->account, split);
|
||||
xaccAccountCommitEdit (acc_val->account);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
xaccSplitSetBaseValue (split, (reverse ? gnc_numeric_neg (acc_val->value)
|
||||
: acc_val->value),
|
||||
invoice->currency);
|
||||
|
||||
if (gnc_commodity_equal(xaccAccountGetCommodity(acc_val->account), invoice->currency))
|
||||
{
|
||||
xaccSplitSetBaseValue (split, (reverse ? gnc_numeric_neg (acc_val->value)
|
||||
: acc_val->value),
|
||||
invoice->currency);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*need to do conversion */
|
||||
GNCPrice *price = gncInvoiceGetPrice(invoice, xaccAccountGetCommodity(acc_val->account));
|
||||
|
||||
if (price == NULL)
|
||||
{
|
||||
/*This is an error, which shouldn't even be able to happen.
|
||||
We can't really do anything sensible about it, and this is
|
||||
a user-interface free zone so we can't try asking the user
|
||||
again either, have to return NULL*/
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
gnc_numeric converted_amount;
|
||||
xaccSplitSetValue(split, (reverse ? gnc_numeric_neg(acc_val->value): acc_val->value));
|
||||
converted_amount = gnc_numeric_div(acc_val->value, gnc_price_get_value(price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND);
|
||||
printf("converting from %f to %f\n", gnc_numeric_to_double(acc_val->value), gnc_numeric_to_double(converted_amount));
|
||||
|
||||
xaccSplitSetAmount(split, reverse ? gnc_numeric_neg(converted_amount): converted_amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is a ccard account, we may have an additional "to_card" payment.
|
||||
|
@ -45,6 +45,8 @@ typedef struct _gncInvoiceClass GncInvoiceClass;
|
||||
#include "gncOwner.h"
|
||||
#include "gnc-lot.h"
|
||||
#include "qofbook.h"
|
||||
#include "qofbook.h"
|
||||
#include "gnc-pricedb.h"
|
||||
|
||||
#define GNC_ID_INVOICE "gncInvoice"
|
||||
|
||||
@ -85,10 +87,14 @@ void gncInvoiceSetToChargeAmount (GncInvoice *invoice, gnc_numeric amount);
|
||||
|
||||
void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry);
|
||||
void gncInvoiceRemoveEntry (GncInvoice *invoice, GncEntry *entry);
|
||||
void gncInvoiceAddPrice (GncInvoice *invoice, GNCPrice *price);
|
||||
void gncInvoiceRemovePrice (GncInvoice *invoice, GNCPrice *price);
|
||||
|
||||
/** Call this function when adding an entry to a bill instead of an invoice */
|
||||
void gncBillAddEntry (GncInvoice *bill, GncEntry *entry);
|
||||
void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry);
|
||||
void gncBillAddPrice (GncInvoice *bill, GNCPrice *price);
|
||||
void gncBillRemovePrice (GncInvoice *bill, GNCPrice *price);
|
||||
|
||||
/** Call this function when an Entry is changed and you want to
|
||||
re-sort the list of entries
|
||||
@ -105,6 +111,7 @@ Timespec gncInvoiceGetDateDue (const GncInvoice *invoice);
|
||||
GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice);
|
||||
const char * gncInvoiceGetBillingID (const GncInvoice *invoice);
|
||||
const char * gncInvoiceGetNotes (const GncInvoice *invoice);
|
||||
GncOwnerType gncInvoiceGetOwnerType (GncInvoice *invoice);
|
||||
const char * gncInvoiceGetType (GncInvoice *invoice);
|
||||
gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice);
|
||||
GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice);
|
||||
@ -124,6 +131,8 @@ gnc_numeric gncInvoiceGetTotalTax (GncInvoice *invoice);
|
||||
|
||||
typedef GList EntryList;
|
||||
EntryList * gncInvoiceGetEntries (GncInvoice *invoice);
|
||||
GList * gncInvoiceGetPrices(GncInvoice *invoice);
|
||||
GNCPrice * gncInvoiceGetPrice(GncInvoice *invoice, gnc_commodity* commodity);
|
||||
|
||||
/** Post this invoice to an account. Returns the new Transaction
|
||||
* that is tied to this invoice. The transaction is set with
|
||||
|
@ -71,6 +71,8 @@
|
||||
#include "gnc-plugin-page-invoice.h"
|
||||
#include "gnc-main-window.h"
|
||||
|
||||
#include "dialog-transfer.h"
|
||||
|
||||
/* Disable -Waddress. GCC 4.2 warns (and fails to compile with -Werror) when
|
||||
* passing the address of a guid on the stack to QOF_BOOK_LOOKUP_ENTITY via
|
||||
* gncInvoiceLookup and friends. When the macro gets inlined, the compiler
|
||||
@ -608,6 +610,11 @@ gnc_invoice_window_postCB (GtkWidget *widget, gpointer data)
|
||||
QofInstance *owner_inst;
|
||||
KvpFrame *kvpf;
|
||||
KvpValue *kvp_val;
|
||||
const char *text;
|
||||
EntryList *entries;
|
||||
GncEntry* entry;
|
||||
gboolean reverse;
|
||||
gboolean show_dialog=TRUE;
|
||||
|
||||
/* Make sure the invoice is ok */
|
||||
if (!gnc_invoice_window_verify_ok (iw))
|
||||
@ -625,6 +632,8 @@ gnc_invoice_window_postCB (GtkWidget *widget, gpointer data)
|
||||
return;
|
||||
}
|
||||
|
||||
reverse = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
|
||||
|
||||
/* Make sure that the invoice has a positive balance */
|
||||
if (gnc_numeric_negative_p(gncInvoiceGetTotal(invoice))) {
|
||||
gnc_error_dialog(iw_get_window(iw),
|
||||
@ -681,6 +690,70 @@ gnc_invoice_window_postCB (GtkWidget *widget, gpointer data)
|
||||
gncInvoiceBeginEdit (invoice);
|
||||
gnc_invoice_window_ok_save (iw);
|
||||
|
||||
/* 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.");
|
||||
|
||||
for (entries=gncInvoiceGetEntries(invoice); entries != NULL; entries=g_list_next(entries))
|
||||
{
|
||||
Account *this_acc;
|
||||
|
||||
entry = (GncEntry*)entries->data;
|
||||
this_acc = (reverse ? gncEntryGetInvAccount (entry) :
|
||||
gncEntryGetBillAccount (entry));
|
||||
|
||||
if (!gnc_commodity_equal(gncInvoiceGetCurrency (invoice), xaccAccountGetCommodity(this_acc)))
|
||||
{
|
||||
GNCPrice *convprice;
|
||||
|
||||
if (show_dialog)
|
||||
{
|
||||
gnc_info_dialog(iw_get_window(iw), "%s", text);
|
||||
show_dialog=FALSE;
|
||||
}
|
||||
|
||||
convprice = gncInvoiceGetPrice(invoice, xaccAccountGetCommodity(this_acc));
|
||||
if (convprice == NULL)
|
||||
{
|
||||
XferDialog *xfer;
|
||||
gnc_numeric exch_rate;
|
||||
Timespec date;
|
||||
gnc_numeric amount = gnc_numeric_create(1,1);
|
||||
|
||||
|
||||
/* create the exchange-rate dialog */
|
||||
xfer = gnc_xfer_dialog (iw_get_window(iw), this_acc);
|
||||
gnc_xfer_dialog_select_to_account(xfer,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);
|
||||
gnc_xfer_dialog_run_until_done(xfer);
|
||||
|
||||
convprice = gnc_price_create(iw->book);
|
||||
gnc_price_begin_edit (convprice);
|
||||
gnc_price_set_commodity (convprice, xaccAccountGetCommodity(this_acc));
|
||||
gnc_price_set_currency (convprice, gncInvoiceGetCurrency (invoice));
|
||||
date.tv_sec = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Save acc as last used account in the kvp frame of the invoice owner */
|
||||
kvp_val = kvp_value_new_guid (qof_instance_get_guid (QOF_INSTANCE (acc)));;
|
||||
qof_begin_edit (owner_inst);
|
||||
|
Loading…
Reference in New Issue
Block a user