gnucash maint: Cache current owner balances

Geert Janssens gjanssens at code.gnucash.org
Sun Sep 23 10:00:49 EDT 2018


Updated	 via  https://github.com/Gnucash/gnucash/commit/3991ccb9 (commit)
	from  https://github.com/Gnucash/gnucash/commit/40bcd1e3 (commit)



commit 3991ccb9c2383f94315ec647e4250a408bc16ad4
Author: Geert Janssens <geert at kobaltwit.be>
Date:   Sun Sep 23 15:59:27 2018 +0200

    Cache current owner balances
    
    These are queried continuously by the owner tree view (on Customer/Vendor/Employee
    Overview pages) and recalculating them is an expensive operation.
    The cache will be invalidated each time a lot reated to the owner
    changes (modify or delete). The net effect is a huge responsiveness
    improvement of said overviews in case of a large book.

diff --git a/libgnucash/engine/gncCustomer.c b/libgnucash/engine/gncCustomer.c
index 48b470f..6260b6c 100644
--- a/libgnucash/engine/gncCustomer.c
+++ b/libgnucash/engine/gncCustomer.c
@@ -46,9 +46,9 @@
 #include "gncJobP.h"
 #include "gncTaxTableP.h"
 
-static gint gs_address_event_handler_id = 0;
-static void listen_for_address_events(QofInstance *entity, QofEventId event_type,
-                                      gpointer user_data, gpointer event_data);
+static gint cust_qof_event_handler_id = 0;
+static void cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
+                                    gpointer user_data, gpointer event_data);
 
 struct _gncCustomer
 {
@@ -66,6 +66,7 @@ struct _gncCustomer
     GncTaxIncluded  taxincluded;
     gboolean        active;
     GList *         jobs;
+    gnc_numeric *   balance; /* cached customer balance, will not be stored */
 
     /* The following fields are unique to 'customer' */
     gnc_numeric     credit;
@@ -319,15 +320,14 @@ GncCustomer *gncCustomerCreate (QofBook *book)
     cust->taxincluded = GNC_TAXINCLUDED_USEGLOBAL;
     cust->active = TRUE;
     cust->jobs = NULL;
+    cust->balance = NULL;
 
     cust->discount = gnc_numeric_zero();
     cust->credit = gnc_numeric_zero();
     cust->shipaddr = gncAddressCreate (book, &cust->inst);
 
-    if (gs_address_event_handler_id == 0)
-    {
-        gs_address_event_handler_id = qof_event_register_handler(listen_for_address_events, NULL);
-    }
+    if (cust_qof_event_handler_id == 0)
+        cust_qof_event_handler_id = qof_event_register_handler (cust_handle_qof_events, NULL);
 
     qof_event_gen (&cust->inst, QOF_EVENT_CREATE, NULL);
 
@@ -356,6 +356,7 @@ static void gncCustomerFree (GncCustomer *cust)
     gncAddressBeginEdit (cust->shipaddr);
     gncAddressDestroy (cust->shipaddr);
     g_list_free (cust->jobs);
+    g_free (cust->balance);
 
     if (cust->terms)
         gncBillTermDecRef (cust->terms);
@@ -825,8 +826,11 @@ gncCustomerEqual(const GncCustomer *a, const GncCustomer *b)
 }
 
 /**
- * Listens for MODIFY events from addresses.   If the address belongs to a customer,
- * mark the customer as dirty.
+ * Listen for qof events.
+ *
+ * - If the address of a customer has changed, mark the customer as dirty.
+ * - If a lot related to a customer has changed, clear the customer's
+ *   cached balance as it likely has become invalid.
  *
  * @param entity Entity for the event
  * @param event_type Event type
@@ -834,28 +838,50 @@ gncCustomerEqual(const GncCustomer *a, const GncCustomer *b)
  * @param event_data Event data passed with the event.
  */
 static void
