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