GnuCash  5.6-150-g038405b370+
gnc-ui-balances.cpp
1 /********************************************************************\
2  * gnc-ui-balances.c -- utility functions for calculating *
3  * account and owner balances used in the *
4  * the GnuCash UI *
5  * Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
6  * Copyright (C) 2011 Geert Janssens <geert@kobaltwit.be> *
7  * *
8  * This program is free software; you can redistribute it and/or *
9  * modify it under the terms of the GNU General Public License as *
10  * published by the Free Software Foundation; either version 2 of *
11  * the License, or (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, contact: *
20  * *
21  * Free Software Foundation Voice: +1-617-542-5942 *
22  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
23  * Boston, MA 02110-1301, USA gnu@gnu.org *
24 \********************************************************************/
25 
26 #include <config.h>
27 
28 #include "gnc-ui-balances.h"
29 #include "gnc-ui-util.h"
30 
31 #include <glib.h>
32 #include <glib/gi18n.h>
33 
34 #include "Account.h"
35 #include "Split.h"
36 #include "gncOwner.h"
37 #include "qof.h"
38 
39 G_GNUC_UNUSED static QofLogModule log_module = GNC_MOD_GUI;
40 
41 /********************************************************************
42  * Balance calculations related to accounts
43  ********************************************************************/
44 
45 /*
46  * This is a wrapper routine around an xaccGetBalanceInCurrency
47  * function that handles additional needs of the gui.
48  *
49  * @param fn The underlying function in Account.c to call to retrieve
50  * a specific balance from the account.
51  * @param account The account to retrieve data about.
52  * @param recurse Include all sub-accounts of this account.
53  * @param negative An indication of whether or not the returned value
54  * is negative. This can be used by the caller to
55  * easily decode whether or not to color the output.
56  * @param commodity The commodity in which the account balance should
57  * be returned. If NULL, the value will be returned in
58  * the commodity of the account. This is normally used
59  * to specify a currency, which forces the conversion
60  * of things like stock account values from share
61  * values to an amount the requested currency.
62  */
63 gnc_numeric
64 gnc_ui_account_get_balance_full (xaccGetBalanceInCurrencyFn fn,
65  const Account *account,
66  gboolean recurse,
67  gboolean *negative,
68  const gnc_commodity *commodity)
69 {
70  gnc_numeric balance;
71 
72  balance = fn(account, commodity, recurse);
73 
74  /* reverse sign if needed */
75  if (gnc_reverse_balance (account))
76  balance = gnc_numeric_neg (balance);
77 
78  /* Record whether the balance is negative. */
79  if (negative)
80  *negative = gnc_numeric_negative_p(balance);
81 
82  return balance;
83 }
84 
85 /*
86  * This routine retrieves the total balance in an account, possibly
87  * including all sub-accounts under the specified account.
88  */
89 gnc_numeric
90 gnc_ui_account_get_balance (const Account *account, gboolean recurse)
91 {
92  return gnc_ui_account_get_balance_full (xaccAccountGetBalanceInCurrency,
93  account, recurse, NULL, NULL);
94 }
95 
96 /*
97  * This routine retrieves the total balance in an account converted to
98  * a given currency, possibly including all sub-accounts under the
99  * specified account.
100  *
101 gnc_numeric
102 gnc_ui_account_get_balance_in_currency (const Account *account,
103  const gnc_commodity *currency,
104  gboolean recurse)
105 {
106  return gnc_ui_account_get_balance_full (xaccAccountGetBalanceInCurrency,
107  account, recurse, NULL, currency);
108 }
109 */
110 /*
111  * This routine retrieves the reconciled balance in an account,
112  * possibly including all sub-accounts under the specified account.
113  */
114 gnc_numeric
115 gnc_ui_account_get_reconciled_balance (const Account *account,
116  gboolean recurse)
117 {
118  return gnc_ui_account_get_balance_full (xaccAccountGetReconciledBalanceInCurrency,
119  account, recurse, NULL, NULL);
120 }
121 
122 
137 gchar *
138 gnc_ui_account_get_print_balance (xaccGetBalanceInCurrencyFn fn,
139  const Account *account,
140  gboolean recurse,
141  gboolean *negative)
142 {
143  GNCPrintAmountInfo print_info;
144  gnc_numeric balance;
145 
146  balance = gnc_ui_account_get_balance_full(fn, account, recurse,
147  negative, NULL);
148  print_info = gnc_account_print_info(account, TRUE);
149 
150  return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
151 }
152 
153 
168 gchar *
169 gnc_ui_account_get_print_report_balance (xaccGetBalanceInCurrencyFn fn,
170  const Account *account,
171  gboolean recurse,
172  gboolean *negative)
173 {
174  GNCPrintAmountInfo print_info;
175  gnc_numeric balance;
176  gnc_commodity *report_commodity;
177 
178  report_commodity = gnc_default_report_currency();
179  balance = gnc_ui_account_get_balance_full(fn, account, recurse,
180  negative, report_commodity);
181  print_info = gnc_commodity_print_info(report_commodity, TRUE);
182 
183  return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
184 }
185 
186 static gnc_numeric
187 account_get_balance_as_of_date (Account *account,
188  time64 date,
189  gboolean include_children,
190  xaccGetBalanceAsOfDateFn fn)
191 {
192  QofBook *book = gnc_account_get_book (account);
193  GNCPriceDB *pdb = gnc_pricedb_get_db (book);
194  gnc_numeric balance;
195  gnc_commodity *currency;
196 
197  if (account == NULL)
198  return gnc_numeric_zero ();
199 
200  currency = xaccAccountGetCommodity (account);
201  balance = fn (account, date);
202 
203  if (include_children)
204  {
205  GList *children, *node;
206 
207  children = gnc_account_get_descendants(account);
208 
209  for (node = children; node; node = node->next)
210  {
211  Account *child;
212  gnc_commodity *child_currency;
213  gnc_numeric child_balance;
214 
215  child = static_cast<Account*>(node->data);
216  child_currency = xaccAccountGetCommodity (child);
217  child_balance = fn (child, date);
218  child_balance =
219  gnc_pricedb_convert_balance_latest_price (pdb, child_balance,
220  child_currency,
221  currency);
222  balance = gnc_numeric_add_fixed (balance, child_balance);
223  }
224 
225  g_list_free(children);
226  }
227 
228  /* reverse sign if needed */
229  if (gnc_reverse_balance (account))
230  balance = gnc_numeric_neg (balance);
231 
232  return balance;
233 }
234 
235 gnc_numeric
236 gnc_ui_account_get_balance_as_of_date (Account *account,
237  time64 date,
238  gboolean include_children)
239 {
240  return account_get_balance_as_of_date (account, date, include_children,
242 }
243 
244 gnc_numeric
245 gnc_ui_account_get_reconciled_balance_as_of_date (Account *account,
246  time64 date,
247  gboolean include_children)
248 {
249  return account_get_balance_as_of_date (account, date, include_children,
251 }
252 
253 // retrieve account's balance to compare against the limit.
254 // we use today's date because account may have future dated splits
255 static gnc_numeric
256 account_balance_for_limit (const Account *account)
257 {
258  return gnc_ui_account_get_balance_as_of_date
259  ((Account*)account, gnc_time64_get_day_end (gnc_time (NULL)),
261 }
262 
263 static gint
264 account_balance_limit_reached (const Account *account, gnc_numeric balance_limit)
265 {
266  gnc_numeric balance = account_balance_for_limit (account);
267 
268  if (gnc_numeric_zero_p (balance))
269  return 0;
270 
271  if (gnc_reverse_balance (account))
272  balance_limit = gnc_numeric_neg (balance_limit);
273 
274  // Returns 1 if a>b, -1 if b>a, 0 if a == b
275  return gnc_numeric_compare (balance, balance_limit);
276 }
277 
278 static gboolean
279 get_limit_info (const Account *account, gnc_numeric *limit, gboolean higher)
280 {
281  gboolean reverse = gnc_reverse_balance (account);
282  if ((higher && reverse) || (!higher && !reverse))
283  return xaccAccountGetLowerBalanceLimit (account, limit);
284  else
285  return xaccAccountGetHigherBalanceLimit (account, limit);
286 }
287 
288 gboolean
289 gnc_ui_account_is_higher_balance_limit_reached (const Account *account,
290  gboolean *is_zero)
291 {
292  gnc_numeric balance_limit;
293  gboolean limit_valid = FALSE;
294  gboolean retval = FALSE;
295 
296  g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE);
297 
298  limit_valid = get_limit_info (account, &balance_limit, TRUE);
299 
300  if (!limit_valid)
301  return retval;
302 
303  if (gnc_numeric_zero_p (balance_limit))
304  *is_zero = TRUE;
305 
306  if (account_balance_limit_reached (account, balance_limit) == 1)
307  retval = TRUE;
308 
309  return retval;
310 }
311 
312 gboolean
313 gnc_ui_account_is_lower_balance_limit_reached (const Account *account,
314  gboolean *is_zero)
315 {
316  gnc_numeric balance_limit;
317  gboolean limit_valid = FALSE;
318  gboolean retval = FALSE;
319 
320  g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE);
321 
322  limit_valid = get_limit_info (account, &balance_limit, FALSE);
323 
324  if (!limit_valid)
325  return retval;
326 
327  if (gnc_numeric_zero_p (balance_limit))
328  *is_zero = TRUE;
329 
330  if (account_balance_limit_reached (account, balance_limit) == -1)
331  retval = TRUE;
332 
333  return retval;
334 }
335 
336 static gchar *
337 make_limit_explanation (const Account *account, const char* template_str,
338  gboolean zero, gboolean higher)
339 {
340  gnc_commodity *currency = xaccAccountGetCommodity (account);
341  GNCPrintAmountInfo pinfo = gnc_commodity_print_info (currency, TRUE);
342  gnc_numeric acct_bal = account_balance_for_limit (account);
343  char *fullname = gnc_account_get_full_name (account);
344  char *bal_str = g_strdup (xaccPrintAmount (acct_bal, pinfo));
345  char *rv;
346  if (zero)
347  rv = g_strdup_printf (_(template_str), fullname, bal_str);
348  else
349  {
350  gnc_numeric limit;
351  get_limit_info (account, &limit, higher);
352  if (gnc_reverse_balance (account))
353  limit = gnc_numeric_neg (limit);
354  char *lim_str = g_strdup (xaccPrintAmount (limit, pinfo));
355  rv = g_strdup_printf (_(template_str), fullname, bal_str, lim_str);
356  g_free (lim_str);
357  }
358  g_free (bal_str);
359  g_free (fullname);
360  return rv;
361 }
362 
363 static gchar *
364 get_balance_limit_info (const Account *account, gboolean icon)
365 {
366  gboolean lower_limit_reached, higher_limit_reached;
367  gboolean lower_is_zero = FALSE;
368  gboolean higher_is_zero = FALSE;
369  const char *higher_template = N_("%s balance of %s is above the upper limit %s.");
370  const char *lower_template = N_("%s balance of %s is below the lower limit %s.");
371  const char *zero_template = N_("%s balance of %s should be zero.");
372 
373  g_return_val_if_fail (GNC_IS_ACCOUNT(account), NULL);
374 
375  higher_limit_reached = gnc_ui_account_is_higher_balance_limit_reached (account, &higher_is_zero);
376 
377  // assume the higher value will be set mostly so test that first
378  if (higher_limit_reached && !higher_is_zero)
379  return icon ? g_strdup ("go-top") : make_limit_explanation (account, higher_template, FALSE, TRUE);
380 
381  lower_limit_reached = gnc_ui_account_is_lower_balance_limit_reached (account, &lower_is_zero);
382 
383  if (lower_limit_reached && (!lower_is_zero || !higher_is_zero))
384  return icon ? g_strdup ("go-bottom") : make_limit_explanation (account, lower_template, FALSE, FALSE);
385 
386  if (higher_limit_reached && !lower_is_zero)
387  return icon ? g_strdup ("go-top") : make_limit_explanation (account, higher_template, FALSE, TRUE);
388 
389  if ((lower_limit_reached || higher_limit_reached ) && lower_is_zero && higher_is_zero)
390  return icon ? g_strdup ("dialog-warning") : make_limit_explanation (account, zero_template, TRUE, FALSE);
391 
392  return NULL;
393 }
394 
395 gchar *
396 gnc_ui_account_get_balance_limit_icon_name (const Account *account)
397 {
398  char *icon = get_balance_limit_info (account, TRUE);
399  return icon ? icon : g_strdup ("");
400 }
401 
402 gchar *
403 gnc_ui_account_get_balance_limit_explanation (const Account *account)
404 {
405  return get_balance_limit_info (account, FALSE);
406 }
407 
408 /********************************************************************
409  * Balance calculations related to owners
410  ********************************************************************/
411 
412 /*
413  * This is a wrapper routine around an gncOwnerGetBalanceInCurrency
414  * function that handles additional needs of the gui.
415  *
416  * @param owner The owner to retrieve data about.
417  * @param negative An indication of whether or not the returned value
418  * is negative. This can be used by the caller to
419  * easily decode whether or not to color the output.
420  * @param commodity The commodity in which the account balance should
421  * be returned. If NULL, the value will be returned in
422  * the commodity of the owner. This is normally used
423  * to specify a currency, which forces the conversion
424  * of things like stock account values from share
425  * values to an amount the requested currency.
426  */
427 gnc_numeric
428 gnc_ui_owner_get_balance_full (GncOwner *owner,
429  gboolean *negative,
430  const gnc_commodity *commodity)
431 {
432  gnc_numeric balance;
433 
434  if (!owner)
435  return gnc_numeric_zero ();
436 
437  balance = gncOwnerGetBalanceInCurrency (owner, commodity);
438 
439  /* reverse sign if needed */
440  if ((gncOwnerGetType (owner) != GNC_OWNER_CUSTOMER))
441  balance = gnc_numeric_neg (balance);
442 
443  /* Record whether the balance is negative. */
444  if (negative)
445  *negative = gnc_numeric_negative_p (balance);
446 
447  return balance;
448 }
449 
450 
462 gchar *
463 gnc_ui_owner_get_print_balance (GncOwner *owner,
464  gboolean *negative)
465 {
466  gnc_numeric balance;
467  GNCPrintAmountInfo print_info;
468 
469  balance = gnc_ui_owner_get_balance_full (owner, negative, NULL);
470  print_info = gnc_commodity_print_info (gncOwnerGetCurrency (owner), TRUE);
471 
472  return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
473 }
474 
486 gchar *
487 gnc_ui_owner_get_print_report_balance (GncOwner *owner,
488  gboolean *negative)
489 {
490  GNCPrintAmountInfo print_info;
491  gnc_numeric balance;
492  gnc_commodity *report_commodity;
493 
494  report_commodity = gnc_default_report_currency ();
495  balance = gnc_ui_owner_get_balance_full (owner, negative,
496  report_commodity);
497  print_info = gnc_commodity_print_info (report_commodity, TRUE);
498 
499  return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
500 }
501 
Business Interface: Object OWNERs.
const char * gnc_print_amount_with_bidi_ltr_isolate(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
utility functions for the GnuCash UI
gnc_numeric gnc_numeric_neg(gnc_numeric a)
Returns a newly created gnc_numeric that is the negative of the given gnc_numeric value...
STRUCTS.
const char * xaccPrintAmount(gnc_numeric val, GNCPrintAmountInfo info)
Make a string representation of a gnc_numeric.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
API for Transactions and Splits (journal entries)
gint gnc_numeric_compare(gnc_numeric a, gnc_numeric b)
Returns 1 if a>b, -1 if b>a, 0 if a == b.
gnc_commodity * gnc_default_report_currency(void)
Return the default currency for use in reports, as set by the user.
GNCPriceDB * gnc_pricedb_get_db(QofBook *book)
Return the pricedb associated with the book.
gboolean gnc_numeric_negative_p(gnc_numeric a)
Returns 1 if a < 0, otherwise returns 0.
gnc_numeric gnc_pricedb_convert_balance_latest_price(GNCPriceDB *pdb, gnc_numeric balance, const gnc_commodity *balance_currency, const gnc_commodity *new_currency)
Convert a balance from one currency to another using the most recent price between the two...
gboolean xaccAccountGetLowerBalanceLimit(const Account *acc, gnc_numeric *balance)
Get the lower balance limit for the account.
Definition: Account.cpp:4681
gchar * gnc_account_get_full_name(const Account *account)
The gnc_account_get_full_name routine returns the fully qualified name of the account using the given...
Definition: Account.cpp:3275
gnc_numeric xaccAccountGetReconciledBalanceAsOfDate(Account *acc, time64 date)
Get the reconciled balance of the account at the end of the day of the date specified.
Definition: Account.cpp:3501
Account handling public routines.
gnc_numeric gncOwnerGetBalanceInCurrency(const GncOwner *owner, const gnc_commodity *report_currency)
Given an owner, extract the open balance from the owner and then convert it to the desired currency...
Definition: gncOwner.c:1478
gnc_numeric xaccAccountGetBalanceAsOfDate(Account *acc, time64 date)
Get the balance of the account at the end of the day before the date specified.
Definition: Account.cpp:3489
GncOwnerType gncOwnerGetType(const GncOwner *owner)
Returns the GncOwnerType of this owner.
Definition: gncOwner.c:200
GList * gnc_account_get_descendants(const Account *account)
This routine returns a flat list of all of the accounts that are descendants of the specified account...
Definition: Account.cpp:3014
gboolean xaccAccountGetIncludeSubAccountBalances(const Account *acc)
Get whether to include balances of sub accounts.
Definition: Account.cpp:4712
gnc_commodity * xaccAccountGetCommodity(const Account *acc)
Get the account&#39;s commodity.
Definition: Account.cpp:3371
gboolean xaccAccountGetHigherBalanceLimit(const Account *acc, gnc_numeric *balance)
Get the higher balance limit for the account.
Definition: Account.cpp:4674
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
time64 gnc_time64_get_day_end(time64 time_val)
The gnc_time64_get_day_end() routine will take the given time in seconds and adjust it to the last se...
Definition: gnc-date.cpp:1316