From b1c5eb14779355db4ccbc09dd99b2dc0fb367ae3 Mon Sep 17 00:00:00 2001 From: Christian Stimming Date: Sun, 29 Aug 2010 20:37:20 +0000 Subject: [PATCH] Implement the evaluation of SX cashflow. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@19511 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/app-utils/gnc-sx-instance-model.c | 186 +++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 6 deletions(-) diff --git a/src/app-utils/gnc-sx-instance-model.c b/src/app-utils/gnc-sx-instance-model.c index cb25118762..5892668197 100644 --- a/src/app-utils/gnc-sx-instance-model.c +++ b/src/app-utils/gnc-sx-instance-model.c @@ -1467,36 +1467,210 @@ GHashTable* gnc_g_hash_new_guid_numeric() } typedef struct { - GHashTable *hash; - GList **creation_errors; + GHashTable *hash; + GList **creation_errors; + const SchedXaction *sx; + gnc_numeric count; } SxCashflowData; +static void add_to_hash_amount(GHashTable* hash, const GncGUID* guid, const gnc_numeric* amount) +{ + gnc_numeric* elem = g_hash_table_lookup(hash, guid); + if (!elem) + { + elem = g_new0(gnc_numeric, 1); + *elem = gnc_numeric_zero(); + g_hash_table_insert(hash, (gpointer) guid, elem); + } + *elem = gnc_numeric_add_fixed(*elem, *amount); + g_debug("Adding to guid [%s] the value [%s]. Value now [%s].", + guid_to_string(guid), + gnc_num_dbg_to_string(*amount), + gnc_num_dbg_to_string(*elem)); +} + static gboolean create_cashflow_helper(Transaction *template_txn, void *user_data) { -/* FIXME: Still unfinished here! */ + SxCashflowData *creation_data = user_data; + GList *template_splits; + gboolean err_flag = FALSE; + const gnc_commodity *first_cmdty = NULL; + + g_debug("Evaluating txn desc [%s] for sx [%s]", + xaccTransGetDescription(template_txn), + xaccSchedXactionGetName(creation_data->sx)); + + /* The accounts and amounts are in the kvp_frames of the + * splits. Hence, we iterate over all splits of this + * transaction. */ + template_splits = xaccTransGetSplitList(template_txn); + + if (template_splits == NULL) + { + g_critical("transaction w/o splits for sx [%s]", + xaccSchedXactionGetName(creation_data->sx)); + return FALSE; + } + + for (; + template_splits; + template_splits = template_splits->next) + { + Account *split_acct; + const gnc_commodity *split_cmdty = NULL; + const Split *template_split = (const Split*) template_splits->data; + + /* Get the account that should be used for this split. */ + if (!_get_template_split_account(creation_data->sx, template_split, &split_acct, creation_data->creation_errors)) + { + g_debug("Could not find account for split"); + err_flag = TRUE; + break; + } + + /* The split's account also has some commodity */ + split_cmdty = xaccAccountGetCommodity(split_acct); + if (first_cmdty == NULL) + { + first_cmdty = split_cmdty; + //xaccTransSetCurrency(new_txn, first_cmdty); + } + + { + gnc_numeric credit_num = gnc_numeric_zero(); + gnc_numeric debit_num = gnc_numeric_zero(); + gnc_numeric final_once, final; + gint gncn_error; + + /* Credit value */ + _get_sx_formula_value(creation_data->sx, template_split, &credit_num, creation_data->creation_errors, GNC_SX_CREDIT_FORMULA, NULL); + /* Debit value */ + _get_sx_formula_value(creation_data->sx, template_split, &debit_num, creation_data->creation_errors, GNC_SX_DEBIT_FORMULA, NULL); + + /* The resulting cash flow number: debit minus credit, + * multiplied with the count factor. */ + final_once = gnc_numeric_sub_fixed( debit_num, credit_num ); + /* Multiply with the count factor. */ + final = gnc_numeric_mul(final_once, creation_data->count, + gnc_numeric_denom(final_once), + GNC_HOW_RND_ROUND); + + gncn_error = gnc_numeric_check(final); + if (gncn_error != GNC_ERROR_OK) + { + GString *err = g_string_new(""); + g_string_printf(err, "error %d in SX [%s] final gnc_numeric value, using 0 instead", + gncn_error, xaccSchedXactionGetName(creation_data->sx)); + g_critical("%s", err->str); + if (creation_data->creation_errors != NULL) + *creation_data->creation_errors = g_list_append(*creation_data->creation_errors, err); + else + g_string_free(err, TRUE); + final = gnc_numeric_zero(); + } + + /* Print error message if we would have needed an exchange rate */ + if (! gnc_commodity_equal(split_cmdty, first_cmdty)) + { + GString *err = g_string_new(""); + g_string_printf(err, "No exchange rate available in SX [%s] for %s -> %s, value is zero", + xaccSchedXactionGetName(creation_data->sx), + gnc_commodity_get_mnemonic(split_cmdty), + gnc_commodity_get_mnemonic(first_cmdty)); + g_critical("%s", err->str); + if (creation_data->creation_errors != NULL) + *creation_data->creation_errors = g_list_append(*creation_data->creation_errors, err); + else + g_string_free(err, TRUE); + final = gnc_numeric_zero(); + } + + /* And add the resulting value to the hash */ + add_to_hash_amount(creation_data->hash, xaccAccountGetGUID(split_acct), &final); + } + } + return FALSE; } -void gnc_sx_instantiate_cashflow(const SchedXaction* sx, - GHashTable* map, GList **creation_errors) +static void +instantiate_cashflow_internal(const SchedXaction* sx, + GHashTable* map, + GList **creation_errors, gint count) { SxCashflowData create_cashflow_data; Account* sx_template_account = gnc_sx_get_template_transaction_account(sx); + if (!sx_template_account) + { + g_critical("Huh? No template account for the SX %s", xaccSchedXactionGetName(sx)); + return; + } + create_cashflow_data.hash = map; create_cashflow_data.creation_errors = creation_errors; + create_cashflow_data.sx = sx; + create_cashflow_data.count = gnc_numeric_create(count, 1); + /* The cash flow numbers are in the transactions of the template + * account, so run this foreach on the transactions. */ xaccAccountForEachTransaction(sx_template_account, create_cashflow_helper, &create_cashflow_data); } +void gnc_sx_instantiate_cashflow(const SchedXaction* sx, + GHashTable* map, GList **creation_errors) +{ + /* Calculate ("Instantiate") the cash flow for exactly one + * occurrence */ + instantiate_cashflow_internal(sx, map, creation_errors, 1); +} + +typedef struct { + GHashTable *hash; + GList **creation_errors; + const GDate *range_start; + const GDate *range_end; +} SxAllCashflow; + +static void instantiate_cashflow_cb(gpointer data, gpointer _user_data) +{ + const SchedXaction* sx = (const SchedXaction*) data; + SxAllCashflow* userdata = (SxAllCashflow*) _user_data; + gint count; + + g_assert(sx); + g_assert(userdata); + + /* How often does this particular SX occur in the date range? */ + count = gnc_sx_get_num_occur_daterange(sx, userdata->range_start, + userdata->range_end); + if (count > 0) + { + /* If it occurs at least once, calculate ("instantiate") its + * cash flow and add it to the result + * g_hash */ + instantiate_cashflow_internal(sx, + userdata->hash, + userdata->creation_errors, + count); + } +} + void gnc_sx_all_instantiate_cashflow(GList *all_sxes, const GDate *range_start, const GDate *range_end, GHashTable* map, GList **creation_errors) { - /* FIXME: Still unfinished here! */ + SxAllCashflow userdata; + userdata.hash = map; + userdata.creation_errors = creation_errors; + userdata.range_start = range_start; + userdata.range_end = range_end; + + /* The work is done in the callback for each SX */ + g_list_foreach(all_sxes, instantiate_cashflow_cb, &userdata); } // Local Variables: