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