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