-listen_for_address_events(QofInstance *entity, QofEventId event_type,
-                          gpointer user_data, gpointer event_data)
+cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
+                        gpointer user_data, gpointer event_data)
 {
-    GncCustomer* cust;
-
-    if ((event_type & QOF_EVENT_MODIFY) == 0)
-    {
-        return;
-    }
-    if (!GNC_IS_ADDRESS(entity))
+    /* Handle address change events */
+    if ((GNC_IS_ADDRESS (entity) &&
+        (event_type & QOF_EVENT_MODIFY) != 0))
     {
+        if (GNC_IS_CUSTOMER (event_data))
+        {
+            GncCustomer* cust = GNC_CUSTOMER (event_data);
+            gncCustomerBeginEdit (cust);
+            mark_customer (cust);
+            gncCustomerCommitEdit (cust);
+        }
         return;
     }
-    if (!GNC_IS_CUSTOMER(event_data))
+
+    /* Handle lot change events */
+    if (GNC_IS_LOT (entity))
     {
+        GNCLot *lot = GNC_LOT (entity);
+        GncOwner lot_owner;
+        const GncOwner *end_owner = NULL;
+        GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
+
+        /* Determine the owner associated with the lot */
+        if (invoice)
+            /* Invoice lots */
+            end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
+        else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
+            /* Pre-payment lots */
+            end_owner = gncOwnerGetEndOwner (&lot_owner);
+
+        if (gncOwnerGetType (end_owner) == GNC_OWNER_CUSTOMER)
+        {
+            /* Clear the cached balance */
+            GncCustomer* cust = gncOwnerGetCustomer (end_owner);
+            g_free (cust->balance);
+            cust->balance = NULL;
+        }
         return;
     }
-    cust = GNC_CUSTOMER(event_data);
-    gncCustomerBeginEdit(cust);
-    mark_customer(cust);
-    gncCustomerCommitEdit(cust);
 }
+
 /* ============================================================== */
 /* Package-Private functions */
 static const char * _gncCustomerPrintable (gpointer item)
@@ -952,3 +978,24 @@ gchar *gncCustomerNextID (QofBook *book)
 {
     return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
 }
+
+const gnc_numeric*
+gncCustomerGetCachedBalance (GncCustomer *cust)
+{
+    return cust->balance;
+}
+
+void gncCustomerSetCachedBalance (GncCustomer *cust, const gnc_numeric *new_bal)
+{
+    if (!new_bal && cust->balance)
+    {
+        g_free (cust->balance);
+        cust->balance = NULL;
+        return;
+    }
+
+    if (!cust->balance)
+        cust->balance = g_new0 (gnc_numeric, 1);
+
+    *cust->balance = *new_bal;
+}
diff --git a/libgnucash/engine/gncCustomerP.h b/libgnucash/engine/gncCustomerP.h
index b0d65ca..7a12387 100644
--- a/libgnucash/engine/gncCustomerP.h
+++ b/libgnucash/engine/gncCustomerP.h
@@ -33,6 +33,8 @@
 
 gboolean gncCustomerRegister (void);
 gchar *gncCustomerNextID (QofBook *book);
+const gnc_numeric *gncCustomerGetCachedBalance (GncCustomer *cust);
+void gncCustomerSetCachedBalance (GncCustomer *cust, const gnc_numeric *new_bal);
 
 #define gncCustomerSetGUID(E,G) qof_instance_set_guid(QOF_INSTANCE(E),(G))
 
diff --git a/libgnucash/engine/gncEmployee.c b/libgnucash/engine/gncEmployee.c
index d992b0a..d398a30 100644
--- a/libgnucash/engine/gncEmployee.c
+++ b/libgnucash/engine/gncEmployee.c
@@ -37,10 +37,12 @@
 #include "gncAddressP.h"
 #include "gncEmployee.h"
 #include "gncEmployeeP.h"
+#include "gnc-lot.h"
+#include "gncOwner.h"
 
