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