1 /********************************************************************\
2 * gnc-lot.c -- AR/AP invoices; inventory lots; stock lots *
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 * FILE:
24 * gnc-lot.c
25 *
26 * FUNCTION:
27 * Lots implement the fundamental conceptual idea behind invoices,
28 * inventory lots, and stock market investment lots. See the file
29 * src/doc/lots.txt for implementation overview.
30 *
31 * XXX Lots are not currently treated in a correct transactional
32 * manner. There's now a per-Lot dirty flag in the QofInstance, but
33 * this code still needs to emit the correct signals when a lot has
34 * changed. This is true both in the Scrub2.c and in
35 * src/gnome/dialog-lot-viewer.c
36 *
37 * HISTORY:
38 * Created by Linas Vepstas May 2002
39 * Copyright (c) 2002,2003 Linas Vepstas <linas@linas.org>
40 */
41
42 #include <config.h>
43
44 #include <glib.h>
45 #include <glib/gi18n.h>
46 #include <qofinstance-p.h>
47
48 #include "Account.h"
49 #include "AccountP.h"
50 #include "gnc-lot.h"
51 #include "gnc-lot-p.h"
52 #include "cap-gains.h"
53 #include "Transaction.h"
54 #include "TransactionP.h"
55 #include "gncInvoice.h"
56
57 /* This static indicates the debugging module that this .o belongs to. */
58 static QofLogModule log_module = GNC_MOD_LOT;
59
60 struct gnc_lot_s
61 {
62 QofInstance inst;
63 };
64
65 enum
66 {
67 PROP_0,
68 // PROP_ACCOUNT, /* Table */
69 PROP_IS_CLOSED, /* Table */
70
71 PROP_INVOICE, /* KVP */
72 PROP_OWNER_TYPE, /* KVP */
73 PROP_OWNER_GUID, /* KVP */
74
75 PROP_RUNTIME_0,
76 PROP_MARKER, /* Runtime */
77 };
78
79 typedef struct GNCLotPrivate
80 {
81 /* Account to which this lot applies. All splits in the lot must
82 * belong to this account.
83 */
84 Account * account;
85
86 /* List of splits that belong to this lot. */
87 SplitList *splits;
88
89 char *title;
90 char *notes;
91
92 GncInvoice *cached_invoice;
93 /* Handy cached value to indicate if lot is closed. */
94 /* If value is negative, then the cache is invalid. */
95 signed char is_closed;
96 #define LOT_CLOSED_UNKNOWN (-1)
97
98 /* traversal marker, handy for preventing recursion */
99 unsigned char marker;
100 } GNCLotPrivate;
101
102 #define GET_PRIVATE(o) \
103 ((GNCLotPrivate*)g_type_instance_get_private((GTypeInstance*)o, GNC_TYPE_LOT))
104
105 #define gnc_lot_set_guid(L,G) qof_instance_set_guid(QOF_INSTANCE(L),&(G))
106
107 /* ============================================================= */
108
109 static char*
110 is_unset = "unset";
111
112 /* GObject Initialization */
G_DEFINE_TYPE_WITH_PRIVATE(GNCLot,gnc_lot,QOF_TYPE_INSTANCE)113 G_DEFINE_TYPE_WITH_PRIVATE(GNCLot, gnc_lot, QOF_TYPE_INSTANCE)
114
115 static void
116 gnc_lot_init(GNCLot* lot)
117 {
118 GNCLotPrivate* priv;
119
120 priv = GET_PRIVATE(lot);
121 priv->account = NULL;
122 priv->splits = NULL;
123 priv->cached_invoice = NULL;
124 priv->is_closed = LOT_CLOSED_UNKNOWN;
125 priv->title = is_unset;
126 priv->notes = is_unset;
127 priv->marker = 0;
128 }
129
130 static void
gnc_lot_dispose(GObject * lotp)131 gnc_lot_dispose(GObject *lotp)
132 {
133 G_OBJECT_CLASS(gnc_lot_parent_class)->dispose(lotp);
134 }
135
136 static void
gnc_lot_finalize(GObject * lotp)137 gnc_lot_finalize(GObject* lotp)
138 {
139 G_OBJECT_CLASS(gnc_lot_parent_class)->finalize(lotp);
140 }
141
142 static void
gnc_lot_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)143 gnc_lot_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
144 {
145 GNCLot* lot;
146 GNCLotPrivate* priv;
147 gchar *key;
148
149 g_return_if_fail(GNC_IS_LOT(object));
150
151 lot = GNC_LOT(object);
152 priv = GET_PRIVATE(lot);
153 switch (prop_id)
154 {
155 case PROP_IS_CLOSED:
156 g_value_set_int(value, priv->is_closed);
157 break;
158 case PROP_MARKER:
159 g_value_set_int(value, priv->marker);
160 break;
161 case PROP_INVOICE:
162 qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
163 break;
164 case PROP_OWNER_TYPE:
165 qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
166 break;
167 case PROP_OWNER_GUID:
168 qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
169 break;
170 default:
171 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
172 break;
173 }
174 }
175
176 static void
gnc_lot_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)177 gnc_lot_set_property (GObject* object,
178 guint prop_id,
179 const GValue* value,
180 GParamSpec* pspec)
181 {
182 GNCLot* lot;
183 GNCLotPrivate* priv;
184 gchar *key = NULL;
185
186 g_return_if_fail(GNC_IS_LOT(object));
187
188 lot = GNC_LOT(object);
189 if (prop_id < PROP_RUNTIME_0)
190 g_assert (qof_instance_get_editlevel(lot));
191
192 priv = GET_PRIVATE(lot);
193 switch (prop_id)
194 {
195 case PROP_IS_CLOSED:
196 priv->is_closed = g_value_get_int(value);
197 break;
198 case PROP_MARKER:
199 priv->marker = g_value_get_int(value);
200 break;
201 case PROP_INVOICE:
202 qof_instance_set_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
203 break;
204 case PROP_OWNER_TYPE:
205 qof_instance_set_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
206 break;
207 case PROP_OWNER_GUID:
208 qof_instance_set_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
209 break;
210 default:
211 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
212 break;
213 }
214 }
215
216 static void
gnc_lot_class_init(GNCLotClass * klass)217 gnc_lot_class_init(GNCLotClass* klass)
218 {
219 GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
220
221 gobject_class->dispose = gnc_lot_dispose;
222 gobject_class->finalize = gnc_lot_finalize;
223 gobject_class->get_property = gnc_lot_get_property;
224 gobject_class->set_property = gnc_lot_set_property;
225
226 g_object_class_install_property(
227 gobject_class,
228 PROP_IS_CLOSED,
229 g_param_spec_int("is-closed",
230 "Is Lot Closed",
231 "Indication of whether this lot is open "
232 "or closed to further changes.",
233 -1, 1, 0,
234 G_PARAM_READWRITE));
235
236 g_object_class_install_property(
237 gobject_class,
238 PROP_MARKER,
239 g_param_spec_int("marker",
240 "Lot marker",
241 "Ipsum Lorem",
242 0, G_MAXINT8, 0,
243 G_PARAM_READWRITE));
244
245 g_object_class_install_property(
246 gobject_class,
247 PROP_INVOICE,
248 g_param_spec_boxed("invoice",
249 "Invoice attached to lot",
250 "Used by GncInvoice",
251 GNC_TYPE_GUID,
252 G_PARAM_READWRITE));
253
254 g_object_class_install_property(
255 gobject_class,
256 PROP_OWNER_TYPE,
257 g_param_spec_int64("owner-type",
258 "Owning Entity Type of lot",
259 "Used by GncOwner",
260 0, G_MAXINT64, 0,
261 G_PARAM_READWRITE));
262
263 g_object_class_install_property(
264 gobject_class,
265 PROP_OWNER_GUID,
266 g_param_spec_boxed("owner-guid",
267 "Owner attached to lot",
268 "Used by GncOwner",
269 GNC_TYPE_GUID,
270 G_PARAM_READWRITE));
271 }
272
273 GNCLot *
gnc_lot_new(QofBook * book)274 gnc_lot_new (QofBook *book)
275 {
276 GNCLot *lot;
277 g_return_val_if_fail (book, NULL);
278
279 lot = g_object_new (GNC_TYPE_LOT, NULL);
280 qof_instance_init_data(QOF_INSTANCE(lot), GNC_ID_LOT, book);
281 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_CREATE, NULL);
282 return lot;
283 }
284
285 static void
gnc_lot_free(GNCLot * lot)286 gnc_lot_free(GNCLot* lot)
287 {
288 GList *node;
289 GNCLotPrivate* priv;
290 if (!lot) return;
291
292 ENTER ("(lot=%p)", lot);
293 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_DESTROY, NULL);
294
295 priv = GET_PRIVATE(lot);
296 for (node = priv->splits; node; node = node->next)
297 {
298 Split *s = node->data;
299 s->lot = NULL;
300 }
301 g_list_free (priv->splits);
302
303 if (priv->account && !qof_instance_get_destroying(priv->account))
304 xaccAccountRemoveLot (priv->account, lot);
305
306 if (priv->notes != is_unset)
307 g_free (priv->notes);
308
309 if (priv->title != is_unset)
310 g_free (priv->title);
311
312 priv->notes = NULL;
313 priv->title = NULL;
314 priv->account = NULL;
315 priv->is_closed = TRUE;
316 /* qof_instance_release (&lot->inst); */
317 g_object_unref (lot);
318
319 LEAVE();
320 }
321
322 void
gnc_lot_destroy(GNCLot * lot)323 gnc_lot_destroy (GNCLot *lot)
324 {
325 if (!lot) return;
326
327 gnc_lot_begin_edit(lot);
328 qof_instance_set_destroying(lot, TRUE);
329 gnc_lot_commit_edit(lot);
330 }
331
332 /* ============================================================= */
333
334 void
gnc_lot_begin_edit(GNCLot * lot)335 gnc_lot_begin_edit (GNCLot *lot)
336 {
337 qof_begin_edit(QOF_INSTANCE(lot));
338 }
339
commit_err(QofInstance * inst,QofBackendError errcode)340 static void commit_err (QofInstance *inst, QofBackendError errcode)
341 {
342 PERR ("Failed to commit: %d", errcode);
343 gnc_engine_signal_commit_error( errcode );
344 }
345
lot_free(QofInstance * inst)346 static void lot_free(QofInstance* inst)
347 {
348 GNCLot* lot = GNC_LOT(inst);
349
350 gnc_lot_free(lot);
351 }
352
noop(QofInstance * inst)353 static void noop (QofInstance *inst) {}
354
355 void
gnc_lot_commit_edit(GNCLot * lot)356 gnc_lot_commit_edit (GNCLot *lot)
357 {
358 if (!qof_commit_edit (QOF_INSTANCE(lot))) return;
359 qof_commit_edit_part2 (QOF_INSTANCE(lot), commit_err, noop, lot_free);
360 }
361
362 /* ============================================================= */
363
364 GNCLot *
gnc_lot_lookup(const GncGUID * guid,QofBook * book)365 gnc_lot_lookup (const GncGUID *guid, QofBook *book)
366 {
367 QofCollection *col;
368 if (!guid || !book) return NULL;
369 col = qof_book_get_collection (book, GNC_ID_LOT);
370 return (GNCLot *) qof_collection_lookup_entity (col, guid);
371 }
372
373 QofBook *
gnc_lot_get_book(GNCLot * lot)374 gnc_lot_get_book (GNCLot *lot)
375 {
376 return qof_instance_get_book(QOF_INSTANCE(lot));
377 }
378
379 /* ============================================================= */
380
381 gboolean
gnc_lot_is_closed(GNCLot * lot)382 gnc_lot_is_closed (GNCLot *lot)
383 {
384 GNCLotPrivate* priv;
385 if (!lot) return TRUE;
386 priv = GET_PRIVATE(lot);
387 if (0 > priv->is_closed) gnc_lot_get_balance (lot);
388 return priv->is_closed;
389 }
390
391 Account *
gnc_lot_get_account(const GNCLot * lot)392 gnc_lot_get_account (const GNCLot *lot)
393 {
394 GNCLotPrivate* priv;
395 if (!lot) return NULL;
396 priv = GET_PRIVATE(lot);
397 return priv->account;
398 }
399
gnc_lot_get_cached_invoice(const GNCLot * lot)400 GncInvoice * gnc_lot_get_cached_invoice (const GNCLot *lot)
401 {
402 if (!lot) return NULL;
403 return GET_PRIVATE(lot)->cached_invoice;
404 }
405
406 void
gnc_lot_set_cached_invoice(GNCLot * lot,GncInvoice * invoice)407 gnc_lot_set_cached_invoice(GNCLot* lot, GncInvoice *invoice)
408 {
409 if (!lot) return;
410 GET_PRIVATE(lot)->cached_invoice = invoice;
411 }
412
413 void
gnc_lot_set_account(GNCLot * lot,Account * account)414 gnc_lot_set_account(GNCLot* lot, Account* account)
415 {
416 if (lot != NULL)
417 {
418 GNCLotPrivate* priv;
419 priv = GET_PRIVATE(lot);
420 priv->account = account;
421 }
422 }
423
424 void
gnc_lot_set_closed_unknown(GNCLot * lot)425 gnc_lot_set_closed_unknown(GNCLot* lot)
426 {
427 GNCLotPrivate* priv;
428 if (lot != NULL)
429 {
430 priv = GET_PRIVATE(lot);
431 priv->is_closed = LOT_CLOSED_UNKNOWN;
432 }
433 }
434
435 SplitList *
gnc_lot_get_split_list(const GNCLot * lot)436 gnc_lot_get_split_list (const GNCLot *lot)
437 {
438 GNCLotPrivate* priv;
439 if (!lot) return NULL;
440 priv = GET_PRIVATE(lot);
441 return priv->splits;
442 }
443
gnc_lot_count_splits(const GNCLot * lot)444 gint gnc_lot_count_splits (const GNCLot *lot)
445 {
446 GNCLotPrivate* priv;
447 if (!lot) return 0;
448 priv = GET_PRIVATE(lot);
449 return g_list_length (priv->splits);
450 }
451
452 /* ============================================================== */
453 /* Hmm, we should probably inline these. */
454
455 const char *
gnc_lot_get_title(const GNCLot * lot)456 gnc_lot_get_title (const GNCLot *lot)
457 {
458 GNCLotPrivate* priv;
459 if (!lot) return NULL;
460 priv = GET_PRIVATE (lot);
461 if (priv->title == is_unset)
462 {
463 GNCLotPrivate* priv = GET_PRIVATE (lot);
464 GValue v = G_VALUE_INIT;
465 qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "title");
466 priv->title = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
467 g_value_unset (&v);
468 }
469 return priv->title;
470 }
471
472 const char *
gnc_lot_get_notes(const GNCLot * lot)473 gnc_lot_get_notes (const GNCLot *lot)
474 {
475 GNCLotPrivate* priv;
476 if (!lot) return NULL;
477 priv = GET_PRIVATE (lot);
478 if (priv->notes == is_unset)
479 {
480 GValue v = G_VALUE_INIT;
481 qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
482 priv->notes = G_VALUE_HOLDS_STRING (&v) ? g_value_dup_string (&v) : NULL;
483 g_value_unset (&v);
484 }
485 return priv->notes;
486 }
487
488 void
gnc_lot_set_title(GNCLot * lot,const char * str)489 gnc_lot_set_title (GNCLot *lot, const char *str)
490 {
491 GValue v = G_VALUE_INIT;
492 GNCLotPrivate* priv;
493 if (!lot) return;
494 priv = GET_PRIVATE (lot);
495 if (priv->title != is_unset)
496 g_free (priv->title);
497
498 qof_begin_edit(QOF_INSTANCE(lot));
499 g_value_init (&v, G_TYPE_STRING);
500 g_value_set_string (&v, str);
501 priv->title = g_strdup (str);
502 qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "title");
503 qof_instance_set_dirty(QOF_INSTANCE(lot));
504 gnc_lot_commit_edit(lot);
505 g_value_unset (&v);
506 }
507
508 void
gnc_lot_set_notes(GNCLot * lot,const char * str)509 gnc_lot_set_notes (GNCLot *lot, const char *str)
510 {
511 GValue v = G_VALUE_INIT;
512 GNCLotPrivate* priv;
513 if (!lot) return;
514 priv = GET_PRIVATE (lot);
515 if (priv->notes != is_unset)
516 g_free (priv->notes);
517 qof_begin_edit(QOF_INSTANCE(lot));
518 g_value_init (&v, G_TYPE_STRING);
519 g_value_set_string (&v, str);
520 priv->notes = g_strdup (str);
521 qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
522 qof_instance_set_dirty(QOF_INSTANCE(lot));
523 gnc_lot_commit_edit(lot);
524 g_value_unset (&v);
525 }
526
527 /* ============================================================= */
528
529 gnc_numeric
gnc_lot_get_balance(GNCLot * lot)530 gnc_lot_get_balance (GNCLot *lot)
531 {
532 GNCLotPrivate* priv;
533 GList *node;
534 gnc_numeric zero = gnc_numeric_zero();
535 gnc_numeric baln = zero;
536 if (!lot) return zero;
537
538 priv = GET_PRIVATE(lot);
539 if (!priv->splits)
540 {
541 priv->is_closed = FALSE;
542 return zero;
543 }
544
545 /* Sum over splits; because they all belong to same account
546 * they will have same denominator.
547 */
548 for (node = priv->splits; node; node = node->next)
549 {
550 Split *s = node->data;
551 gnc_numeric amt = xaccSplitGetAmount (s);
552 baln = gnc_numeric_add_fixed (baln, amt);
553 g_assert (gnc_numeric_check (baln) == GNC_ERROR_OK);
554 }
555
556 /* cache a zero balance as a closed lot */
557 if (gnc_numeric_equal (baln, zero))
558 {
559 priv->is_closed = TRUE;
560 }
561 else
562 {
563 priv->is_closed = FALSE;
564 }
565
566 return baln;
567 }
568
569 /* ============================================================= */
570
571 void
gnc_lot_get_balance_before(const GNCLot * lot,const Split * split,gnc_numeric * amount,gnc_numeric * value)572 gnc_lot_get_balance_before (const GNCLot *lot, const Split *split,
573 gnc_numeric *amount, gnc_numeric *value)
574 {
575 GNCLotPrivate* priv;
576 GList *node;
577 gnc_numeric zero = gnc_numeric_zero();
578 gnc_numeric amt = zero;
579 gnc_numeric val = zero;
580
581 *amount = amt;
582 *value = val;
583 if (lot == NULL) return;
584
585 priv = GET_PRIVATE(lot);
586 if (priv->splits)
587 {
588 Transaction *ta, *tb;
589 const Split *target;
590 /* If this is a gains split, find the source of the gains and use
591 its transaction for the comparison. Gains splits are in separate
592 transactions that may sort after non-gains transactions. */
593 target = xaccSplitGetGainsSourceSplit (split);
594 if (target == NULL)
595 target = split;
596 tb = xaccSplitGetParent (target);
597 for (node = priv->splits; node; node = node->next)
598 {
599 Split *s = node->data;
600 Split *source = xaccSplitGetGainsSourceSplit (s);
601 if (source == NULL)
602 source = s;
603 ta = xaccSplitGetParent (source);
604 if ((ta == tb && source != target) ||
605 xaccTransOrder (ta, tb) < 0)
606 {
607 gnc_numeric tmpval = xaccSplitGetAmount (s);
608 amt = gnc_numeric_add_fixed (amt, tmpval);
609 tmpval = xaccSplitGetValue (s);
610 val = gnc_numeric_add_fixed (val, tmpval);
611 }
612 }
613 }
614
615 *amount = amt;
616 *value = val;
617 }
618
619 /* ============================================================= */
620
621 void
gnc_lot_add_split(GNCLot * lot,Split * split)622 gnc_lot_add_split (GNCLot *lot, Split *split)
623 {
624 GNCLotPrivate* priv;
625 Account * acc;
626 if (!lot || !split) return;
627 priv = GET_PRIVATE(lot);
628
629 ENTER ("(lot=%p, split=%p) %s amt=%s val=%s", lot, split,
630 gnc_lot_get_title (lot),
631 gnc_num_dbg_to_string (split->amount),
632 gnc_num_dbg_to_string (split->value));
633 gnc_lot_begin_edit(lot);
634 acc = xaccSplitGetAccount (split);
635 qof_instance_set_dirty(QOF_INSTANCE(lot));
636 if (NULL == priv->account)
637 {
638 xaccAccountInsertLot (acc, lot);
639 }
640 else if (priv->account != acc)
641 {
642 PERR ("splits from different accounts cannot "
643 "be added to this lot!\n"
644 "\tlot account=\'%s\', split account=\'%s\'\n",
645 xaccAccountGetName(priv->account), xaccAccountGetName (acc));
646 gnc_lot_commit_edit(lot);
647 LEAVE("different accounts");
648 return;
649 }
650
651 if (lot == split->lot)
652 {
653 gnc_lot_commit_edit(lot);
654 LEAVE("already in lot");
655 return; /* handle not-uncommon no-op */
656 }
657 if (split->lot)
658 {
659 gnc_lot_remove_split (split->lot, split);
660 }
661 xaccSplitSetLot(split, lot);
662
663 priv->splits = g_list_append (priv->splits, split);
664
665 /* for recomputation of is-closed */
666 priv->is_closed = LOT_CLOSED_UNKNOWN;
667 gnc_lot_commit_edit(lot);
668
669 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, NULL);
670 LEAVE("added to lot");
671 }
672
673 void
gnc_lot_remove_split(GNCLot * lot,Split * split)674 gnc_lot_remove_split (GNCLot *lot, Split *split)
675 {
676 GNCLotPrivate* priv;
677 if (!lot || !split) return;
678 priv = GET_PRIVATE(lot);
679
680 ENTER ("(lot=%p, split=%p)", lot, split);
681 gnc_lot_begin_edit(lot);
682 qof_instance_set_dirty(QOF_INSTANCE(lot));
683 priv->splits = g_list_remove (priv->splits, split);
684 xaccSplitSetLot(split, NULL);
685 priv->is_closed = LOT_CLOSED_UNKNOWN; /* force an is-closed computation */
686
687 if (NULL == priv->splits)
688 {
689 xaccAccountRemoveLot (priv->account, lot);
690 priv->account = NULL;
691 }
692 gnc_lot_commit_edit(lot);
693 qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, NULL);
694 LEAVE("removed from lot");
695 }
696
697 /* ============================================================== */
698 /* Utility function, get earliest split in lot */
699
700 Split *
gnc_lot_get_earliest_split(GNCLot * lot)701 gnc_lot_get_earliest_split (GNCLot *lot)
702 {
703 GNCLotPrivate* priv;
704 if (!lot) return NULL;
705 priv = GET_PRIVATE(lot);
706 if (! priv->splits) return NULL;
707 priv->splits = g_list_sort (priv->splits, (GCompareFunc) xaccSplitOrderDateOnly);
708 return priv->splits->data;
709 }
710
711 /* Utility function, get latest split in lot */
712 Split *
gnc_lot_get_latest_split(GNCLot * lot)713 gnc_lot_get_latest_split (GNCLot *lot)
714 {
715 GNCLotPrivate* priv;
716 SplitList *node;
717
718 if (!lot) return NULL;
719 priv = GET_PRIVATE(lot);
720 if (! priv->splits) return NULL;
721 priv->splits = g_list_sort (priv->splits, (GCompareFunc) xaccSplitOrderDateOnly);
722
723 for (node = priv->splits; node->next; node = node->next)
724 ;
725
726 return node->data;
727 }
728
729 /* ============================================================= */
730
731 static void
destroy_lot_on_book_close(QofInstance * ent,gpointer data)732 destroy_lot_on_book_close(QofInstance *ent, gpointer data)
733 {
734 GNCLot* lot = GNC_LOT(ent);
735
736 gnc_lot_destroy(lot);
737 }
738
739 static void
gnc_lot_book_end(QofBook * book)740 gnc_lot_book_end(QofBook* book)
741 {
742 QofCollection *col;
743
744 col = qof_book_get_collection(book, GNC_ID_LOT);
745 qof_collection_foreach(col, destroy_lot_on_book_close, NULL);
746 }
747
748 #ifdef _MSC_VER
749 /* MSVC compiler doesn't have C99 "designated initializers"
750 * so we wrap them in a macro that is empty on MSVC. */
751 # define DI(x) /* */
752 #else
753 # define DI(x) x
754 #endif
755 static QofObject gncLotDesc =
756 {
757 DI(.interface_version = ) QOF_OBJECT_VERSION,
758 DI(.e_type = ) GNC_ID_LOT,
759 DI(.type_label = ) "Lot",
760 DI(.create = ) (gpointer)gnc_lot_new,
761 DI(.book_begin = ) NULL,
762 DI(.book_end = ) gnc_lot_book_end,
763 DI(.is_dirty = ) qof_collection_is_dirty,
764 DI(.mark_clean = ) qof_collection_mark_clean,
765 DI(.foreach = ) qof_collection_foreach,
766 DI(.printable = ) NULL,
767 DI(.version_cmp = ) (int (*)(gpointer, gpointer))qof_instance_version_cmp,
768 };
769
770
gnc_lot_register(void)771 gboolean gnc_lot_register (void)
772 {
773 static const QofParam params[] =
774 {
775 {
776 LOT_TITLE, QOF_TYPE_STRING,
777 (QofAccessFunc) gnc_lot_get_title,
778 (QofSetterFunc) gnc_lot_set_title
779 },
780 {
781 LOT_NOTES, QOF_TYPE_STRING,
782 (QofAccessFunc) gnc_lot_get_notes,
783 (QofSetterFunc) gnc_lot_set_notes
784 },
785 {
786 QOF_PARAM_GUID, QOF_TYPE_GUID,
787 (QofAccessFunc) qof_entity_get_guid, NULL
788 },
789 {
790 QOF_PARAM_BOOK, QOF_ID_BOOK,
791 (QofAccessFunc) gnc_lot_get_book, NULL
792 },
793 {
794 LOT_IS_CLOSED, QOF_TYPE_BOOLEAN,
795 (QofAccessFunc) gnc_lot_is_closed, NULL
796 },
797 {
798 LOT_BALANCE, QOF_TYPE_NUMERIC,
799 (QofAccessFunc) gnc_lot_get_balance, NULL
800 },
801 { NULL },
802 };
803
804 qof_class_register (GNC_ID_LOT, NULL, params);
805 return qof_object_register(&gncLotDesc);
806 }
807
gnc_lot_make_default(Account * acc)808 GNCLot * gnc_lot_make_default (Account *acc)
809 {
810 GNCLot * lot;
811 gint64 id = 0;
812 gchar *buff;
813
814 lot = gnc_lot_new (qof_instance_get_book(acc));
815
816 /* Provide a reasonable title for the new lot */
817 xaccAccountBeginEdit (acc);
818 qof_instance_get (QOF_INSTANCE (acc), "lot-next-id", &id, NULL);
819 buff = g_strdup_printf ("%s %" G_GINT64_FORMAT, _("Lot"), id);
820 gnc_lot_set_title (lot, buff);
821 id ++;
822 qof_instance_set (QOF_INSTANCE (acc), "lot-next-id", id, NULL);
823 xaccAccountCommitEdit (acc);
824 g_free (buff);
825 return lot;
826 }
827
828 /* ========================== END OF FILE ========================= */
829