-static gint gs_address_event_handler_id = 0;
-static void listen_for_address_events(QofInstance *entity, QofEventId event_type,
-                                      gpointer user_data, gpointer event_data);
+static gint empl_qof_event_handler_id = 0;
+static void empl_handle_qof_events (QofInstance *entity, QofEventId event_type,
+                                    gpointer user_data, gpointer event_data);
 
 struct _gncEmployee
 {
@@ -50,6 +52,7 @@ struct _gncEmployee
     GncAddress *    addr;
     gnc_commodity * currency;
     gboolean        active;
+    gnc_numeric *   balance;
 
     char *          language;
     char *          acl;
@@ -439,11 +442,10 @@ GncEmployee *gncEmployeeCreate (QofBook *book)
     employee->workday = gnc_numeric_zero();
     employee->rate = gnc_numeric_zero();
     employee->active = TRUE;
+    employee->balance = NULL;
 
-    if (gs_address_event_handler_id == 0)
-    {
-        gs_address_event_handler_id = qof_event_register_handler(listen_for_address_events, NULL);
-    }
+    if (empl_qof_event_handler_id == 0)
+        empl_qof_event_handler_id = qof_event_register_handler (empl_handle_qof_events, NULL);
 
     qof_event_gen (&employee->inst, QOF_EVENT_CREATE, NULL);
 
@@ -469,6 +471,7 @@ static void gncEmployeeFree (GncEmployee *employee)
     CACHE_REMOVE (employee->acl);
     gncAddressBeginEdit (employee->addr);
     gncAddressDestroy (employee->addr);
+    g_free (employee->balance);
 
     /* qof_instance_release (&employee->inst); */
     g_object_unref (employee);
@@ -816,8 +819,11 @@ static const char * _gncEmployeePrintable (gpointer item)
 }
 
 /**
- * Listens for MODIFY events from addresses.   If the address belongs to an employee,
- * mark the employee as dirty.
+ * Listen for qof events.
+ *
+ * - If the address of an employee has changed, mark the employee as dirty.
+ * - If a lot related to an employee has changed, clear the employee's
+ *   cached balance as it likely has become invalid.
  *
  * @param entity Entity for the event
  * @param event_type Event type
@@ -825,27 +831,49 @@ static const char * _gncEmployeePrintable (gpointer item)
  * @param event_data Event data passed with the event.
  */
 static void
-listen_for_address_events(QofInstance *entity, QofEventId event_type,
-                          gpointer user_data, gpointer event_data)
+empl_handle_qof_events (QofInstance *entity, QofEventId event_type,
+                        gpointer user_data, gpointer event_data)
 {
-    GncEmployee* empl;
 
-    if ((event_type & QOF_EVENT_MODIFY) == 0)
-    {
-        return;
-    }
-    if (!GNC_IS_ADDRESS(entity))
+    /* Handle address change events */
+    if ((GNC_IS_ADDRESS (entity) &&
+        (event_type & QOF_EVENT_MODIFY) != 0))
     {
+        if (GNC_IS_EMPLOYEE (event_data))
+        {
+            GncEmployee* empl = GNC_EMPLOYEE (event_data);
+            gncEmployeeBeginEdit (empl);
+            mark_employee (empl);
+            gncEmployeeCommitEdit (empl);
+        }
         return;
     }
-    if (!GNC_IS_EMPLOYEE(event_data))
+
+    /* Handle lot change events */
+    if (GNC_IS_LOT (entity))
     {
+        GNCLot *lot = GNC_LOT (entity);
+        GncOwner lot_owner;
+        const GncOwner *end_owner = NULL;
+        GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
+
+        /* Determine the owner associated with the lot */
+        if (invoice)
+            /* Invoice lots */
+            end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
+        else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
+            /* Pre-payment lots */
+            end_owner = gncOwnerGetEndOwner (&lot_owner);
+
+        if (gncOwnerGetType (end_owner) == GNC_OWNER_EMPLOYEE)
+        {
+            /* Clear the cached balance */
+            GncEmployee* empl = gncOwnerGetEmployee (end_owner);
+            g_free (empl->balance);
+            empl->balance = NULL;
+        }
         return;
     }
-    empl = GNC_EMPLOYEE(event_data);
-    gncEmployeeBeginEdit(empl);
-    mark_employee(empl);
-    gncEmployeeCommitEdit(empl);
 }
 
 static void
