/********************************************************************\ * gnc-ui-balances.c -- utility functions for calculating * * account and owner balances used in the * * the GnuCash UI * * Copyright (C) 2000 Dave Peticolas * * Copyright (C) 2011 Geert Janssens * * * * 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 "gnc-ui-balances.h" #include "gnc-ui-util.h" #include #include #include "Account.h" #include "Split.h" #include "gncOwner.h" #include "qof.h" G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI; /******************************************************************** * Balance calculations related to accounts ********************************************************************/ /* * This is a wrapper routine around an xaccGetBalanceInCurrency * function that handles additional needs of the gui. * * @param fn The underlying function in Account.c to call to retrieve * a specific balance from the account. * @param account The account to retrieve data about. * @param recurse Include all sub-accounts of this account. * @param negative An indication of whether or not the returned value * is negative. This can be used by the caller to * easily decode whether or not to color the output. * @param commodity The commodity in which the account balance should * be returned. If NULL, the value will be returned in * the commodity of the account. This is normally used * to specify a currency, which forces the conversion * of things like stock account values from share * values to an amount the requested currency. */ gnc_numeric gnc_ui_account_get_balance_full (xaccGetBalanceInCurrencyFn fn, const Account *account, gboolean recurse, gboolean *negative, const gnc_commodity *commodity) { gnc_numeric balance; balance = fn(account, commodity, recurse); /* reverse sign if needed */ if (gnc_reverse_balance (account)) balance = gnc_numeric_neg (balance); /* Record whether the balance is negative. */ if (negative) *negative = gnc_numeric_negative_p(balance); return balance; } /* * This routine retrieves the total balance in an account, possibly * including all sub-accounts under the specified account. */ gnc_numeric gnc_ui_account_get_balance (const Account *account, gboolean recurse) { return gnc_ui_account_get_balance_full (xaccAccountGetBalanceInCurrency, account, recurse, NULL, NULL); } /* * This routine retrieves the total balance in an account converted to * a given currency, possibly including all sub-accounts under the * specified account. * gnc_numeric gnc_ui_account_get_balance_in_currency (const Account *account, const gnc_commodity *currency, gboolean recurse) { return gnc_ui_account_get_balance_full (xaccAccountGetBalanceInCurrency, account, recurse, NULL, currency); } */ /* * This routine retrieves the reconciled balance in an account, * possibly including all sub-accounts under the specified account. */ gnc_numeric gnc_ui_account_get_reconciled_balance (const Account *account, gboolean recurse) { return gnc_ui_account_get_balance_full (xaccAccountGetReconciledBalanceInCurrency, account, recurse, NULL, NULL); } /** * Wrapper around gnc_ui_account_get_balance_full that converts * the resulting number to a character string. The number is * formatted according to the specification of the account currency. * The caller is responsible for g_free'ing the returned memory. * * @param fn The underlying function in Account.c to call to retrieve * a specific balance from the account. * @param account The account to retrieve data about. * @param recurse Include all sub-accounts of this account. * @param negative An indication of whether or not the returned value * is negative. This can be used by the caller to * easily decode whether or not to color the output. */ gchar * gnc_ui_account_get_print_balance (xaccGetBalanceInCurrencyFn fn, const Account *account, gboolean recurse, gboolean *negative) { GNCPrintAmountInfo print_info; gnc_numeric balance; balance = gnc_ui_account_get_balance_full(fn, account, recurse, negative, NULL); print_info = gnc_account_print_info(account, TRUE); return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); } /** * Wrapper around gnc_ui_account_get_balance_full that converts * the resulting number to a character string. The number is * formatted according to the specification of the default reporting * currency. * * @param fn The underlying function in Account.c to call to retrieve * a specific balance from the account. * @param account The account to retrieve data about. * @param recurse Include all sub-accounts of this account. * @param negative An indication of whether or not the returned value * is negative. This can be used by the caller to * easily decode whether or not to color the output. */ gchar * gnc_ui_account_get_print_report_balance (xaccGetBalanceInCurrencyFn fn, const Account *account, gboolean recurse, gboolean *negative) { GNCPrintAmountInfo print_info; gnc_numeric balance; gnc_commodity *report_commodity; report_commodity = gnc_default_report_currency(); balance = gnc_ui_account_get_balance_full(fn, account, recurse, negative, report_commodity); print_info = gnc_commodity_print_info(report_commodity, TRUE); return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); } static gnc_numeric account_get_balance_as_of_date (Account *account, time64 date, gboolean include_children, xaccGetBalanceAsOfDateFn fn) { QofBook *book = gnc_account_get_book (account); GNCPriceDB *pdb = gnc_pricedb_get_db (book); gnc_numeric balance; gnc_commodity *currency; if (account == NULL) return gnc_numeric_zero (); currency = xaccAccountGetCommodity (account); balance = fn (account, date); if (include_children) { GList *children, *node; children = gnc_account_get_descendants(account); for (node = children; node; node = node->next) { Account *child; gnc_commodity *child_currency; gnc_numeric child_balance; child = static_cast(node->data); child_currency = xaccAccountGetCommodity (child); child_balance = fn (child, date); child_balance = gnc_pricedb_convert_balance_latest_price (pdb, child_balance, child_currency, currency); balance = gnc_numeric_add_fixed (balance, child_balance); } g_list_free(children); } /* reverse sign if needed */ if (gnc_reverse_balance (account)) balance = gnc_numeric_neg (balance); return balance; } gnc_numeric gnc_ui_account_get_balance_as_of_date (Account *account, time64 date, gboolean include_children) { return account_get_balance_as_of_date (account, date, include_children, xaccAccountGetBalanceAsOfDate); } gnc_numeric gnc_ui_account_get_reconciled_balance_as_of_date (Account *account, time64 date, gboolean include_children) { return account_get_balance_as_of_date (account, date, include_children, xaccAccountGetReconciledBalanceAsOfDate); } // retrieve account's balance to compare against the limit. // we use today's date because account may have future dated splits static gnc_numeric account_balance_for_limit (const Account *account) { return gnc_ui_account_get_balance_as_of_date ((Account*)account, gnc_time64_get_day_end (gnc_time (NULL)), xaccAccountGetIncludeSubAccountBalances (account)); } static gint account_balance_limit_reached (const Account *account, gnc_numeric balance_limit) { gnc_numeric balance = account_balance_for_limit (account); if (gnc_numeric_zero_p (balance)) return 0; if (gnc_reverse_balance (account)) balance_limit = gnc_numeric_neg (balance_limit); // Returns 1 if a>b, -1 if b>a, 0 if a == b return gnc_numeric_compare (balance, balance_limit); } static gboolean get_limit_info (const Account *account, gnc_numeric *limit, gboolean higher) { gboolean reverse = gnc_reverse_balance (account); if ((higher && reverse) || (!higher && !reverse)) return xaccAccountGetLowerBalanceLimit (account, limit); else return xaccAccountGetHigherBalanceLimit (account, limit); } gboolean gnc_ui_account_is_higher_balance_limit_reached (const Account *account, gboolean *is_zero) { gnc_numeric balance_limit; gboolean limit_valid = FALSE; gboolean retval = FALSE; g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE); limit_valid = get_limit_info (account, &balance_limit, TRUE); if (!limit_valid) return retval; if (gnc_numeric_zero_p (balance_limit)) *is_zero = TRUE; if (account_balance_limit_reached (account, balance_limit) == 1) retval = TRUE; return retval; } gboolean gnc_ui_account_is_lower_balance_limit_reached (const Account *account, gboolean *is_zero) { gnc_numeric balance_limit; gboolean limit_valid = FALSE; gboolean retval = FALSE; g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE); limit_valid = get_limit_info (account, &balance_limit, FALSE); if (!limit_valid) return retval; if (gnc_numeric_zero_p (balance_limit)) *is_zero = TRUE; if (account_balance_limit_reached (account, balance_limit) == -1) retval = TRUE; return retval; } static gchar * make_limit_explanation (const Account *account, const char* template_str, gboolean zero, gboolean higher) { gnc_commodity *currency = xaccAccountGetCommodity (account); GNCPrintAmountInfo pinfo = gnc_commodity_print_info (currency, TRUE); gnc_numeric acct_bal = account_balance_for_limit (account); char *fullname = gnc_account_get_full_name (account); char *bal_str = g_strdup (xaccPrintAmount (acct_bal, pinfo)); char *rv; if (zero) rv = g_strdup_printf (_(template_str), fullname, bal_str); else { gnc_numeric limit; get_limit_info (account, &limit, higher); if (gnc_reverse_balance (account)) limit = gnc_numeric_neg (limit); char *lim_str = g_strdup (xaccPrintAmount (limit, pinfo)); rv = g_strdup_printf (_(template_str), fullname, bal_str, lim_str); g_free (lim_str); } g_free (bal_str); g_free (fullname); return rv; } static gchar * get_balance_limit_info (const Account *account, gboolean icon) { gboolean lower_limit_reached, higher_limit_reached; gboolean lower_is_zero = FALSE; gboolean higher_is_zero = FALSE; const char *higher_template = N_("%s balance of %s is above the upper limit %s."); const char *lower_template = N_("%s balance of %s is below the lower limit %s."); const char *zero_template = N_("%s balance of %s should be zero."); g_return_val_if_fail (GNC_IS_ACCOUNT(account), NULL); higher_limit_reached = gnc_ui_account_is_higher_balance_limit_reached (account, &higher_is_zero); // assume the higher value will be set mostly so test that first if (higher_limit_reached && !higher_is_zero) return icon ? g_strdup ("go-top") : make_limit_explanation (account, higher_template, FALSE, TRUE); lower_limit_reached = gnc_ui_account_is_lower_balance_limit_reached (account, &lower_is_zero); if (lower_limit_reached && (!lower_is_zero || !higher_is_zero)) return icon ? g_strdup ("go-bottom") : make_limit_explanation (account, lower_template, FALSE, FALSE); if (higher_limit_reached && !lower_is_zero) return icon ? g_strdup ("go-top") : make_limit_explanation (account, higher_template, FALSE, TRUE); if ((lower_limit_reached || higher_limit_reached ) && lower_is_zero && higher_is_zero) return icon ? g_strdup ("dialog-warning") : make_limit_explanation (account, zero_template, TRUE, FALSE); return NULL; } gchar * gnc_ui_account_get_balance_limit_icon_name (const Account *account) { char *icon = get_balance_limit_info (account, TRUE); return icon ? icon : g_strdup (""); } gchar * gnc_ui_account_get_balance_limit_explanation (const Account *account) { return get_balance_limit_info (account, FALSE); } /******************************************************************** * Balance calculations related to owners ********************************************************************/ /* * This is a wrapper routine around an gncOwnerGetBalanceInCurrency * function that handles additional needs of the gui. * * @param owner The owner to retrieve data about. * @param negative An indication of whether or not the returned value * is negative. This can be used by the caller to * easily decode whether or not to color the output. * @param commodity The commodity in which the account balance should * be returned. If NULL, the value will be returned in * the commodity of the owner. This is normally used * to specify a currency, which forces the conversion * of things like stock account values from share * values to an amount the requested currency. */ gnc_numeric gnc_ui_owner_get_balance_full (GncOwner *owner, gboolean *negative, const gnc_commodity *commodity) { gnc_numeric balance; if (!owner) return gnc_numeric_zero (); balance = gncOwnerGetBalanceInCurrency (owner, commodity); /* reverse sign if needed */ if ((gncOwnerGetType (owner) != GNC_OWNER_CUSTOMER)) balance = gnc_numeric_neg (balance); /* Record whether the balance is negative. */ if (negative) *negative = gnc_numeric_negative_p (balance); return balance; } /** * Wrapper around gnc_ui_owner_get_balance_full that converts * the resulting number to a character string. The number is * formatted according to the specification of the owner currency. * The caller is responsible for g_free'ing the returned memory. * * @param owner The owner to retrieve data about. * @param negative An indication of whether or not the returned value * is negative. This can be used by the caller to * easily decode whether or not to color the output. */ gchar * gnc_ui_owner_get_print_balance (GncOwner *owner, gboolean *negative) { gnc_numeric balance; GNCPrintAmountInfo print_info; balance = gnc_ui_owner_get_balance_full (owner, negative, NULL); print_info = gnc_commodity_print_info (gncOwnerGetCurrency (owner), TRUE); return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); } /** * Wrapper around gnc_ui_owner_get_balance_full that converts * the resulting number to a character string. The number is * formatted according to the specification of the default reporting * currency. * * @param account The owner to retrieve data about. * @param negative An indication of whether or not the returned value * is negative. This can be used by the caller to * easily decode whether or not to color the output. */ gchar * gnc_ui_owner_get_print_report_balance (GncOwner *owner, gboolean *negative) { GNCPrintAmountInfo print_info; gnc_numeric balance; gnc_commodity *report_commodity; report_commodity = gnc_default_report_currency (); balance = gnc_ui_owner_get_balance_full (owner, negative, report_commodity); print_info = gnc_commodity_print_info (report_commodity, TRUE); return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info)); }