1 /********************************************************************\
2  * Query.c : api for finding transactions                           *
3  * Copyright (C) 2000 Bill Gribble <grib@billgribble.com>           *
4  * Copyright (C) 2002 Linas Vepstas <linas@linas.org>               *
5  *                                                                  *
6  * This program is free software; you can redistribute it and/or    *
7  * modify it under the terms of the GNU General Public License as   *
8  * published by the Free Software Foundation; either version 2 of   *
9  * the License, or (at your option) any later version.              *
10  *                                                                  *
11  * This program is distributed in the hope that it will be useful,  *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
14  * GNU General Public License for more details.                     *
15  *                                                                  *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact:                        *
18  *                                                                  *
19  * Free Software Foundation           Voice:  +1-617-542-5942       *
20  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
21  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
22 \********************************************************************/
23 
24 #include <config.h>
25 
26 #include <platform.h>
27 #if PLATFORM(WINDOWS)
28 #include <windows.h>
29 #endif
30 
31 #include <ctype.h>
32 #include <glib.h>
33 #include <math.h>
34 #include <string.h>
35 #include <sys/types.h>
36 
37 #include <regex.h>
38 #ifdef HAVE_UNISTD_H
39 # include <unistd.h>
40 #endif
41 
42 #include "gnc-lot.h"
43 #include "Account.h"
44 #include "Query.h"
45 #include "Transaction.h"
46 #include "TransactionP.h"
47 
48 static QofLogModule log_module = GNC_MOD_QUERY;
49 
50 static GSList *
build_param_list_internal(const char * first,va_list rest)51 build_param_list_internal (const char *first, va_list rest)
52 {
53     GSList *list = NULL;
54     char const *param;
55 
56     for (param = first; param; param = va_arg (rest, const char *))
57         list = g_slist_prepend (list, (gpointer)param);
58 
59     return (g_slist_reverse (list));
60 }
61 
62 /********************************************************************
63  * xaccQueryGetSplitsUniqueTrans
64  * Get splits but no more than one from a given transaction.
65  ********************************************************************/
66 
67 SplitList *
xaccQueryGetSplitsUniqueTrans(QofQuery * q)68 xaccQueryGetSplitsUniqueTrans(QofQuery *q)
69 {
70     GList       * splits = qof_query_run(q);
71     GList       * current;
72     GList       * result = NULL;
73     GHashTable  * trans_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
74 
75     for (current = splits; current; current = current->next)
76     {
77         Split *split = current->data;
78         Transaction *trans = xaccSplitGetParent (split);
79 
80         if (!g_hash_table_lookup (trans_hash, trans))
81         {
82             g_hash_table_insert (trans_hash, trans, trans);
83             result = g_list_prepend (result, split);
84         }
85     }
86 
87     g_hash_table_destroy (trans_hash);
88 
89     return g_list_reverse (result);
90 }
91 
92 /********************************************************************
93  * xaccQueryGetTransactions
94  * Get transactions matching the query terms, specifying whether
95  * we require some or all splits to match
96  ********************************************************************/
97 
98 static void
query_match_all_filter_func(gpointer key,gpointer value,gpointer user_data)99 query_match_all_filter_func(gpointer key, gpointer value, gpointer user_data)
100 {
101     Transaction * t = key;
102     int         num_matches = GPOINTER_TO_INT(value);
103     GList       ** matches = user_data;
104 
105     if (num_matches == xaccTransCountSplits(t))
106     {
107         *matches = g_list_prepend(*matches, t);
108     }
109 }
110 
111 static void
query_match_any_filter_func(gpointer key,gpointer value,gpointer user_data)112 query_match_any_filter_func(gpointer key, gpointer value, gpointer user_data)
113 {
114     Transaction * t = key;
115     GList       ** matches = user_data;
116     *matches = g_list_prepend(*matches, t);
117 }
118 
119 TransList *
xaccQueryGetTransactions(QofQuery * q,query_txn_match_t runtype)120 xaccQueryGetTransactions (QofQuery * q, query_txn_match_t runtype)
121 {
122     GList       * splits = qof_query_run(q);
123     GList       * current = NULL;
124     GList       * retval = NULL;
125     GHashTable  * trans_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
126     Transaction * trans = NULL;
127     gpointer    val = NULL;
128     int         count = 0;
129 
130     /* iterate over matching splits, incrementing a match-count in
131      * the hash table */
132     for (current = splits; current; current = current->next)
133     {
134         trans = xaccSplitGetParent((Split *)(current->data));
135 
136         /* don't waste time looking up unless we need the count
137          * information */
138         if (runtype == QUERY_TXN_MATCH_ALL)
139         {
140             val   = g_hash_table_lookup(trans_hash, trans);
141             count = GPOINTER_TO_INT(val);
142         }
143         g_hash_table_insert(trans_hash, trans, GINT_TO_POINTER(count + 1));
144     }
145 
146     /* now pick out the transactions that match */
147     if (runtype == QUERY_TXN_MATCH_ALL)
148     {
149         g_hash_table_foreach(trans_hash, query_match_all_filter_func,
150                              &retval);
151     }
152     else
153     {
154         g_hash_table_foreach(trans_hash, query_match_any_filter_func,
155                              &retval);
156     }
157 
158     g_hash_table_destroy(trans_hash);
159 
160     return retval;
161 }
162 
163 /********************************************************************
164  * xaccQueryGetLots
165  * Get lots matching the query terms, specifying whether
166  * we require some or all splits to match
167  ********************************************************************/
168 
169 static void
query_match_all_lot_filter_func(gpointer key,gpointer value,gpointer user_data)170 query_match_all_lot_filter_func(gpointer key, gpointer value, gpointer user_data)
171 {
172     GNCLot *	l = key;
173     int		num_matches = GPOINTER_TO_INT(value);
174     GList **	matches = user_data;
175 
176     if (num_matches == gnc_lot_count_splits(l))
177     {
178         *matches = g_list_prepend(*matches, l);
179     }
180 }
181 
182 static void
query_match_any_lot_filter_func(gpointer key,gpointer value,gpointer user_data)183 query_match_any_lot_filter_func(gpointer key, gpointer value, gpointer user_data)
184 {
185     GNCLot *	t = key;
186     GList **	matches = user_data;
187     *matches = g_list_prepend(*matches, t);
188 }
189 
190 LotList *
xaccQueryGetLots(QofQuery * q,query_txn_match_t runtype)191 xaccQueryGetLots (QofQuery * q, query_txn_match_t runtype)
192 {
193     GList       * splits = qof_query_run(q);
194     GList       * current = NULL;
195     GList       * retval = NULL;
196     GHashTable  * lot_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
197     GNCLot      * lot = NULL;
198     gpointer    val = NULL;
199     int         count = 0;
200 
201     /* iterate over matching splits, incrementing a match-count in
202      * the hash table */
203     for (current = splits; current; current = current->next)
204     {
205         lot = xaccSplitGetLot((Split *)(current->data));
206 
207         /* don't waste time looking up unless we need the count
208          * information */
209         if (runtype == QUERY_TXN_MATCH_ALL)
210         {
211             val   = g_hash_table_lookup(lot_hash, lot);
212             count = GPOINTER_TO_INT(val);
213         }
214         g_hash_table_insert(lot_hash, lot, GINT_TO_POINTER(count + 1));
215     }
216 
217     /* now pick out the transactions that match */
218     if (runtype == QUERY_TXN_MATCH_ALL)
219     {
220         g_hash_table_foreach(lot_hash, query_match_all_lot_filter_func,
221                              &retval);
222     }
223     else
224     {
225         g_hash_table_foreach(lot_hash, query_match_any_lot_filter_func,
226                              &retval);
227     }
228 
229     g_hash_table_destroy(lot_hash);
230 
231     return retval;
232 }
233 
234 /*******************************************************************
235  *  match-adding API
236  *******************************************************************/
237 
238 void
xaccQueryAddAccountMatch(QofQuery * q,AccountList * acct_list,QofGuidMatch how,QofQueryOp op)239 xaccQueryAddAccountMatch(QofQuery *q, AccountList *acct_list,
240                          QofGuidMatch how, QofQueryOp op)
241 {
242     GList *list = NULL;
243 
244     if (!q) return;
245     for (; acct_list; acct_list = acct_list->next)
246     {
247         Account *acc = acct_list->data;
248         const GncGUID *guid;
249 
250         if (!acc)
251         {
252             PWARN ("acct_list has NULL account");
253             continue;
254         }
255 
256         guid = qof_entity_get_guid (QOF_INSTANCE(acc));
257         if (!guid)
258         {
259             PWARN ("acct returns NULL GncGUID");
260             continue;
261         }
262 
263         list = g_list_prepend (list, (gpointer)guid);
264     }
265     xaccQueryAddAccountGUIDMatch (q, list, how, op);
266     g_list_free (list);
267 }
268 
269 void
xaccQueryAddAccountGUIDMatch(QofQuery * q,AccountGUIDList * guid_list,QofGuidMatch how,QofQueryOp op)270 xaccQueryAddAccountGUIDMatch(QofQuery *q, AccountGUIDList *guid_list,
271                              QofGuidMatch how, QofQueryOp op)
272 {
273     QofQueryPredData *pred_data;
274     GSList *param_list = NULL;
275 
276     if (!q) return;
277 
278     if (!guid_list && how != QOF_GUID_MATCH_NULL)
279     {
280         g_warning("Got a NULL guid_list but the QofGuidMatch is not MATCH_NULL (but instead %d). In other words, the list of GUID matches is empty but it must contain something non-empty.", how);
281         /* qof_query_guid_predicate() would trigger a g_warning as well */
282         return;
283     }
284     pred_data = qof_query_guid_predicate (how, guid_list);
285     if (!pred_data)
286         return;
287 
288     switch (how)
289     {
290     case QOF_GUID_MATCH_ANY:
291     case QOF_GUID_MATCH_NONE:
292         param_list = qof_query_build_param_list (SPLIT_ACCOUNT, QOF_PARAM_GUID, NULL);
293         break;
294     case QOF_GUID_MATCH_ALL:
295         param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_SPLITLIST,
296                      SPLIT_ACCOUNT_GUID, NULL);
297         break;
298     default:
299         PERR ("Invalid match type: %d", how);
300         break;
301     }
302 
303     qof_query_add_term (q, param_list, pred_data, op);
304 }
305 
306 void
xaccQueryAddSingleAccountMatch(QofQuery * q,Account * acc,QofQueryOp op)307 xaccQueryAddSingleAccountMatch(QofQuery *q, Account *acc, QofQueryOp op)
308 {
309     GList *list;
310     const GncGUID *guid;
311 
312     if (!q || !acc)
313         return;
314 
315     guid = qof_entity_get_guid (QOF_INSTANCE(acc));
316     g_return_if_fail (guid);
317 
318     list = g_list_prepend (NULL, (gpointer)guid);
319     xaccQueryAddAccountGUIDMatch (q, list, QOF_GUID_MATCH_ANY, op);
320     g_list_free (list);
321 }
322 
323 void
xaccQueryAddStringMatch(QofQuery * q,const char * matchstring,gboolean case_sens,gboolean use_regexp,QofQueryCompare how,QofQueryOp op,const char * path,...)324 xaccQueryAddStringMatch (QofQuery* q, const char *matchstring,
325                          gboolean case_sens, gboolean use_regexp,
326                          QofQueryCompare how, QofQueryOp op,
327                          const char * path, ...)
328 {
329     QofQueryPredData *pred_data;
330     GSList *param_list;
331     va_list ap;
332 
333     if (!path || !q)
334         return;
335 
336     pred_data = qof_query_string_predicate (how, (char *)matchstring,
337                                             (case_sens ? QOF_STRING_MATCH_NORMAL :
338                                                     QOF_STRING_MATCH_CASEINSENSITIVE),
339                                             use_regexp);
340     if (!pred_data)
341         return;
342 
343     va_start (ap, path);
344     param_list = build_param_list_internal (path, ap);
345     va_end (ap);
346 
347     qof_query_add_term (q, param_list, pred_data, op);
348 }
349 
350 void
xaccQueryAddNumericMatch(QofQuery * q,gnc_numeric amount,QofNumericMatch sign,QofQueryCompare how,QofQueryOp op,const char * path,...)351 xaccQueryAddNumericMatch (QofQuery *q, gnc_numeric amount, QofNumericMatch sign,
352                           QofQueryCompare how, QofQueryOp op,
353                           const char * path, ...)
354 {
355     QofQueryPredData *pred_data;
356     GSList *param_list;
357     va_list ap;
358 
359     if (!q || !path)
360         return;
361 
362     pred_data = qof_query_numeric_predicate (how, sign, amount);
363     if (!pred_data)
364         return;
365 
366     va_start (ap, path);
367     param_list = build_param_list_internal (path, ap);
368     va_end (ap);
369 
370     qof_query_add_term (q, param_list, pred_data, op);
371 }
372 
373 /* The DateMatch queries match transactions whose posted date
374  *    is in a date range.  If use_start is TRUE, then a matching
375  *    posted date will be greater than the start date.   If
376  *    use_end is TRUE, then a match occurs for posted dates earlier
377  *    than the end date.  If both flags are set, then *both*
378  *    conditions must hold ('and').  If neither flag is set, then
379  *    all transactions are matched.
380  */
381 
382 void
xaccQueryAddDateMatchTT(QofQuery * q,gboolean use_start,time64 stt,gboolean use_end,time64 ett,QofQueryOp op)383 xaccQueryAddDateMatchTT (QofQuery * q,
384                          gboolean use_start, time64 stt,
385                          gboolean use_end, time64 ett,
386                          QofQueryOp op)
387 {
388     QofQuery *tmp_q = NULL;
389     QofQueryPredData *pred_data;
390     GSList *param_list;
391 
392     if (!q || (!use_start && !use_end))
393         return;
394 
395     tmp_q = qof_query_create ();
396 
397     if (use_start)
398     {
399         pred_data = qof_query_date_predicate (QOF_COMPARE_GTE, QOF_DATE_MATCH_NORMAL, stt);
400         if (!pred_data)
401         {
402             qof_query_destroy (tmp_q);
403             return;
404         }
405 
406         param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_DATE_POSTED, NULL);
407         qof_query_add_term (tmp_q, param_list, pred_data, QOF_QUERY_AND);
408     }
409 
410     if (use_end)
411     {
412         pred_data = qof_query_date_predicate (QOF_COMPARE_LTE, QOF_DATE_MATCH_NORMAL, ett);
413         if (!pred_data)
414         {
415             qof_query_destroy (tmp_q);
416             return;
417         }
418 
419         param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_DATE_POSTED, NULL);
420         qof_query_add_term (tmp_q, param_list, pred_data, QOF_QUERY_AND);
421     }
422 
423     qof_query_merge_in_place (q, tmp_q, op);
424     qof_query_destroy (tmp_q);
425 }
426 
427 void
xaccQueryGetDateMatchTT(QofQuery * q,time64 * stt,time64 * ett)428 xaccQueryGetDateMatchTT (QofQuery * q,
429                          time64 * stt,
430                          time64 * ett)
431 {
432     QofQueryPredData *term_data;
433     GSList *param_list;
434     GSList *terms, *tmp;
435 
436     *stt = 0;
437     *ett = 0;
438 
439     param_list = qof_query_build_param_list (SPLIT_TRANS, TRANS_DATE_POSTED, NULL);
440     terms = qof_query_get_term_type (q, param_list);
441     g_slist_free(param_list);
442 
443     for (tmp = terms; tmp; tmp = g_slist_next(tmp))
444     {
445         term_data = tmp->data;
446         if (term_data->how == QOF_COMPARE_GTE)
447             qof_query_date_predicate_get_date(term_data, stt);
448         if (term_data->how == QOF_COMPARE_LTE)
449             qof_query_date_predicate_get_date(term_data, ett);
450     }
451     g_slist_free(terms);
452 }
453 
454 /********************************************************************
455  * xaccQueryAddDateMatch
456  * Add a date filter to an existing query.
457  ********************************************************************/
458 
459 void
xaccQueryAddDateMatch(QofQuery * q,gboolean use_start,int sday,int smonth,int syear,gboolean use_end,int eday,int emonth,int eyear,QofQueryOp op)460 xaccQueryAddDateMatch(QofQuery * q,
461                       gboolean use_start, int sday, int smonth, int syear,
462                       gboolean use_end, int eday, int emonth, int eyear,
463                       QofQueryOp op)
464 {
465     /* gcc -O3 will auto-inline this function, avoiding a call overhead */
466     xaccQueryAddDateMatchTT (q, use_start,
467                              gnc_dmy2time64(sday, smonth, syear),
468                              use_end,
469                              gnc_dmy2time64_end(eday, emonth, eyear),
470                              op);
471 }
472 
473 void
xaccQueryAddClearedMatch(QofQuery * q,cleared_match_t how,QofQueryOp op)474 xaccQueryAddClearedMatch(QofQuery * q, cleared_match_t how, QofQueryOp op)
475 {
476     QofQueryPredData *pred_data;
477     GSList *param_list;
478     char chars[6];
479     int i = 0;
480 
481     if (!q)
482         return;
483 
484     if (how & CLEARED_CLEARED)
485         chars[i++] = CREC;
486     if (how & CLEARED_RECONCILED)
487         chars[i++] = YREC;
488     if (how & CLEARED_FROZEN)
489         chars[i++] = FREC;
490     if (how & CLEARED_NO)
491         chars[i++] = NREC;
492     if (how & CLEARED_VOIDED)
493         chars[i++] = VREC;
494     chars[i] = '\0';
495 
496     pred_data = qof_query_char_predicate (QOF_CHAR_MATCH_ANY, chars);
497     if (!pred_data)
498         return;
499 
500     param_list = qof_query_build_param_list (SPLIT_RECONCILE, NULL);
501 
502     qof_query_add_term (q, param_list, pred_data, op);
503 }
504 
505 void
xaccQueryAddGUIDMatch(QofQuery * q,const GncGUID * guid,QofIdType id_type,QofQueryOp op)506 xaccQueryAddGUIDMatch(QofQuery * q, const GncGUID *guid,
507                       QofIdType id_type, QofQueryOp op)
508 {
509     GSList *param_list = NULL;
510 
511     if (!q || !guid || !id_type)
512         return;
513 
514     if (!g_strcmp0 (id_type, GNC_ID_SPLIT))
515         param_list = qof_query_build_param_list (QOF_PARAM_GUID, NULL);
516     else if (!g_strcmp0 (id_type, GNC_ID_TRANS))
517         param_list = qof_query_build_param_list (SPLIT_TRANS, QOF_PARAM_GUID, NULL);
518     else if (!g_strcmp0 (id_type, GNC_ID_ACCOUNT))
519         param_list = qof_query_build_param_list (SPLIT_ACCOUNT, QOF_PARAM_GUID, NULL);
520     else
521         PERR ("Invalid match type: %s", id_type);
522 
523     qof_query_add_guid_match (q, param_list, guid, op);
524 }
525 
526 
527 /********************************************************************
528  * xaccQueryAddClosingTransMatch
529  * Add a filter that matches book closing entries to an existing query.
530  ********************************************************************/
531 
532 void
xaccQueryAddClosingTransMatch(QofQuery * q,gboolean value,QofQueryOp op)533 xaccQueryAddClosingTransMatch(QofQuery *q, gboolean value, QofQueryOp op)
534 {
535     GSList *param_list;
536 
537     param_list = qof_query_build_param_list(SPLIT_TRANS, TRANS_IS_CLOSING, NULL);
538     qof_query_add_boolean_match(q, param_list, value, op);
539 }
540 
541 /*******************************************************************
542  *  xaccQueryGetEarliestDateFound
543  *******************************************************************/
544 
545 time64
xaccQueryGetEarliestDateFound(QofQuery * q)546 xaccQueryGetEarliestDateFound(QofQuery * q)
547 {
548     GList * spl;
549     Split * sp;
550     time64 earliest;
551 
552     if (!q) return 0;
553     spl = qof_query_last_run (q);
554     if (!spl) return 0;
555 
556     /* Safe until 2038 on archs where time64 is 32bit */
557     sp = spl->data;
558     earliest = sp->parent->date_posted;
559     for (; spl; spl = spl->next)
560     {
561         sp = spl->data;
562         if (sp->parent->date_posted < earliest)
563         {
564             earliest = sp->parent->date_posted;
565         }
566     }
567     return earliest;
568 }
569 
570 /*******************************************************************
571  *  xaccQueryGetLatestDateFound
572  *******************************************************************/
573 
574 time64
xaccQueryGetLatestDateFound(QofQuery * q)575 xaccQueryGetLatestDateFound(QofQuery * q)
576 {
577     Split  * sp;
578     GList  * spl;
579     time64 latest = 0;
580 
581     if (!q) return 0;
582     spl = qof_query_last_run (q);
583     if (!spl) return 0;
584 
585     for (; spl; spl = spl->next)
586     {
587         sp = spl->data;
588         if (sp->parent->date_posted > latest)
589         {
590             latest = sp->parent->date_posted;
591         }
592     }
593     return latest;
594 }
595 
596 void
xaccQueryAddDescriptionMatch(QofQuery * q,const char * m,gboolean c,gboolean r,QofQueryCompare h,QofQueryOp o)597 xaccQueryAddDescriptionMatch(QofQuery *q, const char *m, gboolean c, gboolean r,
598                              QofQueryCompare h, QofQueryOp o)
599 {
600     xaccQueryAddStringMatch ((q), (m), (c), (r), (h), (o), SPLIT_TRANS,
601                              TRANS_DESCRIPTION, NULL);
602 }
603 
604 void
xaccQueryAddNotesMatch(QofQuery * q,const char * m,gboolean c,gboolean r,QofQueryCompare h,QofQueryOp o)605 xaccQueryAddNotesMatch(QofQuery *q, const char *m, gboolean c, gboolean r,
606                              QofQueryCompare h, QofQueryOp o)
607 {
608     xaccQueryAddStringMatch ((q), (m), (c), (r), (h), (o), SPLIT_TRANS,
609                              TRANS_NOTES, NULL);
610 }
611 
612 void
xaccQueryAddNumberMatch(QofQuery * q,const char * m,gboolean c,gboolean r,QofQueryCompare h,QofQueryOp o)613 xaccQueryAddNumberMatch(QofQuery *q, const char *m, gboolean c, gboolean r,
614                         QofQueryCompare h, QofQueryOp o)
615 {
616     xaccQueryAddStringMatch ((q), (m), (c), (r), (h), (o), SPLIT_TRANS,
617                              TRANS_NUM, NULL);
618 }
619 
620 void
xaccQueryAddActionMatch(QofQuery * q,const char * m,gboolean c,gboolean r,QofQueryCompare h,QofQueryOp o)621 xaccQueryAddActionMatch(QofQuery *q, const char *m, gboolean c, gboolean r,
622                         QofQueryCompare h, QofQueryOp o)
623 {
624     xaccQueryAddStringMatch ((q), (m), (c), (r), (h), (o), SPLIT_ACTION, NULL);
625 }
626 
627 void
xaccQueryAddMemoMatch(QofQuery * q,const char * m,gboolean c,gboolean r,QofQueryCompare h,QofQueryOp o)628 xaccQueryAddMemoMatch(QofQuery *q, const char *m, gboolean c, gboolean r,
629                       QofQueryCompare h, QofQueryOp o)
630 {
631     xaccQueryAddStringMatch ((q), (m), (c), (r), (h), (o), SPLIT_MEMO, NULL);
632 }
633 
634 void
xaccQueryAddValueMatch(QofQuery * q,gnc_numeric amt,QofNumericMatch sgn,QofQueryCompare how,QofQueryOp op)635 xaccQueryAddValueMatch(QofQuery *q, gnc_numeric amt, QofNumericMatch sgn,
636                        QofQueryCompare how, QofQueryOp op)
637 {
638     xaccQueryAddNumericMatch ((q), (amt), (sgn), (how), (op),
639                               SPLIT_VALUE, NULL);
640 }
641 
642 void
xaccQueryAddSharePriceMatch(QofQuery * q,gnc_numeric amt,QofQueryCompare how,QofQueryOp op)643 xaccQueryAddSharePriceMatch(QofQuery *q, gnc_numeric amt, QofQueryCompare how,
644                             QofQueryOp op)
645 {
646     xaccQueryAddNumericMatch ((q), (amt), QOF_NUMERIC_MATCH_ANY, (how), (op),
647                               SPLIT_SHARE_PRICE, NULL);
648 }
649 
650 void
xaccQueryAddSharesMatch(QofQuery * q,gnc_numeric amt,QofQueryCompare how,QofQueryOp op)651 xaccQueryAddSharesMatch(QofQuery *q, gnc_numeric amt, QofQueryCompare how,
652                         QofQueryOp op)
653 {
654     xaccQueryAddNumericMatch ((q), (amt), QOF_NUMERIC_MATCH_ANY, (how), (op),
655                               SPLIT_AMOUNT, NULL);
656 }
657 
658 void
xaccQueryAddBalanceMatch(QofQuery * q,QofQueryCompare bal,QofQueryOp op)659 xaccQueryAddBalanceMatch(QofQuery *q, QofQueryCompare bal, QofQueryOp op)
660 {
661     xaccQueryAddNumericMatch(
662         (q), gnc_numeric_zero(), QOF_NUMERIC_MATCH_ANY,
663         ((bal) ? QOF_COMPARE_EQUAL : QOF_COMPARE_NEQ), (op),
664         SPLIT_TRANS, TRANS_IMBALANCE, NULL);
665 }
666 
667 
668 /* ======================== END OF FILE ======================= */
669