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