diff --git a/po/POTFILES.in b/po/POTFILES.in index f4d0905d57e..4fd491b5af4 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -404,7 +404,6 @@ src/import-export/import-backend.c src/import-export/import-commodity-matcher.c src/import-export/import-format-dialog.c src/import-export/import-main-matcher.c -src/import-export/import-match-map.c src/import-export/import-match-picker.c src/import-export/import-parse.c src/import-export/import-settings.c diff --git a/src/app-utils/business-helpers.c b/src/app-utils/business-helpers.c index f4c75fae131..618e01e8f48 100644 --- a/src/app-utils/business-helpers.c +++ b/src/app-utils/business-helpers.c @@ -33,7 +33,7 @@ GncTaxTable* gnc_business_get_default_tax_table (QofBook *book, GncOwnerType own GNCOptionDB *odb; odb = gnc_option_db_new_for_type (GNC_ID_BOOK); - gnc_option_db_load_from_kvp (odb, qof_book_get_slots (book)); + qof_book_load_options (book, gnc_option_db_load_from_kvp, odb); switch (ownertype) { diff --git a/src/app-utils/gnc-accounting-period.c b/src/app-utils/gnc-accounting-period.c index 3c17ac9a073..e7dd8e7eab0 100644 --- a/src/app-utils/gnc-accounting-period.c +++ b/src/app-utils/gnc-accounting-period.c @@ -102,15 +102,13 @@ get_fy_end(void) { QofBook *book; KvpFrame *book_frame; - gint64 month, day; + GDate *date = NULL; book = gnc_get_current_book(); - book_frame = qof_book_get_slots(book); - month = kvp_frame_get_gint64(book_frame, "/book/fyear_end/month"); - day = kvp_frame_get_gint64(book_frame, "/book/fyear_end/day"); - if (g_date_valid_dmy(day, month, 2005 /* not leap year */)) - return g_date_new_dmy(day, month, G_DATE_BAD_YEAR); - return NULL; + qof_instance_get (QOF_INSTANCE (book), + "fy-end", &date, + NULL); + return date; } time64 diff --git a/src/app-utils/gnc-sx-instance-model.c b/src/app-utils/gnc-sx-instance-model.c index 744c274ccfd..bfe2701ff6c 100644 --- a/src/app-utils/gnc-sx-instance-model.c +++ b/src/app-utils/gnc-sx-instance-model.c @@ -176,10 +176,9 @@ _get_vars_helper(Transaction *txn, void *var_hash_data) { GHashTable *var_hash = (GHashTable*)var_hash_data; GList *split_list; - kvp_frame *kvpf; - kvp_value *kvp_val; Split *s; - char *str; + gchar *credit_formula = NULL; + gchar *debit_formula = NULL; gnc_commodity *first_cmdty = NULL; split_list = xaccTransGetSplitList(txn); @@ -191,16 +190,16 @@ _get_vars_helper(Transaction *txn, void *var_hash_data) for ( ; split_list; split_list = split_list->next) { gnc_commodity *split_cmdty = NULL; - GncGUID *acct_guid; + GncGUID *acct_guid = NULL; Account *acct; s = (Split*)split_list->data; - kvpf = xaccSplitGetSlots(s); - kvp_val = kvp_frame_get_slot_path(kvpf, - GNC_SX_ID, - GNC_SX_ACCOUNT, - NULL); - acct_guid = kvp_value_get_guid(kvp_val); + + qof_instance_get (QOF_INSTANCE (s), + "sx-account", &acct_guid, + "sx-credit-formula", &credit_formula, + "sx-debit-formula", &debit_formula, + NULL); acct = xaccAccountLookup(acct_guid, gnc_get_current_book()); split_cmdty = xaccAccountGetCommodity(acct); if (first_cmdty == NULL) @@ -226,31 +225,16 @@ _get_vars_helper(Transaction *txn, void *var_hash_data) } // existing... ------------------------------------------ - kvp_val = kvp_frame_get_slot_path(kvpf, - GNC_SX_ID, - GNC_SX_CREDIT_FORMULA, - NULL); - if (kvp_val != NULL) - { - str = kvp_value_get_string(kvp_val); - if (str && strlen(str) != 0) - { - gnc_sx_parse_vars_from_formula(str, var_hash, NULL); - } - } - - kvp_val = kvp_frame_get_slot_path(kvpf, - GNC_SX_ID, - GNC_SX_DEBIT_FORMULA, - NULL); - if (kvp_val != NULL) - { - str = kvp_value_get_string(kvp_val); - if (str && strlen(str) != 0) - { - gnc_sx_parse_vars_from_formula(str, var_hash, NULL); - } - } + if (credit_formula && strlen(credit_formula) != 0) + { + gnc_sx_parse_vars_from_formula(credit_formula, var_hash, NULL); + } + if (debit_formula && strlen(debit_formula) != 0) + { + gnc_sx_parse_vars_from_formula(debit_formula, var_hash, NULL); + } + g_free (credit_formula); + g_free (debit_formula); } return 0; @@ -900,31 +884,15 @@ typedef struct _SxTxnCreationData } SxTxnCreationData; static gboolean -_get_template_split_account(const SchedXaction* sx, const Split *template_split, Account **split_acct, GList **creation_errors) +_get_template_split_account(const SchedXaction* sx, + const Split *template_split, + Account **split_acct, + GList **creation_errors) { - GncGUID *acct_guid; - kvp_frame *split_kvpf; - kvp_value *kvp_val; - - split_kvpf = xaccSplitGetSlots(template_split); - /* contains the guid of the split's actual account. */ - kvp_val = kvp_frame_get_slot_path(split_kvpf, - GNC_SX_ID, - GNC_SX_ACCOUNT, - NULL); - if (kvp_val == NULL) - { - GString *err = g_string_new(""); - g_string_printf(err, "Null account kvp value for SX [%s], cancelling creation.", - xaccSchedXactionGetName(sx)); - g_critical("%s", err->str); - if (creation_errors != NULL) - *creation_errors = g_list_append(*creation_errors, err); - else - g_string_free(err, TRUE); - return FALSE; - } - acct_guid = kvp_value_get_guid( kvp_val ); + GncGUID *acct_guid = NULL; + qof_instance_get (QOF_INSTANCE (template_split), + "sx-account", &acct_guid, + NULL); *split_acct = xaccAccountLookup(acct_guid, gnc_get_current_book()); if (*split_acct == NULL) { @@ -946,34 +914,34 @@ _get_template_split_account(const SchedXaction* sx, const Split *template_split, } static void -_get_sx_formula_value(const SchedXaction* sx, const Split *template_split, gnc_numeric *numeric, GList **creation_errors, const char *formula_key, const char* numeric_key, GHashTable *variable_bindings) +_get_sx_formula_value(const SchedXaction* sx, + const Split *template_split, + gnc_numeric *numeric, + GList **creation_errors, + const char *formula_key, + const char* numeric_key, + GHashTable *variable_bindings) { - kvp_frame *split_kvpf; - kvp_value *kvp_val; - char *formula_str, *parseErrorLoc; - - split_kvpf = xaccSplitGetSlots(template_split); - - /* First look up the gnc_numeric value in the template split */ - kvp_val = kvp_frame_get_slot_path(split_kvpf, - GNC_SX_ID, - numeric_key, - NULL); - *numeric = kvp_value_get_numeric(kvp_val); - if ((gnc_numeric_check(*numeric) == GNC_ERROR_OK) - && !gnc_numeric_zero_p(*numeric)) + + char *formula_str = NULL, *parseErrorLoc = NULL; + gnc_numeric *numeric_val = NULL; + qof_instance_get (QOF_INSTANCE (template_split), + formula_key, &formula_str, + numeric_key, &numeric_val, + NULL); + + if (numeric_val != NULL && + gnc_numeric_check(*numeric_val) == GNC_ERROR_OK && + !gnc_numeric_zero_p(*numeric_val)) { /* Already a valid non-zero result? Then return and don't * parse the string. Luckily we avoid any locale problems with * decimal points here! Phew. */ + numeric->num = numeric_val->num; + numeric->denom = numeric_val->denom; return; } - kvp_val = kvp_frame_get_slot_path(split_kvpf, - GNC_SX_ID, - formula_key, - NULL); - formula_str = kvp_value_get_string(kvp_val); if (formula_str != NULL && strlen(formula_str) != 0) { GHashTable *parser_vars = NULL; @@ -1010,13 +978,17 @@ _get_sx_formula_value(const SchedXaction* sx, const Split *template_split, gnc_n static void _get_credit_formula_value(GncSxInstance *instance, const Split *template_split, gnc_numeric *credit_num, GList **creation_errors) { - _get_sx_formula_value(instance->parent->sx, template_split, credit_num, creation_errors, GNC_SX_CREDIT_FORMULA, GNC_SX_CREDIT_NUMERIC, instance->variable_bindings); + _get_sx_formula_value(instance->parent->sx, template_split, credit_num, + creation_errors, "sx-credit-formula", + "sx-credit-numeric", instance->variable_bindings); } static void _get_debit_formula_value(GncSxInstance *instance, const Split *template_split, gnc_numeric *debit_num, GList **creation_errors) { - _get_sx_formula_value(instance->parent->sx, template_split, debit_num, creation_errors, GNC_SX_DEBIT_FORMULA, GNC_SX_DEBIT_NUMERIC, instance->variable_bindings); + _get_sx_formula_value(instance->parent->sx, template_split, debit_num, + creation_errors, "sx-debit-formula", + "sx-debit-numeric", instance->variable_bindings); } static gboolean @@ -1035,7 +1007,7 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data) as not finding the approrpiate Accounts and not being able to parse the formula|credit/debit strings. */ - new_txn = xaccTransClone(template_txn); + new_txn = xaccTransCloneNoKvp(template_txn); xaccTransBeginEdit(new_txn); g_debug("creating template txn desc [%s] for sx [%s]", @@ -1044,9 +1016,6 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data) g_debug("template txn currency is %s", gnc_commodity_get_mnemonic(xaccTransGetCurrency (template_txn))); - /* clear any copied KVP data */ - qof_instance_set_slots(QOF_INSTANCE(new_txn), kvp_frame_new()); - /* Bug#500427: copy the notes, if any */ if (xaccTransGetNotes(template_txn) != NULL) { @@ -1090,9 +1059,6 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data) break; } - /* clear out any copied Split frame data. */ - qof_instance_set_slots(QOF_INSTANCE(copying_split), kvp_frame_new()); - split_cmdty = xaccAccountGetCommodity(split_acct); if (first_cmdty == NULL) { @@ -1215,13 +1181,10 @@ create_each_transaction_helper(Transaction *template_txn, void *user_data) } { - kvp_frame *txn_frame; - txn_frame = xaccTransGetSlots(new_txn); - kvp_frame_set_guid(txn_frame, "from-sched-xaction", - xaccSchedXactionGetGUID(creation_data->instance->parent->sx)); -/* The transaction was probably marked dirty by xaccTransSetCurrency, - * but just in case: */ - qof_instance_set_dirty (QOF_INSTANCE (new_txn)); + qof_instance_set (QOF_INSTANCE (new_txn), + "from-sched-xaction", + xaccSchedXactionGetGUID(creation_data->instance->parent->sx), + NULL); } xaccTransCommitEdit(new_txn); @@ -1631,9 +1594,14 @@ create_cashflow_helper(Transaction *template_txn, void *user_data) gint gncn_error; /* Credit value */ - _get_sx_formula_value(creation_data->sx, template_split, &credit_num, creation_data->creation_errors, GNC_SX_CREDIT_FORMULA, GNC_SX_CREDIT_NUMERIC, NULL); + _get_sx_formula_value(creation_data->sx, template_split, + &credit_num, creation_data->creation_errors, + "sx-credit-formula", "sx-credit-numeric", + NULL); /* Debit value */ - _get_sx_formula_value(creation_data->sx, template_split, &debit_num, creation_data->creation_errors, GNC_SX_DEBIT_FORMULA, GNC_SX_DEBIT_NUMERIC, NULL); + _get_sx_formula_value(creation_data->sx, template_split, + &debit_num, creation_data->creation_errors, + "sx-debit-formula", "sx-debit-numeric", NULL); /* The resulting cash flow number: debit minus credit, * multiplied with the count factor. */ diff --git a/src/app-utils/option-util.h b/src/app-utils/option-util.h index a37fccb6eff..5ad66e2ea62 100644 --- a/src/app-utils/option-util.h +++ b/src/app-utils/option-util.h @@ -33,7 +33,7 @@ typedef struct gnc_option GNCOption; typedef struct gnc_option_section GNCOptionSection; -typedef struct gnc_option_db GNCOptionDB; +/* typedef struct gnc_option_db GNCOptionDB is in qof-book.h */ typedef int GNCOptionDBHandle; diff --git a/src/app-utils/test/Makefile.am b/src/app-utils/test/Makefile.am index 3a00263bb79..ca5ec09ef0a 100644 --- a/src/app-utils/test/Makefile.am +++ b/src/app-utils/test/Makefile.am @@ -1,3 +1,5 @@ +include $(top_srcdir)/test-templates/Makefile.decl + TESTS = \ test-link-module \ test-load-module \ @@ -66,3 +68,17 @@ AM_CPPFLAGS = \ -I${top_srcdir}/src/libqof/qof \ ${GUILE_CFLAGS} \ ${GLIB_CFLAGS} + +TEST_PROGS += test-app-utils + +noinst_PROGRAMS = ${TEST_PROGS} ${CHECK_PROGS} + +test_app_utils_SOURCES = \ + test-app-utils.c \ + test-option-util.c + +test_app_utils_CFLAGS = \ + ${DEFAULT_INCLUDES} \ + -I${top_srcdir}/${MODULEPATH}/ \ + -DTESTPROG=test_app_utils \ + ${GLIB_CFLAGS} diff --git a/src/app-utils/test/test-app-utils.c b/src/app-utils/test/test-app-utils.c new file mode 100644 index 00000000000..ee0f274c93a --- /dev/null +++ b/src/app-utils/test/test-app-utils.c @@ -0,0 +1,56 @@ +/******************************************************************** + * test-app-utils.c: GLib g_test test execution file. * + * Copyright 2013 John Ralls * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#include +#include +#include +#include +#include + +extern void test_suite_option_util (void); + +static void +guile_main (void *closure, int argc, char **argv) +{ + GNCModule mod; + int retval; + gnc_module_system_init (); + mod = gnc_module_load ("gnucash/app-utils", 0); + + test_suite_option_util (); + retval = g_test_run (); + + exit (retval); +} + +int +main (int argc, char *argv[]) +{ + qof_init (); /* Initialize the GObject system */ + qof_log_init_filename_special ("stderr"); /* Init the log system */ + g_test_init (&argc, &argv, NULL); /* initialize test program */ + //qof_log_set_level("gnc", G_LOG_LEVEL_DEBUG); + g_test_bug_base("https://bugzilla.gnome.org/show_bug.cgi?id="); /* init the bugzilla URL */ + g_setenv ("GNC_UNINSTALLED", "1", TRUE); + scm_boot_guile (argc, argv, guile_main, NULL); + +} diff --git a/src/app-utils/test/test-option-util.c b/src/app-utils/test/test-option-util.c new file mode 100644 index 00000000000..dc99f32a9a9 --- /dev/null +++ b/src/app-utils/test/test-option-util.c @@ -0,0 +1,126 @@ +/******************************************************************** + * test-option-util.c: GLib g_test test suite for Split.c. * + * Copyright 2013 John Ralls * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, you can retrieve it from * + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * + * or contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + ********************************************************************/ + +#include +#include +#include +#include + +#include "../option-util.h" + +static const gchar *suitename = "/app-utils/option-util"; +void test_suite_option_util (void); + +typedef struct +{ + QofBook *book; + GSList *hdlrs; +} Fixture; + +/* Expose a mostly-private QofInstance function to load options into + * the Book. + */ +extern KvpFrame *qof_instance_get_slots (QofInstance*); + +static void +setup (Fixture *fixture, gconstpointer pData) +{ + fixture->book = qof_book_new (); + fixture->hdlrs = NULL; +} + +static void +setup_kvp (Fixture *fixture, gconstpointer pData) +{ + QofBook *book; + KvpFrame *slots; + setup (fixture, pData); + book = fixture->book; + slots = qof_instance_get_slots (QOF_INSTANCE (book)); + qof_begin_edit (QOF_INSTANCE (book)); + qof_instance_set (QOF_INSTANCE (book), + "trading-accts", "t", + "split-action-num-field", "t", + "autoreadonly-days", (double)21, + NULL); + + kvp_frame_set_string (slots, "options/Business/Company Name", + "Bogus Company"); + qof_commit_edit (QOF_INSTANCE (book)); +} + +static void +teardown (Fixture *fixture, gconstpointer pData) +{ + qof_book_destroy (fixture->book); + g_slist_free_full (fixture->hdlrs, test_free_log_handler); + test_clear_error_list(); +} + +static void +test_option_load_from_kvp (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = fixture->book; + GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); + + qof_book_load_options (book, gnc_option_db_load_from_kvp, odb); + g_assert (gnc_option_db_lookup_boolean_option (odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_TRADING_ACCOUNTS, FALSE)); + g_assert_cmpstr (gnc_option_db_lookup_string_option (odb, "Business", "Company Name", FALSE), ==, "Bogus Company"); + g_assert_cmpfloat (gnc_option_db_lookup_number_option (odb, OPTION_SECTION_ACCOUNTS, OPTION_NAME_AUTO_READONLY_DAYS, FALSE), ==, 21); + + gnc_option_db_destroy (odb); +} + +static void +test_option_save_to_kvp (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = fixture->book; + GNCOptionDB *odb = gnc_option_db_new_for_type (QOF_ID_BOOK); + KvpFrame *slots = qof_instance_get_slots (QOF_INSTANCE (book)); + + g_assert (gnc_option_db_set_boolean_option (odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_TRADING_ACCOUNTS, + TRUE)); + g_assert (gnc_option_db_set_string_option (odb, "Business", "Company Name", + "Bogus Company")); + g_assert (gnc_option_db_set_number_option (odb, OPTION_SECTION_ACCOUNTS, + OPTION_NAME_AUTO_READONLY_DAYS, + 17)); + qof_book_save_options (book, gnc_option_db_save_to_kvp, odb, TRUE); + g_assert_cmpstr (kvp_frame_get_string (slots, "options/Accounts/Use Trading Accounts"), == , "t"); + g_assert_cmpstr (kvp_frame_get_string (slots, "options/Business/Company Name"), ==, "Bogus Company"); + g_assert_cmpfloat (kvp_frame_get_double (slots, "options/Accounts/Day Threshold for Read-Only Transactions (red line)"), ==, 17); + + gnc_option_db_destroy (odb); +} + + +void +test_suite_option_util (void) +{ + GNC_TEST_ADD (suitename, "Option DB Load from KVP", Fixture, NULL, setup_kvp, test_option_load_from_kvp, teardown); + GNC_TEST_ADD (suitename, "Option DB Save to KVP", Fixture, NULL, setup, test_option_save_to_kvp, teardown); + +} diff --git a/src/backend/dbi/test/test-dbi-stuff.c b/src/backend/dbi/test/test-dbi-stuff.c index 4ac96ae2b7c..c0e1d1e73a5 100644 --- a/src/backend/dbi/test/test-dbi-stuff.c +++ b/src/backend/dbi/test/test-dbi-stuff.c @@ -168,6 +168,9 @@ compare_sxs( QofBook* book_1, QofBook* book_2 ) compare_single_sx, "Scheduled transaction lists match" ); } +/* EFFECTIVE FRIEND FUNCTION */ +extern KvpFrame *qof_instance_get_slots (const QofInstance *); + static void compare_single_lot( QofInstance* inst, gpointer user_data ) { @@ -181,8 +184,8 @@ compare_single_lot( QofInstance* inst, gpointer user_data ) gnc_lot_get_account (lot_2), FALSE )); g_assert_cmpint (gnc_lot_is_closed (lot_1), ==, gnc_lot_is_closed (lot_2)); - g_assert (kvp_frame_compare (gnc_lot_get_slots (lot_1), - gnc_lot_get_slots (lot_2)) == 0); + g_assert (kvp_frame_compare (qof_instance_get_slots (QOF_INSTANCE (lot_1)), + qof_instance_get_slots (QOF_INSTANCE (lot_2))) == 0); splits1 = gnc_lot_get_split_list (lot_1); splits2 = gnc_lot_get_split_list (lot_2); g_assert_cmpint (g_list_length (splits1), ==, g_list_length (splits2)); diff --git a/src/backend/sql/gnc-account-sql.c b/src/backend/sql/gnc-account-sql.c index fb1ce982f4a..7d6b3a3e1c8 100644 --- a/src/backend/sql/gnc-account-sql.c +++ b/src/backend/sql/gnc-account-sql.c @@ -294,12 +294,14 @@ load_all_accounts( GncSqlBackend* be ) { acct_balances_t* balances = (acct_balances_t*)bal->data; + qof_instance_increase_editlevel (balances->acct); g_object_set( balances->acct, "start-balance", &balances->balance, "start-cleared-balance", &balances->cleared_balance, "start-reconciled-balance", &balances->reconciled_balance, NULL); + qof_instance_decrease_editlevel (balances->acct); } if ( bal_slist != NULL ) { @@ -418,7 +420,9 @@ load_account_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, account, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-address-sql.c b/src/backend/sql/gnc-address-sql.c index a29d4aac560..a3f399440f0 100644 --- a/src/backend/sql/gnc-address-sql.c +++ b/src/backend/sql/gnc-address-sql.c @@ -115,7 +115,9 @@ load_address( const GncSqlBackend* be, GncSqlRow* row, } if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, addr, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-backend-sql.c b/src/backend/sql/gnc-backend-sql.c index 6169388ca02..85f2daaf1c6 100644 --- a/src/backend/sql/gnc-backend-sql.c +++ b/src/backend/sql/gnc-backend-sql.c @@ -1204,7 +1204,11 @@ const GncSqlColumnTableEntry* table_row ) s = g_value_get_string( val ); if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, s, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_decrease_editlevel (QOF_INSTANCE (pObject)); } else { @@ -1308,7 +1312,11 @@ load_int( const GncSqlBackend* be, GncSqlRow* row, } if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, int_value, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_decrease_editlevel (QOF_INSTANCE (pObject)); } else { @@ -1406,7 +1414,11 @@ load_boolean( const GncSqlBackend* be, GncSqlRow* row, } if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, int_value, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_decrease_editlevel (QOF_INSTANCE (pObject)); } else { @@ -1499,7 +1511,11 @@ load_int64( const GncSqlBackend* be, GncSqlRow* row, } if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, i64_value, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_decrease_editlevel (QOF_INSTANCE (pObject)); } else { @@ -1606,7 +1622,11 @@ load_double( const GncSqlBackend* be, GncSqlRow* row, } if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, d_value, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_decrease_editlevel (QOF_INSTANCE (pObject)); } else { @@ -1705,7 +1725,11 @@ load_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, pGuid, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_decrease_editlevel (QOF_INSTANCE (pObject)); } else { @@ -1912,7 +1936,11 @@ load_timespec( const GncSqlBackend* be, GncSqlRow* row, { if (table_row->gobj_param_name != NULL) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, &ts, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_decrease_editlevel (QOF_INSTANCE (pObject)); } else { @@ -2014,7 +2042,11 @@ load_date( const GncSqlBackend* be, GncSqlRow* row, g_date_time_unref (gdt); if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, date, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); } else { @@ -2048,7 +2080,12 @@ load_date( const GncSqlBackend* be, GncSqlRow* row, date = g_date_new_dmy( day, month, year ); if ( table_row->gobj_param_name != NULL ) { - g_object_set( pObject, table_row->gobj_param_name, date, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); + g_object_set (pObject, table_row->gobj_param_name, + date, NULL); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); } else { @@ -2187,7 +2224,11 @@ load_numeric( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); g_object_set( pObject, table_row->gobj_param_name, &n, NULL ); + if (QOF_IS_INSTANCE (pObject)) + qof_instance_increase_editlevel (QOF_INSTANCE (pObject)); } else { diff --git a/src/backend/sql/gnc-bill-term-sql.c b/src/backend/sql/gnc-bill-term-sql.c index 4846b0f763d..eaf7243b243 100644 --- a/src/backend/sql/gnc-bill-term-sql.c +++ b/src/backend/sql/gnc-bill-term-sql.c @@ -363,7 +363,9 @@ load_billterm_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, term, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-budget-sql.c b/src/backend/sql/gnc-budget-sql.c index 59a7efb8da8..4c697c9f9e3 100644 --- a/src/backend/sql/gnc-budget-sql.c +++ b/src/backend/sql/gnc-budget-sql.c @@ -500,7 +500,9 @@ load_budget_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, budget, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-commodity-sql.c b/src/backend/sql/gnc-commodity-sql.c index 23899468980..1f8f556ddf4 100644 --- a/src/backend/sql/gnc-commodity-sql.c +++ b/src/backend/sql/gnc-commodity-sql.c @@ -283,7 +283,9 @@ load_commodity_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, commodity, NULL ); + qof_instance_decrease_editlevel (pObject); } else if ( setter != NULL ) { diff --git a/src/backend/sql/gnc-invoice-sql.c b/src/backend/sql/gnc-invoice-sql.c index 87889609769..72c1cd2a77a 100644 --- a/src/backend/sql/gnc-invoice-sql.c +++ b/src/backend/sql/gnc-invoice-sql.c @@ -303,7 +303,9 @@ load_invoice_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, invoice, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-lots-sql.c b/src/backend/sql/gnc-lots-sql.c index a6ee240996f..22c4a705d57 100644 --- a/src/backend/sql/gnc-lots-sql.c +++ b/src/backend/sql/gnc-lots-sql.c @@ -235,7 +235,9 @@ load_lot_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, lot, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-order-sql.c b/src/backend/sql/gnc-order-sql.c index 46b0ae0222c..a7bc177c132 100644 --- a/src/backend/sql/gnc-order-sql.c +++ b/src/backend/sql/gnc-order-sql.c @@ -220,7 +220,9 @@ load_order_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, order, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-owner-sql.c b/src/backend/sql/gnc-owner-sql.c index 4b89c548dd7..2e779f0e9fc 100644 --- a/src/backend/sql/gnc-owner-sql.c +++ b/src/backend/sql/gnc-owner-sql.c @@ -156,7 +156,9 @@ load_owner( const GncSqlBackend* be, GncSqlRow* row, if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, &owner, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-tax-table-sql.c b/src/backend/sql/gnc-tax-table-sql.c index 21466d4e10a..fbf5d372f0b 100644 --- a/src/backend/sql/gnc-tax-table-sql.c +++ b/src/backend/sql/gnc-tax-table-sql.c @@ -523,7 +523,9 @@ load_taxtable_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, taxtable, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/sql/gnc-transaction-sql.c b/src/backend/sql/gnc-transaction-sql.c index 0ea21895fac..46fc3d9790c 100644 --- a/src/backend/sql/gnc-transaction-sql.c +++ b/src/backend/sql/gnc-transaction-sql.c @@ -415,6 +415,7 @@ query_transactions( GncSqlBackend* be, GncSqlStatement* stmt ) "end-reconciled-balance", &pnew_end_r_bal, NULL ); + qof_instance_increase_editlevel (balns-acc); if ( !gnc_numeric_eq( *pnew_end_bal, balns->end_bal ) ) { adj = gnc_numeric_sub( balns->end_bal, *pnew_end_bal, @@ -422,6 +423,7 @@ query_transactions( GncSqlBackend* be, GncSqlStatement* stmt ) balns->start_bal = gnc_numeric_add( balns->start_bal, adj, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); g_object_set( balns->acc, "start-balance", &balns->start_bal, NULL ); + qof_instance_decrease_editlevel (balns-acc); } if ( !gnc_numeric_eq( *pnew_end_c_bal, balns->end_cleared_bal ) ) { @@ -439,6 +441,7 @@ query_transactions( GncSqlBackend* be, GncSqlStatement* stmt ) GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD ); g_object_set( balns->acc, "start-reconciled-balance", &balns->start_reconciled_bal, NULL ); } + qof_instance_decrease_editlevel (balns-acc); xaccAccountRecomputeBalance( balns->acc ); g_free( pnew_end_bal ); g_free( pnew_end_c_bal ); @@ -1461,7 +1464,9 @@ load_tx_guid( const GncSqlBackend* be, GncSqlRow* row, { if ( table_row->gobj_param_name != NULL ) { + qof_instance_increase_editlevel (pObject); g_object_set( pObject, table_row->gobj_param_name, tx, NULL ); + qof_instance_decrease_editlevel (pObject); } else { diff --git a/src/backend/xml/gnc-account-xml-v2.c b/src/backend/xml/gnc-account-xml-v2.c index 8137147047a..85d475ec5bd 100644 --- a/src/backend/xml/gnc-account-xml-v2.c +++ b/src/backend/xml/gnc-account-xml-v2.c @@ -71,6 +71,9 @@ const gchar *account_version_string = "2.0.0"; #define act_hidden_string "act:hidden" #define act_placeholder_string "act:placeholder" +/* EFFECTIVE FRIEND FUNCTION */ +extern KvpFrame *qof_instance_get_slots (const QofInstance *); + xmlNodePtr gnc_account_dom_tree_create(Account *act, gboolean exporting, @@ -134,7 +137,7 @@ gnc_account_dom_tree_create(Account *act, xmlAddChild(ret, text_to_dom_tree(act_description_string, str)); } - kf = xaccAccountGetSlots(act); + kf = qof_instance_get_slots (QOF_INSTANCE (act)); if (kf) { xmlNodePtr kvpnode = kvp_frame_to_dom_tree(act_slots_string, kf); @@ -370,7 +373,7 @@ account_slots_handler (xmlNodePtr node, gpointer act_pdata) struct account_pdata *pdata = act_pdata; return dom_tree_to_kvp_frame_given - (node, xaccAccountGetSlots (pdata->account)); + (node, qof_instance_get_slots (QOF_INSTANCE (pdata->account))); } static gboolean diff --git a/src/backend/xml/gnc-book-xml-v2.c b/src/backend/xml/gnc-book-xml-v2.c index aa7a14feacf..6fcae46dc25 100644 --- a/src/backend/xml/gnc-book-xml-v2.c +++ b/src/backend/xml/gnc-book-xml-v2.c @@ -109,10 +109,10 @@ gnc_book_dom_tree_create(QofBook *book) xmlAddChild(ret, guid_to_dom_tree(book_id_string, qof_book_get_guid(book))); - if (qof_book_get_slots(book)) + if (qof_instance_get_slots (QOF_INSTANCE (book))) { xmlNodePtr kvpnode = kvp_frame_to_dom_tree(book_slots_string, - qof_book_get_slots(book)); + qof_instance_get_slots (QOF_INSTANCE (book))); if (kvpnode) xmlAddChild(ret, kvpnode); } @@ -162,10 +162,10 @@ write_book_parts(FILE *out, QofBook *book) if (ferror(out) || fprintf(out, "\n") < 0) return FALSE; - if (qof_book_get_slots(book)) + if (qof_instance_get_slots (QOF_INSTANCE (book))) { xmlNodePtr kvpnode = kvp_frame_to_dom_tree(book_slots_string, - qof_book_get_slots(book)); + qof_instance_get_slots (QOF_INSTANCE (book))); if (kvpnode) { xmlElemDump(out, NULL, kvpnode); @@ -203,7 +203,7 @@ book_slots_handler (xmlNodePtr node, gpointer book_pdata) /* the below works only because the get is gaurenteed to return * a frame, even if its empty */ - success = dom_tree_to_kvp_frame_given (node, qof_book_get_slots (book)); + success = dom_tree_to_kvp_frame_given (node, qof_instance_get_slots (QOF_INSTANCE (book))); g_return_val_if_fail(success, FALSE); diff --git a/src/backend/xml/gnc-invoice-xml-v2.c b/src/backend/xml/gnc-invoice-xml-v2.c index 2a9c769a2ad..901a73323e0 100644 --- a/src/backend/xml/gnc-invoice-xml-v2.c +++ b/src/backend/xml/gnc-invoice-xml-v2.c @@ -72,6 +72,9 @@ const gchar *invoice_version_string = "2.0.0"; #define invoice_tochargeamt_string "invoice:charge-amt" #define invoice_slots_string "invoice:slots" +/* EFFECTIVE FRIEND FUNCTION */ +extern KvpFrame *qof_instance_get_slots (const QofInstance *); + static void maybe_add_string (xmlNodePtr ptr, const char *tag, const char *str) { @@ -410,7 +413,7 @@ invoice_slots_handler (xmlNodePtr node, gpointer invoice_pdata) struct invoice_pdata *pdata = invoice_pdata; return dom_tree_to_kvp_frame_given - (node, xaccAccountGetSlots (pdata->invoice)); + (node, qof_instance_get_slots (QOF_INSTANCE (pdata->invoice))); } static struct dom_tree_handler invoice_handlers_v2[] = diff --git a/src/backend/xml/gnc-lot-xml-v2.c b/src/backend/xml/gnc-lot-xml-v2.c index f24001a3509..503e9816d49 100644 --- a/src/backend/xml/gnc-lot-xml-v2.c +++ b/src/backend/xml/gnc-lot-xml-v2.c @@ -53,6 +53,8 @@ const gchar *lot_version_string = "2.0.0"; #define gnc_lot_string "gnc:lot" #define lot_id_string "lot:id" #define lot_slots_string "lot:slots" +/* EFFECTIVE FRIEND FUNCTION */ +extern KvpFrame *qof_instance_get_slots (const QofInstance *); xmlNodePtr gnc_lot_dom_tree_create(GNCLot *lot) @@ -66,7 +68,7 @@ gnc_lot_dom_tree_create(GNCLot *lot) xmlAddChild(ret, guid_to_dom_tree(lot_id_string, gnc_lot_get_guid(lot))); - kf = gnc_lot_get_slots (lot); + kf = qof_instance_get_slots (QOF_INSTANCE (lot)); if (kf) { xmlNodePtr kvpnode = kvp_frame_to_dom_tree(lot_slots_string, kf); @@ -112,7 +114,7 @@ lot_slots_handler (xmlNodePtr node, gpointer p) ENTER("(lot=%p)", pdata->lot); success = dom_tree_to_kvp_frame_given - (node, gnc_lot_get_slots (pdata->lot)); + (node, qof_instance_get_slots (QOF_INSTANCE (pdata->lot))); LEAVE(""); g_return_val_if_fail(success, FALSE); diff --git a/src/backend/xml/gnc-transaction-xml-v2.c b/src/backend/xml/gnc-transaction-xml-v2.c index 2ccd67aa00a..a2a35131565 100644 --- a/src/backend/xml/gnc-transaction-xml-v2.c +++ b/src/backend/xml/gnc-transaction-xml-v2.c @@ -49,6 +49,9 @@ const gchar *transaction_version_string = "2.0.0"; +/* EFFECTIVE FRIEND FUNCTION */ +extern KvpFrame *qof_instance_get_slots (const QofInstance *); + static void add_gnc_num(xmlNodePtr node, const gchar *tag, gnc_numeric num) { @@ -129,7 +132,7 @@ split_to_dom_tree(const gchar *tag, Split *spl) } { xmlNodePtr kvpnode = kvp_frame_to_dom_tree("split:slots", - xaccSplitGetSlots(spl)); + qof_instance_get_slots (QOF_INSTANCE (spl))); if (kvpnode) { xmlAddChild(ret, kvpnode); @@ -192,7 +195,7 @@ gnc_transaction_dom_tree_create(Transaction *trn) { xmlNodePtr kvpnode = kvp_frame_to_dom_tree("trn:slots", - xaccTransGetSlots(trn)); + qof_instance_get_slots (QOF_INSTANCE (trn))); if (kvpnode) { xmlAddChild(ret, kvpnode); @@ -368,7 +371,7 @@ spl_slots_handler(xmlNodePtr node, gpointer data) gboolean successful; successful = dom_tree_to_kvp_frame_given(node, - xaccSplitGetSlots (pdata->split)); + qof_instance_get_slots (QOF_INSTANCE (pdata->split))); g_return_val_if_fail(successful, FALSE); return TRUE; @@ -527,7 +530,7 @@ trn_slots_handler(xmlNodePtr node, gpointer trans_pdata) Transaction *trn = pdata->trans; gboolean successful; - successful = dom_tree_to_kvp_frame_given(node, xaccTransGetSlots(trn)); + successful = dom_tree_to_kvp_frame_given(node, qof_instance_get_slots (QOF_INSTANCE (trn))); g_return_val_if_fail(successful, FALSE); diff --git a/src/backend/xml/io-gncxml-v2.c b/src/backend/xml/io-gncxml-v2.c index 908bff1d189..43e9590c320 100644 --- a/src/backend/xml/io-gncxml-v2.c +++ b/src/backend/xml/io-gncxml-v2.c @@ -2182,7 +2182,7 @@ gnc_xml2_parse_with_subst (FileBackend *fbe, QofBook *book, GHashTable *subst) push_data, GNC_BOOK_XML2_FILE); if (success) - qof_book_kvp_changed(book); + qof_instance_set_dirty (QOF_INSTANCE (book)); return success; } diff --git a/src/backend/xml/test/test-xml-account.c b/src/backend/xml/test/test-xml-account.c index b6838e83bc6..8055489b026 100644 --- a/src/backend/xml/test/test-xml-account.c +++ b/src/backend/xml/test/test-xml-account.c @@ -144,7 +144,7 @@ node_and_account_equal(xmlNodePtr node, Account *act) { /* xaccAccountDeleteOldData (act); */ - if (!equals_node_val_vs_kvp_frame(mark, xaccAccountGetSlots(act))) + if (!equals_node_val_vs_kvp_frame(mark, qof_instance_get_slots(QOF_INSTANCE (act)))) { return g_strdup("slots differ"); } diff --git a/src/backend/xml/test/test-xml-transaction.c b/src/backend/xml/test/test-xml-transaction.c index c19439747c7..14edc799a64 100644 --- a/src/backend/xml/test/test-xml-transaction.c +++ b/src/backend/xml/test/test-xml-transaction.c @@ -306,7 +306,7 @@ node_and_transaction_equal(xmlNodePtr node, Transaction *trn) } else if (g_strcmp0((char*)mark->name, "trn:slots") == 0) { - if (!equals_node_val_vs_kvp_frame(mark, xaccTransGetSlots(trn))) + if (!equals_node_val_vs_kvp_frame(mark, qof_instance_get_slots (QOF_INSTANCE (trn)))) { return "slots differ"; } diff --git a/src/business/business-gnome/dialog-invoice.c b/src/business/business-gnome/dialog-invoice.c index 5acee72689f..c00a5e42221 100644 --- a/src/business/business-gnome/dialog-invoice.c +++ b/src/business/business-gnome/dialog-invoice.c @@ -90,8 +90,6 @@ #define GNC_PREF_ACCUM_SPLITS "accumulate-splits" #define GNC_PREF_DAYS_IN_ADVANCE "days-in-advance" -#define LAST_POSTED_TO_ACCT "last-posted-to-acct" - void gnc_invoice_window_ok_cb (GtkWidget *widget, gpointer data); void gnc_invoice_window_cancel_cb (GtkWidget *widget, gpointer data); void gnc_invoice_window_help_cb (GtkWidget *widget, gpointer data); @@ -681,7 +679,6 @@ gnc_dialog_post_invoice(InvoiceWindow *iw, char *message, GList * acct_types = NULL; GList * acct_commodities = NULL; QofInstance *owner_inst; - KvpFrame *kvpf; EntryList *entries, *entries_iter; invoice = iw_get_invoice (iw); @@ -726,12 +723,14 @@ gnc_dialog_post_invoice(InvoiceWindow *iw, char *message, /* Get the due date and posted account */ *ddue = *postdate; *memo = NULL; - - owner_inst = qofOwnerGetOwner (gncOwnerGetEndOwner (&(iw->owner))); - kvpf = qof_instance_get_slots (owner_inst); - *acc = xaccAccountLookup (kvp_frame_get_guid (kvpf, LAST_POSTED_TO_ACCT), - iw->book); - + { + GncGUID *guid = NULL; + owner_inst = qofOwnerGetOwner (gncOwnerGetEndOwner (&(iw->owner))); + qof_instance_get (owner_inst, + "invoice-last-posted-account", &guid, + NULL); + *acc = xaccAccountLookup (guid, iw->book); + } /* Get the default for the accumulate option */ *accumulate = gnc_prefs_get_bool(GNC_PREFS_GROUP_INVOICE, GNC_PREF_ACCUM_SPLITS); @@ -762,8 +761,6 @@ gnc_invoice_post(InvoiceWindow *iw, struct post_invoice_params *post_params) Timespec ddue, postdate; gboolean accumulate; QofInstance *owner_inst; - KvpFrame *kvpf; - KvpValue *kvp_val; const char *text; GHashTable *foreign_currs; GHashTableIter foreign_currs_iter; @@ -911,14 +908,18 @@ gnc_invoice_post(InvoiceWindow *iw, struct post_invoice_params *post_params) } - /* Save account as last used account in the kvp frame of the invoice owner */ + /* Save account as last used account in the owner's + * invoice-last-posted-account property. + */ owner_inst = qofOwnerGetOwner (gncOwnerGetEndOwner (&(iw->owner))); - kvpf = qof_instance_get_slots (owner_inst); - kvp_val = kvp_value_new_guid (qof_instance_get_guid (QOF_INSTANCE (acc)));; - qof_begin_edit (owner_inst); - kvp_frame_set_slot_nc (kvpf, LAST_POSTED_TO_ACCT, kvp_val); - qof_instance_set_dirty (owner_inst); - qof_commit_edit (owner_inst); + { + const GncGUID *guid = qof_instance_get_guid (QOF_INSTANCE (acc)); + qof_begin_edit (owner_inst); + qof_instance_set (owner_inst, + "invoice-last-posted-account", guid, + NULL); + qof_commit_edit (owner_inst); + } /* ... post it ... */ if (is_cust_doc) diff --git a/src/business/business-gnome/dialog-payment.c b/src/business/business-gnome/dialog-payment.c index b380f3c4597..151f678068f 100644 --- a/src/business/business-gnome/dialog-payment.c +++ b/src/business/business-gnome/dialog-payment.c @@ -452,23 +452,15 @@ gnc_payment_dialog_owner_changed (PaymentWindow *pw) { Account *last_acct = NULL; GncGUID *guid = NULL; - KvpValue* value; - KvpFrame* slots; GncOwner *owner = &pw->owner; /* If the owner changed, the initial invoice is no longer valid */ pw->invoice = NULL; /* Now handle the account tree */ - slots = gncOwnerGetSlots(owner); - if (slots) - { - value = kvp_frame_get_slot_path(slots, "payment", "last_acct", NULL); - if (value) - { - guid = kvp_value_get_guid(value); - } - } + qof_instance_get (QOF_INSTANCE (owner), + "payment-last-account", &guid, + NULL); /* refresh the post and acc available accounts, but cleanup first */ if (pw->acct_types) @@ -514,20 +506,17 @@ gnc_payment_dialog_post_to_changed (PaymentWindow *pw) static void gnc_payment_dialog_remember_account (PaymentWindow *pw, Account *acc) { - KvpValue* value; - KvpFrame* slots = gncOwnerGetSlots(&pw->owner); + GncOwner *owner = &pw->owner; + const GncGUID *guid; if (!acc) return; - if (!slots) return; - - value = kvp_value_new_guid(xaccAccountGetGUID(acc)); - if (!value) return; - xaccAccountBeginEdit (acc); - kvp_frame_set_slot_path(slots, value, "payment", "last_acct", NULL); - qof_instance_set_dirty (QOF_INSTANCE (acc)); - xaccAccountCommitEdit (acc); - kvp_value_delete(value); + guid = xaccAccountGetGUID(acc); + qof_begin_edit (QOF_INSTANCE (owner)); + qof_instance_set (QOF_INSTANCE (owner), + "payment-last-account", guid, + NULL); + qof_commit_edit (QOF_INSTANCE (owner)); } diff --git a/src/core-utils/gnc-features.c b/src/core-utils/gnc-features.c index 0336fe2de96..f4846f4e1a3 100644 --- a/src/core-utils/gnc-features.c +++ b/src/core-utils/gnc-features.c @@ -67,10 +67,12 @@ static void gnc_features_init () g_strdup (known_features[i].desc)); } -static void gnc_features_test_one(const gchar *key, KvpValue *value, gpointer data) +static void gnc_features_test_one(gpointer pkey, gpointer value, + gpointer data) { + const gchar *key = (const gchar*)pkey; + const gchar *feature_desc = (const gchar*)value; GList **unknown_features; - gchar *feature_desc; g_assert(data); unknown_features = (GList**) data; @@ -80,54 +82,47 @@ static void gnc_features_test_one(const gchar *key, KvpValue *value, gpointer da return; /* It is unknown, so add the description to the unknown features list: */ - feature_desc = kvp_value_get_string(value); g_assert(feature_desc); - *unknown_features = g_list_prepend(*unknown_features, feature_desc); + *unknown_features = g_list_prepend(*unknown_features, + (gpointer)feature_desc); } /* Check if the session requires features unknown to this version of GnuCash. * - * Returns a message to display if we found unknown features, NULL if we're okay. + * Returns a message to display if we found unknown features, NULL if + * we're okay. */ gchar *gnc_features_test_unknown (QofBook *book) { - KvpFrame *frame = qof_book_get_slots (book); - KvpValue *value; /* Setup the known_features hash table */ gnc_features_init(); - g_assert(frame); - value = kvp_frame_get_value(frame, "features"); + GList* features_list = NULL; + GHashTable *features_used = qof_book_get_features (book); - if (value) + /* Iterate over the members of this frame for unknown features */ + g_hash_table_foreach (features_used, &gnc_features_test_one, + &features_list); + if (features_list) { - GList* features_list = NULL; - frame = kvp_value_get_frame(value); - g_assert(frame); - - /* Iterate over the members of this frame for unknown features */ - kvp_frame_for_each_slot(frame, &gnc_features_test_one, &features_list); - if (features_list) - { - GList *i; - char* msg = g_strdup( - _("This Dataset contains features not supported by this " - "version of GnuCash. You must use a newer version of " - "GnuCash in order to support the following features:" + GList *i; + char* msg = g_strdup(_("This Dataset contains features not supported " + "by this version of GnuCash. You must use a " + "newer version of GnuCash in order to support " + "the following features:" )); - for (i = features_list; i; i = i->next) - { - char *tmp = g_strconcat(msg, "\n* ", i->data, NULL); - g_free (msg); - msg = tmp; - } + for (i = features_list; i; i = i->next) + { + char *tmp = g_strconcat(msg, "\n* ", i->data, NULL); + g_free (msg); + msg = tmp; + } - g_list_free(features_list); - return msg; - } + g_list_free(features_list); + return msg; } return NULL; @@ -135,9 +130,7 @@ gchar *gnc_features_test_unknown (QofBook *book) void gnc_features_set_used (QofBook *book, const gchar *feature) { - KvpFrame *frame; const gchar *description; - gchar *kvp_path; g_return_if_fail (book); g_return_if_fail (feature); @@ -152,10 +145,7 @@ void gnc_features_set_used (QofBook *book, const gchar *feature) return; } - frame = qof_book_get_slots (book); - kvp_path = g_strconcat ("/features/", feature, NULL); - kvp_frame_set_string (frame, kvp_path, description); - qof_book_kvp_changed (book); + qof_book_set_feature (book, feature, description); } diff --git a/src/engine/Account.c b/src/engine/Account.c index 4b3095343ba..9bc7ddaed26 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -38,12 +38,20 @@ #include "gnc-glib-utils.h" #include "gnc-lot.h" #include "gnc-pricedb.h" +#include "qofinstance-p.h" static QofLogModule log_module = GNC_MOD_ACCOUNT; /* The Canonical Account Separator. Pre-Initialized. */ static gchar account_separator[8] = "."; static gunichar account_uc_separator = ':'; +/* Predefined KVP paths */ +static const char *KEY_ASSOC_INCOME_ACCOUNT = "ofx/associated-income-account"; +#define AB_KEY "hbci" +#define AB_ACCOUNT_ID "account-id" +#define AB_ACCOUNT_UID "account-uid" +#define AB_BANK_CODE "bank-code" +#define AB_TRANS_RETRIEVAL "trans-retrieval" enum { @@ -53,37 +61,48 @@ enum enum { PROP_0, - PROP_NAME, - PROP_FULL_NAME, - PROP_CODE, - PROP_DESCRIPTION, - PROP_COLOR, - PROP_NOTES, - PROP_TYPE, - - PROP_COMMODITY, - PROP_COMMODITY_SCU, - PROP_NON_STD_SCU, - PROP_SORT_DIRTY, - PROP_BALANCE_DIRTY, - PROP_START_BALANCE, - PROP_START_CLEARED_BALANCE, - PROP_START_RECONCILED_BALANCE, - PROP_END_BALANCE, - PROP_END_CLEARED_BALANCE, - PROP_END_RECONCILED_BALANCE, - - PROP_POLICY, - PROP_MARK, - PROP_TAX_RELATED, - PROP_TAX_CODE, - PROP_TAX_SOURCE, - PROP_TAX_COPY_NUMBER, - - PROP_HIDDEN, - PROP_PLACEHOLDER, - PROP_FILTER, - PROP_SORT_ORDER, + PROP_NAME, /* Table */ + PROP_FULL_NAME, /* Constructed */ + PROP_CODE, /* Table */ + PROP_DESCRIPTION, /* Table */ + PROP_COLOR, /* KVP */ + PROP_NOTES, /* KVP */ + PROP_TYPE, /* Table */ + +// PROP_PARENT, /* Table, Not a property */ + PROP_COMMODITY, /* Table */ + PROP_COMMODITY_SCU, /* Table */ + PROP_NON_STD_SCU, /* Table */ + PROP_END_BALANCE, /* Constructed */ + PROP_END_CLEARED_BALANCE, /* Constructed */ + PROP_END_RECONCILED_BALANCE, /* Constructed */ + + PROP_TAX_RELATED, /* KVP */ + PROP_TAX_CODE, /* KVP */ + PROP_TAX_SOURCE, /* KVP */ + PROP_TAX_COPY_NUMBER, /* KVP */ + + PROP_HIDDEN, /* Table slot exists, but in KVP in memory & xml */ + PROP_PLACEHOLDER, /* Table slot exists, but in KVP in memory & xml */ + PROP_FILTER, /* KVP */ + PROP_SORT_ORDER, /* KVP */ + + PROP_LOT_NEXT_ID, /* KVP */ + PROP_ONLINE_ACCOUNT, /* KVP */ + PROP_OFX_INCOME_ACCOUNT, /* KVP */ + PROP_AB_ACCOUNT_ID, /* KVP */ + PROP_AB_ACCOUNT_UID, /* KVP */ + PROP_AB_BANK_CODE, /* KVP */ + PROP_AB_TRANS_RETRIEVAL, /* KVP */ + + PROP_RUNTIME_0, + PROP_POLICY, /* Cached Value */ + PROP_MARK, /* Runtime Value */ + PROP_SORT_DIRTY, /* Runtime Value */ + PROP_BALANCE_DIRTY, /* Runtime Value */ + PROP_START_BALANCE, /* Runtime Value */ + PROP_START_CLEARED_BALANCE, /* Runtime Value */ + PROP_START_RECONCILED_BALANCE, /* Runtime Value */ }; #define GET_PRIVATE(o) \ @@ -282,6 +301,8 @@ gnc_account_get_property (GObject *object, { Account *account; AccountPrivate *priv; + const gchar *key; + GValue *temp; g_return_if_fail(GNC_IS_ACCOUNT(object)); @@ -377,6 +398,36 @@ gnc_account_get_property (GObject *object, case PROP_SORT_ORDER: g_value_set_string(value, xaccAccountGetSortOrder(account)); break; + case PROP_LOT_NEXT_ID: + key = "lot-mgmt/next-id"; + /* Pre-set the value in case the frame is empty */ + g_value_set_int64 (value, 0); + qof_instance_get_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_ONLINE_ACCOUNT: + key = "online_id"; + qof_instance_get_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_OFX_INCOME_ACCOUNT: + key = KEY_ASSOC_INCOME_ACCOUNT; + qof_instance_get_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_ACCOUNT_ID: + key = AB_KEY "/" AB_ACCOUNT_ID; + qof_instance_get_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_ACCOUNT_UID: + key = AB_KEY "/" AB_ACCOUNT_UID; + qof_instance_get_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_BANK_CODE: + key = AB_KEY "/" AB_BANK_CODE; + qof_instance_get_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_TRANS_RETRIEVAL: + key = AB_KEY "/" AB_TRANS_RETRIEVAL; + qof_instance_get_kvp (QOF_INSTANCE (account), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -391,10 +442,13 @@ gnc_account_set_property (GObject *object, { Account *account; gnc_numeric *number; + const gchar *key = NULL; g_return_if_fail(GNC_IS_ACCOUNT(object)); account = GNC_ACCOUNT(object); + if (prop_id < PROP_RUNTIME_0) + g_assert (qof_instance_get_editlevel(account)); switch (prop_id) { @@ -476,6 +530,34 @@ gnc_account_set_property (GObject *object, case PROP_SORT_ORDER: xaccAccountSetSortOrder(account, g_value_get_string(value)); break; + case PROP_LOT_NEXT_ID: + key = "lot-mgmt/next-id"; + qof_instance_set_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_ONLINE_ACCOUNT: + key = "online_id"; + qof_instance_set_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_OFX_INCOME_ACCOUNT: + key = KEY_ASSOC_INCOME_ACCOUNT; + qof_instance_set_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_ACCOUNT_ID: + key = AB_KEY "/" AB_ACCOUNT_ID; + qof_instance_set_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_ACCOUNT_UID: + key = AB_KEY "/" AB_ACCOUNT_UID; + qof_instance_set_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_BANK_CODE: + key = AB_KEY "/" AB_BANK_CODE; + qof_instance_set_kvp (QOF_INSTANCE (account), key, value); + break; + case PROP_AB_TRANS_RETRIEVAL: + key = AB_KEY "/" AB_TRANS_RETRIEVAL; + qof_instance_set_kvp (QOF_INSTANCE (account), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -833,6 +915,77 @@ gnc_account_class_init (AccountClass *klass) "the sort order to be recalled.", NULL, G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_LOT_NEXT_ID, + g_param_spec_int64 ("lot-next-id", + "Lot Next ID", + "Tracks the next id to use in gnc_lot_make_default.", + (gint64)1, + G_MAXINT64, + (gint64)1, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_ONLINE_ACCOUNT, + g_param_spec_string ("online-id", + "Online Account ID", + "The online account which corresponds to this " + "account for OFX import", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_OFX_INCOME_ACCOUNT, + g_param_spec_boxed("ofx-income-account", + "Associated income account", + "Used by the OFX importer.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_AB_ACCOUNT_ID, + g_param_spec_string ("ab-account-id", + "AQBanking Account ID", + "The AqBanking account which corresponds to this " + "account for AQBanking import", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_AB_BANK_CODE, + g_param_spec_string ("ab-bank-code", + "AQBanking Bank Code", + "The online account which corresponds to this " + "account for AQBanking import", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_AB_ACCOUNT_UID, + g_param_spec_int64 ("ab-account-uid", + "AQBanking Account UID", + "Tracks the next id to use in gnc_lot_make_default.", + (gint64)1, + G_MAXINT64, + (gint64)1, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_AB_TRANS_RETRIEVAL, + g_param_spec_boxed("ab-trans-retrieval", + "AQBanking Last Transaction Retrieval", + "The time of the last transaction retrieval for this " + "account.", + GNC_TYPE_TIMESPEC, + G_PARAM_READWRITE)); + } static void @@ -4279,6 +4432,80 @@ xaccAccountSetLastNum (Account *acc, const char *num) xaccAccountCommitEdit (acc); } +static Account * +GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency) +{ + char * accname; + Account * acc; + + g_return_val_if_fail (root, NULL); + + /* build the account name */ + if (!currency) + { + PERR ("No currency specified!"); + return NULL; + } + + accname = g_strconcat (_("Orphaned Gains"), "-", + gnc_commodity_get_mnemonic (currency), NULL); + + /* See if we've got one of these going already ... */ + acc = gnc_account_lookup_by_name(root, accname); + + if (acc == NULL) + { + /* Guess not. We'll have to build one. */ + acc = xaccMallocAccount (gnc_account_get_book(root)); + xaccAccountBeginEdit (acc); + xaccAccountSetName (acc, accname); + xaccAccountSetCommodity (acc, currency); + xaccAccountSetType (acc, ACCT_TYPE_INCOME); + xaccAccountSetDescription (acc, _("Realized Gain/Loss")); + xaccAccountSetNotes (acc, + _("Realized Gains or Losses from " + "Commodity or Trading Accounts " + "that haven't been recorded elsewhere.")); + + /* Hang the account off the root. */ + gnc_account_append_child (root, acc); + xaccAccountCommitEdit (acc); + } + + g_free (accname); + + return acc; +} + +Account * +xaccAccountGainsAccount (Account *acc, gnc_commodity *curr) +{ + KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (acc)); + const gchar *curr_name = gnc_commodity_get_unique_name (curr); + GncGUID *guid; + Account *gains_account; + + frame = kvp_frame_get_frame_slash (frame, "/lot-mgmt/gains-act/"); + guid = kvp_frame_get_guid (frame, curr_name); + if (guid == NULL) /* No gains account for this currency */ + { + gains_account = GetOrMakeOrphanAccount (gnc_account_get_root (acc), + curr); + guid = (GncGUID*)qof_instance_get_guid (QOF_INSTANCE (gains_account)); + xaccAccountBeginEdit (acc); + { + kvp_frame_set_guid (frame, curr_name, guid); + qof_instance_set_dirty (QOF_INSTANCE (acc)); + } + xaccAccountCommitEdit (acc); + } + else + gains_account = xaccAccountLookup (guid, + qof_instance_get_book(acc)); + + return gains_account; +} + /********************************************************************\ \********************************************************************/ @@ -4718,6 +4945,508 @@ xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc, return xaccAccountStagedTransactionTraversal(acc, 42, proc, data); } +/* ================================================================ */ +/* The following functions are used by + * src/import-export/import-backend.c to manipulate the contra-account + * matching data. See src/import-export/import-backend.c for explanations. + */ +/* FIXME: These data are stored per-account in KVP and the functions + * work directly on KVP data structures. This prevents moving KVP to a + * backend-only abstraction. + */ + + +typedef struct _GncImportMatchMap +{ + kvp_frame * frame; + Account * acc; + QofBook * book; +} GncImportMatchMap; + +#define IMAP_FRAME "import-map" +#define IMAP_FRAME_BAYES "import-map-bayes" +GncImportMatchMap * gnc_account_create_imap (Account *acc); +Account* gnc_imap_find_account(GncImportMatchMap *imap, const char* category, + const char *key); +void gnc_imap_add_account (GncImportMatchMap *imap, const char *category, + const char *key, Account *acc); +Account* gnc_imap_find_account_bayes (GncImportMatchMap *imap, GList* tokens); +void gnc_imap_add_account_bayes (GncImportMatchMap *imap, GList* tokens, + Account *acc); + +/* Obtain an ImportMatchMap object from an Account or a Book */ +GncImportMatchMap * +gnc_account_create_imap (Account *acc) +{ + GncImportMatchMap *imap; + kvp_frame *frame; + + if (!acc) return NULL; + frame = qof_instance_get_slots (QOF_INSTANCE (acc)); + g_return_val_if_fail (frame != NULL, NULL); + g_return_val_if_fail (frame != NULL, NULL); + + imap = g_new0(GncImportMatchMap, 1); + imap->frame = frame; + + /* Cache the book for easy lookups; store the account/book for + * marking dirtiness + */ + imap->acc = acc; + imap->book = gnc_account_get_book (acc); + + return imap; +} + +/* Look up an Account in the map */ +Account* +gnc_imap_find_account (GncImportMatchMap *imap, + const char *category, + const char *key) +{ + kvp_value *value; + GncGUID * guid; + + if (!imap || !key) return NULL; + if (!category) + { + category = key; + key = NULL; + } + + value = kvp_frame_get_slot_path (imap->frame, IMAP_FRAME, + category, key, NULL); + if (!value) return NULL; + + guid = kvp_value_get_guid (value); + return xaccAccountLookup (guid, imap->book); +} + +/* Store an Account in the map */ +void +gnc_imap_add_account (GncImportMatchMap *imap, + const char *category, + const char *key, + Account *acc) +{ + kvp_value *value; + + if (!imap || !key || !acc || (strlen (key) == 0)) return; + if (!category) + { + category = key; + key = NULL; + } + g_return_if_fail (acc != NULL); + + value = kvp_value_new_guid (xaccAccountGetGUID (acc)); + g_return_if_fail (value != NULL); + xaccAccountBeginEdit (imap->acc); + kvp_frame_set_slot_path (imap->frame, value, IMAP_FRAME, category, key, NULL); + qof_instance_set_dirty (QOF_INSTANCE (imap->acc)); + xaccAccountCommitEdit (imap->acc); + kvp_value_delete (value); + + /* XXX Mark the account (or book) as dirty! */ +} + + + + +/*-------------------------------------------------------------------------- + Below here is the bayes transaction to account matching system +--------------------------------------------------------------------------*/ + + +struct account_token_count +{ + char* account_name; + gint64 token_count; /**< occurances of a given token for this account_name */ +}; + +/** total_count and the token_count for a given account let us calculate the + * probability of a given account with any single token + */ +struct token_accounts_info +{ + GList *accounts; /**< array of struct account_token_count */ + gint64 total_count; +}; + +/** gpointer is a pointer to a struct token_accounts_info + * \note Can always assume that keys are unique, reduces code in this function + */ +static void +buildTokenInfo(const char *key, kvp_value *value, gpointer data) +{ + struct token_accounts_info *tokenInfo = (struct token_accounts_info*)data; + struct account_token_count* this_account; + + // PINFO("buildTokenInfo: account '%s', token_count: '%ld'\n", (char*)key, + // (long)kvp_value_get_gint64(value)); + + /* add the count to the total_count */ + tokenInfo->total_count += kvp_value_get_gint64(value); + + /* allocate a new structure for this account and it's token count */ + this_account = (struct account_token_count*) + g_new0(struct account_token_count, 1); + + /* fill in the account name and number of tokens found for this account name */ + this_account->account_name = (char*)key; + this_account->token_count = kvp_value_get_gint64(value); + + /* append onto the glist a pointer to the new account_token_count structure */ + tokenInfo->accounts = g_list_prepend(tokenInfo->accounts, this_account); +} + +/** intermediate values used to calculate the bayes probability of a given account + where p(AB) = (a*b)/[a*b + (1-a)(1-b)], product is (a*b), + product_difference is (1-a) * (1-b) + */ +struct account_probability +{ + double product; /* product of probabilities */ + double product_difference; /* product of (1-probabilities) */ +}; + +/** convert a hash table of account names and (struct account_probability*) + into a hash table of 100000x the percentage match value, ie. 10% would be + 0.10 * 100000 = 10000 + */ +#define PROBABILITY_FACTOR 100000 +static void +buildProbabilities(gpointer key, gpointer value, gpointer data) +{ + GHashTable *final_probabilities = (GHashTable*)data; + struct account_probability *account_p = (struct account_probability*)value; + + /* P(AB) = A*B / [A*B + (1-A)*(1-B)] + * NOTE: so we only keep track of a running product(A*B*C...) + * and product difference ((1-A)(1-B)...) + */ + gint32 probability = + (account_p->product / + (account_p->product + account_p->product_difference)) + * PROBABILITY_FACTOR; + + PINFO("P('%s') = '%d'\n", (char*)key, probability); + + g_hash_table_insert(final_probabilities, key, GINT_TO_POINTER(probability)); +} + +/** Frees an array of the same time that buildProperties built */ +static void +freeProbabilities(gpointer key, gpointer value, gpointer data) +{ + /* free up the struct account_probability that was allocated + * in gnc_account_find_account_bayes() + */ + g_free(value); +} + +/** holds an account name and its corresponding integer probability + the integer probability is some factor of 10 + */ +struct account_info +{ + char* account_name; + gint32 probability; +}; + +/** Find the highest probability and the corresponding account name + store in data, a (struct account_info*) + NOTE: this is a g_hash_table_foreach() function for a hash table of entries + key is a pointer to the account name, value is a gint32, 100000x + the probability for this account +*/ +static void +highestProbability(gpointer key, gpointer value, gpointer data) +{ + struct account_info *account_i = (struct account_info*)data; + + /* if the current probability is greater than the stored, store the current */ + if (GPOINTER_TO_INT(value) > account_i->probability) + { + /* Save the new highest probability and the assoaciated account name */ + account_i->probability = GPOINTER_TO_INT(value); + account_i->account_name = key; + } +} + + +#define threshold (.90 * PROBABILITY_FACTOR) /* 90% */ + +/** Look up an Account in the map */ +Account* +gnc_imap_find_account_bayes (GncImportMatchMap *imap, GList *tokens) +{ + struct token_accounts_info tokenInfo; /**< holds the accounts and total + * token count for a single token */ + GList *current_token; /**< pointer to the current + * token from the input GList + * tokens */ + GList *current_account_token; /**< pointer to the struct + * account_token_count */ + struct account_token_count *account_c; /**< an account name and the number + * of times a token has appeared + * for the account */ + struct account_probability *account_p; /**< intermediate storage of values + * to compute the bayes probability + * of an account */ + GHashTable *running_probabilities = g_hash_table_new(g_str_hash, + g_str_equal); + GHashTable *final_probabilities = g_hash_table_new(g_str_hash, + g_str_equal); + struct account_info account_i; + kvp_value* value; + kvp_frame* token_frame; + + ENTER(" "); + + /* check to see if the imap is NULL */ + if (!imap) + { + PINFO("imap is null, returning null"); + LEAVE(" "); + return NULL; + } + + /* find the probability for each account that contains any of the tokens + * in the input tokens list + */ + for (current_token = tokens; current_token; + current_token = current_token->next) + { + /* zero out the token_accounts_info structure */ + memset(&tokenInfo, 0, sizeof(struct token_accounts_info)); + + PINFO("token: '%s'", (char*)current_token->data); + + /* find the slot for the given token off of the source account + * for these tokens, search off of the IMAP_FRAME_BAYES path so + * we aren't looking from the parent of the entire kvp tree + */ + value = kvp_frame_get_slot_path(imap->frame, IMAP_FRAME_BAYES, + (char*)current_token->data, NULL); + + /* if value is null we should skip over this token */ + if (!value) + continue; + + /* convert the slot(value) into a the frame that contains the + * list of accounts + */ + token_frame = kvp_value_get_frame(value); + + /* token_frame should NEVER be null */ + if (!token_frame) + { + PERR("token '%s' has no accounts", (char*)current_token->data); + continue; /* skip over this token */ + } + + /* process the accounts for this token, adding the account if it + * doesn't already exist or adding to the existing accounts token + * count if it does + */ + kvp_frame_for_each_slot(token_frame, buildTokenInfo, &tokenInfo); + + /* for each account we have just found, see if the account + * already exists in the list of account probabilities, if not + * add it + */ + for (current_account_token = tokenInfo.accounts; current_account_token; + current_account_token = current_account_token->next) + { + /* get the account name and corresponding token count */ + account_c = (struct account_token_count*)current_account_token->data; + + PINFO("account_c->account_name('%s'), " + "account_c->token_count('%ld')/total_count('%ld')", + account_c->account_name, (long)account_c->token_count, + (long)tokenInfo.total_count); + + account_p = g_hash_table_lookup(running_probabilities, + account_c->account_name); + + /* if the account exists in the list then continue + * the running probablities + */ + if (account_p) + { + account_p->product = (((double)account_c->token_count / + (double)tokenInfo.total_count) + * account_p->product); + account_p->product_difference = + ((double)1 - ((double)account_c->token_count / + (double)tokenInfo.total_count)) + * account_p->product_difference; + PINFO("product == %f, product_difference == %f", + account_p->product, account_p->product_difference); + } + else + { + /* add a new entry */ + PINFO("adding a new entry for this account"); + account_p = (struct account_probability*) + g_new0(struct account_probability, 1); + + /* set the product and product difference values */ + account_p->product = ((double)account_c->token_count / + (double)tokenInfo.total_count); + account_p->product_difference = + (double)1 - ((double)account_c->token_count / + (double)tokenInfo.total_count); + + PINFO("product == %f, product_difference == %f", + account_p->product, account_p->product_difference); + + /* add the account name and (struct account_probability*) + * to the hash table */ + g_hash_table_insert(running_probabilities, + account_c->account_name, account_p); + } + } /* for all accounts in tokenInfo */ + + /* free the data in tokenInfo */ + for (current_account_token = tokenInfo.accounts; current_account_token; + current_account_token = current_account_token->next) + { + /* free up each struct account_token_count we allocated */ + g_free((struct account_token_count*)current_account_token->data); + } + + g_list_free(tokenInfo.accounts); /* free the accounts GList */ + } + + /* build a hash table of account names and their final probabilities + * from each entry in the running_probabilties hash table + */ + g_hash_table_foreach(running_probabilities, buildProbabilities, + final_probabilities); + + /* find the highest probabilty and the corresponding account */ + memset(&account_i, 0, sizeof(struct account_info)); + g_hash_table_foreach(final_probabilities, highestProbability, &account_i); + + /* free each element of the running_probabilities hash */ + g_hash_table_foreach(running_probabilities, freeProbabilities, NULL); + + /* free the hash tables */ + g_hash_table_destroy(running_probabilities); + g_hash_table_destroy(final_probabilities); + + PINFO("highest P('%s') = '%d'", + account_i.account_name ? account_i.account_name : "(null)", + account_i.probability); + + /* has this probability met our threshold? */ + if (account_i.probability >= threshold) + { + PINFO("found match"); + LEAVE(" "); + return gnc_account_lookup_by_full_name(gnc_book_get_root_account(imap->book), + account_i.account_name); + } + + PINFO("no match"); + LEAVE(" "); + + return NULL; /* we didn't meet our threshold, return NULL for an account */ +} + + +/** Updates the imap for a given account using a list of tokens */ +void +gnc_imap_add_account_bayes(GncImportMatchMap *imap, + GList *tokens, + Account *acc) +{ + GList *current_token; + kvp_value *value; + gint64 token_count; + char* account_fullname; + kvp_value *new_value; /* the value that will be added back into + * the kvp tree */ + + ENTER(" "); + + /* if imap is null return */ + if (!imap) + { + LEAVE(" "); + return; + } + + g_return_if_fail (acc != NULL); + account_fullname = gnc_account_get_full_name(acc); + xaccAccountBeginEdit (imap->acc); + + PINFO("account name: '%s'\n", account_fullname); + + /* process each token in the list */ + for (current_token = g_list_first(tokens); current_token; + current_token = current_token->next) + { + /* Jump to next iteration if the pointer is not valid or if the + string is empty. In HBCI import we almost always get an empty + string, which doesn't work in the kvp loopkup later. So we + skip this case here. */ + if (!current_token->data || (*((char*)current_token->data) == '\0')) + continue; + + /* start off with no tokens for this account */ + token_count = 0; + + PINFO("adding token '%s'\n", (char*)current_token->data); + + /* is this token/account_name already in the kvp tree? */ + value = kvp_frame_get_slot_path(imap->frame, IMAP_FRAME_BAYES, + (char*)current_token->data, + account_fullname, + NULL); + + /* if the token/account is already in the tree, read the current + * value from the tree and use this for the basis of the value we + * are putting back + */ + if (value) + { + PINFO("found existing value of '%ld'\n", + (long)kvp_value_get_gint64(value)); + + /* convert this value back into an integer */ + token_count += kvp_value_get_gint64(value); + } + + /* increment the token count */ + token_count++; + + /* create a new value */ + new_value = kvp_value_new_gint64(token_count); + + /* insert the value into the kvp tree at + * /imap->frame/IMAP_FRAME/token_string/account_name_string + */ + kvp_frame_set_slot_path(imap->frame, new_value, + IMAP_FRAME_BAYES, + (char*)current_token->data, + account_fullname, + NULL); + /* kvp_frame_set_slot_path() copied the value so we + * need to delete this one ;-) */ + kvp_value_delete(new_value); + } + + /* free up the account fullname string */ + qof_instance_set_dirty (QOF_INSTANCE (imap->acc)); + xaccAccountCommitEdit (imap->acc); + g_free(account_fullname); + + LEAVE(" "); +} + /* ================================================================ */ /* QofObject function implementation and registration */ diff --git a/src/engine/Account.h b/src/engine/Account.h index 30980439e77..0e57ac78054 100644 --- a/src/engine/Account.h +++ b/src/engine/Account.h @@ -417,6 +417,17 @@ void xaccAccountSortSplits (Account *acc, gboolean force); */ gchar * gnc_account_get_full_name (const Account *account); +/** Retrieve the gains account used by this account for the indicated + * currency, creating and recording a new one if necessary. + * + * FIXME: There is at present no interface to designate an existing + * account, and the new account name is hard coded to + * "Orphaned Gains -- CUR" + * + * FIXME: There is no provision for creating separate accounts for + * anything other than currency, e.g. holding period of a security. + */ +Account * xaccAccountGainsAccount (Account *acc, gnc_commodity *curr); /** Set a string that identifies the Finance::Quote backend that * should be used to retrieve online prices. See price-quotes.scm * for more information @@ -860,8 +871,6 @@ gboolean xaccAccountGetReconcileChildrenStatus(const Account *account); */ gboolean xaccAccountHasAncestor(const Account *acc, const Account *ancestor); -#define xaccAccountGetSlots(X) qof_instance_get_slots(QOF_INSTANCE(X)) - /** @} */ /** @name Lookup Accounts and Subaccounts by name or code diff --git a/src/engine/SX-book.c b/src/engine/SX-book.c index 57ee6182821..7440442dce6 100644 --- a/src/engine/SX-book.c +++ b/src/engine/SX-book.c @@ -150,6 +150,9 @@ sxtg_is_dirty(const QofCollection *col) return dirty; } +/* EFFECTIVE FRIEND FUNCTION declared in qofinstance-p.h */ +extern void qof_instance_mark_clean (QofInstance *); + static void sxtg_mark_clean(QofCollection *col) { @@ -372,9 +375,9 @@ gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct) for (; splits != NULL; splits = splits->next) { Split *s = (Split*)splits->data; - KvpFrame *frame = kvp_frame_get_frame(xaccSplitGetSlots(s), GNC_SX_ID); - GncGUID *sx_split_acct_guid = kvp_frame_get_guid(frame, GNC_SX_ACCOUNT); - if (guid_equal(acct_guid, sx_split_acct_guid)) + GncGUID *guid = NULL; + qof_instance_get (QOF_INSTANCE (s), "sx-account", &guid, NULL); + if (guid_equal(acct_guid, guid)) { rtn = g_list_append(rtn, sx); } diff --git a/src/engine/SchedXaction.c b/src/engine/SchedXaction.c index ceb30b0b7c8..c794bac407e 100644 --- a/src/engine/SchedXaction.c +++ b/src/engine/SchedXaction.c @@ -36,6 +36,7 @@ #include "Transaction.h" #include "gnc-engine.h" #include "engine-helpers.h" +#include "qofinstance-p.h" #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "gnc.engine.sx" @@ -43,19 +44,19 @@ enum { PROP_0, - PROP_NAME, - PROP_ENABLED, - PROP_NUM_OCCURANCE, - PROP_REM_OCCURANCE, - PROP_AUTO_CREATE, - PROP_AUTO_CREATE_NOTIFY, - PROP_ADVANCE_CREATION_DAYS, - PROP_ADVANCE_REMINDER_DAYS, - PROP_START_DATE, - PROP_END_DATE, - PROP_LAST_OCCURANCE_DATE, - PROP_INSTANCE_COUNT, - PROP_TEMPLATE_ACCOUNT + PROP_NAME, /* Table */ + PROP_ENABLED, /* Table */ + PROP_START_DATE, /* Table */ + PROP_END_DATE, /* Table */ + PROP_LAST_OCCURANCE_DATE, /* Table */ + PROP_NUM_OCCURANCE, /* Table */ + PROP_REM_OCCURANCE, /* Table */ + PROP_AUTO_CREATE, /* Table */ + PROP_AUTO_CREATE_NOTIFY, /* Table */ + PROP_ADVANCE_CREATION_DAYS, /* Table */ + PROP_ADVANCE_REMINDER_DAYS, /* Table */ + PROP_INSTANCE_COUNT, /* Table */ + PROP_TEMPLATE_ACCOUNT /* Table */ }; /* GObject initialization */ @@ -173,6 +174,8 @@ gnc_schedxaction_set_property (GObject *object, g_return_if_fail(GNC_IS_SCHEDXACTION(object)); sx = GNC_SCHEDXACTION(object); + g_assert (qof_instance_get_editlevel(sx)); + switch (prop_id) { case PROP_NAME: @@ -1042,8 +1045,8 @@ pack_split_info (TTSplitInfo *s_info, Account *parent_acct, Transaction *parent_trans, QofBook *book) { Split *split; - KvpFrame *split_frame; - KvpValue *tmp_value; + const gchar *credit_formula; + const gchar *debit_formula; const GncGUID *acc_guid; split = xaccMallocSplit(book); @@ -1059,40 +1062,14 @@ pack_split_info (TTSplitInfo *s_info, Account *parent_acct, xaccAccountInsertSplit(parent_acct, split); - split_frame = xaccSplitGetSlots(split); - - tmp_value - = kvp_value_new_string(gnc_ttsplitinfo_get_credit_formula(s_info)); - - kvp_frame_set_slot_path(split_frame, - tmp_value, - GNC_SX_ID, - GNC_SX_CREDIT_FORMULA, - NULL); - kvp_value_delete(tmp_value); - - tmp_value - = kvp_value_new_string(gnc_ttsplitinfo_get_debit_formula(s_info)); - - kvp_frame_set_slot_path(split_frame, - tmp_value, - GNC_SX_ID, - GNC_SX_DEBIT_FORMULA, - NULL); - - kvp_value_delete(tmp_value); - + credit_formula = gnc_ttsplitinfo_get_credit_formula(s_info); + debit_formula = gnc_ttsplitinfo_get_debit_formula(s_info); acc_guid = qof_entity_get_guid(QOF_INSTANCE(gnc_ttsplitinfo_get_account(s_info))); - - tmp_value = kvp_value_new_guid(acc_guid); - - kvp_frame_set_slot_path(split_frame, - tmp_value, - GNC_SX_ID, - GNC_SX_ACCOUNT, - NULL); - - kvp_value_delete(tmp_value); + qof_instance_set (QOF_INSTANCE (split), + "sx-credit-formula", credit_formula, + "sx-debit-formula", debit_formula, + "sx-account", acc_guid, + NULL); return split; } diff --git a/src/engine/SchedXaction.h b/src/engine/SchedXaction.h index 08031e188de..c02183eaf5a 100644 --- a/src/engine/SchedXaction.h +++ b/src/engine/SchedXaction.h @@ -304,15 +304,7 @@ void gnc_sx_remove_defer_instance( SchedXaction *sx, void *deferStateData ); GList *gnc_sx_get_defer_instances( SchedXaction *sx ); /* #defines for KvpFrame strings and QOF */ -#define GNC_SX_ID "sched-xaction" -#define GNC_SX_ACCOUNT "account" -#define GNC_SX_CREDIT_FORMULA "credit-formula" -#define GNC_SX_DEBIT_FORMULA "debit-formula" -#define GNC_SX_CREDIT_NUMERIC "credit-numeric" -#define GNC_SX_DEBIT_NUMERIC "debit-numeric" #define GNC_SX_SHARES "shares" -#define GNC_SX_AMOUNT "amnt" -#define GNC_SX_FROM_SCHED_XACTION "from-sched-xaction" #define GNC_SX_FREQ_SPEC "scheduled-frequency" #define GNC_SX_NAME "sched-xname" #define GNC_SX_START_DATE "sched-start-date" diff --git a/src/engine/Scrub.c b/src/engine/Scrub.c index 055e95a89c6..70d1e6c4f7d 100644 --- a/src/engine/Scrub.c +++ b/src/engine/Scrub.c @@ -1133,6 +1133,9 @@ xaccAccountScrubCommodity (Account *account) /* ================================================================ */ +/* EFFECTIVE FRIEND FUNCTION declared in qofinstance-p.h */ +extern void qof_instance_set_dirty (QofInstance*); + static void xaccAccountDeleteOldData (Account *account) { diff --git a/src/engine/Scrub2.c b/src/engine/Scrub2.c index 8c041f57dd2..d938cae518a 100644 --- a/src/engine/Scrub2.c +++ b/src/engine/Scrub2.c @@ -157,7 +157,7 @@ xaccLotScrubDoubleBalance (GNCLot *lot) if (!lot) return; - ENTER ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title")); + ENTER ("lot=%s", gnc_lot_get_title(lot)); for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next) { @@ -216,7 +216,7 @@ xaccLotScrubDoubleBalance (GNCLot *lot) } } - LEAVE ("lot=%s", kvp_frame_get_string (gnc_lot_get_slots (lot), "/title")); + LEAVE ("lot=%s", gnc_lot_get_title(lot)); } /* ================================================================= */ diff --git a/src/engine/Split.c b/src/engine/Split.c index b49389f6017..53fa590474f 100644 --- a/src/engine/Split.c +++ b/src/engine/Split.c @@ -51,6 +51,7 @@ #include "gnc-engine.h" #include "gnc-lot.h" #include "gnc-event.h" +#include "qofinstance-p.h" const char *void_former_amt_str = "void-former-amount"; const char *void_former_val_str = "void-former-value"; @@ -60,17 +61,40 @@ const char *void_former_val_str = "void-former-value"; /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = GNC_MOD_ENGINE; +/* KVP key values used for SX info stored Split's slots. */ +#define GNC_SX_ID "sched-xaction" +#define GNC_SX_ACCOUNT "account" +#define GNC_SX_CREDIT_FORMULA "credit-formula" +#define GNC_SX_DEBIT_FORMULA "debit-formula" +#define GNC_SX_CREDIT_NUMERIC "credit-numeric" +#define GNC_SX_DEBIT_NUMERIC "debit-numeric" +#define GNC_SX_SHARES "shares" + enum { PROP_0, - PROP_ACTION, - PROP_MEMO, - PROP_VALUE, - PROP_AMOUNT, - PROP_RECONCILE_DATE, - PROP_TX, - PROP_ACCOUNT, - PROP_LOT + PROP_TX, /* Table */ + PROP_ACCOUNT, /* Table */ + PROP_MEMO, /* Table */ + PROP_ACTION, /* Table */ +// PROP_RECONCILE_STATE, /* Table */ + PROP_RECONCILE_DATE, /* Table */ + PROP_VALUE, /* Table, in 2 fields */ + PROP_SX_ACCOUNT, /* KVP */ + PROP_SX_CREDIT_FORMULA, /* KVP */ + PROP_SX_CREDIT_NUMERIC, /* KVP */ + PROP_SX_DEBIT_FORMULA, /* KVP */ + PROP_SX_DEBIT_NUMERIC, /* KVP */ + PROP_SX_SHARES, /* KVP */ + PROP_LOT, /* KVP */ + PROP_ONLINE_ACCOUNT, /* KVP */ + PROP_LOT_SPLIT, /* KVP */ + PROP_PEER_GUID, /* KVP */ + PROP_GAINS_SPLIT, /* KVP */ + PROP_GAINS_SOURCE, /* KVP */ + PROP_RUNTIME_0, + PROP_AMOUNT, /* Runtime */ + }; /* GObject Initialization */ @@ -126,6 +150,7 @@ gnc_split_get_property(GObject *object, GParamSpec *pspec) { Split *split; + gchar *key; g_return_if_fail(GNC_IS_SPLIT(object)); @@ -156,6 +181,50 @@ gnc_split_get_property(GObject *object, case PROP_LOT: g_value_take_object(value, split->lot); break; + case PROP_SX_CREDIT_FORMULA: + key = GNC_SX_ID "/" GNC_SX_CREDIT_FORMULA; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_CREDIT_NUMERIC: + key = GNC_SX_ID "/" GNC_SX_CREDIT_NUMERIC; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_DEBIT_FORMULA: + key = GNC_SX_ID "/" GNC_SX_DEBIT_FORMULA; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_DEBIT_NUMERIC: + key = GNC_SX_ID "/" GNC_SX_DEBIT_NUMERIC; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_ACCOUNT: + key = GNC_SX_ID "/" GNC_SX_ACCOUNT; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_SHARES: + key = GNC_SX_ID "/" GNC_SX_SHARES; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_ONLINE_ACCOUNT: + key = "online_id"; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_LOT_SPLIT: + key = "lot-split"; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_PEER_GUID: + key = "peer_guid"; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_GAINS_SPLIT: + key = "gains-split"; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_GAINS_SOURCE: + key = "gains-source"; + qof_instance_get_kvp (QOF_INSTANCE (split), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -170,10 +239,14 @@ gnc_split_set_property(GObject *object, { Split *split; gnc_numeric* number; + gchar *key; g_return_if_fail(GNC_IS_SPLIT(object)); split = GNC_SPLIT(object); + if (prop_id < PROP_RUNTIME_0 && split->parent != NULL) + g_assert (qof_instance_get_editlevel(split->parent)); + switch (prop_id) { case PROP_ACTION: @@ -202,6 +275,50 @@ gnc_split_set_property(GObject *object, case PROP_LOT: xaccSplitSetLot(split, g_value_get_object(value)); break; + case PROP_SX_CREDIT_FORMULA: + key = GNC_SX_ID "/" GNC_SX_CREDIT_FORMULA; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_CREDIT_NUMERIC: + key = GNC_SX_ID "/" GNC_SX_CREDIT_NUMERIC; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_DEBIT_FORMULA: + key = GNC_SX_ID "/" GNC_SX_DEBIT_FORMULA; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_DEBIT_NUMERIC: + key = GNC_SX_ID "/" GNC_SX_DEBIT_NUMERIC; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_ACCOUNT: + key = GNC_SX_ID "/" GNC_SX_ACCOUNT; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_SX_SHARES: + key = GNC_SX_ID "/" GNC_SX_SHARES; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_ONLINE_ACCOUNT: + key = "online_id"; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_LOT_SPLIT: + key = "lot-split"; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_PEER_GUID: + key = "peer_guid"; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_GAINS_SPLIT: + key = "gains-split"; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; + case PROP_GAINS_SOURCE: + key = "gains-source"; + qof_instance_set_kvp (QOF_INSTANCE (split), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -299,6 +416,122 @@ gnc_split_class_init(SplitClass* klass) "The lot that this split belongs to.", GNC_TYPE_LOT, G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_SX_DEBIT_FORMULA, + g_param_spec_string("sx-debit-formula", + "Schedule Transaction Debit Formula", + "The formula used to calculate the actual debit " + "amount when a real split is generated from this " + "SX split.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_SX_DEBIT_NUMERIC, + g_param_spec_boxed("sx-debit-numeric", + "Scheduled Transaction Debit Numeric", + "Numeric value to plug into the Debit Formula when a " + "real split is generated from this SX split.", + GNC_TYPE_NUMERIC, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_SX_CREDIT_FORMULA, + g_param_spec_string("sx-credit-formula", + "Schedule Transaction Credit Formula", + "The formula used to calculate the actual credit " + "amount when a real split is generated from this " + "SX split.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_SX_CREDIT_NUMERIC, + g_param_spec_boxed("sx-credit-numeric", + "Scheduled Transaction Credit Numeric", + "Numeric value to plug into the Credit Formula when a " + "real split is generated from this SX split.", + GNC_TYPE_NUMERIC, + G_PARAM_READWRITE)); +/* FIXME: PROP_SX_SHARES should be stored as a gnc_numeric, but the function + * which uses it, gnc_template_register_save_shares_cell, stores a + * phony string. This is maintained until backwards compatibility can + * be established. + */ + g_object_class_install_property + (gobject_class, + PROP_SX_SHARES, + g_param_spec_string("sx-shares", + "Scheduled Transaction Shares", + "Numeric value of shares to insert in a new split when " + "it's generated from this SX split.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_SX_ACCOUNT, + g_param_spec_boxed("sx-account", + "Scheduled Transaction Account", + "The target account for a scheduled transaction split.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_ONLINE_ACCOUNT, + g_param_spec_string ("online-id", + "Online Account ID", + "The online account which corresponds to this " + "account for OFX/HCBI import", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_LOT_SPLIT, + g_param_spec_int64 ("lot-split", + "Lot Split", + "Indicates that the split was divided into two " + "splits in order to balance a lot capital gains " + "transaction. Contains a timestamp of the action.", + G_MININT64, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_PEER_GUID, + g_param_spec_boxed ("peer-guid", + "Peer GUID", + "The other split in the division.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_GAINS_SPLIT, + g_param_spec_boxed ("gains-split", + "Gains Split", + "The capital gains split associated with this " + "split when this split represents the proceeds " + "from the sale of a commodity inside a Lot.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_GAINS_SOURCE, + g_param_spec_boxed ("gains-source", + "Gains Source", + "The source split for which this split this is " + "the gains split. ", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); } /********************************************************************\ @@ -406,7 +639,7 @@ xaccDupeSplit (const Split *s) } Split * -xaccSplitClone (const Split *s) +xaccSplitCloneNoKvp (const Split *s) { Split *split = g_object_new (GNC_TYPE_SPLIT, NULL); @@ -424,10 +657,8 @@ xaccSplitClone (const Split *s) split->gains = GAINS_STATUS_UNKNOWN; split->gains_split = NULL; - qof_instance_init_data(&split->inst, GNC_ID_SPLIT, qof_instance_get_book(s)); - kvp_frame_delete(split->inst.kvp_data); - split->inst.kvp_data = kvp_frame_copy(s->inst.kvp_data); - + qof_instance_init_data(&split->inst, GNC_ID_SPLIT, + qof_instance_get_book(s)); xaccAccountInsertSplit(s->acc, split); if (s->lot) { @@ -437,6 +668,11 @@ xaccSplitClone (const Split *s) return split; } +void +xaccSplitCopyKvp (const Split *from, Split *to) +{ + to->inst.kvp_data = kvp_frame_copy(from->inst.kvp_data); +} /*################## Added for Reg2 #################*/ @@ -954,26 +1190,6 @@ get_commodity_denom(const Split * s) } } -/******************************************************************** - * xaccSplitGetSlots - ********************************************************************/ - -KvpFrame * -xaccSplitGetSlots (const Split * s) -{ - return qof_instance_get_slots(QOF_INSTANCE(s)); -} -/* Used for testing only: _get_random_split in test-engine-stuff.c */ -void -xaccSplitSetSlots_nc(Split *s, KvpFrame *frm) -{ - if (!s || !frm) return; - xaccTransBeginEdit(s->parent); - qof_instance_set_slots(QOF_INSTANCE(s), frm); - xaccTransCommitEdit(s->parent); - -} - /********************************************************************\ \********************************************************************/ @@ -2115,7 +2331,7 @@ gboolean xaccSplitRegister (void) { SPLIT_ACCT_FULLNAME, SPLIT_ACCT_FULLNAME, no_op, NULL }, { SPLIT_CORR_ACCT_NAME, SPLIT_CORR_ACCT_NAME, no_op, NULL }, { SPLIT_CORR_ACCT_CODE, SPLIT_CORR_ACCT_CODE, no_op, NULL }, - { SPLIT_KVP, QOF_TYPE_KVP, (QofAccessFunc)xaccSplitGetSlots, NULL }, + { SPLIT_KVP, QOF_TYPE_KVP, (QofAccessFunc)qof_instance_get_slots, NULL }, { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)xaccSplitGetBook, NULL }, { QOF_PARAM_GUID, QOF_TYPE_GUID, diff --git a/src/engine/Split.h b/src/engine/Split.h index d240bd83c18..cc512d15ab3 100644 --- a/src/engine/Split.h +++ b/src/engine/Split.h @@ -144,20 +144,6 @@ GNCLot * xaccSplitGetLot (const Split *split); /** Assigns the split to a specific Lot */ void xaccSplitSetLot(Split* split, GNCLot* lot); - -/** Returns the KvpFrame slots of this split for direct editing. - * - * Split slots are used to store arbitrary strings, numbers, and - * structures which aren't members of the transaction struct. See - * kvp_doc.txt for reserved slot names. - */ -KvpFrame *xaccSplitGetSlots(const Split *split); - -/** Set the KvpFrame slots of this split to the given frm by directly - * using the frm pointer (i.e. non-copying). */ -void xaccSplitSetSlots_nc(Split *s, KvpFrame *frm); - - /** The memo is an arbitrary string associated with a split. It is * intended to hold a short (zero to forty character) string that is * displayed by the GUI along with this split. Users typically type diff --git a/src/engine/SplitP.h b/src/engine/SplitP.h index 93f0ed5af81..2832dc8ff43 100644 --- a/src/engine/SplitP.h +++ b/src/engine/SplitP.h @@ -146,7 +146,8 @@ struct _SplitClass */ void xaccFreeSplit (Split *split); /* frees memory */ -Split * xaccSplitClone (const Split *s); +Split *xaccSplitCloneNoKvp (const Split *s); +void xaccSplitCopyKvp (const Split *from, Split *to); Split *xaccDupeSplit (const Split *s); void mark_split (Split *s); diff --git a/src/engine/Transaction.c b/src/engine/Transaction.c index 2df9f84e534..5146826b012 100644 --- a/src/engine/Transaction.c +++ b/src/engine/Transaction.c @@ -184,6 +184,7 @@ const char *assoc_uri_str = "assoc_uri"; #define TRANS_TXN_TYPE_KVP "trans-txn-type" #define TRANS_READ_ONLY_REASON "trans-read-only" #define TRANS_REVERSED_BY "reversed-by" +#define GNC_SX_FROM "from-sched-xaction" #define ISO_DATELENGTH 32 /* length of an iso 8601 date string. */ @@ -193,11 +194,14 @@ static QofLogModule log_module = GNC_MOD_ENGINE; enum { PROP_0, - PROP_NUM, - PROP_DESCRIPTION, - PROP_CURRENCY, - PROP_POST_DATE, - PROP_ENTER_DATE + PROP_CURRENCY, /* Table */ + PROP_NUM, /* Table */ + PROP_POST_DATE, /* Table */ + PROP_ENTER_DATE, /* Table */ + PROP_DESCRIPTION, /* Table */ + PROP_INVOICE, /* KVP */ + PROP_SX_TXN, /* KVP */ + PROP_ONLINE_ACCOUNT,/* KVP */ }; void @@ -303,6 +307,9 @@ gnc_transaction_get_property(GObject* object, GParamSpec* pspec) { Transaction* tx; + KvpFrame *frame; + gchar *key; + GValue *temp; g_return_if_fail(GNC_IS_TRANSACTION(object)); @@ -324,6 +331,18 @@ gnc_transaction_get_property(GObject* object, case PROP_ENTER_DATE: g_value_set_boxed(value, &tx->date_entered); break; + case PROP_INVOICE: + key = GNC_INVOICE_ID "/" GNC_INVOICE_GUID; + qof_instance_get_kvp (QOF_INSTANCE (tx), key, value); + break; + case PROP_SX_TXN: + key = GNC_SX_FROM; + qof_instance_get_kvp (QOF_INSTANCE (tx), key, value); + break; + case PROP_ONLINE_ACCOUNT: + key = "online_id"; + qof_instance_get_kvp (QOF_INSTANCE (tx), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -337,10 +356,14 @@ gnc_transaction_set_property(GObject* object, GParamSpec* pspec) { Transaction* tx; + KvpFrame *frame; + gchar *key; g_return_if_fail(GNC_IS_TRANSACTION(object)); tx = GNC_TRANSACTION(object); + g_assert (qof_instance_get_editlevel(tx)); + switch (prop_id) { case PROP_NUM: @@ -358,6 +381,18 @@ gnc_transaction_set_property(GObject* object, case PROP_ENTER_DATE: xaccTransSetDateEnteredTS(tx, g_value_get_boxed(value)); break; + case PROP_INVOICE: + key = GNC_INVOICE_ID "/" GNC_INVOICE_GUID; + qof_instance_set_kvp (QOF_INSTANCE (tx), key, value); + break; + case PROP_SX_TXN: + key = GNC_SX_FROM; + qof_instance_set_kvp (QOF_INSTANCE (tx), key, value); + break; + case PROP_ONLINE_ACCOUNT: + key = "online_id"; + qof_instance_set_kvp (QOF_INSTANCE (tx), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -426,6 +461,36 @@ gnc_transaction_class_init(TransactionClass* klass) "The date the transaction was entered.", GNC_TYPE_TIMESPEC, G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_INVOICE, + g_param_spec_boxed("invoice", + "Invoice attached to lot", + "Used by GncInvoice", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_SX_TXN, + g_param_spec_boxed("from-sched-xaction", + "From Scheduled Transaction", + "Used by Scheduled Transastions to record the " + "originating template transaction for created " + "transactions", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_ONLINE_ACCOUNT, + g_param_spec_string ("online-id", + "Online Account ID", + "The online account which corresponds to this " + "account for OFX/HCBI import", + NULL, + G_PARAM_READWRITE)); } /********************************************************************\ @@ -532,9 +597,8 @@ xaccTransSortSplits (Transaction *trans) * This routine is prone to programmer snafu if not used correctly. * It is used only by the edit-rollback code. */ -/* Actually, it *is* public, and used by Period.c */ -Transaction * -xaccDupeTransaction (const Transaction *from) +static Transaction * +dupe_trans (const Transaction *from) { Transaction *to; GList *node; @@ -571,10 +635,11 @@ xaccDupeTransaction (const Transaction *from) /********************************************************************\ * Use this routine to externally duplicate a transaction. It creates - * a full fledged transaction with unique guid, splits, etc. + * a full fledged transaction with unique guid, splits, etc. and + * writes it to the database. \********************************************************************/ Transaction * -xaccTransClone (const Transaction *from) +xaccTransCloneNoKvp (const Transaction *from) { Transaction *to; Split *split; @@ -593,14 +658,13 @@ xaccTransClone (const Transaction *from) to->orig = NULL; - qof_instance_init_data (&to->inst, GNC_ID_TRANS, qof_instance_get_book(from)); - kvp_frame_delete (to->inst.kvp_data); - to->inst.kvp_data = kvp_frame_copy (from->inst.kvp_data); + qof_instance_init_data (&to->inst, GNC_ID_TRANS, + qof_instance_get_book(from)); xaccTransBeginEdit(to); for (node = from->splits; node; node = node->next) { - split = xaccSplitClone(node->data); + split = xaccSplitCloneNoKvp(node->data); split->parent = to; to->splits = g_list_append (to->splits, split); } @@ -611,11 +675,28 @@ xaccTransClone (const Transaction *from) return to; } +Transaction * +xaccTransClone (const Transaction *from) +{ + Transaction *to = xaccTransCloneNoKvp (from); + int i = 0; + int length = g_list_length (from->splits); + + xaccTransBeginEdit (to); + to->inst.kvp_data = kvp_frame_copy (from->inst.kvp_data); + g_assert (g_list_length (to->splits) == length); + for (i = 0; i < length; ++i) + xaccSplitCopyKvp (g_list_nth_data (from->splits, i), + g_list_nth_data (to->splits, i)); + xaccTransCommitEdit (to); + return to; +} + /*################## Added for Reg2 #################*/ /********************************************************************\ * Copy a transaction to the 'clipboard' transaction using - * xaccDupeTransaction. The 'clipboard' transaction must never + * dupe_trans. The 'clipboard' transaction must never * be dereferenced. \********************************************************************/ Transaction * xaccTransCopyToClipBoard(const Transaction *from_trans) @@ -625,7 +706,7 @@ Transaction * xaccTransCopyToClipBoard(const Transaction *from_trans) if (!from_trans) return NULL; - to_trans = xaccDupeTransaction(from_trans); + to_trans = dupe_trans(from_trans); return to_trans; } @@ -642,7 +723,7 @@ xaccTransCopyOnto(const Transaction *from_trans, Transaction *to_trans) /********************************************************************\ * This function explicitly must robustly handle some unusual input. * - * 'from_trans' may be a duped trans (see xaccDupeTransaction), so its + * 'from_trans' may be a duped trans (see dupe_trans), so its * splits may not really belong to the accounts that they say they do. * * 'from_acc' need not be a valid account. It may be an already freed @@ -1309,7 +1390,7 @@ xaccTransBeginEdit (Transaction *trans) /* Make a clone of the transaction; we will use this * in case we need to roll-back the edit. */ - trans->orig = xaccDupeTransaction (trans); + trans->orig = dupe_trans (trans); } /********************************************************************\ @@ -2615,6 +2696,7 @@ xaccTransFindSplitByAccount(const Transaction *trans, const Account *acc) return NULL; } + /********************************************************************\ \********************************************************************/ /* QofObject function implementation */ @@ -2776,6 +2858,7 @@ _utest_trans_fill_functions (void) func->trans_on_error = trans_on_error; func->trans_cleanup_commit = trans_cleanup_commit; func->xaccTransScrubGainsDate = xaccTransScrubGainsDate; + func->dupe_trans = dupe_trans; return func; } diff --git a/src/engine/Transaction.h b/src/engine/Transaction.h index 8091dbaef0b..e26ac0896dd 100644 --- a/src/engine/Transaction.h +++ b/src/engine/Transaction.h @@ -156,6 +156,12 @@ void xaccTransDestroy (Transaction *trans); */ Transaction * xaccTransClone (const Transaction *t); +/** + The xaccTransCloneNoKvp() method will create a complete copy of an + existing transaction except that the KVP slots will be empty. + */ +Transaction * xaccTransCloneNoKvp (const Transaction *t); + /** Equality. * * @param ta First transaction to compare @@ -223,7 +229,7 @@ Transaction * xaccTransLookup (const GncGUID *guid, QofBook *book); /*################## Added for Reg2 #################*/ /** Copy a transaction to the 'clipboard' transaction using - * xaccDupeTransaction. The 'clipboard' transaction must never + * dupe_transaction. The 'clipboard' transaction must never * be dereferenced. */ Transaction * xaccTransCopyToClipBoard(const Transaction *from_trans); @@ -749,8 +755,6 @@ void xaccTransDump (const Transaction *trans, const char *tag); #define xaccTransGetGUID(X) qof_entity_get_guid(QOF_INSTANCE(X)) /** \deprecated */ #define xaccTransReturnGUID(X) (X ? *(qof_entity_get_guid(QOF_INSTANCE(X))) : *(guid_null())) -/** \deprecated */ -#define xaccTransGetSlots(X) qof_instance_get_slots (QOF_INSTANCE(X)) #endif /* XACC_TRANSACTION_H */ /** @} */ diff --git a/src/engine/TransactionP.h b/src/engine/TransactionP.h index d7935e8d221..87b89bf9e80 100644 --- a/src/engine/TransactionP.h +++ b/src/engine/TransactionP.h @@ -186,6 +186,7 @@ typedef struct void (*trans_on_error)(Transaction*, QofBackendError); void (*trans_cleanup_commit)(Transaction*); void (*xaccTransScrubGainsDate)(Transaction*); + Transaction *(*dupe_trans)(const Transaction*); } TransTestFunctions; diff --git a/src/engine/cap-gains.c b/src/engine/cap-gains.c index e73bdfc2595..b7ea3f15f38 100644 --- a/src/engine/cap-gains.c +++ b/src/engine/cap-gains.c @@ -215,150 +215,6 @@ xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign, return lot; } -/* ============================================================== */ -/* Similar to GetOrMakeAccount, but different in important ways */ - -static Account * -GetOrMakeLotOrphanAccount (Account *root, gnc_commodity * currency) -{ - char * accname; - Account * acc; - - g_return_val_if_fail (root, NULL); - - /* build the account name */ - if (!currency) - { - PERR ("No currency specified!"); - return NULL; - } - - accname = g_strconcat (_("Orphaned Gains"), "-", - gnc_commodity_get_mnemonic (currency), NULL); - - /* See if we've got one of these going already ... */ - acc = gnc_account_lookup_by_name(root, accname); - - if (acc == NULL) - { - /* Guess not. We'll have to build one. */ - acc = xaccMallocAccount (gnc_account_get_book(root)); - xaccAccountBeginEdit (acc); - xaccAccountSetName (acc, accname); - xaccAccountSetCommodity (acc, currency); - xaccAccountSetType (acc, ACCT_TYPE_INCOME); - xaccAccountSetDescription (acc, _("Realized Gain/Loss")); - xaccAccountSetNotes (acc, - _("Realized Gains or Losses from " - "Commodity or Trading Accounts " - "that haven't been recorded elsewhere.")); - - /* Hang the account off the root. */ - gnc_account_append_child (root, acc); - xaccAccountCommitEdit (acc); - } - - g_free (accname); - - return acc; -} - -/* ============================================================== */ - -void -xaccAccountSetDefaultGainAccount (Account *acc, const Account *gain_acct) -{ - KvpFrame *cwd; - KvpValue *vvv; - const char * cur_name; - gnc_commodity *acc_comm; - - if (!acc || !gain_acct) return; - - cwd = xaccAccountGetSlots (acc); - cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/"); - - /* Accounts are indexed by thier unique currency name */ - acc_comm = xaccAccountGetCommodity(acc); - cur_name = gnc_commodity_get_unique_name (acc_comm); - - xaccAccountBeginEdit (acc); - vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct)); - kvp_frame_set_slot_nc (cwd, cur_name, vvv); - qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data); - xaccAccountCommitEdit (acc); -} - -/* ============================================================== */ - -Account * -xaccAccountGetDefaultGainAccount (const Account *acc, const gnc_commodity * currency) -{ - Account *gain_acct = NULL; - KvpFrame *cwd; - KvpValue *vvv; - GncGUID * gain_acct_guid; - const char * cur_name; - - if (!acc || !currency) return NULL; - - cwd = xaccAccountGetSlots (acc); - cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/"); - - /* Accounts are indexed by thier unique currency name */ - cur_name = gnc_commodity_get_unique_name (currency); - vvv = kvp_frame_get_slot (cwd, cur_name); - gain_acct_guid = kvp_value_get_guid (vvv); - - gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc)); - return gain_acct; -} - -/* ============================================================== */ -/* Functionally identical to the following: - * if (!xaccAccountGetDefaultGainAccount()) { - * xaccAccountSetDefaultGainAccount (); } - * except that it saves a few cycles. - */ - -static Account * -GetOrMakeGainAcct (Account *acc, gnc_commodity * currency) -{ - Account *gain_acct = NULL; - KvpFrame *cwd; - KvpValue *vvv; - GncGUID * gain_acct_guid; - const char * cur_name; - - cwd = xaccAccountGetSlots (acc); - cwd = kvp_frame_get_frame_slash (cwd, "/lot-mgmt/gains-act/"); - - /* Accounts are indexed by thier unique currency name */ - cur_name = gnc_commodity_get_unique_name (currency); - vvv = kvp_frame_get_slot (cwd, cur_name); - gain_acct_guid = kvp_value_get_guid (vvv); - - gain_acct = xaccAccountLookup (gain_acct_guid, qof_instance_get_book(acc)); - - /* If there is no default place to put gains/losses - * for this account, then create such a place */ - if (NULL == gain_acct) - { - Account *root; - - xaccAccountBeginEdit (acc); - root = gnc_account_get_root(acc); - gain_acct = GetOrMakeLotOrphanAccount (root, currency); - - vvv = kvp_value_new_guid (xaccAccountGetGUID (gain_acct)); - kvp_frame_set_slot_nc (cwd, cur_name, vvv); - qof_instance_set_slots(QOF_INSTANCE(acc), acc->inst.kvp_data); - xaccAccountCommitEdit (acc); - - } - return gain_acct; -} - /* ============================================================== */ Split * @@ -541,22 +397,18 @@ xaccSplitAssignToLot (Split *split, GNCLot *lot) ts = xaccSplitRetDateReconciledTS (split); xaccSplitSetDateReconciledTS (new_split, &ts); - /* We do not copy the KVP tree, as it seems like a dangerous - * thing to do. If the user wants to access stuff in the 'old' - * kvp tree from the 'new' split, they shoudl follow the - * 'split-lot' pointers. Yes, this is complicated, but what - * else can one do ?? - */ - /* Add kvp markup to indicate that these two splits used - * to be one before being 'split' + /* Set the lot-split and peer_guid properties on the two + * splits to indicate that they're linked. */ - gnc_kvp_bag_add (split->inst.kvp_data, "lot-split", now, - "peer_guid", xaccSplitGetGUID (new_split), - NULL); + qof_instance_set (QOF_INSTANCE (split), + "lot-split", now, + "peer_guid", xaccSplitGetGUID (new_split), + NULL); - gnc_kvp_bag_add (new_split->inst.kvp_data, "lot-split", now, - "peer_guid", xaccSplitGetGUID (split), - NULL); + qof_instance_set (QOF_INSTANCE (new_split), + "lot-split", now, + "peer_guid", xaccSplitGetGUID (split), + NULL); xaccAccountInsertSplit (acc, new_split); xaccTransAppendSplit (trans, new_split); @@ -633,15 +485,14 @@ xaccSplitAssign (Split *split) Split * xaccSplitGetCapGainsSplit (const Split *split) { - KvpValue *val; GncGUID *gains_guid; Split *gains_split; if (!split) return NULL; - val = kvp_frame_get_slot (split->inst.kvp_data, "gains-split"); - if (!val) return NULL; - gains_guid = kvp_value_get_guid (val); + qof_instance_get (QOF_INSTANCE (split), + "gains-split", &gains_guid, + NULL); if (!gains_guid) return NULL; /* Both splits will be in the same collection, so search there. */ @@ -656,15 +507,14 @@ xaccSplitGetCapGainsSplit (const Split *split) Split * xaccSplitGetGainsSourceSplit (const Split *split) { - KvpValue *val; GncGUID *source_guid; Split *source_split; if (!split) return NULL; - val = kvp_frame_get_slot (split->inst.kvp_data, "gains-source"); - if (!val) return NULL; - source_guid = kvp_value_get_guid (val); + qof_instance_get (QOF_INSTANCE (split), + "gains-source", &source_guid, + NULL); if (!source_guid) return NULL; /* Both splits will be in the same collection, so search there. */ @@ -697,8 +547,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) currency = split->parent->common_currency; ENTER ("(split=%p gains=%p status=0x%x lot=%s)", split, - split->gains_split, split->gains, - kvp_frame_get_string (gnc_lot_get_slots (lot), "/title")); + split->gains_split, split->gains, gnc_lot_get_title(lot)); /* Make sure the status flags and pointers are initialized */ xaccSplitDetermineGainStatus(split); @@ -942,7 +791,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) (FALSE == gnc_commodity_equiv (currency, xaccAccountGetCommodity(gain_acc)))) { - gain_acc = GetOrMakeGainAcct (lot_acc, currency); + gain_acc = xaccAccountGainsAccount (lot_acc, currency); } xaccAccountBeginEdit (gain_acc); @@ -965,17 +814,18 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) xaccSplitSetMemo (lot_split, _("Realized Gain/Loss")); xaccSplitSetMemo (gain_split, _("Realized Gain/Loss")); - /* For the new transaction, install KVP markup indicating + /* For the new transaction, set the split properties indicating * that this is the gains transaction that corresponds * to the gains source. */ xaccTransBeginEdit (base_txn); - kvp_frame_set_guid (split->inst.kvp_data, "gains-split", - xaccSplitGetGUID (lot_split)); - qof_instance_set_dirty (QOF_INSTANCE (split)); + qof_instance_set (QOF_INSTANCE (split), + "gains-split", xaccSplitGetGUID (lot_split), + NULL); xaccTransCommitEdit (base_txn); - kvp_frame_set_guid (lot_split->inst.kvp_data, "gains-source", - xaccSplitGetGUID (split)); + qof_instance_set (QOF_INSTANCE (lot_split), + "gains-source", xaccSplitGetGUID (split), + NULL); } else @@ -1031,7 +881,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) xaccSplitSetAmount (gain_split, negvalue); xaccSplitSetValue (gain_split, negvalue); - /* Some short-cuts to help avoid the above kvp lookup. */ + /* Some short-cuts to help avoid the above property lookup. */ split->gains = GAINS_STATUS_CLEAN; split->gains_split = lot_split; lot_split->gains = GAINS_STATUS_GAINS; diff --git a/src/engine/cap-gains.h b/src/engine/cap-gains.h index 62c8f83c79a..76792bd13fe 100644 --- a/src/engine/cap-gains.h +++ b/src/engine/cap-gains.h @@ -124,25 +124,6 @@ GNCLot * xaccAccountFindLatestOpenLot (Account *acc, gnc_numeric sign, gnc_commodity *currency); -/** The xaccAccountGetDefaultGainAccount() routine will return - * the account to which realized gains/losses may be posted. - * Because gains may be in different currencies, one must - * specify the currency type in which the gains will be posted. - * This routine does nothing more than return the value of - * the "/lot-mgmt/gains-act/XXX" key, where XXX is the unique - * currency name. IOf there is no default account for this - * currency, NULL will be returned. - */ -Account * xaccAccountGetDefaultGainAccount (const Account *acc, const gnc_commodity * currency); - -/** The xaccAccountSetDefaultGainAccount() routine can be used - * to set the account to which realized gains/losses will be - * posted by default. This routine does nothing more than set - * value of the "/lot-mgmt/gains-act/XXX" key, where XXX is the - * unique currency name of the currency of gains account. - */ -void xaccAccountSetDefaultGainAccount (Account *acc, const Account *gains_acct); - /** The xaccSplitGetCapGainsSplit() routine returns the split * that records the cap gains for this split. It returns NULL * if not found. This routine does nothing more than search for diff --git a/src/engine/engine.i b/src/engine/engine.i index 49cfc23c822..8fe362f0c35 100644 --- a/src/engine/engine.i +++ b/src/engine/engine.i @@ -140,10 +140,18 @@ functions. */ QofSession * qof_session_new (void); QofBook * qof_session_get_book (QofSession *session); - -// TODO: Maybe unroll -void qof_book_kvp_changed (QofBook *book); - +/* This horror is to permit the scheme options in + * src/app-utils/options.scm to read and write the book's KVP (another + * horror) directly. It should be refactored into book functions that + * handle the KVP access. + */ +%inline { + KvpFrame *qof_book_get_slots (QofBook *book); + extern KvpFrame *qof_instance_get_slots (QofInstance*); + KvpFrame *qof_book_get_slots (QofBook *book) { + return qof_instance_get_slots (QOF_INSTANCE (book)); + } +} // TODO: Unroll/remove const char *qof_session_get_url (QofSession *session); @@ -173,7 +181,6 @@ SplitList * qof_query_run_subquery (QofQuery *q, const QofQuery *q); %include %include -KvpFrame* qof_book_get_slots(QofBook* book); %ignore GNC_DENOM_AUTO; %ignore GNCNumericErrorCodes; %ignore GNC_ERROR_OK; diff --git a/src/engine/gnc-budget.c b/src/engine/gnc-budget.c index ae1e2e391e6..7e255b768fe 100644 --- a/src/engine/gnc-budget.c +++ b/src/engine/gnc-budget.c @@ -22,29 +22,31 @@ * * \********************************************************************/ -#include "config.h" +#include #include #include #include #include -#include "qof.h" -#include "qofbookslots.h" +#include +#include +#include +#include #include "Account.h" #include "gnc-budget.h" #include "gnc-commodity.h" -#include "gnc-gdate-utils.h" static QofLogModule log_module = GNC_MOD_ENGINE; enum { PROP_0, - PROP_NAME, - PROP_DESCRIPTION, - PROP_NUM_PERIODS, - PROP_RECURRENCE, + PROP_NAME, /* Table */ + PROP_DESCRIPTION, /* Table */ + PROP_NUM_PERIODS, /* Table */ + PROP_RUNTIME_0, + PROP_RECURRENCE, /* Cached pointer; Recurrence table holds budget guid */ }; struct budget_s @@ -156,6 +158,9 @@ gnc_budget_set_property( GObject* object, g_return_if_fail(GNC_IS_BUDGET(object)); budget = GNC_BUDGET(object); + if (prop_id < PROP_RUNTIME_0) + g_assert (qof_instance_get_editlevel(budget)); + switch ( prop_id ) { case PROP_NAME: @@ -626,31 +631,23 @@ gnc_budget_get_default (QofBook *book) { QofCollection *col; GncBudget *bgt = NULL; - kvp_value *kvp_default_budget; - const GncGUID *default_budget_guid; + const GncGUID *default_budget_guid = NULL; g_return_val_if_fail(book, NULL); /* See if there is a budget selected in the KVP perferences */ - kvp_default_budget = kvp_frame_get_slot_path(qof_book_get_slots (book), - KVP_OPTION_PATH, - OPTION_SECTION_BUDGETING, - OPTION_NAME_DEFAULT_BUDGET, - NULL); - - if (kvp_default_budget != NULL ) + qof_instance_get (QOF_INSTANCE (book), + "default-budget", &default_budget_guid, + NULL); + if (default_budget_guid != NULL) { - default_budget_guid = kvp_value_get_guid(kvp_default_budget); - if (default_budget_guid != NULL) - { - col = qof_book_get_collection(book, GNC_ID_BUDGET); - bgt = (GncBudget *) qof_collection_lookup_entity(col, - default_budget_guid); - } + col = qof_book_get_collection(book, GNC_ID_BUDGET); + bgt = (GncBudget *) qof_collection_lookup_entity(col, + default_budget_guid); } - /* Revert to 2.2.x behavior if there is no defined budget in KVP */ + /* Revert to 2.2.x behavior if the book has no default budget. */ if ( bgt == NULL ) { diff --git a/src/engine/gnc-commodity.c b/src/engine/gnc-commodity.c index 77c643b9cbb..851bba566a1 100644 --- a/src/engine/gnc-commodity.c +++ b/src/engine/gnc-commodity.c @@ -23,7 +23,7 @@ * * *******************************************************************/ -#include "config.h" +#include #include #include @@ -33,6 +33,7 @@ #include #include #include +#include #include "gnc-commodity.h" #include "gnc-prefs.h" @@ -47,16 +48,16 @@ static QofLogModule log_module = GNC_MOD_COMMODITY; enum { PROP_0, - PROP_NAMESPACE, - PROP_FULL_NAME, - PROP_MNEMONIC, - PROP_PRINTNAME, - PROP_CUSIP, - PROP_FRACTION, - PROP_UNIQUE_NAME, - PROP_QUOTE_FLAG, - PROP_QUOTE_SOURCE, - PROP_QUOTE_TZ, + PROP_NAMESPACE, /* Table */ + PROP_FULL_NAME, /* Table */ + PROP_MNEMONIC, /* Table */ + PROP_PRINTNAME, /* Constructed */ + PROP_CUSIP, /* Table */ + PROP_FRACTION, /* Table */ + PROP_UNIQUE_NAME, /* Constructed */ + PROP_QUOTE_FLAG, /* Table */ + PROP_QUOTE_SOURCE, /* Table */ + PROP_QUOTE_TZ, /* Table */ }; struct gnc_commodity_s @@ -698,6 +699,7 @@ gnc_commodity_set_property (GObject *object, g_return_if_fail(GNC_IS_COMMODITY(object)); commodity = GNC_COMMODITY(object); + g_assert (qof_instance_get_editlevel(commodity)); switch (prop_id) { diff --git a/src/engine/gnc-engine.h b/src/engine/gnc-engine.h index f4943fba437..4522e1eb501 100644 --- a/src/engine/gnc-engine.h +++ b/src/engine/gnc-engine.h @@ -251,5 +251,16 @@ void gnc_engine_add_commit_error_callback( EngineCommitErrorCallback cb, gpointe void gnc_engine_signal_commit_error( QofBackendError errcode ); +/** STRING CONSTANTS ********************************************** + * Used to declare constant KVP keys used in more than one class + */ +#define GNC_INVOICE_ID "gncInvoice" +#define GNC_INVOICE_GUID "invoice-guid" +#define GNC_OWNER_ID "gncOwner" +#define GNC_OWNER_TYPE "owner-type" +#define GNC_OWNER_GUID "owner-guid" +#define GNC_SX_ID "sched-xaction" + + #endif /** @} */ diff --git a/src/engine/gnc-lot.c b/src/engine/gnc-lot.c index 08fdef68cbe..d86f0bec81a 100644 --- a/src/engine/gnc-lot.c +++ b/src/engine/gnc-lot.c @@ -39,10 +39,11 @@ * Copyright (c) 2002,2003 Linas Vepstas */ -#include "config.h" +#include #include #include +#include #include "Account.h" #include "AccountP.h" @@ -63,8 +64,15 @@ struct gnc_lot_s enum { PROP_0, - PROP_IS_CLOSED, - PROP_MARKER +// PROP_ACCOUNT, /* Table */ + PROP_IS_CLOSED, /* Table */ + + PROP_INVOICE, /* KVP */ + PROP_OWNER_TYPE, /* KVP */ + PROP_OWNER_GUID, /* KVP */ + + PROP_RUNTIME_0, + PROP_MARKER, /* Runtime */ }; typedef struct LotPrivate @@ -93,6 +101,11 @@ typedef struct LotPrivate /* ============================================================= */ +static void gnc_lot_set_invoice (GNCLot* lot, GncGUID *guid); +static GncGUID *gnc_lot_get_invoice (GNCLot* lot); + +/* ============================================================= */ + /* GObject Initialization */ G_DEFINE_TYPE(GNCLot, gnc_lot, QOF_TYPE_INSTANCE) @@ -125,6 +138,9 @@ gnc_lot_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* { GNCLot* lot; LotPrivate* priv; + KvpFrame *frame; + gchar *key; + GValue *temp; g_return_if_fail(GNC_IS_LOT(object)); @@ -138,18 +154,41 @@ gnc_lot_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* case PROP_MARKER: g_value_set_int(value, priv->marker); break; + case PROP_INVOICE: + key = GNC_INVOICE_ID "/" GNC_INVOICE_GUID; + qof_instance_get_kvp (QOF_INSTANCE (lot), key, value); + break; + case PROP_OWNER_TYPE: + key = GNC_OWNER_ID"/" GNC_OWNER_TYPE; + qof_instance_get_kvp (QOF_INSTANCE (lot), key, value); + break; + case PROP_OWNER_GUID: + key = GNC_OWNER_ID "/" GNC_OWNER_GUID; + qof_instance_get_kvp (QOF_INSTANCE (lot), key, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; } } static void -gnc_lot_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) +gnc_lot_set_property (GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) { GNCLot* lot; LotPrivate* priv; + KvpFrame *frame; + gchar *key = NULL; g_return_if_fail(GNC_IS_LOT(object)); lot = GNC_LOT(object); + if (prop_id < PROP_RUNTIME_0) + g_assert (qof_instance_get_editlevel(lot)); + priv = GET_PRIVATE(lot); switch (prop_id) { @@ -159,7 +198,22 @@ gnc_lot_set_property(GObject* object, guint prop_id, const GValue* value, GParam case PROP_MARKER: priv->marker = g_value_get_int(value); break; - } + case PROP_INVOICE: + key = GNC_INVOICE_ID"/" GNC_INVOICE_GUID; + qof_instance_set_kvp (QOF_INSTANCE (lot), key, value); + break; + case PROP_OWNER_TYPE: + key = GNC_OWNER_ID "/" GNC_OWNER_TYPE; + qof_instance_set_kvp (QOF_INSTANCE (lot), key, value); + break; + case PROP_OWNER_GUID: + key = GNC_OWNER_ID "/" GNC_OWNER_GUID; + qof_instance_set_kvp (QOF_INSTANCE (lot), key, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } } static void @@ -193,8 +247,32 @@ gnc_lot_class_init(GNCLotClass* klass) 0, G_MAXINT8, 0, G_PARAM_READWRITE)); - - + g_object_class_install_property( + gobject_class, + PROP_INVOICE, + g_param_spec_boxed("invoice", + "Invoice attached to lot", + "Used by GncInvoice", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_OWNER_TYPE, + g_param_spec_int64("owner-type", + "Owning Entity Type of lot", + "Used by GncOwner", + 0, G_MAXINT64, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_OWNER_GUID, + g_param_spec_boxed("owner-guid", + "Owner attached to lot", + "Used by GncOwner", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); } GNCLot * @@ -333,12 +411,6 @@ gnc_lot_set_closed_unknown(GNCLot* lot) } } -KvpFrame * -gnc_lot_get_slots (const GNCLot *lot) -{ - return qof_instance_get_slots(QOF_INSTANCE(lot)); -} - SplitList * gnc_lot_get_split_list (const GNCLot *lot) { @@ -363,14 +435,16 @@ const char * gnc_lot_get_title (const GNCLot *lot) { if (!lot) return NULL; - return kvp_frame_get_string (gnc_lot_get_slots(lot), "/title"); + return kvp_frame_get_string (qof_instance_get_slots(QOF_INSTANCE (lot)), + "/title"); } const char * gnc_lot_get_notes (const GNCLot *lot) { if (!lot) return NULL; - return kvp_frame_get_string (gnc_lot_get_slots(lot), "/notes"); + return kvp_frame_get_string (qof_instance_get_slots(QOF_INSTANCE (lot)), + "/notes"); } void @@ -379,7 +453,8 @@ gnc_lot_set_title (GNCLot *lot, const char *str) if (!lot) return; qof_begin_edit(QOF_INSTANCE(lot)); qof_instance_set_dirty(QOF_INSTANCE(lot)); - kvp_frame_set_str (gnc_lot_get_slots(lot), "/title", str); + kvp_frame_set_str (qof_instance_get_slots(QOF_INSTANCE (lot)), + "/title", str); gnc_lot_commit_edit(lot); } @@ -389,7 +464,8 @@ gnc_lot_set_notes (GNCLot *lot, const char *str) if (!lot) return; gnc_lot_begin_edit(lot); qof_instance_set_dirty(QOF_INSTANCE(lot)); - kvp_frame_set_str (gnc_lot_get_slots(lot), "/notes", str); + kvp_frame_set_str (qof_instance_get_slots (QOF_INSTANCE (lot)), + "/notes", str); gnc_lot_commit_edit(lot); } @@ -675,20 +751,20 @@ gboolean gnc_lot_register (void) GNCLot * gnc_lot_make_default (Account *acc) { GNCLot * lot; - gint64 id; - char buff[200]; + gint64 id = 0; + gchar *buff; lot = gnc_lot_new (qof_instance_get_book(acc)); /* Provide a reasonable title for the new lot */ xaccAccountBeginEdit (acc); - id = kvp_frame_get_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id"); - snprintf (buff, 200, ("%s %" G_GINT64_FORMAT), _("Lot"), id); - kvp_frame_set_str (gnc_lot_get_slots (lot), "/title", buff); + qof_instance_get (QOF_INSTANCE (acc), "lot-next-id", &id, NULL); + buff = g_strdup_printf ("%s %" G_GINT64_FORMAT, _("Lot"), id); + gnc_lot_set_title (lot, buff); id ++; - kvp_frame_set_gint64 (xaccAccountGetSlots (acc), "/lot-mgmt/next-id", id); - qof_instance_set_dirty (QOF_INSTANCE(acc)); + qof_instance_set (QOF_INSTANCE (acc), "lot-next-id", id, NULL); xaccAccountCommitEdit (acc); + g_free (buff); return lot; } diff --git a/src/engine/gnc-lot.h b/src/engine/gnc-lot.h index 1e477e1f53e..64e1dfaa0c5 100644 --- a/src/engine/gnc-lot.h +++ b/src/engine/gnc-lot.h @@ -165,11 +165,6 @@ const char * gnc_lot_get_notes (const GNCLot *); void gnc_lot_set_title (GNCLot *, const char *); void gnc_lot_set_notes (GNCLot *, const char *); -/** Every lot has a place to hang kvp data. This routine returns that - * place. - * */ -KvpFrame * gnc_lot_get_slots (const GNCLot *); - /** XXX: Document? */ GNCLot * gnc_lot_make_default (Account * acc); diff --git a/src/engine/gnc-pricedb.c b/src/engine/gnc-pricedb.c index 54b7385da5c..23fbae09f87 100644 --- a/src/engine/gnc-pricedb.c +++ b/src/engine/gnc-pricedb.c @@ -41,12 +41,12 @@ static GNCPrice *lookup_nearest_in_time(GNCPriceDB *db, const gnc_commodity *c, enum { PROP_0, - PROP_COMMODITY, - PROP_CURRENCY, - PROP_DATE, - PROP_SOURCE, - PROP_TYPE, - PROP_VALUE + PROP_COMMODITY, /* Table */ + PROP_CURRENCY, /* Table */ + PROP_DATE, /* Table */ + PROP_SOURCE, /* Table */ + PROP_TYPE, /* Table */ + PROP_VALUE, /* Table, 2 fields (numeric) */ }; /* GObject Initialization */ @@ -123,6 +123,8 @@ gnc_price_set_property(GObject* object, guint prop_id, const GValue* value, GPar g_return_if_fail(GNC_IS_PRICE(object)); price = GNC_PRICE(object); + g_assert (qof_instance_get_editlevel(price)); + switch (prop_id) { case PROP_SOURCE: diff --git a/src/engine/gncAddress.c b/src/engine/gncAddress.c index 97bbfd78e5c..103e35a5ce3 100644 --- a/src/engine/gncAddress.c +++ b/src/engine/gncAddress.c @@ -25,9 +25,10 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include +#include #include "gncAddress.h" #include "gncAddressP.h" diff --git a/src/engine/gncBillTerm.c b/src/engine/gncBillTerm.c index 835f052fa15..9f2c7b97dd8 100644 --- a/src/engine/gncBillTerm.c +++ b/src/engine/gncBillTerm.c @@ -26,9 +26,10 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include +#include #include "gnc-engine.h" #include "gncBillTermP.h" @@ -192,6 +193,8 @@ gnc_billterm_set_property (GObject *object, g_return_if_fail(GNC_IS_BILLTERM(object)); bt = GNC_BILLTERM(object); + g_assert (qof_instance_get_editlevel(bt)); + switch (prop_id) { case PROP_NAME: diff --git a/src/engine/gncBusiness.h b/src/engine/gncBusiness.h index b9a960f4062..11584b78f24 100644 --- a/src/engine/gncBusiness.h +++ b/src/engine/gncBusiness.h @@ -37,6 +37,12 @@ #include #include "qof.h" +/* KVP key for report PDF directories */ +#define OWNER_EXPORT_PDF_DIRNAME "export-pdf-directory" +#define LAST_POSTED_TO_ACCT "last-posted-to-acct" +#define GNC_PAYMENT "payment" +#define GNC_LAST_ACCOUNT "last_acct" + /* @deprecated backwards-compat definitions */ #define GNC_BILLTERM_MODULE_NAME GNC_ID_BILLTERM #define GNC_CUSTOMER_MODULE_NAME GNC_ID_CUSTOMER diff --git a/src/engine/gncCustomer.c b/src/engine/gncCustomer.c index 581e97af0be..5d3d7c58447 100644 --- a/src/engine/gncCustomer.c +++ b/src/engine/gncCustomer.c @@ -30,6 +30,7 @@ #include #include +#include #include "gnc-commodity.h" @@ -96,7 +97,10 @@ void mark_customer (GncCustomer *customer) enum { PROP_0, - PROP_NAME + PROP_NAME, + PROP_PDF_DIRNAME, + PROP_LAST_POSTED, + PROP_PAYMENT_LAST_ACCT, }; /* GObject Initialization */ @@ -126,7 +130,7 @@ gnc_customer_get_property (GObject *object, GParamSpec *pspec) { GncCustomer *cust; - + gchar *key; g_return_if_fail(GNC_IS_CUSTOMER(object)); cust = GNC_CUSTOMER(object); @@ -135,6 +139,18 @@ gnc_customer_get_property (GObject *object, case PROP_NAME: g_value_set_string(value, cust->name); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_get_kvp (QOF_INSTANCE (cust), key, value); + break; + case PROP_LAST_POSTED: + key = LAST_POSTED_TO_ACCT; + qof_instance_get_kvp (QOF_INSTANCE (cust), key, value); + break; + case PROP_PAYMENT_LAST_ACCT: + key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT; + qof_instance_get_kvp (QOF_INSTANCE (cust), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -144,19 +160,34 @@ gnc_customer_get_property (GObject *object, static void gnc_customer_set_property (GObject *object, guint prop_id, - const GValue *value, + const GValue *value, GParamSpec *pspec) { GncCustomer *cust; + gchar *key; g_return_if_fail(GNC_IS_CUSTOMER(object)); cust = GNC_CUSTOMER(object); + g_assert (qof_instance_get_editlevel(cust)); + switch (prop_id) { case PROP_NAME: gncCustomerSetName(cust, g_value_get_string(value)); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_set_kvp (QOF_INSTANCE (cust), key, value); + break; + case PROP_LAST_POSTED: + key = LAST_POSTED_TO_ACCT; + qof_instance_set_kvp (QOF_INSTANCE (cust), key, value); + break; + case PROP_PAYMENT_LAST_ACCT: + key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT; + qof_instance_set_kvp (QOF_INSTANCE (cust), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -242,6 +273,39 @@ gnc_customer_class_init (GncCustomerClass *klass) "customer name.", NULL, G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_PDF_DIRNAME, + g_param_spec_string ("export-pdf-dir", + "Export PDF Directory Name", + "A subdirectory for exporting PDF reports which is " + "appended to the target directory when writing them " + "out. It is retrieved from preferences and stored on " + "each 'Owner' object which prints items after " + "printing.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_LAST_POSTED, + g_param_spec_boxed("invoice-last-posted-account", + "Invoice Last Posted Account", + "The last account to which an invoice belonging to " + "this owner was posted.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_PAYMENT_LAST_ACCT, + g_param_spec_boxed("payment-last-account", + "Payment Last Account", + "The last account to which an payment belonging to " + "this owner was posted.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); } /* Create/Destroy Functions */ @@ -866,7 +930,6 @@ gboolean gncCustomerRegister (void) (QofSetterFunc)gncCustomerSetTaxTableOverride }, { CUSTOMER_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncCustomerGetTerms, (QofSetterFunc)gncCustomerSetTerms }, - { CUSTOMER_SLOTS, QOF_TYPE_KVP, (QofAccessFunc)qof_instance_get_slots, NULL }, { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetActive, (QofSetterFunc)gncCustomerSetActive }, { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL }, { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL }, diff --git a/src/engine/gncEmployee.c b/src/engine/gncEmployee.c index 94d7a72fcec..ebfb5185cc9 100644 --- a/src/engine/gncEmployee.c +++ b/src/engine/gncEmployee.c @@ -26,10 +26,11 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include #include +#include #include "Account.h" #include "gnc-commodity.h" @@ -79,16 +80,19 @@ void mark_employee (GncEmployee *employee) enum { PROP_0, - PROP_USERNAME, - PROP_ID, - PROP_ACTIVE, - PROP_LANGUAGE, - PROP_CURRENCY, - PROP_ACL, - PROP_ADDRESS, - PROP_WORKDAY, - PROP_RATE, - PROP_CCARD + PROP_USERNAME, /* Table */ + PROP_ID, /* Table */ + PROP_LANGUAGE, /* Table */ + PROP_ACL, /* Table */ + PROP_ACTIVE, /* Table */ + PROP_CURRENCY, /* Table */ + PROP_CCARD, /* Table */ + PROP_WORKDAY, /* Table (numeric) */ + PROP_RATE, /* Table (numeric) */ + PROP_ADDRESS, /* Table, 8 fields */ + PROP_PDF_DIRNAME, /* KVP */ + PROP_LAST_POSTED, /* KVP */ + PROP_PAYMENT_LAST_ACCT, /* KVP */ }; /* GObject Initialization */ @@ -124,6 +128,7 @@ gnc_employee_get_property (GObject *object, GParamSpec *pspec) { GncEmployee *emp; + gchar *key; g_return_if_fail(GNC_IS_EMPLOYEE(object)); @@ -160,6 +165,18 @@ gnc_employee_get_property (GObject *object, case PROP_CCARD: g_value_take_object(value, emp->ccard_acc); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_get_kvp (QOF_INSTANCE (emp), key, value); + break; + case PROP_LAST_POSTED: + key = LAST_POSTED_TO_ACCT; + qof_instance_get_kvp (QOF_INSTANCE (emp), key, value); + break; + case PROP_PAYMENT_LAST_ACCT: + key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT; + qof_instance_get_kvp (QOF_INSTANCE (emp), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -173,10 +190,13 @@ gnc_employee_set_property (GObject *object, GParamSpec *pspec) { GncEmployee *emp; + gchar *key; g_return_if_fail(GNC_IS_EMPLOYEE(object)); emp = GNC_EMPLOYEE(object); + g_assert (qof_instance_get_editlevel(emp)); + switch (prop_id) { case PROP_USERNAME: @@ -209,6 +229,18 @@ gnc_employee_set_property (GObject *object, case PROP_CCARD: gncEmployeeSetCCard(emp, g_value_get_object(value)); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_set_kvp (QOF_INSTANCE (emp), key, value); + break; + case PROP_LAST_POSTED: + key = LAST_POSTED_TO_ACCT; + qof_instance_set_kvp (QOF_INSTANCE (emp), key, value); + break; + case PROP_PAYMENT_LAST_ACCT: + key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT; + qof_instance_set_kvp (QOF_INSTANCE (emp), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -367,6 +399,39 @@ gnc_employee_class_init (GncEmployeeClass *klass) "The credit card account for this employee.", GNC_TYPE_ACCOUNT, G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_PDF_DIRNAME, + g_param_spec_string ("export-pdf-dir", + "Export PDF Directory Name", + "A subdirectory for exporting PDF reports which is " + "appended to the target directory when writing them " + "out. It is retrieved from preferences and stored on " + "each 'Owner' object which prints items after " + "printing.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_LAST_POSTED, + g_param_spec_boxed("invoice-last-posted-account", + "Invoice Last Posted Account", + "The last account to which an invoice belonging to " + "this owner was posted.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_PAYMENT_LAST_ACCT, + g_param_spec_boxed("payment-last-account", + "Payment Last Account", + "The last account to which an payment belonging to " + "this owner was posted.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); } /* Create/Destroy Functions */ diff --git a/src/engine/gncEntry.c b/src/engine/gncEntry.c index 2593bed1062..173edbc2c07 100644 --- a/src/engine/gncEntry.c +++ b/src/engine/gncEntry.c @@ -25,9 +25,10 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include +#include #include "gnc-commodity.h" @@ -211,7 +212,32 @@ void mark_entry (GncEntry *entry) enum { PROP_0, - PROP_DESCRIPTION +// PROP_DATE, /* Table */ +// PROP_DATE_ENTERED, /* Table */ + PROP_DESCRIPTION, /* Table */ +// PROP_ACTION, /* Table */ +// PROP_NOTES, /* Table */ +// PROP_QUANTITY, /* Table (numeric) */ +// PROP_I_ACCT, /* Table */ +// PROP_I_PRICE, /* Table (numeric) */ +// PROP_I_DISCOUNT, /* Table (numeric) */ +// PROP_INVOICE, /* Table */ +// PROP_I_DISC_TYPE, /* Table */ +// PROP_I_DISC_HOW, /* Table */ +// PROP_I_TAXABLE, /* Table */ +// PROP_I_TAX_INCL, /* Table */ +// PROP_I_TAXTABLE, /* Table */ +// PROP_B_ACCT, /* Table */ +// PROP_B_PRICE, /* Table (numeric) */ +// PROP_BILL, /* Table */ +// PROP_B_TAXTABLE_1, /* Table */ +// PROP_B_TAX_INCL, /* Table */ +// PROP_B_TAXTABLE, /* Table */ +// PROP_B_PAYTYPE, /* Table */ +// PROP_BILLABLE, /* Table */ +// PROP_BILLTO_TYPE, /* Table */ +// PROP_BILLTO, /* Table */ +// PROP_ORDER, /* Table */ }; /* GObject Initialization */ @@ -267,6 +293,8 @@ gnc_entry_set_property (GObject *object, g_return_if_fail(GNC_IS_ENTRY(object)); entry = GNC_ENTRY(object); + g_assert (qof_instance_get_editlevel(entry)); + switch (prop_id) { case PROP_DESCRIPTION: diff --git a/src/engine/gncInvoice.c b/src/engine/gncInvoice.c index 9b9fed49386..0198958be99 100644 --- a/src/engine/gncInvoice.c +++ b/src/engine/gncInvoice.c @@ -27,10 +27,11 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include #include +#include #include "Transaction.h" #include "Account.h" @@ -81,8 +82,6 @@ static QofLogModule log_module = GNC_MOD_BUSINESS; #define _GNC_MOD_NAME GNC_ID_INVOICE -#define GNC_INVOICE_ID "gncInvoice" -#define GNC_INVOICE_GUID "invoice-guid" #define GNC_INVOICE_IS_CN "credit-note" #define SET_STR(obj, member, str) { \ @@ -113,7 +112,22 @@ QofBook * gncInvoiceGetBook(GncInvoice *x) enum { PROP_0, - PROP_NOTES +// PROP_ID, /* Table */ +// PROP_DATE_OPENED, /* Table */ +// PROP_DATE_POSTED, /* Table */ + PROP_NOTES, /* Table */ +// PROP_ACTIVE, /* Table */ +// PROP_CURRENCY, /* Table */ +// PROP_OWNER_TYPE, /* Table */ +// PROP_OWNER, /* Table */ +// PROP_TERMS, /* Table */ +// PROP_BILLING_ID, /* Table */ +// PROP_POST_TXN, /* Table */ +// PROP_POST_LOT, /* Table */ +// PROP_POST_ACCOUNT, /* Table */ +// PROP_BILLTO_TYPE, /* Table */ +// PROP_BILLTO, /* Table */ +// PROP_CHARGE_AMOUNT, /* Table, (numeric) */ }; /* GObject Initialization */ @@ -169,6 +183,8 @@ gnc_invoice_set_property (GObject *object, g_return_if_fail(GNC_IS_INVOICE(object)); inv = GNC_INVOICE(object); + g_assert (qof_instance_get_editlevel(inv)); + switch (prop_id) { case PROP_NOTES: @@ -1133,52 +1149,37 @@ qofInvoiceSetJob (GncInvoice *invoice, GncJob *job) static void gncInvoiceDetachFromLot (GNCLot *lot) { - KvpFrame *kvp; - if (!lot) return; + gnc_lot_begin_edit (lot); - kvp = gnc_lot_get_slots (lot); - kvp_frame_set_slot_path (kvp, NULL, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); - qof_instance_set_dirty (QOF_INSTANCE (lot)); + qof_instance_set (QOF_INSTANCE (lot), "invoice", NULL, NULL); gnc_lot_commit_edit (lot); } static void gncInvoiceAttachToLot (GncInvoice *invoice, GNCLot *lot) { - KvpFrame *kvp; - KvpValue *value; - + GncGUID *guid; if (!invoice || !lot) return; if (invoice->posted_lot) return; /* Cannot reset invoice's lot */ - + guid = (GncGUID*)qof_instance_get_guid (QOF_INSTANCE (invoice)); gnc_lot_begin_edit (lot); - kvp = gnc_lot_get_slots (lot); - value = kvp_value_new_guid (qof_instance_get_guid (QOF_INSTANCE(invoice))); - kvp_frame_set_slot_path (kvp, value, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); - qof_instance_set_dirty (QOF_INSTANCE (lot)); + qof_instance_set (QOF_INSTANCE (lot), "invoice", guid, NULL); gnc_lot_commit_edit (lot); - kvp_value_delete (value); gncInvoiceSetPostedLot (invoice, lot); } GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot) { - KvpFrame *kvp; - KvpValue *value; - GncGUID *guid; + GncGUID *guid = NULL; QofBook *book; if (!lot) return NULL; book = gnc_lot_get_book (lot); - kvp = gnc_lot_get_slots (lot); - value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); - if (!value) return NULL; - - guid = kvp_value_get_guid (value); + qof_instance_get (QOF_INSTANCE (lot), "invoice", &guid, NULL); return gncInvoiceLookup(book, guid); } @@ -1194,10 +1195,8 @@ gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn) if (invoice->posted_txn) return; /* Cannot reset invoice's txn */ xaccTransBeginEdit (txn); - kvp = xaccTransGetSlots (txn); - value = kvp_value_new_guid (qof_instance_get_guid(QOF_INSTANCE(invoice))); - kvp_frame_set_slot_path (kvp, value, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); - kvp_value_delete (value); + qof_instance_set (QOF_INSTANCE (txn), "invoice", + qof_instance_get_guid (QOF_INSTANCE (invoice)), NULL); xaccTransSetTxnType (txn, TXN_TYPE_INVOICE); xaccTransCommitEdit (txn); gncInvoiceSetPostedTxn (invoice, txn); @@ -1206,19 +1205,13 @@ gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn) GncInvoice * gncInvoiceGetInvoiceFromTxn (const Transaction *txn) { - KvpFrame *kvp; - KvpValue *value; - GncGUID *guid; + GncGUID *guid = NULL; QofBook *book; if (!txn) return NULL; book = xaccTransGetBook (txn); - kvp = xaccTransGetSlots (txn); - value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); - if (!value) return NULL; - - guid = kvp_value_get_guid (value); + qof_instance_get (QOF_INSTANCE (txn), "invoice", &guid, NULL); return gncInvoiceLookup(book, guid); } diff --git a/src/engine/gncInvoice.h b/src/engine/gncInvoice.h index e3e41df282f..49144322e3c 100644 --- a/src/engine/gncInvoice.h +++ b/src/engine/gncInvoice.h @@ -268,6 +268,7 @@ GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot); */ static inline GncInvoice * gncInvoiceLookup (const QofBook *book, const GncGUID *guid) { + if (book == NULL || guid == NULL) return NULL; QOF_BOOK_RETURN_ENTITY(book, guid, GNC_ID_INVOICE, GncInvoice); } @@ -305,7 +306,6 @@ QofBook *gncInvoiceGetBook(GncInvoice *x); /** deprecated functions */ #define gncInvoiceGetGUID(x) qof_instance_get_guid(QOF_INSTANCE(x)) #define gncInvoiceRetGUID(x) (x ? *(qof_instance_get_guid(QOF_INSTANCE(x))) : *(guid_null())) -#define gncInvoiceLookupDirect(G,B) gncInvoiceLookup((B),&(G)) /** Test support function used by test-dbi-business-stuff.c */ gboolean gncInvoiceEqual(const GncInvoice *a, const GncInvoice *b); diff --git a/src/engine/gncJob.c b/src/engine/gncJob.c index fe7043de905..d4200a1f270 100644 --- a/src/engine/gncJob.c +++ b/src/engine/gncJob.c @@ -26,10 +26,11 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include #include +#include #include "gnc-features.h" #include "gncInvoice.h" @@ -71,7 +72,13 @@ void mark_job (GncJob *job) enum { PROP_0, - PROP_NAME +// PROP_ID, /* Table */ + PROP_NAME, /* Table */ +// PROP_REFERENCE, /* Table */ +// PROP_ACTIVE, /* Table */ +// PROP_OWNER_TYPE, /* Table */ +// PROP_OWNER, /* Table */ + PROP_PDF_DIRNAME, /* KVP */ }; /* GObject Initialization */ @@ -101,6 +108,7 @@ gnc_job_get_property (GObject *object, GParamSpec *pspec) { GncJob *job; + gchar *key; g_return_if_fail(GNC_IS_JOB(object)); @@ -110,6 +118,10 @@ gnc_job_get_property (GObject *object, case PROP_NAME: g_value_set_string(value, job->name); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_get_kvp (QOF_INSTANCE (job), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -123,15 +135,22 @@ gnc_job_set_property (GObject *object, GParamSpec *pspec) { GncJob *job; + gchar *key; g_return_if_fail(GNC_IS_JOB(object)); job = GNC_JOB(object); + g_assert (qof_instance_get_editlevel(job)); + switch (prop_id) { case PROP_NAME: gncJobSetName(job, g_value_get_string(value)); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_set_kvp (QOF_INSTANCE (job), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -177,6 +196,19 @@ gnc_job_class_init (GncJobClass *klass) "by the GUI as the job mnemonic.", NULL, G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_PDF_DIRNAME, + g_param_spec_string ("export-pdf-dir", + "Export PDF Directory Name", + "A subdirectory for exporting PDF reports which is " + "appended to the target directory when writing them " + "out. It is retrieved from preferences and stored on " + "each 'Owner' object which prints items after " + "printing.", + NULL, + G_PARAM_READWRITE)); } /* Create/Destroy Functions */ diff --git a/src/engine/gncOrder.c b/src/engine/gncOrder.c index dd613c4b32f..2d36b54c889 100644 --- a/src/engine/gncOrder.c +++ b/src/engine/gncOrder.c @@ -25,10 +25,11 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include #include +#include #include "gncEntry.h" #include "gncEntryP.h" @@ -84,12 +85,14 @@ void mark_order (GncOrder *order) enum { PROP_0, - PROP_ID, - PROP_NOTES, - PROP_ACTIVE, - PROP_DATE_OPENED, - PROP_DATE_CLOSED, - PROP_REFERENCE + PROP_ID, /* Table */ + PROP_NOTES, /* Table */ + PROP_REFERENCE, /* Table */ + PROP_ACTIVE, /* Table */ + PROP_DATE_OPENED, /* Table */ + PROP_DATE_CLOSED, /* Table */ +// PROP_OWNER_TYPE, /* Table */ +// PROP_OWNER, /* Table */ }; /* GObject Initialization */ @@ -160,6 +163,8 @@ gnc_order_set_property (GObject *object, g_return_if_fail(GNC_IS_ORDER(object)); order = GNC_ORDER(object); + g_assert (qof_instance_get_editlevel(order)); + switch (prop_id) { case PROP_ID: diff --git a/src/engine/gncOwner.c b/src/engine/gncOwner.c index 8a1d0bf3abd..6fa69d4bdac 100644 --- a/src/engine/gncOwner.c +++ b/src/engine/gncOwner.c @@ -29,11 +29,12 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include #include #include /* for memcpy() */ +#include #include "gncCustomerP.h" #include "gncEmployeeP.h" @@ -50,8 +51,6 @@ #define _GNC_MOD_NAME GNC_ID_OWNER #define GNC_OWNER_ID "gncOwner" -#define GNC_OWNER_TYPE "owner-type" -#define GNC_OWNER_GUID "owner-guid" GncOwner * gncOwnerNew (void) { @@ -99,6 +98,36 @@ void gncOwnerBeginEdit (GncOwner *owner) } } +void gncOwnerCommitEdit (GncOwner *owner) +{ + if (!owner) return; + switch (owner->type) + { + case GNC_OWNER_NONE : + case GNC_OWNER_UNDEFINED : + break; + case GNC_OWNER_CUSTOMER : + { + gncCustomerCommitEdit(owner->owner.customer); + break; + } + case GNC_OWNER_JOB : + { + gncJobCommitEdit(owner->owner.job); + break; + } + case GNC_OWNER_VENDOR : + { + gncVendorCommitEdit(owner->owner.vendor); + break; + } + case GNC_OWNER_EMPLOYEE : + { + gncEmployeeCommitEdit(owner->owner.employee); + break; + } + } +} void gncOwnerDestroy (GncOwner *owner) { @@ -567,52 +596,31 @@ const GncGUID * gncOwnerGetEndGUID (const GncOwner *owner) void gncOwnerAttachToLot (const GncOwner *owner, GNCLot *lot) { - KvpFrame *kvp; - KvpValue *value; - - if (!owner || !lot) + if (!owner || !lot) return; - kvp = gnc_lot_get_slots (lot); gnc_lot_begin_edit (lot); - value = kvp_value_new_gint64 (gncOwnerGetType (owner)); - kvp_frame_set_slot_path (kvp, value, GNC_OWNER_ID, GNC_OWNER_TYPE, NULL); - kvp_value_delete (value); - - value = kvp_value_new_guid (gncOwnerGetGUID (owner)); - kvp_frame_set_slot_path (kvp, value, GNC_OWNER_ID, GNC_OWNER_GUID, NULL); - qof_instance_set_dirty (QOF_INSTANCE (lot)); + qof_instance_set (QOF_INSTANCE (lot), + "owner-type", (gint64)gncOwnerGetType (owner), + "owner-guid", gncOwnerGetGUID (owner), + NULL); gnc_lot_commit_edit (lot); - kvp_value_delete (value); - } gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner) { - KvpFrame *kvp; - KvpValue *value; - GncGUID *guid; + GncGUID *guid = NULL; QofBook *book; - GncOwnerType type; + GncOwnerType type = GNC_OWNER_NONE; if (!lot || !owner) return FALSE; book = gnc_lot_get_book (lot); - kvp = gnc_lot_get_slots (lot); - - value = kvp_frame_get_slot_path (kvp, GNC_OWNER_ID, GNC_OWNER_TYPE, NULL); - if (!value) return FALSE; - - type = kvp_value_get_gint64 (value); - - value = kvp_frame_get_slot_path (kvp, GNC_OWNER_ID, GNC_OWNER_GUID, NULL); - if (!value) return FALSE; - - guid = kvp_value_get_guid (value); - if (!guid) - return FALSE; - + qof_instance_get (QOF_INSTANCE (lot), + "owner-type", &type, + "owner-guid", &guid, + NULL); switch (type) { case GNC_OWNER_CUSTOMER: @@ -640,23 +648,6 @@ gboolean gncOwnerIsValid (const GncOwner *owner) return (owner->owner.undefined != NULL); } -KvpFrame* gncOwnerGetSlots(GncOwner* owner) -{ - if (!owner) return NULL; - - switch (gncOwnerGetType(owner)) - { - case GNC_OWNER_CUSTOMER: - case GNC_OWNER_VENDOR: - case GNC_OWNER_EMPLOYEE: - return qof_instance_get_slots(QOF_INSTANCE(owner->owner.undefined)); - case GNC_OWNER_JOB: - return gncOwnerGetSlots(gncJobGetOwner(gncOwnerGetJob(owner))); - default: - return NULL; - } -} - gboolean gncOwnerLotMatchOwnerFunc (GNCLot *lot, gpointer user_data) { diff --git a/src/engine/gncOwner.h b/src/engine/gncOwner.h index ac987bc3413..0874102fca3 100644 --- a/src/engine/gncOwner.h +++ b/src/engine/gncOwner.h @@ -197,9 +197,6 @@ gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner); gboolean gncOwnerGetOwnerFromTypeGuid (QofBook *book, GncOwner *owner, QofIdType type, GncGUID *guid); -/** Get the kvp-frame from the underlying owner object */ -KvpFrame* gncOwnerGetSlots(GncOwner* owner); - /** * Create a lot for a payment to the owner using the other * parameters passed in. If a transaction is set, this transaction will be @@ -307,6 +304,7 @@ void gncOwnerFree (GncOwner *owner); * without knowing its type. */ void gncOwnerBeginEdit (GncOwner *owner); +void gncOwnerCommitEdit (GncOwner *owner); void gncOwnerDestroy (GncOwner *owner); #endif /* GNC_OWNER_H_ */ diff --git a/src/engine/gncTaxTable.c b/src/engine/gncTaxTable.c index 8650217b3b3..2c525f4a5d0 100644 --- a/src/engine/gncTaxTable.c +++ b/src/engine/gncTaxTable.c @@ -26,9 +26,10 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include +#include #include "gnc-features.h" #include "gncTaxTableP.h" @@ -208,9 +209,10 @@ gncTaxTableRemoveChild (GncTaxTable *table, const GncTaxTable *child) enum { PROP_0, - PROP_NAME, - PROP_INVISIBLE, - PROP_REFCOUNT + PROP_NAME, /* Table */ + PROP_INVISIBLE, /* Table */ + PROP_REFCOUNT, /* Table */ +// PROP_PARENT, /* Table */ }; /* GObject Initialization */ @@ -272,6 +274,8 @@ gnc_taxtable_set_property (GObject *object, g_return_if_fail(GNC_IS_TAXTABLE(object)); tt = GNC_TAXTABLE(object); + g_assert (qof_instance_get_editlevel(tt)); + switch (prop_id) { case PROP_NAME: diff --git a/src/engine/gncVendor.c b/src/engine/gncVendor.c index d416db4627f..3ebdb2bb3e1 100644 --- a/src/engine/gncVendor.c +++ b/src/engine/gncVendor.c @@ -26,10 +26,11 @@ * Author: Derek Atkins */ -#include "config.h" +#include #include #include +#include #include "gnc-commodity.h" #include "gncAddressP.h" @@ -88,17 +89,20 @@ void mark_vendor (GncVendor *vendor) enum { PROP_0, - PROP_NAME, - PROP_ID, - PROP_NOTES, - PROP_CURRENCY, - PROP_ACTIVE, - PROP_TAXTABLE_OVERRIDE, - PROP_BILLTERMS, - PROP_TAXTABLE, - PROP_ADDRESS, - PROP_TAX_INCLUDED, - PROP_TAX_INCLUDED_STR + PROP_NAME, /* Table */ + PROP_ID, /* Table */ + PROP_NOTES, /* Table */ + PROP_CURRENCY, /* Table */ + PROP_ACTIVE, /* Table */ + PROP_TAXTABLE_OVERRIDE, /* Table */ + PROP_BILLTERMS, /* Table */ + PROP_TAXTABLE, /* Table */ + PROP_ADDRESS, /* Table, 8 fields */ + PROP_TAX_INCLUDED, /* Table */ + PROP_TAX_INCLUDED_STR, /* Alternate setter for PROP_TAX_INCLUDED */ + PROP_PDF_DIRNAME, /* KVP */ + PROP_LAST_POSTED, /* KVP */ + PROP_PAYMENT_LAST_ACCT, /* KVP */ }; /* GObject Initialization */ @@ -134,6 +138,7 @@ gnc_vendor_get_property (GObject *object, GParamSpec *pspec) { GncVendor *vendor; + gchar *key; g_return_if_fail(GNC_IS_VENDOR(object)); @@ -173,6 +178,18 @@ gnc_vendor_get_property (GObject *object, case PROP_TAX_INCLUDED_STR: g_value_set_string(value, qofVendorGetTaxIncluded(vendor)); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_get_kvp (QOF_INSTANCE (vendor), key, value); + break; + case PROP_LAST_POSTED: + key = LAST_POSTED_TO_ACCT; + qof_instance_get_kvp (QOF_INSTANCE (vendor), key, value); + break; + case PROP_PAYMENT_LAST_ACCT: + key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT; + qof_instance_get_kvp (QOF_INSTANCE (vendor), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -186,10 +203,13 @@ gnc_vendor_set_property (GObject *object, GParamSpec *pspec) { GncVendor *vendor; + gchar *key; g_return_if_fail(GNC_IS_VENDOR(object)); vendor = GNC_VENDOR(object); + g_assert (qof_instance_get_editlevel(vendor)); + switch (prop_id) { case PROP_NAME: @@ -225,6 +245,18 @@ gnc_vendor_set_property (GObject *object, case PROP_TAX_INCLUDED_STR: qofVendorSetTaxIncluded(vendor, g_value_get_string(value)); break; + case PROP_PDF_DIRNAME: + key = OWNER_EXPORT_PDF_DIRNAME; + qof_instance_set_kvp (QOF_INSTANCE (vendor), key, value); + break; + case PROP_LAST_POSTED: + key = LAST_POSTED_TO_ACCT; + qof_instance_set_kvp (QOF_INSTANCE (vendor), key, value); + break; + case PROP_PAYMENT_LAST_ACCT: + key = GNC_PAYMENT "/" GNC_LAST_ACCOUNT; + qof_instance_set_kvp (QOF_INSTANCE (vendor), key, value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -390,6 +422,38 @@ gnc_vendor_class_init (GncVendorClass *klass) "The tax-included-string property contains a character version of tax-included.", FALSE, G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_PDF_DIRNAME, + g_param_spec_string ("export-pdf-dir", + "Export PDF Directory Name", + "A subdirectory for exporting PDF reports which is " + "appended to the target directory when writing them " + "out. It is retrieved from preferences and stored on " + "each 'Owner' object which prints items after " + "printing.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_LAST_POSTED, + g_param_spec_boxed("invoice-last-posted-account", + "Invoice Last Posted Account", + "The last account to which an invoice belonging to " + "this owner was posted.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_PAYMENT_LAST_ACCT, + g_param_spec_boxed("payment-last-account", + "Payment Last Account", + "The last account to which an payment belonging to " + "this owner was posted.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); } /* Create/Destroy Functions */ diff --git a/src/engine/test-core/test-engine-stuff.c b/src/engine/test-core/test-engine-stuff.c index ad35d9b4443..77086f0f4df 100644 --- a/src/engine/test-core/test-engine-stuff.c +++ b/src/engine/test-core/test-engine-stuff.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "Account.h" #include "AccountP.h" @@ -1339,7 +1340,7 @@ get_random_split(QofBook *book, Account *acct, Transaction *trn) else g_assert(!gnc_numeric_positive_p(amt)); /* non-positive amt */ - xaccSplitSetSlots_nc(ret, get_random_kvp_frame()); + qof_instance_set_slots(QOF_INSTANCE (ret), get_random_kvp_frame()); xaccTransCommitEdit(trn); return ret; @@ -1366,7 +1367,7 @@ make_random_changes_to_split (Split *split) xaccSplitSetDateReconciledTS (split, ts); g_free(ts); - xaccSplitSetSlots_nc (split, get_random_kvp_frame()); + qof_instance_set_slots (QOF_INSTANCE (split), get_random_kvp_frame()); /* Don't change share values/prices here, since that would * throw transactions out of balance. Do that in the corresponding @@ -2169,13 +2170,13 @@ make_trans_query (Transaction *trans, TestQueryTypes query_types) } if (query_types & SPLIT_KVP_QT) - add_kvp_query (q, xaccSplitGetSlots (s), GNC_ID_SPLIT); + add_kvp_query (q, qof_instance_get_slots (QOF_INSTANCE (s)), GNC_ID_SPLIT); if (query_types & TRANS_KVP_QT) - add_kvp_query (q, xaccTransGetSlots (trans), GNC_ID_TRANS); + add_kvp_query (q, qof_instance_get_slots (QOF_INSTANCE (trans)), GNC_ID_TRANS); if (query_types & ACCOUNT_KVP_QT) - add_kvp_query (q, xaccAccountGetSlots (a), GNC_ID_ACCOUNT); + add_kvp_query (q, qof_instance_get_slots (QOF_INSTANCE (a)), GNC_ID_ACCOUNT); return q; } diff --git a/src/engine/test/Makefile.am b/src/engine/test/Makefile.am index ec7bed021d6..9e1f098dd64 100644 --- a/src/engine/test/Makefile.am +++ b/src/engine/test/Makefile.am @@ -116,6 +116,7 @@ test_engine_SOURCES = \ utest-Account.c \ utest-Budget.c \ utest-Invoice.c \ + test-engine-kvp-properties.c \ dummy.cpp test_engine_LDADD = \ diff --git a/src/engine/test/test-account-object.c b/src/engine/test/test-account-object.c index 60765b5721a..b87bb220332 100644 --- a/src/engine/test/test-account-object.c +++ b/src/engine/test/test-account-object.c @@ -33,6 +33,7 @@ #include "cashobjects.h" #include "test-stuff.h" #include "test-engine-stuff.h" +#include static void run_test (void) @@ -61,7 +62,9 @@ run_test (void) /*****/ five = gnc_numeric_create(5, 1); + qof_instance_increase_editlevel (acc); g_object_set(acc, "start-balance", &five, NULL); + qof_instance_decrease_editlevel (acc); xaccAccountRecomputeBalance(acc); g_object_get(acc, "start-balance", &start, "end-balance", &end, NULL); end2 = xaccAccountGetBalance(acc); diff --git a/src/engine/test/test-customer.c b/src/engine/test/test-customer.c index 02f67df3f82..fe8fb2f6b7e 100644 --- a/src/engine/test/test-customer.c +++ b/src/engine/test/test-customer.c @@ -24,9 +24,11 @@ * *********************************************************************/ -#include "config.h" +#include #include -#include "qof.h" +#include +#include + #include "cashobjects.h" #include "gncCustomerP.h" #include "gncInvoiceP.h" diff --git a/src/engine/test/test-employee.c b/src/engine/test/test-employee.c index 03bb0665f7e..1835bbf21c9 100644 --- a/src/engine/test/test-employee.c +++ b/src/engine/test/test-employee.c @@ -24,9 +24,11 @@ * *********************************************************************/ -#include "config.h" +#include #include -#include "qof.h" +#include +#include + #include "gncEmployeeP.h" #include "gncCustomerP.h" #include "gncJobP.h" diff --git a/src/engine/test/test-engine-kvp-properties.c b/src/engine/test/test-engine-kvp-properties.c new file mode 100644 index 00000000000..8b955267743 --- /dev/null +++ b/src/engine/test/test-engine-kvp-properties.c @@ -0,0 +1,458 @@ +/******************************************************************** + * test-engine-kvp-properties.c: GLib g_test test suite for * + * KVP-based properties in several engine classes. * + * Copyright 2013 John Ralls * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +/** + * Test Engine KVP Properties Acceptance testing for KVP Properties + * added to various engine classes to make private the internals of + * KVP storage used for a variety of parameters on several engine + * classes. + */ + +#include +#include +#include +#include +#include "../Transaction.h" +#include "../Split.h" +#include "../Account.h" +#include "../SchedXAction.h" +#include "../gncCustomer.h" +#include "../gncEmployee.h" +#include "../gncJob.h" +#include "../gncVendor.h" + +typedef struct +{ + union + { + Account *acct; + Transaction *trans; + Split *split; + GNCLot *lot; + GncCustomer *cust; + GncEmployee *emp; + GncJob *job; + GncVendor *vend; + }; + GSList *hdlrs; +} Fixture; + +/* Prototype to shut clang up */ +void test_suite_engine_kvp_properties (void); + +/* Private QofInstance functions needed for testing */ +extern void qof_instance_mark_clean (QofInstance*); + +const gchar *suitename = "/engine/kvp-properties"; + +static void +setup_account (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->acct = xaccMallocAccount (book); +} + +static void +setup_trans (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->trans = xaccMallocTransaction (book); +} + +static void +setup_split (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->split = xaccMallocSplit (book); +} + +static void +setup_lot (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->lot = gnc_lot_new (book); +} + +static void +setup_customer (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->cust = gncCustomerCreate (book); +} + +static void +setup_employee (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->emp = gncEmployeeCreate (book); +} + +static void +setup_job (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->job = gncJobCreate (book); +} + +static void +setup_vendor (Fixture *fixture, gconstpointer pData) +{ + QofBook *book = qof_book_new (); + fixture->vend = gncVendorCreate (book); +} + +static void +teardown (Fixture *fixture, gconstpointer pData) +{ +/* It doesn't actually matter which union member we use here, they're + * all QofInstances, so this will work for any of them. + */ + QofBook *book = qof_instance_get_book (QOF_INSTANCE (fixture->acct)); + test_destroy (fixture->acct); + test_destroy (book); +} + +static void +test_account_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + gint64 next_id = 12345678909876; + gint64 ab_acct_uid = 67890987654321; + gint64 next_id_r, ab_acct_uid_r; + gchar *online_id = "my online id"; + gchar *ab_acct_id = "1234-5678-9087"; + gchar *ab_bank_code = "0032340"; + gchar *online_id_r, *ab_acct_id_r, *ab_bank_code_r; + GncGUID *ofx_income_acct = guid_malloc (); + GncGUID *ofx_income_acct_r; + Timespec trans_retr = timespec_now (); + Timespec *trans_retr_r; + + xaccAccountBeginEdit (fixture->acct); + qof_instance_set (QOF_INSTANCE (fixture->acct), + "lot-next-id", next_id, + "online-id", online_id, + "ofx-income-account", ofx_income_acct, + "ab-account-id", ab_acct_id, + "ab-bank-code", ab_bank_code, + "ab-account-uid", ab_acct_uid, + "ab-trans-retrieval", &trans_retr, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->acct))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->acct)); + + qof_instance_get (QOF_INSTANCE (fixture->acct), + "lot-next-id", &next_id_r, + "online-id", &online_id_r, + "ofx-income-account", &ofx_income_acct_r, + "ab-account-id", &ab_acct_id_r, + "ab-bank-code", &ab_bank_code_r, + "ab-account-uid", &ab_acct_uid_r, + "ab-trans-retrieval", &trans_retr_r, + NULL); + g_assert_cmpint (next_id, ==, next_id_r); + g_assert_cmpstr (online_id, ==, online_id_r); + g_assert (guid_equal (ofx_income_acct, ofx_income_acct_r)); + g_assert_cmpstr (ab_acct_id, ==, ab_acct_id_r); + g_assert_cmpstr (ab_bank_code, ==, ab_bank_code_r); + g_assert_cmpint (ab_acct_uid, ==, ab_acct_uid_r); + g_assert (timespec_equal (&trans_retr, trans_retr_r)); + g_assert (!qof_instance_is_dirty (QOF_INSTANCE (fixture->acct))); +} + +static void +test_trans_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + GncGUID *invoice = guid_malloc (); + GncGUID *from_sx = guid_malloc (); + GncGUID *invoice_r, *from_sx_r; + gchar *online_id = "my online id"; + gchar *online_id_r; + + xaccTransBeginEdit (fixture->trans); + qof_instance_set (QOF_INSTANCE (fixture->trans), + "invoice", invoice, + "from-sched-xaction", from_sx, + "online-id", online_id, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->trans))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->trans)); + + qof_instance_get (QOF_INSTANCE (fixture->trans), + "invoice", &invoice_r, + "from-sched-xaction", &from_sx_r, + "online-id", &online_id_r, + NULL); + g_assert (guid_equal (invoice, invoice_r)); + g_assert (guid_equal (from_sx, from_sx_r)); + g_assert_cmpstr (online_id, ==, online_id_r); + g_assert (!qof_instance_is_dirty (QOF_INSTANCE (fixture->trans))); + guid_free (invoice); + guid_free (invoice_r); + guid_free (from_sx); + guid_free (from_sx_r); + g_free (online_id_r); +} + +static void +test_split_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + gchar *debit_formula = "e^xdydx"; + gchar *credit_formula = "seccostansin"; + gchar *sx_shares = "43"; + gchar *online_id = "my_online_id"; + gchar *debit_formula_r, *credit_formula_r, *sx_shares_r; + gchar *online_id_r; + GncGUID *sx_account = guid_malloc (); + GncGUID *sx_account_r; + gnc_numeric debit_numeric = gnc_numeric_create (123, 456); + gnc_numeric credit_numeric = gnc_numeric_create (789, 456); + gnc_numeric *debit_numeric_r, *credit_numeric_r; + + qof_begin_edit (QOF_INSTANCE (fixture->split)); + qof_instance_set (QOF_INSTANCE (fixture->split), + "sx-debit-formula", debit_formula, + "sx-debit-numeric", &debit_numeric, + "sx-credit-formula", credit_formula, + "sx-credit-numeric", &credit_numeric, + "sx-account", sx_account, + "sx-shares", sx_shares, + "online-id", online_id, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->split))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->split)); + + qof_instance_get (QOF_INSTANCE (fixture->split), + "sx-debit-formula", &debit_formula_r, + "sx-debit-numeric", &debit_numeric_r, + "sx-credit-formula", &credit_formula_r, + "sx-credit-numeric", &credit_numeric_r, + "sx-account", &sx_account_r, + "sx-shares", &sx_shares_r, + "online-id", &online_id_r, + NULL); + g_assert_cmpstr (debit_formula, ==, debit_formula_r); + g_assert (gnc_numeric_equal (debit_numeric, *debit_numeric_r)); + g_assert_cmpstr (credit_formula, ==, credit_formula_r); + g_assert (gnc_numeric_equal (credit_numeric, *credit_numeric_r)); + g_assert (guid_equal (sx_account, sx_account_r)); + g_assert_cmpstr (sx_shares, ==, sx_shares_r); + g_assert_cmpstr (online_id, ==, online_id_r); + g_assert (!qof_instance_is_dirty (QOF_INSTANCE (fixture->split))); + g_free (debit_formula_r); + g_free (debit_numeric_r); + g_free (credit_formula_r); + g_free (credit_numeric_r); + qof_begin_edit (QOF_INSTANCE (fixture->split)); + qof_instance_set (QOF_INSTANCE (fixture->split), + "sx-credit-formula", NULL, + NULL); + qof_instance_get (QOF_INSTANCE (fixture->split), + "sx-credit-formula", &credit_numeric_r, + NULL); + g_assert (credit_numeric_r == NULL); + g_free (sx_shares_r); + g_free (online_id_r); + guid_free (sx_account); + guid_free (sx_account_r); +} + +static void +test_lot_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + GncGUID *invoice = guid_malloc (); + GncGUID *invoice_r; + gint64 owner_type = 47; + gint64 owner_type_r; + GncGUID *owner = guid_malloc (); + GncGUID *owner_r; + + qof_begin_edit (QOF_INSTANCE (fixture->lot)); + qof_instance_set (QOF_INSTANCE (fixture->lot), + "invoice", invoice, + "owner-type", owner_type, + "owner-guid", owner, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->lot))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->lot)); + + qof_instance_get (QOF_INSTANCE (fixture->lot), + "invoice", &invoice_r, + "owner-type", &owner_type_r, + "owner-guid", &owner_r, + NULL); + g_assert (guid_equal (invoice, invoice_r)); + g_assert_cmpint (owner_type, ==, owner_type_r); + g_assert (guid_equal (owner, owner_r)); + g_assert (!qof_instance_is_dirty (QOF_INSTANCE (fixture->lot))); + guid_free (invoice); + guid_free (invoice_r); + guid_free (owner); + guid_free (owner_r); +} + +static void +test_customer_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + gchar *pdf_dir = "/foo/bar/baz"; + gchar *pdf_dir_r; + GncGUID *inv_acct = guid_malloc (); + GncGUID *pmt_acct = guid_malloc (); + GncGUID *inv_acct_r, *pmt_acct_r; + + qof_begin_edit (QOF_INSTANCE (fixture->cust)); + qof_instance_set (QOF_INSTANCE (fixture->cust), + "export-pdf-dir", pdf_dir, + "invoice-last-posted-account", inv_acct, + "payment-last-account", pmt_acct, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->cust))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->cust)); + + qof_instance_get (QOF_INSTANCE (fixture->cust), + "export-pdf-dir", &pdf_dir_r, + "invoice-last-posted-account", &inv_acct_r, + "payment-last-account", &pmt_acct_r, + NULL); + + g_assert_cmpstr (pdf_dir, ==, pdf_dir_r); + g_assert (guid_equal (inv_acct, inv_acct_r)); + g_assert (guid_equal (pmt_acct, pmt_acct_r)); + guid_free (inv_acct); + guid_free (inv_acct_r); + guid_free (pmt_acct); + guid_free (pmt_acct_r); + g_free (pdf_dir_r); + +} + +static void +test_employee_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + gchar *pdf_dir = "/foo/bar/baz"; + gchar *pdf_dir_r; + GncGUID *inv_acct = guid_malloc (); + GncGUID *pmt_acct = guid_malloc (); + GncGUID *inv_acct_r, *pmt_acct_r; + + qof_begin_edit (QOF_INSTANCE (fixture->emp)); + qof_instance_set (QOF_INSTANCE (fixture->emp), + "export-pdf-dir", pdf_dir, + "invoice-last-posted-account", inv_acct, + "payment-last-account", pmt_acct, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->emp))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->emp)); + + qof_instance_get (QOF_INSTANCE (fixture->emp), + "export-pdf-dir", &pdf_dir_r, + "invoice-last-posted-account", &inv_acct_r, + "payment-last-account", &pmt_acct_r, + NULL); + + g_assert_cmpstr (pdf_dir, ==, pdf_dir_r); + g_assert (guid_equal (inv_acct, inv_acct_r)); + g_assert (guid_equal (pmt_acct, pmt_acct_r)); + guid_free (inv_acct); + guid_free (inv_acct_r); + guid_free (pmt_acct); + guid_free (pmt_acct_r); + g_free (pdf_dir_r); + +} + +static void +test_job_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + gchar *pdf_dir = "/foo/bar/baz"; + gchar *pdf_dir_r; + + qof_begin_edit (QOF_INSTANCE (fixture->job)); + qof_instance_set (QOF_INSTANCE (fixture->job), + "export-pdf-dir", pdf_dir, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->job))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->job)); + + qof_instance_get (QOF_INSTANCE (fixture->job), + "export-pdf-dir", &pdf_dir_r, + NULL); + + g_assert_cmpstr (pdf_dir, ==, pdf_dir_r); + g_free (pdf_dir_r); + +} + +static void +test_vendor_kvp_properties (Fixture *fixture, gconstpointer pData) +{ + gchar *pdf_dir = "/foo/bar/baz"; + gchar *pdf_dir_r; + GncGUID *inv_acct = guid_malloc (); + GncGUID *pmt_acct = guid_malloc (); + GncGUID *inv_acct_r, *pmt_acct_r; + + qof_begin_edit (QOF_INSTANCE (fixture->vend)); + qof_instance_set (QOF_INSTANCE (fixture->vend), + "export-pdf-dir", pdf_dir, + "invoice-last-posted-account", inv_acct, + "payment-last-account", pmt_acct, + NULL); + + g_assert (qof_instance_is_dirty (QOF_INSTANCE (fixture->vend))); + qof_instance_mark_clean (QOF_INSTANCE (fixture->vend)); + + qof_instance_get (QOF_INSTANCE (fixture->vend), + "export-pdf-dir", &pdf_dir_r, + "invoice-last-posted-account", &inv_acct_r, + "payment-last-account", &pmt_acct_r, + NULL); + + g_assert_cmpstr (pdf_dir, ==, pdf_dir_r); + g_assert (guid_equal (inv_acct, inv_acct_r)); + g_assert (guid_equal (pmt_acct, pmt_acct_r)); + guid_free (inv_acct); + guid_free (inv_acct_r); + guid_free (pmt_acct); + guid_free (pmt_acct_r); + g_free (pdf_dir_r); + +} + +void test_suite_engine_kvp_properties (void) +{ + GNC_TEST_ADD (suitename, "Account", Fixture, NULL, setup_account, test_account_kvp_properties, teardown); + GNC_TEST_ADD (suitename, "Transaction", Fixture, NULL, setup_trans, test_trans_kvp_properties, teardown); + GNC_TEST_ADD (suitename, "Split", Fixture, NULL, setup_split, test_split_kvp_properties, teardown); + GNC_TEST_ADD (suitename, "Lot", Fixture, NULL, setup_lot, test_lot_kvp_properties, teardown); + GNC_TEST_ADD (suitename, "Customer", Fixture, NULL, setup_customer, test_customer_kvp_properties, teardown); + GNC_TEST_ADD (suitename, "Employee", Fixture, NULL, setup_employee, test_employee_kvp_properties, teardown); + GNC_TEST_ADD (suitename, "Job", Fixture, NULL, setup_job, test_job_kvp_properties, teardown); + GNC_TEST_ADD (suitename, "Vendor", Fixture, NULL, setup_vendor, test_vendor_kvp_properties, teardown); +} diff --git a/src/engine/test/test-engine.c b/src/engine/test/test-engine.c index 508856e8a02..19221b80903 100644 --- a/src/engine/test/test-engine.c +++ b/src/engine/test/test-engine.c @@ -31,6 +31,7 @@ extern void test_suite_budget(); extern void test_suite_gncInvoice(); extern void test_suite_transaction(); extern void test_suite_split(); +extern void test_suite_engine_kvp_properties (void); int main (int argc, @@ -49,6 +50,7 @@ main (int argc, test_suite_gncInvoice(); test_suite_transaction(); test_suite_split(); + test_suite_engine_kvp_properties (); return g_test_run( ); } diff --git a/src/engine/test/test-job.c b/src/engine/test/test-job.c index b90950af5f8..9791b4f9642 100644 --- a/src/engine/test/test-job.c +++ b/src/engine/test/test-job.c @@ -24,9 +24,11 @@ * *********************************************************************/ -#include "config.h" +#include #include -#include "qof.h" +#include +#include + #include "gncJobP.h" #include "gncInvoiceP.h" #include "gncCustomerP.h" diff --git a/src/engine/test/test-vendor.c b/src/engine/test/test-vendor.c index 65adf3eaaef..c36ff72b1fa 100644 --- a/src/engine/test/test-vendor.c +++ b/src/engine/test/test-vendor.c @@ -24,8 +24,10 @@ * *********************************************************************/ -#include "config.h" +#include #include +#include + #include "gncInvoiceP.h" #include "gncCustomerP.h" #include "gncJobP.h" diff --git a/src/engine/test/utest-Account.c b/src/engine/test/utest-Account.c index 2a8fe336c6e..f96abf3f379 100644 --- a/src/engine/test/utest-Account.c +++ b/src/engine/test/utest-Account.c @@ -25,6 +25,7 @@ #include #include #include +#include /* Add specific headers for this class */ #include "../Account.h" #include "../AccountP.h" @@ -444,11 +445,11 @@ test_gnc_account_name_violations_errmsg () message = gnc_account_name_violations_errmsg (separator, nonames); g_assert (message == NULL); validation_message = g_strdup_printf ( - "The separator character \"%s\" is used in one or more account " - "names.\n\nThis will result in unexpected behaviour. " - "Either change the account names or choose another separator " - "character.\n\nBelow you will find the list of invalid account names:\n" - "%s", separator, account_list); + "The separator character \"%s\" is used in one or more account " + "names.\n\nThis will result in unexpected behaviour. " + "Either change the account names or choose another separator " + "character.\n\nBelow you will find the list of invalid account names:\n" + "%s", separator, account_list); message = gnc_account_name_violations_errmsg (separator, badnames); g_assert_cmpstr ( message, == , validation_message); g_free (validation_message); @@ -1356,6 +1357,8 @@ test_xaccAccountOrder ( ) g_assert (xaccAccountOrder (ab, aa) == 1); ab = xaccMallocAccount (book); + qof_instance_increase_editlevel (aa); + qof_instance_increase_editlevel (ab); g_object_set (G_OBJECT (aa), "code", "3333", "type", ACCT_TYPE_ASSET, @@ -1398,6 +1401,8 @@ test_xaccAccountOrder ( ) "name", "bar", NULL); g_assert_cmpint (xaccAccountOrder (aa, ab), < , 0); + qof_instance_decrease_editlevel (aa); + qof_instance_decrease_editlevel (ab); xaccAccountBeginEdit (aa); xaccAccountDestroy (aa); @@ -2157,7 +2162,9 @@ test_xaccAccountType_Stuff (void) g_assert_cmpstr (typestr_uc, == , typename); g_free (typestr_uc); + qof_instance_increase_editlevel (acc); g_object_set (acc, "type", type, NULL); + qof_instance_decrease_editlevel (acc); if (type == ACCT_TYPE_STOCK || type == ACCT_TYPE_MUTUAL || type == ACCT_TYPE_CURRENCY) g_assert (xaccAccountIsPriced (acc)); @@ -2401,11 +2408,13 @@ test_gnc_account_merge_children (Fixture *fixture, gconstpointer pData) */ sig4 = test_signal_new (QOF_INSTANCE (div), QOF_EVENT_MODIFY, NULL); sig5 = test_signal_new (QOF_INSTANCE (div1), QOF_EVENT_MODIFY, NULL); + qof_instance_increase_editlevel (div1); g_object_set (div1, "name", "div", NULL); + qof_instance_decrease_editlevel (div1); gnc_account_merge_children (taxable); g_assert_cmpint (gnc_account_n_descendants (taxable), == , taxable_desc - 1); test_signal_assert_hits (sig4, 1); - test_signal_assert_hits (sig5, 4); + test_signal_assert_hits (sig5, 3); test_signal_free (sig4); test_signal_free (sig5); gnc_account_merge_children (expense); diff --git a/src/engine/test/utest-Split.cpp b/src/engine/test/utest-Split.cpp index 17f062c2940..e4165e578c3 100644 --- a/src/engine/test/utest-Split.cpp +++ b/src/engine/test/utest-Split.cpp @@ -38,7 +38,7 @@ extern "C" #include #include #include -#include +#include #ifdef HAVE_GLIB_2_38 #define _Q "'" @@ -318,15 +318,15 @@ test_xaccDupeSplit (Fixture *fixture, gconstpointer pData) g_assert (split->gains_split != f_split->gains_split); } -/* xaccSplitClone +/* xaccSplitCloneNoKvp Split * -xaccSplitClone (const Split *s)// C: 1 +xaccSplitCloneNoKvp (const Split *s)// C: 1 */ static void -test_xaccSplitClone (Fixture *fixture, gconstpointer pData) +test_xaccSplitCloneNoKvp (Fixture *fixture, gconstpointer pData) { Split *f_split = fixture->split; - Split *split = xaccSplitClone (f_split); + Split *split = xaccSplitCloneNoKvp (f_split); g_assert (split != fixture->split); g_assert (qof_instance_get_guid (split) != qof_instance_get_guid (f_split)); @@ -339,7 +339,7 @@ test_xaccSplitClone (Fixture *fixture, gconstpointer pData) g_assert (split->lot == f_split->lot); g_assert_cmpstr (split->memo, ==, f_split->memo); g_assert_cmpstr (split->action, ==, f_split->action); - g_assert (kvp_frame_compare (split->inst.kvp_data, f_split->inst.kvp_data) == 0); + g_assert (kvp_frame_is_empty (split->inst.kvp_data)); g_assert_cmpint (split->reconciled, ==, f_split->reconciled); g_assert (timespec_equal (&(split->date_reconciled), &(f_split->date_reconciled))); g_assert (gnc_numeric_equal (split->value, f_split->value)); @@ -417,7 +417,7 @@ xaccSplitEqual(const Split *sa, const Split *sb,// C: 2 in 2 SCM: 1 static void test_xaccSplitEqual (Fixture *fixture, gconstpointer pData) { - Split *split1 = xaccSplitClone (fixture->split); + Split *split1 = xaccSplitCloneNoKvp (fixture->split); Split *split2 = xaccDupeSplit (fixture->split); gchar *msg01 = "[xaccSplitEqual] one is NULL"; gchar *msg02 = "[xaccSplitEqual] GUIDs differ"; @@ -468,9 +468,11 @@ test_xaccSplitEqual (Fixture *fixture, gconstpointer pData) split1->parent = fixture->split->parent; g_assert (xaccSplitEqual (fixture->split, split1, FALSE, TRUE, TRUE) == TRUE); /* Now set the GUIDs equal and see that the comparison passes */ + qof_instance_increase_editlevel (split1->parent); g_object_set (G_OBJECT (split1), "guid", qof_instance_get_guid (QOF_INSTANCE(fixture->split)), NULL); + qof_instance_increase_editlevel (split1->parent); g_assert (xaccSplitEqual (fixture->split, split1, TRUE, TRUE, TRUE) == TRUE); g_assert_cmpint (checkA.hits, ==, 3); g_assert_cmpint (checkB.hits, ==, 1); @@ -607,10 +609,12 @@ test_xaccSplitCommitEdit (Fixture *fixture, gconstpointer pData) g_assert_cmpint (checkB.hits, ==, 2); qof_instance_mark_clean (QOF_INSTANCE (fixture->split->parent)); + qof_instance_increase_editlevel (fixture->split->acc); g_object_set (fixture->split->acc, "sort-dirty", FALSE, "balance-dirty", FALSE, NULL); + qof_instance_decrease_editlevel (fixture->split->acc); qof_instance_set_dirty (QOF_INSTANCE (fixture->split)); xaccSplitCommitEdit (fixture->split); @@ -780,16 +784,6 @@ test_get_commodity_denom (Fixture *fixture, gconstpointer pData) fixture->split->acc = acc; g_assert_cmpint (fixture->func->get_commodity_denom (fixture->split), ==, denom); } -/* xaccSplitGetSlots -KvpFrame * -xaccSplitGetSlots (const Split * s)// C: 17 in 8 -Simple passthrough, no test. -*/ -// Not Used -/* xaccSplitSetSlots_nc -void -xaccSplitSetSlots_nc(Split *s, KvpFrame *frm)// -*/ /* xaccSplitSetSharePriceAndAmount void xaccSplitSetSharePriceAndAmount (Split *s, gnc_numeric price, gnc_numeric amt)// C: 1 @@ -1115,8 +1109,9 @@ test_xaccSplitOrder (Fixture *fixture, gconstpointer pData) { const char *slot_path; Split *split = fixture->split; - Split *o_split = xaccMallocSplit (xaccSplitGetBook (split)); - Transaction *o_txn = xaccMallocTransaction (xaccSplitGetBook (split)); + QofBook *book = xaccSplitGetBook (split); + Split *o_split = xaccMallocSplit (book); + Transaction *o_txn = xaccMallocTransaction (book); Transaction *txn = split->parent; g_assert_cmpint (xaccSplitOrder (split, split), ==, 0); @@ -1155,11 +1150,12 @@ test_xaccSplitOrder (Fixture *fixture, gconstpointer pData) */ /* create correct slot path */ - slot_path = (const char *) g_strconcat( KVP_OPTION_PATH, "/", - OPTION_SECTION_ACCOUNTS, "/", OPTION_NAME_NUM_FIELD_SOURCE, NULL ); - g_assert( slot_path != NULL ); g_test_message( "Testing with use-split-action-for-num set to true - t" ); - qof_book_set_string_option( xaccSplitGetBook (split), slot_path, "t" ); + qof_book_begin_edit (book); + qof_instance_set (QOF_INSTANCE (book), + "split-action-num-field", "t", + NULL); + qof_book_commit_edit (book); g_assert(qof_book_use_split_action_for_num_field(xaccSplitGetBook(split)) == TRUE); g_assert_cmpint (xaccSplitOrder (split, o_split), ==, -1); @@ -1171,7 +1167,11 @@ test_xaccSplitOrder (Fixture *fixture, gconstpointer pData) o_split->action = NULL; split->action = "foo"; o_split->parent = NULL; - qof_book_set_string_option( xaccSplitGetBook (split), slot_path, "f" ); + qof_book_begin_edit (book); + qof_instance_set (QOF_INSTANCE (book), + "split-action-num-field", "f", + NULL); + qof_book_commit_edit (book); g_assert(qof_book_use_split_action_for_num_field(xaccSplitGetBook(split)) == FALSE); split->parent = NULL; /* This should return > 0 because o_split has no memo string */ @@ -1772,7 +1772,6 @@ test_xaccSplitGetOtherSplit (Fixture *fixture, gconstpointer pData) Split *split2 = xaccMallocSplit (book); Account *acc2 = xaccMallocAccount (book); KvpValue *kvptrue = kvp_value_new_string ("t"); - KvpFrame *book_slots = qof_book_get_slots (book); g_assert (xaccSplitGetOtherSplit (NULL) == NULL); g_assert (xaccSplitGetOtherSplit (split1) == NULL); @@ -1801,9 +1800,11 @@ test_xaccSplitGetOtherSplit (Fixture *fixture, gconstpointer pData) g_assert (kvp_frame_get_slot (split->inst.kvp_data, "lot-split") == NULL); kvp_frame_set_slot (split1->inst.kvp_data, "lot-split", NULL); g_assert (kvp_frame_get_slot (split1->inst.kvp_data, "lot-split") == NULL); - kvp_frame_set_slot_path (book_slots, kvptrue, KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_TRADING_ACCOUNTS, NULL); + qof_book_begin_edit (book); + qof_instance_set (QOF_INSTANCE (book), + "trading-accts", "t", + NULL); + qof_book_commit_edit (book); g_assert (xaccTransUseTradingAccounts (txn)); g_assert (xaccSplitGetOtherSplit (split) == NULL); split2->acc = acc2; @@ -1882,7 +1883,7 @@ test_suite_split (void) GNC_TEST_ADD_FUNC (suitename, "gnc split set & get property", test_gnc_split_set_get_property); GNC_TEST_ADD (suitename, "xaccMallocSplit", Fixture, NULL, setup, test_xaccMallocSplit, teardown); GNC_TEST_ADD (suitename, "xaccDupeSplit", Fixture, NULL, setup, test_xaccDupeSplit, teardown); - GNC_TEST_ADD (suitename, "xaccSplitClone", Fixture, NULL, setup, test_xaccSplitClone, teardown); + GNC_TEST_ADD (suitename, "xaccSplitCloneNoKvp", Fixture, NULL, setup, test_xaccSplitCloneNoKvp, teardown); GNC_TEST_ADD (suitename, "mark split", Fixture, NULL, setup, test_mark_split, teardown); GNC_TEST_ADD (suitename, "xaccSplitEqualCheckBal", Fixture, NULL, setup, test_xaccSplitEqualCheckBal, teardown); GNC_TEST_ADD (suitename, "xaccSplitEqual", Fixture, NULL, setup, test_xaccSplitEqual, teardown); diff --git a/src/engine/test/utest-Transaction.c b/src/engine/test/utest-Transaction.c index 35330a47e5c..c2a963fa450 100644 --- a/src/engine/test/utest-Transaction.c +++ b/src/engine/test/utest-Transaction.c @@ -1,5 +1,5 @@ /******************************************************************** - * utest-Transaction.c: GLib g_test test suite for Transaction.c. * + * utest-Transaction.c: GLib g_test test suite for Transaction.c. * * Copyright 2012 John Ralls * * * * This program is free software; you can redistribute it and/or * @@ -33,7 +33,6 @@ #include "../gnc-lot.h" #include "../gnc-event.h" #include -#include #include #ifdef HAVE_GLIB_2_38 @@ -569,12 +568,12 @@ test_xaccTransSortSplits (Fixture *fixture, gconstpointer pData) xaccTransCommitEdit (txn); } -/* xaccDupeTransaction -Transaction * -xaccDupeTransaction (const Transaction *from)// Local: 1:0:0 +/* dupe_trans +static Transaction * +dupe_trans (const Transaction *from)// Local: 1:0:0 */ static void -test_xaccDupeTransaction (Fixture *fixture, gconstpointer pData) +test_dupe_trans (Fixture *fixture, gconstpointer pData) { Timespec posted = gnc_dmy2timespec (12, 7, 2011); Timespec entered = gnc_dmy2timespec (14, 7, 2011); @@ -587,7 +586,7 @@ test_xaccDupeTransaction (Fixture *fixture, gconstpointer pData) kvp_frame_set_string (old->inst.kvp_data, "/foo/bar/baz", "The Great Waldo Pepper"); - new = xaccDupeTransaction (old); + new = fixture->func->dupe_trans (old); g_assert_cmpstr (new->num, ==, old->num); g_assert_cmpstr (new->description, ==, old->description); @@ -1048,14 +1047,14 @@ test_xaccTransGetImbalance_trading (Fixture *fixture, Account *acc1 = xaccMallocAccount (book); Account *acc2 = xaccMallocAccount (book); gnc_numeric value; - gchar *trading_account_path = g_strdup_printf("%s/%s/%s", KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_TRADING_ACCOUNTS); MonetaryList *mlist; + qof_book_begin_edit (book); + qof_instance_set (QOF_INSTANCE (book), + "trading-accts", "t", + NULL); + qof_book_commit_edit (book); - qof_book_set_string_option( book, trading_account_path, "t" ); - g_free (trading_account_path); - /* Without trading splits, the list is unbalanced */ + /* Without trading splits, the list is unbalanced */ mlist = xaccTransGetImbalance (fixture->txn); g_assert_cmpint (g_list_length (mlist), ==, 2); gnc_monetary_list_free (mlist); @@ -1135,12 +1134,13 @@ test_xaccTransIsBalanced_trading (Fixture *fixture, gconstpointer pData) Split *split2 = xaccMallocSplit (book); Account *acc1 = xaccMallocAccount (book); Account *acc2 = xaccMallocAccount (book); - gchar *trading_account_path = g_strdup_printf("%s/%s/%s", KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_TRADING_ACCOUNTS); - qof_book_set_string_option( book, trading_account_path, "t" ); - g_free (trading_account_path); + qof_book_begin_edit (book); + qof_instance_set (QOF_INSTANCE (book), + "trading-accts", "t", + NULL); + qof_book_commit_edit (book); + xaccAccountSetCommodity (acc1, fixture->curr); xaccAccountSetCommodity (acc2, fixture->comm); xaccAccountSetType (acc1, ACCT_TYPE_TRADING); @@ -1398,29 +1398,27 @@ void xaccTransDestroy (Transaction *trans)// C: 26 in 15 SCM: 4 in 4 Local: 3:0:0 */ static void -test_xaccTransDestroy () +test_xaccTransDestroy (Fixture *fixture, gconstpointer pData) { - QofBook *book = qof_book_new (); - Transaction *txn = xaccMallocTransaction (book); - Transaction *dupe = xaccDupeTransaction (txn); + Transaction *txn = fixture->txn; + QofBook *book = qof_instance_get_book (QOF_INSTANCE (txn)); + Transaction *dupe = xaccTransClone (txn); xaccTransBeginEdit (txn); g_assert (!qof_instance_get_destroying (QOF_INSTANCE (txn))); - g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, TRUE, TRUE)); + g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, FALSE, TRUE)); xaccTransDestroy (txn); g_assert (qof_instance_get_destroying (QOF_INSTANCE (txn))); - g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, TRUE, TRUE)); + g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, FALSE, TRUE)); xaccTransRollbackEdit (txn); qof_book_mark_readonly (book); xaccTransBeginEdit (txn); xaccTransDestroy (txn); g_assert (qof_instance_get_destroying (QOF_INSTANCE (txn))); - g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, TRUE, TRUE)); + g_assert (xaccTransEqual (txn, dupe, FALSE, TRUE, FALSE, TRUE)); xaccTransRollbackEdit (txn); - test_destroy (txn); test_destroy (dupe); - test_destroy (book); } /* destroy_gains static void @@ -1645,10 +1643,6 @@ test_xaccTransCommitEdit (void) xaccTransSetCurrency (txn, curr); xaccSplitSetParent (split1, txn); xaccSplitSetParent (split2, txn); - /* xaccTransCommitEdit doesn't do anything with kvp - kvp_frame_set_double (frame, "/qux/quux/corge", 123.456); - qof_instance_set_slots (QOF_INSTANCE (txn), frame); - */ } /* Setup's done, now test: */ xaccTransCommitEdit (txn); @@ -1790,7 +1784,7 @@ static void test_xaccTransOrder_num_action (Fixture *fixture, gconstpointer pData) { Transaction *txnA = fixture->txn; - Transaction *txnB = xaccDupeTransaction (txnA); + Transaction *txnB = fixture->func->dupe_trans (txnA); g_assert_cmpint (xaccTransOrder_num_action (txnA, NULL, NULL, NULL), ==, -1); g_assert_cmpint (xaccTransOrder_num_action (NULL, NULL, txnA, NULL), ==, 1); @@ -2042,7 +2036,7 @@ test_suite_transaction (void) GNC_TEST_ADD (suitename, "gnc transaction set/get property", Fixture, NULL, setup, test_gnc_transaction_set_get_property, teardown); GNC_TEST_ADD (suitename, "xaccMallocTransaction", Fixture, NULL, setup, test_xaccMallocTransaction, teardown); GNC_TEST_ADD (suitename, "xaccTransSortSplits", Fixture, NULL, setup, test_xaccTransSortSplits, teardown); - GNC_TEST_ADD (suitename, "xaccDupeTransaction", Fixture, NULL, setup, test_xaccDupeTransaction, teardown); + GNC_TEST_ADD (suitename, "dupe_trans", Fixture, NULL, setup, test_dupe_trans, teardown); GNC_TEST_ADD (suitename, "xaccTransClone", Fixture, NULL, setup, test_xaccTransClone, teardown); GNC_TEST_ADD (suitename, "xaccTransCopyFromClipBoard", Fixture, NULL, setup, test_xaccTransCopyFromClipBoard, teardown); GNC_TEST_ADD (suitename, "xaccTransCopyFromClipBoard No-Start", Fixture, NULL, setup, test_xaccTransCopyFromClipBoard_no_start, teardown); @@ -2063,7 +2057,7 @@ test_suite_transaction (void) GNC_TEST_ADD (suitename, "xaccTransSetCurrency", Fixture, NULL, setup, test_xaccTransSetCurrency, teardown); GNC_TEST_ADD_FUNC (suitename, "xaccTransBeginEdit", test_xaccTransBeginEdit); - GNC_TEST_ADD_FUNC (suitename, "xaccTransDestroy", test_xaccTransDestroy); + GNC_TEST_ADD (suitename, "xaccTransDestroy", Fixture, NULL, setup, test_xaccTransDestroy, teardown); GNC_TEST_ADD (suitename, "destroy gains", GainsFixture, NULL, setup_with_gains, test_destroy_gains, teardown_with_gains); GNC_TEST_ADD (suitename, "do destroy", GainsFixture, NULL, setup_with_gains, test_do_destroy, teardown_with_gains); GNC_TEST_ADD (suitename, "was trans emptied", Fixture, NULL, setup, test_was_trans_emptied, teardown); diff --git a/src/gnome-utils/dialog-preferences.c b/src/gnome-utils/dialog-preferences.c index 53f2e94849e..4e371be5bf1 100644 --- a/src/gnome-utils/dialog-preferences.c +++ b/src/gnome-utils/dialog-preferences.c @@ -1054,7 +1054,7 @@ gnc_preferences_dialog_create(void) GtkWidget *dialog, *notebook, *label, *image; GtkWidget *box, *date, *period, *currency; GHashTable *prefs_table; - GDate* gdate; + GDate* gdate = NULL; gchar buf[128]; GtkListStore *store; GtkTreePath *path; @@ -1120,16 +1120,10 @@ gnc_preferences_dialog_create(void) book = gnc_get_current_book(); - book_frame = qof_book_get_slots(book); - month = kvp_frame_get_gint64(book_frame, "/book/fyear_end/month"); - day = kvp_frame_get_gint64(book_frame, "/book/fyear_end/day"); - date_is_valid = g_date_valid_dmy(day, month, 2005 /* not leap year */); - if (date_is_valid) - { - g_date_clear(&fy_end, 1); - g_date_set_dmy(&fy_end, day, month, G_DATE_BAD_YEAR); - } - + g_date_clear (&fy_end, 1); + qof_instance_get (QOF_INSTANCE (book), + "fy-end", &fy_end, + NULL); box = GTK_WIDGET(gtk_builder_get_object (builder, "pref/" GNC_PREFS_GROUP_ACCT_SUMMARY "/" GNC_PREF_START_PERIOD)); period = gnc_period_select_new(TRUE); diff --git a/src/gnome-utils/gnc-main-window.c b/src/gnome-utils/gnc-main-window.c index 4ab4257e1d2..923c877565d 100644 --- a/src/gnome-utils/gnc-main-window.c +++ b/src/gnome-utils/gnc-main-window.c @@ -3973,20 +3973,21 @@ gnc_book_options_dialog_apply_cb(GNCOptionWin * optionwin, gpointer user_data) { GNCOptionDB * options = user_data; - kvp_frame *slots = qof_book_get_slots (gnc_get_current_book ()); gboolean use_split_action_for_num_before = qof_book_use_split_action_for_num_field (gnc_get_current_book ()); gboolean use_split_action_for_num_after; + QofBook *book = gnc_get_current_book (); if (!options) return; gnc_option_db_commit (options); - gnc_option_db_save_to_kvp (options, slots, TRUE); - qof_book_kvp_changed (gnc_get_current_book()); + qof_book_begin_edit (book); + qof_book_save_options (book, gnc_option_db_save_to_kvp, options, TRUE); use_split_action_for_num_after = qof_book_use_split_action_for_num_field (gnc_get_current_book ()); if (use_split_action_for_num_before != use_split_action_for_num_after) gnc_book_option_num_field_source_change_cb (use_split_action_for_num_after); + qof_book_commit_edit (book); } static void @@ -4002,12 +4003,12 @@ gnc_book_options_dialog_close_cb(GNCOptionWin * optionwin, GtkWidget * gnc_book_options_dialog_cb (gboolean modal, gchar *title) { - kvp_frame *slots = qof_book_get_slots (gnc_get_current_book ()); + QofBook *book = gnc_get_current_book (); GNCOptionDB *options; GNCOptionWin *optionwin; options = gnc_option_db_new_for_type (QOF_ID_BOOK); - gnc_option_db_load_from_kvp (options, slots); + qof_book_load_options (book, gnc_option_db_load_from_kvp, options); gnc_option_db_clean (options); optionwin = gnc_options_dialog_new_modal (modal, diff --git a/src/gnome-utils/gnc-tree-util-split-reg.c b/src/gnome-utils/gnc-tree-util-split-reg.c index 88c4ee3376e..d6e574760e4 100644 --- a/src/gnome-utils/gnc-tree-util-split-reg.c +++ b/src/gnome-utils/gnc-tree-util-split-reg.c @@ -397,30 +397,20 @@ const char * gnc_tree_util_split_reg_template_get_transfer_entry (Split *split) { static char *name = NULL; + Account *account; + GncGUID *guid = NULL; - kvp_frame *kvpf; + /* Callers either g_strdup the return or use it as a temp for comparison, + so we keep our static ref and free it on every call. */ + g_free (name); if (!split) return NULL; - - kvpf = xaccSplitGetSlots (split); - - g_free (name); - - if (kvpf) - { - Account *account; - GncGUID *guid; - - guid = kvp_value_get_guid( - kvp_frame_get_slot_path (kvpf, "sched-xaction", "account", NULL)); - - account = xaccAccountLookup (guid, gnc_get_current_book ()); - - name = account ? gnc_get_account_name_for_register (account) : NULL; - } - else - name = NULL; + qof_instance_get (QOF_INSTANCE (split), + "sx-account", &guid, + NULL); + account = xaccAccountLookup (guid, gnc_get_current_book ()); + name = account ? gnc_get_account_name_for_register (account) : NULL; return name; } @@ -429,20 +419,27 @@ gnc_tree_util_split_reg_template_get_transfer_entry (Split *split) const char * gnc_tree_util_split_reg_template_get_fdebt_entry (Split *split) { - kvp_frame *kvpf = xaccSplitGetSlots (split); + gchar *formula = NULL; - return kvp_value_get_string( - kvp_frame_get_slot_path (kvpf, "sched-xaction", "debit-formula", NULL)); -} + g_return_val_if_fail (split != NULL, NULL); + qof_instance_get (QOF_INSTANCE (split), + "sx-debit-formula", &formula, + NULL); + return formula; +} const char * gnc_tree_util_split_reg_template_get_fcred_entry (Split *split) { - kvp_frame *kvpf = xaccSplitGetSlots (split); + gchar *formula = NULL; + + g_return_val_if_fail (split != NULL, NULL); + qof_instance_get (QOF_INSTANCE (split), + "sx-credit-formula", &formula, + NULL); - return kvp_value_get_string( - kvp_frame_get_slot_path (kvpf, "sched-xaction", "credit-formula", NULL)); + return formula; } diff --git a/src/gnome-utils/gnc-tree-view-account.c b/src/gnome-utils/gnc-tree-view-account.c index d1912e5ae0c..bde9de59779 100644 --- a/src/gnome-utils/gnc-tree-view-account.c +++ b/src/gnome-utils/gnc-tree-view-account.c @@ -1730,43 +1730,44 @@ gtva_currency_changed_cb (void) gtva_update_column_names (ptr->data); } } -/* This function implements a custom mapping between an account's KVP - * and the cell renderer's 'text' property. */ +/* Retrieve a specified account string property and put the result + * into the tree column's text property. + */ static void -account_cell_kvp_data_func (GtkTreeViewColumn *tree_column, - GtkCellRenderer *cell, - GtkTreeModel *s_model, - GtkTreeIter *s_iter, - gpointer key) +account_cell_property_data_func (GtkTreeViewColumn *tree_column, + GtkCellRenderer *cell, + GtkTreeModel *s_model, + GtkTreeIter *s_iter, + gpointer key) { Account *account; - kvp_frame * frame; + gchar *string = NULL; g_return_if_fail (GTK_IS_TREE_MODEL_SORT (s_model)); account = gnc_tree_view_account_get_account_from_iter(s_model, s_iter); - frame = xaccAccountGetSlots(account); - - g_object_set (G_OBJECT (cell), - "text", kvp_frame_get_string(frame, (gchar *)key), - "xalign", 0.0, - NULL); + qof_instance_get (QOF_INSTANCE (account), + key, &string, + NULL); + if (string == NULL) + string = ""; + g_object_set (G_OBJECT (cell), "text", string, "xalign", 0.0, NULL); } GtkTreeViewColumn * -gnc_tree_view_account_add_kvp_column (GncTreeViewAccount *view, +gnc_tree_view_account_add_property_column (GncTreeViewAccount *view, const gchar *column_title, - const gchar *kvp_key) + const gchar *propname) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; g_return_val_if_fail (GNC_IS_TREE_VIEW_ACCOUNT (view), NULL); - g_return_val_if_fail (kvp_key != NULL, NULL); + g_return_val_if_fail (propname != NULL, NULL); column = gnc_tree_view_add_text_column(GNC_TREE_VIEW(view), column_title, - kvp_key, NULL, "Sample text", + propname, NULL, "Sample text", -1, -1, NULL); /* This new kvp column has only had one renderer added to it so @@ -1775,8 +1776,8 @@ gnc_tree_view_account_add_kvp_column (GncTreeViewAccount *view, g_object_set (G_OBJECT (renderer), "xalign", 1.0, NULL); gtk_tree_view_column_set_cell_data_func (column, renderer, - account_cell_kvp_data_func, - g_strdup(kvp_key), g_free); + account_cell_property_data_func, + g_strdup(propname), g_free); return column; } diff --git a/src/gnome-utils/gnc-tree-view-account.h b/src/gnome-utils/gnc-tree-view-account.h index 87778cd490d..5088b75c79e 100644 --- a/src/gnome-utils/gnc-tree-view-account.h +++ b/src/gnome-utils/gnc-tree-view-account.h @@ -203,20 +203,19 @@ void gnc_tree_view_account_notes_edited_cb(Account *account, GtkTreeViewColumn * /** Add a new column to the set of columns in an account tree view. * This column will be visible as soon as it is added and will - * display the contents of the specified KVP slot. + * display the contents of the specified account property * * @param view A pointer to an account tree view. * * @param column_title The title for this new column. * - * @param kvp_key The lookup key to use for looking up data in the - * account KVP structures. The value associated with this key is what - * will be displayed in the column. + * @param propname The g_object_property name of the desired + * value. This must be a string property. */ GtkTreeViewColumn * -gnc_tree_view_account_add_kvp_column (GncTreeViewAccount *view, - const gchar *column_title, - const gchar *kvp_key); +gnc_tree_view_account_add_property_column (GncTreeViewAccount *view, + const gchar *column_title, + const gchar *propname); /** @} */ diff --git a/src/gnome-utils/gnc-tree-view-split-reg.c b/src/gnome-utils/gnc-tree-view-split-reg.c index 74d4b98e2b2..c1ff190e99c 100644 --- a/src/gnome-utils/gnc-tree-view-split-reg.c +++ b/src/gnome-utils/gnc-tree-view-split-reg.c @@ -4801,9 +4801,8 @@ gtv_sr_edited_template_cb (GtkCellRendererText *cell, const gchar *path_string, if (viewcol == COL_TRANSFERVOID) { Account *template_acc; + Account *acct; const GncGUID *acctGUID; - kvp_frame *kvpf; - Account *acct; /* save the account GncGUID into the kvp_data. */ view->priv->stop_cell_move = FALSE; @@ -4822,9 +4821,9 @@ gtv_sr_edited_template_cb (GtkCellRendererText *cell, const gchar *path_string, } acctGUID = xaccAccountGetGUID (acct); - kvpf = xaccSplitGetSlots (split); - kvp_frame_set_slot_path (kvpf, kvp_value_new_guid (acctGUID), - GNC_SX_ID, GNC_SX_ACCOUNT, NULL); + qof_instance_set (QOF_INSTANCE (split), + "sx-account", acctGUID, + NULL); template_acc = gnc_tree_model_split_reg_get_template_account (model); @@ -4844,20 +4843,11 @@ gtv_sr_edited_template_cb (GtkCellRendererText *cell, const gchar *path_string, /* Setup the debit and credit fields */ if (viewcol == COL_DEBIT) { - kvp_frame *kvpf; char *error_loc; gnc_numeric new_value; gboolean parse_result; - kvpf = xaccSplitGetSlots (split); - - DEBUG ("kvp_frame debit before: %s\n", kvp_frame_to_string (kvpf)); - /* Setup the debit formula */ - kvp_frame_set_slot_path (kvpf, kvp_value_new_string (new_text), - GNC_SX_ID, - GNC_SX_DEBIT_FORMULA, - NULL); /* If the value can be parsed into a numeric result, store that * numeric value additionally. See above comment.*/ @@ -4866,45 +4856,22 @@ gtv_sr_edited_template_cb (GtkCellRendererText *cell, const gchar *path_string, { new_value = gnc_numeric_zero(); } - kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_value), - GNC_SX_ID, - GNC_SX_DEBIT_NUMERIC, - NULL); - - DEBUG ("kvp_frame debit after: %s\n", kvp_frame_to_string (kvpf)); - - /* Blank the credit formula */ - kvp_frame_set_slot_path (kvpf, kvp_value_new_string (NULL), - GNC_SX_ID, - GNC_SX_CREDIT_FORMULA, - NULL); - - new_value = gnc_numeric_zero(); - kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_value), - GNC_SX_ID, - GNC_SX_CREDIT_NUMERIC, - NULL); + qof_instance_set (QOF_INSTANCE (split), + "sx-debit-formula", new_text, + "sx-debit-numeric", &new_value, + "sx-credit-formula", NULL, + "sx-credit-numeric", NULL, + NULL); } /* Setup the debit and credit fields */ if (viewcol == COL_CREDIT) { - kvp_frame *kvpf; char *error_loc; gnc_numeric new_value; gboolean parse_result; - kvpf = xaccSplitGetSlots (split); - - DEBUG ("kvp_frame credit before: %s\n", kvp_frame_to_string (kvpf)); - - /* Setup the credit formula */ - kvp_frame_set_slot_path (kvpf, kvp_value_new_string (new_text), - GNC_SX_ID, - GNC_SX_CREDIT_FORMULA, - NULL); - - /* If the value can be parsed into a numeric result (without any + /* If the value can be parsed into a numeric result (without any * further variable definitions), store that numeric value * additionally in the kvp. Otherwise store a zero numeric * there.*/ @@ -4913,24 +4880,12 @@ gtv_sr_edited_template_cb (GtkCellRendererText *cell, const gchar *path_string, { new_value = gnc_numeric_zero(); } - kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_value), - GNC_SX_ID, - GNC_SX_CREDIT_NUMERIC, - NULL); - - DEBUG ("kvp_frame credit after: %s\n", kvp_frame_to_string (kvpf)); - - /* Blank the debit formula */ - kvp_frame_set_slot_path (kvpf, kvp_value_new_string (NULL), - GNC_SX_ID, - GNC_SX_DEBIT_FORMULA, - NULL); - - new_value = gnc_numeric_zero(); - kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_value), - GNC_SX_ID, - GNC_SX_DEBIT_NUMERIC, - NULL); + qof_instance_set (QOF_INSTANCE (split), + "sx-credit-formula", new_text, + "sx-credit-numeric", &new_value, + "sx-debit-formula", NULL, + "sx-debit-numeric", NULL, + NULL); } /* set the amount to an innocuous value */ xaccSplitSetValue (split, gnc_numeric_create (0, 1)); diff --git a/src/gnome/assistant-hierarchy.c b/src/gnome/assistant-hierarchy.c index da0d5af6be0..23b98f87451 100644 --- a/src/gnome/assistant-hierarchy.c +++ b/src/gnome/assistant-hierarchy.c @@ -1008,18 +1008,17 @@ finish_book_options_helper(GNCOptionWin * optionwin, gpointer user_data) { GNCOptionDB * options = user_data; - kvp_frame *slots = qof_book_get_slots (gnc_get_current_book ()); + QofBook *book = gnc_get_current_book (); gboolean use_split_action_for_num_before = - qof_book_use_split_action_for_num_field (gnc_get_current_book ()); + qof_book_use_split_action_for_num_field (book); gboolean use_split_action_for_num_after; if (!options) return; gnc_option_db_commit (options); - gnc_option_db_save_to_kvp (options, slots, TRUE); - qof_book_kvp_changed (gnc_get_current_book()); + qof_book_save_options (book, gnc_option_db_save_to_kvp, options, TRUE); use_split_action_for_num_after = - qof_book_use_split_action_for_num_field (gnc_get_current_book ()); + qof_book_use_split_action_for_num_field (book); if (use_split_action_for_num_before != use_split_action_for_num_after) gnc_book_option_num_field_source_change_cb (use_split_action_for_num_after); } @@ -1119,11 +1118,11 @@ book_options_dialog_close_cb(GNCOptionWin * optionwin, static void assistant_instert_book_options_page (hierarchy_data *data) { - kvp_frame *slots = qof_book_get_slots (gnc_get_current_book ()); GtkWidget *vbox = gtk_vbox_new (FALSE, 0); data->options = gnc_option_db_new_for_type (QOF_ID_BOOK); - gnc_option_db_load_from_kvp (data->options, slots); + qof_book_load_options (gnc_get_current_book (), + gnc_option_db_load_from_kvp, data->options); gnc_option_db_clean (data->options); data->optionwin = gnc_options_dialog_new_modal (TRUE, _("New Book Options")); diff --git a/src/gnome/dialog-sx-editor.c b/src/gnome/dialog-sx-editor.c index b3fb1e85a61..4b7b95f52a1 100644 --- a/src/gnome/dialog-sx-editor.c +++ b/src/gnome/dialog-sx-editor.c @@ -549,9 +549,7 @@ gnc_sxed_check_consistent( GncSxEditorDialog *sxed ) int numIters, i; GHashTable *vars, *txns; GList *splitList = NULL; - char *str; - kvp_frame *f; - kvp_value *v; + gchar *credit_formula = NULL, *debit_formula = NULL; Split *s; Transaction *t; gnc_numeric tmp; @@ -559,8 +557,10 @@ gnc_sxed_check_consistent( GncSxEditorDialog *sxed ) gpointer unusedKey, unusedValue; unbalanceable = FALSE; /* innocent until proven guilty */ - vars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)gnc_sx_variable_free); - txns = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + vars = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)gnc_sx_variable_free); + txns = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, + g_free); numIters = NUM_ITERS_NO_VARS; /** * Plan: @@ -604,10 +604,10 @@ gnc_sxed_check_consistent( GncSxEditorDialog *sxed ) for ( ; splitList; splitList = splitList->next ) { - GncGUID *acct_guid; - Account *acct; - gnc_commodity *split_cmdty; - txnCreditDebitSums *tcds; + GncGUID *acct_guid = NULL; + Account *acct = NULL; + gnc_commodity *split_cmdty = NULL; + txnCreditDebitSums *tcds = NULL; s = (Split*)splitList->data; t = xaccSplitGetParent( s ); @@ -622,14 +622,11 @@ gnc_sxed_check_consistent( GncSxEditorDialog *sxed ) g_hash_table_insert( txns, (gpointer)t, (gpointer)tcds ); } - f = xaccSplitGetSlots( s ); - - /* contains the guid of the split's actual account. */ - v = kvp_frame_get_slot_path(f, - GNC_SX_ID, - GNC_SX_ACCOUNT, - NULL); - acct_guid = kvp_value_get_guid( v ); + qof_instance_get (QOF_INSTANCE (s), + "sx-account", &acct_guid, + "sx-credit-formula", &credit_formula, + "sx-debit-formula", &debit_formula, + NULL); acct = xaccAccountLookup( acct_guid, gnc_get_current_book ()); split_cmdty = xaccAccountGetCommodity(acct); if (base_cmdty == NULL) @@ -638,62 +635,48 @@ gnc_sxed_check_consistent( GncSxEditorDialog *sxed ) } multi_commodity |= !gnc_commodity_equal(split_cmdty, base_cmdty); - v = kvp_frame_get_slot_path( f, - GNC_SX_ID, - GNC_SX_CREDIT_FORMULA, - NULL ); - if ( v - && (str = kvp_value_get_string(v)) - && strlen( str ) != 0 ) - { - if ( gnc_sx_parse_vars_from_formula( str, vars, &tmp ) < 0 ) - { - GString *errStr; - - errStr = g_string_sized_new( 32 ); - g_string_printf( errStr, - _( "Couldn't parse credit formula for " - "split \"%s\"." ), - xaccSplitGetMemo( s ) ); - gnc_error_dialog( GTK_WIDGET(sxed->dialog), "%s", - errStr->str ); - g_string_free( errStr, TRUE ); - - return FALSE; - } - tcds->creditSum = - gnc_numeric_add( tcds->creditSum, tmp, 100, - (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) ); - tmp = gnc_numeric_zero(); - } - v = kvp_frame_get_slot_path( f, - GNC_SX_ID, - GNC_SX_DEBIT_FORMULA, - NULL ); - if ( v - && (str = kvp_value_get_string(v)) - && strlen(str) != 0 ) - { - if ( gnc_sx_parse_vars_from_formula( str, vars, &tmp ) < 0 ) - { - GString *errStr; - - errStr = g_string_sized_new( 32 ); - g_string_printf( errStr, - _( "Couldn't parse debit formula for " - "split \"%s\"." ), - xaccSplitGetMemo( s ) ); - gnc_error_dialog( GTK_WIDGET(sxed->dialog), "%s", - (gchar*)errStr->str ); - g_string_free( errStr, TRUE ); - - return FALSE; - } - tcds->debitSum = gnc_numeric_add( tcds->debitSum, tmp, 100, - (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) ); - tmp = gnc_numeric_zero(); - } - } + if ( g_strcmp0 (credit_formula, "") != 0 && + gnc_sx_parse_vars_from_formula(credit_formula, vars, + &tmp ) < 0 ) + { + GString *errStr; + + errStr = g_string_sized_new( 32 ); + g_string_printf( errStr, + _( "Couldn't parse credit formula for " + "split \"%s\"." ), + xaccSplitGetMemo( s ) ); + gnc_error_dialog( GTK_WIDGET(sxed->dialog), "%s", + errStr->str ); + g_string_free( errStr, TRUE ); + + return FALSE; + } + tcds->creditSum = + gnc_numeric_add( tcds->creditSum, tmp, 100, + (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) ); + tmp = gnc_numeric_zero(); + if ( g_strcmp0 (debit_formula, "") != 0 && + gnc_sx_parse_vars_from_formula( debit_formula, vars, + &tmp ) < 0 ) + { + GString *errStr; + + errStr = g_string_sized_new( 32 ); + g_string_printf( errStr, + _( "Couldn't parse debit formula for " + "split \"%s\"." ), + xaccSplitGetMemo( s ) ); + gnc_error_dialog( GTK_WIDGET(sxed->dialog), "%s", + (gchar*)errStr->str ); + g_string_free( errStr, TRUE ); + + return FALSE; + } + tcds->debitSum = gnc_numeric_add( tcds->debitSum, tmp, 100, + (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) ); + tmp = gnc_numeric_zero(); + } g_hash_table_foreach( txns, check_credit_debit_balance, diff --git a/src/gnome/dialog-sx-editor2.c b/src/gnome/dialog-sx-editor2.c index c9de4dc9f72..a5c5d61a11c 100644 --- a/src/gnome/dialog-sx-editor2.c +++ b/src/gnome/dialog-sx-editor2.c @@ -546,9 +546,7 @@ gnc_sxed_check_consistent (GncSxEditorDialog2 *sxed) int numIters, i; GHashTable *vars, *txns; GList *splitList = NULL; - char *str; - kvp_frame *f; - kvp_value *v; + char *credit_formula = NULL, *debit_formula = NULL; Split *s; Transaction *t; gnc_numeric tmp; @@ -597,7 +595,7 @@ gnc_sxed_check_consistent (GncSxEditorDialog2 *sxed) for (; splitList; splitList = splitList->next) { - GncGUID *acct_guid; + GncGUID *acct_guid = NULL; Account *acct; gnc_commodity *split_cmdty; txnCreditDebitSums *tcds; @@ -615,77 +613,60 @@ gnc_sxed_check_consistent (GncSxEditorDialog2 *sxed) g_hash_table_insert (txns, (gpointer)t, (gpointer)tcds); } - f = xaccSplitGetSlots (s); - - /* contains the guid of the split's actual account. */ - v = kvp_frame_get_slot_path (f, - GNC_SX_ID, - GNC_SX_ACCOUNT, - NULL); - acct_guid = kvp_value_get_guid (v); - acct = xaccAccountLookup (acct_guid, gnc_get_current_book ()); - split_cmdty = xaccAccountGetCommodity (acct); + qof_instance_get (QOF_INSTANCE (s), + "sx-account", &acct_guid, + "sx-credit-formula", &credit_formula, + "sx-debit-formula", &debit_formula, + NULL); + acct = xaccAccountLookup( acct_guid, gnc_get_current_book ()); + split_cmdty = xaccAccountGetCommodity(acct); if (base_cmdty == NULL) { base_cmdty = split_cmdty; } - multi_commodity |= !gnc_commodity_equal (split_cmdty, base_cmdty); - - v = kvp_frame_get_slot_path (f, - GNC_SX_ID, - GNC_SX_CREDIT_FORMULA, - NULL); - if (v - && (str = kvp_value_get_string (v)) - && strlen( str ) != 0) - { - if (gnc_sx_parse_vars_from_formula (str, vars, &tmp ) < 0) - { - GString *errStr; - - errStr = g_string_sized_new (32); - g_string_printf (errStr, - _( "Couldn't parse credit formula for " - "split \"%s\"."), - xaccSplitGetMemo (s)); - gnc_error_dialog (GTK_WIDGET (sxed->dialog), "%s", - errStr->str); - g_string_free (errStr, TRUE); - - return FALSE; - } - tcds->creditSum = - gnc_numeric_add (tcds->creditSum, tmp, 100, - (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD)); - tmp = gnc_numeric_zero(); - } - v = kvp_frame_get_slot_path (f, - GNC_SX_ID, - GNC_SX_DEBIT_FORMULA, - NULL); - if (v - && (str = kvp_value_get_string (v)) - && strlen(str) != 0 ) - { - if (gnc_sx_parse_vars_from_formula (str, vars, &tmp ) < 0) - { - GString *errStr; - - errStr = g_string_sized_new (32); - g_string_printf (errStr, - _( "Couldn't parse debit formula for " - "split \"%s\"."), - xaccSplitGetMemo (s)); - gnc_error_dialog (GTK_WIDGET (sxed->dialog), "%s", - (gchar*)errStr->str); - g_string_free (errStr, TRUE); - - return FALSE; - } - tcds->debitSum = gnc_numeric_add (tcds->debitSum, tmp, 100, - (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD)); - tmp = gnc_numeric_zero(); - } + multi_commodity |= !gnc_commodity_equal(split_cmdty, base_cmdty); + + if ( g_strcmp0 (credit_formula, "") != 0 && + gnc_sx_parse_vars_from_formula(credit_formula, vars, + &tmp ) < 0 ) + { + GString *errStr; + + errStr = g_string_sized_new( 32 ); + g_string_printf( errStr, + _( "Couldn't parse credit formula for " + "split \"%s\"." ), + xaccSplitGetMemo( s ) ); + gnc_error_dialog( GTK_WIDGET(sxed->dialog), "%s", + errStr->str ); + g_string_free( errStr, TRUE ); + + return FALSE; + } + tcds->creditSum = + gnc_numeric_add( tcds->creditSum, tmp, 100, + (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) ); + tmp = gnc_numeric_zero(); + if ( g_strcmp0 (debit_formula, "") != 0 && + gnc_sx_parse_vars_from_formula( debit_formula, vars, + &tmp ) < 0 ) + { + GString *errStr; + + errStr = g_string_sized_new( 32 ); + g_string_printf( errStr, + _( "Couldn't parse debit formula for " + "split \"%s\"." ), + xaccSplitGetMemo( s ) ); + gnc_error_dialog( GTK_WIDGET(sxed->dialog), "%s", + (gchar*)errStr->str ); + g_string_free( errStr, TRUE ); + + return FALSE; + } + tcds->debitSum = gnc_numeric_add( tcds->debitSum, tmp, 100, + (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) ); + tmp = gnc_numeric_zero(); } g_hash_table_foreach (txns, diff --git a/src/gnome/gnc-plugin-page-register2.c b/src/gnome/gnc-plugin-page-register2.c index c5494e4d635..b24793d4fcd 100644 --- a/src/gnome/gnc-plugin-page-register2.c +++ b/src/gnome/gnc-plugin-page-register2.c @@ -3646,39 +3646,30 @@ gnc_plugin_page_register2_cmd_schedule (GtkAction *action, /* If the transaction has a sched-xact KVP frame, then go to the editor * for the existing SX; otherwise, do the sx-from-trans dialog. */ { - kvp_frame *txn_frame; - kvp_value *kvp_val; - /* set a kvp-frame element in the transaction indicating and - * pointing-to the SX this was created from. */ - txn_frame = xaccTransGetSlots (trans); - if ( txn_frame != NULL ) - { - kvp_val = kvp_frame_get_slot (txn_frame, "from-sched-xaction"); - if (kvp_val) - { - GncGUID *fromSXId = kvp_value_get_guid (kvp_val); - SchedXaction *theSX = NULL; - GList *sxElts; - - /* Get the correct SX */ - for ( sxElts = gnc_book_get_schedxactions (gnc_get_current_book())->sx_list; - (!theSX) && sxElts; - sxElts = sxElts->next ) - { - SchedXaction *sx = (SchedXaction*)sxElts->data; - theSX = - ((guid_equal (xaccSchedXactionGetGUID (sx), fromSXId)) - ? sx : NULL); - } - - if (theSX) - { - gnc_ui_scheduled_xaction_editor_dialog_create2 (theSX, FALSE); - LEAVE(" "); - return; - } - } - } + GncGUID *fromSXId = NULL; + SchedXaction *theSX = NULL; + GList *sxElts; + qof_instance_get (QOF_INSTANCE (trans), + "from-sched-xaction", &fromSXId, + NULL); + + /* Get the correct SX */ + for ( sxElts = gnc_book_get_schedxactions (gnc_get_current_book())->sx_list; + (!theSX) && sxElts; + sxElts = sxElts->next ) + { + SchedXaction *sx = (SchedXaction*)sxElts->data; + theSX = + ((guid_equal (xaccSchedXactionGetGUID (sx), fromSXId)) + ? sx : NULL); + } + + if (theSX) + { + gnc_ui_scheduled_xaction_editor_dialog_create2 (theSX, FALSE); + LEAVE(" "); + return; + } } gnc_sx_create_from_trans (trans); LEAVE(" "); diff --git a/src/gnome/gnc-split-reg.c b/src/gnome/gnc-split-reg.c index b9d198a1e22..e5e56ac7455 100644 --- a/src/gnome/gnc-split-reg.c +++ b/src/gnome/gnc-split-reg.c @@ -1387,38 +1387,29 @@ gsr_default_schedule_handler( GNCSplitReg *gsr, gpointer data ) /* If the transaction has a sched-xact KVP frame, then go to the editor * for the existing SX; otherwise, do the sx-from-trans dialog. */ { - kvp_frame *txn_frame; - kvp_value *kvp_val; - /* set a kvp-frame element in the transaction indicating and - * pointing-to the SX this was created from. */ - txn_frame = xaccTransGetSlots( pending_trans ); - if ( txn_frame != NULL ) - { - kvp_val = kvp_frame_get_slot( txn_frame, "from-sched-xaction" ); - if ( kvp_val ) - { - GncGUID *fromSXId = kvp_value_get_guid( kvp_val ); - SchedXaction *theSX = NULL; - GList *sxElts; - - /* Get the correct SX */ - for ( sxElts = gnc_book_get_schedxactions(gnc_get_current_book())->sx_list; - (!theSX) && sxElts; - sxElts = sxElts->next ) - { - SchedXaction *sx = (SchedXaction*)sxElts->data; - theSX = - ( ( guid_equal( xaccSchedXactionGetGUID( sx ), fromSXId ) ) - ? sx : NULL ); - } - - if ( theSX ) - { - gnc_ui_scheduled_xaction_editor_dialog_create(theSX, FALSE); - return; - } - } - } + GncGUID *fromSXId = NULL; + SchedXaction *theSX = NULL; + GList *sxElts; + qof_instance_get (QOF_INSTANCE (pending_trans), + "from-sched-xaction", &fromSXId, + NULL); + + /* Get the correct SX */ + for ( sxElts = gnc_book_get_schedxactions (gnc_get_current_book())->sx_list; + (!theSX) && sxElts; + sxElts = sxElts->next ) + { + SchedXaction *sx = (SchedXaction*)sxElts->data; + theSX = + ((guid_equal (xaccSchedXactionGetGUID (sx), fromSXId)) + ? sx : NULL); + } + + if ( theSX ) + { + gnc_ui_scheduled_xaction_editor_dialog_create(theSX, FALSE); + return; + } } gnc_sx_create_from_trans(pending_trans); diff --git a/src/import-export/Makefile.am b/src/import-export/Makefile.am index 0f07e244bec..4230c835742 100644 --- a/src/import-export/Makefile.am +++ b/src/import-export/Makefile.am @@ -18,13 +18,11 @@ libgncmod_generic_import_la_SOURCES = \ import-parse.c \ import-utilities.c \ import-settings.c \ - import-match-map.c \ import-main-matcher.c \ gncmod-generic-import.c gncincludedir = ${GNC_INCLUDE_DIR} gncinclude_HEADERS = \ - import-match-map.h \ import-parse.h noinst_HEADERS = \ @@ -32,7 +30,6 @@ noinst_HEADERS = \ import-backend.h \ import-commodity-matcher.h \ import-main-matcher.h \ - import-match-map.h \ import-match-picker.h \ import-settings.h \ import-utilities.h diff --git a/src/import-export/aqb/gnc-ab-kvp.c b/src/import-export/aqb/gnc-ab-kvp.c index 074a37b5697..b99390b258c 100644 --- a/src/import-export/aqb/gnc-ab-kvp.c +++ b/src/import-export/aqb/gnc-ab-kvp.c @@ -32,134 +32,105 @@ #include "gnc-ab-kvp.h" -#define AB_KEY "hbci" -#define AB_ACCOUNT_ID "account-id" -#define AB_ACCOUNT_UID "account-uid" -#define AB_BANK_CODE "bank-code" -#define AB_TRANS_RETRIEVAL "trans-retrieval" -#define AB_TEMPLATES "template-list" - /* This static indicates the debugging module that this .o belongs to. */ G_GNUC_UNUSED static QofLogModule log_module = G_LOG_DOMAIN; -static kvp_frame *gnc_ab_get_account_kvp(const Account *a, gboolean create); static kvp_frame *gnc_ab_get_book_kvp(QofBook *b, gboolean create); const gchar * gnc_ab_get_account_accountid(const Account *a) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, FALSE); - kvp_value *value = kvp_frame_get_slot(frame, AB_ACCOUNT_ID); - return kvp_value_get_string(value); + gchar *id = NULL; + qof_instance_get (QOF_INSTANCE (a), + "ab-account-id", &id, + NULL); + return id; } void gnc_ab_set_account_accountid(Account *a, const gchar *id) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, TRUE); - kvp_value *value = kvp_value_new_string(id); xaccAccountBeginEdit(a); - kvp_frame_set_slot_nc(frame, AB_ACCOUNT_ID, value); - qof_instance_set_dirty(QOF_INSTANCE (a)); + qof_instance_set (QOF_INSTANCE (a), + "ab-account-id", id, + NULL); xaccAccountCommitEdit(a); } const gchar * gnc_ab_get_account_bankcode(const Account *a) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, FALSE); - kvp_value *value = kvp_frame_get_slot(frame, AB_BANK_CODE); - return kvp_value_get_string(value); + gchar *code = NULL; + qof_instance_get (QOF_INSTANCE (a), + "ab-bank-code", &code, + NULL); + return code; } void gnc_ab_set_account_bankcode(Account *a, const gchar *code) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, TRUE); - kvp_value *value = kvp_value_new_string(code); xaccAccountBeginEdit(a); - kvp_frame_set_slot_nc(frame, AB_BANK_CODE, value); - qof_instance_set_dirty(QOF_INSTANCE (a)); + qof_instance_set (QOF_INSTANCE (a), + "ab-bank-code", code, + NULL); xaccAccountCommitEdit(a); } guint32 gnc_ab_get_account_uid(const Account *a) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, FALSE); - kvp_value *value = kvp_frame_get_slot(frame, AB_ACCOUNT_UID); - return (guint32) kvp_value_get_gint64(value); + guint64 uid = 0LL; + qof_instance_get (QOF_INSTANCE (a), + "ab-account-uid", &uid, + NULL); + return (guint32)uid; } void gnc_ab_set_account_uid(Account *a, guint32 uid) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, TRUE); - kvp_value *value = kvp_value_new_gint64(uid); xaccAccountBeginEdit(a); - kvp_frame_set_slot_nc(frame, AB_ACCOUNT_UID, value); - qof_instance_set_dirty(QOF_INSTANCE (a)); + qof_instance_set (QOF_INSTANCE (a), + "ab-account-uid", (guint64)uid, + NULL); xaccAccountCommitEdit(a); } Timespec gnc_ab_get_account_trans_retrieval(const Account *a) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, FALSE); - kvp_value *value = kvp_frame_get_slot(frame, AB_TRANS_RETRIEVAL); - return kvp_value_get_timespec(value); + Timespec t = {0LL, 0LL}; + qof_instance_get (QOF_INSTANCE (a), + "ab-trans-retrieval", &t, + NULL); + return t; } void gnc_ab_set_account_trans_retrieval(Account *a, Timespec time) { - kvp_frame *frame = gnc_ab_get_account_kvp(a, TRUE); - kvp_value *value = kvp_value_new_timespec(time); xaccAccountBeginEdit(a); - kvp_frame_set_slot_nc(frame, AB_TRANS_RETRIEVAL, value); - qof_instance_set_dirty(QOF_INSTANCE (a)); + qof_instance_set (QOF_INSTANCE (a), + "ab-trans-retrieval", &time, + NULL); xaccAccountCommitEdit(a); } GList * gnc_ab_get_book_template_list(QofBook *b) { - kvp_frame *frame = gnc_ab_get_book_kvp(b, FALSE); - kvp_value *value = kvp_frame_get_slot(frame, AB_TEMPLATES); - return kvp_value_get_glist(value); + GList *template_list = NULL; + qof_instance_get (QOF_INSTANCE (b), + "ab-templates", &template_list, + NULL); + return template_list; } void gnc_ab_set_book_template_list(QofBook *b, GList *template_list) { - kvp_frame *frame = gnc_ab_get_book_kvp(b, TRUE); - kvp_value *value = kvp_value_new_glist_nc(template_list); - kvp_frame_set_slot_nc(frame, AB_TEMPLATES, value); - qof_book_kvp_changed (b); -} - -static kvp_frame * -gnc_ab_get_account_kvp(const Account *a, gboolean create) -{ - kvp_frame *toplevel = xaccAccountGetSlots(a); - kvp_frame *result = kvp_frame_get_frame(toplevel, AB_KEY); - if (!result && create) - { - result = kvp_frame_new(); - kvp_frame_add_frame_nc(toplevel, AB_KEY, result); - } - return result; -} - -static kvp_frame * -gnc_ab_get_book_kvp(QofBook *b, gboolean create) -{ - kvp_frame *toplevel = qof_book_get_slots(b); - kvp_frame *result = kvp_frame_get_frame(toplevel, AB_KEY); - if (!result && create) - { - result = kvp_frame_new(); - kvp_frame_add_frame_nc(toplevel, AB_KEY, result); - } - return result; + qof_instance_set (QOF_INSTANCE (b), + "ab-templates", &template_list, + NULL); } diff --git a/src/import-export/import-account-matcher.c b/src/import-export/import-account-matcher.c index a828311b4e7..27d7fe310a0 100644 --- a/src/import-export/import-account-matcher.c +++ b/src/import-export/import-account-matcher.c @@ -114,8 +114,8 @@ build_acct_tree(AccountPickerDialog *picker) g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1)); /* Add our custom column. */ - col = gnc_tree_view_account_add_kvp_column (picker->account_tree, - _("Account ID"), "online_id"); + col = gnc_tree_view_account_add_property_column (picker->account_tree, + _("Account ID"), "online-id"); g_object_set_data(G_OBJECT(col), DEFAULT_VISIBLE, GINT_TO_POINTER(1)); gtk_container_add(GTK_CONTAINER(picker->account_tree_sw), diff --git a/src/import-export/import-backend.c b/src/import-export/import-backend.c index 4e0d34e3ae8..a372bc4bf73 100644 --- a/src/import-export/import-backend.c +++ b/src/import-export/import-backend.c @@ -44,6 +44,40 @@ #include "gnc-prefs.h" #include "gnc-ui-util.h" +/* Private interface to Account GncImportMatchMap functions */ + +/** @{ +Obtain an ImportMatchMap object from an Account */ +extern GncImportMatchMap * gnc_account_create_imap (Account *acc); +/*@}*/ + +/* Look up an Account in the map */ +extern Account* gnc_imap_find_account(GncImportMatchMap *imap, + const char* category, + const char *key); + +/* Store an Account in the map. This mapping is immediatly stored in + the underlying kvp frame, regardless of whether the MatchMap is + destroyed later or not. */ +extern void gnc_imap_add_account (GncImportMatchMap *imap, + const char *category, + const char *key, Account *acc); + +/* Look up an Account in the map from a GList* of pointers to strings(tokens) + from the current transaction */ +extern Account* gnc_imap_find_account_bayes (GncImportMatchMap *imap, + GList* tokens); + +/* Store an Account in the map. This mapping is immediatly stored in + the underlying kvp frame, regardless of whether the MatchMap is + destroyed later or not. */ +extern void gnc_imap_add_account_bayes (GncImportMatchMap *imap, + GList* tokens, + Account *acc); + +#define GNCIMPORT_DESC "desc" +#define GNCIMPORT_MEMO "memo" +#define GNCIMPORT_PAYEE "payee" /********************************************************************\ * Constants * @@ -457,6 +491,15 @@ TransactionGetTokens(GNCImportTransInfo *info) /* return the pointer to the GList */ return tokens; } +/* Destroy an import map. But all stored entries will still continue + * to exist in the underlying kvp frame of the account. + */ +static void +gnc_imap_destroy (GncImportMatchMap *imap) +{ + if (!imap) return; + g_free (imap); +} /* searches using the GNCImportTransInfo through all existing transactions * if there is an exact match of the description and memo @@ -471,7 +514,7 @@ matchmap_find_destination (GncImportMatchMap *matchmap, GNCImportTransInfo *info g_assert (info); tmp_map = ((matchmap != NULL) ? matchmap : - gnc_imap_create_from_account + gnc_account_create_imap (xaccSplitGetAccount (gnc_import_TransInfo_get_fsplit (info)))); @@ -541,7 +584,7 @@ matchmap_store_destination (GncImportMatchMap *matchmap, tmp_matchmap = ((matchmap != NULL) ? matchmap : - gnc_imap_create_from_account + gnc_account_create_imap (xaccSplitGetAccount (gnc_import_TransInfo_get_fsplit (trans_info)))); diff --git a/src/import-export/import-backend.h b/src/import-export/import-backend.h index 2978fd54803..99e64213e9e 100644 --- a/src/import-export/import-backend.h +++ b/src/import-export/import-backend.h @@ -29,11 +29,11 @@ #define TRANSACTION_MATCHER_H #include "Transaction.h" -#include "import-match-map.h" #include "import-settings.h" typedef struct _transactioninfo GNCImportTransInfo; typedef struct _matchinfo GNCImportMatchInfo; +typedef struct _GncImportMatchMap GncImportMatchMap; typedef enum _action { diff --git a/src/import-export/import-main-matcher.c b/src/import-export/import-main-matcher.c index 3793212a2b0..24b8b630397 100644 --- a/src/import-export/import-main-matcher.c +++ b/src/import-export/import-main-matcher.c @@ -44,7 +44,6 @@ #include "gnc-ui-util.h" #include "gnc-engine.h" #include "import-settings.h" -#include "import-match-map.h" #include "import-match-picker.h" #include "import-backend.h" #include "import-account-matcher.h" diff --git a/src/import-export/import-match-map.c b/src/import-export/import-match-map.c deleted file mode 100644 index 558800d1ef1..00000000000 --- a/src/import-export/import-match-map.c +++ /dev/null @@ -1,545 +0,0 @@ -/********************************************************************\ - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License as * - * published by the Free Software Foundation; either version 2 of * - * the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License* - * along with this program; if not, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * -\********************************************************************/ -/** @addtogroup Import_Export - @{ */ -/** @internal - @file import-match-map.c - @brief Generic import mapper service, maps strings->accounts - * - An import mapper service that stores Account Maps for the - generic importer. This allows importers to map various - "strings" to Gnucash accounts in a generic manner. - @author Copyright (C) 2002,2003 Derek Atkins - */ -#include "config.h" -#include -#include -#include "import-match-map.h" -#include "gnc-ui-util.h" -#include "gnc-engine.h" - -/********************************************************************\ - * Constants * -\********************************************************************/ - -static QofLogModule log_module = GNC_MOD_IMPORT; - - -struct _GncImportMatchMap -{ - kvp_frame * frame; - Account * acc; - QofBook * book; -}; - -#define IMAP_FRAME "import-map" -#define IMAP_FRAME_BAYES "import-map-bayes" - -static GncImportMatchMap * -gnc_imap_create_from_frame (kvp_frame *frame, Account *acc, QofBook *book) -{ - GncImportMatchMap *imap; - - g_return_val_if_fail (frame != NULL, NULL); - g_return_val_if_fail ((acc && !book) || (!acc && book), NULL); - - imap = g_new0(GncImportMatchMap, 1); - imap->frame = frame; - - /* Cache the book for easy lookups; store the account/book for - * marking dirtiness - */ - if (acc) - book = gnc_account_get_book (acc); - imap->acc = acc; - imap->book = book; - - return imap; -} - -/** Obtain an ImportMatchMap object from an Account or a Book */ -GncImportMatchMap * gnc_imap_create_from_account (Account *acc) -{ - kvp_frame * frame; - - if (!acc) return NULL; - frame = xaccAccountGetSlots (acc); - g_return_val_if_fail (frame != NULL, NULL); - - return gnc_imap_create_from_frame (frame, acc, NULL); -} - -GncImportMatchMap * gnc_imap_create_from_book (QofBook *book) -{ - kvp_frame * frame; - - if (!book) return NULL; - frame = qof_book_get_slots (book); - g_return_val_if_fail (frame != NULL, NULL); - - return gnc_imap_create_from_frame (frame, NULL, book); -} - -/** Destroy an import map */ -void gnc_imap_destroy (GncImportMatchMap *imap) -{ - if (!imap) return; - g_free (imap); -} - -/** Clear an import map -- this removes ALL entries in the map */ -void gnc_imap_clear (GncImportMatchMap *imap) -{ - if (!imap) return; - xaccAccountBeginEdit (imap->acc); - /* Clear the IMAP_FRAME kvp */ - kvp_frame_set_slot_path (imap->frame, NULL, IMAP_FRAME); - - /* Clear the bayes kvp, IMAP_FRAME_BAYES */ - kvp_frame_set_slot_path (imap->frame, NULL, IMAP_FRAME_BAYES); - qof_instance_set_dirty (QOF_INSTANCE (imap->acc)); - xaccAccountCommitEdit (imap->acc); -} - -/** Look up an Account in the map */ -Account * gnc_imap_find_account (GncImportMatchMap *imap, const char *category, - const char *key) -{ - kvp_value *value; - GncGUID * guid; - - if (!imap || !key) return NULL; - if (!category) - { - category = key; - key = NULL; - } - - value = kvp_frame_get_slot_path (imap->frame, IMAP_FRAME, category, key, NULL); - if (!value) return NULL; - - guid = kvp_value_get_guid (value); - return xaccAccountLookup (guid, imap->book); -} - -/** Store an Account in the map */ -void gnc_imap_add_account (GncImportMatchMap *imap, const char *category, - const char *key, Account *acc) -{ - kvp_value *value; - - if (!imap || !key || !acc || (strlen (key) == 0)) return; - if (!category) - { - category = key; - key = NULL; - } - g_return_if_fail (acc != NULL); - - value = kvp_value_new_guid (xaccAccountGetGUID (acc)); - g_return_if_fail (value != NULL); - xaccAccountBeginEdit (imap->acc); - kvp_frame_set_slot_path (imap->frame, value, IMAP_FRAME, category, key, NULL); - qof_instance_set_dirty (QOF_INSTANCE (imap->acc)); - xaccAccountCommitEdit (imap->acc); - kvp_value_delete (value); - - /* XXX Mark the account (or book) as dirty! */ -} - - - - -/*-------------------------------------------------------------------------- - Below here is the bayes transaction to account matching system ---------------------------------------------------------------------------*/ - - -struct account_token_count -{ - char* account_name; - gint64 token_count; /**< occurances of a given token for this account_name */ -}; - -/** total_count and the token_count for a given account let us calculate the - * probability of a given account with any single token - */ -struct token_accounts_info -{ - GList *accounts; /**< array of struct account_token_count */ - gint64 total_count; -}; - -/** gpointer is a pointer to a struct token_accounts_info - * \note Can always assume that keys are unique, reduces code in this function - */ -static void buildTokenInfo(const char *key, kvp_value *value, gpointer data) -{ - struct token_accounts_info *tokenInfo = (struct token_accounts_info*)data; - struct account_token_count* this_account; - - // PINFO("buildTokenInfo: account '%s', token_count: '%ld'\n", (char*)key, - // (long)kvp_value_get_gint64(value)); - - /* add the count to the total_count */ - tokenInfo->total_count += kvp_value_get_gint64(value); - - /* allocate a new structure for this account and it's token count */ - this_account = (struct account_token_count*) - g_new0(struct account_token_count, 1); - - /* fill in the account name and number of tokens found for this account name */ - this_account->account_name = (char*)key; - this_account->token_count = kvp_value_get_gint64(value); - - /* append onto the glist a pointer to the new account_token_count structure */ - tokenInfo->accounts = g_list_prepend(tokenInfo->accounts, this_account); -} - -/** intermediate values used to calculate the bayes probability of a given account - where p(AB) = (a*b)/[a*b + (1-a)(1-b)], product is (a*b), - product_difference is (1-a) * (1-b) - */ -struct account_probability -{ - double product; /* product of probabilities */ - double product_difference; /* product of (1-probabilities) */ -}; - -/** convert a hash table of account names and (struct account_probability*) - into a hash table of 100000x the percentage match value, ie. 10% would be - 0.10 * 100000 = 10000 - */ -#define PROBABILITY_FACTOR 100000 -static void buildProbabilities(gpointer key, gpointer value, gpointer data) -{ - GHashTable *final_probabilities = (GHashTable*)data; - struct account_probability *account_p = (struct account_probability*)value; - - /* P(AB) = A*B / [A*B + (1-A)*(1-B)] - * NOTE: so we only keep track of a running product(A*B*C...) - * and product difference ((1-A)(1-B)...) - */ - gint32 probability = - (account_p->product / - (account_p->product + account_p->product_difference)) - * PROBABILITY_FACTOR; - - PINFO("P('%s') = '%d'\n", (char*)key, probability); - - g_hash_table_insert(final_probabilities, key, GINT_TO_POINTER(probability)); -} - -/** Frees an array of the same time that buildProperties built */ -static void freeProbabilities(gpointer key, gpointer value, gpointer data) -{ - /* free up the struct account_probability that was allocated - * in gnc_imap_find_account_bayes() - */ - g_free(value); -} - -/** holds an account name and its corresponding integer probability - the integer probability is some factor of 10 - */ -struct account_info -{ - char* account_name; - gint32 probability; -}; - -/** Find the highest probability and the corresponding account name - store in data, a (struct account_info*) - NOTE: this is a g_hash_table_foreach() function for a hash table of entries - key is a pointer to the account name, value is a gint32, 100000x - the probability for this account -*/ -static void highestProbability(gpointer key, gpointer value, gpointer data) -{ - struct account_info *account_i = (struct account_info*)data; - - /* if the current probability is greater than the stored, store the current */ - if (GPOINTER_TO_INT(value) > account_i->probability) - { - /* Save the new highest probability and the assoaciated account name */ - account_i->probability = GPOINTER_TO_INT(value); - account_i->account_name = key; - } -} - - -#define threshold (.90 * PROBABILITY_FACTOR) /* 90% */ - -/** Look up an Account in the map */ -Account* gnc_imap_find_account_bayes(GncImportMatchMap *imap, GList *tokens) -{ - struct token_accounts_info tokenInfo; /**< holds the accounts and total - * token count for a single token */ - GList *current_token; /**< pointer to the current token from the - * input GList *tokens */ - GList *current_account_token; /**< pointer to the struct - * account_token_count */ - struct account_token_count *account_c; /**< an account name and the number - * of times a token has appeared - * for the account */ - struct account_probability *account_p; /**< intermediate storage of values - * to compute the bayes probability - * of an account */ - GHashTable *running_probabilities = g_hash_table_new(g_str_hash, g_str_equal); - GHashTable *final_probabilities = g_hash_table_new(g_str_hash, g_str_equal); - struct account_info account_i; - kvp_value* value; - kvp_frame* token_frame; - - ENTER(" "); - - /* check to see if the imap is NULL */ - if (!imap) - { - PINFO("imap is null, returning null"); - LEAVE(" "); - return NULL; - } - - /* find the probability for each account that contains any of the tokens - * in the input tokens list - */ - for (current_token = tokens; current_token; current_token = current_token->next) - { - /* zero out the token_accounts_info structure */ - memset(&tokenInfo, 0, sizeof(struct token_accounts_info)); - - PINFO("token: '%s'", (char*)current_token->data); - - /* find the slot for the given token off of the source account - * for these tokens, search off of the IMAP_FRAME_BAYES path so - * we aren't looking from the parent of the entire kvp tree - */ - value = kvp_frame_get_slot_path(imap->frame, IMAP_FRAME_BAYES, - (char*)current_token->data, NULL); - - /* if value is null we should skip over this token */ - if (!value) - continue; - - /* convert the slot(value) into a the frame that contains the - * list of accounts - */ - token_frame = kvp_value_get_frame(value); - - /* token_frame should NEVER be null */ - if (!token_frame) - { - PERR("token '%s' has no accounts", (char*)current_token->data); - continue; /* skip over this token */ - } - - /* process the accounts for this token, adding the account if it - * doesn't already exist or adding to the existing accounts token - * count if it does - */ - kvp_frame_for_each_slot(token_frame, buildTokenInfo, &tokenInfo); - - /* for each account we have just found, see if the account already exists - * in the list of account probabilities, if not add it - */ - for (current_account_token = tokenInfo.accounts; current_account_token; - current_account_token = current_account_token->next) - { - /* get the account name and corresponding token count */ - account_c = (struct account_token_count*)current_account_token->data; - - PINFO("account_c->account_name('%s'), " - "account_c->token_count('%ld')/total_count('%ld')", - account_c->account_name, (long)account_c->token_count, - (long)tokenInfo.total_count); - - account_p = g_hash_table_lookup(running_probabilities, - account_c->account_name); - - /* if the account exists in the list then continue - * the running probablities - */ - if (account_p) - { - account_p->product = - ((double)account_c->token_count / (double)tokenInfo.total_count) - * account_p->product; - account_p->product_difference = - ((double)1 - ((double)account_c->token_count / - (double)tokenInfo.total_count)) - * account_p->product_difference; - PINFO("product == %f, product_difference == %f", - account_p->product, account_p->product_difference); - } - else - { - /* add a new entry */ - PINFO("adding a new entry for this account"); - account_p = (struct account_probability*) - g_new0(struct account_probability, 1); - - /* set the product and product difference values */ - account_p->product = ((double)account_c->token_count / - (double)tokenInfo.total_count); - account_p->product_difference = - (double)1 - ((double)account_c->token_count / - (double)tokenInfo.total_count); - - PINFO("product == %f, product_difference == %f", - account_p->product, account_p->product_difference); - - /* add the account name and (struct account_probability*) - * to the hash table */ - g_hash_table_insert(running_probabilities, - account_c->account_name, account_p); - } - } /* for all accounts in tokenInfo */ - - /* free the data in tokenInfo */ - for (current_account_token = tokenInfo.accounts; current_account_token; - current_account_token = current_account_token->next) - { - /* free up each struct account_token_count we allocated */ - g_free((struct account_token_count*)current_account_token->data); - } - - g_list_free(tokenInfo.accounts); /* free the accounts GList */ - } - - /* build a hash table of account names and their final probabilities - * from each entry in the running_probabilties hash table - */ - g_hash_table_foreach(running_probabilities, buildProbabilities, - final_probabilities); - - /* find the highest probabilty and the corresponding account */ - memset(&account_i, 0, sizeof(struct account_info)); - g_hash_table_foreach(final_probabilities, highestProbability, &account_i); - - /* free each element of the running_probabilities hash */ - g_hash_table_foreach(running_probabilities, freeProbabilities, NULL); - - /* free the hash tables */ - g_hash_table_destroy(running_probabilities); - g_hash_table_destroy(final_probabilities); - - PINFO("highest P('%s') = '%d'", - account_i.account_name ? account_i.account_name : "(null)", - account_i.probability); - - /* has this probability met our threshold? */ - if (account_i.probability >= threshold) - { - PINFO("found match"); - LEAVE(" "); - return gnc_account_lookup_by_full_name(gnc_book_get_root_account(imap->book), - account_i.account_name); - } - - PINFO("no match"); - LEAVE(" "); - - return NULL; /* we didn't meet our threshold, return NULL for an account */ -} - - -/** Updates the imap for a given account using a list of tokens */ -void gnc_imap_add_account_bayes(GncImportMatchMap *imap, GList *tokens, Account *acc) -{ - GList *current_token; - kvp_value *value; - gint64 token_count; - char* account_fullname; - kvp_value *new_value; /* the value that will be added back into the kvp tree */ - - ENTER(" "); - - /* if imap is null return */ - if (!imap) - { - LEAVE(" "); - return; - } - - g_return_if_fail (acc != NULL); - account_fullname = gnc_account_get_full_name(acc); - xaccAccountBeginEdit (imap->acc); - - PINFO("account name: '%s'\n", account_fullname); - - /* process each token in the list */ - for (current_token = g_list_first(tokens); current_token; - current_token = current_token->next) - { - /* Jump to next iteration if the pointer is not valid or if the - string is empty. In HBCI import we almost always get an empty - string, which doesn't work in the kvp loopkup later. So we - skip this case here. */ - if (!current_token->data || (*((char*)current_token->data) == '\0')) - continue; - - /* start off with no tokens for this account */ - token_count = 0; - - PINFO("adding token '%s'\n", (char*)current_token->data); - - /* is this token/account_name already in the kvp tree? */ - value = kvp_frame_get_slot_path(imap->frame, IMAP_FRAME_BAYES, - (char*)current_token->data, account_fullname, - NULL); - - /* if the token/account is already in the tree, read the current - * value from the tree and use this for the basis of the value we - * are putting back - */ - if (value) - { - PINFO("found existing value of '%ld'\n", - (long)kvp_value_get_gint64(value)); - - /* convert this value back into an integer */ - token_count += kvp_value_get_gint64(value); - } - - /* increment the token count */ - token_count++; - - /* create a new value */ - new_value = kvp_value_new_gint64(token_count); - - /* insert the value into the kvp tree at - * /imap->frame/IMAP_FRAME/token_string/account_name_string - */ - kvp_frame_set_slot_path(imap->frame, new_value, IMAP_FRAME_BAYES, - (char*)current_token->data, account_fullname, NULL); - /* kvp_frame_set_slot_path() copied the value so we - * need to delete this one ;-) */ - kvp_value_delete(new_value); - } - - /* free up the account fullname string */ - qof_instance_set_dirty (QOF_INSTANCE (imap->acc)); - xaccAccountCommitEdit (imap->acc); - g_free(account_fullname); - - LEAVE(" "); -} - -/** @} */ diff --git a/src/import-export/import-match-map.h b/src/import-export/import-match-map.h deleted file mode 100644 index 88c694571fa..00000000000 --- a/src/import-export/import-match-map.h +++ /dev/null @@ -1,84 +0,0 @@ -/********************************************************************\ - * This program is free software; you can redistribute it and/or * - * modify it under the terms of the GNU General Public License as * - * published by the Free Software Foundation; either version 2 of * - * the License, or (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License* - * along with this program; if not, contact: * - * * - * Free Software Foundation Voice: +1-617-542-5942 * - * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * - * Boston, MA 02110-1301, USA gnu@gnu.org * -\********************************************************************/ -/** @addtogroup Import_Export - @{ */ -/** @file import-match-map.h - @brief Generic import mapper service, maps strings->accounts - * - An import mapper service that stores Account Maps for the - generic importer. This allows importers to map various - "strings" to Gnucash accounts in a generic manner. - @author Copyright (C) 2002,2003 Derek Atkins - */ -#ifndef GNC_IMPORT_MATCH_MAP_H -#define GNC_IMPORT_MATCH_MAP_H - -typedef struct _GncImportMatchMap GncImportMatchMap; - -#include "Account.h" - -/** @{ -Obtain an ImportMatchMap object from an Account or a Book */ -GncImportMatchMap * gnc_imap_create_from_account (Account *acc); -GncImportMatchMap * gnc_imap_create_from_book (QofBook *book); -/*@}*/ - -/** Destroy an import map. But all stored entries will still continue - to exist in the underlying kvp frame of the account or book. */ -void gnc_imap_destroy (GncImportMatchMap *imap); - -/** Clear an import map -- this removes ALL entries in the map */ -void gnc_imap_clear (GncImportMatchMap *imap); - -/** Look up an Account in the map */ -Account* gnc_imap_find_account(GncImportMatchMap *imap, const char* category, - const char *key); - -/** Store an Account in the map. This mapping is immediatly stored in - the underlying kvp frame, regardless of whether the MatchMap is - destroyed later or not. */ -void gnc_imap_add_account (GncImportMatchMap *imap, const char *category, - const char *key, Account *acc); - -/** Look up an Account in the map from a GList* of pointers to strings(tokens) - from the current transaction */ -Account* gnc_imap_find_account_bayes (GncImportMatchMap *imap, GList* tokens); - -/** Store an Account in the map. This mapping is immediatly stored in - the underlying kvp frame, regardless of whether the MatchMap is - destroyed later or not. */ -void gnc_imap_add_account_bayes (GncImportMatchMap *imap, GList* tokens, - Account *acc); - - -/** @name Some well-known categories - - NOTE: You DO NOT have to use these values in your importer -- these - are just "well known" values, not "mandatory" values. You are free - to use these if they apply, map your own fields to these labels, or - create your own category strings. -*/ -/** @{*/ -#define GNCIMPORT_DESC "desc" -#define GNCIMPORT_MEMO "memo" -#define GNCIMPORT_PAYEE "payee" -/**@}*/ - -#endif /* GNC_IMPORT_MATCH_MAP_H */ -/**@}*/ diff --git a/src/import-export/import-utilities.c b/src/import-export/import-utilities.c index 51b8f530efa..60bf1ae727f 100644 --- a/src/import-export/import-utilities.c +++ b/src/import-export/import-utilities.c @@ -40,42 +40,36 @@ * Account, Transaction and Split \********************************************************************/ -const gchar * gnc_import_get_acc_online_id(Account * account) +const gchar * gnc_import_get_acc_online_id (Account * account) { - kvp_frame * frame; - frame = xaccAccountGetSlots(account); - return kvp_frame_get_string(frame, "online_id"); + gchar *id = NULL; + qof_instance_get (QOF_INSTANCE (account), "online-id", &id, NULL); + return id; } /* Used in the midst of editing a transaction; make it save the * account data. */ -void gnc_import_set_acc_online_id(Account * account, - const gchar * string_value) +void gnc_import_set_acc_online_id (Account *account, const gchar *id) { - kvp_frame * frame; g_return_if_fail (account != NULL); - frame = xaccAccountGetSlots(account); xaccAccountBeginEdit (account); - kvp_frame_set_str(frame, "online_id", string_value); - qof_instance_set_dirty (QOF_INSTANCE (account)); + qof_instance_set (QOF_INSTANCE (account), "online-id", &id, NULL); xaccAccountCommitEdit (account); } -const gchar * gnc_import_get_trans_online_id(Transaction * transaction) +const gchar * gnc_import_get_trans_online_id (Transaction * transaction) { - kvp_frame * frame; - frame = xaccTransGetSlots(transaction); - return kvp_frame_get_string(frame, "online_id"); + gchar *id = NULL; + qof_instance_get (QOF_INSTANCE (transaction), "online-id", &id, NULL); + return id; } /* Not actually used */ -void gnc_import_set_trans_online_id(Transaction * transaction, - const gchar * string_value) +void gnc_import_set_trans_online_id (Transaction *transaction, + const gchar *id) { - kvp_frame * frame; + g_return_if_fail (transaction != NULL); xaccTransBeginEdit (transaction); - frame = xaccTransGetSlots(transaction); - kvp_frame_set_str (frame, "online_id", string_value); - qof_instance_set_dirty (QOF_INSTANCE (transaction)); + qof_instance_set (QOF_INSTANCE (transaction), "online-id", &id, NULL); xaccTransCommitEdit (transaction); } @@ -86,24 +80,19 @@ gboolean gnc_import_trans_has_online_id(Transaction * transaction) return (online_id != NULL && strlen(online_id) > 0); } -const gchar * gnc_import_get_split_online_id(Split * split) +const gchar * gnc_import_get_split_online_id (Split * split) { - kvp_frame * frame; - frame = xaccSplitGetSlots(split); - return kvp_frame_get_string(frame, "online_id"); + gchar *id = NULL; + qof_instance_get (QOF_INSTANCE (split), "online-id", &id, NULL); + return id; } /* Used several places in a transaction edit where many other * parameters are also being set, so individual commits wouldn't be - * appropriate. */ -void gnc_import_set_split_online_id(Split * split, - const gchar * string_value) + * appropriate. Besides, there isn't a function for one.*/ +void gnc_import_set_split_online_id (Split *split, const gchar *id) { - kvp_frame * frame; - xaccTransBeginEdit (xaccSplitGetParent (split)); - frame = xaccSplitGetSlots(split); - kvp_frame_set_str (frame, "online_id", string_value); - qof_instance_set_dirty (QOF_INSTANCE (split)); - xaccTransCommitEdit (xaccSplitGetParent (split)); + g_return_if_fail (split != NULL); + qof_instance_set (QOF_INSTANCE (split), "online-id", &id, NULL); } gboolean gnc_import_split_has_online_id(Split * split) diff --git a/src/import-export/log-replay/gnc-log-replay.c b/src/import-export/log-replay/gnc-log-replay.c index 55775719638..bbd26d73e03 100644 --- a/src/import-export/log-replay/gnc-log-replay.c +++ b/src/import-export/log-replay/gnc-log-replay.c @@ -45,6 +45,9 @@ #define GNC_PREFS_GROUP "dialogs.log-replay" +/* EFFECTIVE FRIEND FUNCTION */ +void qof_instance_set_guid (gpointer inst, const GncGUID *guid); + /* NW: If you want a new log_module, just define a unique string either in gnc-engine.h or locally.*/ @@ -435,7 +438,8 @@ static void process_trans_record( FILE *log_file) xaccTransBeginEdit(trans); } - xaccTransSetGUID (trans, &(record.trans_guid)); + qof_instance_set_guid (QOF_INSTANCE (trans), + &(record.trans_guid)); /*Fill the transaction info*/ if (record.date_entered_present) { diff --git a/src/import-export/ofx/gnc-ofx-kvp.c b/src/import-export/ofx/gnc-ofx-kvp.c index 05b26e72c90..e4e5d6fbdf2 100644 --- a/src/import-export/ofx/gnc-ofx-kvp.c +++ b/src/import-export/ofx/gnc-ofx-kvp.c @@ -26,44 +26,34 @@ #include "config.h" #include "gnc-ofx-kvp.h" -static const char *KEY_ASSOC_INCOME_ACCOUNT = "ofx/associated-income-account"; +static void force_account_dirty(Account *acct); +/* OUTSIDE SLOT ACCESS */ Account *gnc_ofx_kvp_get_assoc_account(const Account* investment_account) { - kvp_frame * acc_frame; - kvp_value * kvp_val; Account *result = NULL; - + GncGUID *income_guid= NULL; g_assert(investment_account); - - acc_frame = xaccAccountGetSlots(investment_account); - kvp_val = kvp_frame_get_slot(acc_frame, KEY_ASSOC_INCOME_ACCOUNT); - if (kvp_val != NULL) - { - result = xaccAccountLookup(kvp_value_get_guid(kvp_val), - gnc_account_get_book(investment_account)); - } - return result; + qof_instance_get (QOF_INSTANCE (investment_account), + "ofx-income-account", &income_guid, + NULL); + return xaccAccountLookup(income_guid, + gnc_account_get_book(investment_account)); } void gnc_ofx_kvp_set_assoc_account(Account* investment_account, const Account *income_account) { - kvp_frame * acc_frame; - kvp_value * kvp_val; const GncGUID * income_acc_guid; g_assert(investment_account); g_assert(income_account); - acc_frame = xaccAccountGetSlots(investment_account); - g_assert(acc_frame); // Must not be NULL, but the QofInstance doc is unclear about this income_acc_guid = xaccAccountGetGUID(income_account); - kvp_val = kvp_value_new_guid(income_acc_guid); xaccAccountBeginEdit(investment_account); - kvp_frame_set_slot_nc(acc_frame, KEY_ASSOC_INCOME_ACCOUNT, - kvp_val); - qof_instance_set_dirty(QOF_INSTANCE (investment_account)); + qof_instance_set (QOF_INSTANCE (investment_account), + "ofx-income-account", income_acc_guid, + NULL); xaccAccountCommitEdit(investment_account); } diff --git a/src/libqof/qof/kvp_frame.cpp b/src/libqof/qof/kvp_frame.cpp index c52b8e8f985..881d029733b 100644 --- a/src/libqof/qof/kvp_frame.cpp +++ b/src/libqof/qof/kvp_frame.cpp @@ -1665,4 +1665,192 @@ kvp_frame_get_hash(const KvpFrame *frame) return frame->hash; } +static GValue *gvalue_from_kvp_value (KvpValue*); +static KvpValue *kvp_value_from_gvalue (const GValue*); + +static void +gvalue_list_from_kvp_value (KvpValue *kval, gpointer pList) +{ + GList **gvlist = NULL; + GValue *gval = gvalue_from_kvp_value (kval); + gvlist = (GList**)pList; + if (G_VALUE_TYPE (gval)) + *gvlist = g_list_prepend (*gvlist, gval); +} + +static void +kvp_value_list_from_gvalue (GValue *gval, gpointer pList) +{ + GList **kvplist = (GList**)pList; + KvpValue *kvp; + if (!(gval && G_VALUE_TYPE (gval))) + return; + kvp = kvp_value_from_gvalue (gval); + *kvplist = g_list_prepend (*kvplist, kvp); +} + +static GValue* +gvalue_from_kvp_value (KvpValue *kval) +{ + GValue *val; + gnc_numeric num; + Timespec tm; + GDate gdate; + + if (kval == NULL) return NULL; + val = g_slice_new0 (GValue); + + switch (kval->type) + { + case KVP_TYPE_GINT64: + g_value_init (val, G_TYPE_INT64); + g_value_set_int64 (val, kvp_value_get_gint64 (kval)); + break; + case KVP_TYPE_DOUBLE: + g_value_init (val, G_TYPE_DOUBLE); + g_value_set_double (val, kvp_value_get_double (kval)); + break; + case KVP_TYPE_NUMERIC: + g_value_init (val, GNC_TYPE_NUMERIC); + num = kvp_value_get_numeric (kval); + g_value_set_boxed (val, &num); + break; + case KVP_TYPE_STRING: + g_value_init (val, G_TYPE_STRING); + g_value_set_string (val, kvp_value_get_string (kval)); + break; + case KVP_TYPE_GUID: + g_value_init (val, GNC_TYPE_GUID); + g_value_set_boxed (val, kvp_value_get_guid (kval)); + break; + case KVP_TYPE_TIMESPEC: + g_value_init (val, GNC_TYPE_TIMESPEC); + tm = kvp_value_get_timespec (kval); + g_value_set_boxed (val, &tm); + break; + case KVP_TYPE_BINARY: + PWARN ("Error! Don't use Kvp Binary!"); + g_slice_free (GValue, val); + val = NULL; + break; + case KVP_TYPE_GDATE: + g_value_init (val, G_TYPE_DATE); + gdate = kvp_value_get_gdate (kval); + g_value_set_boxed (val, &gdate); + break; + case KVP_TYPE_GLIST: + { + GList *gvalue_list = NULL; + GList *kvp_list = kvp_value_get_glist (kval); + g_list_foreach (kvp_list, (GFunc)gvalue_list_from_kvp_value, &gvalue_list); + g_value_init (val, GNC_TYPE_VALUE_LIST); + gvalue_list = g_list_reverse (gvalue_list); + g_value_set_boxed (val, gvalue_list); + break; + } +/* No transfer of KVP frames outside of QofInstance-derived classes! */ + case KVP_TYPE_FRAME: + PWARN ("Error! Attempt to transfer KvpFrame!"); + default: + PWARN ("Error! Invalid KVP Transfer Request!"); + g_slice_free (GValue, val); + val = NULL; + break; + } + return val; +} + +KvpValue* +kvp_value_from_gvalue (const GValue *gval) +{ + KvpValue *val = NULL; + GType type = G_VALUE_TYPE (gval); + g_return_val_if_fail (G_VALUE_TYPE (gval), NULL); + + if (type == G_TYPE_INT64) + val = kvp_value_new_gint64 (g_value_get_int64 (gval)); + else if (type == G_TYPE_DOUBLE) + val = kvp_value_new_double (g_value_get_double (gval)); + else if (type == GNC_TYPE_NUMERIC) + val = kvp_value_new_numeric (*(gnc_numeric*)g_value_get_boxed (gval)); + else if (type == G_TYPE_STRING) + val = kvp_value_new_string (g_value_get_string (gval)); + else if (type == GNC_TYPE_GUID) + val = kvp_value_new_guid ((GncGUID*)g_value_get_boxed (gval)); + else if (type == GNC_TYPE_TIMESPEC) + val = kvp_value_new_timespec (*(Timespec*)g_value_get_boxed (gval)); + else if (type == G_TYPE_DATE) + val = kvp_value_new_gdate (*(GDate*)g_value_get_boxed (gval)); + else if (type == GNC_TYPE_VALUE_LIST) + { + GList *gvalue_list = (GList*)g_value_get_boxed (gval); + GList *kvp_list = NULL; + g_list_foreach (gvalue_list, (GFunc)kvp_value_list_from_gvalue, &kvp_list); + kvp_list = g_list_reverse (kvp_list); + val = kvp_value_new_glist_nc (kvp_list); +// g_list_free_full (gvalue_list, (GDestroyNotify)g_value_unset); +// gvalue_list = NULL; + } + else + PWARN ("Error! Don't know how to make a KvpValue from a %s", + G_VALUE_TYPE_NAME (gval)); + + return val; +} + +GValue* +kvp_frame_get_gvalue (KvpFrame *frame, const gchar *key) +{ + KvpValue *kval = kvp_frame_get_value (frame, key); + GValue *value = gvalue_from_kvp_value (kval); + return value; +} + +void +kvp_frame_set_gvalue (KvpFrame *frame, const gchar *key, const GValue *value) +{ + kvp_frame_set_value_nc (frame, key, kvp_value_from_gvalue (value)); +} + +static GValue* +gnc_gvalue_copy (GValue *src, gpointer uData) +{ + GValue *dest = g_value_init (g_slice_new0 (GValue), G_VALUE_TYPE (src)); + g_value_copy (src, dest); + return dest; +} + +void +gnc_gvalue_free (GValue *val) +{ + if (val == NULL || ! G_IS_VALUE (val)) return; + g_value_unset (val); + g_slice_free (GValue, val); +} + +static GList* +gnc_value_list_copy (GList *list) +{ + return g_list_copy_deep (list, (GCopyFunc)gnc_gvalue_copy, NULL); +} + +static void +gnc_value_list_free (GList *list) +{ + g_list_free_full (list, (GDestroyNotify)gnc_gvalue_free); +} + +GType +gnc_value_list_get_type (void) +{ + static GType type = 0; + if (type == 0) + { + type = g_boxed_type_register_static ("gnc_value_list", + (GBoxedCopyFunc)gnc_value_list_copy, + (GBoxedFreeFunc)gnc_value_list_free); + } + return type; +} + /* ========================== END OF FILE ======================= */ diff --git a/src/libqof/qof/kvp_frame.h b/src/libqof/qof/kvp_frame.h index 440377a31cd..12f9ab56ffe 100644 --- a/src/libqof/qof/kvp_frame.h +++ b/src/libqof/qof/kvp_frame.h @@ -594,6 +594,59 @@ gchar* kvp_frame_to_string(const KvpFrame *frame); gchar* binary_to_string(const void *data, guint32 size); GHashTable* kvp_frame_get_hash(const KvpFrame *frame); +/** KvpItem: GValue Exchange + * \brief Transfer of KVP to and from GValue, with the key + * + * Used to parameterize KVP <-> GValue exchanges. + */ +typedef struct +{ + const gchar *key; + GValue *value; +}KvpItem; + +/** Return a KvpItem containing the value of a KvpFrame + * + * Structure types (gnc_numeric, Timespec) are converted to pointers + * and must be extracted with g_value_get_boxed. A KVP_TYPE_GLIST will + * have all of its contents converted from KvpValues to GValues, so + * the return type will be a GValue containing a GList of GValue*, not + * GValue. Use gnc_value_list_free() to free such a list if you take + * it out of the GValue. + * + * \param frame: (transfer-none) The KvpFrame retrieved with kvp_get_frame_foo() + * \param key: (transfer-none) A slash-delimited string with the path to + * the stored value. Must not be NULL or empty. + * \return (transfer-full) A KvpItem* which must be freed with kvp_item_free(). + */ +GValue *kvp_frame_get_gvalue (KvpFrame *frame, const gchar *key); + +/** Replace or create a Kvp slot from a KvpItem + * + * Structure types (gnc_numeric, Timespec) should be stored as + * boxed-type pointers in the GValue with the appropriate type from + * qof.h. Lists should be stored as a GValue containing a GList of + * GValues of type convertable to KvpValues. Unsupported types will + * emit a warning message and will be skipped. + * + * \param frame: (transfer none) The KvpFrame into which the value + * will be added. + * \param key: (transfer none) The subkey of the frame at which to + * store the value + * \param value: GValue containing the paramter to store. + */ +void kvp_frame_set_gvalue (KvpFrame *frame, const gchar *key, const GValue *value); + +/** + * \brief Convenience function to release the value in a GValue + * acquired by kvp_frame_get_gvalue and to free the GValue. + * \param value: A GValue* created by kvp_frame_get_gvalue + */ +void gnc_gvalue_free (GValue *value); + +GType gnc_value_list_get_type (void); +#define GNC_TYPE_VALUE_LIST (gnc_value_list_get_type ()) + /** @} */ #ifdef __cplusplus } diff --git a/src/libqof/qof/qof.h b/src/libqof/qof/qof.h index f34dc2ecc7a..68ad9ff8f47 100644 --- a/src/libqof/qof/qof.h +++ b/src/libqof/qof/qof.h @@ -84,7 +84,6 @@ #include "kvp-util-p.h" #include "qofbackend.h" #include "qofid-p.h" -#include "qofinstance-p.h" #include "qofbook.h" #include "qofclass.h" #include "qofevent.h" diff --git a/src/libqof/qof/qofbook.cpp b/src/libqof/qof/qofbook.cpp index 0ae2b21b618..a21d04ba3d7 100644 --- a/src/libqof/qof/qofbook.cpp +++ b/src/libqof/qof/qofbook.cpp @@ -58,8 +58,28 @@ extern "C" #include "qofbookslots.h" static QofLogModule log_module = QOF_MOD_ENGINE; +#define AB_KEY "hbci" +#define AB_TEMPLATES "template-list" + +enum +{ + PROP_0, +// PROP_ROOT_ACCOUNT, /* Table */ +// PROP_ROOT_TEMPLATE, /* Table */ + PROP_OPT_TRADING_ACCOUNTS, /* KVP */ + PROP_OPT_AUTO_READONLY_DAYS,/* KVP */ + PROP_OPT_NUM_FIELD_SOURCE, /* KVP */ + PROP_OPT_DEFAULT_BUDGET, /* KVP */ + PROP_OPT_FY_END, /* KVP */ + PROP_AB_TEMPLATES, /* KVP */ + N_PROPERTIES /* Just a counter */ +}; + +QOF_GOBJECT_GET_TYPE(QofBook, qof_book, QOF_TYPE_INSTANCE, {}); +QOF_GOBJECT_DISPOSE(qof_book); +QOF_GOBJECT_FINALIZE(qof_book); -QOF_GOBJECT_IMPL(qof_book, QofBook, QOF_TYPE_INSTANCE); +static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; /* ====================================================================== */ /* constructor / destructor */ @@ -90,6 +110,186 @@ qof_book_init (QofBook *book) book->version = 0; } +static void +qof_book_get_property (GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) +{ + QofBook *book; + gchar *key; + + g_return_if_fail (QOF_IS_BOOK (object)); + book = QOF_BOOK (object); + switch (prop_id) + { + case PROP_OPT_TRADING_ACCOUNTS: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_TRADING_ACCOUNTS); + qof_instance_get_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; + case PROP_OPT_AUTO_READONLY_DAYS: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_AUTO_READONLY_DAYS); + qof_instance_get_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; + case PROP_OPT_NUM_FIELD_SOURCE: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_NUM_FIELD_SOURCE); + qof_instance_get_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; + case PROP_OPT_DEFAULT_BUDGET: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_DEFAULT_BUDGET); + qof_instance_get_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + case PROP_OPT_FY_END: + key = "fy_end"; + qof_instance_get_kvp (QOF_INSTANCE (book), key, value); + break; + case PROP_AB_TEMPLATES: + key = AB_KEY "/" AB_TEMPLATES; + qof_instance_get_kvp (QOF_INSTANCE (book), key, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +qof_book_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + QofBook *book; + gchar *key; + + g_return_if_fail (QOF_IS_BOOK (object)); + book = QOF_BOOK (object); + g_assert (qof_instance_get_editlevel(book)); + + switch (prop_id) + { + case PROP_OPT_TRADING_ACCOUNTS: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_TRADING_ACCOUNTS); + qof_instance_set_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; + case PROP_OPT_AUTO_READONLY_DAYS: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_AUTO_READONLY_DAYS); + qof_instance_set_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; + case PROP_OPT_NUM_FIELD_SOURCE: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_NUM_FIELD_SOURCE); + qof_instance_set_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; + case PROP_OPT_DEFAULT_BUDGET: + key = g_strdup_printf ("%s/%s/%s", KVP_OPTION_PATH, + OPTION_SECTION_ACCOUNTS, + OPTION_NAME_DEFAULT_BUDGET); + qof_instance_set_kvp (QOF_INSTANCE (book), key, value); + g_free (key); + break; + case PROP_OPT_FY_END: + key = "fy_end"; + qof_instance_set_kvp (QOF_INSTANCE (book), key, value); + break; + case PROP_AB_TEMPLATES: + key = AB_KEY "/" AB_TEMPLATES; + qof_instance_set_kvp (QOF_INSTANCE (book), key, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +qof_book_class_init (QofBookClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->dispose = qof_book_dispose; + gobject_class->finalize = qof_book_finalize; + gobject_class->get_property = qof_book_get_property; + gobject_class->set_property = qof_book_set_property; + + g_object_class_install_property + (gobject_class, + PROP_OPT_TRADING_ACCOUNTS, + g_param_spec_string("trading-accts", + "Use Trading Accounts", + "Scheme true ('t') or NULL. If 't', then the book " + "uses trading accounts for managing multiple-currency " + "transactions.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_OPT_NUM_FIELD_SOURCE, + g_param_spec_string("split-action-num-field", + "Use Split-Action in the Num Field", + "Scheme true ('t') or NULL. If 't', then the book " + "will put the split action value in the Num field.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_OPT_AUTO_READONLY_DAYS, + g_param_spec_double("autoreadonly-days", + "Transaction Auto-read-only Days", + "Prevent editing of transactions posted more than " + "this many days ago.", + 0, + G_MAXDOUBLE, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_OPT_DEFAULT_BUDGET, + g_param_spec_boxed("default-budget", + "Book Default Budget", + "The default Budget for this book.", + GNC_TYPE_GUID, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_OPT_FY_END, + g_param_spec_boxed("fy-end", + "Book Fiscal Year End", + "A GDate with a bogus year having the last Month and " + "Day of the Fiscal year for the book.", + G_TYPE_DATE, + G_PARAM_READWRITE)); + g_object_class_install_property + (gobject_class, + PROP_AB_TEMPLATES, + g_param_spec_boxed("ab-templates", + "AQBanking Template List", + "A GList of AQBanking Templates", + GNC_TYPE_VALUE_LIST, + G_PARAM_READWRITE)); +} + QofBook * qof_book_new (void) { @@ -161,6 +361,7 @@ qof_book_destroy (QofBook *book) LEAVE ("book=%p", book); } + /* ====================================================================== */ gboolean @@ -253,20 +454,7 @@ qof_book_set_backend (QofBook *book, QofBackend *be) LEAVE (" "); } -void qof_book_kvp_changed (QofBook *book) -{ - qof_book_begin_edit(book); - qof_instance_set_dirty (QOF_INSTANCE (book)); - qof_book_commit_edit(book); -} - /* ====================================================================== */ - -KvpFrame *qof_book_get_slots(const QofBook *book) -{ - return qof_instance_get_slots(QOF_INSTANCE(book)); -} - /* Store arbitrary pointers in the QofBook for data storage extensibility */ /* XXX if data is NULL, we should remove the key from the hash table! */ @@ -389,7 +577,7 @@ qof_book_get_counter (QofBook *book, const char *counter_name) } /* Use the KVP in the book */ - kvp = qof_book_get_slots (book); + kvp = qof_instance_get_slots (QOF_INSTANCE (book)); if (!kvp) { @@ -441,7 +629,7 @@ qof_book_increment_and_format_counter (QofBook *book, const char *counter_name) counter++; /* Get the KVP from the current book */ - kvp = qof_book_get_slots (book); + kvp = qof_instance_get_slots (QOF_INSTANCE (book)); if (!kvp) { @@ -490,7 +678,7 @@ qof_book_get_counter_format(const QofBook *book, const char *counter_name) } /* Get the KVP from the current book */ - kvp = qof_book_get_slots (book); + kvp = qof_instance_get_slots (QOF_INSTANCE (book)); if (!kvp) { @@ -641,19 +829,10 @@ qof_book_validate_counter_format_internal(const gchar *p, gboolean qof_book_use_trading_accounts (const QofBook *book) { - const char *opt; - kvp_value *kvp_val; - - kvp_val = kvp_frame_get_slot_path (qof_book_get_slots (book), - KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_TRADING_ACCOUNTS, - NULL); - if (kvp_val == NULL) - return FALSE; - - opt = kvp_value_get_string (kvp_val); - + const char *opt = NULL; + qof_instance_get (QOF_INSTANCE (book), + "trading-accts", &opt, + NULL); if (opt && opt[0] == 't' && opt[1] == 0) return TRUE; return FALSE; @@ -664,19 +843,10 @@ qof_book_use_trading_accounts (const QofBook *book) gboolean qof_book_use_split_action_for_num_field (const QofBook *book) { - const char *opt; - kvp_value *kvp_val; - - g_assert(book); - kvp_val = kvp_frame_get_slot_path (qof_book_get_slots (book), - KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_NUM_FIELD_SOURCE, - NULL); - if (kvp_val == NULL) - return FALSE; - - opt = kvp_value_get_string (kvp_val); + const char *opt = NULL; + qof_instance_get (QOF_INSTANCE (book), + "split-action-num-field", &opt, + NULL); if (opt && opt[0] == 't' && opt[1] == 0) return TRUE; @@ -692,21 +862,13 @@ gboolean qof_book_uses_autoreadonly (const QofBook *book) gint qof_book_get_num_days_autoreadonly (const QofBook *book) { kvp_value *kvp_val; - double tmp; - g_assert(book); - kvp_val = kvp_frame_get_slot_path (qof_book_get_slots (book), - KVP_OPTION_PATH, - OPTION_SECTION_ACCOUNTS, - OPTION_NAME_AUTO_READONLY_DAYS, - NULL); + double tmp = 0; + KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book)); - if (kvp_val == NULL) - { - //PWARN("kvp_val for slot '%s' is NULL", OPTION_NAME_AUTO_READONLY_DAYS); - return 0; - } - - tmp = kvp_value_get_double (kvp_val); + g_assert(book); + qof_instance_get (QOF_INSTANCE (book), + "autoreadonly-days", &tmp, + NULL); return (gint) tmp; } @@ -728,14 +890,16 @@ GDate* qof_book_get_autoreadonly_gdate (const QofBook *book) const char* qof_book_get_string_option(const QofBook* book, const char* opt_name) { - return kvp_frame_get_string(qof_book_get_slots(book), opt_name); + return kvp_frame_get_string(qof_instance_get_slots(QOF_INSTANCE (book)), + opt_name); } void qof_book_set_string_option(QofBook* book, const char* opt_name, const char* opt_val) { qof_book_begin_edit(book); - kvp_frame_set_string(qof_book_get_slots(book), opt_name, opt_val); + kvp_frame_set_string(qof_instance_get_slots(QOF_INSTANCE (book)), + opt_name, opt_val); qof_instance_set_dirty (QOF_INSTANCE (book)); qof_book_commit_edit(book); } @@ -752,7 +916,53 @@ static void commit_err (G_GNUC_UNUSED QofInstance *inst, QofBackendError errcode // gnc_engine_signal_commit_error( errcode ); } -static void noop (G_GNUC_UNUSED QofInstance *inst) {} +#define GNC_FEATURES "/features/" +static void +add_feature_to_hash (const gchar *key, KvpValue *value, gpointer user_data) +{ + gchar *descr = kvp_value_get_string (value); + g_hash_table_insert (*(GHashTable**)user_data, (gchar*)key, descr); +} + +GHashTable * +qof_book_get_features (QofBook *book) +{ + KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book)); + GHashTable *features = g_hash_table_new (g_str_hash, g_str_equal); + + frame = kvp_frame_get_frame (frame, GNC_FEATURES); + kvp_frame_for_each_slot (frame, &add_feature_to_hash, &features); + return features; +} + +void +qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr) +{ + KvpFrame *frame = qof_instance_get_slots (QOF_INSTANCE (book)); + gchar *path = g_strconcat (GNC_FEATURES, key, NULL); + qof_book_begin_edit (book); + kvp_frame_set_string (frame, path, descr); + qof_instance_set_dirty (QOF_INSTANCE (book)); + qof_book_commit_edit (book); +} + +void +qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, GNCOptionDB *odb) +{ + KvpFrame *slots = qof_instance_get_slots (QOF_INSTANCE (book)); + load_cb (odb, slots); +} + +void +qof_book_save_options (QofBook *book, GNCOptionSave save_cb, + GNCOptionDB* odb, gboolean clear) +{ + KvpFrame *slots = qof_instance_get_slots (QOF_INSTANCE (book)); + save_cb (odb, slots, clear); + qof_instance_set_dirty (QOF_INSTANCE (book)); +} + +static void noop (QofInstance *inst) {} void qof_book_commit_edit(QofBook *book) diff --git a/src/libqof/qof/qofbook.h b/src/libqof/qof/qofbook.h index c3bd2557895..c1e5ae4b439 100644 --- a/src/libqof/qof/qofbook.h +++ b/src/libqof/qof/qofbook.h @@ -69,6 +69,11 @@ typedef struct _QofBookClass QofBookClass; typedef void (*QofBookDirtyCB) (QofBook *, gboolean dirty, gpointer user_data); +typedef struct gnc_option_db GNCOptionDB; + +typedef void (*GNCOptionSave) (GNCOptionDB*, KvpFrame*, gboolean); +typedef void (*GNCOptionLoad) (GNCOptionDB*, KvpFrame*); + /* Book structure */ struct _QofBook { @@ -211,24 +216,11 @@ QofCollection * qof_book_get_collection (const QofBook *, QofIdType); typedef void (*QofCollectionForeachCB) (QofCollection *, gpointer user_data); void qof_book_foreach_collection (const QofBook *, QofCollectionForeachCB, gpointer); -/** Return The kvp data for the book. - * Note that the book KVP data is persistent, and is stored/retrieved - * from the file/database. Thus, the book KVP is the correct place to - * store data that needs to be persistent accross sessions (or shared - * between multiple users). To store application runtime data, use - * qof_book_set_data() instead. - */ -KvpFrame *qof_book_get_slots(const QofBook *book); - /** The qof_book_set_data() allows arbitrary pointers to structs * to be stored in QofBook. This is the "preferred" method for * extending QofBook to hold new data types. This is also * the ideal location to store other arbitrary runtime data * that the application may need. - * - * The book data differs from the book KVP in that the contents - * of the book KVP are persistent (are saved and restored to file - * or database), whereas the data pointers exist only at runtime. */ void qof_book_set_data (QofBook *book, const gchar *key, gpointer data); @@ -312,10 +304,6 @@ time64 qof_book_get_session_dirty_time(const QofBook *book); */ void qof_book_set_dirty_cb(QofBook *book, QofBookDirtyCB cb, gpointer user_data); -/** Call this function when you change the book kvp, to make sure the book - * is marked 'dirty'. */ -void qof_book_kvp_changed (QofBook *book); - /** This will get the named counter for this book. The return value is * -1 on error or the current value of the counter. */ @@ -343,9 +331,22 @@ const char *qof_book_get_counter_format (const QofBook *book, const char* qof_book_get_string_option(const QofBook* book, const char* opt_name); void qof_book_set_string_option(QofBook* book, const char* opt_name, const char* opt_val); +/** Access functions for reading and setting the used-features on this book. + */ +GHashTable *qof_book_get_features (QofBook *book); +void qof_book_set_feature (QofBook *book, const gchar *key, const gchar *descr); + void qof_book_begin_edit(QofBook *book); void qof_book_commit_edit(QofBook *book); +/* Access functions for loading and saving the file options */ +void qof_book_load_options (QofBook *book, GNCOptionLoad load_cb, + GNCOptionDB *odb); +void +qof_book_save_options (QofBook *book, GNCOptionSave save_cb, + GNCOptionDB* odb, gboolean clear); + + /** deprecated */ #define qof_book_get_guid(X) qof_entity_get_guid (QOF_INSTANCE(X)) diff --git a/src/libqof/qof/qofid.cpp b/src/libqof/qof/qofid.cpp index 6febf643116..6ff67034e80 100644 --- a/src/libqof/qof/qofid.cpp +++ b/src/libqof/qof/qofid.cpp @@ -38,6 +38,7 @@ extern "C" #include "qof.h" #include "qofid-p.h" +#include "qofinstance-p.h" static QofLogModule log_module = QOF_MOD_ENGINE; static gboolean qof_alt_dirty_mode = FALSE; diff --git a/src/libqof/qof/qofinstance-p.h b/src/libqof/qof/qofinstance-p.h index aede28ca02e..5be89b37399 100644 --- a/src/libqof/qof/qofinstance-p.h +++ b/src/libqof/qof/qofinstance-p.h @@ -54,6 +54,61 @@ void qof_instance_set_last_update (QofInstance *inst, Timespec ts); * collection flag at all. */ void qof_instance_set_dirty_flag (gconstpointer inst, gboolean flag); +/** Set the GncGUID of this instance */ +void qof_instance_set_guid (gpointer inst, const GncGUID *guid); + +/** Copy the GncGUID from one instance to another. This routine should + * be used with extreme caution, since GncGUID values are everywhere + * assumed to be unique. */ +void qof_instance_copy_guid (gpointer to, gconstpointer from); + +//QofIdType qof_instance_get_e_type (const QofInstance *inst); +//void qof_instance_set_e_type (QofInstance *ent, QofIdType e_type); + +/** Return the pointer to the kvp_data */ +/*@ dependent @*/ +KvpFrame* qof_instance_get_slots (const QofInstance *); +void qof_instance_set_editlevel(gpointer inst, gint level); +void qof_instance_increase_editlevel (gpointer ptr); +void qof_instance_decrease_editlevel (gpointer ptr); +void qof_instance_reset_editlevel (gpointer ptr); +/** Set the flag that indicates whether or not this object is about to + * be destroyed. + * + * @param ptr The object whose flag should be set. + * + * @param value The new value to be set for this object. */ +void qof_instance_set_destroying (gpointer ptr, gboolean value); + +/** \brief Set the dirty flag +Sets this instance AND the collection as dirty. +*/ +void qof_instance_set_dirty(QofInstance* inst); + +/* reset the dirty flag */ +void qof_instance_mark_clean (QofInstance *); +/** Get the version number on this instance. The version number is + * used to manage multi-user updates. */ +gint32 qof_instance_get_version (gconstpointer inst); + +/** Set the version number on this instance. The version number is + * used to manage multi-user updates. */ +void qof_instance_set_version (gpointer inst, gint32 value); +/** Copy the version number on this instance. The version number is + * used to manage multi-user updates. */ +void qof_instance_copy_version (gpointer to, gconstpointer from); + +/** Get the instance version_check number */ +guint32 qof_instance_get_version_check (gconstpointer inst); +/** Set the instance version_check number */ +void qof_instance_set_version_check (gpointer inst, guint32 value); +/** copy the instance version_check number */ +void qof_instance_copy_version_check (gpointer to, gconstpointer from); +void qof_instance_set_idata(gpointer inst, guint32 idata); +/* Convenience functions to save some typing in property handlers */ +void qof_instance_set_kvp (QofInstance *inst, const gchar *key, const GValue *value); +void qof_instance_get_kvp (QofInstance *inst, const gchar *key, GValue *value); + #ifdef __cplusplus } #endif diff --git a/src/libqof/qof/qofinstance.cpp b/src/libqof/qof/qofinstance.cpp index dc32af7cef7..b5ac0035541 100644 --- a/src/libqof/qof/qofinstance.cpp +++ b/src/libqof/qof/qofinstance.cpp @@ -62,7 +62,6 @@ enum PROP_GUID, PROP_COLLECTION, PROP_BOOK, - PROP_KVP_DATA, PROP_LAST_UPDATE, PROP_EDITLEVEL, PROP_DESTROYING, @@ -83,12 +82,6 @@ typedef struct QofInstancePrivate /* The entity_table in which this instance is stored */ QofBook * book; - /* kvp_data is a key-value pair database for storing arbirtary - * information associated with this instance. - * See src/engine/kvp_doc.txt for a list and description of the - * important keys. */ -// KvpFrame *kvp_data; - /* Timestamp used to track the last modification to this * instance. Typically used to compare two versions of the * same object, to see which is newer. When used with the @@ -180,15 +173,6 @@ static void qof_instance_class_init(QofInstanceClass *klass) QOF_TYPE_BOOK, G_PARAM_READWRITE)); - g_object_class_install_property - (object_class, - PROP_KVP_DATA, - g_param_spec_pointer ("kvp-data", - "Object KVP Data", - "A pointer to the key-value data associated " - "with this object.", - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, PROP_LAST_UPDATE, @@ -396,9 +380,6 @@ qof_instance_get_property (GObject *object, case PROP_BOOK: g_value_take_object(value, priv->book); break; - case PROP_KVP_DATA: - g_value_set_pointer(value, inst->kvp_data); - break; case PROP_LAST_UPDATE: g_value_set_pointer(value, &priv->last_update); break; @@ -455,9 +436,6 @@ qof_instance_set_property (GObject *object, qof_instance_set_book(inst, static_cast(g_value_get_object(value))); break; - case PROP_KVP_DATA: - qof_instance_set_slots(inst, static_cast(g_value_get_pointer(value))); - break; case PROP_LAST_UPDATE: ts = static_cast(g_value_get_pointer(value)); qof_instance_set_last_update(inst, *ts); @@ -960,6 +938,32 @@ gboolean qof_instance_refers_to_object(const QofInstance* inst, const QofInstanc } } +/* g_object_set/get wrappers */ +void +qof_instance_get (const QofInstance *inst, const gchar *first_prop, ...) +{ + va_list ap; + g_return_if_fail (QOF_IS_INSTANCE (inst)); + + va_start (ap, first_prop); + g_object_get_valist (G_OBJECT (inst), first_prop, ap); + va_end (ap); +} + +void +qof_instance_set (QofInstance *inst, const gchar *first_prop, ...) +{ + va_list ap; + QofInstancePrivate *priv = GET_PRIVATE(inst); + g_return_if_fail (QOF_IS_INSTANCE (inst)); + + qof_instance_set_dirty (inst); + va_start (ap, first_prop); + g_object_set_valist (G_OBJECT (inst), first_prop, ap); + va_end (ap); +} + + /* =================================================================== */ /* Entity edit and commit utilities */ /* =================================================================== */ @@ -1064,5 +1068,25 @@ qof_commit_edit_part2(QofInstance *inst, return TRUE; } +void +qof_instance_set_kvp (QofInstance *inst, const gchar *key, const GValue *value) +{ + KvpFrame *frame = qof_instance_get_slots (inst); + kvp_frame_set_gvalue (frame, key, value); +} + +void +qof_instance_get_kvp (QofInstance *inst, const gchar *key, GValue *value) +{ + KvpFrame *frame = qof_instance_get_slots (inst); + GValue *temp = kvp_frame_get_gvalue (frame, key); + if (temp) + { + g_value_copy (temp, value); + gnc_gvalue_free (temp); + } +} + + /* ========================== END OF FILE ======================= */ diff --git a/src/libqof/qof/qofinstance.h b/src/libqof/qof/qofinstance.h index a296099b190..ca485c3b903 100644 --- a/src/libqof/qof/qofinstance.h +++ b/src/libqof/qof/qofinstance.h @@ -121,30 +121,12 @@ const GncGUID * qof_entity_get_guid (gconstpointer); /*@ dependent @*/ QofCollection* qof_instance_get_collection (gconstpointer inst); -/** Set the GncGUID of this instance */ -void qof_instance_set_guid (gpointer inst, const GncGUID *guid); - -/** Copy the GncGUID from one instance to another. This routine should - * be used with extreme caution, since GncGUID values are everywhere - * assumed to be unique. */ -void qof_instance_copy_guid (gpointer to, gconstpointer from); - /** Compare the GncGUID values of two instances. This routine returns 0 * if the two values are equal, <0 if the first is smaller than the * second, or >0 if the second is smaller tan the first. */ gint qof_instance_guid_compare(const gconstpointer ptr1, const gconstpointer ptr2); -//QofIdType qof_instance_get_e_type (const QofInstance *inst); -//void qof_instance_set_e_type (QofInstance *ent, QofIdType e_type); - -/** Return the pointer to the kvp_data */ -/*@ dependent @*/ -KvpFrame* qof_instance_get_slots (const QofInstance *); -void qof_instance_set_editlevel(gpointer inst, gint level); gint qof_instance_get_editlevel (gconstpointer ptr); -void qof_instance_increase_editlevel (gpointer ptr); -void qof_instance_decrease_editlevel (gpointer ptr); -void qof_instance_reset_editlevel (gpointer ptr); /** Compare two instances, based on thier last update times. * Returns a negative, zero or positive value, respectively, @@ -164,14 +146,6 @@ int qof_instance_version_cmp (const QofInstance *left, const QofInstance *right) * is passed to the function. */ gboolean qof_instance_get_destroying (gconstpointer ptr); -/** Set the flag that indicates whether or not this object is about to - * be destroyed. - * - * @param ptr The object whose flag should be set. - * - * @param value The new value to be set for this object. */ -void qof_instance_set_destroying (gpointer ptr, gboolean value); - /** Retrieve the flag that indicates whether or not this object has * been modified. This is specifically the flag on the object. It * does not perform any other checking which might normally be @@ -191,39 +165,22 @@ void qof_instance_print_dirty (const QofInstance *entity, gpointer dummy); #define qof_instance_is_dirty qof_instance_get_dirty gboolean qof_instance_get_dirty (QofInstance *); -/** \brief Set the dirty flag - -Sets this instance AND the collection as dirty. -*/ -void qof_instance_set_dirty(QofInstance* inst); - -/* reset the dirty flag */ -void qof_instance_mark_clean (QofInstance *); - gboolean qof_instance_get_infant(const QofInstance *inst); -/** Get the version number on this instance. The version number is - * used to manage multi-user updates. */ -gint32 qof_instance_get_version (gconstpointer inst); - -/** Set the version number on this instance. The version number is - * used to manage multi-user updates. */ -void qof_instance_set_version (gpointer inst, gint32 value); -/** Copy the version number on this instance. The version number is - * used to manage multi-user updates. */ -void qof_instance_copy_version (gpointer to, gconstpointer from); +/** + * \brief Wrapper for g_object_get + */ +void qof_instance_get (const QofInstance *inst, const gchar *first_param, ...); -/** Get the instance version_check number */ -guint32 qof_instance_get_version_check (gconstpointer inst); -/** Set the instance version_check number */ -void qof_instance_set_version_check (gpointer inst, guint32 value); -/** copy the instance version_check number */ -void qof_instance_copy_version_check (gpointer to, gconstpointer from); +/** + * \brief Wrapper for g_object_set + * Group setting multiple parameters in a single begin/commit/rollback + */ +void qof_instance_set (QofInstance *inst, const gchar *first_param, ...); /** get the instance tag number used for kvp management in sql backends. */ guint32 qof_instance_get_idata (gconstpointer inst); -void qof_instance_set_idata(gpointer inst, guint32 idata); /** * Returns a displayable name for this object. The returned string must be freed by the caller. diff --git a/src/libqof/qof/test/test-kvp_frame.c b/src/libqof/qof/test/test-kvp_frame.c index 6267f70c7b3..ce356375c52 100644 --- a/src/libqof/qof/test/test-kvp_frame.c +++ b/src/libqof/qof/test/test-kvp_frame.c @@ -41,18 +41,22 @@ void test_suite_kvp_frame ( void ); typedef struct { KvpFrame *frame; + GSList *hdlrs; } Fixture; static void setup( Fixture *fixture, gconstpointer pData ) { fixture->frame = kvp_frame_new(); + fixture->hdlrs = NULL; } static void teardown( Fixture *fixture, gconstpointer pData ) { kvp_frame_delete( fixture->frame ); + g_slist_free_full (fixture->hdlrs, test_free_log_handler); + test_clear_error_list (); } extern KvpFrame* ( *p_get_trailer_make )( KvpFrame *frame, const char *key_path, char **end_key ); @@ -67,6 +71,7 @@ static void setup_static( Fixture *fixture, gconstpointer pData ) { fixture->frame = kvp_frame_new(); + fixture->hdlrs = NULL; init_static_test_pointers(); g_assert( p_get_trailer_make && p_kvp_value_glist_to_string && p_get_or_make && p_kvp_frame_get_frame_or_null_slash_trash && @@ -77,6 +82,8 @@ static void teardown_static( Fixture *fixture, gconstpointer pData ) { kvp_frame_delete( fixture->frame ); + g_slist_free_full (fixture->hdlrs, test_free_log_handler); + test_clear_error_list (); p_get_trailer_make = NULL; p_kvp_value_glist_to_string = NULL; p_get_or_make = NULL; @@ -84,21 +91,19 @@ teardown_static( Fixture *fixture, gconstpointer pData ) p_get_trailer_or_null = NULL; } -static void -test_kvp_frame_new_delete( void ) +static GncGUID* +populate_frame (KvpFrame *frame) { - KvpFrame *frame; + GList *list = NULL; Timespec ts; GncGUID *guid; + GDate gdate; ts.tv_sec = 1; ts.tv_nsec = 1; - guid = guid_malloc(); - guid_new( guid ); - - frame = kvp_frame_new(); - g_assert( frame ); - g_assert( kvp_frame_is_empty( frame ) ); + guid = guid_malloc (); + guid_new (guid); + g_date_set_dmy (&gdate, 26, 1, 1957); kvp_frame_set_gint64( frame, "gint64-type", 100 ); kvp_frame_set_double( frame, "double-type", 3.14159 ); @@ -106,14 +111,37 @@ test_kvp_frame_new_delete( void ) kvp_frame_set_timespec( frame, "timespec-type", ts ); kvp_frame_set_string( frame, "string-type", "abcdefghijklmnop" ); kvp_frame_set_guid( frame, "guid-type", guid ); + kvp_frame_set_value_nc (frame, "gdate-type", kvp_value_new_gdate (gdate)); kvp_frame_set_frame( frame, "frame-type", kvp_frame_new() ); + list = g_list_prepend (list, kvp_value_new_guid (guid)); + list = g_list_prepend (list, kvp_value_new_string ("qrstuvwxyz")); + list = g_list_prepend (list, kvp_value_new_timespec (ts)); + list = g_list_prepend (list, kvp_value_new_numeric (gnc_numeric_create (256, 120))); + list = g_list_prepend (list, kvp_value_new_double (0.4342944819)); + list = g_list_prepend (list, kvp_value_new_gint64 (0x1f2e3d4c5b6a79LL)); + kvp_frame_set_value (frame, "list-type", kvp_value_new_glist_nc (list)); + + return guid; +} + +static void +test_kvp_frame_new_delete( void ) +{ + KvpFrame *frame; + GncGUID *guid; + + frame = kvp_frame_new(); + g_assert( frame ); + g_assert( kvp_frame_is_empty( frame ) ); + + guid = populate_frame (frame); + g_assert( !kvp_frame_is_empty( frame ) ); kvp_frame_delete( frame ); g_assert( frame ); - - guid_free( guid ); + guid_free (guid); } static void @@ -1465,6 +1493,203 @@ test_get_trailer_or_null( Fixture *fixture, gconstpointer pData ) g_assert_cmpstr( last_key, == , "test2" ); } +static void +test_kvp_frame_get_gvalue (Fixture *fixture, gconstpointer pData) +{ + KvpFrame *frame = fixture->frame; + GValue *value; + Timespec ts = {1, 1}; + GncGUID *guid = populate_frame (frame); + GDate date; + gchar *log_domain = "qof.kvp"; + gint log_level = G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL; + gchar *msg1 = "[gvalue_from_kvp_value()] Error! Attempt to transfer KvpFrame!"; + gchar *msg2 = "[gvalue_from_kvp_value()] Error! Invalid KVP Transfer Request!"; +#undef _func + TestErrorStruct *check1 = test_error_struct_new (log_domain, log_level, + msg1); + TestErrorStruct *check2 = test_error_struct_new (log_domain, log_level, + msg2); + fixture->hdlrs = test_log_set_fatal_handler (fixture->hdlrs, check1, + (GLogFunc)test_list_handler); + test_add_error (check1); + test_add_error (check2); + + g_date_clear (&date, 1); + g_date_set_dmy (&date, 26, 1, 1957); + + value = kvp_frame_get_gvalue (frame, "gint64-type"); + g_assert (value != NULL); + g_assert (G_VALUE_HOLDS_INT64 (value)); + g_assert_cmpint (g_value_get_int64 (value), ==, 100); + gnc_gvalue_free (value); + + value = kvp_frame_get_gvalue (frame, "double-type"); + g_assert (value != NULL); + g_assert (G_VALUE_HOLDS_DOUBLE (value)); + g_assert_cmpfloat (g_value_get_double (value), ==, 3.14159); + gnc_gvalue_free (value); + + value = kvp_frame_get_gvalue (frame, "numeric-type"); + g_assert (value != NULL); + g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_NUMERIC); + g_assert (gnc_numeric_zero_p (*(gnc_numeric*)g_value_get_boxed (value))); + gnc_gvalue_free (value); + + value = kvp_frame_get_gvalue (frame, "timespec-type"); + g_assert (value != NULL); + g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_TIMESPEC); + g_assert (timespec_equal (&ts, (Timespec*)g_value_get_boxed (value))); + gnc_gvalue_free (value); + + value = kvp_frame_get_gvalue (frame, "string-type"); + g_assert (value != NULL); + g_assert (G_VALUE_HOLDS_STRING (value)); + g_assert_cmpstr (g_value_get_string (value), ==, "abcdefghijklmnop"); + gnc_gvalue_free (value); + + value = kvp_frame_get_gvalue (frame, "guid-type"); + g_assert (value != NULL); + g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_GUID); + g_assert (guid_equal (guid, (GncGUID*)g_value_get_boxed (value))); + gnc_gvalue_free (value); + + value = kvp_frame_get_gvalue (frame, "gdate-type"); + g_assert (value != NULL); + g_assert_cmpint (G_VALUE_TYPE (value), ==, G_TYPE_DATE); + g_assert_cmpint (g_date_compare (&date, (GDate*)g_value_get_boxed (value)), ==, 0); + gnc_gvalue_free (value); + + value = kvp_frame_get_gvalue (frame, "frame-type"); + g_assert (value == NULL); + g_assert_cmpint (check1->hits, ==, 1); + g_assert_cmpint (check2->hits, ==, 1); + + value = kvp_frame_get_gvalue (frame, "list-type"); + g_assert (value != NULL); + g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_VALUE_LIST); + { + GList *list = (GList*)g_value_get_boxed (value); + GValue *value = NULL; + + value = (GValue*)(list->data); + g_assert (G_VALUE_HOLDS_INT64 (value)); + g_assert (g_value_get_int64 (value) == 0x1f2e3d4c5b6a79LL); + list = g_list_next (list); + + value = (GValue*)(list->data); + g_assert (G_VALUE_HOLDS_DOUBLE (value)); + g_assert_cmpfloat (g_value_get_double (value), ==, 0.4342944819); + list = g_list_next (list); + + value = (GValue*)(list->data); + g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_NUMERIC); + g_assert (gnc_numeric_eq (*(gnc_numeric*)g_value_get_boxed (value), + gnc_numeric_create (256, 120))); + list = g_list_next (list); + + value = (GValue*)(list->data); + g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_TIMESPEC); + g_assert (timespec_equal (&ts, (Timespec*)g_value_get_boxed (value))); + list = g_list_next (list); + + value = (GValue*)(list->data); + g_assert (G_VALUE_HOLDS_STRING (value)); + g_assert_cmpstr (g_value_get_string (value), ==, "qrstuvwxyz"); + list = g_list_next (list); + + value = (GValue*)(list->data); + g_assert_cmpint (G_VALUE_TYPE (value), ==, GNC_TYPE_GUID); + g_assert (guid_equal (guid, (GncGUID*)g_value_get_boxed (value))); + list = g_list_next (list); + + g_assert (list == NULL); + + } + gnc_gvalue_free (value); +} + +static void +test_kvp_frame_set_gvalue (Fixture *fixture, gconstpointer pData) +{ +/* Bit of a shortcut: We'll use kvp_frame_get_item to make our KvpItem + * and feed it into a new frame; something of a round-trip test. + */ + KvpFrame *o_frame = fixture->frame; + KvpFrame *n_frame = kvp_frame_new (); + GValue *value; + GList *o_list, *n_list; + + populate_frame (o_frame); + + value = kvp_frame_get_gvalue (o_frame, "gint64-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "gint64-type", value); + g_assert_cmpint (kvp_frame_get_gint64 (o_frame, "gint64-type"), ==, + kvp_frame_get_gint64 (n_frame, "gint64-type")); + + value = kvp_frame_get_gvalue (o_frame, "double-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "double-type", value); + g_assert_cmpint (kvp_frame_get_double (o_frame, "double-type"), ==, + kvp_frame_get_double (n_frame, "double-type")); + + value = kvp_frame_get_gvalue (o_frame, "numeric-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "numeric-type", value); + g_assert (gnc_numeric_equal (kvp_frame_get_numeric (o_frame, "numeric-type"), + kvp_frame_get_numeric (n_frame, "numeric-type"))); + + value = kvp_frame_get_gvalue (o_frame, "timespec-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "timespec-type", value); + { + Timespec o_ts = kvp_frame_get_timespec (o_frame, "timespec-type"); + Timespec n_ts = kvp_frame_get_timespec (n_frame, "timespec-type"); + g_assert (timespec_equal (&o_ts, &n_ts)); + } + + value = kvp_frame_get_gvalue (o_frame, "string-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "string-type", value); + g_assert_cmpstr (kvp_frame_get_string (o_frame, "string-type"), ==, + kvp_frame_get_string (n_frame, "string-type")); + + value = kvp_frame_get_gvalue (o_frame, "gdate-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "gdate-type", value); + { + GDate o_date = kvp_value_get_gdate (kvp_frame_get_slot (o_frame, + "gdate-type")); + GDate n_date = kvp_value_get_gdate (kvp_frame_get_slot (n_frame, + "gdate-type")); + g_assert_cmpint (g_date_compare (&o_date, &n_date), ==, 0); + } + + value = kvp_frame_get_gvalue (o_frame, "guid-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "guid-type", value); + g_assert (guid_equal (kvp_frame_get_guid (o_frame, "guid-type"), + kvp_frame_get_guid (n_frame, "guid-type"))); + + value = kvp_frame_get_gvalue (o_frame, "list-type"); + g_assert (value != NULL); + kvp_frame_set_gvalue (n_frame, "list-type", value); + o_list = kvp_value_get_glist (kvp_frame_get_slot (o_frame, "list_type")); + n_list = kvp_value_get_glist (kvp_frame_get_slot (n_frame, "list_type")); + + g_assert_cmpint (g_list_length (o_list), ==, g_list_length (n_list)); + while (o_list && n_list) + { + g_assert_cmpint (kvp_value_compare ((KvpValue*)o_list->data, + (KvpValue*)n_list->data), ==, 0); + o_list = g_list_next (o_list); + n_list = g_list_next (n_list); + } + kvp_frame_delete (n_frame); +} + + void test_suite_kvp_frame( void ) { @@ -1492,4 +1717,6 @@ test_suite_kvp_frame( void ) GNC_TEST_ADD( suitename, "get or make", Fixture, NULL, setup_static, test_get_or_make, teardown_static ); GNC_TEST_ADD( suitename, "kvp frame get frame or null slash trash", Fixture, NULL, setup_static, test_kvp_frame_get_frame_or_null_slash_trash, teardown_static ); GNC_TEST_ADD( suitename, "get trailer or null", Fixture, NULL, setup_static, test_get_trailer_or_null, teardown_static ); + GNC_TEST_ADD ( suitename, "kvp frame get gvalue", Fixture, NULL, setup, test_kvp_frame_get_gvalue, teardown); + GNC_TEST_ADD ( suitename, "kvp frame set gvalue", Fixture, NULL, setup, test_kvp_frame_set_gvalue, teardown); } diff --git a/src/libqof/qof/test/test-qofbook.c b/src/libqof/qof/test/test-qofbook.c index 475a5f24448..e6bd41b7ca1 100644 --- a/src/libqof/qof/test/test-qofbook.c +++ b/src/libqof/qof/test/test-qofbook.c @@ -364,100 +364,81 @@ test_book_increment_and_format_counter ( Fixture *fixture, gconstpointer pData ) g_free( r ); } -static void -test_book_kvp_changed( Fixture *fixture, gconstpointer pData ) -{ - g_test_message( "Testing book is marked dirty after kvp_changed" ); - g_assert( !qof_instance_is_dirty (QOF_INSTANCE (fixture->book)) ); - qof_book_kvp_changed( fixture->book ); - g_assert( qof_instance_is_dirty (QOF_INSTANCE (fixture->book)) ); -} - static void test_book_use_trading_accounts( Fixture *fixture, gconstpointer pData ) { - const char *slot_path; - - /* create correct slot path */ - slot_path = (const char *) g_strconcat( KVP_OPTION_PATH, "/", OPTION_SECTION_ACCOUNTS, "/", OPTION_NAME_TRADING_ACCOUNTS, NULL ); - g_assert( slot_path != NULL ); - - g_test_message( "Testing when no trading accounts are used" ); - g_assert( qof_book_use_trading_accounts( fixture-> book ) == FALSE ); - - g_test_message( "Testing with incorrect slot path and correct value - t" ); - qof_book_set_string_option( fixture->book, OPTION_NAME_TRADING_ACCOUNTS, "t" ); g_assert( qof_book_use_trading_accounts( fixture-> book ) == FALSE ); g_test_message( "Testing with existing trading accounts set to true - t" ); - qof_book_set_string_option( fixture->book, slot_path, "t" ); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "trading-accts", "t", + NULL); g_assert( qof_book_use_trading_accounts( fixture-> book ) == TRUE ); g_test_message( "Testing with existing trading accounts and incorrect value - tt" ); - qof_book_set_string_option( fixture->book, slot_path, "tt" ); + qof_instance_set (QOF_INSTANCE (fixture->book), + "trading-accts", "tt", + NULL); g_assert( qof_book_use_trading_accounts( fixture-> book ) == FALSE ); + qof_book_commit_edit (fixture->book); } static void test_book_get_num_days_autofreeze( Fixture *fixture, gconstpointer pData ) { - const char *slot_path; - - /* create correct slot path */ - slot_path = (const char *) g_strconcat( KVP_OPTION_PATH, "/", OPTION_SECTION_ACCOUNTS, "/", OPTION_NAME_AUTO_READONLY_DAYS, NULL ); - g_assert( slot_path != NULL ); - g_test_message( "Testing default: No auto-freeze days are set" ); g_assert( qof_book_uses_autoreadonly( fixture-> book ) == FALSE ); g_assert( qof_book_get_num_days_autoreadonly( fixture-> book ) == 0 ); - g_test_message( "Testing with incorrect slot path and some correct value - 17" ); - kvp_frame_set_double(qof_book_get_slots(fixture->book), OPTION_NAME_AUTO_READONLY_DAYS, 17); g_assert( qof_book_uses_autoreadonly( fixture-> book ) == FALSE ); g_assert( qof_book_get_num_days_autoreadonly( fixture-> book ) == 0 ); - g_test_message( "Testing when setting this correctly with some correct value - 17" ); - kvp_frame_set_double(qof_book_get_slots(fixture->book), slot_path, 17); + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "autoreadonly-days", (gdouble)17, + NULL); g_assert( qof_book_uses_autoreadonly( fixture-> book ) == TRUE ); g_assert( qof_book_get_num_days_autoreadonly( fixture-> book ) == 17 ); g_test_message( "Testing when setting this correctly to zero again" ); - kvp_frame_set_double(qof_book_get_slots(fixture->book), slot_path, 0); + + qof_instance_set (QOF_INSTANCE (fixture->book), + "autoreadonly-days", (gdouble)0, + NULL); g_assert( qof_book_uses_autoreadonly( fixture-> book ) == FALSE ); g_assert( qof_book_get_num_days_autoreadonly( fixture-> book ) == 0 ); - g_test_message( "Testing when setting this correctly with some correct value - 32" ); - kvp_frame_set_double(qof_book_get_slots(fixture->book), slot_path, 32); + qof_instance_set (QOF_INSTANCE (fixture->book), + "autoreadonly-days", (gdouble)32, + NULL); g_assert( qof_book_uses_autoreadonly( fixture-> book ) == TRUE ); g_assert( qof_book_get_num_days_autoreadonly( fixture-> book ) == 32 ); + qof_book_commit_edit (fixture->book); } static void test_book_use_split_action_for_num_field( Fixture *fixture, gconstpointer pData ) { - const char *slot_path; - - /* create correct slot path */ - slot_path = (const char *) g_strconcat( KVP_OPTION_PATH, "/", - OPTION_SECTION_ACCOUNTS, "/", OPTION_NAME_NUM_FIELD_SOURCE, NULL ); - g_assert( slot_path != NULL ); - g_test_message( "Testing default: No selection has been specified" ); g_assert( qof_book_use_split_action_for_num_field( fixture-> book ) == FALSE ); - g_test_message( "Testing with incorrect slot path and correct value - t" ); - qof_book_set_string_option( fixture->book, OPTION_NAME_NUM_FIELD_SOURCE, "t" ); - g_assert( qof_book_use_split_action_for_num_field( fixture-> book ) == FALSE ); - g_test_message( "Testing with existing use split action for num set to true - t" ); - qof_book_set_string_option( fixture->book, slot_path, "t" ); + + qof_book_begin_edit (fixture->book); + qof_instance_set (QOF_INSTANCE (fixture->book), + "split-action-num-field", "t", + NULL); g_assert( qof_book_use_split_action_for_num_field( fixture-> book ) == TRUE ); g_test_message( "Testing with existing use split action for num and incorrect value - tt" ); - qof_book_set_string_option( fixture->book, slot_path, "tt" ); + qof_instance_set (QOF_INSTANCE (fixture->book), + "split-action-num-field", "tt", + NULL); g_assert( qof_book_use_split_action_for_num_field( fixture-> book ) == FALSE ); + qof_book_commit_edit (fixture->book); } static void @@ -775,7 +756,6 @@ test_suite_qofbook ( void ) GNC_TEST_ADD( suitename, "get counter", Fixture, NULL, setup, test_book_get_counter, teardown ); GNC_TEST_ADD( suitename, "get counter format", Fixture, NULL, setup, test_book_get_counter_format, teardown ); GNC_TEST_ADD( suitename, "increment and format counter", Fixture, NULL, setup, test_book_increment_and_format_counter, teardown ); - GNC_TEST_ADD( suitename, "kvp changed", Fixture, NULL, setup, test_book_kvp_changed, teardown ); GNC_TEST_ADD( suitename, "use trading accounts", Fixture, NULL, setup, test_book_use_trading_accounts, teardown ); GNC_TEST_ADD( suitename, "get autofreeze days", Fixture, NULL, setup, test_book_get_num_days_autofreeze, teardown ); GNC_TEST_ADD( suitename, "use split action for num field", Fixture, NULL, setup, test_book_use_split_action_for_num_field, teardown ); diff --git a/src/register/ledger-core/split-register-model-save.c b/src/register/ledger-core/split-register-model-save.c index b22bdee66a4..e95816f4c25 100644 --- a/src/register/ledger-core/split-register-model-save.c +++ b/src/register/ledger-core/split-register-model-save.c @@ -674,10 +674,9 @@ gnc_template_register_save_xfrm_cell (BasicCell * cell, } acctGUID = xaccAccountGetGUID (acct); - kvpf = xaccSplitGetSlots (sd->split); - kvp_frame_set_slot_path (kvpf, kvp_value_new_guid(acctGUID), - GNC_SX_ID, GNC_SX_ACCOUNT, NULL); - + qof_instance_set (QOF_INSTANCE (sd->split), + "sx-account", acctGUID, + NULL); template_acc = xaccAccountLookup (&info->template_account, gnc_get_current_book ()); @@ -700,10 +699,9 @@ gnc_template_register_save_debcred_cell (BasicCell * cell, { SRSaveData *sd = save_data; SplitRegister *reg = user_data; - kvp_frame *kvpf; - const char *value; + const char *credit_formula, *debit_formula; char *error_loc; - gnc_numeric new_amount; + gnc_numeric credit_amount, debit_amount; gboolean parse_result; g_return_if_fail (gnc_basic_cell_has_name (cell, FDEBT_CELL) || @@ -712,54 +710,37 @@ gnc_template_register_save_debcred_cell (BasicCell * cell, if (sd->handled_dc) return; - kvpf = xaccSplitGetSlots (sd->split); - - DEBUG ("kvp_frame before: %s\n", kvp_frame_to_string (kvpf)); - /* amountStr = gnc_numeric_to_string (new_amount); */ - value = gnc_table_layout_get_cell_value (reg->table->layout, FCRED_CELL); - kvp_frame_set_slot_path (kvpf, kvp_value_new_string (value), - GNC_SX_ID, - GNC_SX_CREDIT_FORMULA, - NULL); - + credit_formula = gnc_table_layout_get_cell_value (reg->table->layout, + FCRED_CELL); /* If the value can be parsed into a numeric result (without any * further variable definitions), store that numeric value * additionally in the kvp. Otherwise store a zero numeric * there.*/ - parse_result = gnc_exp_parser_parse_separate_vars(value, &new_amount, &error_loc, NULL); + parse_result = gnc_exp_parser_parse_separate_vars(credit_formula, + &credit_amount, + &error_loc, NULL); if (!parse_result) - { - new_amount = gnc_numeric_zero(); - } - kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_amount), - GNC_SX_ID, - GNC_SX_CREDIT_NUMERIC, - NULL); - - value = gnc_table_layout_get_cell_value (reg->table->layout, FDEBT_CELL); + credit_amount = gnc_numeric_zero(); - kvp_frame_set_slot_path (kvpf, - kvp_value_new_string (value), - GNC_SX_ID, - GNC_SX_DEBIT_FORMULA, - NULL); + debit_formula = gnc_table_layout_get_cell_value (reg->table->layout, + FDEBT_CELL); /* If the value can be parsed into a numeric result, store that * numeric value additionally. See above comment.*/ - parse_result = gnc_exp_parser_parse_separate_vars(value, &new_amount, &error_loc, NULL); + parse_result = gnc_exp_parser_parse_separate_vars(debit_formula, + &debit_amount, + &error_loc, NULL); if (!parse_result) - { - new_amount = gnc_numeric_zero(); - } - kvp_frame_set_slot_path (kvpf, kvp_value_new_numeric (new_amount), - GNC_SX_ID, - GNC_SX_DEBIT_NUMERIC, - NULL); - - DEBUG ("kvp_frame after: %s\n", kvp_frame_to_string (kvpf)); - + debit_amount = gnc_numeric_zero(); + + qof_instance_set (QOF_INSTANCE (sd->split), + "sx-credit-formula", credit_formula, + "sx-credit-numeric", &credit_amount, + "sx-debit-formula", debit_formula, + "sx-debit-numeric", &debit_amount, + NULL); /* set the amount to an innocuous value */ /* Note that this marks the split dirty */ xaccSplitSetValue (sd->split, gnc_numeric_create (0, 1)); @@ -773,24 +754,13 @@ gnc_template_register_save_shares_cell (BasicCell * cell, gpointer user_data) { SRSaveData *sd = save_data; - kvp_frame *kvpf; char *sharesStr = "(x + y)/42"; g_return_if_fail (gnc_basic_cell_has_name (cell, SHRS_CELL)); - - kvpf = xaccSplitGetSlots (sd->split); - /* FIXME: shares cells are numeric by definition. */ - DEBUG ("kvp_frame before: %s\n", kvp_frame_to_string (kvpf)); - - /* sharesStr = gnc_numeric_to_string( sharesStr ); */ - kvp_frame_set_slot_path (kvpf, - kvp_value_new_string (sharesStr), - GNC_SX_ID, - GNC_SX_SHARES, - NULL); - - DEBUG ("kvp_frame after: %s\n", kvp_frame_to_string (kvpf)); + qof_instance_set (QOF_INSTANCE (sd->split), + "sx-shares", sharesStr, + NULL); /* set the shares to an innocuous value */ /* Note that this marks the split dirty */ diff --git a/src/register/ledger-core/split-register-model.c b/src/register/ledger-core/split-register-model.c index 5af4a3b5396..503c9ba6529 100644 --- a/src/register/ledger-core/split-register-model.c +++ b/src/register/ledger-core/split-register-model.c @@ -2091,31 +2091,23 @@ gnc_template_register_get_xfrm_entry (VirtualLocation virt_loc, static char *name = NULL; SplitRegister *reg = user_data; - kvp_frame *kvpf; Split *split; + Account *account; + GncGUID *guid = NULL; split = gnc_split_register_get_split (reg, virt_loc.vcell_loc); if (!split) return NULL; - - kvpf = xaccSplitGetSlots (split); - + /* Caller either uses the return as a temporary in a boolean + * expression or g_strdups it, so we keep it static and free the + * old one on every call to avoid leaks. Ugly, but it works. + */ g_free (name); - - if (kvpf) - { - Account *account; - GncGUID *guid; - - guid = kvp_value_get_guid( - kvp_frame_get_slot_path(kvpf, "sched-xaction", "account", NULL)); - - account = xaccAccountLookup (guid, gnc_get_current_book ()); - - name = account ? gnc_get_account_name_for_register (account) : NULL; - } - else - name = NULL; + qof_instance_get (QOF_INSTANCE (split), + "sx-account", &guid, + NULL); + account = xaccAccountLookup (guid, gnc_get_current_book ()); + name = account ? gnc_get_account_name_for_register (account) : NULL; return name; } @@ -2128,10 +2120,13 @@ gnc_template_register_get_fdebt_entry (VirtualLocation virt_loc, { SplitRegister *reg = user_data; Split *split = gnc_split_register_get_split(reg, virt_loc.vcell_loc); - kvp_frame *kvpf = xaccSplitGetSlots(split); + char *formula = NULL; - return kvp_value_get_string( - kvp_frame_get_slot_path (kvpf, "sched-xaction", "debit-formula", NULL)); + qof_instance_get (QOF_INSTANCE (split), + "sx-debit-formula", &formula, + NULL); + + return formula; } static char * @@ -2155,14 +2150,15 @@ gnc_template_register_get_fcred_entry (VirtualLocation virt_loc, gpointer user_data) { SplitRegister *reg = user_data; - kvp_frame *kvpf; - Split *split; + Split *split = gnc_split_register_get_split(reg, virt_loc.vcell_loc); + char *formula = NULL; - split = gnc_split_register_get_split (reg, virt_loc.vcell_loc); - kvpf = xaccSplitGetSlots (split); + qof_instance_get (QOF_INSTANCE (split), + "sx-credit-formula", &formula, + NULL); + + return formula; - return kvp_value_get_string( - kvp_frame_get_slot_path (kvpf, "sched-xaction", "credit-formula", NULL)); } static char * @@ -2194,8 +2190,13 @@ gnc_template_register_get_debcred_entry (VirtualLocation virt_loc, gboolean *conditionally_changed, gpointer user_data) { + PERR("The function called always returned either NULL or an empty string " + "while issuing dire warnings about how incorrect it is. That code " + "has been removed and the function if called raises this error and " + "returns NULL"); + return NULL; +#if 0 SplitRegister *reg = user_data; - kvp_frame *kvpf; Split *split; split = gnc_split_register_get_split (reg, virt_loc.vcell_loc); @@ -2241,6 +2242,7 @@ gnc_template_register_get_debcred_entry (VirtualLocation virt_loc, } return NULL; +#endif } static void diff --git a/src/report/report-gnome/gnc-plugin-page-report.c b/src/report/report-gnome/gnc-plugin-page-report.c index 88781770bbe..828f4b53f7f 100644 --- a/src/report/report-gnome/gnc-plugin-page-report.c +++ b/src/report/report-gnome/gnc-plugin-page-report.c @@ -1778,8 +1778,6 @@ gnc_plugin_page_report_print_cb( GtkAction *action, GncPluginPageReport *report g_free (job_name); } -#define KVP_OWNER_EXPORT_PDF_DIRNAME "export-pdf-directory" - static void gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *report ) { @@ -1787,7 +1785,6 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep gchar *job_name = report_create_jobname(priv); GncInvoice *invoice; GncOwner *owner = NULL; - KvpFrame *kvp = NULL; // Do we have an invoice report? invoice = lookup_invoice(priv); @@ -1797,20 +1794,19 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep owner = (GncOwner*) gncInvoiceGetOwner(invoice); if (owner) { + QofInstance *inst = qofOwnerGetOwner (owner); + gchar *dirname = NULL; + qof_instance_get (inst, "export-pdf-dir", &dirname, NULL); // Yes. In the kvp, look up the key for the Export-PDF output // directory. If it exists, prepend this to the job name so that // we can export to PDF. - kvp = gncOwnerGetSlots(owner); - if (kvp) - { - const char *dirname = kvp_frame_get_string(kvp, KVP_OWNER_EXPORT_PDF_DIRNAME); - if (dirname && g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) - { - gchar *tmp = g_build_filename(dirname, job_name, NULL); - g_free(job_name); - job_name = tmp; - } - } + if (dirname && g_file_test(dirname, + G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) + { + gchar *tmp = g_build_filename(dirname, job_name, NULL); + g_free(job_name); + job_name = tmp; + } } } @@ -1818,10 +1814,12 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep gnc_html_print(priv->html, job_name, TRUE); - if (owner && kvp) + if (owner) { - // As this is an invoice report with some owner, we will try to look up the - // chosen output directory from the print settings and store it again in the owner kvp. + /* As this is an invoice report with some owner, we will try + * to look up the chosen output directory from the print + * settings and store it again in the owner kvp. + */ GtkPrintSettings *print_settings = gnc_print_get_settings(); if (print_settings && gtk_print_settings_has_key(print_settings, @@ -1832,15 +1830,10 @@ gnc_plugin_page_report_exportpdf_cb( GtkAction *action, GncPluginPageReport *rep // Only store the directory if it exists. if (g_file_test(dirname, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - QofInstance *qofinstance = qofOwnerGetOwner(owner); - if (qofinstance) - { - gncOwnerBeginEdit(owner); - kvp_frame_set_string(kvp, KVP_OWNER_EXPORT_PDF_DIRNAME, - dirname); - qof_instance_set_dirty(qofinstance); - qof_commit_edit (qofinstance); - } + QofInstance *inst = qofOwnerGetOwner(owner); + gncOwnerBeginEdit(owner); + qof_instance_set (inst, "export-pdf-dir", dirname); + gncOwnerCommitEdit(owner); } } } diff --git a/test-templates/Makefile.decl b/test-templates/Makefile.decl index dcc69c1ec0e..0f163a32389 100644 --- a/test-templates/Makefile.decl +++ b/test-templates/Makefile.decl @@ -23,7 +23,7 @@ endif # test-nonrecursive: run tests only in cwd test-nonrecursive: ${TEST_PROGS} if !PLATFORM_WIN32 - @test -z "${TEST_PROGS}" || MALLOC_CHECK_=2 MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) ${GTESTER} --verbose ${TEST_PROGS} + @test -z "${TEST_PROGS}" || MALLOC_CHECK_=2 MALLOC_PERTURB_=$$(($${RANDOM:-256} % 256)) ${TESTS_ENVIRONMENT} ${GTESTER} --verbose ${TEST_PROGS} endif # test-report: run tests in subdirs and generate report