Branch data Line data Source code
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 : 0 : 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 : 0 : balance = fn(account, commodity, recurse);
73 : :
74 : : /* reverse sign if needed */
75 : 0 : if (gnc_reverse_balance (account))
76 : 0 : balance = gnc_numeric_neg (balance);
77 : :
78 : : /* Record whether the balance is negative. */
79 : 0 : if (negative)
80 : 0 : *negative = gnc_numeric_negative_p(balance);
81 : :
82 : 0 : 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 : 0 : gnc_ui_account_get_balance (const Account *account, gboolean recurse)
91 : : {
92 : 0 : return gnc_ui_account_get_balance_full (xaccAccountGetBalanceInCurrency,
93 : 0 : 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 : 0 : gnc_ui_account_get_reconciled_balance (const Account *account,
116 : : gboolean recurse)
117 : : {
118 : 0 : return gnc_ui_account_get_balance_full (xaccAccountGetReconciledBalanceInCurrency,
119 : 0 : account, recurse, NULL, NULL);
120 : : }
121 : :
122 : :
123 : : /**
124 : : * Wrapper around gnc_ui_account_get_balance_full that converts
125 : : * the resulting number to a character string. The number is
126 : : * formatted according to the specification of the account currency.
127 : : * The caller is responsible for g_free'ing the returned memory.
128 : : *
129 : : * @param fn The underlying function in Account.c to call to retrieve
130 : : * a specific balance from the account.
131 : : * @param account The account to retrieve data about.
132 : : * @param recurse Include all sub-accounts of this account.
133 : : * @param negative An indication of whether or not the returned value
134 : : * is negative. This can be used by the caller to
135 : : * easily decode whether or not to color the output.
136 : : */
137 : : gchar *
138 : 0 : 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 : 0 : balance = gnc_ui_account_get_balance_full(fn, account, recurse,
147 : : negative, NULL);
148 : 0 : print_info = gnc_account_print_info(account, TRUE);
149 : :
150 : 0 : return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
151 : : }
152 : :
153 : :
154 : : /**
155 : : * Wrapper around gnc_ui_account_get_balance_full that converts
156 : : * the resulting number to a character string. The number is
157 : : * formatted according to the specification of the default reporting
158 : : * currency.
159 : : *
160 : : * @param fn The underlying function in Account.c to call to retrieve
161 : : * a specific balance from the account.
162 : : * @param account The account to retrieve data about.
163 : : * @param recurse Include all sub-accounts of this account.
164 : : * @param negative An indication of whether or not the returned value
165 : : * is negative. This can be used by the caller to
166 : : * easily decode whether or not to color the output.
167 : : */
168 : : gchar *
169 : 0 : 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 : 0 : report_commodity = gnc_default_report_currency();
179 : 0 : balance = gnc_ui_account_get_balance_full(fn, account, recurse,
180 : : negative, report_commodity);
181 : 0 : print_info = gnc_commodity_print_info(report_commodity, TRUE);
182 : :
183 : 0 : return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
184 : : }
185 : :
186 : : static gnc_numeric
187 : 0 : account_get_balance_as_of_date (Account *account,
188 : : time64 date,
189 : : gboolean include_children,
190 : : xaccGetBalanceAsOfDateFn fn)
191 : : {
192 : 0 : QofBook *book = gnc_account_get_book (account);
193 : 0 : GNCPriceDB *pdb = gnc_pricedb_get_db (book);
194 : : gnc_numeric balance;
195 : : gnc_commodity *currency;
196 : :
197 : 0 : if (account == NULL)
198 : 0 : return gnc_numeric_zero ();
199 : :
200 : 0 : currency = xaccAccountGetCommodity (account);
201 : 0 : balance = fn (account, date);
202 : :
203 : 0 : if (include_children)
204 : : {
205 : : GList *children, *node;
206 : :
207 : 0 : children = gnc_account_get_descendants(account);
208 : :
209 : 0 : for (node = children; node; node = node->next)
210 : : {
211 : : Account *child;
212 : : gnc_commodity *child_currency;
213 : : gnc_numeric child_balance;
214 : :
215 : 0 : child = static_cast<Account*>(node->data);
216 : 0 : child_currency = xaccAccountGetCommodity (child);
217 : 0 : child_balance = fn (child, date);
218 : : child_balance =
219 : 0 : gnc_pricedb_convert_balance_latest_price (pdb, child_balance,
220 : : child_currency,
221 : : currency);
222 : 0 : balance = gnc_numeric_add_fixed (balance, child_balance);
223 : : }
224 : :
225 : 0 : g_list_free(children);
226 : : }
227 : :
228 : : /* reverse sign if needed */
229 : 0 : if (gnc_reverse_balance (account))
230 : 0 : balance = gnc_numeric_neg (balance);
231 : :
232 : 0 : return balance;
233 : : }
234 : :
235 : : gnc_numeric
236 : 0 : gnc_ui_account_get_balance_as_of_date (Account *account,
237 : : time64 date,
238 : : gboolean include_children)
239 : : {
240 : 0 : return account_get_balance_as_of_date (account, date, include_children,
241 : 0 : xaccAccountGetBalanceAsOfDate);
242 : : }
243 : :
244 : : gnc_numeric
245 : 0 : gnc_ui_account_get_reconciled_balance_as_of_date (Account *account,
246 : : time64 date,
247 : : gboolean include_children)
248 : : {
249 : 0 : return account_get_balance_as_of_date (account, date, include_children,
250 : 0 : xaccAccountGetReconciledBalanceAsOfDate);
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 : 0 : account_balance_for_limit (const Account *account)
257 : : {
258 : : return gnc_ui_account_get_balance_as_of_date
259 : 0 : ((Account*)account, gnc_time64_get_day_end (gnc_time (NULL)),
260 : 0 : xaccAccountGetIncludeSubAccountBalances (account));
261 : : }
262 : :
263 : : static gint
264 : 0 : account_balance_limit_reached (const Account *account, gnc_numeric balance_limit)
265 : : {
266 : 0 : gnc_numeric balance = account_balance_for_limit (account);
267 : :
268 : 0 : if (gnc_numeric_zero_p (balance))
269 : 0 : return 0;
270 : :
271 : 0 : if (gnc_reverse_balance (account))
272 : 0 : balance_limit = gnc_numeric_neg (balance_limit);
273 : :
274 : : // Returns 1 if a>b, -1 if b>a, 0 if a == b
275 : 0 : return gnc_numeric_compare (balance, balance_limit);
276 : : }
277 : :
278 : : static gboolean
279 : 0 : get_limit_info (const Account *account, gnc_numeric *limit, gboolean higher)
280 : : {
281 : 0 : gboolean reverse = gnc_reverse_balance (account);
282 : 0 : if ((higher && reverse) || (!higher && !reverse))
283 : 0 : return xaccAccountGetLowerBalanceLimit (account, limit);
284 : : else
285 : 0 : return xaccAccountGetHigherBalanceLimit (account, limit);
286 : : }
287 : :
288 : : gboolean
289 : 0 : gnc_ui_account_is_higher_balance_limit_reached (const Account *account,
290 : : gboolean *is_zero)
291 : : {
292 : : gnc_numeric balance_limit;
293 : 0 : gboolean limit_valid = FALSE;
294 : 0 : gboolean retval = FALSE;
295 : :
296 : 0 : g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE);
297 : :
298 : 0 : limit_valid = get_limit_info (account, &balance_limit, TRUE);
299 : :
300 : 0 : if (!limit_valid)
301 : 0 : return retval;
302 : :
303 : 0 : if (gnc_numeric_zero_p (balance_limit))
304 : 0 : *is_zero = TRUE;
305 : :
306 : 0 : if (account_balance_limit_reached (account, balance_limit) == 1)
307 : 0 : retval = TRUE;
308 : :
309 : 0 : return retval;
310 : : }
311 : :
312 : : gboolean
313 : 0 : gnc_ui_account_is_lower_balance_limit_reached (const Account *account,
314 : : gboolean *is_zero)
315 : : {
316 : : gnc_numeric balance_limit;
317 : 0 : gboolean limit_valid = FALSE;
318 : 0 : gboolean retval = FALSE;
319 : :
320 : 0 : g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE);
321 : :
322 : 0 : limit_valid = get_limit_info (account, &balance_limit, FALSE);
323 : :
324 : 0 : if (!limit_valid)
325 : 0 : return retval;
326 : :
327 : 0 : if (gnc_numeric_zero_p (balance_limit))
328 : 0 : *is_zero = TRUE;
329 : :
330 : 0 : if (account_balance_limit_reached (account, balance_limit) == -1)
331 : 0 : retval = TRUE;
332 : :
333 : 0 : return retval;
334 : : }
335 : :
336 : : static gchar *
337 : 0 : make_limit_explanation (const Account *account, const char* template_str,
338 : : gboolean zero, gboolean higher)
339 : : {
340 : 0 : gnc_commodity *currency = xaccAccountGetCommodity (account);
341 : 0 : GNCPrintAmountInfo pinfo = gnc_commodity_print_info (currency, TRUE);
342 : 0 : gnc_numeric acct_bal = account_balance_for_limit (account);
343 : 0 : char *fullname = gnc_account_get_full_name (account);
344 : 0 : char *bal_str = g_strdup (xaccPrintAmount (acct_bal, pinfo));
345 : : char *rv;
346 : 0 : if (zero)
347 : 0 : rv = g_strdup_printf (_(template_str), fullname, bal_str);
348 : : else
349 : : {
350 : : gnc_numeric limit;
351 : 0 : get_limit_info (account, &limit, higher);
352 : 0 : if (gnc_reverse_balance (account))
353 : 0 : limit = gnc_numeric_neg (limit);
354 : 0 : char *lim_str = g_strdup (xaccPrintAmount (limit, pinfo));
355 : 0 : rv = g_strdup_printf (_(template_str), fullname, bal_str, lim_str);
356 : 0 : g_free (lim_str);
357 : : }
358 : 0 : g_free (bal_str);
359 : 0 : g_free (fullname);
360 : 0 : return rv;
361 : : }
362 : :
363 : : static gchar *
364 : 0 : get_balance_limit_info (const Account *account, gboolean icon)
365 : : {
366 : : gboolean lower_limit_reached, higher_limit_reached;
367 : 0 : gboolean lower_is_zero = FALSE;
368 : 0 : gboolean higher_is_zero = FALSE;
369 : 0 : const char *higher_template = N_("%s balance of %s is above the upper limit %s.");
370 : 0 : const char *lower_template = N_("%s balance of %s is below the lower limit %s.");
371 : 0 : const char *zero_template = N_("%s balance of %s should be zero.");
372 : :
373 : 0 : g_return_val_if_fail (GNC_IS_ACCOUNT(account), NULL);
374 : :
375 : 0 : 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 : 0 : if (higher_limit_reached && !higher_is_zero)
379 : 0 : return icon ? g_strdup ("go-top") : make_limit_explanation (account, higher_template, FALSE, TRUE);
380 : :
381 : 0 : lower_limit_reached = gnc_ui_account_is_lower_balance_limit_reached (account, &lower_is_zero);
382 : :
383 : 0 : if (lower_limit_reached && (!lower_is_zero || !higher_is_zero))
384 : 0 : return icon ? g_strdup ("go-bottom") : make_limit_explanation (account, lower_template, FALSE, FALSE);
385 : :
386 : 0 : if (higher_limit_reached && !lower_is_zero)
387 : 0 : return icon ? g_strdup ("go-top") : make_limit_explanation (account, higher_template, FALSE, TRUE);
388 : :
389 : 0 : if ((lower_limit_reached || higher_limit_reached ) && lower_is_zero && higher_is_zero)
390 : 0 : return icon ? g_strdup ("dialog-warning") : make_limit_explanation (account, zero_template, TRUE, FALSE);
391 : :
392 : 0 : return NULL;
393 : : }
394 : :
395 : : gchar *
396 : 0 : gnc_ui_account_get_balance_limit_icon_name (const Account *account)
397 : : {
398 : 0 : char *icon = get_balance_limit_info (account, TRUE);
399 : 0 : return icon ? icon : g_strdup ("");
400 : : }
401 : :
402 : : gchar *
403 : 0 : gnc_ui_account_get_balance_limit_explanation (const Account *account)
404 : : {
405 : 0 : 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 : 0 : gnc_ui_owner_get_balance_full (GncOwner *owner,
429 : : gboolean *negative,
430 : : const gnc_commodity *commodity)
431 : : {
432 : : gnc_numeric balance;
433 : :
434 : 0 : if (!owner)
435 : 0 : return gnc_numeric_zero ();
436 : :
437 : 0 : balance = gncOwnerGetBalanceInCurrency (owner, commodity);
438 : :
439 : : /* reverse sign if needed */
440 : 0 : if ((gncOwnerGetType (owner) != GNC_OWNER_CUSTOMER))
441 : 0 : balance = gnc_numeric_neg (balance);
442 : :
443 : : /* Record whether the balance is negative. */
444 : 0 : if (negative)
445 : 0 : *negative = gnc_numeric_negative_p (balance);
446 : :
447 : 0 : return balance;
448 : : }
449 : :
450 : :
451 : : /**
452 : : * Wrapper around gnc_ui_owner_get_balance_full that converts
453 : : * the resulting number to a character string. The number is
454 : : * formatted according to the specification of the owner currency.
455 : : * The caller is responsible for g_free'ing the returned memory.
456 : : *
457 : : * @param owner The owner to retrieve data about.
458 : : * @param negative An indication of whether or not the returned value
459 : : * is negative. This can be used by the caller to
460 : : * easily decode whether or not to color the output.
461 : : */
462 : : gchar *
463 : 0 : gnc_ui_owner_get_print_balance (GncOwner *owner,
464 : : gboolean *negative)
465 : : {
466 : : gnc_numeric balance;
467 : : GNCPrintAmountInfo print_info;
468 : :
469 : 0 : balance = gnc_ui_owner_get_balance_full (owner, negative, NULL);
470 : 0 : print_info = gnc_commodity_print_info (gncOwnerGetCurrency (owner), TRUE);
471 : :
472 : 0 : return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
473 : : }
474 : :
475 : : /**
476 : : * Wrapper around gnc_ui_owner_get_balance_full that converts
477 : : * the resulting number to a character string. The number is
478 : : * formatted according to the specification of the default reporting
479 : : * currency.
480 : : *
481 : : * @param account The owner to retrieve data about.
482 : : * @param negative An indication of whether or not the returned value
483 : : * is negative. This can be used by the caller to
484 : : * easily decode whether or not to color the output.
485 : : */
486 : : gchar *
487 : 0 : 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 : 0 : report_commodity = gnc_default_report_currency ();
495 : 0 : balance = gnc_ui_owner_get_balance_full (owner, negative,
496 : : report_commodity);
497 : 0 : print_info = gnc_commodity_print_info (report_commodity, TRUE);
498 : :
499 : 0 : return g_strdup (gnc_print_amount_with_bidi_ltr_isolate (balance, print_info));
500 : : }
501 : :
|