LCOV - code coverage report
Current view: top level - libgnucash/engine - gncOwner.c (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 36.6 % 809 296
Test Date: 2026-11-02 18:19:43 Functions: 56.7 % 60 34
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * gncOwner.c -- Business Interface:  Object OWNERs                 *
       3              :  *                                                                  *
       4              :  * This program is free software; you can redistribute it and/or    *
       5              :  * modify it under the terms of the GNU General Public License as   *
       6              :  * published by the Free Software Foundation; either version 2 of   *
       7              :  * the License, or (at your option) any later version.              *
       8              :  *                                                                  *
       9              :  * This program is distributed in the hope that it will be useful,  *
      10              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      11              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      12              :  * GNU General Public License for more details.                     *
      13              :  *                                                                  *
      14              :  * You should have received a copy of the GNU General Public License*
      15              :  * along with this program; if not, contact:                        *
      16              :  *                                                                  *
      17              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      18              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      19              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      20              :  *                                                                  *
      21              : \********************************************************************/
      22              : 
      23              : /*
      24              :  * Copyright (C) 2001, 2002 Derek Atkins
      25              :  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
      26              :  * Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk>
      27              :  * Copyright (c) 2006 David Hampton <hampton@employees.org>
      28              :  * Copyright (c) 2011 Geert Janssens <geert@kobaltwit.be>
      29              :  * Author: Derek Atkins <warlord@MIT.EDU>
      30              :  */
      31              : 
      32              : #include <config.h>
      33              : 
      34              : #include <glib.h>
      35              : #include <glib/gi18n.h>
      36              : #include <qofinstance-p.h>
      37              : 
      38              : #include "gncCustomerP.h"
      39              : #include "gncEmployeeP.h"
      40              : #include "gncJobP.h"
      41              : #include "gncOwner.h"
      42              : #include "gncOwnerP.h"
      43              : #include "gncVendorP.h"
      44              : #include "gncInvoice.h"
      45              : #include "gnc-commodity.h"
      46              : #include "Scrub2.h"
      47              : #include "Split.h"
      48              : #include "Transaction.h"
      49              : #include "engine-helpers.h"
      50              : 
      51              : #define _GNC_MOD_NAME   GNC_ID_OWNER
      52              : 
      53              : #define GNC_OWNER_ID    "gncOwner"
      54              : 
      55              : static QofLogModule log_module = GNC_MOD_ENGINE;
      56              : 
      57           82 : GncOwner * gncOwnerNew (void)
      58              : {
      59              :     GncOwner *o;
      60              : 
      61           82 :     o = g_new0 (GncOwner, 1);
      62           82 :     o->type = GNC_OWNER_NONE;
      63           82 :     return o;
      64              : }
      65              : 
      66           17 : void gncOwnerFree (GncOwner *owner)
      67              : {
      68           17 :     if (!owner) return;
      69           17 :     g_free (owner);
      70              : }
      71              : 
      72            0 : void gncOwnerBeginEdit (GncOwner *owner)
      73              : {
      74            0 :     if (!owner) return;
      75            0 :     switch (owner->type)
      76              :     {
      77            0 :     case GNC_OWNER_NONE :
      78              :     case GNC_OWNER_UNDEFINED :
      79            0 :         break;
      80            0 :     case GNC_OWNER_CUSTOMER :
      81              :     {
      82            0 :         gncCustomerBeginEdit(owner->owner.customer);
      83            0 :         break;
      84              :     }
      85            0 :     case GNC_OWNER_JOB :
      86              :     {
      87            0 :         gncJobBeginEdit(owner->owner.job);
      88            0 :         break;
      89              :     }
      90            0 :     case GNC_OWNER_VENDOR :
      91              :     {
      92            0 :         gncVendorBeginEdit(owner->owner.vendor);
      93            0 :         break;
      94              :     }
      95            0 :     case GNC_OWNER_EMPLOYEE :
      96              :     {
      97            0 :         gncEmployeeBeginEdit(owner->owner.employee);
      98            0 :         break;
      99              :     }
     100              :     }
     101              : }
     102              : 
     103            0 : void gncOwnerCommitEdit (GncOwner *owner)
     104              : {
     105            0 :     if (!owner) return;
     106            0 :     switch (owner->type)
     107              :     {
     108            0 :     case GNC_OWNER_NONE :
     109              :     case GNC_OWNER_UNDEFINED :
     110            0 :         break;
     111            0 :     case GNC_OWNER_CUSTOMER :
     112              :     {
     113            0 :         gncCustomerCommitEdit(owner->owner.customer);
     114            0 :         break;
     115              :     }
     116            0 :     case GNC_OWNER_JOB :
     117              :     {
     118            0 :         gncJobCommitEdit(owner->owner.job);
     119            0 :         break;
     120              :     }
     121            0 :     case GNC_OWNER_VENDOR :
     122              :     {
     123            0 :         gncVendorCommitEdit(owner->owner.vendor);
     124            0 :         break;
     125              :     }
     126            0 :     case GNC_OWNER_EMPLOYEE :
     127              :     {
     128            0 :         gncEmployeeCommitEdit(owner->owner.employee);
     129            0 :         break;
     130              :     }
     131              :     }
     132              : }
     133              : 
     134            0 : void gncOwnerDestroy (GncOwner *owner)
     135              : {
     136            0 :     if (!owner) return;
     137            0 :     switch (owner->type)
     138              :     {
     139            0 :     case GNC_OWNER_NONE :
     140              :     case GNC_OWNER_UNDEFINED :
     141            0 :         break;
     142            0 :     case GNC_OWNER_CUSTOMER :
     143              :     {
     144            0 :         gncCustomerDestroy(owner->owner.customer);
     145            0 :         break;
     146              :     }
     147            0 :     case GNC_OWNER_JOB :
     148              :     {
     149            0 :         gncJobDestroy(owner->owner.job);
     150            0 :         break;
     151              :     }
     152            0 :     case GNC_OWNER_VENDOR :
     153              :     {
     154            0 :         gncVendorDestroy(owner->owner.vendor);
     155            0 :         break;
     156              :     }
     157            0 :     case GNC_OWNER_EMPLOYEE :
     158              :     {
     159            0 :         gncEmployeeDestroy(owner->owner.employee);
     160            0 :         break;
     161              :     }
     162              :     }
     163              : }
     164              : 
     165            0 : void gncOwnerInitUndefined (GncOwner *owner, gpointer obj)
     166              : {
     167            0 :     if (!owner) return;
     168            0 :     owner->type = GNC_OWNER_UNDEFINED;
     169            0 :     owner->owner.undefined = obj;
     170              : }
     171              : 
     172          248 : void gncOwnerInitCustomer (GncOwner *owner, GncCustomer *customer)
     173              : {
     174          248 :     if (!owner) return;
     175          248 :     owner->type = GNC_OWNER_CUSTOMER;
     176          248 :     owner->owner.customer = customer;
     177              : }
     178              : 
     179           10 : void gncOwnerInitJob (GncOwner *owner, GncJob *job)
     180              : {
     181           10 :     if (!owner) return;
     182           10 :     owner->type = GNC_OWNER_JOB;
     183           10 :     owner->owner.job = job;
     184              : }
     185              : 
     186           13 : void gncOwnerInitVendor (GncOwner *owner, GncVendor *vendor)
     187              : {
     188           13 :     if (!owner) return;
     189           13 :     owner->type = GNC_OWNER_VENDOR;
     190           13 :     owner->owner.vendor = vendor;
     191              : }
     192              : 
     193            5 : void gncOwnerInitEmployee (GncOwner *owner, GncEmployee *employee)
     194              : {
     195            5 :     if (!owner) return;
     196            5 :     owner->type = GNC_OWNER_EMPLOYEE;
     197            5 :     owner->owner.employee = employee;
     198              : }
     199              : 
     200         2623 : GncOwnerType gncOwnerGetType (const GncOwner *owner)
     201              : {
     202         2623 :     if (!owner) return GNC_OWNER_NONE;
     203         2169 :     return owner->type;
     204              : }
     205              : 
     206            0 : const char * gncOwnerGetTypeString (const GncOwner *owner)
     207              : {
     208            0 :     GncOwnerType type = gncOwnerGetType(owner);
     209            0 :     switch (type)
     210              :     {
     211            0 :     case GNC_OWNER_NONE:
     212            0 :         return N_("None");
     213            0 :     case GNC_OWNER_UNDEFINED:
     214            0 :         return N_("Undefined");
     215            0 :     case GNC_OWNER_CUSTOMER:
     216            0 :         return N_("Customer");
     217            0 :     case GNC_OWNER_JOB:
     218            0 :         return N_("Job");
     219            0 :     case GNC_OWNER_VENDOR:
     220            0 :         return N_("Vendor");
     221            0 :     case GNC_OWNER_EMPLOYEE:
     222            0 :         return N_("Employee");
     223            0 :     default:
     224            0 :         PWARN ("Unknown owner type");
     225            0 :         return NULL;
     226              :     }
     227              : }
     228              : 
     229              : QofIdTypeConst
     230            0 : qofOwnerGetType(const GncOwner *owner)
     231              : {
     232            0 :     return gncOwnerTypeToQofIdType(owner->type);
     233              : }
     234              : 
     235            0 : QofIdTypeConst gncOwnerTypeToQofIdType(GncOwnerType t)
     236              : {
     237            0 :     QofIdTypeConst type = NULL;
     238            0 :     switch (t)
     239              :     {
     240            0 :     case GNC_OWNER_NONE :
     241              :     {
     242            0 :         type = NULL;
     243            0 :         break;
     244              :     }
     245            0 :     case GNC_OWNER_UNDEFINED :
     246              :     {
     247            0 :         type = NULL;
     248            0 :         break;
     249              :     }
     250            0 :     case GNC_OWNER_CUSTOMER :
     251              :     {
     252            0 :         type = GNC_ID_CUSTOMER;
     253            0 :         break;
     254              :     }
     255            0 :     case GNC_OWNER_JOB :
     256              :     {
     257            0 :         type = GNC_ID_JOB;
     258            0 :         break;
     259              :     }
     260            0 :     case GNC_OWNER_VENDOR :
     261              :     {
     262            0 :         type = GNC_ID_VENDOR;
     263            0 :         break;
     264              :     }
     265            0 :     case GNC_OWNER_EMPLOYEE :
     266              :     {
     267            0 :         type = GNC_ID_EMPLOYEE;
     268            0 :         break;
     269              :     }
     270              :     }
     271            0 :     return type;
     272              : }
     273              : 
     274              : QofInstance*
     275            1 : qofOwnerGetOwner (const GncOwner *owner)
     276              : {
     277              :     QofInstance *ent;
     278              : 
     279            1 :     if (!owner)
     280              :     {
     281            0 :         return NULL;
     282              :     }
     283            1 :     ent = NULL;
     284            1 :     switch (owner->type)
     285              :     {
     286            0 :     case GNC_OWNER_NONE :
     287              :     {
     288            0 :         break;
     289              :     }
     290            0 :     case GNC_OWNER_UNDEFINED :
     291              :     {
     292            0 :         break;
     293              :     }
     294            1 :     case GNC_OWNER_CUSTOMER :
     295              :     {
     296            1 :         ent = QOF_INSTANCE(owner->owner.customer);
     297            1 :         break;
     298              :     }
     299            0 :     case GNC_OWNER_JOB :
     300              :     {
     301            0 :         ent = QOF_INSTANCE(owner->owner.job);
     302            0 :         break;
     303              :     }
     304            0 :     case GNC_OWNER_VENDOR :
     305              :     {
     306            0 :         ent = QOF_INSTANCE(owner->owner.vendor);
     307            0 :         break;
     308              :     }
     309            0 :     case GNC_OWNER_EMPLOYEE :
     310              :     {
     311            0 :         ent = QOF_INSTANCE(owner->owner.employee);
     312            0 :         break;
     313              :     }
     314              :     }
     315            1 :     return ent;
     316              : }
     317              : 
     318              : void
     319            0 : qofOwnerSetEntity (GncOwner *owner, QofInstance *ent)
     320              : {
     321            0 :     if (!owner || !ent)
     322              :     {
     323            0 :         return;
     324              :     }
     325            0 :     if (0 == g_strcmp0(ent->e_type, GNC_ID_CUSTOMER))
     326              :     {
     327            0 :         owner->type = GNC_OWNER_CUSTOMER;
     328            0 :         gncOwnerInitCustomer(owner, (GncCustomer*)ent);
     329              :     }
     330            0 :     else if (0 == g_strcmp0(ent->e_type, GNC_ID_JOB))
     331              :     {
     332            0 :         owner->type = GNC_OWNER_JOB;
     333            0 :         gncOwnerInitJob(owner, (GncJob*)ent);
     334              :     }
     335            0 :     else if (0 == g_strcmp0(ent->e_type, GNC_ID_VENDOR))
     336              :     {
     337            0 :         owner->type = GNC_OWNER_VENDOR;
     338            0 :         gncOwnerInitVendor(owner, (GncVendor*)ent);
     339              :     }
     340            0 :     else if (0 == g_strcmp0(ent->e_type, GNC_ID_EMPLOYEE))
     341              :     {
     342            0 :         owner->type = GNC_OWNER_EMPLOYEE;
     343            0 :         gncOwnerInitEmployee(owner, (GncEmployee*)ent);
     344              :     }
     345              :     else
     346              :     {
     347            0 :         owner->type = GNC_OWNER_NONE;
     348            0 :         owner->owner.undefined = NULL;
     349              :     }
     350              : }
     351              : 
     352            0 : gboolean GNC_IS_OWNER (QofInstance *ent)
     353              : {
     354            0 :     if (!ent)
     355            0 :         return FALSE;
     356              : 
     357            0 :     return (GNC_IS_VENDOR(ent) ||
     358            0 :             GNC_IS_CUSTOMER(ent) ||
     359            0 :             GNC_IS_EMPLOYEE(ent) ||
     360            0 :             GNC_IS_JOB(ent));
     361              : }
     362            0 : gpointer gncOwnerGetUndefined (const GncOwner *owner)
     363              : {
     364            0 :     if (!owner) return NULL;
     365            0 :     if (owner->type != GNC_OWNER_UNDEFINED) return NULL;
     366            0 :     return owner->owner.undefined;
     367              : }
     368              : 
     369          486 : GncCustomer * gncOwnerGetCustomer (const GncOwner *owner)
     370              : {
     371          486 :     if (!owner) return NULL;
     372          486 :     if (owner->type != GNC_OWNER_CUSTOMER) return NULL;
     373          486 :     return owner->owner.customer;
     374              : }
     375              : 
     376           61 : GncJob * gncOwnerGetJob (const GncOwner *owner)
     377              : {
     378           61 :     if (!owner) return NULL;
     379           61 :     if (owner->type != GNC_OWNER_JOB) return NULL;
     380           13 :     return owner->owner.job;
     381              : }
     382              : 
     383           58 : GncVendor * gncOwnerGetVendor (const GncOwner *owner)
     384              : {
     385           58 :     if (!owner) return NULL;
     386           58 :     if (owner->type != GNC_OWNER_VENDOR) return NULL;
     387           58 :     return owner->owner.vendor;
     388              : }
     389              : 
     390           58 : GncEmployee * gncOwnerGetEmployee (const GncOwner *owner)
     391              : {
     392           58 :     if (!owner) return NULL;
     393           58 :     if (owner->type != GNC_OWNER_EMPLOYEE) return NULL;
     394           58 :     return owner->owner.employee;
     395              : }
     396              : 
     397          179 : void gncOwnerCopy (const GncOwner *src, GncOwner *dest)
     398              : {
     399          179 :     if (!src || !dest) return;
     400          179 :     if (src == dest) return;
     401          179 :     *dest = *src;
     402              : }
     403              : 
     404          116 : gboolean gncOwnerEqual (const GncOwner *a, const GncOwner *b)
     405              : {
     406          116 :     if (!a || !b) return FALSE;
     407          116 :     if (gncOwnerGetType (a) != gncOwnerGetType (b)) return FALSE;
     408           36 :     return (a->owner.undefined == b->owner.undefined);
     409              : }
     410              : 
     411            0 : int gncOwnerGCompareFunc (const GncOwner *a, const GncOwner *b)
     412              : {
     413            0 :     if (gncOwnerEqual (a, b))
     414            0 :         return 0;
     415              :     else
     416            0 :         return 1;
     417              : }
     418              : 
     419            0 : const char * gncOwnerGetID (const GncOwner *owner)
     420              : {
     421            0 :     if (!owner) return NULL;
     422            0 :     switch (owner->type)
     423              :     {
     424            0 :     case GNC_OWNER_NONE:
     425              :     case GNC_OWNER_UNDEFINED:
     426              :     default:
     427            0 :         return NULL;
     428            0 :     case GNC_OWNER_CUSTOMER:
     429            0 :         return gncCustomerGetID (owner->owner.customer);
     430            0 :     case GNC_OWNER_JOB:
     431            0 :         return gncJobGetID (owner->owner.job);
     432            0 :     case GNC_OWNER_VENDOR:
     433            0 :         return gncVendorGetID (owner->owner.vendor);
     434            0 :     case GNC_OWNER_EMPLOYEE:
     435            0 :         return gncEmployeeGetID (owner->owner.employee);
     436              :     }
     437              : }
     438              : 
     439          185 : const char * gncOwnerGetName (const GncOwner *owner)
     440              : {
     441          185 :     if (!owner) return NULL;
     442          185 :     switch (owner->type)
     443              :     {
     444            0 :     case GNC_OWNER_NONE:
     445              :     case GNC_OWNER_UNDEFINED:
     446              :     default:
     447            0 :         return NULL;
     448          120 :     case GNC_OWNER_CUSTOMER:
     449          120 :         return gncCustomerGetName (owner->owner.customer);
     450            5 :     case GNC_OWNER_JOB:
     451            5 :         return gncJobGetName (owner->owner.job);
     452           30 :     case GNC_OWNER_VENDOR:
     453           30 :         return gncVendorGetName (owner->owner.vendor);
     454           30 :     case GNC_OWNER_EMPLOYEE:
     455           30 :         return gncEmployeeGetName (owner->owner.employee);
     456              :     }
     457              : }
     458              : 
     459            7 : GncAddress * gncOwnerGetAddr (const GncOwner *owner)
     460              : {
     461            7 :     if (!owner) return NULL;
     462            7 :     switch (owner->type)
     463              :     {
     464            0 :     case GNC_OWNER_NONE:
     465              :     case GNC_OWNER_UNDEFINED:
     466              :     case GNC_OWNER_JOB:
     467              :     default:
     468            0 :         return NULL;
     469            3 :     case GNC_OWNER_CUSTOMER:
     470            3 :         return gncCustomerGetAddr (owner->owner.customer);
     471            2 :     case GNC_OWNER_VENDOR:
     472            2 :         return gncVendorGetAddr (owner->owner.vendor);
     473            2 :     case GNC_OWNER_EMPLOYEE:
     474            2 :         return gncEmployeeGetAddr (owner->owner.employee);
     475              :     }
     476              : }
     477              : 
     478            2 : gnc_commodity * gncOwnerGetCurrency (const GncOwner *owner)
     479              : {
     480            2 :     if (!owner) return NULL;
     481            2 :     switch (owner->type)
     482              :     {
     483            0 :     case GNC_OWNER_NONE:
     484              :     case GNC_OWNER_UNDEFINED:
     485              :     default:
     486            0 :         return NULL;
     487            2 :     case GNC_OWNER_CUSTOMER:
     488            2 :         return gncCustomerGetCurrency (owner->owner.customer);
     489            0 :     case GNC_OWNER_VENDOR:
     490            0 :         return gncVendorGetCurrency (owner->owner.vendor);
     491            0 :     case GNC_OWNER_EMPLOYEE:
     492            0 :         return gncEmployeeGetCurrency (owner->owner.employee);
     493            0 :     case GNC_OWNER_JOB:
     494            0 :         return gncOwnerGetCurrency (gncJobGetOwner (owner->owner.job));
     495              :     }
     496              : }
     497              : 
     498           14 : gboolean gncOwnerGetActive (const GncOwner *owner)
     499              : {
     500           14 :     if (!owner) return FALSE;
     501           14 :     switch (owner->type)
     502              :     {
     503            0 :     case GNC_OWNER_NONE:
     504              :     case GNC_OWNER_UNDEFINED:
     505              :     default:
     506            0 :         return FALSE;
     507            6 :     case GNC_OWNER_CUSTOMER:
     508            6 :         return gncCustomerGetActive (owner->owner.customer);
     509            4 :     case GNC_OWNER_VENDOR:
     510            4 :         return gncVendorGetActive (owner->owner.vendor);
     511            4 :     case GNC_OWNER_EMPLOYEE:
     512            4 :         return gncEmployeeGetActive (owner->owner.employee);
     513            0 :     case GNC_OWNER_JOB:
     514            0 :         return gncJobGetActive (owner->owner.job);
     515              :     }
     516              : }
     517              : 
     518           74 : const GncGUID * gncOwnerGetGUID (const GncOwner *owner)
     519              : {
     520           74 :     if (!owner) return NULL;
     521              : 
     522           73 :     switch (owner->type)
     523              :     {
     524            0 :     case GNC_OWNER_NONE:
     525              :     case GNC_OWNER_UNDEFINED:
     526              :     default:
     527            0 :         return NULL;
     528           67 :     case GNC_OWNER_CUSTOMER:
     529           67 :         return qof_instance_get_guid (QOF_INSTANCE(owner->owner.customer));
     530            0 :     case GNC_OWNER_JOB:
     531            0 :         return qof_instance_get_guid (QOF_INSTANCE(owner->owner.job));
     532            4 :     case GNC_OWNER_VENDOR:
     533            4 :         return qof_instance_get_guid (QOF_INSTANCE(owner->owner.vendor));
     534            2 :     case GNC_OWNER_EMPLOYEE:
     535            2 :         return qof_instance_get_guid (QOF_INSTANCE(owner->owner.employee));
     536              :     }
     537              : }
     538              : 
     539              : void
     540            0 : gncOwnerSetActive (const GncOwner *owner, gboolean active)
     541              : {
     542            0 :     if (!owner) return;
     543            0 :     switch (owner->type)
     544              :     {
     545            0 :     case GNC_OWNER_CUSTOMER:
     546            0 :         gncCustomerSetActive (owner->owner.customer, active);
     547            0 :         break;
     548            0 :     case GNC_OWNER_VENDOR:
     549            0 :         gncVendorSetActive (owner->owner.vendor, active);
     550            0 :         break;
     551            0 :     case GNC_OWNER_EMPLOYEE:
     552            0 :         gncEmployeeSetActive (owner->owner.employee, active);
     553            0 :         break;
     554            0 :     case GNC_OWNER_JOB:
     555            0 :         gncJobSetActive (owner->owner.job, active);
     556            0 :         break;
     557            0 :     case GNC_OWNER_NONE:
     558              :     case GNC_OWNER_UNDEFINED:
     559              :     default:
     560            0 :         break;
     561              :     }
     562              : }
     563              : 
     564           12 : GncGUID gncOwnerRetGUID (GncOwner *owner)
     565              : {
     566           12 :     const GncGUID *guid = gncOwnerGetGUID (owner);
     567           12 :     if (guid)
     568           11 :         return *guid;
     569            1 :     return *guid_null ();
     570              : }
     571              : 
     572         2035 : const GncOwner * gncOwnerGetEndOwner (const GncOwner *owner)
     573              : {
     574         2035 :     if (!owner) return NULL;
     575         2003 :     switch (owner->type)
     576              :     {
     577            0 :     case GNC_OWNER_NONE:
     578              :     case GNC_OWNER_UNDEFINED:
     579              :     default:
     580            0 :         return NULL;
     581         1875 :     case GNC_OWNER_CUSTOMER:
     582              :     case GNC_OWNER_VENDOR:
     583              :     case GNC_OWNER_EMPLOYEE:
     584         1875 :         return owner;
     585          128 :     case GNC_OWNER_JOB:
     586          128 :         return gncJobGetOwner (owner->owner.job);
     587              :     }
     588              : }
     589              : 
     590            0 : int gncOwnerCompare (const GncOwner *a, const GncOwner *b)
     591              : {
     592            0 :     if (!a && !b) return 0;
     593            0 :     if (!a && b) return 1;
     594            0 :     if (a && !b) return -1;
     595              : 
     596            0 :     if (a->type != b->type)
     597            0 :         return (a->type - b->type);
     598              : 
     599            0 :     switch (a->type)
     600              :     {
     601            0 :     case GNC_OWNER_NONE:
     602              :     case GNC_OWNER_UNDEFINED:
     603              :     default:
     604            0 :         return 0;
     605            0 :     case GNC_OWNER_CUSTOMER:
     606            0 :         return gncCustomerCompare (a->owner.customer, b->owner.customer);
     607            0 :     case GNC_OWNER_VENDOR:
     608            0 :         return gncVendorCompare (a->owner.vendor, b->owner.vendor);
     609            0 :     case GNC_OWNER_EMPLOYEE:
     610            0 :         return gncEmployeeCompare (a->owner.employee, b->owner.employee);
     611            0 :     case GNC_OWNER_JOB:
     612            0 :         return gncJobCompare (a->owner.job, b->owner.job);
     613              :     }
     614              : }
     615              : 
     616          107 : const GncGUID * gncOwnerGetEndGUID (const GncOwner *owner)
     617              : {
     618          107 :     if (!owner) return NULL;
     619           36 :     return gncOwnerGetGUID (gncOwnerGetEndOwner (owner));
     620              : }
     621              : 
     622           24 : void gncOwnerAttachToLot (const GncOwner *owner, GNCLot *lot)
     623              : {
     624           24 :      if (!owner || !lot)
     625            0 :         return;
     626              : 
     627           24 :     gnc_lot_begin_edit (lot);
     628              : 
     629           48 :     qof_instance_set (QOF_INSTANCE (lot),
     630           24 :                       GNC_OWNER_TYPE, (gint64)gncOwnerGetType (owner),
     631              :                       GNC_OWNER_GUID, gncOwnerGetGUID (owner),
     632              :                       NULL);
     633           24 :     gnc_lot_commit_edit (lot);
     634              : }
     635              : 
     636          681 : gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner)
     637              : {
     638          681 :     GncGUID *guid = NULL;
     639              :     QofBook *book;
     640          681 :     GncOwnerType type = GNC_OWNER_NONE;
     641          681 :     guint64 type64 = 0;
     642              : 
     643          681 :     if (!lot || !owner) return FALSE;
     644              : 
     645          678 :     book = gnc_lot_get_book (lot);
     646          678 :     qof_instance_get (QOF_INSTANCE (lot),
     647              :                       GNC_OWNER_TYPE, &type64,
     648              :                       GNC_OWNER_GUID, &guid,
     649              :                       NULL);
     650          678 :     type = (GncOwnerType) type64;
     651          678 :     switch (type)
     652              :     {
     653          212 :     case GNC_OWNER_CUSTOMER:
     654          212 :         gncOwnerInitCustomer (owner, gncCustomerLookup (book, guid));
     655          212 :         break;
     656            0 :     case GNC_OWNER_VENDOR:
     657            0 :         gncOwnerInitVendor (owner, gncVendorLookup (book, guid));
     658            0 :         break;
     659            0 :     case GNC_OWNER_EMPLOYEE:
     660            0 :         gncOwnerInitEmployee (owner, gncEmployeeLookup (book, guid));
     661            0 :         break;
     662            0 :     case GNC_OWNER_JOB:
     663            0 :         gncOwnerInitJob (owner, gncJobLookup (book, guid));
     664            0 :         break;
     665          466 :     default:
     666          466 :         guid_free (guid);
     667          466 :         return FALSE;
     668              :     }
     669              : 
     670          212 :     guid_free (guid);
     671          212 :     return (owner->owner.undefined != NULL);
     672              : }
     673              : 
     674            0 : gboolean gncOwnerGetOwnerFromTxn (Transaction *txn, GncOwner *owner)
     675              : {
     676            0 :     Split *apar_split = NULL;
     677              : 
     678            0 :     if (!txn || !owner) return FALSE;
     679              : 
     680            0 :     if (xaccTransGetTxnType (txn) == TXN_TYPE_NONE)
     681            0 :         return FALSE;
     682              : 
     683            0 :     apar_split = xaccTransGetFirstAPARAcctSplit (txn, TRUE);
     684            0 :     if (apar_split)
     685              :     {
     686            0 :         GNCLot *lot = xaccSplitGetLot (apar_split);
     687            0 :         GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
     688            0 :         if (invoice)
     689            0 :             gncOwnerCopy (gncInvoiceGetOwner (invoice), owner);
     690            0 :         else if (!gncOwnerGetOwnerFromLot (lot, owner))
     691            0 :                 return FALSE;
     692              : 
     693            0 :         return TRUE; // Got owner from either invoice or lot
     694              :     }
     695              : 
     696            0 :     return FALSE;
     697              : }
     698              : 
     699           46 : gboolean gncOwnerIsValid (const GncOwner *owner)
     700              : {
     701           46 :     if (!owner) return FALSE;
     702           14 :     return (owner->owner.undefined != NULL);
     703              : }
     704              : 
     705              : gboolean
     706            0 : gncOwnerLotMatchOwnerFunc (GNCLot *lot, gpointer user_data)
     707              : {
     708            0 :     const GncOwner *req_owner = user_data;
     709              :     GncOwner lot_owner;
     710              :     const GncOwner *end_owner;
     711            0 :     GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
     712              : 
     713              :     /* Determine the owner associated to the lot */
     714            0 :     if (invoice)
     715              :         /* Invoice lots */
     716            0 :         end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
     717            0 :     else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
     718              :         /* Pre-payment lots */
     719            0 :         end_owner = gncOwnerGetEndOwner (&lot_owner);
     720              :     else
     721            0 :         return FALSE;
     722              : 
     723              :     /* Is this a lot for the requested owner ? */
     724            0 :     return gncOwnerEqual (end_owner, req_owner);
     725              : }
     726              : 
     727              : gint
     728            0 : gncOwnerLotsSortFunc (GNCLot *lotA, GNCLot *lotB)
     729              : {
     730              :     GncInvoice *ia, *ib;
     731              :     time64 da, db;
     732              : 
     733            0 :     ia = gncInvoiceGetInvoiceFromLot (lotA);
     734            0 :     ib = gncInvoiceGetInvoiceFromLot (lotB);
     735              : 
     736            0 :     if (ia)
     737            0 :         da = gncInvoiceGetDateDue (ia);
     738              :     else
     739            0 :         da = xaccTransRetDatePosted (xaccSplitGetParent (gnc_lot_get_earliest_split (lotA)));
     740              : 
     741            0 :     if (ib)
     742            0 :         db = gncInvoiceGetDateDue (ib);
     743              :     else
     744            0 :         db = xaccTransRetDatePosted (xaccSplitGetParent (gnc_lot_get_earliest_split (lotB)));
     745              : 
     746            0 :     return (da > db) - (da < db);
     747              : }
     748              : 
     749              : GNCLot *
     750           14 : gncOwnerCreatePaymentLotSecs (const GncOwner *owner, Transaction **preset_txn,
     751              :                               Account *posted_acc, Account *xfer_acc,
     752              :                               gnc_numeric amount, gnc_numeric exch, time64 date,
     753              :                               const char *memo, const char *num)
     754              : {
     755              :     QofBook *book;
     756              :     Split *split;
     757              :     const char *name;
     758              :     gnc_commodity *post_comm, *xfer_comm;
     759           14 :     Split *xfer_split = NULL;
     760           14 :     Transaction *txn = NULL;
     761              :     GNCLot *payment_lot;
     762           14 :     gnc_numeric xfer_amount = gnc_numeric_zero();
     763           14 :     gnc_numeric txn_value = gnc_numeric_zero();
     764              : 
     765              :     /* Verify our arguments */
     766           14 :     if (!owner || !posted_acc || !xfer_acc) return NULL;
     767           14 :     g_return_val_if_fail (owner->owner.undefined != NULL, NULL);
     768              : 
     769              :     /* Compute the ancillary data */
     770           14 :     book = gnc_account_get_book (posted_acc);
     771           14 :     name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner));
     772           14 :     post_comm = xaccAccountGetCommodity (posted_acc);
     773           14 :     xfer_comm = xaccAccountGetCommodity (xfer_acc);
     774              : 
     775              : //    reverse = use_reversed_payment_amounts(owner);
     776              : 
     777           14 :     if (preset_txn && *preset_txn)
     778            0 :         txn = *preset_txn;
     779              : 
     780           14 :     if (txn)
     781              :     {
     782            0 :         int i = 0;
     783              : 
     784              :         /* Pre-existing transaction was specified. We completely clear it,
     785              :          * except for a pre-existing transfer split. We're very conservative
     786              :          * in preserving that one as it may have been reconciled already. */
     787            0 :         xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc);
     788            0 :         xaccTransBeginEdit (txn);
     789            0 :         while (i < xaccTransCountSplits(txn))
     790              :         {
     791            0 :             Split *split = xaccTransGetSplit (txn, i);
     792            0 :             if (split == xfer_split)
     793            0 :                 ++i;
     794              :             else
     795            0 :                 xaccSplitDestroy(split);
     796              :         }
     797              :         /* Note: don't commit transaction now - that would insert an imbalance split.*/
     798              :     }
     799              :     else
     800              :     {
     801           14 :         txn = xaccMallocTransaction (book);
     802           14 :         xaccTransBeginEdit (txn);
     803              :     }
     804              : 
     805              :     /* Complete transaction setup */
     806           14 :     xaccTransSetDescription (txn, name ? name : "");
     807           28 :     if (!gnc_commodity_equal(xaccTransGetCurrency (txn), post_comm) &&
     808           14 :         !gnc_commodity_equal (xaccTransGetCurrency (txn), xfer_comm))
     809           14 :         xaccTransSetCurrency (txn, xfer_comm);
     810              : 
     811              :     /* With all commodities involved known, define split amounts and txn value.
     812              :      * - post amount (amount passed in as parameter) is always in post_acct commodity,
     813              :      * - xfer amount requires conversion if the xfer account has a different
     814              :      *   commodity than the post account.
     815              :      * - txn value requires conversion if the post account has a different
     816              :      *   commodity than the transaction */
     817           14 :     if (gnc_commodity_equal(post_comm, xfer_comm))
     818           14 :         xfer_amount = amount;
     819              :     else
     820            0 :         xfer_amount = gnc_numeric_mul (amount, exch, GNC_DENOM_AUTO,
     821              :                                        GNC_HOW_RND_ROUND_HALF_UP);
     822              : 
     823           14 :     if (gnc_commodity_equal(post_comm, xaccTransGetCurrency (txn)))
     824           14 :         txn_value = amount;
     825              :     else
     826            0 :         txn_value = gnc_numeric_mul (amount, exch, GNC_DENOM_AUTO,
     827              :                                       GNC_HOW_RND_ROUND_HALF_UP);
     828              : 
     829              :     /* Insert a split for the transfer account if we don't have one yet */
     830           14 :     if (!xfer_split)
     831              :     {
     832              :         /* The split for the transfer account */
     833           14 :         xfer_split = xaccMallocSplit (book);
     834           14 :         xaccSplitSetMemo (xfer_split, memo);
     835              :         /* set per book option */
     836           14 :         gnc_set_num_action (NULL, xfer_split, num, _("Payment"));
     837           14 :         xaccAccountBeginEdit (xfer_acc);
     838           14 :         xaccAccountInsertSplit (xfer_acc, xfer_split);
     839           14 :         xaccAccountCommitEdit (xfer_acc);
     840           14 :         xaccTransAppendSplit (txn, xfer_split);
     841              : 
     842           14 :         xaccSplitSetAmount(xfer_split, xfer_amount); /* Payment in xfer account currency */
     843           14 :         xaccSplitSetValue(xfer_split, txn_value); /* Payment in transaction currency */
     844              :     }
     845              :     /* For a pre-existing xfer split, let's check if the amount and value
     846              :      * have changed. If so, update them and unreconcile. */
     847            0 :     else if (!gnc_numeric_equal (xaccSplitGetAmount (xfer_split), xfer_amount) ||
     848            0 :              !gnc_numeric_equal (xaccSplitGetValue (xfer_split), txn_value))
     849              :     {
     850            0 :         xaccSplitSetAmount (xfer_split, xfer_amount);
     851            0 :         xaccSplitSetValue (xfer_split, txn_value);
     852            0 :         xaccSplitSetReconcile (xfer_split, NREC);
     853              :     }
     854              : 
     855              :     /* Add a split in the post account */
     856           14 :     split = xaccMallocSplit (book);
     857           14 :     xaccSplitSetMemo (split, memo);
     858              :     /* set per book option */
     859           14 :     xaccSplitSetAction (split, _("Payment"));
     860           14 :     xaccAccountBeginEdit (posted_acc);
     861           14 :     xaccAccountInsertSplit (posted_acc, split);
     862           14 :     xaccAccountCommitEdit (posted_acc);
     863           14 :     xaccTransAppendSplit (txn, split);
     864           14 :     xaccSplitSetAmount (split, gnc_numeric_neg (amount));
     865           14 :     xaccSplitSetValue (split, gnc_numeric_neg (txn_value));
     866              : 
     867              :     /* Create a new lot for the payment */
     868           14 :     payment_lot = gnc_lot_new (book);
     869           14 :     gncOwnerAttachToLot (owner, payment_lot);
     870           14 :     gnc_lot_add_split (payment_lot, split);
     871              : 
     872              :     /* Mark the transaction as a payment */
     873           14 :     xaccTransSetNum (txn, num);
     874           14 :     xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
     875              : 
     876              :     /* Set date for transaction */
     877           14 :     xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
     878           14 :     xaccTransSetDatePostedSecs (txn, date);
     879              : 
     880              :     /* Commit this new transaction */
     881           14 :     xaccTransCommitEdit (txn);
     882           14 :     if (preset_txn)
     883           13 :         *preset_txn = txn;
     884              : 
     885           14 :     return payment_lot;
     886              : }
     887              : 
     888              : typedef enum
     889              : {
     890              :     is_equal     = 8,
     891              :     is_more      = 4,
     892              :     is_less      = 2,
     893              :     is_pay_split = 1
     894              : } split_flags;
     895              : 
     896           14 : Split *gncOwnerFindOffsettingSplit (GNCLot *lot, gnc_numeric target_amount)
     897              : {
     898           14 :     SplitList *ls_iter = NULL;
     899           14 :     Split *best_split = NULL;
     900           14 :     gnc_numeric best_amt = { 0, 1};
     901           14 :     gint best_flags = 0;
     902              : 
     903           14 :     if (!lot)
     904            0 :         return NULL;
     905              : 
     906           28 :     for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
     907              :     {
     908           14 :         Split *split = ls_iter->data;
     909              : 
     910           14 :         if (!split)
     911            0 :             continue;
     912              : 
     913              : 
     914           14 :         Transaction *txn = xaccSplitGetParent (split);
     915           14 :         if (!txn)
     916              :         {
     917              :             // Ooops - the split doesn't belong to any transaction !
     918              :             // This is not expected so issue a warning and continue with next split
     919            0 :             PWARN("Encountered a split in a payment lot that's not part of any transaction. "
     920              :                   "This is unexpected! Skipping split %p.", split);
     921            0 :             continue;
     922              :         }
     923              : 
     924              :         // Check if this split has the opposite sign of the target amount we want to offset
     925           14 :         gnc_numeric split_amount = xaccSplitGetAmount (split);
     926           14 :         if (gnc_numeric_positive_p (target_amount) == gnc_numeric_positive_p (split_amount))
     927            0 :             continue;
     928              : 
     929              :         // Ok we have found a split that potentially can offset the target value
     930              :         // Let's see if it's better than what we have found already.
     931           14 :         gint amt_cmp = gnc_numeric_compare (gnc_numeric_abs (split_amount),
     932              :                                             gnc_numeric_abs (target_amount));
     933           14 :         gint new_flags = 0;
     934           14 :         if (amt_cmp == 0)
     935            8 :             new_flags += is_equal;
     936            6 :         else if (amt_cmp > 0)
     937            0 :             new_flags += is_more;
     938              :         else
     939            6 :             new_flags += is_less;
     940              : 
     941           14 :         if (xaccTransGetTxnType (txn) != TXN_TYPE_LINK)
     942           14 :             new_flags += is_pay_split;
     943              : 
     944           28 :         if ((new_flags >= best_flags) &&
     945           14 :             (gnc_numeric_compare (gnc_numeric_abs (split_amount),
     946              :                                   gnc_numeric_abs (best_amt)) > 0))
     947              :         {
     948              :             // The new split is a better match than what we found so far
     949           14 :             best_split = split;
     950           14 :             best_flags = new_flags;
     951           14 :             best_amt   = split_amount;
     952              :         }
     953              :     }
     954              : 
     955           14 :     return best_split;
     956              : }
     957              : 
     958              : gboolean
     959            0 : gncOwnerReduceSplitTo (Split *split, gnc_numeric target_amount)
     960              : {
     961            0 :     gnc_numeric split_amt = xaccSplitGetAmount (split);
     962            0 :     if (gnc_numeric_positive_p (split_amt) != gnc_numeric_positive_p (target_amount))
     963            0 :         return FALSE; // Split and target amount have to be of the same sign
     964              : 
     965            0 :     if (gnc_numeric_equal (split_amt, target_amount))
     966            0 :         return FALSE; // Split already has the target amount
     967              : 
     968            0 :     if (gnc_numeric_zero_p (split_amt))
     969            0 :         return FALSE; // We can't reduce a split that already has zero amount
     970              : 
     971            0 :     Transaction *txn = xaccSplitGetParent (split);
     972            0 :     xaccTransBeginEdit (txn);
     973              : 
     974              :     /* Calculate new value for reduced split. This can be different from
     975              :      * the reduced split's new amount (when account and transaction
     976              :      * commodity differ) */
     977            0 :     gnc_numeric split_val = xaccSplitGetValue (split);
     978            0 :     gnc_numeric exch = gnc_numeric_div (split_val, split_amt,
     979              :                                         GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
     980              : 
     981            0 :     gint64 txn_comm_fraction = gnc_commodity_get_fraction (xaccTransGetCurrency (txn));
     982            0 :     gnc_numeric target_val = gnc_numeric_mul (target_amount, exch,
     983              :                                               txn_comm_fraction,
     984              :                                               GNC_HOW_RND_ROUND_HALF_UP);
     985            0 :     xaccSplitSetAmount (split, target_amount);
     986            0 :     xaccSplitSetValue (split, target_val);
     987              : 
     988              :     /* Calculate amount and value for remainder split.
     989              :      * Note we calculate the remaining value by subtracting the new target value
     990              :      * from the original split's value rather than multiplying the remaining
     991              :      * amount with the exchange rate to avoid imbalances due to rounding errors. */
     992            0 :     gnc_numeric rem_amt = gnc_numeric_sub (split_amt, target_amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
     993            0 :     gnc_numeric rem_val = gnc_numeric_sub (split_val, target_val, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
     994              : 
     995            0 :     Split *rem_split = xaccMallocSplit (xaccSplitGetBook (split));
     996            0 :     xaccSplitCopyOnto (split, rem_split);
     997            0 :     xaccSplitSetAmount (rem_split, rem_amt);
     998            0 :     xaccSplitSetValue (rem_split, rem_val);
     999            0 :     xaccSplitSetParent (rem_split, txn);
    1000              : 
    1001            0 :     xaccTransCommitEdit (txn);
    1002              : 
    1003            0 :     GNCLot *lot = xaccSplitGetLot (split);
    1004            0 :     gnc_lot_add_split (lot, rem_split);
    1005              : 
    1006            0 :     return TRUE;
    1007              : }
    1008              : 
    1009              : void
    1010            0 : gncOwnerSetLotLinkMemo (Transaction *ll_txn)
    1011              : {
    1012            0 :     gchar *memo_prefix = _("Offset between business items: ");
    1013              :     gchar *new_memo;
    1014              :     SplitList *lts_iter;
    1015            0 :     SplitList *splits = NULL, *siter;
    1016            0 :     GList *titles = NULL, *titer;
    1017              : 
    1018            0 :     if (!ll_txn)
    1019            0 :         return;
    1020              : 
    1021            0 :     if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
    1022            0 :         return;
    1023              : 
    1024              :     // Find all splits in the lot link transaction that are also in a document lot
    1025            0 :     for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
    1026              :     {
    1027            0 :         Split *split = lts_iter->data;
    1028              :         GNCLot *lot;
    1029              :         GncInvoice *invoice;
    1030              :         gchar *title;
    1031              : 
    1032            0 :         if (!split)
    1033            0 :             continue;
    1034              : 
    1035            0 :         lot = xaccSplitGetLot (split);
    1036            0 :         if (!lot)
    1037            0 :             continue;
    1038              : 
    1039            0 :         invoice = gncInvoiceGetInvoiceFromLot (lot);
    1040            0 :         if (!invoice)
    1041            0 :             continue;
    1042              : 
    1043            0 :         title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));
    1044              : 
    1045            0 :         titles = g_list_prepend (titles, title);
    1046            0 :         splits = g_list_prepend (splits, split); // splits don't need to be sorted
    1047              :     }
    1048              : 
    1049            0 :     if (!titles)
    1050            0 :         return; // We didn't find document lots
    1051              : 
    1052            0 :     titles = g_list_sort (titles, (GCompareFunc)g_strcmp0);
    1053              : 
    1054              :     // Create the memo as we'd want it to be
    1055            0 :     new_memo = g_strconcat (memo_prefix, titles->data, NULL);
    1056            0 :     for (titer = titles->next; titer; titer = titer->next)
    1057              :     {
    1058            0 :         gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
    1059            0 :         g_free (new_memo);
    1060            0 :         new_memo = tmp_memo;
    1061              :     }
    1062            0 :     g_list_free_full (titles, g_free);
    1063              : 
    1064              :     // Update the memos of all the splits we found previously (if needed)
    1065            0 :     for (siter = splits; siter; siter = siter->next)
    1066              :     {
    1067            0 :         if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
    1068            0 :             xaccSplitSetMemo (siter->data, new_memo);
    1069              :     }
    1070              : 
    1071            0 :     g_list_free (splits);
    1072            0 :     g_free (new_memo);
    1073              : }
    1074              : 
    1075              : /* Find an existing lot link transaction in the given lot
    1076              :  * Only use a lot link that already links at least two
    1077              :  * documents (to avoid perpetuating the lot link proliferation
    1078              :  * that happened in 2.6.0-2.6.3).
    1079              :  */
    1080              : static Transaction *
    1081            0 : get_ll_transaction_from_lot (GNCLot *lot)
    1082              : {
    1083              :     SplitList *ls_iter;
    1084              : 
    1085              :     /* This should really only be called on a document lot */
    1086            0 :     if (!gncInvoiceGetInvoiceFromLot (lot))
    1087            0 :         return NULL;
    1088              : 
    1089              :     /* The given lot is a valid document lot. Now iterate over all
    1090              :      * other lot links in this lot to find one more document lot.
    1091              :      */
    1092            0 :     for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
    1093              :     {
    1094            0 :         Split *ls = ls_iter->data;
    1095            0 :         Transaction *ll_txn = xaccSplitGetParent (ls);
    1096              :         SplitList *ts_iter;
    1097              : 
    1098            0 :         if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
    1099            0 :             continue;
    1100              : 
    1101            0 :         for (ts_iter = xaccTransGetSplitList (ll_txn); ts_iter; ts_iter = ts_iter->next)
    1102              :         {
    1103            0 :             Split *ts = ts_iter->data;
    1104            0 :             GNCLot *tslot = xaccSplitGetLot (ts);
    1105              : 
    1106            0 :             if (!tslot)
    1107            0 :                 continue;
    1108              : 
    1109            0 :             if (tslot == lot)
    1110            0 :                 continue;
    1111              : 
    1112            0 :             if (gncInvoiceGetInvoiceFromLot (lot))
    1113            0 :                 return ll_txn; /* Got one more document lot - mission accomplished */
    1114              :         }
    1115              :     }
    1116              : 
    1117              :     /* The lot doesn't have an ll_txn with the requested criteria... */
    1118            0 :     return NULL;
    1119              : }
    1120              : 
    1121              : static void
    1122            0 : gncOwnerCreateLotLink (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
    1123              : {
    1124            0 :     const gchar *action = _("Lot Link");
    1125            0 :     Account *acct = gnc_lot_get_account (from_lot);
    1126            0 :     const gchar *name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
    1127            0 :     Transaction *ll_txn = NULL;
    1128              :     gnc_numeric from_lot_bal, to_lot_bal;
    1129              :     time64 from_time, to_time;
    1130              :     time64 time_posted;
    1131              :     Split *split;
    1132              : 
    1133              :     /* Sanity check */
    1134            0 :     if (!gncInvoiceGetInvoiceFromLot (from_lot) ||
    1135            0 :         !gncInvoiceGetInvoiceFromLot (to_lot))
    1136            0 :         return;
    1137              : 
    1138              :     /* Determine transaction date based on lot splits */
    1139            0 :     from_time = xaccTransRetDatePosted (xaccSplitGetParent (gnc_lot_get_latest_split (from_lot)));
    1140            0 :     to_time   = xaccTransRetDatePosted (xaccSplitGetParent (gnc_lot_get_latest_split (to_lot)));
    1141            0 :     if (from_time >= to_time)
    1142            0 :         time_posted = from_time;
    1143              :     else
    1144            0 :         time_posted = to_time;
    1145              : 
    1146              :     /* Figure out how much we can offset between the lots */
    1147            0 :     from_lot_bal = gnc_lot_get_balance (from_lot);
    1148            0 :     to_lot_bal = gnc_lot_get_balance (to_lot);
    1149            0 :     if (gnc_numeric_compare (gnc_numeric_abs (from_lot_bal),
    1150              :                              gnc_numeric_abs (to_lot_bal)) > 0)
    1151            0 :         from_lot_bal = gnc_numeric_neg (to_lot_bal);
    1152              :     else
    1153            0 :         to_lot_bal = gnc_numeric_neg (from_lot_bal);
    1154              : 
    1155            0 :     xaccAccountBeginEdit (acct);
    1156              : 
    1157              :     /* Look for a pre-existing lot link we can extend */
    1158            0 :     ll_txn = get_ll_transaction_from_lot (from_lot);
    1159              : 
    1160            0 :     if (!ll_txn)
    1161            0 :         ll_txn = get_ll_transaction_from_lot (to_lot);
    1162              : 
    1163            0 :     if (!ll_txn)
    1164              :     {
    1165              :         /* No pre-existing lot link. Create one. */
    1166            0 :         ll_txn = xaccMallocTransaction (gnc_lot_get_book (from_lot));
    1167            0 :         xaccTransBeginEdit (ll_txn);
    1168            0 :         xaccTransSetDescription (ll_txn, name ? name : "(Unknown)");
    1169            0 :         xaccTransSetCurrency (ll_txn, xaccAccountGetCommodity(acct));
    1170            0 :         xaccTransSetDateEnteredSecs (ll_txn, gnc_time (NULL));
    1171            0 :         xaccTransSetDatePostedSecs (ll_txn, time_posted);
    1172            0 :         xaccTransSetTxnType (ll_txn, TXN_TYPE_LINK);
    1173              :     }
    1174              :     else
    1175              :     {
    1176            0 :         time64 time = xaccTransRetDatePosted (ll_txn);
    1177            0 :         xaccTransBeginEdit (ll_txn);
    1178              : 
    1179              :         /* Maybe we need to update the post date of the transaction ? */
    1180            0 :         if (time_posted > time)
    1181            0 :             xaccTransSetDatePostedSecs (ll_txn, time_posted);
    1182              :     }
    1183              : 
    1184              :     /* Create a split for the from_lot */
    1185            0 :     split = xaccMallocSplit (gnc_lot_get_book (from_lot));
    1186              :     /* set Action using utility function */
    1187            0 :     gnc_set_num_action (NULL, split, NULL, action);
    1188            0 :     xaccAccountInsertSplit (acct, split);
    1189            0 :     xaccTransAppendSplit (ll_txn, split);
    1190              :     /* To offset the lot balance, the split must be of the opposite sign */
    1191            0 :     xaccSplitSetBaseValue (split, gnc_numeric_neg (from_lot_bal), xaccAccountGetCommodity(acct));
    1192            0 :     gnc_lot_add_split (from_lot, split);
    1193              : 
    1194              :     /* Create a split for the to_lot */
    1195            0 :     split = xaccMallocSplit (gnc_lot_get_book (to_lot));
    1196              :     /* set Action using utility function */
    1197            0 :     gnc_set_num_action (NULL, split, NULL, action);
    1198            0 :     xaccAccountInsertSplit (acct, split);
    1199            0 :     xaccTransAppendSplit (ll_txn, split);
    1200              :     /* To offset the lot balance, the split must be of the opposite sign */
    1201            0 :     xaccSplitSetBaseValue (split, gnc_numeric_neg (to_lot_bal), xaccAccountGetCommodity(acct));
    1202            0 :     gnc_lot_add_split (to_lot, split);
    1203              : 
    1204            0 :     xaccTransCommitEdit (ll_txn);
    1205              : 
    1206              : 
    1207              :     /* Do some post-cleaning on the lots
    1208              :      * The above actions may have created splits that are
    1209              :      * in the same transaction and lot. These can be merged.
    1210              :      */
    1211            0 :     xaccScrubMergeLotSubSplits (to_lot, FALSE);
    1212            0 :     xaccScrubMergeLotSubSplits (from_lot, FALSE);
    1213              :     /* And finally set the same memo for all remaining splits
    1214              :      * It's a convenience for the users to identify all documents
    1215              :      * involved in the link.
    1216              :      */
    1217            0 :     gncOwnerSetLotLinkMemo (ll_txn);
    1218            0 :     xaccAccountCommitEdit (acct);
    1219              : }
    1220              : 
    1221           14 : static void gncOwnerOffsetLots (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
    1222              : {
    1223              :     gnc_numeric target_offset;
    1224              :     Split *split;
    1225              : 
    1226              :     /* from lot should not be a document lot because we're removing a split from there ! */
    1227           14 :     if (gncInvoiceGetInvoiceFromLot (from_lot))
    1228              :     {
    1229            0 :         PWARN ("from_lot %p is a document lot. That is not allowed in gncOwnerOffsetLots", from_lot);
    1230            0 :         return;
    1231              :     }
    1232              : 
    1233              :     /* Get best matching split from from_lot to offset to_lot */
    1234           14 :     target_offset = gnc_lot_get_balance (to_lot);
    1235           14 :     if (gnc_numeric_zero_p (target_offset))
    1236            0 :         return; // to_lot is already balanced, nothing more to do
    1237              : 
    1238           14 :     split = gncOwnerFindOffsettingSplit (from_lot, target_offset);
    1239           14 :     if (!split)
    1240            0 :         return; // No suitable offsetting split found, nothing more to do
    1241              : 
    1242              :     /* If the offsetting split is bigger than the amount needed to balance
    1243              :      * to_lot, reduce the split so its reduced amount closes to_lot exactly.
    1244              :      * Note the negation in the reduction function. The split must be of
    1245              :      * opposite sign of to_lot's balance in order to be able to close it.
    1246              :      */
    1247           14 :     if (gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetAmount (split)),
    1248              :                              gnc_numeric_abs (target_offset)) > 0)
    1249            0 :         gncOwnerReduceSplitTo (split, gnc_numeric_neg (target_offset));
    1250              : 
    1251              :     /* Move the reduced split from from_lot to to_lot */
    1252           14 :     gnc_lot_add_split (to_lot, split);
    1253              : 
    1254              : }
    1255              : 
    1256           15 : void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
    1257              : {
    1258              :     GList *left_iter;
    1259              : 
    1260              :     /* General note: in the code below the term "payment" can
    1261              :      * both mean a true payment or a document of
    1262              :      * the opposite sign (invoice vs credit note) relative to
    1263              :      * the lot being processed. In general this function will
    1264              :      * perform a balancing action on a set of lots, so you
    1265              :      * will also find frequent references to balancing instead. */
    1266              : 
    1267              :     /* Payments can only be applied when at least an owner
    1268              :      * and a list of lots to use are given */
    1269           15 :     if (!owner) return;
    1270           15 :     if (!lots) return;
    1271              : 
    1272           44 :     for (left_iter = lots; left_iter; left_iter = left_iter->next)
    1273              :     {
    1274           29 :         GNCLot *left_lot = left_iter->data;
    1275              :         gnc_numeric left_lot_bal;
    1276              :         gboolean left_lot_has_doc;
    1277           29 :         gboolean left_modified = FALSE;
    1278              :         Account *acct;
    1279              :         GList *right_iter;
    1280              : 
    1281              :         /* Only attempt to apply payments to open lots.
    1282              :          * Note that due to the iterative nature of this function lots
    1283              :          * in the list may become empty/closed before they are evaluated as
    1284              :          * base lot, so we should check this for each lot. */
    1285           29 :         if (!left_lot)
    1286            0 :             continue;
    1287           29 :         if (gnc_lot_count_splits (left_lot) == 0)
    1288              :         {
    1289            0 :             gnc_lot_destroy (left_lot);
    1290            0 :             left_iter->data = NULL;
    1291            0 :             continue;
    1292              :         }
    1293           29 :         if (gnc_lot_is_closed (left_lot))
    1294            9 :             continue;
    1295              : 
    1296           20 :         acct = gnc_lot_get_account (left_lot);
    1297           20 :         xaccAccountBeginEdit (acct);
    1298              : 
    1299           20 :         left_lot_bal = gnc_lot_get_balance (left_lot);
    1300           20 :         left_lot_has_doc = (gncInvoiceGetInvoiceFromLot (left_lot) != NULL);
    1301              : 
    1302              :         /* Attempt to offset left_lot with any of the remaining lots. To do so
    1303              :          * iterate over the remaining lots adding lot links or moving payments
    1304              :          * around.
    1305              :          */
    1306           34 :         for (right_iter = left_iter->next; right_iter; right_iter = right_iter->next)
    1307              :         {
    1308           14 :             GNCLot *right_lot = right_iter->data;
    1309              :             gnc_numeric right_lot_bal;
    1310              :             gboolean right_lot_has_doc;
    1311              : 
    1312              :             /* Only attempt to use open lots to balance the base lot.
    1313              :              * Note that due to the iterative nature of this function lots
    1314              :              * in the list may become empty/closed before they are evaluated as
    1315              :              * base lot, so we should check this for each lot. */
    1316           14 :             if (!right_lot)
    1317            0 :                 continue;
    1318           14 :             if (gnc_lot_count_splits (right_lot) == 0)
    1319              :             {
    1320            0 :                 gnc_lot_destroy (right_lot);
    1321            0 :                 right_iter->data = NULL;
    1322            0 :                 continue;
    1323              :             }
    1324           14 :             if (gnc_lot_is_closed (right_lot))
    1325            0 :                 continue;
    1326              : 
    1327              :             /* Balancing transactions for invoice/payments can only happen
    1328              :              * in the same account. */
    1329           14 :             if (acct != gnc_lot_get_account (right_lot))
    1330            0 :                 continue;
    1331              : 
    1332              : 
    1333              :             /* Only attempt to balance if the base lot and balancing lot are
    1334              :              * of the opposite sign. (Otherwise we would increase the balance
    1335              :              * of the lot - Duh */
    1336           14 :             right_lot_bal = gnc_lot_get_balance (right_lot);
    1337           14 :             if (gnc_numeric_positive_p (left_lot_bal) == gnc_numeric_positive_p (right_lot_bal))
    1338            0 :                 continue;
    1339              : 
    1340              :             /* Ok we found two lots than can (partly) offset each other.
    1341              :              * Depending on the lot types, a different action is needed to accomplish this.
    1342              :              * 1. Both lots are document lots (invoices/credit notes)
    1343              :              *    -> Create a lot linking transaction between the lots
    1344              :              * 2. Both lots are payment lots (lots without a document attached)
    1345              :              *    -> Use part of the bigger lot to the close the smaller lot
    1346              :              * 3. One document lot with one payment lot
    1347              :              *    -> Use (part of) the payment to offset (part of) the document lot,
    1348              :              *       Which one will be closed depends on which is the bigger one
    1349              :              */
    1350           14 :             right_lot_has_doc = (gncInvoiceGetInvoiceFromLot (right_lot) != NULL);
    1351           14 :             if (left_lot_has_doc && right_lot_has_doc)
    1352            0 :                 gncOwnerCreateLotLink (left_lot, right_lot, owner);
    1353           14 :             else if (!left_lot_has_doc && !right_lot_has_doc)
    1354            0 :             {
    1355            0 :                 gint cmp = gnc_numeric_compare (gnc_numeric_abs (left_lot_bal),
    1356              :                                                 gnc_numeric_abs (right_lot_bal));
    1357            0 :                 if (cmp >= 0)
    1358            0 :                     gncOwnerOffsetLots (left_lot, right_lot, owner);
    1359              :                 else
    1360            0 :                     gncOwnerOffsetLots (right_lot, left_lot, owner);
    1361              :             }
    1362              :             else
    1363              :             {
    1364           14 :                 GNCLot *doc_lot = left_lot_has_doc ? left_lot : right_lot;
    1365           14 :                 GNCLot *pay_lot = left_lot_has_doc ? right_lot : left_lot;
    1366              :                 // Ok, let's try to move a payment from pay_lot to doc_lot
    1367           14 :                 gncOwnerOffsetLots (pay_lot, doc_lot, owner);
    1368              :             }
    1369              : 
    1370              :             /* If we get here, then right_lot was modified
    1371              :              * If the lot has a document, send an event for send an event for it as well
    1372              :              * so it gets potentially updated as paid */
    1373              : 
    1374              :             {
    1375           14 :                 GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(right_lot);
    1376           14 :                 if (this_invoice)
    1377           14 :                     qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
    1378              :             }
    1379           14 :             left_modified = TRUE;
    1380              :         }
    1381              : 
    1382              :         /* If left_lot was modified and the lot has a document,
    1383              :          * send an event for send an event for it as well
    1384              :          * so it gets potentially updated as paid */
    1385           20 :         if (left_modified)
    1386              :         {
    1387           14 :             GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(left_lot);
    1388           14 :             if (this_invoice)
    1389            0 :                 qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
    1390              :         }
    1391           20 :         xaccAccountCommitEdit (acct);
    1392              : 
    1393              :     }
    1394              : }
    1395              : 
    1396              : /*
    1397              :  * Create a payment of "amount" for the owner and match it with
    1398              :  * the set of lots passed in.
    1399              :  * If
    1400              :  * - no lots were given
    1401              :  * - auto_pay is true
    1402              :  * then all open lots for the owner are considered.
    1403              :  */
    1404              : void
    1405            1 : gncOwnerApplyPaymentSecs (const GncOwner *owner, Transaction **preset_txn,
    1406              :                           GList *lots, Account *posted_acc, Account *xfer_acc,
    1407              :                           gnc_numeric amount, gnc_numeric exch, time64 date,
    1408              :                           const char *memo, const char *num, gboolean auto_pay)
    1409              : {
    1410            1 :     GNCLot *payment_lot = NULL;
    1411            1 :     GList *selected_lots = NULL;
    1412              : 
    1413              :     /* Verify our arguments */
    1414            1 :     if (!owner || !posted_acc
    1415            1 :                || (!xfer_acc && !gnc_numeric_zero_p (amount)) ) return;
    1416            1 :     g_return_if_fail (owner->owner.undefined);
    1417              : 
    1418            1 :     if (lots)
    1419            1 :         selected_lots = lots;
    1420            0 :     else if (auto_pay)
    1421            0 :         selected_lots = xaccAccountFindOpenLots (posted_acc, gncOwnerLotMatchOwnerFunc,
    1422              :                         (gpointer)owner, NULL);
    1423              : 
    1424              :     /* If there's a real amount to transfer create a lot for this payment */
    1425            1 :     if (!gnc_numeric_zero_p (amount))
    1426            1 :         payment_lot = gncOwnerCreatePaymentLotSecs (owner, preset_txn,
    1427              :                                                     posted_acc, xfer_acc,
    1428              :                                                     amount, exch, date, memo,
    1429              :                                                     num);
    1430              : 
    1431              :     /* And link the selected lots and the payment lot together as well
    1432              :      * as possible.  If the payment was bigger than the selected
    1433              :      * documents/overpayments, only part of the payment will be
    1434              :      * used. Similarly if more documents were selected than the
    1435              :      * payment value set, not all documents will be marked as paid. */
    1436            1 :     if (payment_lot)
    1437            1 :         selected_lots = g_list_prepend (selected_lots, payment_lot);
    1438              : 
    1439            1 :     gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots);
    1440            1 :     g_list_free (selected_lots);
    1441              : }
    1442              : 
    1443              : GList *
    1444            0 : gncOwnerGetAccountTypesList (const GncOwner *owner)
    1445              : {
    1446            0 :     g_return_val_if_fail (owner, NULL);
    1447              : 
    1448            0 :     switch (gncOwnerGetType (owner))
    1449              :     {
    1450            0 :     case GNC_OWNER_CUSTOMER:
    1451            0 :         return (g_list_prepend (NULL, (gpointer)ACCT_TYPE_RECEIVABLE));
    1452            0 :     case GNC_OWNER_VENDOR:
    1453              :     case GNC_OWNER_EMPLOYEE:
    1454            0 :         return (g_list_prepend (NULL, (gpointer)ACCT_TYPE_PAYABLE));
    1455              :         break;
    1456            0 :     default:
    1457            0 :         return (g_list_prepend (NULL, (gpointer)ACCT_TYPE_NONE));
    1458              :     }
    1459              : }
    1460              : 
    1461              : GList *
    1462            1 : gncOwnerGetCommoditiesList (const GncOwner *owner)
    1463              : {
    1464            1 :     g_return_val_if_fail (owner, NULL);
    1465            1 :     g_return_val_if_fail (gncOwnerGetCurrency(owner), NULL);
    1466              : 
    1467            1 :     return (g_list_prepend (NULL, gncOwnerGetCurrency(owner)));
    1468              : }
    1469              : 
    1470              : /*********************************************************************/
    1471              : /* Owner balance calculation routines                                */
    1472              : 
    1473              : /*
    1474              :  * Given an owner, extract the open balance from the owner and then
    1475              :  * convert it to the desired currency.
    1476              :  */
    1477              : gnc_numeric
    1478            0 : gncOwnerGetBalanceInCurrency (const GncOwner *owner,
    1479              :                               const gnc_commodity *report_currency)
    1480              : {
    1481            0 :     gnc_numeric balance = gnc_numeric_zero ();
    1482              :     QofBook *book;
    1483              :     gnc_commodity *owner_currency;
    1484              :     GNCPriceDB *pdb;
    1485            0 :     const gnc_numeric *cached_balance = NULL;
    1486              : 
    1487            0 :     g_return_val_if_fail (owner, gnc_numeric_zero ());
    1488              : 
    1489            0 :     book       = qof_instance_get_book (qofOwnerGetOwner (owner));
    1490            0 :     owner_currency = gncOwnerGetCurrency (owner);
    1491              : 
    1492            0 :     cached_balance = gncOwnerGetCachedBalance (owner);
    1493            0 :     if (cached_balance)
    1494            0 :         balance = *cached_balance;
    1495              :     else
    1496              :     {
    1497              :         /* No valid cache value found for balance. Let's recalculate */
    1498            0 :         GList *acct_list  = gnc_account_get_descendants (gnc_book_get_root_account (book));
    1499            0 :         GList *acct_types = gncOwnerGetAccountTypesList (owner);
    1500              :         GList *acct_node;
    1501              : 
    1502              :         /* For each account */
    1503            0 :         for (acct_node = acct_list; acct_node; acct_node = acct_node->next)
    1504              :         {
    1505            0 :             Account *account = acct_node->data;
    1506            0 :             GList *lot_list = NULL, *lot_node;
    1507              : 
    1508              :             /* Check if this account can have lots for the owner, otherwise skip to next */
    1509            0 :             if (g_list_index (acct_types, (gpointer)xaccAccountGetType (account))
    1510              :                     == -1)
    1511            0 :                 continue;
    1512              : 
    1513              : 
    1514            0 :             if (!gnc_commodity_equal (owner_currency, xaccAccountGetCommodity (account)))
    1515            0 :                 continue;
    1516              : 
    1517              :             /* Get a list of open lots for this owner and account */
    1518            0 :             lot_list = xaccAccountFindOpenLots (account, gncOwnerLotMatchOwnerFunc,
    1519              :                                                 (gpointer)owner, NULL);
    1520              :             /* For each lot */
    1521            0 :             for (lot_node = lot_list; lot_node; lot_node = lot_node->next)
    1522              :             {
    1523            0 :                 GNCLot *lot = lot_node->data;
    1524            0 :                 gnc_numeric lot_balance = gnc_lot_get_balance (lot);
    1525            0 :                 GncInvoice *invoice = gncInvoiceGetInvoiceFromLot(lot);
    1526            0 :                 if (invoice)
    1527            0 :                 balance = gnc_numeric_add (balance, lot_balance,
    1528            0 :                                             gnc_commodity_get_fraction (owner_currency), GNC_HOW_RND_ROUND_HALF_UP);
    1529              :             }
    1530            0 :             g_list_free (lot_list);
    1531              :         }
    1532            0 :         g_list_free (acct_list);
    1533            0 :         g_list_free (acct_types);
    1534              : 
    1535            0 :         gncOwnerSetCachedBalance (owner, &balance);
    1536              :     }
    1537              : 
    1538            0 :     pdb = gnc_pricedb_get_db (book);
    1539              : 
    1540            0 :     if (report_currency)
    1541            0 :         balance = gnc_pricedb_convert_balance_latest_price (
    1542              :                       pdb, balance, owner_currency, report_currency);
    1543              : 
    1544            0 :     return balance;
    1545              : }
    1546              : 
    1547              : 
    1548              : /* XXX: Yea, this is broken, but it should work fine for Queries.
    1549              :  * We're single-threaded, right?
    1550              :  */
    1551              : static GncOwner *
    1552           33 : owner_from_lot (GNCLot *lot)
    1553              : {
    1554              :     static GncOwner owner;
    1555              : 
    1556           33 :     if (!lot) return NULL;
    1557           12 :     if (gncOwnerGetOwnerFromLot (lot, &owner))
    1558            4 :         return &owner;
    1559              : 
    1560            8 :     return NULL;
    1561              : }
    1562              : 
    1563              : static void
    1564           83 : reg_lot (void)
    1565              : {
    1566              :     static QofParam params[] =
    1567              :     {
    1568              :         { OWNER_FROM_LOT, _GNC_MOD_NAME, (QofAccessFunc)owner_from_lot, NULL },
    1569              :         { NULL },
    1570              :     };
    1571              : 
    1572           83 :     qof_class_register (GNC_ID_LOT, NULL, params);
    1573           83 : }
    1574              : 
    1575            0 : gboolean gncOwnerGetOwnerFromTypeGuid (QofBook *book, GncOwner *owner, QofIdType type, GncGUID *guid)
    1576              : {
    1577            0 :     if (!book || !owner || !type || !guid) return FALSE;
    1578              : 
    1579            0 :     if (0 == g_strcmp0(type, GNC_ID_CUSTOMER))
    1580              :     {
    1581            0 :         GncCustomer *customer = gncCustomerLookup(book, guid);
    1582            0 :         gncOwnerInitCustomer(owner, customer);
    1583            0 :         return (NULL != customer);
    1584              :     }
    1585            0 :     else if (0 == g_strcmp0(type, GNC_ID_JOB))
    1586              :     {
    1587            0 :         GncJob *job = gncJobLookup(book, guid);
    1588            0 :         gncOwnerInitJob(owner, job);
    1589            0 :         return (NULL != job);
    1590              :     }
    1591            0 :     else if (0 == g_strcmp0(type, GNC_ID_VENDOR))
    1592              :     {
    1593            0 :         GncVendor *vendor = gncVendorLookup(book, guid);
    1594            0 :         gncOwnerInitVendor(owner, vendor);
    1595            0 :         return (NULL != vendor);
    1596              :     }
    1597            0 :     else if (0 == g_strcmp0(type, GNC_ID_EMPLOYEE))
    1598              :     {
    1599            0 :         GncEmployee *employee = gncEmployeeLookup(book, guid);
    1600            0 :         gncOwnerInitEmployee(owner, employee);
    1601            0 :         return (NULL != employee);
    1602              :     }
    1603            0 :     return 0;
    1604              : }
    1605              : 
    1606           83 : gboolean gncOwnerRegister (void)
    1607              : {
    1608              :     static QofParam params[] =
    1609              :     {
    1610              :         { OWNER_TYPE, QOF_TYPE_INT64,      (QofAccessFunc)gncOwnerGetType,          NULL },
    1611              :         { OWNER_CUSTOMER, GNC_ID_CUSTOMER, (QofAccessFunc)gncOwnerGetCustomer,      NULL },
    1612              :         { OWNER_JOB, GNC_ID_JOB,           (QofAccessFunc)gncOwnerGetJob,           NULL },
    1613              :         { OWNER_VENDOR, GNC_ID_VENDOR,     (QofAccessFunc)gncOwnerGetVendor,        NULL },
    1614              :         { OWNER_EMPLOYEE, GNC_ID_EMPLOYEE, (QofAccessFunc)gncOwnerGetEmployee,      NULL },
    1615              :         { OWNER_PARENT, GNC_ID_OWNER,      (QofAccessFunc)gncOwnerGetEndOwner,      NULL },
    1616              :         { OWNER_PARENTG, QOF_TYPE_GUID,    (QofAccessFunc)gncOwnerGetEndGUID,       NULL },
    1617              :         { OWNER_NAME, QOF_TYPE_STRING,     (QofAccessFunc)gncOwnerGetName, NULL },
    1618              :         { QOF_PARAM_GUID, QOF_TYPE_GUID,   (QofAccessFunc)gncOwnerGetGUID, NULL },
    1619              :         { NULL },
    1620              :     };
    1621              : 
    1622           83 :     qof_class_register (GNC_ID_OWNER, (QofSortFunc)gncOwnerCompare, params);
    1623           83 :     reg_lot ();
    1624              : 
    1625           83 :     return TRUE;
    1626              : }
    1627              : 
    1628              : const gnc_numeric*
    1629            0 : gncOwnerGetCachedBalance (const GncOwner *owner)
    1630              : {
    1631            0 :     if (!owner) return NULL;
    1632              : 
    1633            0 :     if (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER)
    1634            0 :         return gncCustomerGetCachedBalance (gncOwnerGetCustomer (owner));
    1635            0 :     else if (gncOwnerGetType (owner) == GNC_OWNER_VENDOR)
    1636            0 :         return gncVendorGetCachedBalance (gncOwnerGetVendor (owner));
    1637            0 :     else if (gncOwnerGetType (owner) == GNC_OWNER_EMPLOYEE)
    1638            0 :         return gncEmployeeGetCachedBalance (gncOwnerGetEmployee (owner));
    1639              : 
    1640            0 :     return NULL;
    1641              : }
    1642              : 
    1643            0 : void gncOwnerSetCachedBalance (const GncOwner *owner, const gnc_numeric *new_bal)
    1644              : {
    1645            0 :     if (!owner) return;
    1646              : 
    1647            0 :     if (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER)
    1648            0 :         gncCustomerSetCachedBalance (gncOwnerGetCustomer (owner), new_bal);
    1649            0 :     else if (gncOwnerGetType (owner) == GNC_OWNER_VENDOR)
    1650            0 :         gncVendorSetCachedBalance (gncOwnerGetVendor (owner), new_bal);
    1651            0 :     else if (gncOwnerGetType (owner) == GNC_OWNER_EMPLOYEE)
    1652            0 :         gncEmployeeSetCachedBalance (gncOwnerGetEmployee (owner), new_bal);
    1653              : }
        

Generated by: LCOV version 2.0-1