@@ -925,3 +953,24 @@ gchar *gncEmployeeNextID (QofBook *book)
 {
     return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
 }
+
+const gnc_numeric*
+gncEmployeeGetCachedBalance (GncEmployee *empl)
+{
+    return empl->balance;
+}
+
+void gncEmployeeSetCachedBalance (GncEmployee *empl, const gnc_numeric *new_bal)
+{
+    if (!new_bal && empl->balance)
+    {
+        g_free (empl->balance);
+        empl->balance = NULL;
+        return;
+    }
+
+    if (!empl->balance)
+        empl->balance = g_new0 (gnc_numeric, 1);
+
+    *empl->balance = *new_bal;
+}
diff --git a/libgnucash/engine/gncEmployeeP.h b/libgnucash/engine/gncEmployeeP.h
index c9a63a9..8dd6e47 100644
--- a/libgnucash/engine/gncEmployeeP.h
+++ b/libgnucash/engine/gncEmployeeP.h
@@ -33,6 +33,8 @@
 
 gboolean gncEmployeeRegister (void);
 gchar *gncEmployeeNextID (QofBook *book);
+const gnc_numeric *gncEmployeeGetCachedBalance (GncEmployee *vend);
+void gncEmployeeSetCachedBalance (GncEmployee *vend, const gnc_numeric *new_bal);
 
 #define gncEmployeeSetGUID(E,G) qof_instance_set_guid(QOF_INSTANCE(E),(G))
 
diff --git a/libgnucash/engine/gncInvoice.c b/libgnucash/engine/gncInvoice.c
index 3d5aae1..e725d6a 100644
--- a/libgnucash/engine/gncInvoice.c
+++ b/libgnucash/engine/gncInvoice.c
@@ -1483,6 +1483,7 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
 
     /* Create a new lot for this invoice */
     lot = gnc_lot_new (book);
+    gncInvoiceAttachToLot (invoice, lot);
     gnc_lot_begin_edit (lot);
 
     type = gncInvoiceGetTypeString (invoice);
@@ -1697,8 +1698,7 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
         gnc_lot_add_split (lot, split);
     }
 
-    /* Now attach this invoice to the txn, lot, and account */
-    gncInvoiceAttachToLot (invoice, lot);
+    /* Now attach this invoice to the txn and account */
     gncInvoiceAttachToTxn (invoice, txn);
     gncInvoiceSetPostedAcc (invoice, acc);
 
