// A helper function that takes two splits. If the splits are of opposite sign // it reduces the biggest split to have the same value (but with opposite sign) // of the smaller split. // To make sure everything still continues to balance in addition a "remainder" split // will be created that will be added to the same lot and transaction as the biggest // split. // The opposite sign restriction is because that's the only scenario that makes sense // in the context of scrubbing business lots below. // If we created new splits, return TRUE, otherwise FALSE static gboolean reduce_biggest_split (Split *splitA, Split *splitB) { gnc_numeric valA = xaccSplitGetValue (splitA); gnc_numeric valB = xaccSplitGetValue (splitB); if (gnc_numeric_compare (gnc_numeric_abs (valA), gnc_numeric_abs (valB)) >= 0) return gncOwnerReduceSplitTo (splitA, gnc_numeric_neg (valB)); else return gncOwnerReduceSplitTo (splitB, gnc_numeric_neg (valA)); }
static void gncOwnerOffsetLots (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner) { gnc_numeric target_offset; Split *split; /* from lot should not be a document lot because we're removing a split from there ! */ if (gncInvoiceGetInvoiceFromLot (from_lot)) { PWARN ("from_lot %p is a document lot. That is not allowed in gncOwnerOffsetLots", from_lot); return; } /* Get best matching split from from_lot to offset to_lot */ target_offset = gnc_lot_get_balance (to_lot); if (gnc_numeric_zero_p (target_offset)) return; // to_lot is already balanced, nothing more to do split = gncOwnerFindOffsettingSplit (from_lot, target_offset); if (!split) return; // No suitable offsetting split found, nothing more to do /* If the offsetting split is bigger than the amount needed to balance * to_lot, reduce the split so its reduced value closes to_lot exactly. * Note the negation in the reduction function. The split must be of * opposite sign of to_lot's balance in order to be able to close it. */ if (gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (split)), gnc_numeric_abs (target_offset)) > 0) gncOwnerReduceSplitTo (split, gnc_numeric_neg (target_offset)); /* Move the reduced split from from_lot to to_lot */ gnc_lot_add_split (to_lot, split); }
static const char * get_taxval_entry (VirtualLocation virt_loc, gboolean translate, gboolean *conditionally_changed, gpointer user_data) { GncEntryLedger *ledger = user_data; gnc_numeric value; /* Check if this is the current cursor */ if (virt_cell_loc_equal (ledger->table->current_cursor_loc.vcell_loc, virt_loc.vcell_loc)) { gnc_entry_ledger_compute_value (ledger, NULL, &value); } else { GncEntry *entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc); if (entry == gnc_entry_ledger_get_blank_entry (ledger)) return NULL; value = gncEntryReturnTaxValue (entry, ledger->is_cust_doc); } /* Credit notes have negative values, but the ledger should * display it as on the document, meaning positive. * So reverse the value for credit notes. */ if (ledger->is_credit_note) value = gnc_numeric_neg (value); return xaccPrintAmount (value, gnc_default_print_info (FALSE)); }
static void check_neg (void) { gnc_numeric a = gnc_numeric_create(2, 6); gnc_numeric b = gnc_numeric_create(1, 4); gnc_numeric c = gnc_numeric_neg (a); gnc_numeric d = gnc_numeric_neg (b); check_unary_op (gnc_numeric_eq, gnc_numeric_create (-2, 6), c, a, "expected %s got %s = -(%s)"); check_unary_op (gnc_numeric_eq, gnc_numeric_create (-1, 4), d, b, "expected %s got %s = -(%s)"); }
void gncEntrySetDocQuantity (GncEntry *entry, gnc_numeric quantity, gboolean is_cn) { if (!entry) return; if (gnc_numeric_eq (entry->quantity, quantity)) return; gncEntryBeginEdit (entry); entry->quantity = (is_cn ? gnc_numeric_neg (quantity) : quantity); entry->values_dirty = TRUE; mark_entry (entry); gncEntryCommitEdit (entry); }
static void starting_balance_helper (Account *account, hierarchy_data *data) { gnc_numeric balance; balance = get_final_balance (data->balance_hash, account); if (gnc_reverse_balance(account)) balance = gnc_numeric_neg(balance); if (!gnc_numeric_zero_p (balance)) gnc_account_create_opening_balance (account, balance, gnc_time (NULL), gnc_get_current_book ()); }
static void * negate_numeric(void *value) { ParserNum *result = value; if (value == NULL) return NULL; result->value = gnc_numeric_neg (result->value); return result; }
static void test_entry_basics ( Fixture *fixture, gconstpointer pData ) { time64 ts1 = gnc_time(NULL), ts2; const char *desc = "Test description with éà unicode chars"; const char *action = "Test action with éà unicode chars"; const char *note = "Test note with éà unicode chars"; gnc_numeric quantity = {500000, 100}; gboolean is_cn = FALSE; GncEntry *entry = gncEntryCreate(fixture->book); g_assert(entry); g_test_message( "Test basic setters/getters" ); g_test_message( " Date" ); gncEntrySetDate (entry, ts1); ts2 = gncEntryGetDate (entry); g_assert(ts2 == ts1); g_test_message( " DateEntered" ); gncEntrySetDateEntered (entry, ts1); ts2 = gncEntryGetDateEntered (entry); g_assert(ts2 == ts1); g_test_message( " Description" ); gncEntrySetDescription (entry, desc); g_assert(g_strcmp0 (gncEntryGetDescription (entry), desc) == 0); g_test_message( " Action" ); gncEntrySetAction (entry, action); g_assert(g_strcmp0 (gncEntryGetAction (entry), action) == 0); g_test_message( " Notes" ); gncEntrySetNotes (entry, note); g_assert(g_strcmp0 (gncEntryGetNotes (entry), note) == 0); g_test_message( " Quantity" ); gncEntrySetQuantity (entry, quantity); g_assert(gnc_numeric_eq (gncEntryGetQuantity (entry), quantity)); g_test_message( " DocQuantity (with is_cn = FALSE)" ); gncEntrySetDocQuantity (entry, quantity, is_cn); g_assert(gnc_numeric_eq (gncEntryGetDocQuantity (entry, is_cn), quantity)); g_assert(gnc_numeric_eq (gncEntryGetQuantity (entry), quantity)); g_test_message( " DocQuantity (with is_cn = TRUE)"); is_cn = TRUE; gncEntrySetDocQuantity (entry, quantity, is_cn); g_assert(gnc_numeric_eq (gncEntryGetDocQuantity (entry, is_cn), quantity)); g_assert(gnc_numeric_eq (gncEntryGetQuantity (entry), gnc_numeric_neg (quantity))); g_test_message( " InvAccount" ); gncEntrySetInvAccount (entry, fixture->account); g_assert(gncEntryGetInvAccount (entry) == fixture->account); }
/* Careful: the returned list is NOT owned by the entry and should be freed by the caller */ AccountValueList * gncEntryGetBalTaxValues (GncEntry *entry, gboolean is_cust_doc) { AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc); AccountValueList *values = NULL, *node; /* Make a copy of the list with negated values if necessary. */ for (node = int_values; node; node = node->next) { GncAccountValue *acct_val = node->data; values = gncAccountValueAdd (values, acct_val->account, (is_cust_doc ? gnc_numeric_neg (acct_val->value) : acct_val->value)); } return values; }
void gnc_price_cell_set_debt_credit_value (PriceCell * debit, PriceCell * credit, gnc_numeric amount) { /* debits are positive, credits are negative */ if (gnc_numeric_positive_p (amount)) { gnc_price_cell_set_value (debit, amount); gnc_price_cell_set_value (credit, gnc_numeric_zero ()); } else { gnc_price_cell_set_value (debit, gnc_numeric_zero ()); gnc_price_cell_set_value (credit, gnc_numeric_neg (amount)); } }
static const char * get_value_entry (VirtualLocation virt_loc, gboolean translate, gboolean *conditionally_changed, gpointer user_data) { GncEntryLedger *ledger = user_data; gnc_numeric value; /* Credit notes need some attention here: the ledger displays values * as on the document, meaning positive for credit notes. Credit note * values are negative internally though. So depending on which values * are used to calculate the subtotal, the resulting subtotal has to be * sign-reversed before displaying. */ /* Check if this is the current cursor */ if (virt_cell_loc_equal (ledger->table->current_cursor_loc.vcell_loc, virt_loc.vcell_loc)) { gnc_entry_ledger_compute_value (ledger, &value, NULL); /* Credit note info: this function works with values as seen * on-screen in the ledger, so they are always in the proper sign. * As per the above no sign reversal is needed for * credit note type ledgers. */ } else { GncEntry *entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc); if (entry == gnc_entry_ledger_get_blank_entry (ledger)) return NULL; value = gncEntryReturnValue (entry, ledger->is_cust_doc); /* Credit note info: this function works with internal values, * so they are negative for credit note type ledgers and have to * be sign-reversed as per the above. */ if (ledger->is_credit_note) value = gnc_numeric_neg (value); } return xaccPrintAmount (value, gnc_default_print_info (FALSE)); }
static Transaction* gsr2_create_balancing_transaction (QofBook *book, Account *account, time64 statement_date, gnc_numeric balancing_amount) { Transaction *trans; Split *split; if (!account) return NULL; if (gnc_numeric_zero_p (balancing_amount)) return NULL; xaccAccountBeginEdit (account); trans = xaccMallocTransaction (book); xaccTransBeginEdit (trans); // fill Transaction xaccTransSetCurrency (trans, gnc_account_or_default_currency (account, NULL)); xaccTransSetDatePostedSecsNormalized (trans, statement_date); xaccTransSetDescription (trans, _("Balancing entry from reconciliation")); // 1. Split split = xaccMallocSplit (book); xaccTransAppendSplit (trans, split); xaccAccountInsertSplit (account, split); xaccSplitSetAmount (split, balancing_amount); xaccSplitSetValue (split, balancing_amount); // 2. Split (no account is defined: split goes to orphan account) split = xaccMallocSplit (book); xaccTransAppendSplit (trans, split); balancing_amount = gnc_numeric_neg (balancing_amount); xaccSplitSetAmount (split, balancing_amount); xaccSplitSetValue (split, balancing_amount); xaccTransCommitEdit (trans); xaccAccountCommitEdit (account); return trans; }
void gnc_ui_payment_window_set_amount (PaymentWindow *pw, gnc_numeric amount) { g_assert(pw); /* Debits are negative, credits are positive */ if (gnc_numeric_positive_p (amount)) { gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_credit_edit), amount); gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_debit_edit), gnc_numeric_zero ()); } else { gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_debit_edit), gnc_numeric_neg (amount)); gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(pw->amount_credit_edit), gnc_numeric_zero ()); } }
/** * Duplicate-code reduction function; retreives, formats and updates the * GtkLabel with the given amount. **/ static void gsr2_update_summary_label (GtkWidget *label, xaccGetBalanceFn getter, Account *leader, GNCPrintAmountInfo print_info, gnc_commodity *cmdty, gboolean reverse, gboolean euroFlag) { gnc_numeric amount; char string[256]; if ( label == NULL ) return; amount = (*getter)( leader ); if ( reverse ) { amount = gnc_numeric_neg( amount ); } xaccSPrintAmount( string, amount, print_info ); if ( euroFlag ) { strcat( string, " / " ); xaccSPrintAmount( string + strlen( string ), gnc_convert_to_euro( cmdty, amount ), gnc_commodity_print_info( gnc_get_euro(), TRUE ) ); } gnc_set_label_color( label, amount ); gtk_label_set_text( GTK_LABEL(label), string ); }
static const char * get_qty_entry (VirtualLocation virt_loc, gboolean translate, gboolean *conditionally_changed, gpointer user_data) { GncEntryLedger *ledger = user_data; GncEntry *entry; gnc_numeric qty; entry = gnc_entry_ledger_get_entry (ledger, virt_loc.vcell_loc); qty = gncEntryGetQuantity (entry); if (gnc_numeric_zero_p (qty)) return NULL; /* Credit notes have negative quantities, but the ledger should * display it as on the document, meaning positive. * So reverse the quantity for credit notes. */ if (ledger->is_credit_note) qty = gnc_numeric_neg (qty); return xaccPrintAmount (qty, gnc_default_print_info (FALSE)); }
static guint sxftd_add_template_trans(SXFromTransInfo *sxfti) { Transaction *tr = sxfti->trans; GList *tt_list = NULL; GList *splits, *template_splits = NULL; TTInfo *tti = gnc_ttinfo_malloc(); TTSplitInfo *ttsi; Split *sp; gnc_numeric runningBalance; gnc_numeric split_value; const char *tmpStr; runningBalance = gnc_numeric_zero(); gnc_ttinfo_set_description(tti, xaccTransGetDescription(tr)); gnc_ttinfo_set_num(tti, gnc_get_num_action(tr, NULL)); gnc_ttinfo_set_currency(tti, xaccTransGetCurrency(tr)); for (splits = xaccTransGetSplitList(tr); splits; splits = splits->next) { sp = splits->data; ttsi = gnc_ttsplitinfo_malloc(); gnc_ttsplitinfo_set_action(ttsi, gnc_get_num_action(NULL, sp)); split_value = xaccSplitGetValue(sp); gnc_ttsplitinfo_set_memo(ttsi, xaccSplitGetMemo(sp)); runningBalance = gnc_numeric_add( runningBalance, split_value, 100, (GNC_DENOM_AUTO | GNC_HOW_DENOM_LCD) ); if (gnc_numeric_positive_p(split_value)) { tmpStr = xaccPrintAmount( split_value, gnc_default_print_info(FALSE) ); gnc_ttsplitinfo_set_debit_formula( ttsi, tmpStr ); } else { /* Negate the numeric so it prints w/o the sign at the front. */ tmpStr = xaccPrintAmount( gnc_numeric_neg( split_value ), gnc_default_print_info(FALSE) ); gnc_ttsplitinfo_set_credit_formula( ttsi, tmpStr ); } /* Copy over per-split account info */ gnc_ttsplitinfo_set_account( ttsi, xaccSplitGetAccount( sp ) ); template_splits = g_list_append(template_splits, ttsi); } if ( ! gnc_numeric_zero_p( runningBalance ) && !gnc_verify_dialog( (GtkWidget *)sxfti->dialog, FALSE, "%s", _("The Scheduled Transaction Editor " "cannot automatically balance " "this transaction. " "Should it still be " "entered?") ) ) { return SXFTD_ERRNO_UNBALANCED_XACTION; } gnc_ttinfo_set_template_splits(tti, template_splits); tt_list = g_list_append(tt_list, tti); gnc_suspend_gui_refresh (); xaccSchedXactionSetTemplateTrans(sxfti->sx, tt_list, gnc_get_current_book ()); gnc_resume_gui_refresh (); return 0; }
/** Create a Transaction from a TransPropertyList. * @param list The list of properties * @param error Contains an error on failure * @return On success, a GncCsvTransLine; on failure, the trans pointer is NULL */ static GncCsvTransLine* trans_property_list_to_trans (TransPropertyList* list, gchar** error) { GncCsvTransLine* trans_line = g_new (GncCsvTransLine, 1); GList* properties_begin = list->properties; QofBook* book = gnc_account_get_book (list->account); gnc_commodity* currency = xaccAccountGetCommodity (list->account); gnc_numeric amount = double_to_gnc_numeric (0.0, xaccAccountGetCommoditySCU (list->account), GNC_HOW_RND_ROUND_HALF_UP); gchar *num = NULL; /* This flag is set to TRUE if we can use the "Deposit" or "Withdrawal" column. */ gboolean amount_set = FALSE; /* The balance is 0 by default. */ trans_line->balance_set = FALSE; trans_line->balance = amount; trans_line->num = NULL; /* We make the line_no -1 just to mark that it hasn't been set. We * may get rid of line_no soon anyway, so it's not particularly * important. */ trans_line->line_no = -1; /* Make sure this is a transaction with all the columns we need. */ if (!trans_property_list_verify_essentials (list, error)) { g_free(trans_line); return NULL; } trans_line->trans = xaccMallocTransaction (book); xaccTransBeginEdit (trans_line->trans); xaccTransSetCurrency (trans_line->trans, currency); /* Go through each of the properties and edit the transaction accordingly. */ list->properties = properties_begin; while (list->properties != NULL) { TransProperty* prop = (TransProperty*)(list->properties->data); switch (prop->type) { case GNC_CSV_DATE: xaccTransSetDatePostedSecsNormalized (trans_line->trans, *((time64*)(prop->value))); break; case GNC_CSV_DESCRIPTION: xaccTransSetDescription (trans_line->trans, (char*)(prop->value)); break; case GNC_CSV_NOTES: xaccTransSetNotes (trans_line->trans, (char*)(prop->value)); break; case GNC_CSV_NUM: /* the 'num' is saved and passed to 'trans_add_split' below where * 'gnc_set_num_action' is used to set tran-num and/or split-action * per book option */ num = g_strdup ((char*)(prop->value)); /* the 'num' is also saved and used in 'gnc_csv_parse_to_trans' when * it calls 'trans_add_split' after deleting the splits added below * when a balance is used by the user */ trans_line->num = g_strdup ((char*)(prop->value)); break; case GNC_CSV_DEPOSIT: /* Add deposits to the existing amount. */ if (prop->value != NULL) { amount = gnc_numeric_add (*((gnc_numeric*)(prop->value)), amount, xaccAccountGetCommoditySCU (list->account), GNC_HOW_RND_ROUND_HALF_UP); amount_set = TRUE; /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */ trans_line->balance_set = FALSE; } break; case GNC_CSV_WITHDRAWAL: /* Withdrawals are just negative deposits. */ if (prop->value != NULL) { amount = gnc_numeric_add (gnc_numeric_neg(*((gnc_numeric*)(prop->value))), amount, xaccAccountGetCommoditySCU (list->account), GNC_HOW_RND_ROUND_HALF_UP); amount_set = TRUE; /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */ trans_line->balance_set = FALSE; } break; case GNC_CSV_BALANCE: /* The balance gets stored in a separate field in trans_line. */ /* We will use the "Deposit" and "Withdrawal" columns in preference to "Balance". */ if (!amount_set && prop->value != NULL) { /* This gets put into the actual transaction at the end of gnc_csv_parse_to_trans. */ trans_line->balance = *((gnc_numeric*)(prop->value)); trans_line->balance_set = TRUE; } break; } list->properties = g_list_next (list->properties); } /* Add a split with the cumulative amount value. */ trans_add_split (trans_line->trans, list->account, book, amount, num); if (num) g_free (num); return trans_line; }
void gnc_stock_split_assistant_finish (GtkAssistant *assistant, gpointer user_data) { StockSplitInfo *info = user_data; GList *account_commits; GList *node; gnc_numeric amount; Transaction *trans; Account *account; Split *split; time64 date; account = info->acct; g_return_if_fail (account != NULL); amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->distribution_edit)); g_return_if_fail (!gnc_numeric_zero_p (amount)); gnc_suspend_gui_refresh (); trans = xaccMallocTransaction (gnc_get_current_book ()); xaccTransBeginEdit (trans); xaccTransSetCurrency (trans, gnc_default_currency ()); date = gnc_date_edit_get_date (GNC_DATE_EDIT (info->date_edit)); xaccTransSetDatePostedSecsNormalized (trans, date); { const char *description; description = gtk_entry_get_text (GTK_ENTRY (info->description_entry)); xaccTransSetDescription (trans, description); } split = xaccMallocSplit (gnc_get_current_book ()); xaccAccountBeginEdit (account); account_commits = g_list_prepend (NULL, account); xaccTransAppendSplit (trans, split); xaccAccountInsertSplit (account, split); xaccSplitSetAmount (split, amount); xaccSplitMakeStockSplit (split); /* Set split-action with gnc_set_num_action which is the same as * xaccSplitSetAction with these arguments */ /* Translators: This string has a disambiguation prefix */ gnc_set_num_action (NULL, split, NULL, Q_("Action Column|Split")); amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->price_edit)); if (gnc_numeric_positive_p (amount)) { QofBook *book; GNCPrice *price; GNCPriceDB *pdb; GNCCurrencyEdit *ce; Timespec ts; ce = GNC_CURRENCY_EDIT (info->price_currency_edit); ts.tv_sec = date; ts.tv_nsec = 0; price = gnc_price_create (gnc_get_current_book ()); gnc_price_begin_edit (price); gnc_price_set_commodity (price, xaccAccountGetCommodity (account)); gnc_price_set_currency (price, gnc_currency_edit_get_currency (ce)); gnc_price_set_time (price, ts); gnc_price_set_source (price, PRICE_SOURCE_STOCK_SPLIT); gnc_price_set_typestr (price, PRICE_TYPE_UNK); gnc_price_set_value (price, amount); gnc_price_commit_edit (price); book = gnc_get_current_book (); pdb = gnc_pricedb_get_db (book); if (!gnc_pricedb_add_price (pdb, price)) gnc_error_dialog (info->window, "%s", _("Error adding price.")); } amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (info->cash_edit)); if (gnc_numeric_positive_p (amount)) { const char *memo; memo = gtk_entry_get_text (GTK_ENTRY (info->memo_entry)); /* asset split */ account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->asset_tree)); split = xaccMallocSplit (gnc_get_current_book ()); xaccAccountBeginEdit (account); account_commits = g_list_prepend (account_commits, account); xaccAccountInsertSplit (account, split); xaccTransAppendSplit (trans, split); xaccSplitSetAmount (split, amount); xaccSplitSetValue (split, amount); xaccSplitSetMemo (split, memo); /* income split */ account = gnc_tree_view_account_get_selected_account (GNC_TREE_VIEW_ACCOUNT(info->income_tree)); split = xaccMallocSplit (gnc_get_current_book ()); xaccAccountBeginEdit (account); account_commits = g_list_prepend (account_commits, account); xaccAccountInsertSplit (account, split); xaccTransAppendSplit (trans, split); xaccSplitSetAmount (split, gnc_numeric_neg (amount)); xaccSplitSetValue (split, gnc_numeric_neg (amount)); xaccSplitSetMemo (split, memo); } xaccTransCommitEdit (trans); for (node = account_commits; node; node = node->next) xaccAccountCommitEdit (node->data); g_list_free (account_commits); gnc_resume_gui_refresh (); gnc_close_gui_component_by_data (ASSISTANT_STOCK_SPLIT_CM_CLASS, info); }
static void check_add_subtract (void) { int i; gnc_numeric a, b, c, d, z; a = gnc_numeric_create(2, 6); b = gnc_numeric_create(1, 4); /* Well, actually 14/24 would be acceptable/better in this case */ check_binary_op (gnc_numeric_create(7, 12), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s + %s for add exact"); check_binary_op (gnc_numeric_create(58, 100), gnc_numeric_add(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 100ths (banker's)"); check_binary_op (gnc_numeric_create(5833, 10000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(4) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 4 sig figs"); check_binary_op (gnc_numeric_create(583333, 1000000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 6 sig figs"); check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s - %s for sub exact"); /* We should try something trickier for reduce & lcd */ check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(8, 100), gnc_numeric_sub(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s - %s for sub 100ths (banker's)"); /* ------------------------------------------------------------ */ /* This test has failed before */ c = gnc_numeric_neg (a); d = gnc_numeric_neg (b); z = gnc_numeric_zero(); check_binary_op (c, gnc_numeric_add_fixed(z, c), z, c, "expected %s got %s = %s + %s for add fixed"); check_binary_op (d, gnc_numeric_add_fixed(z, d), z, d, "expected %s got %s = %s + %s for add fixed"); /* ------------------------------------------------------------ */ /* Same as above, but with signs reviersed */ a = c; b = d; /* Well, actually 14/24 would be acceptable/better in this case */ check_binary_op (gnc_numeric_create(-7, 12), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s + %s for add exact"); check_binary_op (gnc_numeric_create(-58, 100), gnc_numeric_add(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 100ths (banker's)"); check_binary_op (gnc_numeric_create(-5833, 10000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(4) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 4 sig figs"); check_binary_op (gnc_numeric_create(-583333, 1000000), gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_SIGFIGS(6) | GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s + %s for add 6 sig figs"); check_binary_op (gnc_numeric_create(-1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s - %s for sub exact"); /* We should try something trickier for reduce & lcd */ check_binary_op (gnc_numeric_create(-1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(-1, 12), gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), a, b, "expected %s got %s = %s - %s for sub reduce"); check_binary_op (gnc_numeric_create(-8, 100), gnc_numeric_sub(a, b, 100, GNC_HOW_RND_ROUND), a, b, "expected %s got %s = %s - %s for sub 100ths (banker's)"); /* ------------------------------------------------------------ */ /* Add and subtract some random numbers */ for (i = 0; i < NREPS; i++) { gnc_numeric e; gint64 deno = rand() + 1; gint64 na = get_random_gint64(); gint64 nb = get_random_gint64(); gint64 ne; /* avoid overflow; */ na /= 2; nb /= 2; a = gnc_numeric_create(na, deno); b = gnc_numeric_create(nb, deno); /* Add */ ne = na + nb; e = gnc_numeric_create(ne, deno); check_binary_op (e, gnc_numeric_add(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s + %s for exact addition"); /* Subtract */ ne = na - nb; e = gnc_numeric_create(ne, deno); check_binary_op (e, gnc_numeric_sub(a, b, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT), a, b, "expected %s got %s = %s - %s for exact subtraction"); } }
static void gnc_entry_ledger_save_cells (gpointer save_data, gpointer user_data) { GncEntryLedger *ledger = user_data; GncEntry *entry = save_data; g_return_if_fail (entry != NULL); /* copy the contents from the cursor to the split */ if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_IACCT_CELL, TRUE)) { Account *acc; acc = gnc_entry_ledger_get_account (ledger, ENTRY_IACCT_CELL); if (acc != NULL) gncEntrySetInvAccount (entry, acc); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_BACCT_CELL, TRUE)) { Account *acc; acc = gnc_entry_ledger_get_account (ledger, ENTRY_BACCT_CELL); if (acc != NULL) gncEntrySetBillAccount (entry, acc); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_ACTN_CELL, TRUE)) { const char *value; value = gnc_table_layout_get_cell_value (ledger->table->layout, ENTRY_ACTN_CELL); gncEntrySetAction (entry, value); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_DATE_CELL, TRUE)) { BasicCell *cell; GDate date; cell = gnc_table_layout_get_cell (ledger->table->layout, ENTRY_DATE_CELL); /* commit any pending changes */ gnc_date_cell_commit ((DateCell *) cell); gnc_date_cell_get_date_gdate ((DateCell *) cell, &date); gncEntrySetDateGDate (entry, &date); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_DESC_CELL, TRUE)) { const char *value; value = gnc_table_layout_get_cell_value (ledger->table->layout, ENTRY_DESC_CELL); gncEntrySetDescription (entry, value); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_DISC_CELL, TRUE)) { gnc_numeric amount; if (gnc_entry_ledger_get_numeric (ledger, ENTRY_DISC_CELL, &amount)) gncEntrySetInvDiscount (entry, amount); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_DISTYPE_CELL, TRUE)) { gint type; type = gnc_entry_ledger_get_type (ledger, ENTRY_DISTYPE_CELL); if (type != -1) gncEntrySetInvDiscountType (entry, type); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_DISHOW_CELL, TRUE)) { gint type; type = gnc_entry_ledger_get_type (ledger, ENTRY_DISHOW_CELL); if (type != -1) gncEntrySetInvDiscountHow (entry, type); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_QTY_CELL, TRUE)) { gnc_numeric amount; if (gnc_entry_ledger_get_numeric (ledger, ENTRY_QTY_CELL, &amount)) { /* Credit notes have negative quantities, but the ledger should * display it as on the document, meaning positive. * So reverse the quantity for credit notes. */ if (ledger->is_credit_note) amount = gnc_numeric_neg (amount); gncEntrySetQuantity (entry, amount); } } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_BILLABLE_CELL, TRUE)) { gboolean billable; billable = gnc_entry_ledger_get_checkmark (ledger, ENTRY_BILLABLE_CELL); gncEntrySetBillable (entry, billable); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_PAYMENT_CELL, TRUE)) { const char *value; value = gnc_table_layout_get_cell_value (ledger->table->layout, ENTRY_PAYMENT_CELL); if (!safe_strcmp (value, _("Cash"))) gncEntrySetBillPayment (entry, GNC_PAYMENT_CASH); else if (!safe_strcmp (value, _("Charge"))) gncEntrySetBillPayment (entry, GNC_PAYMENT_CARD); else g_warning ("Invalid Payment cell: %s", value ? value : "(null)"); } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_PRIC_CELL, TRUE)) { gnc_numeric amount; if (gnc_entry_ledger_get_numeric (ledger, ENTRY_PRIC_CELL, &amount)) { if (ledger->is_cust_doc) gncEntrySetInvPrice (entry, amount); else gncEntrySetBillPrice (entry, amount); } } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_TAXABLE_CELL, TRUE)) { gboolean taxable; taxable = gnc_entry_ledger_get_checkmark (ledger, ENTRY_TAXABLE_CELL); if (ledger->is_cust_doc) gncEntrySetInvTaxable (entry, taxable); else gncEntrySetBillTaxable (entry, taxable); } /* XXX: Only (re-set) these if taxable is TRUE? */ if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_TAXTABLE_CELL, TRUE)) { GncTaxTable *table; table = gnc_entry_ledger_get_taxtable (ledger, ENTRY_TAXTABLE_CELL); if (table) { if (ledger->is_cust_doc) gncEntrySetInvTaxTable (entry, table); else gncEntrySetBillTaxTable (entry, table); } } if (gnc_table_layout_get_cell_changed (ledger->table->layout, ENTRY_TAXINCLUDED_CELL, TRUE)) { gboolean taxincluded; taxincluded = gnc_entry_ledger_get_checkmark (ledger, ENTRY_TAXINCLUDED_CELL); if (ledger->is_cust_doc) gncEntrySetInvTaxIncluded (entry, taxincluded); else gncEntrySetBillTaxIncluded (entry, taxincluded); } if (ledger->type == GNCENTRY_INVOICE_ENTRY || ledger->type == GNCENTRY_CUST_CREDIT_NOTE_ENTRY) { gboolean inv_value; inv_value = gnc_entry_ledger_get_checkmark (ledger, ENTRY_INV_CELL); if (inv_value) { /* Add this to the invoice (if it's not already attached) */ if (gncEntryGetInvoice (entry) == NULL) gncInvoiceAddEntry (ledger->invoice, entry); } else { /* Remove from the invoice iff we're attached to an order or bill */ if ((gncEntryGetOrder (entry) != NULL) || (gncEntryGetBill (entry) != NULL)) gncInvoiceRemoveEntry (ledger->invoice, entry); } } }
gboolean gnc_tree_util_split_reg_get_debcred_entry (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gboolean is_blank, gnc_numeric *ret_num, GNCPrintAmountInfo *ret_print_info) { GncTreeModelSplitReg *model; gnc_commodity *currency; model = gnc_tree_view_split_reg_get_model_from_view (view); currency = xaccTransGetCurrency (trans); if (!currency) currency = gnc_default_currency (); if (is_blank) { gnc_numeric imbalance; Account *acc; imbalance = xaccTransGetImbalanceValue (trans); if (gnc_numeric_zero_p (imbalance)) return FALSE; if (xaccTransUseTradingAccounts (trans)) { MonetaryList *imbal_list; gnc_monetary *imbal_mon; imbal_list = xaccTransGetImbalance (trans); if (!imbal_list) { /* No commodity imbalance, there shouldn't be a value imablance. */ return FALSE; } if (imbal_list->next) { /* Multiple currency imbalance. */ gnc_monetary_list_free (imbal_list); return FALSE; } imbal_mon = imbal_list->data; if (!gnc_commodity_equal (gnc_monetary_commodity (*imbal_mon), currency)) { /* Imbalance is in wrong currency */ gnc_monetary_list_free (imbal_list); return FALSE; } if (!gnc_numeric_equal (gnc_monetary_value (*imbal_mon), imbalance)) { /* Value and commodity imbalances differ */ gnc_monetary_list_free (imbal_list); return FALSE; } /* Done with the imbalance list */ gnc_monetary_list_free (imbal_list); } imbalance = gnc_numeric_neg (imbalance); acc = gnc_tree_model_split_reg_get_anchor (model); if (gnc_tree_util_split_reg_needs_conv_rate (view, trans, acc)) { imbalance = gnc_numeric_mul (imbalance, xaccTransGetAccountConvRate (trans, acc), gnc_commodity_get_fraction (currency), GNC_HOW_RND_ROUND_HALF_UP); } else { imbalance = gnc_numeric_convert (imbalance, gnc_commodity_get_fraction (currency), GNC_HOW_RND_ROUND_HALF_UP); } *ret_num = imbalance; *ret_print_info = gnc_account_print_info (acc, FALSE); return TRUE; } { gnc_numeric amount; gnc_commodity *split_commodity; GNCPrintAmountInfo print_info; Account *account; gnc_commodity *commodity; account = gnc_tree_model_split_reg_get_anchor (model); commodity = xaccAccountGetCommodity (account); split_commodity = xaccAccountGetCommodity (xaccSplitGetAccount (split)); if (xaccTransUseTradingAccounts (trans)) { gboolean use_symbol, is_current = FALSE; Split *current_split = gnc_tree_view_split_reg_get_current_split (view); RowDepth depth = gnc_tree_view_reg_get_selected_row_depth (view); if ((split == current_split) && (depth == SPLIT3)) is_current = TRUE; if (model->type == STOCK_REGISTER2 || model->type == CURRENCY_REGISTER2 || model->type == PORTFOLIO_LEDGER2) { gnc_commodity *amount_commodity; /* security register. If this split has price and shares columns, use the value, otherwise use the amount. */ if (gtu_sr_use_security (view)) { amount = xaccSplitGetValue (split); amount_commodity = currency; } else { amount = xaccSplitGetAmount (split); amount_commodity = split_commodity; } /* Show the currency if it is not the default currency */ if (is_current || gnc_commodity_equiv (amount_commodity, gnc_default_currency ())) use_symbol = FALSE; else use_symbol = TRUE; print_info = gnc_commodity_print_info (amount_commodity, use_symbol); } else { /* non-security register, always use the split amount. */ amount = xaccSplitGetAmount (split); if (is_current || gnc_commodity_equiv (split_commodity, commodity)) use_symbol = FALSE; else use_symbol = TRUE; print_info = gnc_commodity_print_info (split_commodity, use_symbol); } } else { /* If this account is not a stock/mutual/currency account, and * currency != the account commodity, then use the SplitAmount * instead of the SplitValue. */ switch (model->type) { case STOCK_REGISTER2: case CURRENCY_REGISTER2: case PORTFOLIO_LEDGER2: amount = xaccSplitGetValue (split); print_info = gnc_commodity_print_info (currency, FALSE); break; default: if (commodity && !gnc_commodity_equal (commodity, currency)) /* Convert this to the "local" value */ amount = xaccSplitConvertAmount (split, account); else amount = xaccSplitGetValue (split); print_info = gnc_account_print_info (account, FALSE); break; } } if (gnc_numeric_zero_p (amount)) return FALSE; *ret_num = amount; *ret_print_info = print_info; return TRUE; } }
/* Takes the input with column and sets the price / amount / value so they are consistent */ void gnc_tree_util_set_number_for_input (GncTreeViewSplitReg *view, Transaction *trans, Split *split, gnc_numeric input, gint viewcol) { GncTreeModelSplitReg *model; gnc_numeric price; gnc_numeric amount; gnc_numeric value; gboolean price_changed = FALSE; // Price of each share gboolean value_changed = FALSE; // Total value of shares gboolean amount_changed = FALSE; // No of shares gboolean recalc_amount = FALSE; gboolean recalc_price = FALSE; gboolean recalc_value = FALSE; gboolean expanded = FALSE; int denom; Account *account = NULL; ENTER("trans %p and split %p and input is %s and viewcol is %d", trans, split, gnc_numeric_to_string (input), viewcol); model = gnc_tree_view_split_reg_get_model_from_view (view); /* Check for sub account view */ if (!gnc_tree_model_split_reg_get_sub_account (model)) account = gnc_tree_model_split_reg_get_anchor (model); expanded = gnc_tree_view_split_reg_trans_expanded (view, trans); if (!account) account = xaccSplitGetAccount (split); if (!xaccAccountIsPriced (account)) return; /* If we are using commodity trading accounts then the value may not really be the value. Punt if so. */ if (xaccTransUseTradingAccounts (xaccSplitGetParent (split))) { gnc_commodity *acc_commodity; acc_commodity = xaccAccountGetCommodity (account); if (!(xaccAccountIsPriced (account) || !gnc_commodity_is_iso (acc_commodity))) return; } if (gnc_numeric_zero_p (input)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE("zero"); return; } amount = xaccSplitGetAmount (split); value = xaccSplitGetValue (split); if (viewcol == COL_AMTVAL && !expanded) { value_changed = TRUE; if (gnc_numeric_zero_p (amount)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } else if (viewcol == COL_AMTVAL && expanded) { amount_changed = TRUE; if (gnc_numeric_zero_p (value)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } if (viewcol == COL_PRICE) { price_changed = TRUE; if (gnc_numeric_zero_p (value)) { amount = gnc_numeric_create (1,1); value = gnc_numeric_mul (input, amount, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND); xaccSplitSetValue (split, input); xaccSplitSetAmount (split, amount); LEAVE(""); return; } } if ((viewcol == COL_CREDIT || viewcol == COL_DEBIT) && !expanded) { amount_changed = TRUE; if (gnc_numeric_zero_p (value)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } else if ((viewcol == COL_CREDIT || viewcol == COL_DEBIT) && expanded) { value_changed = TRUE; if (gnc_numeric_zero_p (value)) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, input); LEAVE(""); return; } } DEBUG("value_changed %d, price_changed %d, amount_changed %d", value_changed, price_changed, amount_changed); { int choice; int default_value; GList *node; GList *radio_list = NULL; const char *title = _("Recalculate Transaction"); const char *message = _("The values entered for this transaction " "are inconsistent. Which value would you " "like to have recalculated?"); if (amount_changed) radio_list = g_list_append (radio_list, g_strdup_printf ("%s (%s)", _("_Shares"), _("Changed"))); else radio_list = g_list_append (radio_list, g_strdup (_("_Shares"))); if (price_changed) radio_list = g_list_append (radio_list, g_strdup_printf ("%s (%s)", _("_Price"), _("Changed"))); else radio_list = g_list_append (radio_list, g_strdup (_("_Price"))); if (value_changed) radio_list = g_list_append (radio_list, g_strdup_printf ("%s (%s)", _("_Value"), _("Changed"))); else radio_list = g_list_append (radio_list, g_strdup (_("_Value"))); if(expanded) { if (price_changed) default_value = 2; /* change the value */ else default_value = 1; /* change the price */ } else { if (price_changed) default_value = 0; /* change the amount / shares */ else default_value = 1; /* change the price */ } choice = gnc_choose_radio_option_dialog (gnc_tree_view_split_reg_get_parent (view), title, message, _("_Recalculate"), default_value, radio_list); for (node = radio_list; node; node = node->next) g_free (node->data); g_list_free (radio_list); switch (choice) { case 0: /* Modify number of shares */ recalc_amount = TRUE; break; case 1: /* Modify the share price */ recalc_price = TRUE; break; case 2: /* Modify total value */ recalc_value = TRUE; break; default: /* Cancel */ LEAVE(" " ); return; } } DEBUG("recalc_value %d, recalc_price %d, recalc_amount %d", recalc_value, recalc_price, recalc_amount); if (recalc_amount) { denom = gtu_sr_get_amount_denom (split); if (amount_changed) { LEAVE(""); return; } if (price_changed) price = input; else price = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); if (value_changed) { xaccSplitSetValue (split, input); amount = gnc_numeric_div (input, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (split, amount); } else { amount = gnc_numeric_div (value, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (split, amount); } } if (recalc_price) { if (price_changed) { LEAVE(""); return; } if (amount_changed) { xaccSplitSetAmount (split, input); xaccSplitSetValue (split, value); } if (value_changed) { xaccSplitSetValue (split, input); xaccSplitSetAmount (split, amount); } } if (recalc_value) { denom = gtu_sr_get_value_denom (split); if (value_changed) { LEAVE(""); return; } if (price_changed) price = input; else price = gnc_numeric_div (value, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); if (amount_changed) { xaccSplitSetAmount (split, input); value = gnc_numeric_mul (input, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } else { value = gnc_numeric_mul (amount, price, denom, GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (split, value); } } /* If the number of splits is two, change other split to balance */ if (!gnc_tree_util_split_reg_is_multi (split) && expanded) { Split *osplit; gnc_commodity *osplit_com; osplit = xaccSplitGetOtherSplit (split); value = xaccSplitGetValue (split); osplit_com = xaccAccountGetCommodity (xaccSplitGetAccount (osplit)); if (gnc_commodity_is_currency (osplit_com)) { xaccSplitSetValue (osplit, gnc_numeric_neg (value)); xaccSplitSetAmount (osplit, gnc_numeric_neg (value)); } } LEAVE(""); }
static void gsr2_redraw_all_cb (GncTreeViewSplitReg *view, gpointer user_data) { GNCSplitReg2 *gsr = user_data; gnc_commodity * commodity; GNCPrintAmountInfo print_info; gnc_numeric amount = gnc_numeric_zero(); Account *leader; gboolean reverse; gboolean euro; if ( gsr->summarybar == NULL ) return; leader = gnc_ledger_display2_leader( gsr->ledger ); commodity = xaccAccountGetCommodity( leader ); /* no EURO converson, if account is already EURO or no EURO currency */ if (commodity != NULL) euro = (gnc_is_euro_currency( commodity ) && (strncasecmp(gnc_commodity_get_mnemonic(commodity), "EUR", 3))); else euro = FALSE; print_info = gnc_account_print_info( leader, TRUE ); reverse = gnc_reverse_balance( leader ); gsr2_update_summary_label( gsr->balance_label, xaccAccountGetPresentBalance, leader, print_info, commodity, reverse, euro ); gsr2_update_summary_label( gsr->cleared_label, xaccAccountGetClearedBalance, leader, print_info, commodity, reverse, euro ); gsr2_update_summary_label( gsr->reconciled_label, xaccAccountGetReconciledBalance, leader, print_info, commodity, reverse, euro ); gsr2_update_summary_label( gsr->future_label, xaccAccountGetBalance, leader, print_info, commodity, reverse, euro ); gsr2_update_summary_label( gsr->projectedminimum_label, xaccAccountGetProjectedMinimumBalance, leader, print_info, commodity, reverse, euro ); /* Print the summary share amount */ if (gsr->shares_label != NULL) { char string[256]; print_info = gnc_account_print_info( leader, TRUE ); amount = xaccAccountGetBalance( leader ); if ( reverse ) amount = gnc_numeric_neg( amount ); xaccSPrintAmount( string, amount, print_info ); gnc_set_label_color( gsr->shares_label, amount ); gtk_label_set_text( GTK_LABEL(gsr->shares_label), string ); } /* Print the summary share value */ if (gsr->value_label != NULL) { char string[256]; gnc_commodity *currency = gnc_default_currency (); print_info = gnc_commodity_print_info (currency, TRUE); xaccSPrintAmount (string, amount, print_info); gnc_set_label_color (gsr->value_label, amount); gtk_label_set_text (GTK_LABEL (gsr->value_label), string); } }
static void gnc_split_register_save_cells (gpointer save_data, gpointer user_data) { SRSaveData *sd = save_data; SplitRegister *reg = user_data; Split *other_split; gnc_commodity *txn_cur; gnc_numeric rate = gnc_numeric_zero(); g_return_if_fail (sd != NULL); if (!sd->do_scrub) return; other_split = xaccSplitGetOtherSplit (sd->split); txn_cur = xaccTransGetCurrency (sd->trans); xaccSplitScrub (sd->split); rate = gnc_split_register_get_rate_cell (reg, RATE_CELL); if (other_split && !sd->reg_expanded) { gnc_numeric amount, value = xaccSplitGetValue (sd->split); Account *acc; gboolean split_needs_amount; split_needs_amount = gnc_split_register_split_needs_amount(reg, sd->split); /* We are changing the rate on the current split, but it was not * handled in the debcred handler, so we need to do it here. */ if (!sd->handled_dc && split_needs_amount && !gnc_numeric_zero_p (rate)) { gnc_numeric amount = xaccSplitGetAmount (sd->split); value = gnc_numeric_div( amount, rate, gnc_commodity_get_fraction(txn_cur), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (sd->split, value); /* XXX: do we need to set the amount on the other split? */ } /* Now reverse the value for the other split */ value = gnc_numeric_neg (value); if (gnc_split_register_split_needs_amount (reg, other_split)) { acc = xaccSplitGetAccount (other_split); /* If we don't have an exchange rate then figure it out. Or, if * BOTH splits require an amount, then most likely we're in the * strange case of having a transaction currency different than * _both_ accounts -- so grab the other exchange rate. */ if (gnc_numeric_zero_p (rate) || split_needs_amount) rate = xaccTransGetAccountConvRate(xaccSplitGetParent (other_split), acc); amount = gnc_numeric_mul (value, rate, xaccAccountGetCommoditySCU (acc), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetAmount (other_split, amount); } xaccSplitSetValue (other_split, value); xaccSplitScrub (other_split); } else if (gnc_split_register_split_needs_amount (reg, sd->split) && ! gnc_numeric_zero_p (rate)) { /* this is either a multi-split or expanded transaction, so only * deal with this split... In particular we need to reset the * Value if the conv-rate changed. * * If we handled the debcred then no need to do anything there -- * the debcred handler did all the computation. If NOT, then the * convrate changed -- reset the value from the amount. */ if (!sd->handled_dc) { gnc_split_register_save_amount_values (sd, reg); #if 0 gnc_numeric value, amount; amount = xaccSplitGetAmount (sd->split); value = gnc_numeric_div (amount, rate, gnc_commodity_get_fraction (txn_cur), GNC_HOW_RND_ROUND_HALF_UP); xaccSplitSetValue (sd->split, value); #endif } } }
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots) { GList *base_iter; /* General note: in the code below the term "payment" can * both mean a true payment or a document of * the opposite sign (invoice vs credit note) relative to * the lot being processed. In general this function will * perform a balancing action on a set of lots, so you * will also find frequent references to balancing instead. */ /* Payments can only be applied when at least an owner * and a list of lots to use are given */ if (!owner) return; if (!lots) return; for (base_iter = lots; base_iter; base_iter = base_iter->next) { GNCLot *base_lot = base_iter->data; QofBook *book; Account *acct; const gchar *name; GList *lot_list, *lot_iter; Transaction *txn = NULL; gnc_numeric base_lot_bal, val_to_pay, val_paid = { 0, 1 }; gboolean base_bal_is_pos; const gchar *action, *memo; /* Only attempt to apply payments to open lots. * Note that due to the iterative nature of this function lots * in the list may become closed before they are evaluated as * base lot, so we should check this for each lot. */ base_lot_bal = gnc_lot_get_balance (base_lot); if (gnc_numeric_zero_p (base_lot_bal)) continue; book = gnc_lot_get_book (base_lot); acct = gnc_lot_get_account (base_lot); name = gncOwnerGetName (gncOwnerGetEndOwner (owner)); lot_list = base_iter->next; /* Strings used when creating splits later on. */ action = _("Lot Link"); memo = _("Internal link between invoice and payment lots"); /* Note: to balance the lot the payment to assign * must have the opposite sign of the existing lot balance */ val_to_pay = gnc_numeric_neg (base_lot_bal); base_bal_is_pos = gnc_numeric_positive_p (base_lot_bal); /* Create splits in a linking transaction between lots until * - either the invoice lot is balanced * - or there are no more balancing lots. */ for (lot_iter = lot_list; lot_iter; lot_iter = lot_iter->next) { gnc_numeric payment_lot_balance; Split *split; Account *bal_acct; gnc_numeric split_amt; GNCLot *balancing_lot = lot_iter->data; /* Only attempt to use open lots to balance the base lot. * Note that due to the iterative nature of this function lots * in the list may become closed before they are evaluated as * base lot, so we should check this for each lot. */ if (gnc_lot_is_closed (balancing_lot)) continue; /* Balancing transactions for invoice/payments can only happen * in the same account. */ bal_acct = gnc_lot_get_account (balancing_lot); if (acct != bal_acct) continue; payment_lot_balance = gnc_lot_get_balance (balancing_lot); /* Only attempt to balance if the base lot and balancing lot are * of the opposite sign. (Otherwise we would increase the balance * of the lot - Duh */ if (base_bal_is_pos == gnc_numeric_positive_p (payment_lot_balance)) continue; /* * If there is less to pay than there's open in the lot; we're done -- apply the base_lot_vale. * Note that payment_value and balance are opposite in sign, so we have to compare absolute values here * * Otherwise, apply the balance, subtract that from the payment_value, * and move on to the next one. */ if (gnc_numeric_compare (gnc_numeric_abs (val_to_pay), gnc_numeric_abs (payment_lot_balance)) <= 0) { /* abs(val_to_pay) <= abs(balance) */ split_amt = val_to_pay; } else { /* abs(val_to_pay) > abs(balance) * Remember payment_value and balance are opposite in sign, * and we want a payment to neutralize the current balance * so we need to negate here */ split_amt = payment_lot_balance; } /* If not created yet, create a new transaction linking * the base lot and the balancing lot(s) */ if (!txn) { Timespec ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (base_lot))); xaccAccountBeginEdit (acct); txn = xaccMallocTransaction (book); xaccTransBeginEdit (txn); xaccTransSetDescription (txn, name ? name : ""); xaccTransSetCurrency (txn, xaccAccountGetCommodity(acct)); xaccTransSetDateEnteredSecs (txn, gnc_time (NULL)); xaccTransSetDatePostedTS (txn, &ts); xaccTransSetTxnType (txn, TXN_TYPE_LINK); } /* Create the split for this link in current balancing lot */ split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set Action using utility function */ gnc_set_num_action (NULL, split, NULL, action); xaccAccountInsertSplit (acct, split); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, gnc_numeric_neg (split_amt), xaccAccountGetCommodity(acct)); gnc_lot_add_split (balancing_lot, split); /* If the balancing lot was linked to a document (invoice/credit note), * send an event for it as well so it gets potentially updated as paid */ { GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(balancing_lot); if (this_invoice) qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); } val_paid = gnc_numeric_add (val_paid, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); val_to_pay = gnc_numeric_sub (val_to_pay, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); if (gnc_numeric_zero_p (val_to_pay)) break; } /* If the above loop managed to create a transaction and some balancing splits, * create the final split for the link transaction in the base lot */ if (txn) { GncInvoice *this_invoice; Split *split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); /* set Action with utiltity function */ gnc_set_num_action (NULL, split, NULL, action); xaccAccountInsertSplit (acct, split); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, val_paid, xaccAccountGetCommodity(acct)); gnc_lot_add_split (base_lot, split); xaccTransCommitEdit (txn); xaccAccountCommitEdit (acct); /* If the base lot was linked to a document (invoice/credit note), * send an event for it as well so it gets potentially updated as paid */ this_invoice = gncInvoiceGetInvoiceFromLot(base_lot); if (this_invoice) qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); } } }
gnc_numeric gncEntryGetBalDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc) { gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc); return (is_cust_doc ? gnc_numeric_neg (value) : value); }
static void refresh_model_row (GNCImportMainMatcher *gui, GtkTreeModel *model, GtkTreeIter *iter, GNCImportTransInfo *info) { GtkListStore *store; GtkTreeSelection *selection; gchar *tmp, *imbalance, *text, *color; const gchar *ro_text; Split *split; g_assert (gui); g_assert (model); g_assert (info); /*DEBUG("Begin");*/ store = GTK_LIST_STORE(model); gtk_list_store_set(store, iter, DOWNLOADED_COL_DATA, info, -1); /*Account:*/ split = gnc_import_TransInfo_get_fsplit (info); g_assert(split); // Must not be NULL ro_text = xaccAccountGetName(xaccSplitGetAccount(split)); gtk_list_store_set(store, iter, DOWNLOADED_COL_ACCOUNT, ro_text, -1); /*Date*/ text = qof_print_date ( xaccTransGetDate( gnc_import_TransInfo_get_trans(info) ) ); gtk_list_store_set(store, iter, DOWNLOADED_COL_DATE, text, -1); g_free(text); /*Amount*/ ro_text = xaccPrintAmount (xaccSplitGetAmount (split), gnc_split_amount_print_info(split, TRUE) ); gtk_list_store_set(store, iter, DOWNLOADED_COL_AMOUNT, ro_text, -1); /*Description*/ ro_text = xaccTransGetDescription(gnc_import_TransInfo_get_trans(info) ); gtk_list_store_set(store, iter, DOWNLOADED_COL_DESCRIPTION, ro_text, -1); /*Memo*/ ro_text = xaccSplitGetMemo(split); gtk_list_store_set(store, iter, DOWNLOADED_COL_MEMO, ro_text, -1); /*Actions*/ /* Action informations */ ro_text = text = color = NULL; switch (gnc_import_TransInfo_get_action(info)) { case GNCImport_ADD: if (gnc_import_TransInfo_is_balanced(info) == TRUE) { ro_text = _("New, already balanced"); color = COLOR_GREEN; } else { /* Assume that importers won't create transactions in two or more currencies so we can use xaccTransGetImbalanceValue */ imbalance = g_strdup (xaccPrintAmount (gnc_numeric_neg(xaccTransGetImbalanceValue (gnc_import_TransInfo_get_trans(info) )), gnc_commodity_print_info (xaccTransGetCurrency(gnc_import_TransInfo_get_trans (info)), TRUE) )); if (gnc_import_TransInfo_get_destacc (info) != NULL) { color = COLOR_GREEN; tmp = gnc_account_get_full_name (gnc_import_TransInfo_get_destacc (info)); if (gnc_import_TransInfo_get_destacc_selected_manually(info) == TRUE) { text = /* Translators: %1$s is the amount to be transferred. %2$s is the destination account. */ g_strdup_printf(_("New, transfer %s to (manual) \"%s\""), imbalance, tmp); } else { text = /* Translators: %1$s is the amount to be transferred. %2$s is the destination account. */ g_strdup_printf(_("New, transfer %s to (auto) \"%s\""), imbalance, tmp); } g_free (tmp); } else { color = COLOR_YELLOW; text = /* Translators: %s is the amount to be transferred. */ g_strdup_printf(_("New, UNBALANCED (need acct to transfer %s)!"), imbalance); } g_free (imbalance); } break; case GNCImport_CLEAR: if (gnc_import_TransInfo_get_selected_match(info)) { color = COLOR_GREEN; if (gnc_import_TransInfo_get_match_selected_manually(info) == TRUE) { ro_text = _("Reconcile (manual) match"); } else { ro_text = _("Reconcile (auto) match"); } } else { color = COLOR_RED; ro_text = _("Match missing!"); } break; case GNCImport_UPDATE: if (gnc_import_TransInfo_get_selected_match(info)) { color = COLOR_GREEN; if (gnc_import_TransInfo_get_match_selected_manually(info) == TRUE) { ro_text = _("Update and reconcile (manual) match"); } else { ro_text = _("Update and reconcile (auto) match"); } } else { color = COLOR_RED; ro_text = _("Match missing!"); } break; case GNCImport_SKIP: color = COLOR_RED; ro_text = _("Do not import (no action selected)"); break; default: color = "white"; ro_text = "WRITEME, this is an unknown action"; break; } gtk_list_store_set(store, iter, DOWNLOADED_COL_COLOR, color, DOWNLOADED_COL_ACTION_INFO, ro_text ? ro_text : text, -1); if (text) g_free(text); /* Set the pixmaps */ gtk_list_store_set(store, iter, DOWNLOADED_COL_ACTION_ADD, gnc_import_TransInfo_get_action(info) == GNCImport_ADD, -1); if (gnc_import_TransInfo_get_action(info) == GNCImport_SKIP) { /*Show the best match's confidence pixmap in the info column*/ gtk_list_store_set(store, iter, DOWNLOADED_COL_ACTION_PIXBUF, gen_probability_pixbuf( gnc_import_MatchInfo_get_probability ( gnc_import_TransInfo_get_selected_match (info)), gui->user_settings, GTK_WIDGET(gui->view)), -1); } gtk_list_store_set(store, iter, DOWNLOADED_COL_ACTION_CLEAR, gnc_import_TransInfo_get_action(info) == GNCImport_CLEAR, -1); if (gnc_import_TransInfo_get_action(info) == GNCImport_CLEAR) { /*Show the best match's confidence pixmap in the info column*/ gtk_list_store_set(store, iter, DOWNLOADED_COL_ACTION_PIXBUF, gen_probability_pixbuf( gnc_import_MatchInfo_get_probability ( gnc_import_TransInfo_get_selected_match (info)), gui->user_settings, GTK_WIDGET(gui->view)), -1); } gtk_list_store_set(store, iter, DOWNLOADED_COL_ACTION_UPDATE, gnc_import_TransInfo_get_action(info) == GNCImport_UPDATE, -1); if (gnc_import_TransInfo_get_action(info) == GNCImport_UPDATE) { /*Show the best match's confidence pixmap in the info column*/ gtk_list_store_set(store, iter, DOWNLOADED_COL_ACTION_PIXBUF, gen_probability_pixbuf( gnc_import_MatchInfo_get_probability ( gnc_import_TransInfo_get_selected_match (info)), gui->user_settings, GTK_WIDGET(gui->view)), -1); } selection = gtk_tree_view_get_selection(gui->view); gtk_tree_selection_unselect_all(selection); }
gnc_numeric gncEntryGetDocTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn) { gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc); return (is_cn ? gnc_numeric_neg (value) : value); }
gnc_numeric gncEntryGetDocQuantity (const GncEntry *entry, gboolean is_cn) { gnc_numeric value = gncEntryGetQuantity (entry); return (is_cn ? gnc_numeric_neg (value) : value); }
void gnc_payment_window_fill_docs_list (PaymentWindow *pw) { GtkListStore *store; GList *list = NULL, *node; g_return_if_fail (pw->docs_list_tree_view && GTK_IS_TREE_VIEW(pw->docs_list_tree_view)); /* Get a list of open lots for this owner and post account */ if (pw->owner.owner.undefined) list = xaccAccountFindOpenLots (pw->post_acct, gncOwnerLotMatchOwnerFunc, &pw->owner, NULL); /* Clear the existing list */ store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(pw->docs_list_tree_view))); gtk_list_store_clear(store); /* Add the documents and overpayments to the tree view */ for (node = list; node; node = node->next) { GNCLot *lot = node->data; time64 doc_date_time = 0; const gchar *doc_type_str = NULL; const gchar *doc_id_str = NULL; const gchar *doc_deb_str = NULL; const gchar *doc_cred_str = NULL; GtkTreeIter iter; Timespec doc_date; GncInvoice *document; gnc_numeric value = gnc_numeric_zero(); gnc_numeric debit = gnc_numeric_zero(); gnc_numeric credit = gnc_numeric_zero(); /* Find the lot's document if it exists, * it could also be a prepayment lot. */ document = gncInvoiceGetInvoiceFromLot (lot); /* Find the document's date or pre-payment date */ if (document) doc_date = gncInvoiceGetDatePosted (document); else { /* Calculate the payment date based on the lot splits */ Transaction *trans = xaccSplitGetParent (gnc_lot_get_latest_split (lot)); if (trans) doc_date = xaccTransRetDatePostedTS (trans); else continue; /* No valid split in this lot, skip it */ } doc_date_time = timespecToTime64 (doc_date); /* Find the document type. No type means pre-payment in this case */ if (document) { doc_type_str = gncInvoiceGetTypeString (document); } else doc_type_str = _("Pre-Payment"); /* Find the document id. Empty for pre-payments. */ if (document) { doc_id_str = gncInvoiceGetID (document); } /* Find the debit/credit amount. * Invoices/vendor credit notes are debit (increasing the balance) * Customer credit notes/bills are credit (decreasing the balance) * Pre-payments are debit or credit depending on their sign */ value = gnc_lot_get_balance (lot); if (gnc_numeric_positive_p (value)) debit = value; else credit = gnc_numeric_neg (value); /* Only display non-zero debits/credits */ if (!gnc_numeric_zero_p (debit)) doc_deb_str = xaccPrintAmount (debit, gnc_default_print_info (FALSE)); if (!gnc_numeric_zero_p (credit)) doc_cred_str = xaccPrintAmount (credit, gnc_default_print_info (FALSE)); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, 0, doc_date_time, 1, doc_id_str, 2, doc_type_str, 3, doc_deb_str, 4, doc_cred_str, 5, (gpointer)lot, -1); } g_list_free (list); /* Highlight the preset invoice if it's in the new list */ gnc_payment_dialog_highlight_document (pw); }