r17719 - gnucash/trunk/src/business - Multicurrency business features: Handle invoice/bill line items

Christian Stimming cstim at cvs.gnucash.org
Wed Nov 26 08:06:44 EST 2008


Author: cstim
Date: 2008-11-26 08:06:44 -0500 (Wed, 26 Nov 2008)
New Revision: 17719
Trac: http://svn.gnucash.org/trac/changeset/17719

Modified:
   gnucash/trunk/src/business/business-core/gncInvoice.c
   gnucash/trunk/src/business/business-core/gncInvoice.h
   gnucash/trunk/src/business/business-gnome/dialog-invoice.c
Log:
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.

Modified: gnucash/trunk/src/business/business-core/gncInvoice.c
===================================================================
--- gnucash/trunk/src/business/business-core/gncInvoice.c	2008-11-26 10:50:04 UTC (rev 17718)
+++ gnucash/trunk/src/business/business-core/gncInvoice.c	2008-11-26 13:06:44 UTC (rev 17719)
@@ -54,6 +54,7 @@
   char        *printname;
   GncBillTerm *terms;
   GList       *entries;
+  GList       *prices;
   GncOwner    owner;
   GncOwner    billto;
   GncJob      *job;
@@ -164,6 +165,7 @@
   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 @@
     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 @@
   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 @@
   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 @@
   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 @@
   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 @@
 	  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 @@
     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.

Modified: gnucash/trunk/src/business/business-core/gncInvoice.h
===================================================================
--- gnucash/trunk/src/business/business-core/gncInvoice.h	2008-11-26 10:50:04 UTC (rev 17718)
+++ gnucash/trunk/src/business/business-core/gncInvoice.h	2008-11-26 13:06:44 UTC (rev 17719)
@@ -45,6 +45,8 @@
 #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 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 @@
 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 @@
 
 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

Modified: gnucash/trunk/src/business/business-gnome/dialog-invoice.c
===================================================================
--- gnucash/trunk/src/business/business-gnome/dialog-invoice.c	2008-11-26 10:50:04 UTC (rev 17718)
+++ gnucash/trunk/src/business/business-gnome/dialog-invoice.c	2008-11-26 13:06:44 UTC (rev 17719)
@@ -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 @@
   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 @@
     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 @@
   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);



More information about the gnucash-changes mailing list