30 #include <glib/gi18n.h>
33 #ifdef HAVE_SYS_TIME_H
34 # include <sys/time.h>
52 #include "TransactionP.h"
175 const char *trans_notes_str =
"notes";
176 const char *void_reason_str =
"void-reason";
177 const char *void_time_str =
"void-time";
178 const char *void_former_notes_str =
"void-former-notes";
179 const char *trans_is_closing_str =
"book_closing";
180 const char *assoc_uri_str =
"assoc_uri";
183 #define TRANS_DATE_DUE_KVP "trans-date-due"
184 #define TRANS_TXN_TYPE_KVP "trans-txn-type"
185 #define TRANS_READ_ONLY_REASON "trans-read-only"
186 #define TRANS_REVERSED_BY "reversed-by"
187 #define GNC_SX_FROM "from-sched-xaction"
189 #define ISO_DATELENGTH 32
210 if (trans && 0 >= qof_instance_get_editlevel(trans))
211 PERR (
"transaction %p not open for editing", trans);
223 #define FOR_EACH_SPLIT(trans, cmd_block) if (trans->splits) { \
225 for (splits = (trans)->splits; splits; splits = splits->next) { \
226 Split *s = splits->data; \
227 if (xaccTransStillHasSplit(trans, s)) { \
233 G_INLINE_FUNC
void mark_trans (
Transaction *trans);
236 FOR_EACH_SPLIT(trans, mark_split(s));
239 G_INLINE_FUNC
void gen_event_trans (
Transaction *trans);
244 for (node = trans->splits; node; node = node->next)
246 Split *s = node->data;
261 G_DEFINE_TYPE(
Transaction, gnc_transaction, QOF_TYPE_INSTANCE)
266 ENTER (
"trans=%p", trans);
268 trans->num = CACHE_INSERT(
"");
269 trans->description = CACHE_INSERT(
"");
271 trans->common_currency = NULL;
272 trans->splits = NULL;
274 trans->date_entered.tv_sec = 0;
275 trans->date_entered.tv_nsec = 0;
277 trans->date_posted.tv_sec = 0;
278 trans->date_posted.tv_nsec = 0;
286 gnc_transaction_dispose(GObject *txnp)
288 G_OBJECT_CLASS(gnc_transaction_parent_class)->dispose(txnp);
292 gnc_transaction_finalize(GObject* txnp)
294 G_OBJECT_CLASS(gnc_transaction_parent_class)->finalize(txnp);
304 gnc_transaction_get_property(GObject*
object,
314 g_return_if_fail(GNC_IS_TRANSACTION(
object));
316 tx = GNC_TRANSACTION(
object);
320 g_value_set_string(value, tx->num);
322 case PROP_DESCRIPTION:
323 g_value_set_string(value, tx->description);
326 g_value_take_object(value, tx->common_currency);
329 g_value_set_boxed(value, &tx->date_posted);
331 case PROP_ENTER_DATE:
332 g_value_set_boxed(value, &tx->date_entered);
336 qof_instance_get_kvp (QOF_INSTANCE (tx), key, value);
340 qof_instance_get_kvp (QOF_INSTANCE (tx), key, value);
342 case PROP_ONLINE_ACCOUNT:
344 qof_instance_get_kvp (QOF_INSTANCE (tx), key, value);
347 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
353 gnc_transaction_set_property(GObject*
object,
362 g_return_if_fail(GNC_IS_TRANSACTION(
object));
364 tx = GNC_TRANSACTION(
object);
365 g_assert (qof_instance_get_editlevel(tx));
372 case PROP_DESCRIPTION:
381 case PROP_ENTER_DATE:
386 qof_instance_set_kvp (QOF_INSTANCE (tx), key, value);
390 qof_instance_set_kvp (QOF_INSTANCE (tx), key, value);
392 case PROP_ONLINE_ACCOUNT:
394 qof_instance_set_kvp (QOF_INSTANCE (tx), key, value);
397 G_OBJECT_WARN_INVALID_PROPERTY_ID(
object, prop_id, pspec);
405 GObjectClass* gobject_class = G_OBJECT_CLASS(klass);
407 gobject_class->dispose = gnc_transaction_dispose;
408 gobject_class->finalize = gnc_transaction_finalize;
409 gobject_class->set_property = gnc_transaction_set_property;
410 gobject_class->get_property = gnc_transaction_get_property;
412 g_object_class_install_property
415 g_param_spec_string(
"num",
416 "Transaction Number",
417 "The transactionNumber is an arbitrary string "
418 "assigned by the user. It is intended to be "
419 "a short 1-6 character string that is displayed "
420 "by the register. For checks, it is usually the "
421 "check number. For other types of transactions, "
422 "it can be any string.",
426 g_object_class_install_property
429 g_param_spec_string(
"description",
430 "Transaction Description",
431 "The transaction description is an arbitrary string "
432 "assigned by the user. It is usually the customer, "
433 "vendor or other organization associated with the "
438 g_object_class_install_property
441 g_param_spec_object (
"currency",
443 "The base currency for this transaction.",
447 g_object_class_install_property
450 g_param_spec_boxed(
"post-date",
452 "The date the transaction occurred.",
456 g_object_class_install_property
459 g_param_spec_boxed(
"enter-date",
461 "The date the transaction was entered.",
465 g_object_class_install_property(
468 g_param_spec_boxed(
"invoice",
469 "Invoice attached to lot",
470 "Used by GncInvoice",
474 g_object_class_install_property(
477 g_param_spec_boxed(
"from-sched-xaction",
478 "From Scheduled Transaction",
479 "Used by Scheduled Transastions to record the "
480 "originating template transaction for created "
485 g_object_class_install_property
488 g_param_spec_string (
"online-id",
490 "The online account which corresponds to this "
491 "account for OFX/HCBI import",
504 ENTER (
"trans=%p", trans);
517 g_return_val_if_fail (book, NULL);
519 trans = g_object_new(GNC_TYPE_TRANSACTION, NULL);
520 xaccInitTransaction (trans, book);
526 #ifdef DUMP_FUNCTIONS
532 xaccTransDump (
const Transaction *trans,
const char *tag)
536 printf(
"%s Trans %p", tag, trans);
539 printf(
" Num: %s\n", trans->num ? trans->num :
"(null)");
540 printf(
" Description: %s\n",
541 trans->description ? trans->description :
"(null)");
542 printf(
" Currency: %s\n",
544 printf(
" version: %x\n", qof_instance_get_version(trans));
545 printf(
" version_chk: %x\n", qof_instance_get_version_check(trans));
546 printf(
" editlevel: %x\n", qof_instance_get_editlevel(trans));
547 printf(
" orig: %p\n", trans->orig);
550 for (node = trans->splits; node; node = node->next)
552 printf(
"%p ", node->data);
555 for (node = trans->splits; node; node = node->next)
557 xaccSplitDump(node->data, tag);
566 GList *node, *new_list = NULL;
570 for (node = trans->splits; node; node = node->next)
575 new_list = g_list_append(new_list, split);
579 for (node = trans->splits; node; node = node->next)
584 new_list = g_list_append(new_list, split);
588 g_list_free(trans->splits);
589 trans->splits = new_list;
606 to = g_object_new (GNC_TYPE_TRANSACTION, NULL);
608 to->num = CACHE_INSERT (from->num);
609 to->description = CACHE_INSERT (from->description);
611 to->splits = g_list_copy (from->splits);
612 for (node = to->splits; node; node = node->next)
614 node->data = xaccDupeSplit (node->data);
617 to->date_entered = from->date_entered;
618 to->date_posted = from->date_posted;
619 qof_instance_copy_version(to, from);
622 to->common_currency = from->common_currency;
649 to = g_object_new (GNC_TYPE_TRANSACTION, NULL);
651 to->date_entered = from->date_entered;
652 to->date_posted = from->date_posted;
653 to->num = CACHE_INSERT (from->num);
654 to->description = CACHE_INSERT (from->description);
655 to->common_currency = from->common_currency;
656 qof_instance_copy_version(to, from);
657 qof_instance_copy_version_check(to, from);
665 for (node = from->splits; node; node = node->next)
667 split = xaccSplitCloneNoKvp(node->data);
669 to->splits = g_list_append (to->splits, split);
671 qof_instance_set_dirty(QOF_INSTANCE(to));
683 int length = g_list_length (from->splits);
687 g_assert (g_list_length (to->splits) == length);
688 for (i = 0; i < length; ++i)
689 xaccSplitCopyKvp (g_list_nth_data (from->splits, i),
690 g_list_nth_data (to->splits, i));
709 to_trans = dupe_trans(from_trans);
746 gboolean change_accounts = FALSE;
749 if (!from_trans || !to_trans)
752 change_accounts = from_acc && GNC_IS_ACCOUNT(to_acc) && from_acc != to_acc;
771 for (node = from_trans->splits; node; node = node->next)
776 xaccSplitSetAccount(new_split, to_acc);
777 xaccSplitSetParent(new_split, to_trans);
794 ENTER (
"(addr=%p)", trans);
795 if (((
char *) 1) == trans->num)
797 PERR (
"double-free %p", trans);
803 for (node = trans->splits; node; node = node->next)
804 xaccFreeSplit (node->data);
805 g_list_free (trans->splits);
806 trans->splits = NULL;
809 CACHE_REMOVE(trans->num);
810 CACHE_REMOVE(trans->description);
813 trans->num = (
char *) 1;
814 trans->description = NULL;
816 trans->date_entered.tv_sec = 0;
817 trans->date_entered.tv_nsec = 0;
818 trans->date_posted.tv_sec = 0;
819 trans->date_posted.tv_nsec = 0;
823 xaccFreeTransaction (trans->orig);
828 g_object_unref(trans);
830 LEAVE (
"(addr=%p)", trans);
845 compare_split_guids (gconstpointer a, gconstpointer b)
850 if (sa == sb)
return 0;
851 if (!sa || !sb)
return 1;
858 gboolean check_guids,
859 gboolean check_splits,
860 gboolean check_balances,
861 gboolean assume_ordered)
865 if (!ta && !tb)
return TRUE;
869 PINFO (
"one is NULL");
873 if (ta == tb)
return TRUE;
881 PINFO (
"GUIDs differ");
888 PINFO (
"commodities differ %s vs %s",
894 if (
timespec_cmp(&(ta->date_entered), &(tb->date_entered)))
901 PINFO (
"date entered differs: '%s' vs '%s'", buf1, buf2);
905 if (
timespec_cmp(&(ta->date_posted), &(tb->date_posted)))
912 PINFO (
"date posted differs: '%s' vs '%s'", buf1, buf2);
919 if ((same_book && ta->num != tb->num) || (!same_book && g_strcmp0(ta->num, tb->num) != 0))
921 PINFO (
"num differs: %s vs %s", ta->num, tb->num);
925 if ((same_book && ta->description != tb->description)
926 || (!same_book && g_strcmp0(ta->description, tb->description)))
928 PINFO (
"descriptions differ: %s vs %s", ta->description, tb->description);
940 PINFO (
"kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
950 if ((!ta->splits && tb->splits) || (!tb->splits && ta->splits))
952 PINFO (
"only one has splits");
956 if (ta->splits && tb->splits)
958 GList *node_a, *node_b;
960 for (node_a = ta->splits, node_b = tb->splits;
962 node_a = node_a->next, node_b = node_b->next)
964 Split *split_a = node_a->data;
969 node_b = g_list_find_custom (tb->splits, split_a,
970 compare_split_guids);
977 PINFO (
"first has split %s and second does not",guidstr);
981 split_b = node_b->data;
983 if (!
xaccSplitEqual (split_a, split_b, check_guids, check_balances,
992 PINFO (
"splits %s and %s differ", str_a, str_b);
997 if (g_list_length (ta->splits) != g_list_length (tb->splits))
999 PINFO (
"different number of splits");
1027 if (!guid || !book)
return NULL;
1039 if (!trans)
return imbal;
1041 ENTER(
"(trans=%p)", trans);
1044 FOR_EACH_SPLIT(trans, imbal =
1058 MonetaryList *imbal_list = NULL;
1060 gboolean trading_accts;
1062 if (!trans)
return imbal_list;
1064 ENTER(
"(trans=%p)", trans);
1076 FOR_EACH_SPLIT(trans,
1080 if (trading_accts &&
1091 imbal_list = gnc_monetary_list_add_value(imbal_list,
1092 trans->common_currency,
1095 imbal_list = gnc_monetary_list_add_value(imbal_list, commodity,
1110 imbal_list = gnc_monetary_list_add_value(imbal_list,
1111 trans->common_currency,
1119 LEAVE(
"(trans=%p), imbal=%p", trans, imbal_list);
1126 MonetaryList *imbal_list;
1131 if (trans == NULL)
return FALSE;
1138 FOR_EACH_SPLIT(trans,
1164 result = imbal_list == NULL;
1174 if (!trans || !acc)
return total;
1189 if (!trans || !acc)
return total;
1194 total = gnc_numeric_add_fixed(
1201 xaccTransGetRateForCommodity(
const Transaction *trans,
1208 if (trans == NULL || split_com == NULL || split == NULL)
1215 *rate = gnc_numeric_create (1, 1);
1219 for (splits = trans->splits; splits; splits = splits->next)
1221 Split *s = splits->data;
1224 if (!xaccTransStillHasSplit (trans, s))
continue;
1255 gboolean found_acc_match = FALSE;
1264 return gnc_numeric_create(1, 1);
1266 for (splits = txn->splits; splits; splits = splits->next)
1273 if (!xaccTransStillHasSplit(txn, s))
1277 if (! (split_acc == acc ||
1281 found_acc_match = TRUE;
1290 PWARN(
"How can amount be nonzero and value be zero?");
1301 if (found_acc_match)
1302 return gnc_numeric_zero();
1304 PERR(
"Cannot convert transaction -- no splits with proper conversion ratio");
1306 return gnc_numeric_create (100, 100);
1314 Split *last_split = NULL;
1319 for (node = trans->splits; node; node = node->next)
1321 Split *split = node->data;
1323 if (!xaccTransStillHasSplit(trans, split))
1350 return trans ? trans->common_currency : NULL;
1356 gint fraction, old_fraction;
1358 if (!trans || !curr || trans->common_currency == curr)
return;
1362 trans->common_currency = curr;
1366 if (fraction != old_fraction)
1371 qof_instance_set_dirty(QOF_INSTANCE(trans));
1395 trans->orig = dupe_trans (trans);
1410 qof_instance_set_destroying(trans, TRUE);
1419 for (node = trans->splits; node; node = node->next)
1421 Split *s = node->data;
1422 if (!xaccTransStillHasSplit(trans, s))
1425 if (GAINS_STATUS_UNKNOWN == s->gains) xaccSplitDetermineGainStatus(s);
1426 if (s->gains_split && (GAINS_STATUS_GAINS & s->gains_split->gains))
1430 s->gains_split = NULL;
1445 destroy_gains (trans);
1460 for (node = trans->splits; node; node = node->next)
1462 Split *s = node->data;
1463 if (s && s->parent == trans)
1468 for (node = trans->splits; node; node = node->next)
1470 Split *s = node->data;
1471 if (s && s->parent == trans)
1473 xaccSplitCommitEdit(s);
1476 g_list_free (trans->splits);
1477 trans->splits = NULL;
1478 xaccFreeTransaction (trans);
1485 static int scrub_data = 1;
1486 void xaccEnableDataScrubbing(
void)
1490 void xaccDisableDataScrubbing(
void)
1496 static gboolean was_trans_emptied(
Transaction *trans)
1498 FOR_EACH_SPLIT(trans,
return FALSE);
1510 PWARN(
"Another user has modified this transaction\n"
1511 "\tjust a moment ago. Please look at their changes,\n"
1512 "\tand try again, if needed.\n");
1516 gnc_engine_signal_commit_error( errcode );
1519 static void trans_cleanup_commit(
Transaction *trans)
1521 GList *slist, *node;
1528 slist = g_list_copy(trans->splits);
1529 for (node = slist; node; node = node->next)
1531 Split *s = node->data;
1541 ed.idx = g_list_index(trans->splits, s);
1542 trans->splits = g_list_remove(trans->splits, s);
1546 if (s->parent == trans)
1552 xaccSplitCommitEdit(s);
1562 PINFO (
"get rid of rollback trans=%p", trans->orig);
1563 xaccFreeTransaction (trans->orig);
1571 qof_instance_decrease_editlevel(trans);
1572 g_assert(qof_instance_get_editlevel(trans) == 0);
1574 gen_event_trans (trans);
1582 ENTER (
"(trans=%p)", trans);
1586 LEAVE(
"editlevel non-zero");
1593 qof_instance_increase_editlevel(trans);
1595 if (was_trans_emptied(trans))
1596 qof_instance_set_destroying(trans, TRUE);
1620 if (g_getenv(
"GNC_AUTO_SCRUB_LOTS") != NULL)
1628 if (0 == trans->date_entered.tv_sec)
1631 #ifdef HAVE_GETTIMEOFDAY
1632 gettimeofday (&tv, NULL);
1634 time (&(tv.tv_sec));
1637 trans->date_entered.tv_sec = tv.tv_sec;
1639 qof_instance_set_dirty(QOF_INSTANCE(trans));
1647 LEAVE (
"(trans=%p)", trans);
1650 #define SWAP(a, b) do { gpointer tmp = (a); (a) = (b); (b) = tmp; } while (0);
1663 GList *node, *onode;
1667 int num_preexist, i;
1676 if (!qof_instance_get_editlevel (QOF_INSTANCE (trans)))
return;
1677 if (qof_instance_get_editlevel (QOF_INSTANCE (trans)) > 1) {
1678 qof_instance_decrease_editlevel (QOF_INSTANCE (trans));
1682 ENTER (
"trans addr=%p\n", trans);
1689 SWAP(trans->num, orig->num);
1690 SWAP(trans->description, orig->description);
1691 trans->date_entered = orig->date_entered;
1692 trans->date_posted = orig->date_posted;
1693 SWAP(trans->common_currency, orig->common_currency);
1694 SWAP(trans->inst.kvp_data, orig->inst.kvp_data);
1702 num_preexist = g_list_length(orig->splits);
1703 slist = g_list_copy(trans->splits);
1704 for (i = 0, node = slist, onode = orig->splits; node;
1705 i++, node = node->next, onode = onode ? onode->next : NULL)
1707 Split *s = node->data;
1712 if (i < num_preexist)
1714 Split *so = onode->data;
1716 xaccSplitRollbackEdit(s);
1717 SWAP(s->action, so->action);
1718 SWAP(s->memo, so->memo);
1719 SWAP(s->inst.kvp_data, so->inst.kvp_data);
1720 s->reconciled = so->reconciled;
1721 s->amount = so->amount;
1722 s->value = so->value;
1724 s->gains_split = so->gains_split;
1726 s->date_reconciled = so->date_reconciled;
1727 qof_instance_mark_clean(QOF_INSTANCE(s));
1735 trans->splits = g_list_remove(trans->splits, s);
1740 xaccSplitRollbackEdit(s);
1741 trans->splits = g_list_remove(trans->splits, s);
1755 g_list_free(orig->splits);
1756 orig->splits = NULL;
1763 if (be && be->rollback)
1772 while (ERR_BACKEND_NO_ERR != errcode);
1774 (be->rollback) (be, &(trans->inst));
1789 LEAVE (
"deleted trans addr=%p\n", trans);
1792 if (ERR_BACKEND_NO_ERR != errcode)
1794 PERR (
"Rollback Failed. Ouch!");
1803 xaccFreeTransaction (trans->orig);
1806 qof_instance_set_destroying(trans, FALSE);
1809 qof_instance_decrease_editlevel(trans);
1813 gen_event_trans (trans);
1815 LEAVE (
"trans addr=%p\n", trans);
1821 return trans ? (0 < qof_instance_get_editlevel(trans)) : FALSE;
1824 #define SECS_PER_DAY 86400
1839 if ( ta && !tb )
return -1;
1840 if ( !ta && tb )
return +1;
1841 if ( !ta && !tb )
return 0;
1844 DATE_CMP(ta, tb, date_posted);
1857 if (na < nb)
return -1;
1858 if (na > nb)
return +1;
1861 DATE_CMP(ta, tb, date_entered);
1864 da = ta->description ? ta->description :
"";
1865 db = tb->description ? tb->description :
"";
1866 retval = g_utf8_collate (da, db);
1885 PINFO (
"addr=%p set date to %" G_GUINT64_FORMAT
".%09ld %s\n",
1886 trans, val.tv_sec, val.tv_nsec, tstr ? tstr :
"(null)");
1891 qof_instance_set_dirty(QOF_INSTANCE(trans));
1907 FOR_EACH_SPLIT(trans, s->gains |= GAINS_STATUS_DATE_DIRTY);
1915 xaccTransSetDateInternal(trans, &trans->date_posted, ts);
1916 set_gains_date_dirty (trans);
1937 kvp_value = kvp_value_new_gdate(date);
1945 xaccTransSetDateInternal(trans, &trans->date_posted,
1947 set_gains_date_dirty (trans);
1955 xaccTransSetDateInternal(trans, &trans->date_entered, ts);
1962 if ((ts.tv_nsec == 0) && (ts.tv_sec == 0))
return;
1964 xaccTransSetDateInternal(trans, &trans->date_posted, ts);
1965 set_gains_date_dirty(trans);
1972 if (!trans || !ts)
return;
1973 xaccTransSetDateInternal(trans, &trans->date_posted, *ts);
1974 set_gains_date_dirty (trans);
1981 if ((ts.tv_nsec == 0) && (ts.tv_sec == 0))
return;
1983 xaccTransSetDateInternal(trans, &trans->date_entered, ts);
1990 if (!trans || !ts)
return;
1991 xaccTransSetDateInternal(trans, &trans->date_entered, *ts);
1999 date = g_date_new_dmy(day, mon, year);
2000 g_assert(g_date_valid(date));
2008 if (!trans || !ts)
return;
2011 qof_instance_set_dirty(QOF_INSTANCE(trans));
2018 char s[2] = {type,
'\0'};
2019 g_return_if_fail(trans);
2022 qof_instance_set_dirty(QOF_INSTANCE(trans));
2032 TRANS_READ_ONLY_REASON, NULL);
2033 qof_instance_set_dirty(QOF_INSTANCE(trans));
2041 if (trans && reason)
2045 TRANS_READ_ONLY_REASON, reason);
2046 qof_instance_set_dirty(QOF_INSTANCE(trans));
2057 qofTransSetNum (
Transaction *trans,
const char *xnum)
2067 if (!trans || !xnum)
return;
2070 CACHE_REPLACE(trans->num, xnum);
2071 qof_instance_set_dirty(QOF_INSTANCE(trans));
2077 qofTransSetDescription (
Transaction *trans,
const char *desc)
2087 if (!trans || !desc)
return;
2090 CACHE_REPLACE(trans->description, desc);
2091 qof_instance_set_dirty(QOF_INSTANCE(trans));
2098 if (!trans || !assoc)
return;
2102 qof_instance_set_dirty(QOF_INSTANCE(trans));
2107 qofTransSetNotes (
Transaction *trans,
const char *notes)
2117 if (!trans || !notes)
return;
2121 qof_instance_set_dirty(QOF_INSTANCE(trans));
2135 qof_instance_set_dirty(QOF_INSTANCE(trans));
2147 if (!trans || i < 0)
return NULL;
2149 FOR_EACH_SPLIT(trans, {
if (i == j)
return s; j++; });
2157 g_return_val_if_fail(trans && split, -1);
2159 FOR_EACH_SPLIT(trans, {
if (s == split)
return j; j++; });
2166 return trans ? trans->splits : NULL;
2173 FOR_EACH_SPLIT(trans, i++);
2180 return trans ? trans->num : NULL;
2186 return trans ? trans->description : NULL;
2193 kvp_frame_get_string (trans->inst.kvp_data, assoc_uri_str) : NULL;
2200 kvp_frame_get_string (trans->inst.kvp_data, trans_notes_str) : NULL;
2207 kvp_frame_get_gint64 (trans->inst.kvp_data, trans_is_closing_str)
2217 return trans ? trans->date_posted.tv_sec : 0;
2224 return trans ? trans->date_entered.tv_sec : 0;
2232 *ts = trans->date_posted;
2239 *ts = trans->date_entered;
2246 return trans ? trans->date_posted : ts;
2259 kvp_frame_get_slot(trans->inst.kvp_data, TRANS_DATE_POSTED);
2267 g_date_clear(&result, 1);
2276 return trans ? trans->date_entered : ts;
2284 if (!trans || !ts)
return;
2286 value = kvp_frame_get_slot (trans->inst.kvp_data, TRANS_DATE_DUE_KVP);
2288 *ts = kvp_value_get_timespec (value);
2306 s = kvp_frame_get_string (trans->inst.kvp_data, TRANS_TXN_TYPE_KVP);
2318 return trans ? kvp_frame_get_string (
2319 trans->inst.kvp_data, TRANS_READ_ONLY_REASON) : NULL;
2328 char* formula = NULL;
2329 g_object_get (split0,
"sx-debit-formula", &formula, NULL);
2330 if (formula != NULL)
2335 g_object_get (split0,
"sx-credit-formula", &formula, NULL);
2336 if (formula != NULL)
2347 GDate *threshold_date;
2358 if (xaccTransIsSXTemplate (trans))
2362 g_assert(threshold_date);
2370 if (g_date_compare(&trans_date, threshold_date) < 0)
2379 g_date_free(threshold_date);
2393 if (trans->date_posted.tv_sec > present)
2411 Split *split = node->data;
2413 if (!xaccTransStillHasSplit(trans, split))
2448 Split *split = node->data;
2450 if (!xaccTransStillHasSplit(trans, split))
2455 if (split->reconciled == state)
2478 (*((guint*)data))++;
2487 counter_thunk, (
void*)&count);
2500 char iso8601_str[ISO_DATELENGTH + 1] =
"";
2502 g_return_if_fail(trans && reason);
2505 frame = trans->inst.kvp_data;
2507 val = kvp_frame_get_slot(frame, trans_notes_str);
2518 FOR_EACH_SPLIT(trans, xaccSplitVoid(s));
2528 g_return_val_if_fail(trans, FALSE);
2529 return (kvp_frame_get_slot(trans->inst.kvp_data, void_reason_str) != NULL);
2535 g_return_val_if_fail(trans, NULL);
2536 return kvp_frame_get_string(trans->inst.kvp_data, void_reason_str);
2545 g_return_val_if_fail(tr, void_time);
2547 val = kvp_frame_get_string(tr->inst.kvp_data, void_time_str);
2557 g_return_if_fail(trans);
2559 frame = trans->inst.kvp_data;
2560 val = kvp_frame_get_slot(frame, void_reason_str);
2565 val = kvp_frame_get_slot(frame, void_former_notes_str);
2571 FOR_EACH_SPLIT(trans, xaccSplitUnvoid(s));
2574 xaccTransClearReadOnly(trans);
2583 g_return_val_if_fail(orig, NULL);
2589 FOR_EACH_SPLIT(trans,
2600 qof_instance_set_dirty(QOF_INSTANCE(trans));
2610 g_return_val_if_fail(trans, NULL);
2611 guid = kvp_frame_get_guid(trans->inst.kvp_data, TRANS_REVERSED_BY);
2626 PERR (
"Transaction doesn't have a currency!");
2651 for (node = trans->splits; node; node = node->next)
2653 Split *s = node->data;
2655 if (!xaccTransStillHasSplit(trans, s))
continue;
2656 xaccSplitDetermineGainStatus(s);
2658 if ((GAINS_STATUS_GAINS & s->gains) &&
2660 ((s->gains_split->gains & GAINS_STATUS_DATE_DIRTY) ||
2661 (s->gains & GAINS_STATUS_DATE_DIRTY)))
2663 Transaction *source_trans = s->gains_split->parent;
2664 ts = source_trans->date_posted;
2665 s->gains &= ~GAINS_STATUS_DATE_DIRTY;
2666 s->gains_split->gains &= ~GAINS_STATUS_DATE_DIRTY;
2669 FOR_EACH_SPLIT(trans, s->gains &= ~GAINS_STATUS_DATE_DIRTY);
2682 ENTER(
"(trans=%p)", trans);
2685 xaccTransScrubGainsDate(trans);
2689 for (node = trans->splits; node; node = node->next)
2691 Split *s = node->data;
2693 if (!xaccTransStillHasSplit(trans, s))
continue;
2695 xaccSplitDetermineGainStatus(s);
2696 if (s->gains & GAINS_STATUS_ADIRTY)
2698 gboolean altered = FALSE;
2699 s->gains &= ~GAINS_STATUS_ADIRTY;
2704 if (altered)
goto restart;
2709 FOR_EACH_SPLIT(trans,
2710 if ((s->gains & GAINS_STATUS_VDIRTY) ||
2712 (s->gains_split->gains & GAINS_STATUS_VDIRTY)))
2716 LEAVE(
"(trans=%p)", trans);
2722 if (!trans || !acc)
return NULL;
2733 destroy_tx_on_book_close(
QofInstance *ent, gpointer data)
2745 gnc_transaction_book_end(
QofBook* book)
2765 DI(.e_type = ) GNC_ID_TRANS,
2768 DI(.book_begin = ) NULL,
2769 DI(.book_end = ) gnc_transaction_book_end,
2778 trans_is_balanced_p (const Transaction *trans)
2783 gboolean xaccTransRegister (
void)
2788 TRANS_NUM, QOF_TYPE_STRING,
2794 TRANS_DESCRIPTION, QOF_TYPE_STRING,
2799 TRANS_DATE_ENTERED, QOF_TYPE_DATE,
2804 TRANS_DATE_POSTED, QOF_TYPE_DATE,
2809 TRANS_DATE_DUE, QOF_TYPE_DATE,
2813 TRANS_IMBALANCE, QOF_TYPE_NUMERIC,
2817 TRANS_NOTES, QOF_TYPE_STRING,
2822 TRANS_ASSOCIATION, QOF_TYPE_STRING,
2827 TRANS_IS_CLOSING, QOF_TYPE_BOOLEAN,
2831 TRANS_IS_BALANCED, QOF_TYPE_BOOLEAN,
2835 TRANS_TYPE, QOF_TYPE_CHAR,
2840 TRANS_VOID_STATUS, QOF_TYPE_BOOLEAN,
2844 TRANS_VOID_REASON, QOF_TYPE_STRING,
2848 TRANS_VOID_TIME, QOF_TYPE_DATE,
2852 TRANS_SPLITLIST, GNC_ID_SPLIT,
2856 TRANS_KVP, QOF_TYPE_KVP,
2864 QOF_PARAM_GUID, QOF_TYPE_GUID,
2876 _utest_trans_fill_functions (
void)
2880 func->mark_trans = mark_trans;
2881 func->gen_event_trans = gen_event_trans;
2882 func->xaccFreeTransaction = xaccFreeTransaction;
2883 func->destroy_gains = destroy_gains;
2884 func->do_destroy = do_destroy;
2885 func->was_trans_emptied = was_trans_emptied;
2886 func->trans_on_error = trans_on_error;
2887 func->trans_cleanup_commit = trans_cleanup_commit;
2888 func->xaccTransScrubGainsDate = xaccTransScrubGainsDate;
2889 func->dupe_trans = dupe_trans;
void xaccSplitSetValue(Split *s, gnc_numeric amt)
int qof_instance_version_cmp(const QofInstance *left, const QofInstance *right)
int xaccAccountTreeForEachTransaction(Account *acc, TransactionCallback proc, void *data)
gint xaccSplitOrder(const Split *sa, const Split *sb)
High-Level API for imposing Lot constraints.
gboolean xaccTransHasReconciledSplits(const Transaction *trans)
Transaction * xaccMallocTransaction(QofBook *book)
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
void xaccTransSetDatePostedSecsNormalized(Transaction *trans, time64 time)
gchar * gnc_timespec_to_iso8601_buff(Timespec ts, gchar *buff)
void kvp_frame_set_slot(KvpFrame *frame, const gchar *key, KvpValue *value)
char xaccTransGetTxnType(const Transaction *trans)
gchar * gnc_num_dbg_to_string(gnc_numeric n)
int gnc_commodity_get_fraction(const gnc_commodity *cm)
void qof_backend_set_error(QofBackend *be, QofBackendError err)
void kvp_frame_set_timespec(KvpFrame *frame, const gchar *path, Timespec ts)
void xaccTransGetDateDueTS(const Transaction *trans, Timespec *ts)
gboolean xaccTransHasSplitsInStateByAccount(const Transaction *trans, const char state, const Account *account)
Split * xaccTransGetSplit(const Transaction *trans, int i)
time64 xaccTransGetDate(const Transaction *trans)
const char * gnc_print_date(Timespec ts)
gboolean xaccTransUseTradingAccounts(const Transaction *trans)
gboolean xaccTransIsReadonlyByPostedDate(const Transaction *trans)
#define qof_instance_is_dirty
QofBook * qof_instance_get_book(gconstpointer)
gboolean qof_collection_is_dirty(const QofCollection *col)
time64 gnc_time64_get_today_end(void)
gboolean xaccTransIsOpen(const Transaction *trans)
gnc_numeric xaccTransGetAccountBalance(const Transaction *trans, const Account *account)
QofInstance * qof_collection_lookup_entity(const QofCollection *, const GncGUID *)
#define PINFO(format, args...)
GNCAccountType xaccAccountGetType(const Account *acc)
gboolean xaccSplitDestroy(Split *split)
const char * xaccTransGetAssociation(const Transaction *trans)
QofBackendError
The errors that can be reported to the GUI & other front-end users.
int xaccAccountGetCommoditySCU(const Account *acc)
gnc_numeric gnc_numeric_neg(gnc_numeric a)
void xaccTransSetNotes(Transaction *trans, const char *notes)
const char * xaccTransGetVoidReason(const Transaction *trans)
void xaccTransWriteLog(Transaction *trans, char flag)
void xaccTransGetDateEnteredTS(const Transaction *trans, Timespec *ts)
gboolean qof_instance_get_destroying(gconstpointer ptr)
void xaccSplitCopyOnto(const Split *from_split, Split *to_split)
gint kvp_frame_compare(const KvpFrame *fa, const KvpFrame *fb)
void qof_class_register(QofIdTypeConst obj_name, QofSortFunc default_sort_fcn, const QofParam *params)
char xaccSplitGetReconcile(const Split *split)
KvpFrame * kvp_frame_copy(const KvpFrame *frame)
gboolean gnc_commodity_equal(const gnc_commodity *a, const gnc_commodity *b)
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
void xaccTransSetDescription(Transaction *trans, const char *desc)
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
void xaccTransSetNum(Transaction *trans, const char *xnum)
Use a 64-bit unsigned int timespec.
void kvp_frame_set_gint64(KvpFrame *frame, const gchar *path, gint64 ival)
gboolean gnc_numeric_zero_p(gnc_numeric a)
void xaccTransCopyOnto(const Transaction *from_trans, Transaction *to_trans)
void xaccSplitSetReconcile(Split *split, char recn)
Transaction * xaccSplitGetParent(const Split *split)
gchar * guid_to_string_buff(const GncGUID *guid, gchar *buff)
gpointer(* QofAccessFunc)(gpointer object, const QofParam *param)
gboolean xaccTransIsBalanced(const Transaction *trans)
#define QOF_OBJECT_VERSION
const char * xaccTransGetNum(const Transaction *trans)
gboolean qof_commit_edit(QofInstance *inst)
#define PERR(format, args...)
int xaccTransOrder_num_action(const Transaction *ta, const char *actna, const Transaction *tb, const char *actnb)
#define ENTER(format, args...)
gnc_numeric xaccSplitGetBalance(const Split *s)
void xaccTransSetDatePostedGDate(Transaction *trans, GDate date)
void qof_collection_foreach(const QofCollection *, QofInstanceForeachCB, gpointer user_data)
void kvp_frame_set_slot_path(KvpFrame *frame, KvpValue *value, const gchar *first_key,...)
void xaccTransSetDateEnteredTS(Transaction *trans, const Timespec *ts)
gboolean gnc_numeric_negative_p(gnc_numeric a)
gboolean xaccTransHasReconciledSplitsByAccount(const Transaction *trans, const Account *account)
KvpValue * kvp_frame_replace_value_nc(KvpFrame *frame, const gchar *slot, KvpValue *new_value)
void xaccTransSetCurrency(Transaction *trans, gnc_commodity *curr)
void xaccTransDestroy(Transaction *trans)
gboolean xaccSplitEqual(const Split *sa, const Split *sb, gboolean check_guids, gboolean check_balances, gboolean check_txn_splits)
#define PWARN(format, args...)
const char * xaccTransGetNotes(const Transaction *trans)
Transaction * xaccTransLookup(const GncGUID *guid, QofBook *book)
void xaccTransSetIsClosingTxn(Transaction *trans, gboolean is_closing)
void qof_instance_init_data(QofInstance *, QofIdType, QofBook *)
gint timespec_cmp(const Timespec *ta, const Timespec *tb)
MonetaryList * gnc_monetary_list_delete_zeros(MonetaryList *list)
gboolean qof_begin_edit(QofInstance *inst)
int xaccTransCountSplits(const Transaction *trans)
void xaccTransSetTxnType(Transaction *trans, char type)
GDate timespec_to_gdate(Timespec ts)
convert single-entry accounts to clean double-entry
GDate * qof_book_get_autoreadonly_gdate(const QofBook *book)
gboolean xaccTransHasSplitsInState(const Transaction *trans, const char state)
guint32 qof_instance_get_idata(gconstpointer inst)
void xaccSplitSetAmount(Split *s, gnc_numeric amt)
void xaccTransSetAssociation(Transaction *trans, const char *assoc)
gboolean xaccTransEqual(const Transaction *ta, const Transaction *tb, gboolean check_guids, gboolean check_splits, gboolean check_balances, gboolean assume_ordered)
gnc_numeric gnc_numeric_convert(gnc_numeric n, gint64 denom, gint how)
gnc_numeric xaccTransGetImbalanceValue(const Transaction *trans)
void xaccTransSetReadOnly(Transaction *trans, const char *reason)
void xaccTransVoid(Transaction *trans, const char *reason)
Transaction * xaccTransClone(const Transaction *from)
#define GUID_ENCODING_LENGTH
gnc_numeric gnc_numeric_error(GNCNumericErrorCode error_code)
void gnc_monetary_list_free(MonetaryList *list)
Timespec xaccTransRetDateEnteredTS(const Transaction *trans)
void xaccTransScrubImbalance(Transaction *trans, Account *root, Account *account)
void qof_instance_copy_book(gpointer ptr1, gconstpointer ptr2)
void xaccSplitScrub(Split *split)
void xaccTransCopyFromClipBoard(const Transaction *from_trans, Transaction *to_trans, const Account *from_acc, Account *to_acc, gboolean no_date)
void xaccTransScrubGains(Transaction *trans, Account *gain_acc)
void xaccTransScrubSplits(Transaction *trans)
Transaction * xaccTransCloneNoKvp(const Transaction *from)
void kvp_frame_set_slot_nc(KvpFrame *frame, const gchar *key, KvpValue *value)
gboolean xaccTransInFutureByPostedDate(const Transaction *trans)
Timespec xaccTransRetDateDueTS(const Transaction *trans)
int(* QofSortFunc)(gconstpointer, gconstpointer)
const char * xaccTransGetDescription(const Transaction *trans)
gboolean qof_commit_edit_part2(QofInstance *inst, void(*on_error)(QofInstance *, QofBackendError), void(*on_done)(QofInstance *), void(*on_free)(QofInstance *))
#define xaccTransGetBook(X)
QofBackendError qof_backend_get_error(QofBackend *be)
void xaccTransSetDate(Transaction *trans, int day, int mon, int year)
void qof_collection_mark_clean(QofCollection *)
void xaccTransCommitEdit(Transaction *trans)
Additional event handling code.
gnc_numeric gnc_numeric_div(gnc_numeric x, gnc_numeric y, gint64 denom, gint how)
#define xaccSplitGetGUID(X)
void xaccTransBeginEdit(Transaction *trans)
gchar * kvp_frame_to_string(const KvpFrame *frame)
const char * xaccTransGetReadOnly(const Transaction *trans)
int xaccTransGetSplitIndex(const Transaction *trans, const Split *split)
void xaccTransUnvoid(Transaction *trans)
Timespec gnc_iso8601_to_timespec_gmt(const gchar *)
KvpFrame * kvp_frame_set_value_nc(KvpFrame *frame, const gchar *path, KvpValue *value)
All type declarations for the whole Gnucash engine.
const GncGUID * qof_entity_get_guid(gconstpointer)
Split * xaccMallocSplit(QofBook *book)
#define xaccTransGetGUID(X)
time64 xaccTransGetDateEntered(const Transaction *trans)
API for the transaction logger.
const char * gnc_commodity_get_printname(const gnc_commodity *cm)
Transaction * xaccTransReverse(Transaction *orig)
guint gnc_book_count_transactions(QofBook *book)
void xaccTransSetDatePostedSecs(Transaction *trans, time64 secs)
private api for data storage backend
gboolean xaccTransGetVoidStatus(const Transaction *trans)
gboolean qof_book_is_readonly(const QofBook *book)
gboolean xaccSplitAssign(Split *split)
gnc_numeric xaccSplitGetValue(const Split *split)
void qof_event_suspend(void)
Suspend all engine events.
int qof_string_number_compare_func(gpointer a, gpointer b, gint options, QofParam *this_param)
Account * xaccSplitGetAccount(const Split *s)
Timespec xaccTransGetVoidTime(const Transaction *tr)
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
const GncGUID * guid_null(void)
gboolean xaccTransGetIsClosingTxn(const Transaction *trans)
gnc_commodity * xaccTransGetCurrency(const Transaction *trans)
void qof_event_resume(void)
gint qof_instance_guid_compare(const gconstpointer ptr1, const gconstpointer ptr2)
MonetaryList * xaccTransGetImbalance(const Transaction *trans)
void xaccTransSetDateDueTS(Transaction *trans, const Timespec *ts)
struct KvpFrameImpl KvpFrame
Transaction * xaccTransGetReversedBy(const Transaction *trans)
GDate kvp_value_get_gdate(const KvpValue *value)
Timespec xaccTransRetDatePostedTS(const Transaction *trans)
#define LEAVE(format, args...)
gboolean xaccScrubLot(GNCLot *lot)
void(* QofSetterFunc)(gpointer, gpointer)
const char * gnc_commodity_get_unique_name(const gnc_commodity *cm)
time64 gnc_time(time64 *tbuf)
get the current local time
int xaccTransOrder(const Transaction *ta, const Transaction *tb)
QofCollection * qof_book_get_collection(const QofBook *, QofIdType)
Timespec gdate_to_timespec(GDate d)
gboolean qof_object_register(const QofObject *object)
void kvp_value_delete(KvpValue *value)
gchar * gnc_ctime(const time64 *secs)
Return a string representation of a date from a 64-bit time value.
void xaccTransSetDateEnteredSecs(Transaction *trans, time64 secs)
gboolean qof_book_uses_autoreadonly(const QofBook *book)
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
gboolean qof_book_shutting_down(const QofBook *book)
void qof_event_gen(QofInstance *entity, QofEventId event_type, gpointer event_data)
Invoke all registered event handlers using the given arguments.
void xaccTransSortSplits(Transaction *trans)
Scheduled Transactions public handling routines.
GDate xaccTransGetDatePostedGDate(const Transaction *trans)
gboolean qof_book_use_trading_accounts(const QofBook *book)
Utilities to Automatically Compute Capital Gains/Losses.
void kvp_frame_set_string(KvpFrame *frame, const gchar *path, const gchar *str)
Store a copy of the string at the indicated path.
struct KvpValueImpl KvpValue
SplitList * xaccTransGetSplitList(const Transaction *trans)
Commodity handling public routines.
void xaccTransRollbackEdit(Transaction *trans)
Transaction * xaccTransCopyToClipBoard(const Transaction *from_trans)
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
void xaccTransGetDatePostedTS(const Transaction *trans, Timespec *ts)
gnc_numeric xaccTransGetAccountAmount(const Transaction *trans, const Account *acc)
gnc_numeric xaccTransGetAccountValue(const Transaction *trans, const Account *acc)
const gchar * QofLogModule
void xaccTransSetDatePostedTS(Transaction *trans, const Timespec *ts)
void gnc_gdate_set_time64(GDate *gd, time64 time)
gnc_numeric xaccSplitGetAmount(const Split *split)