Query.c

00001 /********************************************************************\
00002  * Query.c : api for finding transactions                           *
00003  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com>           *
00004  * Copyright (C) 2002 Linas Vepstas <linas@linas.org>               *
00005  *                                                                  *
00006  * This program is free software; you can redistribute it and/or    *
00007  * modify it under the terms of the GNU General Public License as   *
00008  * published by the Free Software Foundation; either version 2 of   *
00009  * the License, or (at your option) any later version.              *
00010  *                                                                  *
00011  * This program is distributed in the hope that it will be useful,  *
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00014  * GNU General Public License for more details.                     *
00015  *                                                                  *
00016  * You should have received a copy of the GNU General Public License*
00017  * along with this program; if not, contact:                        *
00018  *                                                                  *
00019  * Free Software Foundation           Voice:  +1-617-542-5942       *
00020  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00021  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00022 \********************************************************************/
00023 
00024 #include "config.h"
00025 
00026 #include <ctype.h>
00027 #include <glib.h>
00028 #include <math.h>
00029 #include <string.h>
00030 #include <sys/types.h>
00031 
00032 #include <regex.h>
00033 #include <sys/time.h>
00034 #include <unistd.h>
00035 
00036 #include "gnc-lot.h"
00037 #include "Account.h"
00038 #include "Query.h"
00039 #include "Transaction.h"
00040 #include "TransactionP.h"
00041 
00042 static QofLogModule log_module = GNC_MOD_QUERY;
00043 
00044 static GSList *
00045 build_param_list_internal (const char *first, va_list rest)
00046 {
00047   GSList *list = NULL;
00048   char const *param;
00049 
00050   for (param = first; param; param = va_arg (rest, const char *))
00051     list = g_slist_prepend (list, (gpointer)param);
00052 
00053   return (g_slist_reverse (list));
00054 }
00055 
00056 /********************************************************************
00057  * xaccQueryGetSplitsUniqueTrans 
00058  * Get splits but no more than one from a given transaction.
00059  ********************************************************************/
00060 
00061 SplitList *
00062 xaccQueryGetSplitsUniqueTrans(Query *q)
00063 {
00064   GList       * splits = xaccQueryGetSplits(q);
00065   GList       * current;
00066   GList       * result = NULL;
00067   GHashTable  * trans_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
00068 
00069   for (current = splits; current; current = current->next)
00070   {
00071     Split *split = current->data;
00072     Transaction *trans = xaccSplitGetParent (split);
00073 
00074     if (!g_hash_table_lookup (trans_hash, trans))
00075     {
00076       g_hash_table_insert (trans_hash, trans, trans);
00077       result = g_list_prepend (result, split);
00078     }
00079   }
00080 
00081   g_hash_table_destroy (trans_hash);
00082 
00083   return g_list_reverse (result);
00084 }
00085 
00086 /********************************************************************
00087  * xaccQueryGetTransactions 
00088  * Get transactions matching the query terms, specifying whether 
00089  * we require some or all splits to match 
00090  ********************************************************************/
00091 
00092 static void
00093 query_match_all_filter_func(gpointer key, gpointer value, gpointer user_data) 
00094 {
00095   Transaction * t = key;
00096   int         num_matches = GPOINTER_TO_INT(value);
00097   GList       ** matches = user_data;
00098 
00099   if(num_matches == xaccTransCountSplits(t)) {
00100     *matches = g_list_prepend(*matches, t);
00101   }
00102 }
00103 
00104 static void
00105 query_match_any_filter_func(gpointer key, gpointer value, gpointer user_data) 
00106 {
00107   Transaction * t = key;
00108   GList       ** matches = user_data;
00109   *matches = g_list_prepend(*matches, t);
00110 }
00111 
00112 TransList * 
00113 xaccQueryGetTransactions (Query * q, query_txn_match_t runtype) 
00114 {
00115   GList       * splits = xaccQueryGetSplits(q);
00116   GList       * current = NULL;
00117   GList       * retval = NULL;
00118   GHashTable  * trans_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
00119   Transaction * trans = NULL;
00120   gpointer    val = NULL;
00121   int         count = 0;
00122 
00123   /* iterate over matching splits, incrementing a match-count in
00124    * the hash table */
00125   for(current = splits; current; current=current->next) {
00126     trans = xaccSplitGetParent((Split *)(current->data));
00127     
00128     /* don't waste time looking up unless we need the count 
00129      * information */
00130     if(runtype == QUERY_TXN_MATCH_ALL) {
00131       val   = g_hash_table_lookup(trans_hash, trans);
00132       count = GPOINTER_TO_INT(val);
00133     }
00134     g_hash_table_insert(trans_hash, trans, GINT_TO_POINTER(count + 1));
00135   }
00136   
00137   /* now pick out the transactions that match */
00138   if(runtype == QUERY_TXN_MATCH_ALL) {
00139     g_hash_table_foreach(trans_hash, query_match_all_filter_func, 
00140                          &retval);
00141   }
00142   else {
00143     g_hash_table_foreach(trans_hash, query_match_any_filter_func, 
00144                          &retval);
00145   }
00146 
00147   g_hash_table_destroy(trans_hash);
00148 
00149   return retval;
00150 }
00151 
00152 /********************************************************************
00153  * xaccQueryGetLots
00154  * Get lots matching the query terms, specifying whether 
00155  * we require some or all splits to match 
00156  ********************************************************************/
00157 
00158 static void
00159 query_match_all_lot_filter_func(gpointer key, gpointer value, gpointer user_data) 
00160 {
00161   GNCLot *      l = key;
00162   int           num_matches = GPOINTER_TO_INT(value);
00163   GList **      matches = user_data;
00164 
00165   if(num_matches == gnc_lot_count_splits(l)) {
00166     *matches = g_list_prepend(*matches, l);
00167   }
00168 }
00169 
00170 static void
00171 query_match_any_lot_filter_func(gpointer key, gpointer value, gpointer user_data) 
00172 {
00173   GNCLot *      t = key;
00174   GList **      matches = user_data;
00175   *matches = g_list_prepend(*matches, t);
00176 }
00177 
00178 LotList * 
00179 xaccQueryGetLots (Query * q, query_txn_match_t runtype) 
00180 {
00181   GList       * splits = xaccQueryGetSplits(q);
00182   GList       * current = NULL;
00183   GList       * retval = NULL;
00184   GHashTable  * lot_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
00185   GNCLot      * lot = NULL;
00186   gpointer    val = NULL;
00187   int         count = 0;
00188 
00189   /* iterate over matching splits, incrementing a match-count in
00190    * the hash table */
00191   for(current = splits; current; current=current->next) {
00192     lot = xaccSplitGetLot((Split *)(current->data));
00193     
00194     /* don't waste time looking up unless we need the count 
00195      * information */
00196     if(runtype == QUERY_TXN_MATCH_ALL) {
00197       val   = g_hash_table_lookup(lot_hash, lot);
00198       count = GPOINTER_TO_INT(val);
00199     }
00200     g_hash_table_insert(lot_hash, lot, GINT_TO_POINTER(count + 1));
00201   }
00202   
00203   /* now pick out the transactions that match */
00204   if(runtype == QUERY_TXN_MATCH_ALL) {
00205     g_hash_table_foreach(lot_hash, query_match_all_lot_filter_func, 
00206                          &retval);
00207   }
00208   else {
00209     g_hash_table_foreach(lot_hash, query_match_any_lot_filter_func, 
00210                          &retval);
00211   }
00212 
00213   g_hash_table_destroy(lot_hash);
00214 
00215   return retval;
00216 }
00217 
00218 /*******************************************************************
00219  *  match-adding API 
00220  *******************************************************************/
00221 
00222 void
00223 xaccQueryAddAccountMatch(Query *q, AccountList *acct_list,
00224                          QofGuidMatch how, QofQueryOp op)
00225 {
00226   GList *list = NULL;
00227 
00228   if (!q) return;
00229   for (; acct_list; acct_list = acct_list->next) {
00230     Account *acc = acct_list->data;
00231     const GUID *guid;
00232 
00233     if (!acc) {
00234       PWARN ("acct_list has NULL account");
00235       continue;
00236     }
00237 
00238     guid = xaccAccountGetGUID (acc);
00239     if (!guid) {
00240       PWARN ("acct returns NULL GUID");
00241       continue;
00242     }
00243 
00244     list = g_list_prepend (list, (gpointer)guid);
00245   }
00246   xaccQueryAddAccountGUIDMatch (q, list, how, op);
00247   g_list_free (list);
00248 }
00249 
00250 void
00251 xaccQueryAddAccountGUIDMatch(Query *q, AccountGUIDList *guid_list,
00252                              QofGuidMatch how, QofQueryOp op)
00253 {
00254   QofQueryPredData *pred_data;
00255   GSList *param_list = NULL;
00256 
00257   if (!q) return;
00258 
00259   pred_data = qof_query_guid_predicate (how, guid_list);
00260   if (!pred_data)
00261     return;
00262 
00263   switch (how) {
00264   case QOF_GUID_MATCH_ANY:
00265   case QOF_GUID_MATCH_NONE:
00266     param_list = qof_query_build_param_list (SPLIT_ACCOUNT, QOF_PARAM_GUID, NULL);
00267     break;
00268   case QOF_GUID_MATCH_ALL:
00269     param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_SPLITLIST,
00270                                          SPLIT_ACCOUNT_GUID, NULL);
00271     break;
00272   default:
00273     PERR ("Invalid match type: %d", how);
00274   }
00275 
00276   qof_query_add_term (q, param_list, pred_data, op);
00277 }
00278 
00279 void
00280 xaccQueryAddSingleAccountMatch(Query *q, Account *acc, QofQueryOp op)
00281 {
00282   GList *list;
00283   const GUID *guid;
00284 
00285   if (!q || !acc)
00286     return;
00287 
00288   guid = xaccAccountGetGUID (acc);
00289   g_return_if_fail (guid);
00290 
00291   list = g_list_prepend (NULL, (gpointer)guid);
00292   xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_ANY, op);
00293   g_list_free (list);
00294 }
00295 
00296 void
00297 xaccQueryAddStringMatch (Query* q, const char *matchstring,
00298                          gboolean case_sens, gboolean use_regexp,
00299                          QofQueryOp op,
00300                          const char * path, ...)
00301 {
00302   QofQueryPredData *pred_data;
00303   GSList *param_list;
00304   va_list ap;
00305 
00306   if (!path || !q)
00307     return;
00308 
00309   pred_data = qof_query_string_predicate (QOF_COMPARE_EQUAL, (char *)matchstring,
00310                                        (case_sens ? QOF_STRING_MATCH_NORMAL :
00311                                         QOF_STRING_MATCH_CASEINSENSITIVE),
00312                                        use_regexp);
00313   if (!pred_data)
00314     return;
00315 
00316   va_start (ap, path);
00317   param_list = build_param_list_internal (path, ap);
00318   va_end (ap);
00319 
00320   qof_query_add_term (q, param_list, pred_data, op);
00321 }
00322 
00323 void
00324 xaccQueryAddNumericMatch (Query *q, gnc_numeric amount, QofNumericMatch sign,
00325                           QofQueryCompare how, QofQueryOp op,
00326                           const char * path, ...)
00327 {
00328   QofQueryPredData *pred_data;
00329   GSList *param_list;
00330   va_list ap;
00331 
00332   if (!q || !path)
00333     return;
00334 
00335   pred_data = qof_query_numeric_predicate (how, sign, amount);
00336   if (!pred_data)
00337     return;
00338 
00339   va_start (ap, path);
00340   param_list = build_param_list_internal (path, ap);
00341   va_end (ap);
00342 
00343   qof_query_add_term (q, param_list, pred_data, op);
00344 }
00345 
00346 /* The DateMatch queries match transactions whose posted date
00347  *    is in a date range.  If use_start is TRUE, then a matching
00348  *    posted date will be greater than the start date.   If 
00349  *    use_end is TRUE, then a match occurs for posted dates earlier 
00350  *    than the end date.  If both flags are set, then *both* 
00351  *    conditions must hold ('and').  If neither flag is set, then 
00352  *    all transactions are matched.
00353  */
00354 
00355 void
00356 xaccQueryAddDateMatchTS (Query * q, 
00357                          gboolean use_start, Timespec sts,
00358                          gboolean use_end, Timespec ets,
00359                          QofQueryOp op)
00360 {
00361   Query *tmp_q = NULL;
00362   QofQueryPredData *pred_data;
00363   GSList *param_list;
00364 
00365   if (!q || (!use_start && !use_end))
00366     return;
00367 
00368   tmp_q = qof_query_create ();
00369 
00370   if (use_start) {
00371     pred_data = qof_query_date_predicate (QOF_COMPARE_GTE, QOF_DATE_MATCH_NORMAL, sts);
00372     if (!pred_data) {
00373       qof_query_destroy (tmp_q);
00374       return;
00375     }
00376 
00377     param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_DATE_POSTED, NULL);
00378     qof_query_add_term (tmp_q, param_list, pred_data, QOF_QUERY_AND);
00379   }
00380 
00381   if (use_end) {
00382     pred_data = qof_query_date_predicate (QOF_COMPARE_LTE, QOF_DATE_MATCH_NORMAL, ets);
00383     if (!pred_data) {
00384       qof_query_destroy (tmp_q);
00385       return;
00386     }
00387 
00388     param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_DATE_POSTED, NULL);
00389     qof_query_add_term (tmp_q, param_list, pred_data, QOF_QUERY_AND);
00390   }
00391 
00392   qof_query_merge_in_place (q, tmp_q, op);
00393   qof_query_destroy (tmp_q);
00394 }
00395 
00396 void
00397 xaccQueryGetDateMatchTS (Query * q, 
00398                          Timespec * sts,
00399                          Timespec * ets)
00400 {
00401   QofQueryPredData *term_data;
00402   GSList *param_list;
00403   GSList *terms, *tmp;
00404 
00405   sts->tv_sec = sts->tv_nsec = 0;
00406   ets->tv_sec = ets->tv_nsec = 0;
00407 
00408   param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_DATE_POSTED, NULL);
00409   terms = qof_query_get_term_type (q, param_list);
00410   g_slist_free(param_list);
00411 
00412   for (tmp = terms; tmp; tmp = g_slist_next(tmp)) {
00413     term_data = tmp->data;
00414     if (term_data->how == QOF_COMPARE_GTE)
00415       qof_query_date_predicate_get_date(term_data, sts);
00416     if (term_data->how == QOF_COMPARE_LTE)
00417       qof_query_date_predicate_get_date(term_data, ets);
00418   }
00419   g_slist_free(terms);
00420 }
00421 
00422 /********************************************************************
00423  * xaccQueryAddDateMatch
00424  * Add a date filter to an existing query. 
00425  ********************************************************************/
00426 
00427 void
00428 xaccQueryAddDateMatch(Query * q, 
00429                       gboolean use_start, int sday, int smonth, int syear,
00430                       gboolean use_end, int eday, int emonth, int eyear,
00431                       QofQueryOp op) 
00432 {
00433   /* gcc -O3 will auto-inline this function, avoiding a call overhead */
00434   xaccQueryAddDateMatchTS (q, use_start,
00435                            gnc_dmy2timespec(sday, smonth, syear),
00436                            use_end,
00437                            gnc_dmy2timespec_end(eday, emonth, eyear),
00438                            op);
00439 }
00440 
00441 /********************************************************************
00442  * xaccQueryAddDateMatchTT
00443  * Add a date filter to an existing query. 
00444  ********************************************************************/
00445 
00446 void
00447 xaccQueryAddDateMatchTT(Query * q, 
00448                         gboolean use_start,
00449                         time_t stt,
00450                         gboolean use_end,
00451                         time_t ett,
00452                         QofQueryOp op) 
00453 {
00454   Timespec   sts;
00455   Timespec   ets;
00456   
00457   sts.tv_sec  = (long long)stt;
00458   sts.tv_nsec = 0;
00459 
00460   ets.tv_sec  = (long long)ett;
00461   ets.tv_nsec = 0;
00462 
00463   /* gcc -O3 will auto-inline this function, avoiding a call overhead */
00464   xaccQueryAddDateMatchTS (q, use_start, sts,
00465                            use_end, ets, op);
00466   
00467 }
00468 
00469 void
00470 xaccQueryGetDateMatchTT (Query * q, 
00471                         time_t * stt,
00472                         time_t * ett)
00473 {
00474   Timespec   sts;
00475   Timespec   ets;
00476 
00477   xaccQueryGetDateMatchTS (q, &sts, &ets);
00478 
00479   *stt = sts.tv_sec;
00480   *ett = ets.tv_sec;
00481 }
00482 
00483 void
00484 xaccQueryAddClearedMatch(Query * q, cleared_match_t how, QofQueryOp op)
00485 {
00486   QofQueryPredData *pred_data;
00487   GSList *param_list;
00488   char chars[6];
00489   int i = 0;
00490 
00491   if (!q)
00492     return;
00493 
00494   if (how & CLEARED_CLEARED)
00495     chars[i++] = CREC;
00496   if (how & CLEARED_RECONCILED)
00497     chars[i++] = YREC;
00498   if (how & CLEARED_FROZEN)
00499     chars[i++] = FREC;
00500   if (how & CLEARED_NO)
00501     chars[i++] = NREC;
00502   if (how & CLEARED_VOIDED)
00503     chars[i++] = VREC;
00504   chars[i] = '\0';
00505 
00506   pred_data = qof_query_char_predicate (QOF_CHAR_MATCH_ANY, chars);
00507   if (!pred_data)
00508     return;
00509 
00510   param_list = qof_query_build_param_list (SPLIT_RECONCILE, NULL);
00511 
00512   qof_query_add_term (q, param_list, pred_data, op);
00513 }
00514 
00515 void
00516 xaccQueryAddGUIDMatch(Query * q, const GUID *guid,
00517                       QofIdType id_type, QofQueryOp op)
00518 {
00519   GSList *param_list = NULL;
00520 
00521   if (!q || !guid || !id_type)
00522     return;
00523 
00524   if (!safe_strcmp (id_type, GNC_ID_SPLIT)) 
00525     param_list = qof_query_build_param_list (QOF_PARAM_GUID, NULL);
00526   else if (!safe_strcmp (id_type, GNC_ID_TRANS))
00527     param_list = qof_query_build_param_list (SPLIT_TRANS, QOF_PARAM_GUID, NULL);
00528   else if (!safe_strcmp (id_type, GNC_ID_ACCOUNT))
00529     param_list = qof_query_build_param_list (SPLIT_ACCOUNT, QOF_PARAM_GUID, NULL);
00530   else
00531     PERR ("Invalid match type: %s", id_type);
00532 
00533   qof_query_add_guid_match (q, param_list, guid, op);
00534 }
00535 
00536 void
00537 xaccQueryAddKVPMatch(QofQuery *q, GSList *path, const KvpValue *value,
00538                      QofQueryCompare how, QofIdType id_type,
00539                      QofQueryOp op)
00540 {
00541   GSList *param_list = NULL;
00542   QofQueryPredData *pred_data;
00543 
00544   if (!q || !path || !value || !id_type)
00545     return;
00546 
00547   pred_data = qof_query_kvp_predicate (how, path, value);
00548   if (!pred_data)
00549     return;
00550 
00551   if (!safe_strcmp (id_type, GNC_ID_SPLIT)) 
00552     param_list = qof_query_build_param_list (SPLIT_KVP, NULL);
00553   else if (!safe_strcmp (id_type, GNC_ID_TRANS))
00554     param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_KVP, NULL);
00555   else if (!safe_strcmp (id_type, GNC_ID_ACCOUNT))
00556     param_list = qof_query_build_param_list (SPLIT_ACCOUNT, ACCOUNT_KVP, NULL);
00557   else
00558     PERR ("Invalid match type: %s", id_type);
00559 
00560   qof_query_add_term (q, param_list, pred_data, op);
00561 }
00562 
00563 /*******************************************************************
00564  *  xaccQueryGetEarliestDateFound
00565  *******************************************************************/
00566 
00567 time_t
00568 xaccQueryGetEarliestDateFound(Query * q) 
00569 {
00570   GList * spl;
00571   Split * sp;
00572   time_t earliest;
00573 
00574   if (!q) return 0;
00575   spl = qof_query_last_run (q);
00576   if (!spl) return 0;
00577 
00578    /* Safe until 2038 on archs where time_t is 32bit */ 
00579    sp = spl->data; 
00580    earliest = (time_t) sp->parent->date_posted.tv_sec; 
00581   for(; spl; spl=spl->next) {
00582     sp = spl->data;
00583     if(sp->parent->date_posted.tv_sec < earliest) {
00584       earliest = (time_t) sp->parent->date_posted.tv_sec;
00585     }
00586   }
00587   return earliest;
00588 }
00589 
00590 /*******************************************************************
00591  *  xaccQueryGetLatestDateFound
00592  *******************************************************************/
00593 
00594 time_t
00595 xaccQueryGetLatestDateFound(Query * q) 
00596 {
00597   Split  * sp;
00598   GList  * spl;
00599   time_t latest = 0;
00600 
00601   if(!q) return 0;
00602   spl = qof_query_last_run (q);
00603   if(!spl) return 0;
00604 
00605   for(; spl; spl=spl->next) {
00606     sp = spl->data;
00607     if(sp->parent->date_posted.tv_sec > latest) {
00608       latest = (time_t) sp->parent->date_posted.tv_sec;
00609     }
00610   }
00611   return latest;
00612 }
00613 
00614 void
00615 xaccQueryAddDescriptionMatch(Query *q, const char *m, gboolean c, gboolean r,
00616                              QofQueryOp o)
00617 {
00618     xaccQueryAddStringMatch ((q), (m), (c), (r), (o), SPLIT_TRANS,
00619                              TRANS_DESCRIPTION, NULL);
00620 }
00621 
00622 void
00623 xaccQueryAddNumberMatch(Query *q, const char *m, gboolean c, gboolean r,
00624                         QofQueryOp o)
00625 {
00626     xaccQueryAddStringMatch ((q), (m), (c), (r), (o), SPLIT_TRANS,
00627                              TRANS_NUM, NULL);
00628 }
00629 
00630 void
00631 xaccQueryAddActionMatch(Query *q, const char *m, gboolean c, gboolean r,
00632                         QofQueryOp o)
00633 {
00634     xaccQueryAddStringMatch ((q), (m), (c), (r), (o), SPLIT_ACTION, NULL);
00635 }
00636 
00637 void
00638 xaccQueryAddMemoMatch(Query *q, const char *m, gboolean c, gboolean r,
00639                       QofQueryOp o)
00640 {
00641     xaccQueryAddStringMatch ((q), (m), (c), (r), (o), SPLIT_MEMO, NULL);
00642 }
00643 
00644 void
00645 xaccQueryAddValueMatch(Query *q, gnc_numeric amt, QofNumericMatch sgn,
00646                        QofQueryCompare how, QofQueryOp op)
00647 {
00648     xaccQueryAddNumericMatch ((q), (amt), (sgn), (how), (op),
00649                               SPLIT_VALUE, NULL);
00650 }
00651 
00652 void
00653 xaccQueryAddSharePriceMatch(Query *q, gnc_numeric amt, QofQueryCompare how,
00654                             QofQueryOp op)
00655 {
00656     xaccQueryAddNumericMatch ((q), (amt), QOF_NUMERIC_MATCH_ANY, (how), (op),
00657                               SPLIT_SHARE_PRICE, NULL);
00658 }
00659 
00660 void
00661 xaccQueryAddSharesMatch(Query *q, gnc_numeric amt, QofQueryCompare how,
00662                         QofQueryOp op)
00663 {
00664     xaccQueryAddNumericMatch ((q), (amt), QOF_NUMERIC_MATCH_ANY, (how), (op),
00665                               SPLIT_AMOUNT, NULL);
00666 }
00667 
00668 void
00669 xaccQueryAddBalanceMatch(Query *q, QofQueryCompare bal, QofQueryOp op)
00670 {
00671     xaccQueryAddNumericMatch(
00672         (q), gnc_numeric_zero(), QOF_NUMERIC_MATCH_ANY,
00673         ((bal) ? QOF_COMPARE_EQUAL : QOF_COMPARE_NEQ), (op),
00674         SPLIT_TRANS, TRANS_IMBALANCE, NULL);
00675 }
00676 
00677 
00678 /* ======================== END OF FILE ======================= */

Generated on Sun Sep 7 05:07:36 2008 for GnuCash by  doxygen 1.5.2