gnc_numeric gnc_split_register_debcred_cell_value (SplitRegister *reg) { PriceCell *cell; gnc_numeric new_amount; gnc_numeric credit; gnc_numeric debit; cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout, CRED_CELL); credit = gnc_price_cell_get_value (cell); cell = (PriceCell *) gnc_table_layout_get_cell (reg->table->layout, DEBT_CELL); debit = gnc_price_cell_get_value (cell); new_amount = gnc_numeric_sub_fixed (debit, credit); return new_amount; }
static gboolean create_each_transaction_helper(Transaction *template_txn, void *user_data) { Transaction *new_txn; GList *txn_splits, *template_splits; Split *copying_split; gnc_commodity *first_cmdty = NULL; gboolean err_flag = FALSE; SxTxnCreationData *creation_data; creation_data = (SxTxnCreationData*)user_data; /* FIXME: In general, this should [correctly] deal with errors such as not finding the approrpiate Accounts and not being able to parse the formula|credit/debit strings. */ new_txn = xaccTransClone(template_txn); xaccTransBeginEdit(new_txn); g_debug("creating template txn desc [%s] for sx [%s]", xaccTransGetDescription(new_txn), xaccSchedXactionGetName(creation_data->instance->parent->sx)); /* 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) { xaccTransSetNotes(new_txn, g_strdup(xaccTransGetNotes(template_txn))); } xaccTransSetDate(new_txn, g_date_get_day(&creation_data->instance->date), g_date_get_month(&creation_data->instance->date), g_date_get_year(&creation_data->instance->date)); /* the accounts and amounts are in the kvp_frames of the splits. */ template_splits = xaccTransGetSplitList(template_txn); txn_splits = xaccTransGetSplitList(new_txn); if ((template_splits == NULL) || (txn_splits == NULL)) { g_critical("transaction w/o splits for sx [%s]", xaccSchedXactionGetName(creation_data->instance->parent->sx)); xaccTransDestroy(new_txn); xaccTransCommitEdit(new_txn); return FALSE; } for (; txn_splits && template_splits; txn_splits = txn_splits->next, template_splits = template_splits->next) { Split *template_split; Account *split_acct; gnc_commodity *split_cmdty = NULL; /* FIXME: Ick. This assumes that the split lists will be ordered identically. :( They are, but we'd rather not have to count on it. --jsled */ template_split = (Split*)template_splits->data; copying_split = (Split*)txn_splits->data; if (!_get_template_split_account(creation_data->instance, template_split, &split_acct, creation_data->creation_errors)) { err_flag = TRUE; 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) { first_cmdty = split_cmdty; xaccTransSetCurrency(new_txn, first_cmdty); } xaccSplitSetAccount(copying_split, split_acct); { gnc_numeric credit_num, debit_num, final; gint gncn_error; credit_num = gnc_numeric_zero(); debit_num = gnc_numeric_zero(); _get_credit_formula_value(creation_data->instance, template_split, &credit_num, creation_data->creation_errors); _get_debit_formula_value(creation_data->instance, template_split, &debit_num, creation_data->creation_errors); final = gnc_numeric_sub_fixed( debit_num, credit_num ); 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->instance->parent->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(); }
void xaccScrubSubSplitPrice (Split *split, int maxmult, int maxamtscu) { gnc_numeric src_amt, src_val; SplitList *node; if (FALSE == is_subsplit (split)) return; ENTER (" "); /* Get 'price' of the indicated split */ src_amt = xaccSplitGetAmount (split); src_val = xaccSplitGetValue (split); /* Loop over splits, adjust each so that it has the same * ratio (i.e. price). Change the value to get things * right; do not change the amount */ for (node = split->parent->splits; node; node = node->next) { Split *s = node->data; Transaction *txn = s->parent; gnc_numeric dst_amt, dst_val, target_val; gnc_numeric frac, delta; int scu; /* Skip the reference split */ if (s == split) continue; scu = gnc_commodity_get_fraction (txn->common_currency); dst_amt = xaccSplitGetAmount (s); dst_val = xaccSplitGetValue (s); frac = gnc_numeric_div (dst_amt, src_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); target_val = gnc_numeric_mul (frac, src_val, scu, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP); if (gnc_numeric_check (target_val)) { PERR ("Numeric overflow of value\n" "\tAcct=%s txn=%s\n" "\tdst_amt=%s src_val=%s src_amt=%s\n", xaccAccountGetName (s->acc), xaccTransGetDescription(txn), gnc_num_dbg_to_string(dst_amt), gnc_num_dbg_to_string(src_val), gnc_num_dbg_to_string(src_amt)); continue; } /* If the required price changes are 'small', do nothing. * That is a case that the user will have to deal with * manually. This routine is really intended only for * a gross level of synchronization. */ delta = gnc_numeric_sub_fixed (target_val, dst_val); delta = gnc_numeric_abs (delta); if (maxmult * delta.num < delta.denom) continue; /* If the amount is small, pass on that too */ if ((-maxamtscu < dst_amt.num) && (dst_amt.num < maxamtscu)) continue; /* Make the actual adjustment */ xaccTransBeginEdit (txn); xaccSplitSetValue (s, target_val); xaccTransCommitEdit (txn); } LEAVE (" "); }
void gnc_autoclear_window_ok_cb (GtkWidget *widget, AutoClearWindow *data) { GList *node, *nc_list = 0, *toclear_list = 0; gnc_numeric toclear_value; GHashTable *sack; gtk_label_set_text(data->status_label, _("Searching for splits to clear ...")); /* Value we have to reach */ toclear_value = gnc_amount_edit_get_amount(data->end_value); toclear_value = gnc_numeric_convert(toclear_value, xaccAccountGetCommoditySCU(data->account), GNC_HOW_RND_NEVER); /* Extract which splits are not cleared and compute the amount we have to clear */ for (node = xaccAccountGetSplitList(data->account); node; node = node->next) { Split *split = (Split *)node->data; char recn; gnc_numeric value; recn = xaccSplitGetReconcile (split); value = xaccSplitGetAmount (split); if (recn == NREC) nc_list = g_list_append(nc_list, split); else toclear_value = gnc_numeric_sub_fixed(toclear_value, value); } /* Pretty print information */ PINFO("Amount to clear: %s\n", gnc_numeric_to_string(toclear_value)); PINFO("Available splits:\n"); for (node = nc_list; node; node = node->next) { Split *split = (Split *)node->data; gnc_numeric value = xaccSplitGetAmount (split); PINFO(" %s\n", gnc_numeric_to_string(value)); } /* Run knapsack */ /* Entries in the hash table are: * - key = amount to which we know how to clear (freed by GHashTable) * - value = last split we used to clear this amount (not managed by GHashTable) */ PINFO("Knapsacking ...\n"); sack = g_hash_table_new_full (ght_gnc_numeric_hash, ght_gnc_numeric_equal, g_free, NULL); for (node = nc_list; node; node = node->next) { Split *split = (Split *)node->data; gnc_numeric split_value = xaccSplitGetAmount(split); GList *node; struct _sack_foreach_data_t data[1]; data->split_value = split_value; data->reachable_list = 0; PINFO(" Split value: %s\n", gnc_numeric_to_string(split_value)); /* For each value in the sack, compute a new reachable value */ g_hash_table_foreach (sack, sack_foreach_func, data); /* Add the value of the split itself to the reachable_list */ data->reachable_list = g_list_append(data->reachable_list, g_memdup(&split_value, sizeof(gnc_numeric))); /* Add everything to the sack, looking out for duplicates */ for (node = data->reachable_list; node; node = node->next) { gnc_numeric *reachable_value = node->data; Split *toinsert_split = split; PINFO(" Reachable value: %s ", gnc_numeric_to_string(*reachable_value)); /* Check if it already exists */ if (g_hash_table_lookup_extended(sack, reachable_value, NULL, NULL)) { /* If yes, we are in trouble, we reached an amount using two solutions */ toinsert_split = NULL; PINFO("dup"); } g_hash_table_insert (sack, reachable_value, toinsert_split); PINFO("\n"); } g_list_free(data->reachable_list); } /* Check solution */ PINFO("Rebuilding solution ...\n"); while (!gnc_numeric_zero_p(toclear_value)) { gpointer psplit = NULL; PINFO(" Left to clear: %s\n", gnc_numeric_to_string(toclear_value)); if (g_hash_table_lookup_extended(sack, &toclear_value, NULL, &psplit)) { if (psplit != NULL) { /* Cast the gpointer to the kind of pointer we actually need */ Split *split = (Split *)psplit; toclear_list = g_list_prepend(toclear_list, split); toclear_value = gnc_numeric_sub_fixed(toclear_value, xaccSplitGetAmount(split)); PINFO(" Cleared: %s -> %s\n", gnc_numeric_to_string(xaccSplitGetAmount(split)), gnc_numeric_to_string(toclear_value)); } else { /* We couldn't reconstruct the solution */ PINFO(" Solution not unique.\n"); gtk_label_set_text(data->status_label, _("Cannot uniquely clear splits. Found multiple possibilities.")); return; } } else { PINFO(" No solution found.\n"); gtk_label_set_text(data->status_label, _("The selected amount cannot be cleared.")); return; } } g_hash_table_destroy (sack); /* Show solution */ PINFO("Clearing splits:\n"); for (node = toclear_list; node; node = node->next) { Split *split = node->data; char recn; gnc_numeric value; recn = xaccSplitGetReconcile (split); value = xaccSplitGetAmount (split); PINFO(" %c %s\n", recn, gnc_numeric_to_string(value)); xaccSplitSetReconcile (split, CREC); } if (toclear_list == 0) PINFO(" None\n"); /* Free lists */ g_list_free(nc_list); g_list_free(toclear_list); /* Close window */ gtk_widget_destroy(data->window); g_free(data); }