gnc-lot.c

00001 /********************************************************************\
00002  * gnc-lot.c -- AR/AP invoices; inventory lots; stock lots          *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020 \********************************************************************/
00021 
00022 /*
00023  * FILE:
00024  * gnc-lot.c
00025  * 
00026  * FUNCTION:
00027  * Lots implement the fundamental conceptual idea behind invoices,
00028  * inventory lots, and stock market investment lots.  See the file
00029  * src/doc/lots.txt for implmentation overview.
00030  *
00031  * XXX Lots are not currently treated in a correct transactional
00032  * manner.  There's now a per-Lot dirty flag in the QofInstance, but
00033  * this code still needs to emit the correct signals when a lot has
00034  * changed.  This is true both in the Scrub2.c and in
00035  * src/gnome/lot-viewer.c
00036  *
00037  * HISTORY:
00038  * Created by Linas Vepstas May 2002
00039  * Copyright (c) 2002,2003 Linas Vepstas <linas@linas.org>
00040  */
00041 
00042 #include <glib.h>
00043 #include <glib/gi18n.h>
00044 
00045 #include "config.h"
00046 #include "Account.h"
00047 #include "AccountP.h"
00048 #include "gnc-lot.h"
00049 #include "gnc-lot-p.h"
00050 #include "cap-gains.h"
00051 #include "Transaction.h"
00052 #include "TransactionP.h"
00053 
00054 /* This static indicates the debugging module that this .o belongs to.  */
00055 static QofLogModule log_module = GNC_MOD_LOT;
00056 
00057 /* ============================================================= */
00058 
00059 /* GObject Initialization */
00060 QOF_GOBJECT_IMPL(gnc_lot, GNCLot, QOF_TYPE_INSTANCE);
00061 
00062 static void
00063 gnc_lot_init(GNCLot* lot)
00064 {
00065 }
00066 
00067 static void
00068 gnc_lot_dispose_real (GObject *lotp)
00069 {
00070 }
00071 
00072 static void
00073 gnc_lot_finalize_real(GObject* lotp)
00074 {
00075 }
00076 
00077 static void
00078 gnc_lot_init_data (GNCLot *lot, QofBook *book)
00079 {
00080    ENTER ("(lot=%p, book=%p)", lot, book);
00081    lot->account = NULL;
00082    lot->splits = NULL;
00083    lot->is_closed = -1;
00084    lot->marker = 0;
00085   
00086    qof_instance_init_data(&lot->inst, GNC_ID_LOT, book);
00087    LEAVE ("(lot=%p, book=%p)", lot, book);
00088 }
00089 
00090 GNCLot *
00091 gnc_lot_new (QofBook *book)
00092 {
00093    GNCLot *lot;
00094    g_return_val_if_fail (book, NULL);
00095 
00096    lot = g_object_new (GNC_TYPE_LOT, NULL);
00097    gnc_lot_init_data (lot, book);
00098    qof_event_gen (&lot->inst, QOF_EVENT_CREATE, NULL);
00099    return lot;
00100 }
00101 
00102 static void
00103 gnc_lot_free(GNCLot* lot)
00104 {
00105    GList *node;
00106    if (!lot) return;
00107    
00108    ENTER ("(lot=%p)", lot);
00109    qof_event_gen (&lot->inst, QOF_EVENT_DESTROY, NULL);
00110 
00111    
00112    for (node=lot->splits; node; node=node->next)
00113    {
00114       Split *s = node->data;
00115       s->lot = NULL;
00116    }
00117    g_list_free (lot->splits);
00118    
00119    lot->account = NULL;
00120    lot->is_closed = TRUE;
00121    /* qof_instance_release (&lot->inst); */
00122    g_object_unref (lot);
00123 }
00124 
00125 void 
00126 gnc_lot_destroy (GNCLot *lot)
00127 {
00128    if (!lot) return;
00129    
00130    gnc_lot_begin_edit(lot);
00131    qof_instance_set_destroying(lot, TRUE);
00132    gnc_lot_commit_edit(lot);
00133 }
00134 
00135 /* ============================================================= */
00136 
00137 void
00138 gnc_lot_begin_edit (GNCLot *lot)
00139 {
00140   qof_begin_edit(&lot->inst);
00141 }
00142 
00143 static void commit_err (QofInstance *inst, QofBackendError errcode)
00144 {
00145   PERR ("Failed to commit: %d", errcode);
00146 }
00147 
00148 static void lot_free(QofInstance* inst)
00149 {
00150         GNCLot* lot = GNC_LOT(inst);
00151 
00152         gnc_lot_free(lot);
00153 }
00154 
00155 static void noop (QofInstance *inst) {}
00156 
00157 void
00158 gnc_lot_commit_edit (GNCLot *lot)
00159 {
00160   if (!qof_commit_edit (QOF_INSTANCE(lot))) return;
00161   qof_commit_edit_part2 (&lot->inst, commit_err, noop, lot_free);
00162 }
00163 
00164 /* ============================================================= */
00165 
00166 GNCLot *
00167 gnc_lot_lookup (const GUID *guid, QofBook *book)
00168 {
00169   QofCollection *col;
00170   if (!guid || !book) return NULL;
00171   col = qof_book_get_collection (book, GNC_ID_LOT);
00172   return (GNCLot *) qof_collection_lookup_entity (col, guid);
00173 }
00174 
00175 QofBook *
00176 gnc_lot_get_book (GNCLot *lot)
00177 {
00178     return qof_instance_get_book(QOF_INSTANCE(lot));
00179 }
00180 
00181 /* ============================================================= */
00182 
00183 gboolean 
00184 gnc_lot_is_closed (GNCLot *lot)
00185 {
00186    if (!lot) return TRUE;
00187    if (0 > lot->is_closed) gnc_lot_get_balance (lot);
00188    return lot->is_closed;
00189 }
00190 
00191 Account *
00192 gnc_lot_get_account (const GNCLot *lot)
00193 {
00194    if (!lot) return NULL;
00195    return lot->account;
00196 }
00197 
00198 KvpFrame *
00199 gnc_lot_get_slots (const GNCLot *lot)
00200 {
00201     return qof_instance_get_slots(QOF_INSTANCE(lot));
00202 }
00203 
00204 SplitList *
00205 gnc_lot_get_split_list (const GNCLot *lot)
00206 {
00207    if (!lot) return NULL;
00208    return lot->splits;
00209 }
00210 
00211 gint gnc_lot_count_splits (const GNCLot *lot)
00212 {
00213    if (!lot) return 0;
00214    return g_list_length (lot->splits);
00215 }
00216 
00217 /* ============================================================== */
00218 /* Hmm, we should probably inline these. */
00219 
00220 const char * 
00221 gnc_lot_get_title (const GNCLot *lot)
00222 {
00223    if (!lot) return NULL;
00224    return kvp_frame_get_string (lot->inst.kvp_data, "/title");
00225 }
00226 
00227 const char * 
00228 gnc_lot_get_notes (const GNCLot *lot)
00229 {
00230    if (!lot) return NULL;
00231    return kvp_frame_get_string (lot->inst.kvp_data, "/notes");
00232 }
00233 
00234 void
00235 gnc_lot_set_title (GNCLot *lot, const char *str)
00236 {
00237    if (!lot) return;
00238    qof_begin_edit(QOF_INSTANCE(lot));
00239    qof_instance_set_dirty(QOF_INSTANCE(lot));
00240    kvp_frame_set_str (lot->inst.kvp_data, "/title", str);
00241    gnc_lot_commit_edit(lot);
00242 }
00243 
00244 void
00245 gnc_lot_set_notes (GNCLot *lot, const char *str)
00246 {
00247    if (!lot) return;
00248    gnc_lot_begin_edit(lot);
00249    qof_instance_set_dirty(QOF_INSTANCE(lot));
00250    kvp_frame_set_str (lot->inst.kvp_data, "/notes", str);
00251    gnc_lot_commit_edit(lot);
00252 }
00253 
00254 /* ============================================================= */
00255 
00256 gnc_numeric
00257 gnc_lot_get_balance (GNCLot *lot)
00258 {
00259    GList *node;
00260    gnc_numeric zero = gnc_numeric_zero();
00261    gnc_numeric baln = zero;
00262    if (!lot) return zero;
00263 
00264    if (!lot->splits) 
00265    {
00266       lot->is_closed = FALSE;
00267       return zero;
00268    }
00269 
00270    /* Sum over splits; because they all belong to same account
00271     * they will have same denominator. 
00272     */
00273    for (node=lot->splits; node; node=node->next)
00274    {
00275       Split *s = node->data;
00276       gnc_numeric amt = xaccSplitGetAmount (s);
00277       baln = gnc_numeric_add_fixed (baln, amt);
00278    }
00279 
00280    /* cache a zero balance as a closed lot */
00281    if (gnc_numeric_equal (baln, zero))
00282    {
00283       lot->is_closed = TRUE;
00284    }
00285    else
00286    {
00287       lot->is_closed = FALSE;
00288    }
00289    
00290    return baln;
00291 }
00292 
00293 /* ============================================================= */
00294 
00295 void
00296 gnc_lot_get_balance_before (const GNCLot *lot, const Split *split,
00297                             gnc_numeric *amount, gnc_numeric *value)
00298 {
00299    GList *node;
00300    gnc_numeric zero = gnc_numeric_zero();
00301    gnc_numeric amt = zero;
00302    gnc_numeric val = zero;
00303    
00304    if (lot && lot->splits)
00305    {
00306       Transaction *ta, *tb;
00307       const Split *target;
00308       /* If this is a gains split, find the source of the gains and use
00309          its transaction for the comparison.  Gains splits are in separate
00310          transactions that may sort after non-gains transactions.  */
00311       target = xaccSplitGetGainsSourceSplit (split);
00312       if (target == NULL)
00313          target = split;
00314       tb = xaccSplitGetParent (target);
00315       for (node = lot->splits; node; node = node->next)
00316       {
00317          Split *s = node->data;
00318          Split *source = xaccSplitGetGainsSourceSplit (s);
00319          if (source == NULL)
00320             source = s;
00321          ta = xaccSplitGetParent (source);
00322          if ((ta == tb && source != target) ||
00323              xaccTransOrder (ta, tb) < 0)
00324          {
00325             gnc_numeric tmpval = xaccSplitGetAmount (s);
00326             amt = gnc_numeric_add_fixed (amt, tmpval);
00327             tmpval = xaccSplitGetValue (s);
00328             val = gnc_numeric_add_fixed (val, tmpval);
00329          }
00330       }
00331    }
00332 
00333    *amount = amt;
00334    *value = val;
00335 }
00336                
00337 /* ============================================================= */
00338 
00339 void
00340 gnc_lot_add_split (GNCLot *lot, Split *split)
00341 {
00342    Account * acc;
00343    if (!lot || !split) return;
00344 
00345    ENTER ("(lot=%p, split=%p) %s amt=%s val=%s", lot, split,
00346         gnc_lot_get_title (lot), 
00347         gnc_num_dbg_to_string (split->amount),
00348         gnc_num_dbg_to_string (split->value));
00349    gnc_lot_begin_edit(lot);
00350    acc = xaccSplitGetAccount (split);
00351    qof_instance_set_dirty(QOF_INSTANCE(lot));
00352    if (NULL == lot->account)
00353    {
00354       xaccAccountInsertLot (acc, lot);
00355    }
00356    else if (lot->account != acc)
00357    {
00358       PERR ("splits from different accounts cannot "
00359             "be added to this lot!\n"
00360             "\tlot account=\'%s\', split account=\'%s\'\n",
00361             xaccAccountGetName(lot->account), xaccAccountGetName (acc));
00362       gnc_lot_commit_edit(lot);
00363       LEAVE("different accounts");
00364       return;
00365    }
00366 
00367    if (lot == split->lot) {
00368         gnc_lot_commit_edit(lot);
00369         LEAVE("already in lot");
00370         return; /* handle not-uncommon no-op */
00371    }
00372    if (split->lot)
00373    {
00374       gnc_lot_remove_split (split->lot, split);
00375    }
00376    xaccSplitSetLot(split, lot);
00377 
00378    lot->splits = g_list_append (lot->splits, split);
00379 
00380     /* for recomputation of is-closed */
00381    lot->is_closed = -1;
00382    gnc_lot_commit_edit(lot);
00383 
00384    qof_event_gen (&lot->inst, QOF_EVENT_MODIFY, NULL);
00385    LEAVE("added to lot");
00386 }
00387 
00388 void
00389 gnc_lot_remove_split (GNCLot *lot, Split *split)
00390 {
00391    if (!lot || !split) return;
00392 
00393    ENTER ("(lot=%p, split=%p)", lot, split);
00394    gnc_lot_begin_edit(lot);
00395    qof_instance_set_dirty(QOF_INSTANCE(lot));
00396    lot->splits = g_list_remove (lot->splits, split);
00397    xaccSplitSetLot(split, NULL);
00398    lot->is_closed = -1;   /* force an is-closed computation */
00399 
00400    if (NULL == lot->splits)
00401    {
00402       xaccAccountRemoveLot (lot->account, lot);
00403       lot->account = NULL;
00404    }
00405    gnc_lot_commit_edit(lot);
00406    qof_event_gen (&lot->inst, QOF_EVENT_MODIFY, NULL);
00407 }
00408 
00409 /* ============================================================== */
00410 /* Utility function, get earliest split in lot */
00411 
00412 Split *
00413 gnc_lot_get_earliest_split (GNCLot *lot)
00414 {
00415   if (! lot->splits)
00416     return NULL;
00417   lot->splits = g_list_sort (lot->splits, (GCompareFunc) xaccSplitOrderDateOnly);
00418   return lot->splits->data;
00419 }
00420 
00421 Split *
00422 gnc_lot_get_latest_split (GNCLot *lot)
00423 {
00424   SplitList *node;
00425 
00426   if (! lot->splits)
00427     return NULL;
00428   lot->splits = g_list_sort (lot->splits, (GCompareFunc) xaccSplitOrderDateOnly);
00429 
00430   for (node = lot->splits; node->next; node = node->next)
00431     ;
00432 
00433   return node->data;
00434 }
00435 
00436 /* ============================================================= */
00437 
00438 static QofObject gncLotDesc =
00439 {
00440     interface_version:  QOF_OBJECT_VERSION,
00441     e_type:             GNC_ID_LOT,
00442     type_label:         "Lot",
00443     create:             (gpointer)gnc_lot_new,
00444     book_begin:         NULL,
00445     book_end:           NULL,
00446     is_dirty:           qof_collection_is_dirty,
00447     mark_clean:         qof_collection_mark_clean,
00448     foreach:            qof_collection_foreach,
00449     printable:          NULL,
00450     version_cmp:        (int (*)(gpointer,gpointer))qof_instance_version_cmp,
00451 };
00452 
00453 
00454 gboolean gnc_lot_register (void)
00455 {
00456     static const QofParam params[] = {
00457         { LOT_TITLE, QOF_TYPE_STRING, 
00458           (QofAccessFunc) gnc_lot_get_title, 
00459           (QofSetterFunc) gnc_lot_set_title },
00460         { LOT_NOTES, QOF_TYPE_STRING, 
00461           (QofAccessFunc) gnc_lot_get_notes, 
00462           (QofSetterFunc) gnc_lot_set_notes },
00463         { QOF_PARAM_GUID, QOF_TYPE_GUID, 
00464           (QofAccessFunc) qof_entity_get_guid, NULL },
00465         { QOF_PARAM_BOOK, QOF_ID_BOOK, 
00466           (QofAccessFunc) gnc_lot_get_book, NULL },
00467         { LOT_IS_CLOSED, QOF_TYPE_BOOLEAN, 
00468           (QofAccessFunc) gnc_lot_is_closed, NULL },
00469         { LOT_BALANCE, QOF_TYPE_NUMERIC, 
00470           (QofAccessFunc) gnc_lot_get_balance, NULL },
00471         { NULL },
00472     };
00473 
00474     qof_class_register (GNC_ID_LOT, NULL, params);
00475     return qof_object_register(&gncLotDesc);
00476 }
00477 
00478 GNCLot * gnc_lot_make_default (Account *acc)
00479 {
00480    GNCLot * lot;
00481    gint64 id;
00482    char buff[200];
00483 
00484    lot = gnc_lot_new (qof_instance_get_book(acc));
00485 
00486    /* Provide a reasonable title for the new lot */
00487    id = kvp_frame_get_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id");
00488    snprintf (buff, 200, ("%s %" G_GINT64_FORMAT), _("Lot"), id);
00489    kvp_frame_set_str (gnc_lot_get_slots (lot), "/title", buff);
00490    id ++;
00491    kvp_frame_set_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id", id);
00492 
00493    return lot;
00494 }
00495 
00496 /* ========================== END OF FILE ========================= */

Generated on Fri Oct 10 05:06:20 2008 for GnuCash by  doxygen 1.5.2