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