LCOV - code coverage report
Current view: top level - libgnucash/engine - gnc-lot.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 91.9 % 298 274
Test Date: 2026-11-02 18:19:43 Functions: 95.2 % 42 40
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.0-1