diff --git a/libgnucash/engine/gncOwner.c b/libgnucash/engine/gncOwner.c
index 5bea047..53e648e 100644
--- a/libgnucash/engine/gncOwner.c
+++ b/libgnucash/engine/gncOwner.c
@@ -1453,50 +1453,61 @@ gncOwnerGetBalanceInCurrency (const GncOwner *owner,
                               const gnc_commodity *report_currency)
 {
     gnc_numeric balance = gnc_numeric_zero ();
-    GList *acct_list, *acct_node, *acct_types, *lot_list = NULL, *lot_node;
     QofBook *book;
     gnc_commodity *owner_currency;
     GNCPriceDB *pdb;
+    const gnc_numeric *cached_balance = NULL;
 
     g_return_val_if_fail (owner, gnc_numeric_zero ());
 
-    /* Get account list */
     book       = qof_instance_get_book (qofOwnerGetOwner (owner));
-    acct_list  = gnc_account_get_descendants (gnc_book_get_root_account (book));
-    acct_types = gncOwnerGetAccountTypesList (owner);
     owner_currency = gncOwnerGetCurrency (owner);
 
-    /* For each account */
-    for (acct_node = acct_list; acct_node; acct_node = acct_node->next)
+    cached_balance = gncOwnerGetCachedBalance (owner);
+    if (cached_balance)
+        balance = *cached_balance;
+    else
     {
-        Account *account = acct_node->data;
+        /* No valid cache value found for balance. Let's recalculate */
+        GList *acct_list  = gnc_account_get_descendants (gnc_book_get_root_account (book));
+        GList *acct_types = gncOwnerGetAccountTypesList (owner);
+        GList *acct_node;
 
-        /* Check if this account can have lots for the owner, otherwise skip to next */
-        if (g_list_index (acct_types, (gpointer)xaccAccountGetType (account))
-                == -1)
-            continue;
+        /* For each account */
+        for (acct_node = acct_list; acct_node; acct_node = acct_node->next)
+        {
+            Account *account = acct_node->data;
+            GList *lot_list = NULL, *lot_node;
 
+            /* Check if this account can have lots for the owner, otherwise skip to next */
+            if (g_list_index (acct_types, (gpointer)xaccAccountGetType (account))
+                    == -1)
+                continue;
 
-        if (!gnc_commodity_equal (owner_currency, xaccAccountGetCommodity (account)))
-            continue;
 
-        /* Get a list of open lots for this owner and account */
-        lot_list = xaccAccountFindOpenLots (account, gncOwnerLotMatchOwnerFunc,
-                                            (gpointer)owner, NULL);
-        /* For each lot */
-        for (lot_node = lot_list; lot_node; lot_node = lot_node->next)
-        {
-            GNCLot *lot = lot_node->data;
-            gnc_numeric lot_balance = gnc_lot_get_balance (lot);
-            GncInvoice *invoice = gncInvoiceGetInvoiceFromLot(lot);
-            if (invoice)
-               balance = gnc_numeric_add (balance, lot_balance,
-                                          gnc_commodity_get_fraction (owner_currency), GNC_HOW_RND_ROUND_HALF_UP);
+            if (!gnc_commodity_equal (owner_currency, xaccAccountGetCommodity (account)))
+                continue;
+
+            /* Get a list of open lots for this owner and account */
+            lot_list = xaccAccountFindOpenLots (account, gncOwnerLotMatchOwnerFunc,
+                                                (gpointer)owner, NULL);
+            /* For each lot */
+            for (lot_node = lot_list; lot_node; lot_node = lot_node->next)
+            {
+                GNCLot *lot = lot_node->data;
+                gnc_numeric lot_balance = gnc_lot_get_balance (lot);
+                GncInvoice *invoice = gncInvoiceGetInvoiceFromLot(lot);
+                if (invoice)
+                balance = gnc_numeric_add (balance, lot_balance,
+                                            gnc_commodity_get_fraction (owner_currency), GNC_HOW_RND_ROUND_HALF_UP);
+            }
+            g_list_free (lot_list);
         }
-        g_list_free (lot_list);
+        g_list_free (acct_list);
+        g_list_free (acct_types);
+
+        gncOwnerSetCachedBalance (owner, &balance);
     }
-    g_list_free (acct_list);
-    g_list_free (acct_types);
 
     pdb = gnc_pricedb_get_db (book);
 
@@ -1587,3 +1598,30 @@ gboolean gncOwnerRegister (void)
 
     return TRUE;
 }
+
+const gnc_numeric*
+gncOwnerGetCachedBalance (const GncOwner *owner)
+{
+    if (!owner) return NULL;
+
+    if (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER)
+        return gncCustomerGetCachedBalance (gncOwnerGetCustomer (owner));
+    else if (gncOwnerGetType (owner) == GNC_OWNER_VENDOR)
+        return gncVendorGetCachedBalance (gncOwnerGetVendor (owner));
+    else if (gncOwnerGetType (owner) == GNC_OWNER_EMPLOYEE)
+        return gncEmployeeGetCachedBalance (gncOwnerGetEmployee (owner));
+
+    return NULL;
+}
+
+void gncOwnerSetCachedBalance (const GncOwner *owner, const gnc_numeric *new_bal)
+{
+    if (!owner) return;
+
+    if (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER)
+        gncCustomerSetCachedBalance (gncOwnerGetCustomer (owner), new_bal);
+    else if (gncOwnerGetType (owner) == GNC_OWNER_VENDOR)
+        gncVendorSetCachedBalance (gncOwnerGetVendor (owner), new_bal);
+    else if (gncOwnerGetType (owner) == GNC_OWNER_EMPLOYEE)
+        gncEmployeeSetCachedBalance (gncOwnerGetEmployee (owner), new_bal);
+}
diff --git a/libgnucash/engine/gncOwnerP.h b/libgnucash/engine/gncOwnerP.h
index 42bdd8e..7387485 100644
--- a/libgnucash/engine/gncOwnerP.h
+++ b/libgnucash/engine/gncOwnerP.h
@@ -31,6 +31,8 @@
 #include "gncOwner.h"
 
 gboolean gncOwnerRegister (void);
+const gnc_numeric *gncOwnerGetCachedBalance (const GncOwner *owner);
+void gncOwnerSetCachedBalance (const GncOwner *owner, const gnc_numeric *new_bal);
 
 
 #endif /* GNC_OWNERP_H_ */
diff --git a/libgnucash/engine/gncVendor.c b/libgnucash/engine/gncVendor.c
index cdfdd0a..19f75dd 100644
--- a/libgnucash/engine/gncVendor.c
+++ b/libgnucash/engine/gncVendor.c
@@ -41,9 +41,9 @@
 #include "gncVendor.h"
 #include "gncVendorP.h"
 
-static gint gs_address_event_handler_id = 0;
-static void listen_for_address_events(QofInstance *entity, QofEventId event_type,
-                                      gpointer user_data, gpointer event_data);
+static gint vend_qof_event_handler_id = 0;
+static void vend_handle_qof_events (QofInstance *entity, QofEventId event_type,
+                                    gpointer user_data, gpointer event_data);
 static void qofVendorSetAddr (GncVendor *vendor, QofInstance *addr_ent);
 static const char* qofVendorGetTaxIncluded(const GncVendor *vendor);
 static void qofVendorSetTaxIncluded(GncVendor *vendor, const char* type_string);
@@ -63,6 +63,7 @@ struct _gncVendor
     GncTaxIncluded  taxincluded;
     gboolean        active;
     GList *         jobs;
+    gnc_numeric *   balance; /* cached vendor balance, will not be stored */
 };
 
 struct _gncVendorClass
