LCOV - code coverage report
Current view: top level - libgnucash/engine - gncOwner.c (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 36.3 % 809 294
Test Date: 2025-02-07 16:25:45 Functions: 58.3 % 60 35
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: - 0 0

             Branch data     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                 :          85 : GncOwner * gncOwnerNew (void)
      58                 :             : {
      59                 :             :     GncOwner *o;
      60                 :             : 
      61                 :          85 :     o = g_new0 (GncOwner, 1);
      62                 :          85 :     o->type = GNC_OWNER_NONE;
      63                 :          85 :     return o;
      64                 :             : }
      65                 :             : 
      66                 :          48 : void gncOwnerFree (GncOwner *owner)
      67                 :             : {
      68                 :          48 :     if (!owner) return;
      69                 :          48 :     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                 :         208 : void gncOwnerInitCustomer (GncOwner *owner, GncCustomer *customer)
     173                 :             : {
     174                 :         208 :     if (!owner) return;
     175                 :         208 :     owner->type = GNC_OWNER_CUSTOMER;
     176                 :         208 :     owner->owner.customer = customer;
     177                 :             : }
     178                 :             : 
     179                 :           9 : void gncOwnerInitJob (GncOwner *owner, GncJob *job)
     180                 :             : {
     181                 :           9 :     if (!owner) return;
     182                 :           9 :     owner->type = GNC_OWNER_JOB;
     183                 :           9 :     owner->owner.job = job;
     184                 :             : }
     185                 :             : 
     186                 :          12 : void gncOwnerInitVendor (GncOwner *owner, GncVendor *vendor)
     187                 :             : {
     188                 :          12 :     if (!owner) return;
     189                 :          12 :     owner->type = GNC_OWNER_VENDOR;
     190                 :          12 :     owner->owner.vendor = vendor;
     191                 :             : }
     192                 :             : 
     193                 :           4 : void gncOwnerInitEmployee (GncOwner *owner, GncEmployee *employee)
     194                 :             : {
     195                 :           4 :     if (!owner) return;
     196                 :           4 :     owner->type = GNC_OWNER_EMPLOYEE;
     197                 :           4 :     owner->owner.employee = employee;
     198                 :             : }
     199                 :             : 
     200                 :        2244 : GncOwnerType gncOwnerGetType (const GncOwner *owner)
     201                 :             : {
     202                 :        2244 :     if (!owner) return GNC_OWNER_NONE;
     203                 :        1900 :     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                 :          12 : QofIdTypeConst gncOwnerTypeToQofIdType(GncOwnerType t)
     236                 :             : {
     237                 :          12 :     QofIdTypeConst type = NULL;
     238                 :          12 :     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                 :          12 :     case GNC_OWNER_CUSTOMER :
     251                 :             :     {
     252                 :          12 :         type = GNC_ID_CUSTOMER;
     253                 :          12 :         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                 :          12 :     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                 :           6 : qofOwnerSetEntity (GncOwner *owner, QofInstance *ent)
     320                 :             : {
     321                 :           6 :     if (!owner || !ent)
     322                 :             :     {
     323                 :           0 :         return;
     324                 :             :     }
     325                 :           6 :     if (0 == g_strcmp0(ent->e_type, GNC_ID_CUSTOMER))
     326                 :             :     {
     327                 :           6 :         owner->type = GNC_OWNER_CUSTOMER;
     328                 :           6 :         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                 :         439 : GncCustomer * gncOwnerGetCustomer (const GncOwner *owner)
     370                 :             : {
     371                 :         439 :     if (!owner) return NULL;
     372                 :         439 :     if (owner->type != GNC_OWNER_CUSTOMER) return NULL;
     373                 :         439 :     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                 :          50 : GncVendor * gncOwnerGetVendor (const GncOwner *owner)
     384                 :             : {
     385                 :          50 :     if (!owner) return NULL;
     386                 :          50 :     if (owner->type != GNC_OWNER_VENDOR) return NULL;
     387                 :          50 :     return owner->owner.vendor;
     388                 :             : }
     389                 :             : 
     390                 :          48 : GncEmployee * gncOwnerGetEmployee (const GncOwner *owner)
     391                 :             : {
     392                 :          48 :     if (!owner) return NULL;
     393                 :          48 :     if (owner->type != GNC_OWNER_EMPLOYEE) return NULL;
     394                 :          48 :     return owner->owner.employee;
     395                 :             : }
     396                 :             : 
     397                 :         167 : void gncOwnerCopy (const GncOwner *src, GncOwner *dest)
     398                 :             : {
     399                 :         167 :     if (!src || !dest) return;
     400                 :         167 :     if (src == dest) return;
     401                 :         167 :     *dest = *src;
     402                 :             : }
     403                 :             : 
     404                 :         104 : gboolean gncOwnerEqual (const GncOwner *a, const GncOwner *b)
     405                 :             : {
     406                 :         104 :     if (!a || !b) return FALSE;
     407                 :         104 :     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                 :         179 : const char * gncOwnerGetName (const GncOwner *owner)
     440                 :             : {
     441                 :         179 :     if (!owner) return NULL;
     442                 :         179 :     switch (owner->type)
     443                 :             :     {
     444                 :           0 :     case GNC_OWNER_NONE:
     445                 :             :     case GNC_OWNER_UNDEFINED:
     446                 :             :     default:
     447                 :           0 :         return NULL;
     448                 :         118 :     case GNC_OWNER_CUSTOMER:
     449                 :         118 :         return gncCustomerGetName (owner->owner.customer);
     450                 :           5 :     case GNC_OWNER_JOB:
     451                 :           5 :         return gncJobGetName (owner->owner.job);
     452                 :          28 :     case GNC_OWNER_VENDOR:
     453                 :          28 :         return gncVendorGetName (owner->owner.vendor);
     454                 :          28 :     case GNC_OWNER_EMPLOYEE:
     455                 :          28 :         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                 :           8 : gboolean gncOwnerGetActive (const GncOwner *owner)
     499                 :             : {
     500                 :           8 :     if (!owner) return FALSE;
     501                 :           8 :     switch (owner->type)
     502                 :             :     {
     503                 :           0 :     case GNC_OWNER_NONE:
     504                 :             :     case GNC_OWNER_UNDEFINED:
     505                 :             :     default:
     506                 :           0 :         return FALSE;
     507                 :           4 :     case GNC_OWNER_CUSTOMER:
     508                 :           4 :         return gncCustomerGetActive (owner->owner.customer);
     509                 :           2 :     case GNC_OWNER_VENDOR:
     510                 :           2 :         return gncVendorGetActive (owner->owner.vendor);
     511                 :           2 :     case GNC_OWNER_EMPLOYEE:
     512                 :           2 :         return gncEmployeeGetActive (owner->owner.employee);
     513                 :           0 :     case GNC_OWNER_JOB:
     514                 :           0 :         return gncJobGetActive (owner->owner.job);
     515                 :             :     }
     516                 :             : }
     517                 :             : 
     518                 :         282 : const GncGUID * gncOwnerGetGUID (const GncOwner *owner)
     519                 :             : {
     520                 :         282 :     if (!owner) return NULL;
     521                 :             : 
     522                 :         281 :     switch (owner->type)
     523                 :             :     {
     524                 :           0 :     case GNC_OWNER_NONE:
     525                 :             :     case GNC_OWNER_UNDEFINED:
     526                 :             :     default:
     527                 :           0 :         return NULL;
     528                 :         227 :     case GNC_OWNER_CUSTOMER:
     529                 :         227 :         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                 :          28 :     case GNC_OWNER_VENDOR:
     533                 :          28 :         return qof_instance_get_guid (QOF_INSTANCE(owner->owner.vendor));
     534                 :          26 :     case GNC_OWNER_EMPLOYEE:
     535                 :          26 :         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                 :          18 : GncGUID gncOwnerRetGUID (GncOwner *owner)
     565                 :             : {
     566                 :          18 :     const GncGUID *guid = gncOwnerGetGUID (owner);
     567                 :          18 :     if (guid)
     568                 :          17 :         return *guid;
     569                 :           1 :     return *guid_null ();
     570                 :             : }
     571                 :             : 
     572                 :        2002 : const GncOwner * gncOwnerGetEndOwner (const GncOwner *owner)
     573                 :             : {
     574                 :        2002 :     if (!owner) return NULL;
     575                 :        1970 :     switch (owner->type)
     576                 :             :     {
     577                 :           0 :     case GNC_OWNER_NONE:
     578                 :             :     case GNC_OWNER_UNDEFINED:
     579                 :             :     default:
     580                 :           0 :         return NULL;
     581                 :        1849 :     case GNC_OWNER_CUSTOMER:
     582                 :             :     case GNC_OWNER_VENDOR:
     583                 :             :     case GNC_OWNER_EMPLOYEE:
     584                 :        1849 :         return owner;
     585                 :         121 :     case GNC_OWNER_JOB:
     586                 :         121 :         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                 :        1907 : const GncGUID * gncOwnerGetEndGUID (const GncOwner *owner)
     617                 :             : {
     618                 :        1907 :     if (!owner) return NULL;
     619                 :         240 :     return gncOwnerGetGUID (gncOwnerGetEndOwner (owner));
     620                 :             : }
     621                 :             : 
     622                 :          22 : void gncOwnerAttachToLot (const GncOwner *owner, GNCLot *lot)
     623                 :             : {
     624                 :          22 :      if (!owner || !lot)
     625                 :           0 :         return;
     626                 :             : 
     627                 :          22 :     gnc_lot_begin_edit (lot);
     628                 :             : 
     629                 :          44 :     qof_instance_set (QOF_INSTANCE (lot),
     630                 :          22 :                       GNC_OWNER_TYPE, (gint64)gncOwnerGetType (owner),
     631                 :             :                       GNC_OWNER_GUID, gncOwnerGetGUID (owner),
     632                 :             :                       NULL);
     633                 :          22 :     gnc_lot_commit_edit (lot);
     634                 :             : }
     635                 :             : 
     636                 :         533 : gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner)
     637                 :             : {
     638                 :         533 :     GncGUID *guid = NULL;
     639                 :             :     QofBook *book;
     640                 :         533 :     GncOwnerType type = GNC_OWNER_NONE;
     641                 :         533 :     guint64 type64 = 0;
     642                 :             : 
     643                 :         533 :     if (!lot || !owner) return FALSE;
     644                 :             : 
     645                 :         530 :     book = gnc_lot_get_book (lot);
     646                 :         530 :     qof_instance_get (QOF_INSTANCE (lot),
     647                 :             :                       GNC_OWNER_TYPE, &type64,
     648                 :             :                       GNC_OWNER_GUID, &guid,
     649                 :             :                       NULL);
     650                 :         530 :     type = (GncOwnerType) type64;
     651                 :         530 :     switch (type)
     652                 :             :     {
     653                 :         174 :     case GNC_OWNER_CUSTOMER:
     654                 :         174 :         gncOwnerInitCustomer (owner, gncCustomerLookup (book, guid));
     655                 :         174 :         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                 :         356 :     default:
     666                 :         356 :         guid_free (guid);
     667                 :         356 :         return FALSE;
     668                 :             :     }
     669                 :             : 
     670                 :         174 :     guid_free (guid);
     671                 :         174 :     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                 :          12 : 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                 :          12 :     Split *xfer_split = NULL;
     760                 :          12 :     Transaction *txn = NULL;
     761                 :             :     GNCLot *payment_lot;
     762                 :          12 :     gnc_numeric xfer_amount = gnc_numeric_zero();
     763                 :          12 :     gnc_numeric txn_value = gnc_numeric_zero();
     764                 :             : 
     765                 :             :     /* Verify our arguments */
     766                 :          12 :     if (!owner || !posted_acc || !xfer_acc) return NULL;
     767                 :          12 :     g_return_val_if_fail (owner->owner.undefined != NULL, NULL);
     768                 :             : 
     769                 :             :     /* Compute the ancillary data */
     770                 :          12 :     book = gnc_account_get_book (posted_acc);
     771                 :          12 :     name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner));
     772                 :          12 :     post_comm = xaccAccountGetCommodity (posted_acc);
     773                 :          12 :     xfer_comm = xaccAccountGetCommodity (xfer_acc);
     774                 :             : 
     775                 :             : //    reverse = use_reversed_payment_amounts(owner);
     776                 :             : 
     777                 :          12 :     if (preset_txn && *preset_txn)
     778                 :           0 :         txn = *preset_txn;
     779                 :             : 
     780                 :          12 :     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                 :          12 :         txn = xaccMallocTransaction (book);
     802                 :          12 :         xaccTransBeginEdit (txn);
     803                 :             :     }
     804                 :             : 
     805                 :             :     /* Complete transaction setup */
     806                 :          12 :     xaccTransSetDescription (txn, name ? name : "");
     807                 :          24 :     if (!gnc_commodity_equal(xaccTransGetCurrency (txn), post_comm) &&
     808                 :          12 :         !gnc_commodity_equal (xaccTransGetCurrency (txn), xfer_comm))
     809                 :          12 :         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                 :          12 :     if (gnc_commodity_equal(post_comm, xfer_comm))
     818                 :          12 :         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                 :          12 :     if (gnc_commodity_equal(post_comm, xaccTransGetCurrency (txn)))
     824                 :          12 :         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                 :          12 :     if (!xfer_split)
     831                 :             :     {
     832                 :             :         /* The split for the transfer account */
     833                 :          12 :         xfer_split = xaccMallocSplit (book);
     834                 :          12 :         xaccSplitSetMemo (xfer_split, memo);
     835                 :             :         /* set per book option */
     836                 :          12 :         gnc_set_num_action (NULL, xfer_split, num, _("Payment"));
     837                 :          12 :         xaccAccountBeginEdit (xfer_acc);
     838                 :          12 :         xaccAccountInsertSplit (xfer_acc, xfer_split);
     839                 :          12 :         xaccAccountCommitEdit (xfer_acc);
     840                 :          12 :         xaccTransAppendSplit (txn, xfer_split);
     841                 :             : 
     842                 :          12 :         xaccSplitSetAmount(xfer_split, xfer_amount); /* Payment in xfer account currency */
     843                 :          12 :         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                 :          12 :     split = xaccMallocSplit (book);
     857                 :          12 :     xaccSplitSetMemo (split, memo);
     858                 :             :     /* set per book option */
     859                 :          12 :     xaccSplitSetAction (split, _("Payment"));
     860                 :          12 :     xaccAccountBeginEdit (posted_acc);
     861                 :          12 :     xaccAccountInsertSplit (posted_acc, split);
     862                 :          12 :     xaccAccountCommitEdit (posted_acc);
     863                 :          12 :     xaccTransAppendSplit (txn, split);
     864                 :          12 :     xaccSplitSetAmount (split, gnc_numeric_neg (amount));
     865                 :          12 :     xaccSplitSetValue (split, gnc_numeric_neg (txn_value));
     866                 :             : 
     867                 :             :     /* Create a new lot for the payment */
     868                 :          12 :     payment_lot = gnc_lot_new (book);
     869                 :          12 :     gncOwnerAttachToLot (owner, payment_lot);
     870                 :          12 :     gnc_lot_add_split (payment_lot, split);
     871                 :             : 
     872                 :             :     /* Mark the transaction as a payment */
     873                 :          12 :     xaccTransSetNum (txn, num);
     874                 :          12 :     xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
     875                 :             : 
     876                 :             :     /* Set date for transaction */
     877                 :          12 :     xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
     878                 :          12 :     xaccTransSetDatePostedSecs (txn, date);
     879                 :             : 
     880                 :             :     /* Commit this new transaction */
     881                 :          12 :     xaccTransCommitEdit (txn);
     882                 :          12 :     if (preset_txn)
     883                 :          12 :         *preset_txn = txn;
     884                 :             : 
     885                 :          12 :     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                 :          12 : Split *gncOwnerFindOffsettingSplit (GNCLot *lot, gnc_numeric target_amount)
     897                 :             : {
     898                 :          12 :     SplitList *ls_iter = NULL;
     899                 :          12 :     Split *best_split = NULL;
     900                 :          12 :     gnc_numeric best_amt = { 0, 1};
     901                 :          12 :     gint best_flags = 0;
     902                 :             : 
     903                 :          12 :     if (!lot)
     904                 :           0 :         return NULL;
     905                 :             : 
     906                 :          24 :     for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
     907                 :             :     {
     908                 :          12 :         Split *split = ls_iter->data;
     909                 :             : 
     910                 :          12 :         if (!split)
     911                 :           0 :             continue;
     912                 :             : 
     913                 :             : 
     914                 :          12 :         Transaction *txn = xaccSplitGetParent (split);
     915                 :          12 :         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                 :          12 :         gnc_numeric split_amount = xaccSplitGetAmount (split);
     926                 :          12 :         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                 :          12 :         gint amt_cmp = gnc_numeric_compare (gnc_numeric_abs (split_amount),
     932                 :             :                                             gnc_numeric_abs (target_amount));
     933                 :          12 :         gint new_flags = 0;
     934                 :          12 :         if (amt_cmp == 0)
     935                 :           6 :             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                 :          12 :         if (xaccTransGetTxnType (txn) != TXN_TYPE_LINK)
     942                 :          12 :             new_flags += is_pay_split;
     943                 :             : 
     944                 :          24 :         if ((new_flags >= best_flags) &&
     945                 :          12 :             (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                 :          12 :             best_split = split;
     950                 :          12 :             best_flags = new_flags;
     951                 :          12 :             best_amt   = split_amount;
     952                 :             :         }
     953                 :             :     }
     954                 :             : 
     955                 :          12 :     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 documents: ");
    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                 :          12 : 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                 :          12 :     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                 :          12 :     target_offset = gnc_lot_get_balance (to_lot);
    1235                 :          12 :     if (gnc_numeric_zero_p (target_offset))
    1236                 :           0 :         return; // to_lot is already balanced, nothing more to do
    1237                 :             : 
    1238                 :          12 :     split = gncOwnerFindOffsettingSplit (from_lot, target_offset);
    1239                 :          12 :     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                 :          12 :     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                 :          12 :     gnc_lot_add_split (to_lot, split);
    1253                 :             : 
    1254                 :             : }
    1255                 :             : 
    1256                 :          13 : 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                 :          13 :     if (!owner) return;
    1270                 :          13 :     if (!lots) return;
    1271                 :             : 
    1272                 :          38 :     for (left_iter = lots; left_iter; left_iter = left_iter->next)
    1273                 :             :     {
    1274                 :          25 :         GNCLot *left_lot = left_iter->data;
    1275                 :             :         gnc_numeric left_lot_bal;
    1276                 :             :         gboolean left_lot_has_doc;
    1277                 :          25 :         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                 :          25 :         if (!left_lot)
    1286                 :           0 :             continue;
    1287                 :          25 :         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                 :          25 :         if (gnc_lot_is_closed (left_lot))
    1294                 :           7 :             continue;
    1295                 :             : 
    1296                 :          18 :         acct = gnc_lot_get_account (left_lot);
    1297                 :          18 :         xaccAccountBeginEdit (acct);
    1298                 :             : 
    1299                 :          18 :         left_lot_bal = gnc_lot_get_balance (left_lot);
    1300                 :          18 :         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                 :          30 :         for (right_iter = left_iter->next; right_iter; right_iter = right_iter->next)
    1307                 :             :         {
    1308                 :          12 :             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                 :          12 :             if (!right_lot)
    1317                 :           0 :                 continue;
    1318                 :          12 :             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                 :          12 :             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                 :          12 :             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                 :          12 :             right_lot_bal = gnc_lot_get_balance (right_lot);
    1337                 :          12 :             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                 :          12 :             right_lot_has_doc = (gncInvoiceGetInvoiceFromLot (right_lot) != NULL);
    1351                 :          12 :             if (left_lot_has_doc && right_lot_has_doc)
    1352                 :           0 :                 gncOwnerCreateLotLink (left_lot, right_lot, owner);
    1353                 :          12 :             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                 :          12 :                 GNCLot *doc_lot = left_lot_has_doc ? left_lot : right_lot;
    1365                 :          12 :                 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                 :          12 :                 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                 :          12 :                 GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(right_lot);
    1376                 :          12 :                 if (this_invoice)
    1377                 :          12 :                     qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
    1378                 :             :             }
    1379                 :          12 :             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                 :          18 :         if (left_modified)
    1386                 :             :         {
    1387                 :          12 :             GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(left_lot);
    1388                 :          12 :             if (this_invoice)
    1389                 :           0 :                 qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
    1390                 :             :         }
    1391                 :          18 :         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                 :           0 : 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                 :           0 :     GNCLot *payment_lot = NULL;
    1411                 :           0 :     GList *selected_lots = NULL;
    1412                 :             : 
    1413                 :             :     /* Verify our arguments */
    1414                 :           0 :     if (!owner || !posted_acc
    1415                 :           0 :                || (!xfer_acc && !gnc_numeric_zero_p (amount)) ) return;
    1416                 :           0 :     g_return_if_fail (owner->owner.undefined);
    1417                 :             : 
    1418                 :           0 :     if (lots)
    1419                 :           0 :         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                 :           0 :     if (!gnc_numeric_zero_p (amount))
    1426                 :           0 :         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                 :           0 :     if (payment_lot)
    1437                 :           0 :         selected_lots = g_list_prepend (selected_lots, payment_lot);
    1438                 :             : 
    1439                 :           0 :     gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots);
    1440                 :           0 :     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                 :          81 : 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                 :          81 :     qof_class_register (GNC_ID_LOT, NULL, params);
    1573                 :          81 : }
    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                 :          81 : 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                 :          81 :     qof_class_register (GNC_ID_OWNER, (QofSortFunc)gncOwnerCompare, params);
    1623                 :          81 :     reg_lot ();
    1624                 :             : 
    1625                 :          81 :     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