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 : 316981 : G_DEFINE_TYPE_WITH_PRIVATE(GNCLot, gnc_lot, QOF_TYPE_INSTANCE)
111 : :
112 : : static void
113 : 212 : gnc_lot_init(GNCLot* lot)
114 : : {
115 : : GNCLotPrivate* priv;
116 : :
117 : 212 : priv = GET_PRIVATE(lot);
118 : 212 : priv->account = nullptr;
119 : 212 : priv->splits = nullptr;
120 : 212 : priv->cached_invoice = nullptr;
121 : 212 : priv->is_closed = LOT_CLOSED_UNKNOWN;
122 : 212 : priv->marker = 0;
123 : 212 : }
124 : :
125 : : static void
126 : 151 : gnc_lot_dispose(GObject *lotp)
127 : : {
128 : 151 : G_OBJECT_CLASS(gnc_lot_parent_class)->dispose(lotp);
129 : 151 : }
130 : :
131 : : static void
132 : 151 : gnc_lot_finalize(GObject* lotp)
133 : : {
134 : 151 : G_OBJECT_CLASS(gnc_lot_parent_class)->finalize(lotp);
135 : 151 : }
136 : :
137 : : static void
138 : 1592 : gnc_lot_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
139 : : {
140 : : GNCLot* lot;
141 : : GNCLotPrivate* priv;
142 : :
143 : 1592 : g_return_if_fail(GNC_IS_LOT(object));
144 : :
145 : 1592 : lot = GNC_LOT(object);
146 : 1592 : priv = GET_PRIVATE(lot);
147 : 1592 : 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 : 529 : case PROP_INVOICE:
156 : 529 : qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
157 : 529 : break;
158 : 531 : case PROP_OWNER_TYPE:
159 : 531 : qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_TYPE);
160 : 531 : break;
161 : 531 : case PROP_OWNER_GUID:
162 : 531 : qof_instance_get_kvp (QOF_INSTANCE (lot), value, 2, GNC_OWNER_ID, GNC_OWNER_GUID);
163 : 531 : 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 : 120 : 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 : 120 : g_return_if_fail(GNC_IS_LOT(object));
180 : :
181 : 120 : lot = GNC_LOT(object);
182 : 120 : if (prop_id < PROP_RUNTIME_0)
183 : 120 : g_assert (qof_instance_get_editlevel(lot));
184 : :
185 : 120 : priv = GET_PRIVATE(lot);
186 : 120 : 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 : 73 : case PROP_INVOICE:
195 : 73 : qof_instance_set_kvp (QOF_INSTANCE (lot), value, 2, GNC_INVOICE_ID, GNC_INVOICE_GUID);
196 : 73 : 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 : 10 : gnc_lot_class_init(GNCLotClass* klass)
211 : : {
212 : 10 : GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
213 : :
214 : 10 : gobject_class->dispose = gnc_lot_dispose;
215 : 10 : gobject_class->finalize = gnc_lot_finalize;
216 : 10 : gobject_class->get_property = gnc_lot_get_property;
217 : 10 : gobject_class->set_property = gnc_lot_set_property;
218 : :
219 : 10 : 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 : 10 : 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 : 10 : 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 : 10 : 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 : 10 : 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 : 10 : }
265 : :
266 : : GNCLot *
267 : 212 : gnc_lot_new (QofBook *book)
268 : : {
269 : : GNCLot *lot;
270 : 212 : g_return_val_if_fail (book, nullptr);
271 : :
272 : 212 : lot = GNC_LOT(g_object_new (GNC_TYPE_LOT, nullptr));
273 : 212 : qof_instance_init_data(QOF_INSTANCE(lot), GNC_ID_LOT, book);
274 : 212 : qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_CREATE, nullptr);
275 : 212 : return lot;
276 : : }
277 : :
278 : : static void
279 : 117 : gnc_lot_free(GNCLot* lot)
280 : : {
281 : : GList *node;
282 : : GNCLotPrivate* priv;
283 : 117 : if (!lot) return;
284 : :
285 : 117 : ENTER ("(lot=%p)", lot);
286 : 117 : qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_DESTROY, nullptr);
287 : :
288 : 117 : priv = GET_PRIVATE(lot);
289 : 255 : for (node = priv->splits; node; node = node->next)
290 : : {
291 : 138 : Split *s = GNC_SPLIT(node->data);
292 : 138 : s->lot = nullptr;
293 : : }
294 : 117 : g_list_free (priv->splits);
295 : :
296 : 117 : if (priv->account && !qof_instance_get_destroying(priv->account))
297 : 99 : xaccAccountRemoveLot (priv->account, lot);
298 : :
299 : 117 : priv->account = nullptr;
300 : 117 : priv->is_closed = TRUE;
301 : : /* qof_instance_release (&lot->inst); */
302 : 117 : g_object_unref (lot);
303 : :
304 : 117 : LEAVE();
305 : : }
306 : :
307 : : void
308 : 117 : gnc_lot_destroy (GNCLot *lot)
309 : : {
310 : 117 : if (!lot) return;
311 : :
312 : 117 : gnc_lot_begin_edit(lot);
313 : 117 : qof_instance_set_destroying(lot, TRUE);
314 : 117 : gnc_lot_commit_edit(lot);
315 : : }
316 : :
317 : : /* ============================================================= */
318 : :
319 : : void
320 : 527 : gnc_lot_begin_edit (GNCLot *lot)
321 : : {
322 : 527 : qof_begin_edit(QOF_INSTANCE(lot));
323 : 527 : }
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 : 117 : static void lot_free(QofInstance* inst)
332 : : {
333 : 117 : GNCLot* lot = GNC_LOT(inst);
334 : :
335 : 117 : gnc_lot_free(lot);
336 : 117 : }
337 : :
338 : 414 : static void noop (QofInstance *inst) {}
339 : :
340 : : void
341 : 659 : gnc_lot_commit_edit (GNCLot *lot)
342 : : {
343 : 659 : if (!qof_commit_edit (QOF_INSTANCE(lot))) return;
344 : 531 : qof_commit_edit_part2 (QOF_INSTANCE(lot), commit_err, noop, lot_free);
345 : : }
346 : :
347 : : /* ============================================================= */
348 : :
349 : : GNCLot *
350 : 12 : gnc_lot_lookup (const GncGUID *guid, QofBook *book)
351 : : {
352 : : QofCollection *col;
353 : 12 : if (!guid || !book) return nullptr;
354 : 12 : col = qof_book_get_collection (book, GNC_ID_LOT);
355 : 12 : return (GNCLot *) qof_collection_lookup_entity (col, guid);
356 : : }
357 : :
358 : : QofBook *
359 : 1058 : gnc_lot_get_book (GNCLot *lot)
360 : : {
361 : 1058 : 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 : 1019 : gnc_lot_get_account (const GNCLot *lot)
378 : : {
379 : : GNCLotPrivate* priv;
380 : 1019 : if (!lot) return nullptr;
381 : 1019 : priv = GET_PRIVATE(lot);
382 : 1019 : return priv->account;
383 : : }
384 : :
385 : 1376 : GncInvoice * gnc_lot_get_cached_invoice (const GNCLot *lot)
386 : : {
387 : 1376 : if (!lot) return nullptr;
388 : : else
389 : : {
390 : 1376 : GNCLotPrivate *priv = GET_PRIVATE(lot);
391 : 1376 : return priv->cached_invoice;
392 : : }
393 : : }
394 : :
395 : : void
396 : 600 : gnc_lot_set_cached_invoice(GNCLot* lot, GncInvoice *invoice)
397 : : {
398 : 600 : if (!lot) return;
399 : 600 : GET_PRIVATE(lot)->cached_invoice = invoice;
400 : : }
401 : :
402 : : void
403 : 213 : gnc_lot_set_account(GNCLot* lot, Account* account)
404 : : {
405 : 213 : if (lot != nullptr)
406 : : {
407 : : GNCLotPrivate* priv;
408 : 213 : priv = GET_PRIVATE(lot);
409 : 213 : priv->account = account;
410 : : }
411 : 213 : }
412 : :
413 : : void
414 : 191 : gnc_lot_set_closed_unknown(GNCLot* lot)
415 : : {
416 : : GNCLotPrivate* priv;
417 : 191 : if (lot != nullptr)
418 : : {
419 : 191 : priv = GET_PRIVATE(lot);
420 : 191 : priv->is_closed = LOT_CLOSED_UNKNOWN;
421 : : }
422 : 191 : }
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 : 151 : gnc_lot_get_title (const GNCLot *lot)
446 : : {
447 : 151 : if (!lot) return nullptr;
448 : :
449 : 14 : GValue v = G_VALUE_INIT;
450 : 14 : qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "title");
451 : 14 : const char *rv = G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : nullptr;
452 : 14 : g_value_unset (&v);
453 : :
454 : 14 : return rv;
455 : : }
456 : :
457 : : const char *
458 : 5 : gnc_lot_get_notes (const GNCLot *lot)
459 : : {
460 : 5 : if (!lot) return nullptr;
461 : :
462 : 5 : GValue v = G_VALUE_INIT;
463 : 5 : qof_instance_get_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
464 : 5 : const char *rv = G_VALUE_HOLDS_STRING (&v) ? g_value_get_string (&v) : nullptr;
465 : 5 : g_value_unset (&v);
466 : 5 : return rv;
467 : : }
468 : :
469 : : void
470 : 128 : gnc_lot_set_title (GNCLot *lot, const char *str)
471 : : {
472 : 128 : GValue v = G_VALUE_INIT;
473 : 128 : if (!lot) return;
474 : :
475 : 128 : qof_begin_edit(QOF_INSTANCE(lot));
476 : 128 : g_value_init (&v, G_TYPE_STRING);
477 : 128 : g_value_set_static_string (&v, str);
478 : 128 : qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "title");
479 : 128 : qof_instance_set_dirty(QOF_INSTANCE(lot));
480 : 128 : gnc_lot_commit_edit(lot);
481 : 128 : g_value_unset (&v);
482 : : }
483 : :
484 : : void
485 : 4 : gnc_lot_set_notes (GNCLot *lot, const char *str)
486 : : {
487 : 4 : GValue v = G_VALUE_INIT;
488 : 4 : if (!lot) return;
489 : :
490 : 4 : qof_begin_edit(QOF_INSTANCE(lot));
491 : 4 : g_value_init (&v, G_TYPE_STRING);
492 : 4 : g_value_set_static_string (&v, str);
493 : 4 : qof_instance_set_kvp (QOF_INSTANCE (lot), &v, 1, "notes");
494 : 4 : qof_instance_set_dirty(QOF_INSTANCE(lot));
495 : 4 : gnc_lot_commit_edit(lot);
496 : 4 : g_value_unset (&v);
497 : : }
498 : :
499 : : /* ============================================================= */
500 : :
501 : : gnc_numeric
502 : 698 : gnc_lot_get_balance (GNCLot *lot)
503 : : {
504 : : GNCLotPrivate* priv;
505 : : GList *node;
506 : 698 : gnc_numeric zero = gnc_numeric_zero();
507 : 698 : gnc_numeric baln = zero;
508 : 698 : if (!lot) return zero;
509 : :
510 : 698 : priv = GET_PRIVATE(lot);
511 : 698 : if (!priv->splits)
512 : : {
513 : 53 : priv->is_closed = FALSE;
514 : 53 : return zero;
515 : : }
516 : :
517 : : /* Sum over splits; because they all belong to same account
518 : : * they will have same denominator.
519 : : */
520 : 1375 : for (node = priv->splits; node; node = node->next)
521 : : {
522 : 730 : Split *s = GNC_SPLIT(node->data);
523 : 730 : gnc_numeric amt = xaccSplitGetAmount (s);
524 : 730 : baln = gnc_numeric_add_fixed (baln, amt);
525 : 730 : g_assert (gnc_numeric_check (baln) == GNC_ERROR_OK);
526 : : }
527 : :
528 : : /* cache a zero balance as a closed lot */
529 : 645 : if (gnc_numeric_equal (baln, zero))
530 : : {
531 : 29 : priv->is_closed = TRUE;
532 : : }
533 : : else
534 : : {
535 : 616 : priv->is_closed = FALSE;
536 : : }
537 : :
538 : 645 : return baln;
539 : : }
540 : :
541 : : /* ============================================================= */
542 : :
543 : : void
544 : 26 : gnc_lot_get_balance_before (const GNCLot *lot, const Split *split,
545 : : gnc_numeric *amount, gnc_numeric *value)
546 : : {
547 : : GNCLotPrivate* priv;
548 : : GList *node;
549 : 26 : gnc_numeric zero = gnc_numeric_zero();
550 : 26 : gnc_numeric amt = zero;
551 : 26 : gnc_numeric val = zero;
552 : :
553 : 26 : *amount = amt;
554 : 26 : *value = val;
555 : 26 : if (lot == nullptr) return;
556 : :
557 : 26 : priv = GET_PRIVATE(lot);
558 : 26 : if (priv->splits)
559 : : {
560 : : Transaction *ta, *tb;
561 : : const Split *target;
562 : : /* If this is a gains split, find the source of the gains and use
563 : : its transaction for the comparison. Gains splits are in separate
564 : : transactions that may sort after non-gains transactions. */
565 : 26 : target = xaccSplitGetGainsSourceSplit (split);
566 : 26 : if (target == nullptr)
567 : 26 : target = split;
568 : 26 : tb = xaccSplitGetParent (target);
569 : 96 : for (node = priv->splits; node; node = node->next)
570 : : {
571 : 70 : Split *s = GNC_SPLIT(node->data);
572 : 70 : Split *source = xaccSplitGetGainsSourceSplit (s);
573 : 70 : if (source == nullptr)
574 : 52 : source = s;
575 : 70 : ta = xaccSplitGetParent (source);
576 : 134 : if ((ta == tb && source != target) ||
577 : 64 : xaccTransOrder (ta, tb) < 0)
578 : : {
579 : 26 : gnc_numeric tmpval = xaccSplitGetAmount (s);
580 : 26 : amt = gnc_numeric_add_fixed (amt, tmpval);
581 : 26 : tmpval = xaccSplitGetValue (s);
582 : 26 : val = gnc_numeric_add_fixed (val, tmpval);
583 : : }
584 : : }
585 : : }
586 : :
587 : 26 : *amount = amt;
588 : 26 : *value = val;
589 : : }
590 : :
591 : : /* ============================================================= */
592 : :
593 : : void
594 : 228 : gnc_lot_add_split (GNCLot *lot, Split *split)
595 : : {
596 : : GNCLotPrivate* priv;
597 : : Account * acc;
598 : 228 : if (!lot || !split) return;
599 : 228 : priv = GET_PRIVATE(lot);
600 : :
601 : 228 : ENTER ("(lot=%p, split=%p) %s amt=%s val=%s", lot, split,
602 : : gnc_lot_get_title (lot),
603 : : gnc_num_dbg_to_string (split->amount),
604 : : gnc_num_dbg_to_string (split->value));
605 : 228 : gnc_lot_begin_edit(lot);
606 : 228 : acc = xaccSplitGetAccount (split);
607 : 228 : qof_instance_set_dirty(QOF_INSTANCE(lot));
608 : 228 : if (nullptr == priv->account)
609 : : {
610 : 167 : xaccAccountInsertLot (acc, lot);
611 : : }
612 : 61 : else if (priv->account != acc)
613 : : {
614 : 0 : PERR ("splits from different accounts cannot "
615 : : "be added to this lot!\n"
616 : : "\tlot account=\'%s\', split account=\'%s\'\n",
617 : : xaccAccountGetName(priv->account), xaccAccountGetName (acc));
618 : 0 : gnc_lot_commit_edit(lot);
619 : 0 : LEAVE("different accounts");
620 : 0 : return;
621 : : }
622 : :
623 : 228 : if (lot == split->lot)
624 : : {
625 : 0 : gnc_lot_commit_edit(lot);
626 : 0 : LEAVE("already in lot");
627 : 0 : return; /* handle not-uncommon no-op */
628 : : }
629 : 228 : if (split->lot)
630 : : {
631 : 12 : gnc_lot_remove_split (split->lot, split);
632 : : }
633 : 228 : xaccSplitSetLot(split, lot);
634 : :
635 : 228 : priv->splits = g_list_append (priv->splits, split);
636 : :
637 : : /* for recomputation of is-closed */
638 : 228 : priv->is_closed = LOT_CLOSED_UNKNOWN;
639 : 228 : gnc_lot_commit_edit(lot);
640 : :
641 : 228 : qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, nullptr);
642 : 228 : LEAVE("added to lot");
643 : : }
644 : :
645 : : void
646 : 23 : gnc_lot_remove_split (GNCLot *lot, Split *split)
647 : : {
648 : : GNCLotPrivate* priv;
649 : 23 : if (!lot || !split) return;
650 : 23 : priv = GET_PRIVATE(lot);
651 : :
652 : 23 : ENTER ("(lot=%p, split=%p)", lot, split);
653 : 23 : gnc_lot_begin_edit(lot);
654 : 23 : qof_instance_set_dirty(QOF_INSTANCE(lot));
655 : 23 : priv->splits = g_list_remove (priv->splits, split);
656 : 23 : xaccSplitSetLot(split, nullptr);
657 : 23 : priv->is_closed = LOT_CLOSED_UNKNOWN; /* force an is-closed computation */
658 : :
659 : 23 : if (!priv->splits && priv->account)
660 : : {
661 : 20 : xaccAccountRemoveLot (priv->account, lot);
662 : 20 : priv->account = nullptr;
663 : : }
664 : 23 : gnc_lot_commit_edit(lot);
665 : 23 : qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, nullptr);
666 : 23 : LEAVE("removed from lot");
667 : : }
668 : :
669 : : /* ============================================================== */
670 : : /* Utility function, get earliest split in lot */
671 : :
672 : : Split *
673 : 998 : gnc_lot_get_earliest_split (GNCLot *lot)
674 : : {
675 : : GNCLotPrivate* priv;
676 : 998 : if (!lot) return nullptr;
677 : 998 : priv = GET_PRIVATE(lot);
678 : 998 : if (! priv->splits) return nullptr;
679 : 998 : priv->splits = g_list_sort (priv->splits, (GCompareFunc) xaccSplitOrderDateOnly);
680 : 998 : return GNC_SPLIT(priv->splits->data);
681 : : }
682 : :
683 : : /* Utility function, get latest split in lot */
684 : : Split *
685 : 48 : gnc_lot_get_latest_split (GNCLot *lot)
686 : : {
687 : : GNCLotPrivate* priv;
688 : : SplitList *node;
689 : :
690 : 48 : if (!lot) return nullptr;
691 : 48 : priv = GET_PRIVATE(lot);
692 : 48 : if (! priv->splits) return nullptr;
693 : 48 : priv->splits = g_list_sort (priv->splits, (GCompareFunc) xaccSplitOrderDateOnly);
694 : :
695 : 50 : for (node = priv->splits; node->next; node = node->next)
696 : : ;
697 : :
698 : 48 : return GNC_SPLIT(node->data);
699 : : }
700 : :
701 : : /* ============================================================= */
702 : :
703 : : static void
704 : 0 : destroy_lot_on_book_close(QofInstance *ent, gpointer data)
705 : : {
706 : 0 : GNCLot* lot = GNC_LOT(ent);
707 : :
708 : 0 : gnc_lot_destroy(lot);
709 : 0 : }
710 : :
711 : : static void
712 : 153 : gnc_lot_book_end(QofBook* book)
713 : : {
714 : : QofCollection *col;
715 : :
716 : 153 : col = qof_book_get_collection(book, GNC_ID_LOT);
717 : 153 : qof_collection_foreach(col, destroy_lot_on_book_close, nullptr);
718 : 153 : }
719 : :
720 : : #ifdef _MSC_VER
721 : : /* MSVC compiler doesn't have C99 "designated initializers"
722 : : * so we wrap them in a macro that is empty on MSVC. */
723 : : # define DI(x) /* */
724 : : #else
725 : : # define DI(x) x
726 : : #endif
727 : : static QofObject gncLotDesc =
728 : : {
729 : : DI(.interface_version = ) QOF_OBJECT_VERSION,
730 : : DI(.e_type = ) GNC_ID_LOT,
731 : : DI(.type_label = ) "Lot",
732 : : DI(.create = ) (void* (*)(QofBook*))gnc_lot_new,
733 : : DI(.book_begin = ) nullptr,
734 : : DI(.book_end = ) gnc_lot_book_end,
735 : : DI(.is_dirty = ) qof_collection_is_dirty,
736 : : DI(.mark_clean = ) qof_collection_mark_clean,
737 : : DI(.foreach = ) qof_collection_foreach,
738 : : DI(.printable = ) nullptr,
739 : : DI(.version_cmp = ) (int (*)(gpointer, gpointer))qof_instance_version_cmp,
740 : : };
741 : :
742 : :
743 : 81 : gboolean gnc_lot_register (void)
744 : : {
745 : : static const QofParam params[] =
746 : : {
747 : : {
748 : : LOT_TITLE, QOF_TYPE_STRING,
749 : : (QofAccessFunc) gnc_lot_get_title,
750 : : (QofSetterFunc) gnc_lot_set_title
751 : : },
752 : : {
753 : : LOT_NOTES, QOF_TYPE_STRING,
754 : : (QofAccessFunc) gnc_lot_get_notes,
755 : : (QofSetterFunc) gnc_lot_set_notes
756 : : },
757 : : {
758 : : QOF_PARAM_GUID, QOF_TYPE_GUID,
759 : : (QofAccessFunc) qof_entity_get_guid, nullptr
760 : : },
761 : : {
762 : : QOF_PARAM_BOOK, QOF_ID_BOOK,
763 : : (QofAccessFunc) gnc_lot_get_book, nullptr
764 : : },
765 : : {
766 : : LOT_IS_CLOSED, QOF_TYPE_BOOLEAN,
767 : : (QofAccessFunc) gnc_lot_is_closed, nullptr
768 : : },
769 : : {
770 : : LOT_BALANCE, QOF_TYPE_NUMERIC,
771 : : (QofAccessFunc) gnc_lot_get_balance, nullptr
772 : : },
773 : : { nullptr },
774 : : };
775 : :
776 : 81 : qof_class_register (GNC_ID_LOT, nullptr, params);
777 : 81 : return qof_object_register(&gncLotDesc);
778 : : }
779 : :
780 : 53 : GNCLot * gnc_lot_make_default (Account *acc)
781 : : {
782 : : GNCLot * lot;
783 : 53 : gint64 id = 0;
784 : : gchar *buff;
785 : :
786 : 53 : lot = gnc_lot_new (qof_instance_get_book(acc));
787 : :
788 : : /* Provide a reasonable title for the new lot */
789 : 53 : xaccAccountBeginEdit (acc);
790 : 53 : qof_instance_get (QOF_INSTANCE (acc), "lot-next-id", &id, nullptr);
791 : 53 : buff = g_strdup_printf ("%s %" G_GINT64_FORMAT, _("Lot"), id);
792 : 53 : gnc_lot_set_title (lot, buff);
793 : 53 : id ++;
794 : 53 : qof_instance_set (QOF_INSTANCE (acc), "lot-next-id", id, nullptr);
795 : 53 : xaccAccountCommitEdit (acc);
796 : 53 : g_free (buff);
797 : 53 : return lot;
798 : : }
799 : :
800 : : /* ========================== END OF FILE ========================= */
|