@@ -467,11 +468,10 @@ GncVendor *gncVendorCreate (QofBook *book)
     vendor->taxincluded = GNC_TAXINCLUDED_USEGLOBAL;
     vendor->active = TRUE;
     vendor->jobs = NULL;
+    vendor->balance = NULL;
 
-    if (gs_address_event_handler_id == 0)
-    {
-        gs_address_event_handler_id = qof_event_register_handler(listen_for_address_events, NULL);
-    }
+    if (vend_qof_event_handler_id == 0)
+        vend_qof_event_handler_id = qof_event_register_handler (vend_handle_qof_events, NULL);
 
     qof_event_gen (&vendor->inst, QOF_EVENT_CREATE, NULL);
 
@@ -497,6 +497,7 @@ static void gncVendorFree (GncVendor *vendor)
     gncAddressBeginEdit (vendor->addr);
     gncAddressDestroy (vendor->addr);
     g_list_free (vendor->jobs);
+    g_free (vendor->balance);
 
     if (vendor->terms)
         gncBillTermDecRef (vendor->terms);
@@ -882,8 +883,11 @@ gncVendorIsDirty (const GncVendor *vendor)
 }
 
 /**
- * Listens for MODIFY events from addresses.   If the address belongs to a vendor,
- * mark the vendor as dirty.
+ * Listen for qof events.
+ *
+ * - If the address of a vendor has changed, mark the vendor as dirty.
+ * - If a lot related to a vendor has changed, clear the vendor's
+ *   cached balance as it likely has become invalid.
  *
  * @param entity Entity for the event
  * @param event_type Event type
@@ -891,28 +895,50 @@ gncVendorIsDirty (const GncVendor *vendor)
  * @param event_data Event data passed with the event.
  */
 static void
-listen_for_address_events(QofInstance *entity, QofEventId event_type,
-                          gpointer user_data, gpointer event_data)
+vend_handle_qof_events (QofInstance *entity, QofEventId event_type,
+                        gpointer user_data, gpointer event_data)
 {
-    GncVendor* v;
-
-    if ((event_type & QOF_EVENT_MODIFY) == 0)
-    {
-        return;
-    }
-    if (!GNC_IS_ADDRESS(entity))
+    /* Handle address change events */
+    if ((GNC_IS_ADDRESS (entity) &&
+        (event_type & QOF_EVENT_MODIFY) != 0))
     {
+        if (GNC_IS_VENDOR (event_data))
+        {
+            GncVendor* vend = GNC_VENDOR (event_data);
+            gncVendorBeginEdit (vend);
+            mark_vendor (vend);
+            gncVendorCommitEdit (vend);
+        }
         return;
     }
-    if (!GNC_IS_VENDOR(event_data))
+
+    /* Handle lot change events */
+    if (GNC_IS_LOT (entity))
     {
+        GNCLot *lot = GNC_LOT (entity);
+        GncOwner lot_owner;
+        const GncOwner *end_owner = NULL;
+        GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
+
+        /* Determine the owner associated with the lot */
+        if (invoice)
+            /* Invoice lots */
+            end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
+        else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
+            /* Pre-payment lots */
+            end_owner = gncOwnerGetEndOwner (&lot_owner);
+
+        if (gncOwnerGetType (end_owner) == GNC_OWNER_VENDOR)
+        {
+            /* Clear the cached balance */
+            GncVendor* vend = gncOwnerGetVendor (end_owner);
+            g_free (vend->balance);
+            vend->balance = NULL;
+        }
         return;
     }
-    v = GNC_VENDOR(event_data);
-    gncVendorBeginEdit(v);
-    mark_vendor(v);
-    gncVendorCommitEdit(v);
 }
+
 /* ============================================================== */
 /* Package-Private functions */
 
@@ -1005,3 +1031,24 @@ gchar *gncVendorNextID (QofBook *book)
 {
     return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
 }
+
+const gnc_numeric*
+gncVendorGetCachedBalance (GncVendor *vend)
+{
+    return vend->balance;
+}
+
+void gncVendorSetCachedBalance (GncVendor *vend, const gnc_numeric *new_bal)
+{
+    if (!new_bal && vend->balance)
+    {
+        g_free (vend->balance);
+        vend->balance = NULL;
+        return;
+    }
+
+    if (!vend->balance)
+        vend->balance = g_new0 (gnc_numeric, 1);
+
+    *vend->balance = *new_bal;
+}
diff --git a/libgnucash/engine/gncVendorP.h b/libgnucash/engine/gncVendorP.h
index 7f4653f..66c183d 100644
--- a/libgnucash/engine/gncVendorP.h
+++ b/libgnucash/engine/gncVendorP.h
@@ -32,6 +32,8 @@
 
 gboolean gncVendorRegister (void);
 gchar *gncVendorNextID (QofBook *book);
+const gnc_numeric *gncVendorGetCachedBalance (GncVendor *vend);
+void gncVendorSetCachedBalance (GncVendor *vend, const gnc_numeric *new_bal);
 
 #define gncVendorSetGUID(V,G) qof_instance_set_guid(QOF_INSTANCE(V),(G))
 



Summary of changes:
 libgnucash/engine/gncCustomer.c  | 93 +++++++++++++++++++++++++++++----------
 libgnucash/engine/gncCustomerP.h |  2 +
 libgnucash/engine/gncEmployee.c  | 93 +++++++++++++++++++++++++++++----------
 libgnucash/engine/gncEmployeeP.h |  2 +
 libgnucash/engine/gncInvoice.c   |  4 +-
 libgnucash/engine/gncOwner.c     | 94 ++++++++++++++++++++++++++++------------
 libgnucash/engine/gncOwnerP.h    |  2 +
 libgnucash/engine/gncVendor.c    | 93 +++++++++++++++++++++++++++++----------
 libgnucash/engine/gncVendorP.h   |  2 +
 9 files changed, 287 insertions(+), 98 deletions(-)



More information about the gnucash-changes mailing list