Line data Source code
1 : /********************************************************************\
2 : * Account.c -- Account data structure implementation *
3 : * Copyright (C) 1997 Robin D. Clark *
4 : * Copyright (C) 1997-2003 Linas Vepstas <linas@linas.org> *
5 : * Copyright (C) 2007 David Hampton <hampton@employees.org> *
6 : * *
7 : * This program is free software; you can redistribute it and/or *
8 : * modify it under the terms of the GNU General Public License as *
9 : * published by the Free Software Foundation; either version 2 of *
10 : * the License, or (at your option) any later version. *
11 : * *
12 : * This program is distributed in the hope that it will be useful, *
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 : * GNU General Public License for more details. *
16 : * *
17 : * You should have received a copy of the GNU General Public License*
18 : * along with this program; if not, contact: *
19 : * *
20 : * Free Software Foundation Voice: +1-617-542-5942 *
21 : * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
22 : * Boston, MA 02110-1301, USA gnu@gnu.org *
23 : * *
24 : \********************************************************************/
25 :
26 : #include <config.h>
27 :
28 : #include "gnc-prefs.h"
29 :
30 : #include <glib.h>
31 : #include <glib/gi18n.h>
32 : #include <stdlib.h>
33 : #include <stdint.h>
34 : #include <string.h>
35 :
36 : #include "AccountP.hpp"
37 : #include "Account.hpp"
38 : #include "Split.h"
39 : #include "Transaction.h"
40 : #include "TransactionP.hpp"
41 : #include "gnc-event.h"
42 : #include "gnc-glib-utils.h"
43 : #include "gnc-lot.h"
44 : #include "gnc-pricedb.h"
45 : #include "qofevent.h"
46 : #include "qofinstance-p.h"
47 : #include "gnc-features.h"
48 : #include "guid.hpp"
49 :
50 : #include <numeric>
51 : #include <map>
52 : #include <unordered_set>
53 : #include <algorithm>
54 :
55 : static QofLogModule log_module = GNC_MOD_ACCOUNT;
56 :
57 : /* The Canonical Account Separator. Pre-Initialized. */
58 : static gchar account_separator[8] = ".";
59 : static gunichar account_uc_separator = ':';
60 :
61 : static bool imap_convert_bayes_to_flat_run = false;
62 :
63 : /* Predefined KVP paths */
64 : static const std::string KEY_ASSOC_INCOME_ACCOUNT("ofx/associated-income-account");
65 : static const std::string KEY_RECONCILE_INFO("reconcile-info");
66 : static const std::string KEY_INCLUDE_CHILDREN("include-children");
67 : static const std::string KEY_POSTPONE("postpone");
68 : static const std::string KEY_LOT_MGMT("lot-mgmt");
69 : static const std::string KEY_ONLINE_ID("online_id");
70 : static const std::string KEY_IMP_APPEND_TEXT("import-append-text");
71 : static const std::string AB_KEY("hbci");
72 : static const std::string AB_ACCOUNT_ID("account-id");
73 : static const std::string AB_ACCOUNT_UID("account-uid");
74 : static const std::string AB_BANK_CODE("bank-code");
75 : static const std::string AB_TRANS_RETRIEVAL("trans-retrieval");
76 :
77 : static const std::string KEY_BALANCE_LIMIT("balance-limit");
78 : static const std::string KEY_BALANCE_HIGHER_LIMIT_VALUE("higher-value");
79 : static const std::string KEY_BALANCE_LOWER_LIMIT_VALUE("lower-value");
80 : static const std::string KEY_BALANCE_INCLUDE_SUB_ACCTS("inlude-sub-accts");
81 :
82 : using FinalProbabilityVec=std::vector<std::pair<std::string, int32_t>>;
83 : using ProbabilityVec=std::vector<std::pair<std::string, struct AccountProbability>>;
84 : using FlatKvpEntry=std::pair<std::string, KvpValue*>;
85 :
86 : enum
87 : {
88 : LAST_SIGNAL
89 : };
90 :
91 : enum
92 : {
93 : PROP_0,
94 : PROP_NAME, /* Table */
95 : PROP_FULL_NAME, /* Constructed */
96 : PROP_CODE, /* Table */
97 : PROP_DESCRIPTION, /* Table */
98 : PROP_COLOR, /* KVP */
99 : PROP_NOTES, /* KVP */
100 : PROP_TYPE, /* Table */
101 :
102 : // PROP_PARENT, /* Table, Not a property */
103 : PROP_COMMODITY, /* Table */
104 : PROP_COMMODITY_SCU, /* Table */
105 : PROP_NON_STD_SCU, /* Table */
106 : PROP_END_BALANCE, /* Constructed */
107 : PROP_END_NOCLOSING_BALANCE, /* Constructed */
108 : PROP_END_CLEARED_BALANCE, /* Constructed */
109 : PROP_END_RECONCILED_BALANCE, /* Constructed */
110 :
111 : PROP_TAX_RELATED, /* KVP */
112 : PROP_TAX_CODE, /* KVP */
113 : PROP_TAX_SOURCE, /* KVP */
114 : PROP_TAX_COPY_NUMBER, /* KVP */
115 :
116 : PROP_HIDDEN, /* Table slot exists, but in KVP in memory & xml */
117 : PROP_PLACEHOLDER, /* Table slot exists, but in KVP in memory & xml */
118 : PROP_AUTO_INTEREST,
119 : PROP_FILTER, /* KVP */
120 : PROP_SORT_ORDER, /* KVP */
121 : PROP_SORT_REVERSED,
122 :
123 : PROP_LOT_NEXT_ID, /* KVP */
124 : PROP_ONLINE_ACCOUNT, /* KVP */
125 : PROP_IMP_APPEND_TEXT, /* KVP */
126 : PROP_IS_OPENING_BALANCE, /* KVP */
127 : PROP_OFX_INCOME_ACCOUNT, /* KVP */
128 : PROP_AB_ACCOUNT_ID, /* KVP */
129 : PROP_AB_ACCOUNT_UID, /* KVP */
130 : PROP_AB_BANK_CODE, /* KVP */
131 : PROP_AB_TRANS_RETRIEVAL, /* KVP */
132 :
133 : PROP_RUNTIME_0,
134 : PROP_POLICY, /* Cached Value */
135 : PROP_MARK, /* Runtime Value */
136 : PROP_SORT_DIRTY, /* Runtime Value */
137 : PROP_BALANCE_DIRTY, /* Runtime Value */
138 : PROP_START_BALANCE, /* Runtime Value */
139 : PROP_START_NOCLOSING_BALANCE, /* Runtime Value */
140 : PROP_START_CLEARED_BALANCE, /* Runtime Value */
141 : PROP_START_RECONCILED_BALANCE, /* Runtime Value */
142 : };
143 :
144 : #define GET_PRIVATE(o) \
145 : ((AccountPrivate*)gnc_account_get_instance_private((Account*)o))
146 :
147 : /* This map contains a set of strings representing the different column types. */
148 : static const std::map<GNCAccountType, const char*> gnc_acct_debit_strs = {
149 : { ACCT_TYPE_NONE, N_("Funds In") },
150 : { ACCT_TYPE_BANK, N_("Deposit") },
151 : { ACCT_TYPE_CASH, N_("Receive") },
152 : { ACCT_TYPE_CREDIT, N_("Payment") },
153 : { ACCT_TYPE_ASSET, N_("Increase") },
154 : { ACCT_TYPE_LIABILITY, N_("Decrease") },
155 : { ACCT_TYPE_STOCK, N_("Buy") },
156 : { ACCT_TYPE_MUTUAL, N_("Buy") },
157 : { ACCT_TYPE_CURRENCY, N_("Buy") },
158 : { ACCT_TYPE_INCOME, N_("Charge") },
159 : { ACCT_TYPE_EXPENSE, N_("Expense") },
160 : { ACCT_TYPE_PAYABLE, N_("Payment") },
161 : { ACCT_TYPE_RECEIVABLE, N_("Invoice") },
162 : { ACCT_TYPE_TRADING, N_("Decrease") },
163 : { ACCT_TYPE_EQUITY, N_("Decrease") },
164 : };
165 : static const char* dflt_acct_debit_str = N_("Debit");
166 :
167 : /* This map contains a set of strings representing the different column types. */
168 : static const std::map<GNCAccountType, const char*> gnc_acct_credit_strs = {
169 : { ACCT_TYPE_NONE, N_("Funds Out") },
170 : { ACCT_TYPE_BANK, N_("Withdrawal") },
171 : { ACCT_TYPE_CASH, N_("Spend") },
172 : { ACCT_TYPE_CREDIT, N_("Charge") },
173 : { ACCT_TYPE_ASSET, N_("Decrease") },
174 : { ACCT_TYPE_LIABILITY, N_("Increase") },
175 : { ACCT_TYPE_STOCK, N_("Sell") },
176 : { ACCT_TYPE_MUTUAL, N_("Sell") },
177 : { ACCT_TYPE_CURRENCY, N_("Sell") },
178 : { ACCT_TYPE_INCOME, N_("Income") },
179 : { ACCT_TYPE_EXPENSE, N_("Rebate") },
180 : { ACCT_TYPE_PAYABLE, N_("Bill") },
181 : { ACCT_TYPE_RECEIVABLE, N_("Payment") },
182 : { ACCT_TYPE_TRADING, N_("Increase") },
183 : { ACCT_TYPE_EQUITY, N_("Increase") },
184 : };
185 : static const char* dflt_acct_credit_str = N_("Credit");
186 :
187 : /********************************************************************\
188 : * Because I can't use C++ for this project, doesn't mean that I *
189 : * can't pretend to! These functions perform actions on the *
190 : * account data structure, in order to encapsulate the knowledge *
191 : * of the internals of the Account in one file. *
192 : \********************************************************************/
193 :
194 : static void xaccAccountBringUpToDate (Account *acc);
195 :
196 :
197 : /********************************************************************\
198 : * gnc_get_account_separator *
199 : * returns the current account separator character *
200 : * *
201 : * Args: none *
202 : * Returns: account separator character *
203 : \*******************************************************************/
204 : const gchar *
205 23 : gnc_get_account_separator_string (void)
206 : {
207 23 : return account_separator;
208 : }
209 :
210 : gunichar
211 3 : gnc_get_account_separator (void)
212 : {
213 3 : return account_uc_separator;
214 : }
215 :
216 : void
217 36 : gnc_set_account_separator (const gchar *separator)
218 : {
219 : gunichar uc;
220 : gint count;
221 :
222 36 : uc = g_utf8_get_char_validated(separator, -1);
223 36 : if ((uc == (gunichar) - 2) || (uc == (gunichar) - 1) || g_unichar_isalnum(uc))
224 : {
225 1 : account_uc_separator = ':';
226 1 : strcpy(account_separator, ":");
227 1 : return;
228 : }
229 :
230 35 : account_uc_separator = uc;
231 35 : count = g_unichar_to_utf8(uc, account_separator);
232 35 : account_separator[count] = '\0';
233 : }
234 :
235 3 : gchar *gnc_account_name_violations_errmsg (const gchar *separator, GList* invalid_account_names)
236 : {
237 3 : gchar *message = nullptr;
238 :
239 3 : if ( !invalid_account_names )
240 2 : return nullptr;
241 :
242 1 : auto account_list {gnc_g_list_stringjoin (invalid_account_names, "\n")};
243 :
244 : /* Translators: The first %s will be the account separator character,
245 : the second %s is a list of account names.
246 : The resulting string will be displayed to the user if there are
247 : account names containing the separator character. */
248 1 : message = g_strdup_printf(
249 1 : _("The separator character \"%s\" is used in one or more account names.\n\n"
250 : "This will result in unexpected behaviour. "
251 : "Either change the account names or choose another separator character.\n\n"
252 : "Below you will find the list of invalid account names:\n"
253 : "%s"), separator, account_list );
254 1 : g_free ( account_list );
255 1 : return message;
256 : }
257 :
258 : struct ViolationData
259 : {
260 : GList *list;
261 : const gchar *separator;
262 : };
263 :
264 : static void
265 3 : check_acct_name (Account *acct, gpointer user_data)
266 : {
267 3 : auto cb {static_cast<ViolationData*>(user_data)};
268 3 : auto name {xaccAccountGetName (acct)};
269 3 : if (g_strstr_len (name, -1, cb->separator))
270 2 : cb->list = g_list_prepend (cb->list, g_strdup (name));
271 3 : }
272 :
273 4 : GList *gnc_account_list_name_violations (QofBook *book, const gchar *separator)
274 : {
275 4 : g_return_val_if_fail (separator != nullptr, nullptr);
276 2 : if (!book) return nullptr;
277 1 : ViolationData cb = { nullptr, separator };
278 1 : gnc_account_foreach_descendant (gnc_book_get_root_account (book),
279 : (AccountCb)check_acct_name, &cb);
280 1 : return cb.list;
281 : }
282 :
283 : /********************************************************************\
284 : \********************************************************************/
285 :
286 : static inline void mark_account (Account *acc);
287 : void
288 16767 : mark_account (Account *acc)
289 : {
290 16767 : qof_instance_set_dirty(&acc->inst);
291 16767 : }
292 :
293 : /********************************************************************\
294 : \********************************************************************/
295 :
296 : /* GObject Initialization */
297 1215262 : G_DEFINE_TYPE_WITH_PRIVATE(Account, gnc_account, QOF_TYPE_INSTANCE)
298 :
299 : static void
300 5073 : gnc_account_init(Account* acc)
301 : {
302 : AccountPrivate *priv;
303 :
304 5073 : priv = GET_PRIVATE(acc);
305 5073 : priv->parent = nullptr;
306 :
307 5073 : priv->accountName = qof_string_cache_insert("");
308 5073 : priv->accountCode = qof_string_cache_insert("");
309 5073 : priv->description = qof_string_cache_insert("");
310 :
311 5073 : priv->type = ACCT_TYPE_NONE;
312 :
313 5073 : priv->mark = 0;
314 :
315 5073 : priv->policy = xaccGetFIFOPolicy();
316 5073 : priv->lots = nullptr;
317 :
318 5073 : priv->commodity = nullptr;
319 5073 : priv->commodity_scu = 0;
320 5073 : priv->non_standard_scu = FALSE;
321 :
322 5073 : priv->balance = gnc_numeric_zero();
323 5073 : priv->noclosing_balance = gnc_numeric_zero();
324 5073 : priv->cleared_balance = gnc_numeric_zero();
325 5073 : priv->reconciled_balance = gnc_numeric_zero();
326 5073 : priv->starting_balance = gnc_numeric_zero();
327 5073 : priv->starting_noclosing_balance = gnc_numeric_zero();
328 5073 : priv->starting_cleared_balance = gnc_numeric_zero();
329 5073 : priv->starting_reconciled_balance = gnc_numeric_zero();
330 5073 : priv->balance_dirty = FALSE;
331 5073 : priv->has_stock_split = false;
332 :
333 5073 : new (&priv->children) AccountVec ();
334 5073 : new (&priv->splits) SplitsVec ();
335 5073 : priv->splits_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
336 5073 : priv->sort_dirty = FALSE;
337 5073 : }
338 :
339 : static void
340 2385 : gnc_account_dispose (GObject *acctp)
341 : {
342 2385 : G_OBJECT_CLASS(gnc_account_parent_class)->dispose(acctp);
343 2385 : }
344 :
345 : static void
346 2385 : gnc_account_finalize(GObject* acctp)
347 : {
348 2385 : G_OBJECT_CLASS(gnc_account_parent_class)->finalize(acctp);
349 2385 : }
350 :
351 : /* Note that g_value_set_object() refs the object, as does
352 : * g_object_get(). But g_object_get() only unrefs once when it disgorges
353 : * the object, leaving an unbalanced ref, which leaks. So instead of
354 : * using g_value_set_object(), use g_value_take_object() which doesn't
355 : * ref the object when used in get_property().
356 : */
357 : static void
358 597 : gnc_account_get_property (GObject *object,
359 : guint prop_id,
360 : GValue *value,
361 : GParamSpec *pspec)
362 : {
363 : Account *account;
364 : AccountPrivate *priv;
365 :
366 597 : g_return_if_fail(GNC_IS_ACCOUNT(object));
367 :
368 597 : account = GNC_ACCOUNT(object);
369 597 : priv = GET_PRIVATE(account);
370 597 : switch (prop_id)
371 : {
372 96 : case PROP_NAME:
373 96 : g_value_set_string(value, priv->accountName);
374 96 : break;
375 1 : case PROP_FULL_NAME:
376 1 : g_value_take_string(value, gnc_account_get_full_name(account));
377 1 : break;
378 83 : case PROP_CODE:
379 83 : g_value_set_string(value, priv->accountCode);
380 83 : break;
381 75 : case PROP_DESCRIPTION:
382 75 : g_value_set_string(value, priv->description);
383 75 : break;
384 1 : case PROP_COLOR:
385 1 : g_value_set_string(value, xaccAccountGetColor(account));
386 1 : break;
387 1 : case PROP_NOTES:
388 1 : g_value_set_string(value, xaccAccountGetNotes(account));
389 1 : break;
390 35 : case PROP_TYPE:
391 : // NEED TO BE CONVERTED TO A G_TYPE_ENUM
392 35 : g_value_set_int(value, priv->type);
393 35 : break;
394 41 : case PROP_COMMODITY:
395 41 : g_value_take_object(value, priv->commodity);
396 41 : break;
397 41 : case PROP_COMMODITY_SCU:
398 41 : g_value_set_int(value, priv->commodity_scu);
399 41 : break;
400 41 : case PROP_NON_STD_SCU:
401 41 : g_value_set_boolean(value, priv->non_standard_scu);
402 41 : break;
403 9 : case PROP_SORT_DIRTY:
404 9 : g_value_set_boolean(value, priv->sort_dirty);
405 9 : break;
406 9 : case PROP_BALANCE_DIRTY:
407 9 : g_value_set_boolean(value, priv->balance_dirty);
408 9 : break;
409 3 : case PROP_START_BALANCE:
410 3 : g_value_set_boxed(value, &priv->starting_balance);
411 3 : break;
412 0 : case PROP_START_NOCLOSING_BALANCE:
413 0 : g_value_set_boxed(value, &priv->starting_noclosing_balance);
414 0 : break;
415 1 : case PROP_START_CLEARED_BALANCE:
416 1 : g_value_set_boxed(value, &priv->starting_cleared_balance);
417 1 : break;
418 1 : case PROP_START_RECONCILED_BALANCE:
419 1 : g_value_set_boxed(value, &priv->starting_reconciled_balance);
420 1 : break;
421 3 : case PROP_END_BALANCE:
422 3 : g_value_set_boxed(value, &priv->balance);
423 3 : break;
424 0 : case PROP_END_NOCLOSING_BALANCE:
425 0 : g_value_set_boxed(value, &priv->noclosing_balance);
426 0 : break;
427 1 : case PROP_END_CLEARED_BALANCE:
428 1 : g_value_set_boxed(value, &priv->cleared_balance);
429 1 : break;
430 1 : case PROP_END_RECONCILED_BALANCE:
431 1 : g_value_set_boxed(value, &priv->reconciled_balance);
432 1 : break;
433 1 : case PROP_POLICY:
434 : /* MAKE THIS A BOXED VALUE */
435 1 : g_value_set_pointer(value, priv->policy);
436 1 : break;
437 1 : case PROP_MARK:
438 1 : g_value_set_int(value, priv->mark);
439 1 : break;
440 1 : case PROP_TAX_RELATED:
441 1 : g_value_set_boolean(value, xaccAccountGetTaxRelated(account));
442 1 : break;
443 1 : case PROP_TAX_CODE:
444 1 : g_value_set_string(value, xaccAccountGetTaxUSCode(account));
445 1 : break;
446 1 : case PROP_TAX_SOURCE:
447 1 : g_value_set_string(value,
448 : xaccAccountGetTaxUSPayerNameSource(account));
449 1 : break;
450 1 : case PROP_TAX_COPY_NUMBER:
451 1 : g_value_set_int64(value,
452 : xaccAccountGetTaxUSCopyNumber(account));
453 1 : break;
454 41 : case PROP_HIDDEN:
455 41 : g_value_set_boolean(value, xaccAccountGetHidden(account));
456 41 : break;
457 0 : case PROP_AUTO_INTEREST:
458 0 : g_value_set_boolean (value, xaccAccountGetAutoInterest (account));
459 0 : break;
460 1 : case PROP_IS_OPENING_BALANCE:
461 1 : g_value_set_boolean(value, xaccAccountGetIsOpeningBalance(account));
462 1 : break;
463 41 : case PROP_PLACEHOLDER:
464 41 : g_value_set_boolean(value, xaccAccountGetPlaceholder(account));
465 41 : break;
466 0 : case PROP_FILTER:
467 0 : g_value_set_string(value, xaccAccountGetFilter(account));
468 0 : break;
469 0 : case PROP_SORT_ORDER:
470 0 : g_value_set_string(value, xaccAccountGetSortOrder(account));
471 0 : break;
472 0 : case PROP_SORT_REVERSED:
473 0 : g_value_set_boolean(value, xaccAccountGetSortReversed(account));
474 0 : break;
475 56 : case PROP_LOT_NEXT_ID:
476 : /* Pre-set the value in case the frame is empty */
477 56 : g_value_set_int64 (value, 0);
478 224 : qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_LOT_MGMT, "next-id"});
479 56 : break;
480 2 : case PROP_ONLINE_ACCOUNT:
481 6 : qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
482 2 : break;
483 0 : case PROP_IMP_APPEND_TEXT:
484 0 : g_value_set_boolean(value, xaccAccountGetAppendText(account));
485 0 : break;
486 1 : case PROP_OFX_INCOME_ACCOUNT:
487 3 : qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
488 1 : break;
489 1 : case PROP_AB_ACCOUNT_ID:
490 4 : qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
491 1 : break;
492 1 : case PROP_AB_ACCOUNT_UID:
493 4 : qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
494 1 : break;
495 1 : case PROP_AB_BANK_CODE:
496 4 : qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
497 1 : break;
498 3 : case PROP_AB_TRANS_RETRIEVAL:
499 12 : qof_instance_get_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
500 3 : break;
501 0 : default:
502 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
503 0 : break;
504 : }
505 121 : }
506 :
507 : static void
508 70774 : gnc_account_set_property (GObject *object,
509 : guint prop_id,
510 : const GValue *value,
511 : GParamSpec *pspec)
512 : {
513 : Account *account;
514 : gnc_numeric *number;
515 70774 : g_return_if_fail(GNC_IS_ACCOUNT(object));
516 70774 : account = GNC_ACCOUNT(object);
517 70774 : if (prop_id < PROP_RUNTIME_0)
518 483 : g_assert (qof_instance_get_editlevel(account));
519 :
520 70774 : switch (prop_id)
521 : {
522 50 : case PROP_NAME:
523 50 : xaccAccountSetName(account, g_value_get_string(value));
524 50 : break;
525 46 : case PROP_CODE:
526 46 : xaccAccountSetCode(account, g_value_get_string(value));
527 46 : break;
528 42 : case PROP_DESCRIPTION:
529 42 : xaccAccountSetDescription(account, g_value_get_string(value));
530 42 : break;
531 0 : case PROP_COLOR:
532 0 : xaccAccountSetColor(account, g_value_get_string(value));
533 0 : break;
534 0 : case PROP_NOTES:
535 0 : xaccAccountSetNotes(account, g_value_get_string(value));
536 0 : break;
537 21 : case PROP_TYPE:
538 : // NEED TO BE CONVERTED TO A G_TYPE_ENUM
539 21 : xaccAccountSetType(account, static_cast<GNCAccountType>(g_value_get_int(value)));
540 21 : break;
541 29 : case PROP_COMMODITY:
542 29 : xaccAccountSetCommodity(account, static_cast<gnc_commodity*>(g_value_get_object(value)));
543 29 : break;
544 42 : case PROP_COMMODITY_SCU:
545 42 : xaccAccountSetCommoditySCU(account, g_value_get_int(value));
546 42 : break;
547 42 : case PROP_NON_STD_SCU:
548 42 : xaccAccountSetNonStdSCU(account, g_value_get_boolean(value));
549 42 : break;
550 35145 : case PROP_SORT_DIRTY:
551 35145 : gnc_account_set_sort_dirty(account);
552 35145 : break;
553 35145 : case PROP_BALANCE_DIRTY:
554 35145 : gnc_account_set_balance_dirty(account);
555 35145 : break;
556 1 : case PROP_START_BALANCE:
557 1 : number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
558 1 : gnc_account_set_start_balance(account, *number);
559 1 : break;
560 0 : case PROP_START_CLEARED_BALANCE:
561 0 : number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
562 0 : gnc_account_set_start_cleared_balance(account, *number);
563 0 : break;
564 0 : case PROP_START_RECONCILED_BALANCE:
565 0 : number = static_cast<gnc_numeric*>(g_value_get_boxed(value));
566 0 : gnc_account_set_start_reconciled_balance(account, *number);
567 0 : break;
568 0 : case PROP_POLICY:
569 0 : gnc_account_set_policy(account, static_cast<GNCPolicy*>(g_value_get_pointer(value)));
570 0 : break;
571 0 : case PROP_MARK:
572 0 : xaccAccountSetMark(account, g_value_get_int(value));
573 0 : break;
574 0 : case PROP_TAX_RELATED:
575 0 : xaccAccountSetTaxRelated(account, g_value_get_boolean(value));
576 0 : break;
577 0 : case PROP_TAX_CODE:
578 0 : xaccAccountSetTaxUSCode(account, g_value_get_string(value));
579 0 : break;
580 0 : case PROP_TAX_SOURCE:
581 0 : xaccAccountSetTaxUSPayerNameSource(account,
582 : g_value_get_string(value));
583 0 : break;
584 0 : case PROP_TAX_COPY_NUMBER:
585 0 : xaccAccountSetTaxUSCopyNumber(account,
586 : g_value_get_int64(value));
587 0 : break;
588 42 : case PROP_HIDDEN:
589 42 : xaccAccountSetHidden(account, g_value_get_boolean(value));
590 42 : break;
591 0 : case PROP_AUTO_INTEREST:
592 0 : xaccAccountSetAutoInterest (account, g_value_get_boolean (value));
593 0 : break;
594 0 : case PROP_IS_OPENING_BALANCE:
595 0 : xaccAccountSetIsOpeningBalance (account, g_value_get_boolean (value));
596 0 : break;
597 42 : case PROP_PLACEHOLDER:
598 42 : xaccAccountSetPlaceholder(account, g_value_get_boolean(value));
599 42 : break;
600 0 : case PROP_FILTER:
601 0 : xaccAccountSetFilter(account, g_value_get_string(value));
602 0 : break;
603 0 : case PROP_SORT_ORDER:
604 0 : xaccAccountSetSortOrder(account, g_value_get_string(value));
605 0 : break;
606 0 : case PROP_SORT_REVERSED:
607 0 : xaccAccountSetSortReversed(account, g_value_get_boolean(value));
608 0 : break;
609 56 : case PROP_LOT_NEXT_ID:
610 224 : qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_LOT_MGMT, "next-id"});
611 56 : break;
612 64 : case PROP_ONLINE_ACCOUNT:
613 192 : qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ONLINE_ID});
614 64 : break;
615 0 : case PROP_IMP_APPEND_TEXT:
616 0 : xaccAccountSetAppendText(account, g_value_get_boolean(value));
617 0 : break;
618 2 : case PROP_OFX_INCOME_ACCOUNT:
619 6 : qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {KEY_ASSOC_INCOME_ACCOUNT});
620 2 : break;
621 1 : case PROP_AB_ACCOUNT_ID:
622 4 : qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_ID});
623 1 : break;
624 1 : case PROP_AB_ACCOUNT_UID:
625 4 : qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_ACCOUNT_UID});
626 1 : break;
627 1 : case PROP_AB_BANK_CODE:
628 4 : qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_BANK_CODE});
629 1 : break;
630 2 : case PROP_AB_TRANS_RETRIEVAL:
631 8 : qof_instance_set_path_kvp (QOF_INSTANCE (account), value, {AB_KEY, AB_TRANS_RETRIEVAL});
632 2 : break;
633 0 : default:
634 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
635 0 : break;
636 : }
637 183 : }
638 :
639 : static void
640 68 : gnc_account_class_init (AccountClass *klass)
641 : {
642 68 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
643 :
644 68 : gobject_class->dispose = gnc_account_dispose;
645 68 : gobject_class->finalize = gnc_account_finalize;
646 68 : gobject_class->set_property = gnc_account_set_property;
647 68 : gobject_class->get_property = gnc_account_get_property;
648 :
649 : g_object_class_install_property
650 68 : (gobject_class,
651 : PROP_NAME,
652 : g_param_spec_string ("name",
653 : "Account Name",
654 : "The accountName is an arbitrary string "
655 : "assigned by the user. It is intended to "
656 : "a short, 5 to 30 character long string "
657 : "that is displayed by the GUI as the "
658 : "account mnemonic. Account names may be "
659 : "repeated. but no two accounts that share "
660 : "a parent may have the same name.",
661 : nullptr,
662 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
663 :
664 : g_object_class_install_property
665 68 : (gobject_class,
666 : PROP_FULL_NAME,
667 : g_param_spec_string ("fullname",
668 : "Full Account Name",
669 : "The name of the account concatenated with "
670 : "all its parent account names to indicate "
671 : "a unique account.",
672 : nullptr,
673 : static_cast<GParamFlags>(G_PARAM_READABLE)));
674 :
675 : g_object_class_install_property
676 68 : (gobject_class,
677 : PROP_CODE,
678 : g_param_spec_string ("code",
679 : "Account Code",
680 : "The account code is an arbitrary string "
681 : "assigned by the user. It is intended to "
682 : "be reporting code that is a synonym for "
683 : "the accountName.",
684 : nullptr,
685 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
686 :
687 : g_object_class_install_property
688 68 : (gobject_class,
689 : PROP_DESCRIPTION,
690 : g_param_spec_string ("description",
691 : "Account Description",
692 : "The account description is an arbitrary "
693 : "string assigned by the user. It is intended "
694 : "to be a longer, 1-5 sentence description of "
695 : "what this account is all about.",
696 : nullptr,
697 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
698 :
699 : g_object_class_install_property
700 68 : (gobject_class,
701 : PROP_COLOR,
702 : g_param_spec_string ("color",
703 : "Account Color",
704 : "The account color is a color string assigned "
705 : "by the user. It is intended to highlight the "
706 : "account based on the users wishes.",
707 : nullptr,
708 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
709 :
710 : g_object_class_install_property
711 68 : (gobject_class,
712 : PROP_NOTES,
713 : g_param_spec_string ("notes",
714 : "Account Notes",
715 : "The account notes is an arbitrary provided "
716 : "for the user to attach any other text that "
717 : "they would like to associate with the account.",
718 : nullptr,
719 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
720 :
721 : g_object_class_install_property
722 68 : (gobject_class,
723 : PROP_TYPE,
724 : g_param_spec_int ("type",
725 : "Account Type",
726 : "The account type, picked from the enumerated list "
727 : "that includes ACCT_TYPE_BANK, ACCT_TYPE_STOCK, "
728 : "ACCT_TYPE_CREDIT, ACCT_TYPE_INCOME, etc.",
729 : ACCT_TYPE_NONE,
730 : NUM_ACCOUNT_TYPES - 1,
731 : ACCT_TYPE_BANK,
732 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
733 :
734 : g_object_class_install_property
735 68 : (gobject_class,
736 : PROP_COMMODITY,
737 : g_param_spec_object ("commodity",
738 : "Commodity",
739 : "The commodity field denotes the kind of "
740 : "'stuff' stored in this account, whether "
741 : "it is USD, gold, stock, etc.",
742 : GNC_TYPE_COMMODITY,
743 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
744 :
745 : g_object_class_install_property
746 68 : (gobject_class,
747 : PROP_COMMODITY_SCU,
748 : g_param_spec_int ("commodity-scu",
749 : "Commodity SCU",
750 : "The smallest fraction of the commodity that is "
751 : "tracked. This number is used as the denominator "
752 : "value in 1/x, so a value of 100 says that the "
753 : "commodity can be divided into hundredths. E.G."
754 : "1 USD can be divided into 100 cents.",
755 : 0,
756 : G_MAXINT32,
757 : GNC_COMMODITY_MAX_FRACTION,
758 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
759 :
760 : g_object_class_install_property
761 68 : (gobject_class,
762 : PROP_NON_STD_SCU,
763 : g_param_spec_boolean ("non-std-scu",
764 : "Non-std SCU",
765 : "TRUE if the account SCU doesn't match "
766 : "the commodity SCU. This indicates a case "
767 : "where the two were accidentally set to "
768 : "mismatched values in older versions of "
769 : "GnuCash.",
770 : FALSE,
771 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
772 :
773 : g_object_class_install_property
774 68 : (gobject_class,
775 : PROP_SORT_DIRTY,
776 : g_param_spec_boolean("sort-dirty",
777 : "Sort Dirty",
778 : "TRUE if the splits in the account needs to be "
779 : "resorted. This flag is set by the accounts "
780 : "code for certain internal modifications, or "
781 : "when external code calls the engine to say a "
782 : "split has been modified in a way that may "
783 : "affect the sort order of the account. Note: "
784 : "This value can only be set to TRUE.",
785 : FALSE,
786 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
787 :
788 : g_object_class_install_property
789 68 : (gobject_class,
790 : PROP_BALANCE_DIRTY,
791 : g_param_spec_boolean("balance-dirty",
792 : "Balance Dirty",
793 : "TRUE if the running balances in the account "
794 : "needs to be recalculated. This flag is set "
795 : "by the accounts code for certain internal "
796 : "modifications, or when external code calls "
797 : "the engine to say a split has been modified. "
798 : "Note: This value can only be set to TRUE.",
799 : FALSE,
800 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
801 :
802 : g_object_class_install_property
803 68 : (gobject_class,
804 : PROP_START_BALANCE,
805 : g_param_spec_boxed("start-balance",
806 : "Starting Balance",
807 : "The starting balance for the account. This "
808 : "parameter is intended for use with backends that "
809 : "do not return the complete list of splits for an "
810 : "account, but rather return a partial list. In "
811 : "such a case, the backend will typically return "
812 : "all of the splits after some certain date, and "
813 : "the 'starting balance' will represent the "
814 : "summation of the splits up to that date.",
815 : GNC_TYPE_NUMERIC,
816 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
817 :
818 : g_object_class_install_property
819 68 : (gobject_class,
820 : PROP_START_NOCLOSING_BALANCE,
821 : g_param_spec_boxed("start-noclosing-balance",
822 : "Starting No-closing Balance",
823 : "The starting balance for the account, ignoring closing."
824 : "This parameter is intended for use with backends "
825 : "that do not return the complete list of splits "
826 : "for an account, but rather return a partial "
827 : "list. In such a case, the backend will "
828 : "typically return all of the splits after "
829 : "some certain date, and the 'starting noclosing "
830 : "balance' will represent the summation of the "
831 : "splits up to that date, ignoring closing splits.",
832 : GNC_TYPE_NUMERIC,
833 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
834 :
835 : g_object_class_install_property
836 68 : (gobject_class,
837 : PROP_START_CLEARED_BALANCE,
838 : g_param_spec_boxed("start-cleared-balance",
839 : "Starting Cleared Balance",
840 : "The starting cleared balance for the account. "
841 : "This parameter is intended for use with backends "
842 : "that do not return the complete list of splits "
843 : "for an account, but rather return a partial "
844 : "list. In such a case, the backend will "
845 : "typically return all of the splits after "
846 : "some certain date, and the 'starting cleared "
847 : "balance' will represent the summation of the "
848 : "splits up to that date.",
849 : GNC_TYPE_NUMERIC,
850 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
851 :
852 : g_object_class_install_property
853 68 : (gobject_class,
854 : PROP_START_RECONCILED_BALANCE,
855 : g_param_spec_boxed("start-reconciled-balance",
856 : "Starting Reconciled Balance",
857 : "The starting reconciled balance for the "
858 : "account. This parameter is intended for use "
859 : "with backends that do not return the complete "
860 : "list of splits for an account, but rather return "
861 : "a partial list. In such a case, the backend "
862 : "will typically return all of the splits after "
863 : "some certain date, and the 'starting reconciled "
864 : "balance' will represent the summation of the "
865 : "splits up to that date.",
866 : GNC_TYPE_NUMERIC,
867 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
868 :
869 : g_object_class_install_property
870 68 : (gobject_class,
871 : PROP_END_BALANCE,
872 : g_param_spec_boxed("end-balance",
873 : "Ending Account Balance",
874 : "This is the current ending balance for the "
875 : "account. It is computed from the sum of the "
876 : "starting balance and all splits in the account.",
877 : GNC_TYPE_NUMERIC,
878 : G_PARAM_READABLE));
879 :
880 : g_object_class_install_property
881 68 : (gobject_class,
882 : PROP_END_NOCLOSING_BALANCE,
883 : g_param_spec_boxed("end-noclosing-balance",
884 : "Ending Account Noclosing Balance",
885 : "This is the current ending no-closing balance for "
886 : "the account. It is computed from the sum of the "
887 : "starting balance and all cleared splits in the "
888 : "account.",
889 : GNC_TYPE_NUMERIC,
890 : G_PARAM_READABLE));
891 :
892 : g_object_class_install_property
893 68 : (gobject_class,
894 : PROP_END_CLEARED_BALANCE,
895 : g_param_spec_boxed("end-cleared-balance",
896 : "Ending Account Cleared Balance",
897 : "This is the current ending cleared balance for "
898 : "the account. It is computed from the sum of the "
899 : "starting balance and all cleared splits in the "
900 : "account.",
901 : GNC_TYPE_NUMERIC,
902 : G_PARAM_READABLE));
903 :
904 : g_object_class_install_property
905 68 : (gobject_class,
906 : PROP_END_RECONCILED_BALANCE,
907 : g_param_spec_boxed("end-reconciled-balance",
908 : "Ending Account Reconciled Balance",
909 : "This is the current ending reconciled balance "
910 : "for the account. It is computed from the sum of "
911 : "the starting balance and all reconciled splits "
912 : "in the account.",
913 : GNC_TYPE_NUMERIC,
914 : static_cast<GParamFlags>(G_PARAM_READABLE)));
915 :
916 : g_object_class_install_property
917 68 : (gobject_class,
918 : PROP_POLICY,
919 : g_param_spec_pointer ("policy",
920 : "Policy",
921 : "The account lots policy.",
922 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
923 :
924 : g_object_class_install_property
925 68 : (gobject_class,
926 : PROP_MARK,
927 : g_param_spec_int ("acct-mark",
928 : "Account Mark",
929 : "Ipsum Lorem",
930 : 0,
931 : G_MAXINT16,
932 : 0,
933 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
934 :
935 : g_object_class_install_property
936 68 : (gobject_class,
937 : PROP_TAX_RELATED,
938 : g_param_spec_boolean ("tax-related",
939 : "Tax Related",
940 : "Whether the account maps to an entry on an "
941 : "income tax document.",
942 : FALSE,
943 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
944 :
945 : g_object_class_install_property
946 68 : (gobject_class,
947 : PROP_IS_OPENING_BALANCE,
948 : g_param_spec_boolean ("opening-balance",
949 : "Opening Balance",
950 : "Whether the account holds opening balances",
951 : FALSE,
952 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
953 :
954 : g_object_class_install_property
955 68 : (gobject_class,
956 : PROP_TAX_CODE,
957 : g_param_spec_string ("tax-code",
958 : "Tax Code",
959 : "This is the code for mapping an account to a "
960 : "specific entry on a taxable document. In the "
961 : "United States it is used to transfer totals "
962 : "into tax preparation software.",
963 : nullptr,
964 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
965 :
966 : g_object_class_install_property
967 68 : (gobject_class,
968 : PROP_TAX_SOURCE,
969 : g_param_spec_string ("tax-source",
970 : "Tax Source",
971 : "This specifies where exported name comes from.",
972 : nullptr,
973 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
974 :
975 : g_object_class_install_property
976 68 : (gobject_class,
977 : PROP_TAX_COPY_NUMBER,
978 : g_param_spec_int64 ("tax-copy-number",
979 : "Tax Copy Number",
980 : "This specifies the copy number of the tax "
981 : "form/schedule.",
982 : (gint64)1,
983 : G_MAXINT64,
984 : (gint64)1,
985 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
986 :
987 : g_object_class_install_property
988 68 : (gobject_class,
989 : PROP_HIDDEN,
990 : g_param_spec_boolean ("hidden",
991 : "Hidden",
992 : "Whether the account should be hidden in the "
993 : "account tree.",
994 : FALSE,
995 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
996 :
997 : g_object_class_install_property
998 68 : (gobject_class,
999 : PROP_AUTO_INTEREST,
1000 : g_param_spec_boolean ("auto-interest-transfer",
1001 : "Auto Interest",
1002 : "Whether an interest transfer should be automatically "
1003 : "added before reconcile.",
1004 : FALSE,
1005 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1006 :
1007 : g_object_class_install_property
1008 68 : (gobject_class,
1009 : PROP_PLACEHOLDER,
1010 : g_param_spec_boolean ("placeholder",
1011 : "Placeholder",
1012 : "Whether the account is a placeholder account which does not "
1013 : "allow transactions to be created, edited or deleted.",
1014 : FALSE,
1015 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1016 :
1017 : g_object_class_install_property
1018 68 : (gobject_class,
1019 : PROP_FILTER,
1020 : g_param_spec_string ("filter",
1021 : "Account Filter",
1022 : "The account filter is a value saved to allow "
1023 : "filters to be recalled.",
1024 : nullptr,
1025 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1026 :
1027 : g_object_class_install_property
1028 68 : (gobject_class,
1029 : PROP_SORT_ORDER,
1030 : g_param_spec_string ("sort-order",
1031 : "Account Sort Order",
1032 : "The account sort order is a value saved to allow "
1033 : "the sort order to be recalled.",
1034 : nullptr,
1035 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1036 :
1037 : g_object_class_install_property
1038 68 : (gobject_class,
1039 : PROP_SORT_REVERSED,
1040 : g_param_spec_boolean ("sort-reversed",
1041 : "Account Sort Reversed",
1042 : "Parameter to store whether the sort order is reversed or not.",
1043 : FALSE,
1044 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1045 :
1046 : g_object_class_install_property
1047 68 : (gobject_class,
1048 : PROP_LOT_NEXT_ID,
1049 : g_param_spec_int64 ("lot-next-id",
1050 : "Lot Next ID",
1051 : "Tracks the next id to use in gnc_lot_make_default.",
1052 : (gint64)1,
1053 : G_MAXINT64,
1054 : (gint64)1,
1055 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1056 :
1057 : g_object_class_install_property
1058 68 : (gobject_class,
1059 : PROP_ONLINE_ACCOUNT,
1060 : g_param_spec_string ("online-id",
1061 : "Online Account ID",
1062 : "The online account which corresponds to this "
1063 : "account for OFX import",
1064 : nullptr,
1065 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1066 :
1067 : g_object_class_install_property
1068 68 : (gobject_class,
1069 : PROP_IMP_APPEND_TEXT,
1070 : g_param_spec_boolean ("import-append-text",
1071 : "Import Append Text",
1072 : "Saved state of Append checkbox for setting initial "
1073 : "value next time this account is imported.",
1074 : FALSE,
1075 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1076 :
1077 68 : g_object_class_install_property(
1078 : gobject_class,
1079 : PROP_OFX_INCOME_ACCOUNT,
1080 : g_param_spec_boxed("ofx-income-account",
1081 : "Associated income account",
1082 : "Used by the OFX importer.",
1083 : GNC_TYPE_GUID,
1084 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1085 :
1086 : g_object_class_install_property
1087 68 : (gobject_class,
1088 : PROP_AB_ACCOUNT_ID,
1089 : g_param_spec_string ("ab-account-id",
1090 : "AQBanking Account ID",
1091 : "The AqBanking account which corresponds to this "
1092 : "account for AQBanking import",
1093 : nullptr,
1094 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1095 : g_object_class_install_property
1096 68 : (gobject_class,
1097 : PROP_AB_BANK_CODE,
1098 : g_param_spec_string ("ab-bank-code",
1099 : "AQBanking Bank Code",
1100 : "The online account which corresponds to this "
1101 : "account for AQBanking import",
1102 : nullptr,
1103 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1104 :
1105 : g_object_class_install_property
1106 68 : (gobject_class,
1107 : PROP_AB_ACCOUNT_UID,
1108 : g_param_spec_int64 ("ab-account-uid",
1109 : "AQBanking Account UID",
1110 : "Tracks the next id to use in gnc_lot_make_default.",
1111 : (gint64)1,
1112 : G_MAXINT64,
1113 : (gint64)1,
1114 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1115 :
1116 : g_object_class_install_property
1117 68 : (gobject_class,
1118 : PROP_AB_TRANS_RETRIEVAL,
1119 : g_param_spec_boxed("ab-trans-retrieval",
1120 : "AQBanking Last Transaction Retrieval",
1121 : "The time of the last transaction retrieval for this "
1122 : "account.",
1123 : GNC_TYPE_TIME64,
1124 : static_cast<GParamFlags>(G_PARAM_READWRITE)));
1125 :
1126 68 : }
1127 :
1128 : static void
1129 5071 : xaccInitAccount (Account * acc, QofBook *book)
1130 : {
1131 5071 : ENTER ("book=%p\n", book);
1132 5071 : qof_instance_init_data (&acc->inst, GNC_ID_ACCOUNT, book);
1133 :
1134 5071 : LEAVE ("account=%p\n", acc);
1135 5071 : }
1136 :
1137 : /********************************************************************\
1138 : \********************************************************************/
1139 :
1140 : void
1141 1260 : gnc_account_foreach_split (const Account *acc, std::function<void(Split*)> func)
1142 : {
1143 1260 : gnc_account_foreach_split_between_dates (acc, {}, {}, false, func);
1144 1260 : }
1145 :
1146 : void
1147 0 : gnc_account_foreach_split_until_date (const Account *acc, time64 end_date,
1148 : std::function<void(Split*)> f)
1149 : {
1150 0 : gnc_account_foreach_split_between_dates (acc, {}, end_date, false, f);
1151 0 : }
1152 :
1153 :
1154 : Split*
1155 2988 : gnc_account_find_split (const Account *acc, std::function<bool(const Split*)> predicate,
1156 : bool reverse)
1157 : {
1158 2988 : if (!GNC_IS_ACCOUNT (acc))
1159 0 : return nullptr;
1160 :
1161 2988 : const auto& splits{GET_PRIVATE(acc)->splits};
1162 2988 : if (reverse)
1163 : {
1164 2940 : auto latest = std::find_if(splits.rbegin(), splits.rend(), predicate);
1165 2940 : return (latest == splits.rend()) ? nullptr : *latest;
1166 : }
1167 : else
1168 : {
1169 48 : auto earliest = std::find_if(splits.begin(), splits.end(), predicate);
1170 96 : return (earliest == splits.end()) ? nullptr : *earliest;
1171 : }
1172 : }
1173 :
1174 : /********************************************************************\
1175 : \********************************************************************/
1176 :
1177 : QofBook *
1178 1146 : gnc_account_get_book(const Account *account)
1179 : {
1180 1146 : if (!account) return nullptr;
1181 1146 : return qof_instance_get_book(QOF_INSTANCE(account));
1182 : }
1183 :
1184 : /********************************************************************\
1185 : \********************************************************************/
1186 :
1187 : static Account *
1188 2835 : gnc_coll_get_root_account (QofCollection *col)
1189 : {
1190 2835 : if (!col) return nullptr;
1191 2835 : return static_cast<Account*>(qof_collection_get_data (col));
1192 : }
1193 :
1194 : static void
1195 335 : gnc_coll_set_root_account (QofCollection *col, Account *root)
1196 : {
1197 : AccountPrivate *rpriv;
1198 : Account *old_root;
1199 335 : if (!col) return;
1200 :
1201 335 : old_root = gnc_coll_get_root_account (col);
1202 335 : if (old_root == root) return;
1203 :
1204 : /* If the new root is already linked into the tree somewhere, then
1205 : * remove it from its current position before adding it at the
1206 : * top. */
1207 334 : rpriv = GET_PRIVATE(root);
1208 334 : if (rpriv->parent)
1209 : {
1210 20 : xaccAccountBeginEdit(root);
1211 20 : gnc_account_remove_child(rpriv->parent, root);
1212 20 : xaccAccountCommitEdit(root);
1213 : }
1214 :
1215 334 : qof_collection_set_data (col, root);
1216 :
1217 334 : if (old_root)
1218 : {
1219 21 : xaccAccountBeginEdit (old_root);
1220 21 : xaccAccountDestroy (old_root);
1221 : }
1222 : }
1223 :
1224 : Account *
1225 2500 : gnc_book_get_root_account (QofBook *book)
1226 : {
1227 : QofCollection *col;
1228 : Account *root;
1229 :
1230 2500 : if (!book) return nullptr;
1231 2499 : col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
1232 2499 : root = gnc_coll_get_root_account (col);
1233 2499 : if (root == nullptr && !qof_book_shutting_down(book))
1234 208 : root = gnc_account_create_root(book);
1235 2499 : return root;
1236 : }
1237 :
1238 : void
1239 336 : gnc_book_set_root_account (QofBook *book, Account *root)
1240 : {
1241 : QofCollection *col;
1242 336 : if (!book) return;
1243 :
1244 336 : if (root && gnc_account_get_book(root) != book)
1245 : {
1246 1 : PERR ("cannot mix and match books freely!");
1247 1 : return;
1248 : }
1249 :
1250 335 : col = qof_book_get_collection (book, GNC_ID_ROOT_ACCOUNT);
1251 335 : gnc_coll_set_root_account (col, root);
1252 : }
1253 :
1254 : /********************************************************************\
1255 : \********************************************************************/
1256 :
1257 : Account *
1258 5070 : xaccMallocAccount (QofBook *book)
1259 : {
1260 : Account *acc;
1261 :
1262 5070 : g_return_val_if_fail (book, nullptr);
1263 :
1264 5070 : acc = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
1265 5070 : xaccInitAccount (acc, book);
1266 5070 : qof_event_gen (&acc->inst, QOF_EVENT_CREATE, nullptr);
1267 :
1268 5070 : return acc;
1269 : }
1270 :
1271 : Account *
1272 292 : gnc_account_create_root (QofBook *book)
1273 : {
1274 : Account *root;
1275 : AccountPrivate *rpriv;
1276 :
1277 292 : root = xaccMallocAccount(book);
1278 292 : rpriv = GET_PRIVATE(root);
1279 292 : xaccAccountBeginEdit(root);
1280 292 : rpriv->type = ACCT_TYPE_ROOT;
1281 292 : rpriv->accountName = qof_string_cache_replace(rpriv->accountName, "Root Account");
1282 292 : mark_account (root);
1283 292 : xaccAccountCommitEdit(root);
1284 292 : gnc_book_set_root_account(book, root);
1285 292 : return root;
1286 : }
1287 :
1288 : Account *
1289 3 : xaccCloneAccount(const Account *from, QofBook *book)
1290 : {
1291 : Account *ret;
1292 : AccountPrivate *from_priv, *priv;
1293 :
1294 3 : g_return_val_if_fail(GNC_IS_ACCOUNT(from), nullptr);
1295 2 : g_return_val_if_fail(QOF_IS_BOOK(book), nullptr);
1296 :
1297 1 : ENTER (" ");
1298 1 : ret = static_cast<Account*>(g_object_new (GNC_TYPE_ACCOUNT, nullptr));
1299 1 : g_return_val_if_fail (ret, nullptr);
1300 :
1301 1 : from_priv = GET_PRIVATE(from);
1302 1 : priv = GET_PRIVATE(ret);
1303 1 : xaccInitAccount (ret, book);
1304 :
1305 : /* Do not Begin/CommitEdit() here; give the caller
1306 : * a chance to fix things up, and let them do it.
1307 : * Also let caller issue the generate_event (EVENT_CREATE) */
1308 1 : priv->type = from_priv->type;
1309 :
1310 1 : priv->accountName = qof_string_cache_replace(priv->accountName, from_priv->accountName);
1311 1 : priv->accountCode = qof_string_cache_replace(priv->accountCode, from_priv->accountCode);
1312 1 : priv->description = qof_string_cache_replace(priv->description, from_priv->description);
1313 :
1314 1 : qof_instance_copy_kvp (QOF_INSTANCE (ret), QOF_INSTANCE (from));
1315 :
1316 : /* The new book should contain a commodity that matches
1317 : * the one in the old book. Find it, use it. */
1318 1 : priv->commodity = gnc_commodity_obtain_twin(from_priv->commodity, book);
1319 1 : gnc_commodity_increment_usage_count(priv->commodity);
1320 :
1321 1 : priv->commodity_scu = from_priv->commodity_scu;
1322 1 : priv->non_standard_scu = from_priv->non_standard_scu;
1323 :
1324 1 : qof_instance_set_dirty(&ret->inst);
1325 1 : LEAVE (" ");
1326 1 : return ret;
1327 : }
1328 :
1329 : /********************************************************************\
1330 : \********************************************************************/
1331 :
1332 : static void
1333 358 : xaccFreeOneChildAccount (Account *acc)
1334 : {
1335 : /* FIXME: this code is kind of hacky. actually, all this code
1336 : * seems to assume that the account edit levels are all 1. */
1337 358 : if (qof_instance_get_editlevel(acc) == 0)
1338 337 : xaccAccountBeginEdit(acc);
1339 358 : xaccAccountDestroy(acc);
1340 358 : }
1341 :
1342 : static void
1343 2229 : xaccFreeAccountChildren (Account *acc)
1344 : {
1345 2229 : auto priv{GET_PRIVATE(acc)};
1346 : /* Copy the list since it will be modified */
1347 2229 : auto children = priv->children;
1348 2229 : std::for_each (children.begin(), children.end(), xaccFreeOneChildAccount);
1349 :
1350 : /* The foreach should have removed all the children already. */
1351 2229 : priv->children.clear();
1352 2229 : }
1353 :
1354 : /* The xaccFreeAccount() routine releases memory associated with the
1355 : * account. It should never be called directly from user code;
1356 : * instead, the xaccAccountDestroy() routine should be used (because
1357 : * xaccAccountDestroy() has the correct commit semantics). */
1358 : static void
1359 2228 : xaccFreeAccount (Account *acc)
1360 : {
1361 : AccountPrivate *priv;
1362 : GList *lp;
1363 :
1364 2228 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
1365 :
1366 2228 : priv = GET_PRIVATE(acc);
1367 2228 : qof_event_gen (&acc->inst, QOF_EVENT_DESTROY, nullptr);
1368 :
1369 : /* Otherwise the lists below get munged while we're iterating
1370 : * them, possibly crashing.
1371 : */
1372 2228 : if (!qof_instance_get_destroying (acc))
1373 1 : qof_instance_set_destroying(acc, TRUE);
1374 :
1375 2228 : if (!priv->children.empty())
1376 : {
1377 1 : PERR (" instead of calling xaccFreeAccount(), please call\n"
1378 : " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1379 :
1380 : /* First, recursively free children, also frees list */
1381 1 : xaccFreeAccountChildren(acc);
1382 : }
1383 :
1384 : /* remove lots -- although these should be gone by now. */
1385 2228 : if (priv->lots)
1386 : {
1387 1 : PERR (" instead of calling xaccFreeAccount(), please call\n"
1388 : " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1389 :
1390 4 : for (lp = priv->lots; lp; lp = lp->next)
1391 : {
1392 3 : GNCLot *lot = static_cast<GNCLot*>(lp->data);
1393 3 : gnc_lot_destroy (lot);
1394 : }
1395 1 : g_list_free (priv->lots);
1396 1 : priv->lots = nullptr;
1397 : }
1398 :
1399 : /* Next, clean up the splits */
1400 : /* NB there shouldn't be any splits by now ... they should
1401 : * have been all been freed by CommitEdit(). We can remove this
1402 : * check once we know the warning isn't occurring any more. */
1403 2228 : if (!priv->splits.empty())
1404 : {
1405 2 : PERR (" instead of calling xaccFreeAccount(), please call\n"
1406 : " xaccAccountBeginEdit(); xaccAccountDestroy();\n");
1407 :
1408 2 : qof_instance_reset_editlevel(acc);
1409 :
1410 10 : for (auto s : priv->splits)
1411 : {
1412 6 : g_assert(xaccSplitGetAccount(s) == acc);
1413 6 : xaccSplitDestroy (s);
1414 : }
1415 : /* Nothing here (or in xaccAccountCommitEdit) nullptrs priv->splits, so this asserts every time.
1416 : g_assert(priv->splits == nullptr);
1417 : */
1418 : }
1419 :
1420 2228 : qof_string_cache_remove(priv->accountName);
1421 2228 : qof_string_cache_remove(priv->accountCode);
1422 2228 : qof_string_cache_remove(priv->description);
1423 2228 : priv->accountName = priv->accountCode = priv->description = nullptr;
1424 :
1425 : /* zero out values, just in case stray
1426 : * pointers are pointing here. */
1427 :
1428 2228 : priv->last_num = nullptr;
1429 2228 : priv->tax_us_code = nullptr;
1430 2228 : priv->tax_us_pns = nullptr;
1431 2228 : priv->color = nullptr;
1432 2228 : priv->sort_order = nullptr;
1433 2228 : priv->notes = nullptr;
1434 2228 : priv->filter = nullptr;
1435 :
1436 2228 : priv->parent = nullptr;
1437 :
1438 2228 : priv->balance = gnc_numeric_zero();
1439 2228 : priv->noclosing_balance = gnc_numeric_zero();
1440 2228 : priv->cleared_balance = gnc_numeric_zero();
1441 2228 : priv->reconciled_balance = gnc_numeric_zero();
1442 :
1443 2228 : priv->type = ACCT_TYPE_NONE;
1444 2228 : gnc_commodity_decrement_usage_count(priv->commodity);
1445 2228 : priv->commodity = nullptr;
1446 :
1447 2228 : priv->balance_dirty = FALSE;
1448 2228 : priv->has_stock_split = false;
1449 2228 : priv->sort_dirty = FALSE;
1450 2228 : priv->splits.~SplitsVec();
1451 2228 : priv->children.~AccountVec();
1452 2228 : g_hash_table_destroy (priv->splits_hash);
1453 :
1454 : /* qof_instance_release (&acc->inst); */
1455 2228 : g_object_unref(acc);
1456 : }
1457 :
1458 : /********************************************************************\
1459 : * transactional routines
1460 : \********************************************************************/
1461 :
1462 : void
1463 34811 : xaccAccountBeginEdit (Account *acc)
1464 : {
1465 34811 : g_return_if_fail(acc);
1466 34811 : qof_begin_edit(&acc->inst);
1467 : }
1468 :
1469 12592 : static void on_done(QofInstance *inst)
1470 : {
1471 : /* old event style */
1472 12592 : qof_event_gen (inst, QOF_EVENT_MODIFY, nullptr);
1473 12592 : }
1474 :
1475 0 : static void on_err (QofInstance *inst, QofBackendError errcode)
1476 : {
1477 0 : PERR("commit error: %d", errcode);
1478 0 : gnc_engine_signal_commit_error( errcode );
1479 0 : }
1480 :
1481 2227 : static void acc_free (QofInstance *inst)
1482 : {
1483 : AccountPrivate *priv;
1484 2227 : Account *acc = (Account *) inst;
1485 :
1486 2227 : priv = GET_PRIVATE(acc);
1487 2227 : if (priv->parent)
1488 1803 : gnc_account_remove_child(priv->parent, acc);
1489 2227 : xaccFreeAccount(acc);
1490 2227 : }
1491 :
1492 : static void
1493 78 : destroy_pending_splits_for_account(QofInstance *ent, gpointer acc)
1494 : {
1495 78 : Transaction *trans = (Transaction *) ent;
1496 : Split *split;
1497 :
1498 78 : if (xaccTransIsOpen(trans))
1499 29 : while ((split = xaccTransFindSplitByAccount(trans, static_cast<Account*>(acc))))
1500 10 : xaccSplitDestroy(split);
1501 78 : }
1502 :
1503 : void
1504 34086 : xaccAccountCommitEdit (Account *acc)
1505 : {
1506 : AccountPrivate *priv;
1507 : QofBook *book;
1508 :
1509 34086 : g_return_if_fail(acc);
1510 34086 : if (!qof_commit_edit(&acc->inst)) return;
1511 :
1512 : /* If marked for deletion, get rid of subaccounts first,
1513 : * and then the splits ... */
1514 14819 : priv = GET_PRIVATE(acc);
1515 14819 : if (qof_instance_get_destroying(acc))
1516 : {
1517 : QofCollection *col;
1518 :
1519 2227 : qof_instance_increase_editlevel(acc);
1520 :
1521 : /* First, recursively free children */
1522 2227 : xaccFreeAccountChildren(acc);
1523 :
1524 2227 : PINFO ("freeing splits for account %p (%s)",
1525 : acc, priv->accountName ? priv->accountName : "(null)");
1526 :
1527 2227 : book = qof_instance_get_book(acc);
1528 :
1529 : /* If book is shutting down, just clear the split list. The splits
1530 : themselves will be destroyed by the transaction code */
1531 2227 : if (!qof_book_shutting_down(book))
1532 : {
1533 : // We need to delete in reverse order so that the vector's iterators aren't invalidated.
1534 516 : for_each(priv->splits.rbegin(), priv->splits.rend(), [](Split *s) {
1535 3 : xaccSplitDestroy (s); });
1536 : }
1537 : else
1538 : {
1539 1711 : priv->splits.clear();
1540 1711 : g_hash_table_remove_all (priv->splits_hash);
1541 : }
1542 :
1543 : /* It turns out there's a case where this assertion does not hold:
1544 : When the user tries to delete an Imbalance account, while also
1545 : deleting all the splits in it. The splits will just get
1546 : recreated and put right back into the same account!
1547 :
1548 : g_assert(priv->splits == nullptr || qof_book_shutting_down(acc->inst.book));
1549 : */
1550 :
1551 2227 : if (!qof_book_shutting_down(book))
1552 : {
1553 516 : col = qof_book_get_collection(book, GNC_ID_TRANS);
1554 516 : qof_collection_foreach(col, destroy_pending_splits_for_account, acc);
1555 :
1556 : /* the lots should be empty by now */
1557 519 : for (auto lp = priv->lots; lp; lp = lp->next)
1558 : {
1559 3 : GNCLot *lot = static_cast<GNCLot*>(lp->data);
1560 3 : gnc_lot_destroy (lot);
1561 : }
1562 : }
1563 2227 : g_list_free(priv->lots);
1564 2227 : priv->lots = nullptr;
1565 :
1566 2227 : qof_instance_set_dirty(&acc->inst);
1567 2227 : qof_instance_decrease_editlevel(acc);
1568 : }
1569 : else
1570 : {
1571 12592 : xaccAccountBringUpToDate(acc);
1572 : }
1573 :
1574 14819 : qof_commit_edit_part2(&acc->inst, on_err, on_done, acc_free);
1575 : }
1576 :
1577 : void
1578 2282 : xaccAccountDestroy (Account *acc)
1579 : {
1580 2282 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
1581 :
1582 2282 : qof_instance_set_destroying(acc, TRUE);
1583 :
1584 2282 : xaccAccountCommitEdit (acc);
1585 : }
1586 :
1587 : void
1588 0 : xaccAccountDestroyAllTransactions(Account *acc)
1589 : {
1590 0 : auto priv = GET_PRIVATE(acc);
1591 0 : std::vector<Transaction*> transactions;
1592 0 : transactions.reserve(priv->splits.size());
1593 0 : std::transform(priv->splits.begin(), priv->splits.end(),
1594 : back_inserter(transactions),
1595 0 : [](auto split) { return split->parent; });
1596 0 : std::stable_sort(transactions.begin(), transactions.end());
1597 0 : transactions.erase(std::unique(transactions.begin(), transactions.end()),
1598 0 : transactions.end());
1599 0 : qof_event_suspend();
1600 0 : std::for_each(transactions.rbegin(), transactions.rend(),
1601 0 : [](auto trans) { xaccTransDestroy (trans); });
1602 0 : qof_event_resume();
1603 0 : }
1604 :
1605 : /********************************************************************\
1606 : \********************************************************************/
1607 :
1608 : static gboolean
1609 42 : xaccAcctChildrenEqual(const AccountVec& na,
1610 : const AccountVec& nb,
1611 : gboolean check_guids)
1612 : {
1613 42 : if (na.size() != nb.size())
1614 : {
1615 0 : PINFO ("Accounts have different numbers of children");
1616 0 : return (FALSE);
1617 : }
1618 :
1619 99 : for (auto aa : na)
1620 : {
1621 15 : auto it_b = std::find_if (nb.begin(), nb.end(), [aa](auto ab) -> bool
1622 : {
1623 28 : if (!aa) return (!ab);
1624 28 : if (!ab) return false;
1625 28 : auto code_a{GET_PRIVATE(aa)->accountCode};
1626 28 : auto code_b{GET_PRIVATE(ab)->accountCode};
1627 28 : if ((code_a && *code_a) || (code_b && *code_b)) return !g_strcmp0 (code_a, code_b);
1628 24 : return !g_strcmp0 (GET_PRIVATE(aa)->accountName, GET_PRIVATE(ab)->accountName);
1629 : });
1630 :
1631 30 : if (it_b == nb.end())
1632 : {
1633 0 : PINFO ("Unable to find matching child account.");
1634 0 : return FALSE;
1635 : }
1636 15 : else if (auto ab = *it_b; !xaccAccountEqual(aa, ab, check_guids))
1637 : {
1638 : char sa[GUID_ENCODING_LENGTH + 1];
1639 : char sb[GUID_ENCODING_LENGTH + 1];
1640 :
1641 0 : guid_to_string_buff (xaccAccountGetGUID (aa), sa);
1642 0 : guid_to_string_buff (xaccAccountGetGUID (ab), sb);
1643 :
1644 0 : PWARN ("accounts %s and %s differ", sa, sb);
1645 :
1646 0 : return(FALSE);
1647 : }
1648 : }
1649 :
1650 42 : return(TRUE);
1651 : }
1652 :
1653 : gboolean
1654 43 : xaccAccountEqual(const Account *aa, const Account *ab, gboolean check_guids)
1655 : {
1656 : AccountPrivate *priv_aa, *priv_ab;
1657 :
1658 43 : if (!aa && !ab) return TRUE;
1659 :
1660 42 : g_return_val_if_fail(GNC_IS_ACCOUNT(aa), FALSE);
1661 42 : g_return_val_if_fail(GNC_IS_ACCOUNT(ab), FALSE);
1662 :
1663 42 : priv_aa = GET_PRIVATE(aa);
1664 42 : priv_ab = GET_PRIVATE(ab);
1665 42 : if (priv_aa->type != priv_ab->type)
1666 : {
1667 0 : PWARN ("types differ: %d vs %d", priv_aa->type, priv_ab->type);
1668 0 : return FALSE;
1669 : }
1670 :
1671 42 : if (g_strcmp0(priv_aa->accountName, priv_ab->accountName) != 0)
1672 : {
1673 0 : PWARN ("names differ: %s vs %s", priv_aa->accountName, priv_ab->accountName);
1674 0 : return FALSE;
1675 : }
1676 :
1677 42 : if (g_strcmp0(priv_aa->accountCode, priv_ab->accountCode) != 0)
1678 : {
1679 0 : PWARN ("codes differ: %s vs %s", priv_aa->accountCode, priv_ab->accountCode);
1680 0 : return FALSE;
1681 : }
1682 :
1683 42 : if (g_strcmp0(priv_aa->description, priv_ab->description) != 0)
1684 : {
1685 0 : PWARN ("descriptions differ: %s vs %s", priv_aa->description, priv_ab->description);
1686 0 : return FALSE;
1687 : }
1688 :
1689 42 : if (!gnc_commodity_equal(priv_aa->commodity, priv_ab->commodity))
1690 : {
1691 0 : PWARN ("commodities differ");
1692 0 : return FALSE;
1693 : }
1694 :
1695 42 : if (check_guids)
1696 : {
1697 24 : if (qof_instance_guid_compare(aa, ab) != 0)
1698 : {
1699 0 : PWARN ("GUIDs differ");
1700 0 : return FALSE;
1701 : }
1702 : }
1703 :
1704 42 : if (qof_instance_compare_kvp (QOF_INSTANCE (aa), QOF_INSTANCE (ab)) != 0)
1705 : {
1706 : char *frame_a;
1707 : char *frame_b;
1708 :
1709 0 : frame_a = qof_instance_kvp_as_string (QOF_INSTANCE (aa));
1710 0 : frame_b = qof_instance_kvp_as_string (QOF_INSTANCE (ab));
1711 :
1712 0 : PWARN ("kvp frames differ:\n%s\n\nvs\n\n%s", frame_a, frame_b);
1713 :
1714 0 : g_free (frame_a);
1715 0 : g_free (frame_b);
1716 :
1717 0 : return FALSE;
1718 : }
1719 :
1720 42 : if (!gnc_numeric_equal(priv_aa->starting_balance, priv_ab->starting_balance))
1721 : {
1722 : char *str_a;
1723 : char *str_b;
1724 :
1725 0 : str_a = gnc_numeric_to_string(priv_aa->starting_balance);
1726 0 : str_b = gnc_numeric_to_string(priv_ab->starting_balance);
1727 :
1728 0 : PWARN ("starting balances differ: %s vs %s", str_a, str_b);
1729 :
1730 0 : g_free (str_a);
1731 0 : g_free (str_b);
1732 :
1733 0 : return FALSE;
1734 : }
1735 :
1736 42 : if (!gnc_numeric_equal(priv_aa->starting_noclosing_balance,
1737 : priv_ab->starting_noclosing_balance))
1738 : {
1739 : char *str_a;
1740 : char *str_b;
1741 :
1742 0 : str_a = gnc_numeric_to_string(priv_aa->starting_noclosing_balance);
1743 0 : str_b = gnc_numeric_to_string(priv_ab->starting_noclosing_balance);
1744 :
1745 0 : PWARN ("starting noclosing balances differ: %s vs %s", str_a, str_b);
1746 :
1747 0 : g_free (str_a);
1748 0 : g_free (str_b);
1749 :
1750 0 : return FALSE;
1751 : }
1752 42 : if (!gnc_numeric_equal(priv_aa->starting_cleared_balance,
1753 : priv_ab->starting_cleared_balance))
1754 : {
1755 : char *str_a;
1756 : char *str_b;
1757 :
1758 0 : str_a = gnc_numeric_to_string(priv_aa->starting_cleared_balance);
1759 0 : str_b = gnc_numeric_to_string(priv_ab->starting_cleared_balance);
1760 :
1761 0 : PWARN ("starting cleared balances differ: %s vs %s", str_a, str_b);
1762 :
1763 0 : g_free (str_a);
1764 0 : g_free (str_b);
1765 :
1766 0 : return FALSE;
1767 : }
1768 :
1769 42 : if (!gnc_numeric_equal(priv_aa->starting_reconciled_balance,
1770 : priv_ab->starting_reconciled_balance))
1771 : {
1772 : char *str_a;
1773 : char *str_b;
1774 :
1775 0 : str_a = gnc_numeric_to_string(priv_aa->starting_reconciled_balance);
1776 0 : str_b = gnc_numeric_to_string(priv_ab->starting_reconciled_balance);
1777 :
1778 0 : PWARN ("starting reconciled balances differ: %s vs %s", str_a, str_b);
1779 :
1780 0 : g_free (str_a);
1781 0 : g_free (str_b);
1782 :
1783 0 : return FALSE;
1784 : }
1785 :
1786 42 : if (!gnc_numeric_equal(priv_aa->balance, priv_ab->balance))
1787 : {
1788 : char *str_a;
1789 : char *str_b;
1790 :
1791 0 : str_a = gnc_numeric_to_string(priv_aa->balance);
1792 0 : str_b = gnc_numeric_to_string(priv_ab->balance);
1793 :
1794 0 : PWARN ("balances differ: %s vs %s", str_a, str_b);
1795 :
1796 0 : g_free (str_a);
1797 0 : g_free (str_b);
1798 :
1799 0 : return FALSE;
1800 : }
1801 :
1802 42 : if (!gnc_numeric_equal(priv_aa->noclosing_balance, priv_ab->noclosing_balance))
1803 : {
1804 : char *str_a;
1805 : char *str_b;
1806 :
1807 0 : str_a = gnc_numeric_to_string(priv_aa->noclosing_balance);
1808 0 : str_b = gnc_numeric_to_string(priv_ab->noclosing_balance);
1809 :
1810 0 : PWARN ("noclosing balances differ: %s vs %s", str_a, str_b);
1811 :
1812 0 : g_free (str_a);
1813 0 : g_free (str_b);
1814 :
1815 0 : return FALSE;
1816 : }
1817 42 : if (!gnc_numeric_equal(priv_aa->cleared_balance, priv_ab->cleared_balance))
1818 : {
1819 : char *str_a;
1820 : char *str_b;
1821 :
1822 0 : str_a = gnc_numeric_to_string(priv_aa->cleared_balance);
1823 0 : str_b = gnc_numeric_to_string(priv_ab->cleared_balance);
1824 :
1825 0 : PWARN ("cleared balances differ: %s vs %s", str_a, str_b);
1826 :
1827 0 : g_free (str_a);
1828 0 : g_free (str_b);
1829 :
1830 0 : return FALSE;
1831 : }
1832 :
1833 42 : if (!gnc_numeric_equal(priv_aa->reconciled_balance, priv_ab->reconciled_balance))
1834 : {
1835 : char *str_a;
1836 : char *str_b;
1837 :
1838 0 : str_a = gnc_numeric_to_string(priv_aa->reconciled_balance);
1839 0 : str_b = gnc_numeric_to_string(priv_ab->reconciled_balance);
1840 :
1841 0 : PWARN ("reconciled balances differ: %s vs %s", str_a, str_b);
1842 :
1843 0 : g_free (str_a);
1844 0 : g_free (str_b);
1845 :
1846 0 : return FALSE;
1847 : }
1848 :
1849 : /* no parent; always compare downwards. */
1850 :
1851 42 : if (!std::equal (priv_aa->splits.begin(), priv_aa->splits.end(),
1852 : priv_ab->splits.begin(), priv_ab->splits.end(),
1853 15 : [check_guids](auto sa, auto sb)
1854 15 : { return xaccSplitEqual(sa, sb, check_guids, true, false); }))
1855 : {
1856 0 : PWARN ("splits differ");
1857 0 : return false;
1858 : }
1859 :
1860 42 : if (!xaccAcctChildrenEqual(priv_aa->children, priv_ab->children, check_guids))
1861 : {
1862 0 : PWARN ("children differ");
1863 0 : return FALSE;
1864 : }
1865 :
1866 42 : return(TRUE);
1867 : }
1868 :
1869 : /********************************************************************\
1870 : \********************************************************************/
1871 : void
1872 35145 : gnc_account_set_sort_dirty (Account *acc)
1873 : {
1874 : AccountPrivate *priv;
1875 :
1876 35145 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
1877 :
1878 35145 : if (qof_instance_get_destroying(acc))
1879 0 : return;
1880 :
1881 35145 : priv = GET_PRIVATE(acc);
1882 35145 : priv->sort_dirty = TRUE;
1883 : }
1884 :
1885 : void
1886 35145 : gnc_account_set_balance_dirty (Account *acc)
1887 : {
1888 : AccountPrivate *priv;
1889 :
1890 35145 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
1891 :
1892 35145 : if (qof_instance_get_destroying(acc))
1893 0 : return;
1894 :
1895 35145 : priv = GET_PRIVATE(acc);
1896 35145 : priv->balance_dirty = TRUE;
1897 : }
1898 :
1899 0 : void gnc_account_set_defer_bal_computation (Account *acc, gboolean defer)
1900 : {
1901 : AccountPrivate *priv;
1902 :
1903 0 : g_return_if_fail (GNC_IS_ACCOUNT (acc));
1904 :
1905 0 : if (qof_instance_get_destroying (acc))
1906 0 : return;
1907 :
1908 0 : priv = GET_PRIVATE (acc);
1909 0 : priv->defer_bal_computation = defer;
1910 : }
1911 :
1912 0 : gboolean gnc_account_get_defer_bal_computation (Account *acc)
1913 : {
1914 : AccountPrivate *priv;
1915 0 : if (!acc)
1916 0 : return false;
1917 0 : priv = GET_PRIVATE (acc);
1918 0 : return priv->defer_bal_computation;
1919 : }
1920 :
1921 :
1922 : /********************************************************************\
1923 : \********************************************************************/
1924 :
1925 538778 : static bool split_cmp_less (const Split* a, const Split* b)
1926 : {
1927 538778 : return xaccSplitOrder (a, b) < 0;
1928 : }
1929 :
1930 : gboolean
1931 7377 : gnc_account_insert_split (Account *acc, Split *s)
1932 : {
1933 : AccountPrivate *priv;
1934 :
1935 7377 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
1936 7376 : g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
1937 :
1938 7375 : priv = GET_PRIVATE(acc);
1939 7375 : if (!g_hash_table_add (priv->splits_hash, s))
1940 2 : return false;
1941 :
1942 7373 : priv->splits.push_back (s);
1943 :
1944 7373 : if (qof_instance_get_editlevel(acc) == 0)
1945 5436 : std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
1946 : else
1947 1937 : priv->sort_dirty = true;
1948 :
1949 : //FIXME: find better event
1950 7373 : qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
1951 : /* Also send an event based on the account */
1952 7373 : qof_event_gen(&acc->inst, GNC_EVENT_ITEM_ADDED, s);
1953 :
1954 7373 : priv->balance_dirty = TRUE;
1955 : // DRH: Should the below be added? It is present in the delete path.
1956 : // xaccAccountRecomputeBalance(acc);
1957 7373 : return TRUE;
1958 : }
1959 :
1960 : gboolean
1961 10003 : gnc_account_remove_split (Account *acc, Split *s)
1962 : {
1963 : AccountPrivate *priv;
1964 :
1965 10003 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
1966 10003 : g_return_val_if_fail(GNC_IS_SPLIT(s), FALSE);
1967 :
1968 10003 : priv = GET_PRIVATE(acc);
1969 :
1970 10003 : if (!g_hash_table_remove (priv->splits_hash, s))
1971 4942 : return false;
1972 :
1973 : // shortcut pruning the last element. this is the most common
1974 : // remove_split operation during UI or book shutdown.
1975 5061 : if (s == priv->splits.back())
1976 5020 : priv->splits.pop_back();
1977 : else
1978 82 : priv->splits.erase (std::remove (priv->splits.begin(), priv->splits.end(), s),
1979 82 : priv->splits.end());
1980 :
1981 : //FIXME: find better event type
1982 5061 : qof_event_gen(&acc->inst, QOF_EVENT_MODIFY, nullptr);
1983 : // And send the account-based event, too
1984 5061 : qof_event_gen(&acc->inst, GNC_EVENT_ITEM_REMOVED, s);
1985 :
1986 5061 : priv->balance_dirty = TRUE;
1987 5061 : xaccAccountRecomputeBalance(acc);
1988 5061 : return TRUE;
1989 : }
1990 :
1991 : void
1992 15634 : xaccAccountSortSplits (Account *acc, gboolean force)
1993 : {
1994 : AccountPrivate *priv;
1995 :
1996 15634 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
1997 :
1998 15634 : priv = GET_PRIVATE(acc);
1999 15634 : if (!priv->sort_dirty || (!force && qof_instance_get_editlevel(acc) > 0))
2000 11882 : return;
2001 3752 : std::sort (priv->splits.begin(), priv->splits.end(), split_cmp_less);
2002 3752 : priv->sort_dirty = FALSE;
2003 3752 : priv->balance_dirty = TRUE;
2004 : }
2005 :
2006 : static void
2007 12592 : xaccAccountBringUpToDate(Account *acc)
2008 : {
2009 12592 : if (!acc) return;
2010 :
2011 : /* if a re-sort happens here, then everything will update, so the
2012 : cost basis and balance calls are no-ops */
2013 12592 : xaccAccountSortSplits(acc, FALSE);
2014 12592 : xaccAccountRecomputeBalance(acc);
2015 : }
2016 :
2017 : /********************************************************************\
2018 : \********************************************************************/
2019 :
2020 : void
2021 1308 : xaccAccountSetGUID (Account *acc, const GncGUID *guid)
2022 : {
2023 1308 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2024 1308 : g_return_if_fail(guid);
2025 :
2026 : /* XXX this looks fishy and weird to me ... */
2027 1308 : PINFO("acct=%p", acc);
2028 1308 : xaccAccountBeginEdit (acc);
2029 1308 : qof_instance_set_guid (&acc->inst, guid);
2030 1308 : qof_instance_set_dirty(&acc->inst);
2031 1308 : xaccAccountCommitEdit (acc);
2032 : }
2033 :
2034 : /********************************************************************\
2035 : \********************************************************************/
2036 :
2037 : Account *
2038 4278 : xaccAccountLookup (const GncGUID *guid, QofBook *book)
2039 : {
2040 : QofCollection *col;
2041 4278 : if (!guid || !book) return nullptr;
2042 4278 : col = qof_book_get_collection (book, GNC_ID_ACCOUNT);
2043 4278 : return (Account *) qof_collection_lookup_entity (col, guid);
2044 : }
2045 :
2046 : /********************************************************************\
2047 : \********************************************************************/
2048 :
2049 : void
2050 0 : xaccAccountSetMark (Account *acc, short m)
2051 : {
2052 : AccountPrivate *priv;
2053 :
2054 0 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2055 :
2056 0 : priv = GET_PRIVATE(acc);
2057 0 : priv->mark = m;
2058 : }
2059 :
2060 : void
2061 0 : xaccClearMark (Account *acc, short val)
2062 : {
2063 : Account *root;
2064 :
2065 0 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2066 :
2067 0 : root = gnc_account_get_root(acc);
2068 0 : xaccClearMarkDown(root ? root : acc, val);
2069 : }
2070 :
2071 : void
2072 0 : xaccClearMarkDown (Account *acc, short val)
2073 : {
2074 : AccountPrivate *priv;
2075 :
2076 0 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2077 :
2078 0 : priv = GET_PRIVATE(acc);
2079 0 : priv->mark = val;
2080 0 : std::for_each (priv->children.begin(), priv->children.end(),
2081 0 : [val](auto acc){ xaccClearMarkDown(acc, val); });
2082 : }
2083 :
2084 : /********************************************************************\
2085 : \********************************************************************/
2086 :
2087 : GNCPolicy *
2088 356 : gnc_account_get_policy (Account *acc)
2089 : {
2090 356 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2091 :
2092 356 : return GET_PRIVATE(acc)->policy;
2093 : }
2094 :
2095 : void
2096 1 : gnc_account_set_policy (Account *acc, GNCPolicy *policy)
2097 : {
2098 : AccountPrivate *priv;
2099 :
2100 1 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2101 :
2102 1 : priv = GET_PRIVATE(acc);
2103 1 : priv->policy = policy ? policy : xaccGetFIFOPolicy();
2104 : }
2105 :
2106 : /********************************************************************\
2107 : \********************************************************************/
2108 :
2109 : void
2110 131 : xaccAccountRemoveLot (Account *acc, GNCLot *lot)
2111 : {
2112 : AccountPrivate *priv;
2113 :
2114 131 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2115 131 : g_return_if_fail(GNC_IS_LOT(lot));
2116 :
2117 131 : priv = GET_PRIVATE(acc);
2118 131 : g_return_if_fail(priv->lots);
2119 :
2120 131 : ENTER ("(acc=%p, lot=%p)", acc, lot);
2121 131 : priv->lots = g_list_remove(priv->lots, lot);
2122 131 : qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_REMOVE, nullptr);
2123 131 : qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
2124 131 : LEAVE ("(acc=%p, lot=%p)", acc, lot);
2125 : }
2126 :
2127 : void
2128 235 : xaccAccountInsertLot (Account *acc, GNCLot *lot)
2129 : {
2130 : /* errors */
2131 235 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2132 235 : g_return_if_fail(GNC_IS_LOT(lot));
2133 :
2134 : /* optimizations */
2135 235 : auto lot_acc = gnc_lot_get_account(lot);
2136 235 : if (lot_acc == acc)
2137 1 : return;
2138 :
2139 234 : ENTER ("(acc=%p, lot=%p)", acc, lot);
2140 :
2141 : /* pull it out of the old account */
2142 234 : if (lot_acc)
2143 : {
2144 1 : auto priv = GET_PRIVATE(lot_acc);
2145 1 : priv->lots = g_list_remove(priv->lots, lot);
2146 1 : qof_event_gen (&lot_acc->inst, QOF_EVENT_MODIFY, nullptr);
2147 : }
2148 :
2149 234 : auto priv = GET_PRIVATE(acc);
2150 234 : priv->lots = g_list_prepend(priv->lots, lot);
2151 234 : gnc_lot_set_account(lot, acc);
2152 :
2153 : /* Don't move the splits to the new account. The caller will do this
2154 : * if appropriate, and doing it here will not work if we are being
2155 : * called from gnc_book_close_period since xaccAccountInsertSplit
2156 : * will try to balance capital gains and things aren't ready for that. */
2157 :
2158 234 : qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_ADD, nullptr);
2159 234 : qof_event_gen (&acc->inst, QOF_EVENT_MODIFY, nullptr);
2160 :
2161 234 : LEAVE ("(acc=%p, lot=%p)", acc, lot);
2162 : }
2163 :
2164 : /********************************************************************\
2165 : \********************************************************************/
2166 : static void
2167 0 : xaccPreSplitMove (Split *split)
2168 : {
2169 0 : xaccTransBeginEdit (xaccSplitGetParent (split));
2170 0 : }
2171 :
2172 : static void
2173 0 : xaccPostSplitMove (Split *split, Account *accto)
2174 : {
2175 : Transaction *trans;
2176 :
2177 0 : xaccSplitSetAccount(split, accto);
2178 0 : xaccSplitSetAmount(split, split->amount);
2179 0 : trans = xaccSplitGetParent (split);
2180 0 : xaccTransCommitEdit (trans);
2181 0 : }
2182 :
2183 : void
2184 0 : xaccAccountMoveAllSplits (Account *accfrom, Account *accto)
2185 : {
2186 : AccountPrivate *from_priv;
2187 :
2188 : /* errors */
2189 0 : g_return_if_fail(GNC_IS_ACCOUNT(accfrom));
2190 0 : g_return_if_fail(GNC_IS_ACCOUNT(accto));
2191 :
2192 : /* optimizations */
2193 0 : from_priv = GET_PRIVATE(accfrom);
2194 0 : if (from_priv->splits.empty() || accfrom == accto)
2195 0 : return;
2196 :
2197 : /* check for book mix-up */
2198 0 : g_return_if_fail (qof_instance_books_equal(accfrom, accto));
2199 0 : ENTER ("(accfrom=%p, accto=%p)", accfrom, accto);
2200 :
2201 0 : xaccAccountBeginEdit(accfrom);
2202 0 : xaccAccountBeginEdit(accto);
2203 : /* Begin editing both accounts and all transactions in accfrom. */
2204 0 : std::for_each (from_priv->splits.begin(), from_priv->splits.end(), xaccPreSplitMove);
2205 :
2206 : /* Concatenate accfrom's lists of splits and lots to accto's lists. */
2207 : //to_priv->splits = g_list_concat(to_priv->splits, from_priv->splits);
2208 : //to_priv->lots = g_list_concat(to_priv->lots, from_priv->lots);
2209 :
2210 : /* Set appropriate flags. */
2211 : //from_priv->balance_dirty = TRUE;
2212 : //from_priv->sort_dirty = FALSE;
2213 : //to_priv->balance_dirty = TRUE;
2214 : //to_priv->sort_dirty = TRUE;
2215 :
2216 : /*
2217 : * Change each split's account back pointer to accto.
2218 : * Convert each split's amount to accto's commodity.
2219 : * Commit to editing each transaction.
2220 : */
2221 0 : auto splits = from_priv->splits;
2222 0 : std::for_each (splits.begin(), splits.end(), [accto](auto s){ xaccPostSplitMove (s, accto); });
2223 :
2224 : /* Finally empty accfrom. */
2225 0 : g_assert(from_priv->splits.empty());
2226 0 : g_assert(from_priv->lots == nullptr);
2227 0 : xaccAccountCommitEdit(accfrom);
2228 0 : xaccAccountCommitEdit(accto);
2229 :
2230 0 : LEAVE ("(accfrom=%p, accto=%p)", accfrom, accto);
2231 0 : }
2232 :
2233 :
2234 : /********************************************************************\
2235 : * xaccAccountRecomputeBalance *
2236 : * recomputes the partial balances and the current balance for *
2237 : * this account. *
2238 : * *
2239 : * The way the computation is done depends on whether the partial *
2240 : * balances are for a monetary account (bank, cash, etc.) or a *
2241 : * certificate account (stock portfolio, mutual fund). For bank *
2242 : * accounts, the invariant amount is the dollar amount. For share *
2243 : * accounts, the invariant amount is the number of shares. For *
2244 : * share accounts, the share price fluctuates, and the current *
2245 : * value of such an account is the number of shares times the *
2246 : * current share price. *
2247 : * *
2248 : * Part of the complexity of this computation stems from the fact *
2249 : * xacc uses a double-entry system, meaning that one transaction *
2250 : * appears in two accounts: one account is debited, and the other *
2251 : * is credited. When the transaction represents a sale of shares, *
2252 : * or a purchase of shares, some care must be taken to compute *
2253 : * balances correctly. For a sale of shares, the stock account must*
2254 : * be debited in shares, but the bank account must be credited *
2255 : * in dollars. Thus, two different mechanisms must be used to *
2256 : * compute balances, depending on account type. *
2257 : * *
2258 : * Args: account -- the account for which to recompute balances *
2259 : * Return: void *
2260 : \********************************************************************/
2261 :
2262 : void
2263 34941 : xaccAccountRecomputeBalance (Account * acc)
2264 : {
2265 56978 : if (nullptr == acc) return;
2266 :
2267 34118 : auto priv = GET_PRIVATE(acc);
2268 34118 : if (qof_instance_get_editlevel(acc) > 0) return;
2269 31735 : if (!priv->balance_dirty || priv->defer_bal_computation) return;
2270 21475 : if (qof_instance_get_destroying(acc)) return;
2271 21475 : if (qof_book_shutting_down(qof_instance_get_book(acc))) return;
2272 :
2273 12081 : auto balance = priv->starting_balance;
2274 12081 : auto noclosing_balance = priv->starting_noclosing_balance;
2275 12081 : auto cleared_balance = priv->starting_cleared_balance;
2276 12081 : auto reconciled_balance = priv->starting_reconciled_balance;
2277 12081 : auto has_stock_split = false;
2278 :
2279 12081 : PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT,
2280 : priv->accountName, balance.num, balance.denom);
2281 121228 : for (auto split : priv->splits)
2282 : {
2283 97066 : auto amt = xaccSplitGetAmount (split);
2284 :
2285 97066 : if (xaccSplitIsStockSplit(split))
2286 : {
2287 97 : if (gnc_numeric_zero_p(balance))
2288 1 : continue;
2289 96 : auto new_balance = gnc_numeric_add_fixed(balance, amt);
2290 96 : if (gnc_numeric_zero_p(new_balance))
2291 0 : continue;
2292 96 : auto ratio = gnc_numeric_div(new_balance, balance, GNC_DENOM_AUTO,
2293 : GNC_HOW_DENOM_REDUCE);
2294 96 : auto denom = xaccAccountGetCommoditySCU(acc);
2295 1403 : for (auto psplit : priv->splits)
2296 : {
2297 1307 : if (psplit == split)
2298 96 : break;
2299 1211 : xaccSplitSetAdjustedAmount(psplit,
2300 : gnc_numeric_mul(xaccSplitGetAdjustedAmount(psplit), ratio, denom,
2301 : GNC_HOW_RND_ROUND_HALF_UP));
2302 : }
2303 96 : balance = new_balance;
2304 96 : has_stock_split = true;
2305 : }
2306 : else
2307 : {
2308 96969 : split->adjusted_amount = amt;
2309 96969 : balance = gnc_numeric_add_fixed(balance, amt);
2310 : }
2311 :
2312 97065 : if (NREC != split->reconciled)
2313 : {
2314 9959 : cleared_balance = gnc_numeric_add_fixed(cleared_balance, amt);
2315 : }
2316 :
2317 97065 : if (YREC == split->reconciled ||
2318 95132 : FREC == split->reconciled)
2319 : {
2320 : reconciled_balance =
2321 3724 : gnc_numeric_add_fixed(reconciled_balance, amt);
2322 : }
2323 :
2324 97065 : if (!(xaccTransGetIsClosingTxn (split->parent)))
2325 96740 : noclosing_balance = gnc_numeric_add_fixed(noclosing_balance, amt);
2326 :
2327 97065 : split->balance = balance;
2328 97065 : split->noclosing_balance = noclosing_balance;
2329 97065 : split->cleared_balance = cleared_balance;
2330 97065 : split->reconciled_balance = reconciled_balance;
2331 :
2332 : }
2333 :
2334 12081 : priv->balance = balance;
2335 12081 : priv->noclosing_balance = noclosing_balance;
2336 12081 : priv->cleared_balance = cleared_balance;
2337 12081 : priv->reconciled_balance = reconciled_balance;
2338 12081 : priv->balance_dirty = FALSE;
2339 12081 : priv->has_stock_split = has_stock_split;
2340 : }
2341 :
2342 : /********************************************************************\
2343 : \********************************************************************/
2344 :
2345 : /* The sort order is used to implicitly define an
2346 : * order for report generation */
2347 :
2348 : static int typeorder[NUM_ACCOUNT_TYPES] =
2349 : {
2350 : ACCT_TYPE_BANK, ACCT_TYPE_STOCK, ACCT_TYPE_MUTUAL, ACCT_TYPE_CURRENCY,
2351 : ACCT_TYPE_CASH, ACCT_TYPE_ASSET, ACCT_TYPE_RECEIVABLE,
2352 : ACCT_TYPE_CREDIT, ACCT_TYPE_LIABILITY, ACCT_TYPE_PAYABLE,
2353 : ACCT_TYPE_INCOME, ACCT_TYPE_EXPENSE, ACCT_TYPE_EQUITY, ACCT_TYPE_TRADING
2354 : };
2355 :
2356 : static int revorder[NUM_ACCOUNT_TYPES] =
2357 : {
2358 : -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
2359 : };
2360 :
2361 :
2362 : int
2363 14502 : xaccAccountOrder (const Account *aa, const Account *ab)
2364 : {
2365 : AccountPrivate *priv_aa, *priv_ab;
2366 : const char *da, *db;
2367 : int ta, tb, result;
2368 :
2369 14502 : if (aa == ab) return 0;
2370 14500 : if (!ab) return -1;
2371 14499 : if (!aa) return +1;
2372 :
2373 14498 : priv_aa = GET_PRIVATE(aa);
2374 14498 : priv_ab = GET_PRIVATE(ab);
2375 :
2376 : /* sort on accountCode strings */
2377 14498 : da = priv_aa->accountCode;
2378 14498 : db = priv_ab->accountCode;
2379 :
2380 : /* Otherwise do a string sort */
2381 14498 : result = g_strcmp0 (da, db);
2382 14498 : if (result)
2383 41 : return result;
2384 :
2385 : /* if account-type-order array not initialized, initialize it */
2386 : /* this will happen at most once during program invocation */
2387 14457 : if (-1 == revorder[0])
2388 : {
2389 : int i;
2390 400 : for (i = 0; i < NUM_ACCOUNT_TYPES; i++)
2391 : {
2392 375 : revorder [typeorder[i]] = i;
2393 : }
2394 : }
2395 :
2396 : /* otherwise, sort on account type */
2397 14457 : ta = priv_aa->type;
2398 14457 : tb = priv_ab->type;
2399 14457 : ta = revorder[ta];
2400 14457 : tb = revorder[tb];
2401 14457 : if (ta < tb) return -1;
2402 13236 : if (ta > tb) return +1;
2403 :
2404 : /* otherwise, sort on accountName strings */
2405 8054 : da = priv_aa->accountName;
2406 8054 : db = priv_ab->accountName;
2407 8054 : result = safe_utf8_collate(da, db);
2408 8054 : if (result)
2409 7944 : return result;
2410 :
2411 : /* guarantee a stable sort */
2412 110 : return qof_instance_guid_compare(aa, ab);
2413 : }
2414 :
2415 : static int
2416 0 : qof_xaccAccountOrder (const Account **aa, const Account **ab)
2417 : {
2418 0 : return xaccAccountOrder(*aa, *ab);
2419 : }
2420 :
2421 : /********************************************************************\
2422 : \********************************************************************/
2423 :
2424 : void
2425 4381 : xaccAccountSetType (Account *acc, GNCAccountType tip)
2426 : {
2427 : AccountPrivate *priv;
2428 :
2429 : /* errors */
2430 4381 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2431 4381 : g_return_if_fail(tip < NUM_ACCOUNT_TYPES);
2432 :
2433 : /* optimizations */
2434 4381 : priv = GET_PRIVATE(acc);
2435 4381 : if (priv->type == tip)
2436 64 : return;
2437 :
2438 4317 : xaccAccountBeginEdit(acc);
2439 4317 : priv->type = tip;
2440 4317 : priv->balance_dirty = TRUE; /* new type may affect balance computation */
2441 4317 : mark_account(acc);
2442 4317 : xaccAccountCommitEdit(acc);
2443 : }
2444 :
2445 : void
2446 5412 : xaccAccountSetName (Account *acc, const char *str)
2447 : {
2448 : AccountPrivate *priv;
2449 :
2450 : /* errors */
2451 5412 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2452 5412 : g_return_if_fail(str);
2453 :
2454 : /* optimizations */
2455 5412 : priv = GET_PRIVATE(acc);
2456 5412 : if (g_strcmp0(str, priv->accountName) == 0)
2457 14 : return;
2458 :
2459 5398 : xaccAccountBeginEdit(acc);
2460 5398 : priv->accountName = qof_string_cache_replace(priv->accountName, str);
2461 5398 : mark_account (acc);
2462 5398 : xaccAccountCommitEdit(acc);
2463 : }
2464 :
2465 : void
2466 1244 : xaccAccountSetCode (Account *acc, const char *str)
2467 : {
2468 : AccountPrivate *priv;
2469 :
2470 : /* errors */
2471 1244 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2472 :
2473 : /* optimizations */
2474 1244 : priv = GET_PRIVATE(acc);
2475 1244 : if (g_strcmp0(str, priv->accountCode) == 0)
2476 89 : return;
2477 :
2478 1155 : xaccAccountBeginEdit(acc);
2479 1155 : priv->accountCode = qof_string_cache_replace(priv->accountCode, str ? str : "");
2480 1155 : mark_account (acc);
2481 1155 : xaccAccountCommitEdit(acc);
2482 : }
2483 :
2484 : void
2485 2165 : xaccAccountSetDescription (Account *acc, const char *str)
2486 : {
2487 : AccountPrivate *priv;
2488 :
2489 : /* errors */
2490 2165 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2491 :
2492 : /* optimizations */
2493 2165 : priv = GET_PRIVATE(acc);
2494 2165 : if (g_strcmp0(str, priv->description) == 0)
2495 638 : return;
2496 :
2497 1527 : xaccAccountBeginEdit(acc);
2498 1527 : priv->description = qof_string_cache_replace(priv->description, str ? str : "");
2499 1527 : mark_account (acc);
2500 1527 : xaccAccountCommitEdit(acc);
2501 : }
2502 :
2503 : static void
2504 6 : set_kvp_gnc_numeric_path (Account *acc, const std::vector<std::string>& path,
2505 : std::optional<gnc_numeric> value)
2506 : {
2507 6 : xaccAccountBeginEdit(acc);
2508 6 : qof_instance_set_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), value, path);
2509 6 : xaccAccountCommitEdit(acc);
2510 6 : }
2511 :
2512 : static std::optional<gnc_numeric>
2513 11 : get_kvp_gnc_numeric_path (const Account *acc, const Path& path)
2514 : {
2515 11 : return qof_instance_get_path_kvp<gnc_numeric> (QOF_INSTANCE(acc), path);
2516 : }
2517 :
2518 : static void
2519 1826 : set_kvp_string_path (Account *acc, std::vector<std::string> const & path,
2520 : const char *value)
2521 : {
2522 1826 : std::optional<const char*> val;
2523 1826 : if (value && *value)
2524 37 : val = g_strdup(value);
2525 :
2526 1826 : xaccAccountBeginEdit(acc);
2527 1826 : qof_instance_set_path_kvp<const char*> (QOF_INSTANCE(acc), val, path);
2528 1826 : xaccAccountCommitEdit(acc);
2529 1826 : }
2530 :
2531 : static const char*
2532 2858 : get_kvp_string_path (const Account *acc, const Path& path)
2533 : {
2534 2858 : auto rv{qof_instance_get_path_kvp<const char*> (QOF_INSTANCE(acc), path)};
2535 5716 : return rv ? *rv : nullptr;
2536 : }
2537 :
2538 : static void
2539 81 : set_kvp_account_path (Account* acc, const Path& path, const Account* kvp_account)
2540 : {
2541 81 : std::optional<GncGUID*> val;
2542 81 : if (kvp_account)
2543 80 : val = guid_copy(xaccAccountGetGUID (kvp_account));
2544 :
2545 81 : xaccAccountBeginEdit(acc);
2546 81 : qof_instance_set_path_kvp<GncGUID*> (QOF_INSTANCE(acc), val, path);
2547 81 : xaccAccountCommitEdit(acc);
2548 81 : }
2549 :
2550 : static Account*
2551 15 : get_kvp_account_path (const Account *acc, const Path& path)
2552 : {
2553 15 : auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
2554 30 : return val ? xaccAccountLookup (*val, gnc_account_get_book (acc)) : nullptr;
2555 : }
2556 :
2557 : static void
2558 177 : set_kvp_boolean_path (Account *acc, const Path& path, gboolean option)
2559 : {
2560 177 : set_kvp_string_path (acc, path, option ? "true" : nullptr);
2561 177 : }
2562 :
2563 : static gboolean
2564 261 : get_kvp_boolean_path (const Account *acc, const Path& path)
2565 : {
2566 261 : auto slot{QOF_INSTANCE(acc)->kvp_data->get_slot(path)};
2567 261 : if (!slot) return false;
2568 10 : switch (slot->get_type())
2569 : {
2570 1 : case KvpValueImpl::Type::INT64:
2571 1 : return slot->get<int64_t>() != 0;
2572 9 : case KvpValueImpl::Type::STRING:
2573 9 : return g_strcmp0 (slot->get<const char*>(), "true") == 0;
2574 0 : default:
2575 0 : return false;
2576 : }
2577 : }
2578 :
2579 : static void
2580 19 : set_kvp_int64_path (Account *acc, const Path& path, std::optional<gint64> value)
2581 : {
2582 19 : xaccAccountBeginEdit(acc);
2583 19 : qof_instance_set_path_kvp<int64_t> (QOF_INSTANCE(acc), value, path);
2584 19 : xaccAccountCommitEdit(acc);
2585 19 : }
2586 :
2587 : static const std::optional<gint64>
2588 21 : get_kvp_int64_path (const Account *acc, const Path& path)
2589 : {
2590 21 : return qof_instance_get_path_kvp<int64_t> (QOF_INSTANCE(acc), path);
2591 : }
2592 :
2593 : static GncGUID*
2594 2 : get_kvp_guid_path (const Account *acc, const Path& path)
2595 : {
2596 2 : auto val{qof_instance_get_path_kvp<GncGUID*> (QOF_INSTANCE(acc), path)};
2597 4 : return val ? guid_copy(*val) : nullptr;
2598 : }
2599 :
2600 : void
2601 707 : xaccAccountSetColor (Account *acc, const char *str)
2602 : {
2603 1414 : set_kvp_string_path (acc, {"color"}, str);
2604 707 : }
2605 :
2606 : void
2607 5 : xaccAccountSetFilter (Account *acc, const char *str)
2608 : {
2609 10 : set_kvp_string_path (acc, {"filter"}, str);
2610 5 : }
2611 :
2612 : void
2613 5 : xaccAccountSetSortOrder (Account *acc, const char *str)
2614 : {
2615 10 : set_kvp_string_path (acc, {"sort-order"}, str);
2616 5 : }
2617 :
2618 : void
2619 2 : xaccAccountSetSortReversed (Account *acc, gboolean sortreversed)
2620 : {
2621 4 : set_kvp_boolean_path (acc, {"sort-reversed"}, sortreversed);
2622 2 : }
2623 :
2624 : static void
2625 1 : qofAccountSetParent (Account *acc, QofInstance *parent)
2626 : {
2627 : Account *parent_acc;
2628 :
2629 1 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2630 1 : g_return_if_fail(GNC_IS_ACCOUNT(parent));
2631 :
2632 1 : parent_acc = GNC_ACCOUNT(parent);
2633 1 : xaccAccountBeginEdit(acc);
2634 1 : xaccAccountBeginEdit(parent_acc);
2635 1 : gnc_account_append_child(parent_acc, acc);
2636 1 : mark_account (parent_acc);
2637 1 : mark_account (acc);
2638 1 : xaccAccountCommitEdit(acc);
2639 1 : xaccAccountCommitEdit(parent_acc);
2640 : }
2641 :
2642 : void
2643 521 : xaccAccountSetNotes (Account *acc, const char *str)
2644 : {
2645 1042 : set_kvp_string_path (acc, {"notes"}, str);
2646 521 : }
2647 :
2648 : void
2649 4 : xaccAccountSetOnlineID (Account *acc, const char *id)
2650 : {
2651 4 : g_return_if_fail (GNC_IS_ACCOUNT(acc));
2652 12 : set_kvp_string_path (acc, {KEY_ONLINE_ID}, id);
2653 4 : }
2654 :
2655 :
2656 : void
2657 71 : xaccAccountSetAssociatedAccount (Account *acc, const char *tag, const Account* assoc_acct)
2658 : {
2659 71 : g_return_if_fail (GNC_IS_ACCOUNT(acc));
2660 70 : g_return_if_fail (tag && *tag);
2661 :
2662 345 : set_kvp_account_path (acc, {"associated-account", tag}, assoc_acct);
2663 138 : }
2664 :
2665 : void
2666 3409 : xaccAccountSetCommodity (Account * acc, gnc_commodity * com)
2667 : {
2668 : AccountPrivate *priv;
2669 :
2670 : /* errors */
2671 3409 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2672 3409 : g_return_if_fail(GNC_IS_COMMODITY(com));
2673 :
2674 : /* optimizations */
2675 3214 : priv = GET_PRIVATE(acc);
2676 3214 : if (com == priv->commodity)
2677 47 : return;
2678 :
2679 3167 : xaccAccountBeginEdit(acc);
2680 3167 : gnc_commodity_decrement_usage_count(priv->commodity);
2681 3167 : priv->commodity = com;
2682 3167 : gnc_commodity_increment_usage_count(com);
2683 3167 : priv->commodity_scu = gnc_commodity_get_fraction(com);
2684 3167 : priv->non_standard_scu = FALSE;
2685 :
2686 : /* iterate over splits */
2687 6343 : for (auto s : priv->splits)
2688 : {
2689 9 : Transaction *trans = xaccSplitGetParent (s);
2690 :
2691 9 : xaccTransBeginEdit (trans);
2692 9 : xaccSplitSetAmount (s, xaccSplitGetAmount(s));
2693 9 : xaccTransCommitEdit (trans);
2694 : }
2695 :
2696 3167 : priv->sort_dirty = TRUE; /* Not needed. */
2697 3167 : priv->balance_dirty = TRUE;
2698 3167 : mark_account (acc);
2699 :
2700 3167 : xaccAccountCommitEdit(acc);
2701 : }
2702 :
2703 : /*
2704 : * Set the account scu and then check to see if it is the same as the
2705 : * commodity scu. This function is called when parsing the data file
2706 : * and is designed to catch cases where the two were accidentally set
2707 : * to mismatched values in the past.
2708 : */
2709 : void
2710 905 : xaccAccountSetCommoditySCU (Account *acc, int scu)
2711 : {
2712 : AccountPrivate *priv;
2713 :
2714 905 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2715 :
2716 905 : priv = GET_PRIVATE(acc);
2717 905 : xaccAccountBeginEdit(acc);
2718 905 : priv->commodity_scu = scu;
2719 905 : if (scu != gnc_commodity_get_fraction(priv->commodity))
2720 100 : priv->non_standard_scu = TRUE;
2721 905 : mark_account(acc);
2722 905 : xaccAccountCommitEdit(acc);
2723 : }
2724 :
2725 : int
2726 706 : xaccAccountGetCommoditySCUi (const Account * acc)
2727 : {
2728 706 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2729 706 : return GET_PRIVATE(acc)->commodity_scu;
2730 : }
2731 :
2732 : int
2733 18300 : xaccAccountGetCommoditySCU (const Account * acc)
2734 : {
2735 : AccountPrivate *priv;
2736 :
2737 18300 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2738 :
2739 18300 : priv = GET_PRIVATE(acc);
2740 18300 : if (priv->non_standard_scu || !priv->commodity)
2741 1805 : return priv->commodity_scu;
2742 16495 : return gnc_commodity_get_fraction(priv->commodity);
2743 : }
2744 :
2745 : void
2746 46 : xaccAccountSetNonStdSCU (Account *acc, gboolean flag)
2747 : {
2748 : AccountPrivate *priv;
2749 :
2750 46 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
2751 :
2752 46 : priv = GET_PRIVATE(acc);
2753 46 : if (priv->non_standard_scu == flag)
2754 42 : return;
2755 4 : xaccAccountBeginEdit(acc);
2756 4 : priv->non_standard_scu = flag;
2757 4 : mark_account (acc);
2758 4 : xaccAccountCommitEdit(acc);
2759 : }
2760 :
2761 : gboolean
2762 251 : xaccAccountGetNonStdSCU (const Account * acc)
2763 : {
2764 251 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), 0);
2765 251 : return GET_PRIVATE(acc)->non_standard_scu;
2766 : }
2767 :
2768 : /********************************************************************\
2769 : \********************************************************************/
2770 : /* below follow the old, deprecated currency/security routines. */
2771 :
2772 : void
2773 0 : DxaccAccountSetCurrency (Account * acc, gnc_commodity * currency)
2774 : {
2775 0 : if ((!acc) || (!currency)) return;
2776 :
2777 0 : auto s = gnc_commodity_get_unique_name (currency);
2778 0 : set_kvp_string_path (acc, {"old-currency"}, s);
2779 :
2780 0 : auto book = qof_instance_get_book(acc);
2781 0 : auto table = gnc_commodity_table_get_table (book);
2782 0 : auto commodity = gnc_commodity_table_lookup_unique (table, s);
2783 :
2784 0 : if (!commodity)
2785 0 : gnc_commodity_table_insert (table, currency);
2786 : }
2787 :
2788 : /********************************************************************\
2789 : \********************************************************************/
2790 :
2791 : void
2792 18211 : gnc_account_foreach_descendant (const Account *acc, std::function<void(Account*)> account_cb)
2793 : {
2794 18211 : g_return_if_fail (GNC_IS_ACCOUNT(acc));
2795 :
2796 : // children must be a vector copy instead of reference because
2797 : // some callers e.g. xaccAccountTreeScrubLots will modify the
2798 : // children
2799 18211 : auto children = GET_PRIVATE(acc)->children;
2800 53065 : for (auto child : children)
2801 : {
2802 16643 : account_cb (child);
2803 16643 : gnc_account_foreach_descendant (child, account_cb);
2804 : }
2805 18211 : }
2806 :
2807 : static void
2808 13149 : account_foreach_descendant_sorted (const Account *acc, std::function<void(Account*)> account_cb)
2809 : {
2810 13149 : g_return_if_fail (GNC_IS_ACCOUNT(acc));
2811 :
2812 13149 : auto children = GET_PRIVATE(acc)->children;
2813 13149 : std::sort (children.begin(), children.end(),
2814 13340 : [](auto a, auto b) { return xaccAccountOrder (a, b) < 0; });
2815 :
2816 37068 : for (auto child : children)
2817 : {
2818 10770 : account_cb (child);
2819 10770 : account_foreach_descendant_sorted (child, account_cb);
2820 : }
2821 13149 : }
2822 :
2823 : void
2824 4355 : gnc_account_append_child (Account *new_parent, Account *child)
2825 : {
2826 : AccountPrivate *ppriv, *cpriv;
2827 : Account *old_parent;
2828 : QofCollection *col;
2829 :
2830 : /* errors */
2831 4355 : g_assert(GNC_IS_ACCOUNT(new_parent));
2832 4355 : g_assert(GNC_IS_ACCOUNT(child));
2833 :
2834 : /* optimizations */
2835 4355 : ppriv = GET_PRIVATE(new_parent);
2836 4355 : cpriv = GET_PRIVATE(child);
2837 4355 : old_parent = cpriv->parent;
2838 4355 : if (old_parent == new_parent)
2839 385 : return;
2840 :
2841 : // xaccAccountBeginEdit(new_parent);
2842 3970 : xaccAccountBeginEdit(child);
2843 3970 : if (old_parent)
2844 : {
2845 16 : gnc_account_remove_child(old_parent, child);
2846 :
2847 16 : if (!qof_instance_books_equal(old_parent, new_parent))
2848 : {
2849 : /* hack alert -- this implementation is not exactly correct.
2850 : * If the entity tables are not identical, then the 'from' book
2851 : * may have a different backend than the 'to' book. This means
2852 : * that we should get the 'from' backend to destroy this account,
2853 : * and the 'to' backend to save it. Right now, this is broken.
2854 : *
2855 : * A 'correct' implementation similar to this is in Period.c
2856 : * except its for transactions ...
2857 : *
2858 : * Note also, we need to reparent the children to the new book as well.
2859 : */
2860 1 : PWARN ("reparenting accounts across books is not correctly supported\n");
2861 :
2862 1 : qof_event_gen (&child->inst, QOF_EVENT_DESTROY, nullptr);
2863 1 : col = qof_book_get_collection (qof_instance_get_book(new_parent),
2864 : GNC_ID_ACCOUNT);
2865 1 : qof_collection_insert_entity (col, &child->inst);
2866 1 : qof_event_gen (&child->inst, QOF_EVENT_CREATE, nullptr);
2867 : }
2868 : }
2869 3970 : cpriv->parent = new_parent;
2870 3970 : ppriv->children.push_back (child);
2871 3970 : qof_instance_set_dirty(&new_parent->inst);
2872 3970 : qof_instance_set_dirty(&child->inst);
2873 :
2874 : /* Send events data. Warning: The call to commit_edit is also going
2875 : * to send a MODIFY event. If the gtktreemodelfilter code gets the
2876 : * MODIFY before it gets the ADD, it gets very confused and thinks
2877 : * that two nodes have been added. */
2878 3970 : qof_event_gen (&child->inst, QOF_EVENT_ADD, nullptr);
2879 : // qof_event_gen (&new_parent->inst, QOF_EVENT_MODIFY, nullptr);
2880 :
2881 3970 : xaccAccountCommitEdit (child);
2882 : // xaccAccountCommitEdit(new_parent);
2883 : }
2884 :
2885 : void
2886 1851 : gnc_account_remove_child (Account *parent, Account *child)
2887 : {
2888 : AccountPrivate *ppriv, *cpriv;
2889 : GncEventData ed;
2890 :
2891 1852 : if (!child) return;
2892 :
2893 : /* Note this routine might be called on accounts which
2894 : * are not yet parented. */
2895 1851 : if (!parent) return;
2896 :
2897 1851 : ppriv = GET_PRIVATE(parent);
2898 1851 : cpriv = GET_PRIVATE(child);
2899 :
2900 1851 : if (cpriv->parent != parent)
2901 : {
2902 1 : PERR ("account not a child of parent");
2903 1 : return;
2904 : }
2905 :
2906 : /* Gather event data */
2907 1850 : ed.node = parent;
2908 1850 : ed.idx = gnc_account_child_index (parent, child);
2909 :
2910 3700 : ppriv->children.erase (std::remove (ppriv->children.begin(), ppriv->children.end(), child),
2911 1850 : ppriv->children.end());
2912 :
2913 : /* Now send the event. */
2914 1850 : qof_event_gen(&child->inst, QOF_EVENT_REMOVE, &ed);
2915 :
2916 : /* clear the account's parent pointer after REMOVE event generation. */
2917 1850 : cpriv->parent = nullptr;
2918 :
2919 1850 : qof_event_gen (&parent->inst, QOF_EVENT_MODIFY, nullptr);
2920 : }
2921 :
2922 : Account *
2923 36967 : gnc_account_get_parent (const Account *acc)
2924 : {
2925 36967 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2926 36967 : return GET_PRIVATE(acc)->parent;
2927 : }
2928 :
2929 : Account *
2930 1603 : gnc_account_get_root (Account *acc)
2931 : {
2932 1603 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
2933 :
2934 4093 : while (auto parent = gnc_account_get_parent (acc))
2935 2490 : acc = parent;
2936 :
2937 1603 : return acc;
2938 : }
2939 :
2940 : gboolean
2941 39029 : gnc_account_is_root (const Account *account)
2942 : {
2943 39029 : g_return_val_if_fail(GNC_IS_ACCOUNT(account), FALSE);
2944 39029 : return (GET_PRIVATE(account)->parent == nullptr);
2945 : }
2946 :
2947 : GList *
2948 3022 : gnc_account_get_children (const Account *account)
2949 : {
2950 3022 : g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2951 3022 : auto& children = GET_PRIVATE(account)->children;
2952 6044 : return std::accumulate (children.rbegin(), children.rend(), static_cast<GList*>(nullptr),
2953 3022 : g_list_prepend);
2954 : }
2955 :
2956 : GList *
2957 1442 : gnc_account_get_children_sorted (const Account *account)
2958 : {
2959 1442 : g_return_val_if_fail(GNC_IS_ACCOUNT(account), nullptr);
2960 1442 : return g_list_sort(gnc_account_get_children (account), (GCompareFunc)xaccAccountOrder);
2961 : }
2962 :
2963 : gint
2964 0 : gnc_account_n_children (const Account *account)
2965 : {
2966 0 : g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
2967 0 : return GET_PRIVATE(account)->children.size();
2968 : }
2969 :
2970 : gint
2971 1853 : gnc_account_child_index (const Account *parent, const Account *child)
2972 : {
2973 1853 : g_return_val_if_fail(GNC_IS_ACCOUNT(parent), -1);
2974 1853 : g_return_val_if_fail(GNC_IS_ACCOUNT(child), -1);
2975 1853 : auto& children = GET_PRIVATE(parent)->children;
2976 1853 : auto find_it = std::find (children.begin(), children.end(), child);
2977 5557 : return find_it == children.end() ? -1 : std::distance (children.begin(), find_it);
2978 : }
2979 :
2980 : Account *
2981 0 : gnc_account_nth_child (const Account *parent, gint num)
2982 : {
2983 0 : g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
2984 0 : if ((size_t)num >= GET_PRIVATE(parent)->children.size())
2985 0 : return nullptr;
2986 0 : return static_cast<Account*>(GET_PRIVATE(parent)->children.at (num));
2987 : }
2988 :
2989 : gint
2990 26 : gnc_account_n_descendants (const Account *account)
2991 : {
2992 26 : int count {0};
2993 26 : gnc_account_foreach_descendant (account, [&count](auto acc){ count++; });
2994 26 : return count;
2995 : }
2996 :
2997 : gint
2998 549 : gnc_account_get_current_depth (const Account *account)
2999 : {
3000 : AccountPrivate *priv;
3001 549 : int depth = 0;
3002 :
3003 549 : g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
3004 :
3005 549 : priv = GET_PRIVATE(account);
3006 2263 : while (priv->parent && (priv->type != ACCT_TYPE_ROOT))
3007 : {
3008 1714 : account = priv->parent;
3009 1714 : priv = GET_PRIVATE(account);
3010 1714 : depth++;
3011 : }
3012 :
3013 549 : return depth;
3014 : }
3015 :
3016 : gint
3017 644 : gnc_account_get_tree_depth (const Account *account)
3018 : {
3019 : AccountPrivate *priv;
3020 644 : g_return_val_if_fail(GNC_IS_ACCOUNT(account), 0);
3021 :
3022 644 : priv = GET_PRIVATE(account);
3023 644 : if (!priv->children.size())
3024 412 : return 1;
3025 :
3026 232 : return 1 + std::accumulate (priv->children.begin(), priv->children.end(),
3027 556 : 0, [](auto a, auto b)
3028 788 : { return std::max (a, gnc_account_get_tree_depth (b)); });
3029 : }
3030 :
3031 : GList *
3032 770 : gnc_account_get_descendants (const Account *account)
3033 : {
3034 770 : GList* list = nullptr;
3035 12765 : gnc_account_foreach_descendant (account, [&list](auto a){ list = g_list_prepend (list, a); });
3036 1540 : return g_list_reverse (list);
3037 : }
3038 :
3039 : GList *
3040 2379 : gnc_account_get_descendants_sorted (const Account *account)
3041 : {
3042 2379 : GList* list = nullptr;
3043 13149 : account_foreach_descendant_sorted (account, [&list](auto a){ list = g_list_prepend (list, a); });
3044 4758 : return g_list_reverse (list);
3045 : }
3046 :
3047 : // because gnc_account_lookup_by_name and gnc_account_lookup_by_code
3048 : // are described in Account.h searching breadth-first until 4.6, and
3049 : // accidentally modified to search depth-first from 4.7
3050 : // onwards. Restore breath-first searching in 4.11 onwards to match
3051 : // previous behaviour and function description in Account.h
3052 : static gpointer
3053 3931 : account_foreach_descendant_breadthfirst_until (const Account *acc,
3054 : AccountCb2 thunk,
3055 : gpointer user_data)
3056 : {
3057 3931 : g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3058 3931 : g_return_val_if_fail (thunk, nullptr);
3059 :
3060 3931 : auto& children{GET_PRIVATE(acc)->children};
3061 :
3062 11991 : for (auto acc : children)
3063 4400 : if (auto result = thunk (acc, user_data))
3064 271 : return result;
3065 :
3066 10538 : for (auto acc: children)
3067 3655 : if (auto result = account_foreach_descendant_breadthfirst_until (acc, thunk, user_data))
3068 437 : return result;
3069 :
3070 3223 : return nullptr;
3071 : }
3072 :
3073 : static gpointer
3074 4369 : is_acct_name (Account *account, gpointer user_data)
3075 : {
3076 4369 : auto name {static_cast<gchar*>(user_data)};
3077 4369 : return (g_strcmp0 (name, xaccAccountGetName (account)) ? nullptr : account);
3078 : }
3079 :
3080 : Account *
3081 268 : gnc_account_lookup_by_name (const Account *parent, const char * name)
3082 : {
3083 268 : return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_name, (char*)name);
3084 : }
3085 :
3086 : static gpointer
3087 31 : is_acct_code (Account *account, gpointer user_data)
3088 : {
3089 31 : auto name {static_cast<gchar*>(user_data)};
3090 31 : return (g_strcmp0 (name, xaccAccountGetCode (account)) ? nullptr : account);
3091 : }
3092 :
3093 : Account *
3094 8 : gnc_account_lookup_by_code (const Account *parent, const char * code)
3095 : {
3096 8 : return (Account*)account_foreach_descendant_breadthfirst_until (parent, is_acct_code, (char*)code);
3097 : }
3098 :
3099 : static gpointer
3100 0 : is_opening_balance_account (Account* account, gpointer data)
3101 : {
3102 0 : gnc_commodity* commodity = GNC_COMMODITY(data);
3103 0 : if (xaccAccountGetIsOpeningBalance(account) && gnc_commodity_equiv(commodity, xaccAccountGetCommodity(account)))
3104 0 : return account;
3105 0 : return nullptr;
3106 : }
3107 :
3108 : Account*
3109 0 : gnc_account_lookup_by_opening_balance (Account* account, gnc_commodity* commodity)
3110 : {
3111 0 : return (Account *)gnc_account_foreach_descendant_until (account, is_opening_balance_account, commodity);
3112 : }
3113 :
3114 : /********************************************************************\
3115 : * Fetch an account, given its full name *
3116 : \********************************************************************/
3117 :
3118 : static Account *
3119 43 : gnc_account_lookup_by_full_name_helper (const Account *parent,
3120 : gchar **names)
3121 : {
3122 43 : g_return_val_if_fail(GNC_IS_ACCOUNT(parent), nullptr);
3123 43 : g_return_val_if_fail(names, nullptr);
3124 :
3125 : /* Look for the first name in the children. */
3126 160 : for (auto account : GET_PRIVATE(parent)->children)
3127 : {
3128 104 : auto priv = GET_PRIVATE(account);
3129 104 : if (g_strcmp0(priv->accountName, names[0]) == 0)
3130 : {
3131 : /* We found an account. If the next entry is nullptr, there is
3132 : * nothing left in the name, so just return the account. */
3133 35 : if (names[1] == nullptr)
3134 30 : return account;
3135 :
3136 : /* No children? We're done. */
3137 22 : if (priv->children.empty())
3138 0 : return nullptr;
3139 :
3140 : /* There's stuff left to search for. Search recursively. */
3141 22 : if (auto found = gnc_account_lookup_by_full_name_helper(account, &names[1]))
3142 17 : return found;
3143 : }
3144 : }
3145 :
3146 13 : return nullptr;
3147 : }
3148 :
3149 :
3150 : Account *
3151 18 : gnc_account_lookup_by_full_name (const Account *any_acc,
3152 : const gchar *name)
3153 : {
3154 : const AccountPrivate *rpriv;
3155 : const Account *root;
3156 : Account *found;
3157 : gchar **names;
3158 :
3159 18 : g_return_val_if_fail(GNC_IS_ACCOUNT(any_acc), nullptr);
3160 18 : g_return_val_if_fail(name, nullptr);
3161 :
3162 18 : root = any_acc;
3163 18 : rpriv = GET_PRIVATE(root);
3164 30 : while (rpriv->parent)
3165 : {
3166 12 : root = rpriv->parent;
3167 12 : rpriv = GET_PRIVATE(root);
3168 : }
3169 18 : names = g_strsplit(name, gnc_get_account_separator_string(), -1);
3170 18 : found = gnc_account_lookup_by_full_name_helper(root, names);
3171 18 : g_strfreev(names);
3172 18 : return found;
3173 : }
3174 :
3175 : GList*
3176 171 : gnc_account_lookup_by_type_and_commodity (Account* root,
3177 : const char* name,
3178 : GNCAccountType acctype,
3179 : gnc_commodity* commodity)
3180 : {
3181 171 : GList *retval{};
3182 171 : auto rpriv{GET_PRIVATE(root)};
3183 455 : for (auto account : rpriv->children)
3184 : {
3185 113 : if (xaccAccountGetType (account) == acctype)
3186 : {
3187 120 : if (commodity &&
3188 60 : !gnc_commodity_equiv(xaccAccountGetCommodity (account),
3189 : commodity))
3190 10 : continue;
3191 :
3192 50 : if (name && strcmp(name, xaccAccountGetName(account)))
3193 26 : continue;
3194 :
3195 24 : retval = g_list_prepend(retval, account);
3196 : }
3197 : }
3198 :
3199 171 : if (!retval) // Recurse through the children
3200 358 : for (auto account : rpriv->children)
3201 : {
3202 64 : auto result = gnc_account_lookup_by_type_and_commodity(account,
3203 : name,
3204 : acctype,
3205 : commodity);
3206 64 : if (result)
3207 0 : retval = g_list_concat(result, retval);
3208 : }
3209 171 : return retval;
3210 : }
3211 :
3212 : void
3213 150 : gnc_account_foreach_child (const Account *acc,
3214 : AccountCb thunk,
3215 : gpointer user_data)
3216 : {
3217 150 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
3218 150 : g_return_if_fail(thunk);
3219 150 : std::for_each (GET_PRIVATE(acc)->children.begin(), GET_PRIVATE(acc)->children.end(),
3220 53 : [user_data, thunk](auto a){ thunk (a, user_data); });
3221 : }
3222 :
3223 : void
3224 715 : gnc_account_foreach_descendant (const Account *acc,
3225 : AccountCb thunk,
3226 : gpointer user_data)
3227 : {
3228 3571 : gnc_account_foreach_descendant (acc, [&](auto acc){ thunk (acc, user_data); });
3229 715 : }
3230 :
3231 : gpointer
3232 91 : gnc_account_foreach_descendant_until (const Account *acc,
3233 : AccountCb2 thunk,
3234 : gpointer user_data)
3235 : {
3236 91 : gpointer result {nullptr};
3237 :
3238 91 : g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3239 91 : g_return_val_if_fail (thunk, nullptr);
3240 :
3241 247 : for (auto child : GET_PRIVATE(acc)->children)
3242 : {
3243 87 : result = thunk (child, user_data);
3244 87 : if (result) break;
3245 :
3246 80 : result = gnc_account_foreach_descendant_until (child, thunk, user_data);
3247 80 : if (result) break;
3248 : }
3249 :
3250 91 : return result;
3251 : }
3252 :
3253 :
3254 : GNCAccountType
3255 16312 : xaccAccountGetType (const Account *acc)
3256 : {
3257 16312 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), ACCT_TYPE_NONE);
3258 16312 : return GET_PRIVATE(acc)->type;
3259 : }
3260 :
3261 : static const char*
3262 40 : qofAccountGetTypeString (const Account *acc)
3263 : {
3264 40 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3265 40 : return xaccAccountTypeEnumAsString(GET_PRIVATE(acc)->type);
3266 : }
3267 :
3268 : static void
3269 42 : qofAccountSetType (Account *acc, const char *type_string)
3270 : {
3271 42 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
3272 42 : g_return_if_fail(type_string);
3273 42 : xaccAccountSetType(acc, xaccAccountStringToEnum(type_string));
3274 : }
3275 :
3276 : const char *
3277 72633 : xaccAccountGetName (const Account *acc)
3278 : {
3279 72633 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3280 72633 : return GET_PRIVATE(acc)->accountName;
3281 : }
3282 :
3283 : std::vector<const Account*>
3284 10403 : gnc_account_get_all_parents (const Account *account)
3285 : {
3286 10403 : std::vector<const Account*> rv;
3287 38782 : for (auto a = account; !gnc_account_is_root (a); a = gnc_account_get_parent (a))
3288 28379 : rv.push_back (a);
3289 10403 : return rv;
3290 0 : }
3291 :
3292 : gchar *
3293 10186 : gnc_account_get_full_name(const Account *account)
3294 : {
3295 : /* So much for hardening the API. Too many callers to this function don't
3296 : * bother to check if they have a non-nullptr pointer before calling. */
3297 10186 : if (nullptr == account)
3298 1 : return g_strdup("");
3299 :
3300 : /* errors */
3301 10185 : g_return_val_if_fail(GNC_IS_ACCOUNT(account), g_strdup(""));
3302 :
3303 10185 : auto path{gnc_account_get_all_parents (account)};
3304 10185 : auto seps_size{path.empty() ? 0 : strlen (account_separator) * (path.size() - 1)};
3305 10185 : auto alloc_size{std::accumulate (path.begin(), path.end(), seps_size,
3306 27879 : [](auto sum, auto acc)
3307 27879 : { return sum + strlen (xaccAccountGetName (acc)); })};
3308 10185 : auto rv = g_new (char, alloc_size + 1);
3309 10185 : auto p = rv;
3310 :
3311 10185 : std::for_each (path.rbegin(), path.rend(),
3312 27879 : [&p, rv](auto a)
3313 : {
3314 27879 : if (p != rv)
3315 17696 : p = stpcpy (p, account_separator);
3316 27879 : p = stpcpy (p, xaccAccountGetName (a));
3317 27879 : });
3318 10185 : *p = '\0';
3319 :
3320 10185 : return rv;
3321 10185 : }
3322 :
3323 : const char *
3324 1982 : xaccAccountGetCode (const Account *acc)
3325 : {
3326 1982 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3327 1982 : return GET_PRIVATE(acc)->accountCode;
3328 : }
3329 :
3330 : const char *
3331 1699 : xaccAccountGetDescription (const Account *acc)
3332 : {
3333 1699 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3334 1699 : return GET_PRIVATE(acc)->description;
3335 : }
3336 :
3337 : const char *
3338 8 : xaccAccountGetColor (const Account *acc)
3339 : {
3340 24 : return get_kvp_string_path (acc, {"color"});
3341 : }
3342 :
3343 : const char *
3344 6 : xaccAccountGetFilter (const Account *acc)
3345 : {
3346 18 : return get_kvp_string_path (acc, {"filter"});
3347 : }
3348 :
3349 : const char *
3350 6 : xaccAccountGetSortOrder (const Account *acc)
3351 : {
3352 18 : return get_kvp_string_path (acc, {"sort-order"});
3353 : }
3354 :
3355 : gboolean
3356 3 : xaccAccountGetSortReversed (const Account *acc)
3357 : {
3358 9 : return get_kvp_boolean_path (acc, {"sort-reversed"});
3359 : }
3360 :
3361 : const char *
3362 1339 : xaccAccountGetNotes (const Account *acc)
3363 : {
3364 4017 : return get_kvp_string_path (acc, {"notes"});
3365 : }
3366 :
3367 : const char *
3368 81 : xaccAccountGetOnlineID (const Account *acc)
3369 : {
3370 81 : g_return_val_if_fail (GNC_IS_ACCOUNT(acc), nullptr);
3371 243 : return get_kvp_string_path (acc, {KEY_ONLINE_ID});
3372 81 : }
3373 :
3374 : Account*
3375 3 : xaccAccountGetAssociatedAccount (const Account *acc, const char *tag)
3376 : {
3377 3 : g_return_val_if_fail (tag && *tag, nullptr);
3378 :
3379 15 : return get_kvp_account_path (acc, {"associated-account", tag});
3380 6 : }
3381 :
3382 :
3383 : gnc_commodity *
3384 1363 : DxaccAccountGetCurrency (const Account *acc)
3385 : {
3386 4089 : if (auto s = get_kvp_string_path (acc, {"old-currency"}))
3387 : {
3388 0 : auto table = gnc_commodity_table_get_table (qof_instance_get_book(acc));
3389 0 : return gnc_commodity_table_lookup_unique (table, s);
3390 : }
3391 :
3392 1363 : return nullptr;
3393 : }
3394 :
3395 : gnc_commodity *
3396 84183 : xaccAccountGetCommodity (const Account *acc)
3397 : {
3398 84183 : if (!GNC_IS_ACCOUNT(acc))
3399 0 : return nullptr;
3400 84183 : return GET_PRIVATE(acc)->commodity;
3401 : }
3402 :
3403 427 : gnc_commodity * gnc_account_get_currency_or_parent(const Account* account)
3404 : {
3405 427 : g_return_val_if_fail (GNC_IS_ACCOUNT (account), nullptr);
3406 :
3407 856 : for (auto acc = account; acc; acc = gnc_account_get_parent (acc))
3408 853 : if (auto comm = xaccAccountGetCommodity (acc); gnc_commodity_is_currency (comm))
3409 424 : return comm;
3410 :
3411 3 : return nullptr; // no suitable commodity found.
3412 : }
3413 :
3414 : /********************************************************************\
3415 : \********************************************************************/
3416 : void
3417 1 : gnc_account_set_start_balance (Account *acc, const gnc_numeric start_baln)
3418 : {
3419 : AccountPrivate *priv;
3420 :
3421 1 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
3422 :
3423 1 : priv = GET_PRIVATE(acc);
3424 1 : priv->starting_balance = start_baln;
3425 1 : priv->balance_dirty = TRUE;
3426 : }
3427 :
3428 : void
3429 0 : gnc_account_set_start_cleared_balance (Account *acc,
3430 : const gnc_numeric start_baln)
3431 : {
3432 : AccountPrivate *priv;
3433 :
3434 0 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
3435 :
3436 0 : priv = GET_PRIVATE(acc);
3437 0 : priv->starting_cleared_balance = start_baln;
3438 0 : priv->balance_dirty = TRUE;
3439 : }
3440 :
3441 : void
3442 0 : gnc_account_set_start_reconciled_balance (Account *acc,
3443 : const gnc_numeric start_baln)
3444 : {
3445 : AccountPrivate *priv;
3446 :
3447 0 : g_return_if_fail(GNC_IS_ACCOUNT(acc));
3448 :
3449 0 : priv = GET_PRIVATE(acc);
3450 0 : priv->starting_reconciled_balance = start_baln;
3451 0 : priv->balance_dirty = TRUE;
3452 : }
3453 :
3454 : gnc_numeric
3455 68 : xaccAccountGetBalance (const Account *acc)
3456 : {
3457 68 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3458 68 : return GET_PRIVATE(acc)->balance;
3459 : }
3460 :
3461 : gnc_numeric
3462 38 : xaccAccountGetClearedBalance (const Account *acc)
3463 : {
3464 38 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3465 38 : return GET_PRIVATE(acc)->cleared_balance;
3466 : }
3467 :
3468 : gnc_numeric
3469 20 : xaccAccountGetReconciledBalance (const Account *acc)
3470 : {
3471 20 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3472 20 : return GET_PRIVATE(acc)->reconciled_balance;
3473 : }
3474 :
3475 : gnc_numeric
3476 2 : xaccAccountGetProjectedMinimumBalance (const Account *acc)
3477 : {
3478 2 : auto today{gnc_time64_get_today_end()};
3479 2 : std::optional<gnc_numeric> minimum;
3480 :
3481 6 : auto before_today_end = [&minimum, today](const Split *s) -> bool
3482 : {
3483 6 : auto bal{xaccSplitGetBalance(s)};
3484 6 : if (!minimum || gnc_numeric_compare (bal, *minimum) < 0)
3485 3 : minimum = bal;
3486 12 : return (xaccTransGetDate(xaccSplitGetParent(s)) < today);
3487 2 : };
3488 : // scan to find today's split, but we're really interested in the
3489 : // minimum balance
3490 2 : [[maybe_unused]] auto todays_split = gnc_account_find_split (acc, before_today_end, true);
3491 4 : return minimum ? *minimum : gnc_numeric_zero();
3492 : }
3493 :
3494 : gboolean
3495 0 : xaccAccountHasStockSplit (const Account *acc)
3496 : {
3497 0 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), false);
3498 0 : return GET_PRIVATE(acc)->has_stock_split;
3499 : }
3500 :
3501 :
3502 : /********************************************************************\
3503 : \********************************************************************/
3504 :
3505 : static gnc_numeric
3506 2936 : GetBalanceAsOfDate (Account *acc, time64 date, std::function<gnc_numeric(Split*)> split_to_numeric)
3507 : {
3508 2936 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3509 :
3510 2936 : xaccAccountSortSplits (acc, TRUE); /* just in case, normally a noop */
3511 2936 : xaccAccountRecomputeBalance (acc); /* just in case, normally a noop */
3512 :
3513 4204 : auto is_before_date = [date](auto s) -> bool
3514 4204 : { return xaccTransGetDate(xaccSplitGetParent(s)) < date; };
3515 :
3516 2936 : auto latest_split{gnc_account_find_split (acc, is_before_date, true)};
3517 2936 : return latest_split ? split_to_numeric (latest_split) : gnc_numeric_zero();
3518 : }
3519 :
3520 : gnc_numeric
3521 374 : xaccAccountGetBalanceAsOfDate (Account *acc, time64 date)
3522 : {
3523 374 : return GetBalanceAsOfDate (acc, date, xaccSplitGetBalance);
3524 : }
3525 :
3526 : static gnc_numeric
3527 0 : xaccAccountGetNoclosingBalanceAsOfDate (Account *acc, time64 date)
3528 : {
3529 0 : return GetBalanceAsOfDate (acc, date, xaccSplitGetNoclosingBalance);
3530 : }
3531 :
3532 : gnc_numeric
3533 0 : xaccAccountGetReconciledBalanceAsOfDate (Account *acc, time64 date)
3534 : {
3535 0 : return GetBalanceAsOfDate (acc, date, xaccSplitGetReconciledBalance);
3536 : }
3537 :
3538 : /*
3539 : * Originally gsr_account_present_balance in gnc-split-reg.c
3540 : */
3541 : gnc_numeric
3542 1 : xaccAccountGetPresentBalance (const Account *acc)
3543 : {
3544 1 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3545 :
3546 1 : return xaccAccountGetBalanceAsOfDate (GNC_ACCOUNT (acc),
3547 1 : gnc_time64_get_today_end ());
3548 : }
3549 :
3550 :
3551 : /********************************************************************\
3552 : \********************************************************************/
3553 : /* XXX TODO: These 'GetBal' routines should be moved to some
3554 : * utility area outside of the core account engine area.
3555 : */
3556 :
3557 : /*
3558 : * Convert a balance from one currency to another.
3559 : */
3560 : gnc_numeric
3561 0 : xaccAccountConvertBalanceToCurrency(const Account *acc, /* for book */
3562 : gnc_numeric balance,
3563 : const gnc_commodity *balance_currency,
3564 : const gnc_commodity *new_currency)
3565 : {
3566 : QofBook *book;
3567 : GNCPriceDB *pdb;
3568 :
3569 0 : if (gnc_numeric_zero_p (balance) ||
3570 0 : gnc_commodity_equiv (balance_currency, new_currency))
3571 0 : return balance;
3572 :
3573 0 : book = gnc_account_get_book (acc);
3574 0 : pdb = gnc_pricedb_get_db (book);
3575 :
3576 0 : balance = gnc_pricedb_convert_balance_latest_price(
3577 : pdb, balance, balance_currency, new_currency);
3578 :
3579 0 : return balance;
3580 : }
3581 :
3582 : /*
3583 : * Convert a balance from one currency to another with price of
3584 : * a given date.
3585 : */
3586 : gnc_numeric
3587 732 : xaccAccountConvertBalanceToCurrencyAsOfDate(const Account *acc, /* for book */
3588 : gnc_numeric balance,
3589 : const gnc_commodity *balance_currency,
3590 : const gnc_commodity *new_currency,
3591 : time64 date)
3592 : {
3593 : QofBook *book;
3594 : GNCPriceDB *pdb;
3595 :
3596 884 : if (gnc_numeric_zero_p (balance) ||
3597 152 : gnc_commodity_equiv (balance_currency, new_currency))
3598 732 : return balance;
3599 :
3600 0 : book = gnc_account_get_book (acc);
3601 0 : pdb = gnc_pricedb_get_db (book);
3602 :
3603 0 : balance = gnc_pricedb_convert_balance_nearest_before_price_t64 (
3604 : pdb, balance, balance_currency, new_currency, date);
3605 :
3606 0 : return balance;
3607 : }
3608 :
3609 : /*
3610 : * Given an account and a GetBalanceFn pointer, extract the requested
3611 : * balance from the account and then convert it to the desired
3612 : * currency.
3613 : */
3614 : static gnc_numeric
3615 0 : xaccAccountGetXxxBalanceInCurrency (const Account *acc,
3616 : xaccGetBalanceFn fn,
3617 : const gnc_commodity *report_currency)
3618 : {
3619 : AccountPrivate *priv;
3620 : gnc_numeric balance;
3621 :
3622 0 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3623 0 : g_return_val_if_fail(fn, gnc_numeric_zero());
3624 0 : g_return_val_if_fail(GNC_IS_COMMODITY(report_currency), gnc_numeric_zero());
3625 :
3626 0 : priv = GET_PRIVATE(acc);
3627 0 : balance = fn(acc);
3628 0 : balance = xaccAccountConvertBalanceToCurrency(acc, balance,
3629 0 : priv->commodity,
3630 : report_currency);
3631 0 : return balance;
3632 : }
3633 :
3634 : static gnc_numeric
3635 0 : xaccAccountGetXxxBalanceAsOfDateInCurrency(Account *acc, time64 date,
3636 : xaccGetBalanceAsOfDateFn fn,
3637 : const gnc_commodity *report_commodity)
3638 : {
3639 : AccountPrivate *priv;
3640 :
3641 0 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), gnc_numeric_zero());
3642 0 : g_return_val_if_fail(fn, gnc_numeric_zero());
3643 0 : g_return_val_if_fail(GNC_IS_COMMODITY(report_commodity), gnc_numeric_zero());
3644 :
3645 0 : priv = GET_PRIVATE(acc);
3646 0 : return xaccAccountConvertBalanceToCurrencyAsOfDate(
3647 0 : acc, fn(acc, date), priv->commodity, report_commodity, date);
3648 : }
3649 :
3650 : /*
3651 : * Data structure used to pass various arguments into the following fn.
3652 : */
3653 : typedef struct
3654 : {
3655 : const gnc_commodity *currency;
3656 : gnc_numeric balance;
3657 : xaccGetBalanceFn fn;
3658 : xaccGetBalanceAsOfDateFn asOfDateFn;
3659 : time64 date;
3660 : } CurrencyBalance;
3661 :
3662 :
3663 : /*
3664 : * A helper function for iterating over all the accounts in a list or
3665 : * tree. This function is called once per account, and sums up the
3666 : * values of all these accounts.
3667 : */
3668 : static void
3669 0 : xaccAccountBalanceHelper (Account *acc, gpointer data)
3670 : {
3671 0 : CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3672 : gnc_numeric balance;
3673 :
3674 0 : if (!cb->fn || !cb->currency)
3675 0 : return;
3676 0 : balance = xaccAccountGetXxxBalanceInCurrency (acc, cb->fn, cb->currency);
3677 0 : cb->balance = gnc_numeric_add (cb->balance, balance,
3678 0 : gnc_commodity_get_fraction (cb->currency),
3679 : GNC_HOW_RND_ROUND_HALF_UP);
3680 : }
3681 :
3682 : static void
3683 0 : xaccAccountBalanceAsOfDateHelper (Account *acc, gpointer data)
3684 : {
3685 0 : CurrencyBalance *cb = static_cast<CurrencyBalance*>(data);
3686 : gnc_numeric balance;
3687 :
3688 0 : g_return_if_fail (cb->asOfDateFn && cb->currency);
3689 :
3690 0 : balance = xaccAccountGetXxxBalanceAsOfDateInCurrency (
3691 : acc, cb->date, cb->asOfDateFn, cb->currency);
3692 0 : cb->balance = gnc_numeric_add (cb->balance, balance,
3693 0 : gnc_commodity_get_fraction (cb->currency),
3694 : GNC_HOW_RND_ROUND_HALF_UP);
3695 : }
3696 :
3697 :
3698 :
3699 : /*
3700 : * Common function that iterates recursively over all accounts below
3701 : * the specified account. It uses xaccAccountBalanceHelper to sum up
3702 : * the balances of all its children, and uses the specified function
3703 : * 'fn' for extracting the balance. This function may extract the
3704 : * current value, the reconciled value, etc.
3705 : *
3706 : * If 'report_commodity' is nullptr, just use the account's commodity.
3707 : * If 'include_children' is FALSE, this function doesn't recurse at all.
3708 : */
3709 : static gnc_numeric
3710 0 : xaccAccountGetXxxBalanceInCurrencyRecursive (const Account *acc,
3711 : xaccGetBalanceFn fn,
3712 : const gnc_commodity *report_commodity,
3713 : gboolean include_children)
3714 : {
3715 : gnc_numeric balance;
3716 :
3717 0 : if (!acc) return gnc_numeric_zero ();
3718 0 : if (!report_commodity)
3719 0 : report_commodity = xaccAccountGetCommodity (acc);
3720 0 : if (!report_commodity)
3721 0 : return gnc_numeric_zero();
3722 :
3723 0 : balance = xaccAccountGetXxxBalanceInCurrency (acc, fn, report_commodity);
3724 :
3725 : /* If needed, sum up the children converting to the *requested*
3726 : commodity. */
3727 0 : if (include_children)
3728 : {
3729 : #ifdef _MSC_VER
3730 : /* MSVC compiler: Somehow, the struct initialization containing a
3731 : gnc_numeric doesn't work. As an exception, we hand-initialize
3732 : that member afterwards. */
3733 : CurrencyBalance cb = { report_commodity, { 0 }, fn, nullptr, 0 };
3734 : cb.balance = balance;
3735 : #else
3736 0 : CurrencyBalance cb = { report_commodity, balance, fn, nullptr, 0 };
3737 : #endif
3738 :
3739 0 : gnc_account_foreach_descendant (acc, xaccAccountBalanceHelper, &cb);
3740 0 : balance = cb.balance;
3741 : }
3742 :
3743 0 : return balance;
3744 : }
3745 :
3746 : static gnc_numeric
3747 0 : xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3748 : Account *acc, time64 date, xaccGetBalanceAsOfDateFn fn,
3749 : const gnc_commodity *report_commodity, gboolean include_children)
3750 : {
3751 : gnc_numeric balance;
3752 :
3753 0 : g_return_val_if_fail(acc, gnc_numeric_zero());
3754 0 : if (!report_commodity)
3755 0 : report_commodity = xaccAccountGetCommodity (acc);
3756 0 : if (!report_commodity)
3757 0 : return gnc_numeric_zero();
3758 :
3759 0 : balance = xaccAccountGetXxxBalanceAsOfDateInCurrency(
3760 : acc, date, fn, report_commodity);
3761 :
3762 : /* If needed, sum up the children converting to the *requested*
3763 : commodity. */
3764 0 : if (include_children)
3765 : {
3766 : #ifdef _MSC_VER
3767 : /* MSVC compiler: Somehow, the struct initialization containing a
3768 : gnc_numeric doesn't work. As an exception, we hand-initialize
3769 : that member afterwards. */
3770 : CurrencyBalance cb = { report_commodity, 0, nullptr, fn, date };
3771 : cb.balance = balance;
3772 : #else
3773 0 : CurrencyBalance cb = { report_commodity, balance, nullptr, fn, date };
3774 : #endif
3775 :
3776 0 : gnc_account_foreach_descendant (acc, xaccAccountBalanceAsOfDateHelper, &cb);
3777 0 : balance = cb.balance;
3778 : }
3779 :
3780 0 : return balance;
3781 : }
3782 :
3783 : gnc_numeric
3784 0 : xaccAccountGetBalanceInCurrency (const Account *acc,
3785 : const gnc_commodity *report_commodity,
3786 : gboolean include_children)
3787 : {
3788 : gnc_numeric rc;
3789 0 : rc = xaccAccountGetXxxBalanceInCurrencyRecursive (
3790 : acc, xaccAccountGetBalance, report_commodity, include_children);
3791 0 : PINFO(" baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, rc.num, rc.denom);
3792 0 : return rc;
3793 : }
3794 :
3795 :
3796 : gnc_numeric
3797 0 : xaccAccountGetClearedBalanceInCurrency (const Account *acc,
3798 : const gnc_commodity *report_commodity,
3799 : gboolean include_children)
3800 : {
3801 0 : return xaccAccountGetXxxBalanceInCurrencyRecursive (
3802 : acc, xaccAccountGetClearedBalance, report_commodity,
3803 0 : include_children);
3804 : }
3805 :
3806 : gnc_numeric
3807 0 : xaccAccountGetReconciledBalanceInCurrency (const Account *acc,
3808 : const gnc_commodity *report_commodity,
3809 : gboolean include_children)
3810 : {
3811 0 : return xaccAccountGetXxxBalanceInCurrencyRecursive (
3812 : acc, xaccAccountGetReconciledBalance, report_commodity,
3813 0 : include_children);
3814 : }
3815 :
3816 : gnc_numeric
3817 0 : xaccAccountGetPresentBalanceInCurrency (const Account *acc,
3818 : const gnc_commodity *report_commodity,
3819 : gboolean include_children)
3820 : {
3821 0 : return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3822 : (Account*)acc, gnc_time64_get_today_end (), xaccAccountGetBalanceAsOfDate,
3823 : report_commodity,
3824 0 : include_children);
3825 : }
3826 :
3827 : gnc_numeric
3828 0 : xaccAccountGetProjectedMinimumBalanceInCurrency (
3829 : const Account *acc,
3830 : const gnc_commodity *report_commodity,
3831 : gboolean include_children)
3832 : {
3833 0 : return xaccAccountGetXxxBalanceInCurrencyRecursive (
3834 : acc, xaccAccountGetProjectedMinimumBalance, report_commodity,
3835 0 : include_children);
3836 : }
3837 :
3838 : gnc_numeric
3839 0 : xaccAccountGetBalanceAsOfDateInCurrency(
3840 : Account *acc, time64 date, gnc_commodity *report_commodity,
3841 : gboolean include_children)
3842 : {
3843 0 : return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive (
3844 : acc, date, xaccAccountGetBalanceAsOfDate, report_commodity,
3845 0 : include_children);
3846 : }
3847 :
3848 : gnc_numeric
3849 0 : xaccAccountGetNoclosingBalanceAsOfDateInCurrency(
3850 : Account *acc, time64 date, gnc_commodity *report_commodity,
3851 : gboolean include_children)
3852 : {
3853 : return xaccAccountGetXxxBalanceAsOfDateInCurrencyRecursive
3854 0 : (acc, date, xaccAccountGetNoclosingBalanceAsOfDate,
3855 0 : report_commodity, include_children);
3856 : }
3857 :
3858 : gnc_numeric
3859 0 : xaccAccountGetBalanceChangeForPeriod (Account *acc, time64 t1, time64 t2,
3860 : gboolean recurse)
3861 : {
3862 : gnc_numeric b1, b2;
3863 :
3864 0 : b1 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3865 0 : b2 = xaccAccountGetBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3866 0 : return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3867 : }
3868 :
3869 : gnc_numeric
3870 0 : xaccAccountGetNoclosingBalanceChangeForPeriod (Account *acc, time64 t1,
3871 : time64 t2, gboolean recurse)
3872 : {
3873 : gnc_numeric b1, b2;
3874 :
3875 0 : b1 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t1, nullptr, recurse);
3876 0 : b2 = xaccAccountGetNoclosingBalanceAsOfDateInCurrency(acc, t2, nullptr, recurse);
3877 0 : return gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3878 : }
3879 :
3880 : typedef struct
3881 : {
3882 : const gnc_commodity *currency;
3883 : gnc_numeric balanceChange;
3884 : time64 t1;
3885 : time64 t2;
3886 : } CurrencyBalanceChange;
3887 :
3888 : static void
3889 732 : xaccAccountBalanceChangeHelper (Account *acc, gpointer data)
3890 : {
3891 732 : CurrencyBalanceChange *cbdiff = static_cast<CurrencyBalanceChange*>(data);
3892 :
3893 : gnc_numeric b1, b2;
3894 732 : b1 = GetBalanceAsOfDate(acc, cbdiff->t1, xaccSplitGetNoclosingBalance);
3895 732 : b2 = GetBalanceAsOfDate(acc, cbdiff->t2, xaccSplitGetNoclosingBalance);
3896 732 : gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3897 732 : gnc_numeric balanceChange_conv = xaccAccountConvertBalanceToCurrencyAsOfDate(acc, balanceChange, xaccAccountGetCommodity(acc), cbdiff->currency, cbdiff->t2);
3898 732 : cbdiff->balanceChange = gnc_numeric_add (cbdiff->balanceChange, balanceChange_conv,
3899 732 : gnc_commodity_get_fraction (cbdiff->currency),
3900 : GNC_HOW_RND_ROUND_HALF_UP);
3901 732 : }
3902 :
3903 : gnc_numeric
3904 549 : xaccAccountGetNoclosingBalanceChangeInCurrencyForPeriod (Account *acc, time64 t1,
3905 : time64 t2, gboolean recurse)
3906 : {
3907 :
3908 :
3909 : gnc_numeric b1, b2;
3910 549 : b1 = GetBalanceAsOfDate(acc, t1, xaccSplitGetNoclosingBalance);
3911 549 : b2 = GetBalanceAsOfDate(acc, t2, xaccSplitGetNoclosingBalance);
3912 549 : gnc_numeric balanceChange = gnc_numeric_sub(b2, b1, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED);
3913 :
3914 549 : gnc_commodity *report_commodity = xaccAccountGetCommodity(acc);
3915 549 : CurrencyBalanceChange cbdiff = { report_commodity, balanceChange, t1, t2 };
3916 :
3917 549 : if(recurse)
3918 : {
3919 549 : gnc_account_foreach_descendant (acc, xaccAccountBalanceChangeHelper, &cbdiff);
3920 549 : balanceChange = cbdiff.balanceChange;
3921 : }
3922 549 : return balanceChange;
3923 : }
3924 :
3925 : /********************************************************************\
3926 : \********************************************************************/
3927 :
3928 : const SplitsVec&
3929 9462 : xaccAccountGetSplits (const Account *account)
3930 : {
3931 9462 : static const SplitsVec empty;
3932 9462 : g_return_val_if_fail (GNC_IS_ACCOUNT(account), empty);
3933 9462 : return GET_PRIVATE(account)->splits;
3934 : }
3935 :
3936 : SplitList *
3937 1 : xaccAccountGetSplitList (const Account *acc)
3938 : {
3939 1 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3940 1 : auto priv{GET_PRIVATE(acc)};
3941 2 : return std::accumulate (priv->splits.rbegin(), priv->splits.rend(),
3942 1 : static_cast<GList*>(nullptr), g_list_prepend);
3943 : }
3944 :
3945 : size_t
3946 25 : xaccAccountGetSplitsSize (const Account *account)
3947 : {
3948 25 : g_return_val_if_fail (GNC_IS_ACCOUNT(account), 0);
3949 25 : return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0;
3950 : }
3951 :
3952 : gboolean
3953 0 : gnc_account_and_descendants_empty (Account *acc)
3954 : {
3955 0 : g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE);
3956 0 : auto priv = GET_PRIVATE (acc);
3957 0 : if (!priv->splits.empty()) return FALSE;
3958 0 : return std::all_of (priv->children.begin(), priv->children.end(),
3959 0 : gnc_account_and_descendants_empty);
3960 : }
3961 :
3962 : LotList *
3963 468 : xaccAccountGetLotList (const Account *acc)
3964 : {
3965 468 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3966 468 : return g_list_copy(GET_PRIVATE(acc)->lots);
3967 : }
3968 :
3969 : LotList *
3970 5 : xaccAccountFindOpenLots (const Account *acc,
3971 : gboolean (*match_func)(GNCLot *lot,
3972 : gpointer user_data),
3973 : gpointer user_data, GCompareFunc sort_func)
3974 : {
3975 : AccountPrivate *priv;
3976 : GList *lot_list;
3977 5 : GList *retval = nullptr;
3978 :
3979 5 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
3980 :
3981 5 : priv = GET_PRIVATE(acc);
3982 18 : for (lot_list = priv->lots; lot_list; lot_list = lot_list->next)
3983 : {
3984 13 : GNCLot *lot = static_cast<GNCLot*>(lot_list->data);
3985 :
3986 : /* If this lot is closed, then ignore it */
3987 13 : if (gnc_lot_is_closed (lot))
3988 5 : continue;
3989 :
3990 8 : if (match_func && !(match_func)(lot, user_data))
3991 2 : continue;
3992 :
3993 : /* Ok, this is a valid lot. Add it to our list of lots */
3994 6 : retval = g_list_prepend (retval, lot);
3995 : }
3996 :
3997 5 : if (sort_func)
3998 1 : retval = g_list_sort (retval, sort_func);
3999 :
4000 5 : return retval;
4001 : }
4002 :
4003 : gpointer
4004 66 : xaccAccountForEachLot(const Account *acc,
4005 : gpointer (*proc)(GNCLot *lot, void *data), void *data)
4006 : {
4007 66 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), nullptr);
4008 66 : g_return_val_if_fail(proc, nullptr);
4009 :
4010 827 : for (auto node = GET_PRIVATE(acc)->lots; node; node = node->next)
4011 762 : if (auto result = proc(GNC_LOT(node->data), data))
4012 1 : return result;
4013 :
4014 65 : return nullptr;
4015 : }
4016 :
4017 :
4018 : /********************************************************************\
4019 : \********************************************************************/
4020 :
4021 : /* These functions use interchange gint64 and gboolean. Is that right? */
4022 : gboolean
4023 1 : xaccAccountGetTaxRelated (const Account *acc)
4024 : {
4025 3 : return get_kvp_boolean_path(acc, {"tax-related"});
4026 : }
4027 :
4028 : void
4029 0 : xaccAccountSetTaxRelated (Account *acc, gboolean tax_related)
4030 : {
4031 0 : set_kvp_boolean_path(acc, {"tax-related"}, tax_related);
4032 0 : }
4033 :
4034 : const char *
4035 7 : xaccAccountGetTaxUSCode (const Account *acc)
4036 : {
4037 21 : return get_kvp_string_path (acc, {"tax-US", "code"});
4038 : }
4039 :
4040 : void
4041 5 : xaccAccountSetTaxUSCode (Account *acc, const char *code)
4042 : {
4043 10 : set_kvp_string_path (acc, {"tax-US", "code"}, code);
4044 5 : }
4045 :
4046 : const char *
4047 7 : xaccAccountGetTaxUSPayerNameSource (const Account *acc)
4048 : {
4049 21 : return get_kvp_string_path (acc, {"tax-US", "payer-name-source"});
4050 : }
4051 :
4052 : void
4053 5 : xaccAccountSetTaxUSPayerNameSource (Account *acc, const char *source)
4054 : {
4055 10 : set_kvp_string_path (acc, {"tax-US", "payer-name-source"}, source);
4056 5 : }
4057 :
4058 : gint64
4059 1 : xaccAccountGetTaxUSCopyNumber (const Account *acc)
4060 : {
4061 2 : auto copy_number = get_kvp_int64_path (acc, {"tax-US", "copy-number"});
4062 2 : return (copy_number && (*copy_number != 0)) ? *copy_number : 1;
4063 : }
4064 :
4065 : void
4066 0 : xaccAccountSetTaxUSCopyNumber (Account *acc, gint64 copy_number)
4067 : {
4068 0 : if (copy_number != 0)
4069 0 : set_kvp_int64_path (acc, {"tax-US", "copy-number"}, copy_number);
4070 : else
4071 : /* deletes KVP if it exists */
4072 0 : set_kvp_int64_path (acc, {"tax-US", "copy-number"}, std::nullopt);
4073 0 : }
4074 :
4075 : /*********************************************************************\
4076 : \ ********************************************************************/
4077 :
4078 :
4079 11 : const char *gnc_account_get_debit_string (GNCAccountType acct_type)
4080 : {
4081 11 : if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4082 0 : return _(dflt_acct_debit_str);
4083 :
4084 11 : auto result = gnc_acct_debit_strs.find(acct_type);
4085 11 : if (result != gnc_acct_debit_strs.end())
4086 11 : return _(result->second);
4087 : else
4088 0 : return _(dflt_acct_debit_str);
4089 : }
4090 :
4091 11 : const char *gnc_account_get_credit_string (GNCAccountType acct_type)
4092 : {
4093 11 : if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_ACCOUNTING_LABELS))
4094 0 : return _(dflt_acct_credit_str);
4095 :
4096 11 : auto result = gnc_acct_credit_strs.find(acct_type);
4097 11 : if (result != gnc_acct_credit_strs.end())
4098 11 : return _(result->second);
4099 : else
4100 0 : return _(dflt_acct_credit_str);
4101 : }
4102 :
4103 : /********************************************************************\
4104 : \********************************************************************/
4105 :
4106 : gboolean
4107 209 : xaccAccountGetPlaceholder (const Account *acc)
4108 : {
4109 627 : return get_kvp_boolean_path(acc, {"placeholder"});
4110 : }
4111 :
4112 : void
4113 127 : xaccAccountSetPlaceholder (Account *acc, gboolean val)
4114 : {
4115 254 : set_kvp_boolean_path(acc, {"placeholder"}, val);
4116 127 : }
4117 :
4118 : gboolean
4119 0 : xaccAccountGetAppendText (const Account *acc)
4120 : {
4121 0 : return get_kvp_boolean_path(acc, {"import-append-text"});
4122 : }
4123 :
4124 : void
4125 0 : xaccAccountSetAppendText (Account *acc, gboolean val)
4126 : {
4127 0 : set_kvp_boolean_path(acc, {"import-append-text"}, val);
4128 0 : }
4129 :
4130 : gboolean
4131 4 : xaccAccountGetIsOpeningBalance (const Account *acc)
4132 : {
4133 4 : g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
4134 4 : if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4135 1 : return false;
4136 :
4137 9 : return !g_strcmp0 (get_kvp_string_path (acc, {"equity-type"}), "opening-balance");
4138 : }
4139 :
4140 : void
4141 2 : xaccAccountSetIsOpeningBalance (Account *acc, gboolean val)
4142 : {
4143 2 : g_return_if_fail (GNC_IS_ACCOUNT(acc));
4144 2 : if (GET_PRIVATE(acc)->type != ACCT_TYPE_EQUITY)
4145 0 : return;
4146 6 : set_kvp_string_path(acc, {"equity-type"}, val ? "opening-balance" : nullptr);
4147 : }
4148 :
4149 : GNCPlaceholderType
4150 0 : xaccAccountGetDescendantPlaceholder (const Account *acc)
4151 : {
4152 0 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), PLACEHOLDER_NONE);
4153 0 : if (xaccAccountGetPlaceholder(acc)) return PLACEHOLDER_THIS;
4154 :
4155 0 : return gnc_account_foreach_descendant_until (acc, (AccountCb2)xaccAccountGetPlaceholder, nullptr)
4156 0 : ? PLACEHOLDER_CHILD : PLACEHOLDER_NONE;
4157 : }
4158 :
4159 : /********************************************************************\
4160 : \********************************************************************/
4161 :
4162 : gboolean
4163 0 : xaccAccountGetAutoInterest (const Account *acc)
4164 : {
4165 0 : return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"});
4166 0 : }
4167 :
4168 : void
4169 0 : xaccAccountSetAutoInterest (Account *acc, gboolean val)
4170 : {
4171 0 : set_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, "auto-interest-transfer"}, val);
4172 0 : }
4173 :
4174 : /********************************************************************\
4175 : \********************************************************************/
4176 :
4177 : gboolean
4178 43 : xaccAccountGetHidden (const Account *acc)
4179 : {
4180 129 : return get_kvp_boolean_path (acc, {"hidden"});
4181 : }
4182 :
4183 : void
4184 46 : xaccAccountSetHidden (Account *acc, gboolean val)
4185 : {
4186 92 : set_kvp_boolean_path (acc, {"hidden"}, val);
4187 46 : }
4188 :
4189 : gboolean
4190 0 : xaccAccountIsHidden (const Account *acc)
4191 : {
4192 : AccountPrivate *priv;
4193 :
4194 0 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4195 :
4196 0 : if (xaccAccountGetHidden(acc))
4197 0 : return TRUE;
4198 0 : priv = GET_PRIVATE(acc);
4199 0 : while ((acc = priv->parent) != nullptr)
4200 : {
4201 0 : priv = GET_PRIVATE(acc);
4202 0 : if (xaccAccountGetHidden(acc))
4203 0 : return TRUE;
4204 : }
4205 0 : return FALSE;
4206 : }
4207 :
4208 : /********************************************************************\
4209 : \********************************************************************/
4210 :
4211 : gboolean
4212 3 : xaccAccountHasAncestor (const Account *acc, const Account * ancestor)
4213 : {
4214 : const Account *parent;
4215 :
4216 3 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4217 3 : g_return_val_if_fail(GNC_IS_ACCOUNT(ancestor), FALSE);
4218 :
4219 3 : parent = acc;
4220 13 : while (parent && parent != ancestor)
4221 10 : parent = GET_PRIVATE(parent)->parent;
4222 :
4223 3 : return (parent == ancestor);
4224 : }
4225 :
4226 : /********************************************************************\
4227 : \********************************************************************/
4228 :
4229 : /* You must edit the functions in this block in tandem. KEEP THEM IN
4230 : SYNC! */
4231 :
4232 : #define GNC_RETURN_ENUM_AS_STRING(x) case (ACCT_TYPE_ ## x): return #x;
4233 :
4234 : const char *
4235 314 : xaccAccountTypeEnumAsString(GNCAccountType type)
4236 : {
4237 314 : switch (type)
4238 : {
4239 1 : GNC_RETURN_ENUM_AS_STRING(NONE);
4240 26 : GNC_RETURN_ENUM_AS_STRING(BANK);
4241 6 : GNC_RETURN_ENUM_AS_STRING(CASH);
4242 4 : GNC_RETURN_ENUM_AS_STRING(CREDIT);
4243 13 : GNC_RETURN_ENUM_AS_STRING(ASSET);
4244 16 : GNC_RETURN_ENUM_AS_STRING(LIABILITY);
4245 2 : GNC_RETURN_ENUM_AS_STRING(STOCK);
4246 4 : GNC_RETURN_ENUM_AS_STRING(MUTUAL);
4247 4 : GNC_RETURN_ENUM_AS_STRING(CURRENCY);
4248 26 : GNC_RETURN_ENUM_AS_STRING(INCOME);
4249 164 : GNC_RETURN_ENUM_AS_STRING(EXPENSE);
4250 11 : GNC_RETURN_ENUM_AS_STRING(EQUITY);
4251 4 : GNC_RETURN_ENUM_AS_STRING(RECEIVABLE);
4252 3 : GNC_RETURN_ENUM_AS_STRING(PAYABLE);
4253 24 : GNC_RETURN_ENUM_AS_STRING(ROOT);
4254 1 : GNC_RETURN_ENUM_AS_STRING(TRADING);
4255 1 : GNC_RETURN_ENUM_AS_STRING(CHECKING);
4256 1 : GNC_RETURN_ENUM_AS_STRING(SAVINGS);
4257 1 : GNC_RETURN_ENUM_AS_STRING(MONEYMRKT);
4258 1 : GNC_RETURN_ENUM_AS_STRING(CREDITLINE);
4259 1 : default:
4260 1 : PERR ("asked to translate unknown account type %d.\n", type);
4261 1 : break;
4262 : }
4263 1 : return(nullptr);
4264 : }
4265 :
4266 : #undef GNC_RETURN_ENUM_AS_STRING
4267 :
4268 : #define GNC_RETURN_ON_MATCH(x) \
4269 : if(g_strcmp0(#x, (str)) == 0) { *type = ACCT_TYPE_ ## x; return(TRUE); }
4270 :
4271 : gboolean
4272 1293 : xaccAccountStringToType(const char* str, GNCAccountType *type)
4273 : {
4274 :
4275 1293 : GNC_RETURN_ON_MATCH(NONE);
4276 1292 : GNC_RETURN_ON_MATCH(BANK);
4277 1202 : GNC_RETURN_ON_MATCH(CASH);
4278 1186 : GNC_RETURN_ON_MATCH(CREDIT);
4279 1169 : GNC_RETURN_ON_MATCH(ASSET);
4280 1091 : GNC_RETURN_ON_MATCH(LIABILITY);
4281 1027 : GNC_RETURN_ON_MATCH(STOCK);
4282 989 : GNC_RETURN_ON_MATCH(MUTUAL);
4283 962 : GNC_RETURN_ON_MATCH(CURRENCY);
4284 953 : GNC_RETURN_ON_MATCH(INCOME);
4285 806 : GNC_RETURN_ON_MATCH(EXPENSE);
4286 141 : GNC_RETURN_ON_MATCH(EQUITY);
4287 99 : GNC_RETURN_ON_MATCH(RECEIVABLE);
4288 91 : GNC_RETURN_ON_MATCH(PAYABLE);
4289 86 : GNC_RETURN_ON_MATCH(ROOT);
4290 7 : GNC_RETURN_ON_MATCH(TRADING);
4291 6 : GNC_RETURN_ON_MATCH(CHECKING);
4292 5 : GNC_RETURN_ON_MATCH(SAVINGS);
4293 4 : GNC_RETURN_ON_MATCH(MONEYMRKT);
4294 3 : GNC_RETURN_ON_MATCH(CREDITLINE);
4295 :
4296 2 : PERR("asked to translate unknown account type string %s.\n",
4297 : str ? str : "(null)");
4298 :
4299 2 : return(FALSE);
4300 : }
4301 :
4302 : #undef GNC_RETURN_ON_MATCH
4303 :
4304 : /* impedance mismatch is a source of loss */
4305 : GNCAccountType
4306 62 : xaccAccountStringToEnum(const char* str)
4307 : {
4308 : GNCAccountType type;
4309 : gboolean rc;
4310 62 : rc = xaccAccountStringToType(str, &type);
4311 62 : if (FALSE == rc) return ACCT_TYPE_INVALID;
4312 62 : return type;
4313 : }
4314 :
4315 : /********************************************************************\
4316 : \********************************************************************/
4317 :
4318 : static char const *
4319 : account_type_name[NUM_ACCOUNT_TYPES] =
4320 : {
4321 : N_("Bank"),
4322 : N_("Cash"),
4323 : N_("Asset"),
4324 : N_("Credit Card"),
4325 : N_("Liability"),
4326 : N_("Stock"),
4327 : N_("Mutual Fund"),
4328 : N_("Currency"),
4329 : N_("Income"),
4330 : N_("Expense"),
4331 : N_("Equity"),
4332 : N_("A/Receivable"),
4333 : N_("A/Payable"),
4334 : N_("Root"),
4335 : N_("Trading")
4336 : /*
4337 : N_("Checking"),
4338 : N_("Savings"),
4339 : N_("Money Market"),
4340 : N_("Credit Line")
4341 : */
4342 : };
4343 :
4344 : const char *
4345 1399 : xaccAccountGetTypeStr(GNCAccountType type)
4346 : {
4347 1399 : if (type < 0 || NUM_ACCOUNT_TYPES <= type ) return "";
4348 1399 : return _(account_type_name [type]);
4349 : }
4350 :
4351 : /********************************************************************\
4352 : \********************************************************************/
4353 :
4354 : guint32
4355 14 : xaccAccountTypesCompatibleWith (GNCAccountType type)
4356 : {
4357 14 : switch (type)
4358 : {
4359 8 : case ACCT_TYPE_BANK:
4360 : case ACCT_TYPE_CASH:
4361 : case ACCT_TYPE_ASSET:
4362 : case ACCT_TYPE_CREDIT:
4363 : case ACCT_TYPE_LIABILITY:
4364 : case ACCT_TYPE_INCOME:
4365 : case ACCT_TYPE_EXPENSE:
4366 : case ACCT_TYPE_EQUITY:
4367 : return
4368 : (1 << ACCT_TYPE_BANK) |
4369 : (1 << ACCT_TYPE_CASH) |
4370 : (1 << ACCT_TYPE_ASSET) |
4371 : (1 << ACCT_TYPE_CREDIT) |
4372 : (1 << ACCT_TYPE_LIABILITY) |
4373 : (1 << ACCT_TYPE_INCOME) |
4374 : (1 << ACCT_TYPE_EXPENSE) |
4375 8 : (1 << ACCT_TYPE_EQUITY);
4376 3 : case ACCT_TYPE_STOCK:
4377 : case ACCT_TYPE_MUTUAL:
4378 : case ACCT_TYPE_CURRENCY:
4379 : return
4380 : (1 << ACCT_TYPE_STOCK) |
4381 : (1 << ACCT_TYPE_MUTUAL) |
4382 3 : (1 << ACCT_TYPE_CURRENCY);
4383 1 : case ACCT_TYPE_RECEIVABLE:
4384 1 : return (1 << ACCT_TYPE_RECEIVABLE);
4385 1 : case ACCT_TYPE_PAYABLE:
4386 1 : return (1 << ACCT_TYPE_PAYABLE);
4387 1 : case ACCT_TYPE_TRADING:
4388 1 : return (1 << ACCT_TYPE_TRADING);
4389 0 : default:
4390 0 : PERR("bad account type: %d", type);
4391 0 : return 0;
4392 : }
4393 : }
4394 : guint32
4395 282 : xaccParentAccountTypesCompatibleWith (GNCAccountType type)
4396 : {
4397 282 : switch (type)
4398 : {
4399 200 : case ACCT_TYPE_BANK:
4400 : case ACCT_TYPE_CASH:
4401 : case ACCT_TYPE_ASSET:
4402 : case ACCT_TYPE_STOCK:
4403 : case ACCT_TYPE_MUTUAL:
4404 : case ACCT_TYPE_CURRENCY:
4405 : case ACCT_TYPE_CREDIT:
4406 : case ACCT_TYPE_LIABILITY:
4407 : case ACCT_TYPE_RECEIVABLE:
4408 : case ACCT_TYPE_PAYABLE:
4409 : return
4410 : (1 << ACCT_TYPE_BANK) |
4411 : (1 << ACCT_TYPE_CASH) |
4412 : (1 << ACCT_TYPE_ASSET) |
4413 : (1 << ACCT_TYPE_STOCK) |
4414 : (1 << ACCT_TYPE_MUTUAL) |
4415 : (1 << ACCT_TYPE_CURRENCY) |
4416 : (1 << ACCT_TYPE_CREDIT) |
4417 : (1 << ACCT_TYPE_LIABILITY) |
4418 : (1 << ACCT_TYPE_RECEIVABLE) |
4419 : (1 << ACCT_TYPE_PAYABLE) |
4420 200 : (1 << ACCT_TYPE_ROOT);
4421 40 : case ACCT_TYPE_INCOME:
4422 : case ACCT_TYPE_EXPENSE:
4423 : return
4424 : (1 << ACCT_TYPE_INCOME) |
4425 : (1 << ACCT_TYPE_EXPENSE) |
4426 40 : (1 << ACCT_TYPE_ROOT);
4427 20 : case ACCT_TYPE_EQUITY:
4428 : return
4429 : (1 << ACCT_TYPE_EQUITY) |
4430 20 : (1 << ACCT_TYPE_ROOT);
4431 20 : case ACCT_TYPE_TRADING:
4432 : return
4433 : (1 << ACCT_TYPE_TRADING) |
4434 20 : (1 << ACCT_TYPE_ROOT);
4435 2 : default:
4436 2 : PERR("bad account type: %d", type);
4437 2 : return 0;
4438 : }
4439 : }
4440 :
4441 : gboolean
4442 280 : xaccAccountTypesCompatible (GNCAccountType parent_type,
4443 : GNCAccountType child_type)
4444 : {
4445 : /* ACCT_TYPE_NONE isn't compatible with anything, even ACCT_TYPE_NONE. */
4446 280 : if (parent_type == ACCT_TYPE_NONE || child_type == ACCT_TYPE_NONE)
4447 14 : return FALSE;
4448 :
4449 : /* ACCT_TYPE_ROOT can't have a parent account, and asking will raise
4450 : * an error. */
4451 266 : if (child_type == ACCT_TYPE_ROOT)
4452 0 : return FALSE;
4453 :
4454 266 : return ((xaccParentAccountTypesCompatibleWith (child_type) &
4455 266 : (1 << parent_type))
4456 266 : != 0);
4457 : }
4458 :
4459 : guint32
4460 0 : xaccAccountTypesValid(void)
4461 : {
4462 0 : guint32 mask = (1 << NUM_ACCOUNT_TYPES) - 1;
4463 0 : mask &= ~((1 << ACCT_TYPE_CURRENCY) | /* DEPRECATED */
4464 : (1 << ACCT_TYPE_ROOT)); /* ROOT */
4465 :
4466 0 : return mask;
4467 : }
4468 :
4469 0 : gboolean xaccAccountIsAssetLiabType(GNCAccountType t)
4470 : {
4471 0 : switch (t)
4472 : {
4473 0 : case ACCT_TYPE_RECEIVABLE:
4474 : case ACCT_TYPE_PAYABLE:
4475 0 : return FALSE;
4476 0 : default:
4477 0 : return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
4478 0 : || xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
4479 : }
4480 : }
4481 :
4482 : GNCAccountType
4483 0 : xaccAccountTypeGetFundamental (GNCAccountType t)
4484 : {
4485 0 : switch (t)
4486 : {
4487 0 : case ACCT_TYPE_BANK:
4488 : case ACCT_TYPE_STOCK:
4489 : case ACCT_TYPE_MONEYMRKT:
4490 : case ACCT_TYPE_CHECKING:
4491 : case ACCT_TYPE_SAVINGS:
4492 : case ACCT_TYPE_MUTUAL:
4493 : case ACCT_TYPE_CURRENCY:
4494 : case ACCT_TYPE_CASH:
4495 : case ACCT_TYPE_ASSET:
4496 : case ACCT_TYPE_RECEIVABLE:
4497 0 : return ACCT_TYPE_ASSET;
4498 0 : case ACCT_TYPE_CREDIT:
4499 : case ACCT_TYPE_LIABILITY:
4500 : case ACCT_TYPE_PAYABLE:
4501 : case ACCT_TYPE_CREDITLINE:
4502 0 : return ACCT_TYPE_LIABILITY;
4503 0 : case ACCT_TYPE_INCOME:
4504 0 : return ACCT_TYPE_INCOME;
4505 0 : case ACCT_TYPE_EXPENSE:
4506 0 : return ACCT_TYPE_EXPENSE;
4507 0 : case ACCT_TYPE_EQUITY:
4508 0 : return ACCT_TYPE_EQUITY;
4509 0 : case ACCT_TYPE_TRADING:
4510 : default:
4511 0 : return ACCT_TYPE_NONE;
4512 : }
4513 : }
4514 :
4515 308 : gboolean xaccAccountIsAPARType(GNCAccountType t)
4516 : {
4517 308 : switch (t)
4518 : {
4519 123 : case ACCT_TYPE_RECEIVABLE:
4520 : case ACCT_TYPE_PAYABLE:
4521 123 : return TRUE;
4522 185 : default:
4523 185 : return FALSE;
4524 : }
4525 : }
4526 :
4527 0 : gboolean xaccAccountIsEquityType(GNCAccountType t)
4528 : {
4529 0 : switch (t)
4530 : {
4531 0 : case ACCT_TYPE_EQUITY:
4532 0 : return TRUE;
4533 0 : default:
4534 0 : return FALSE;
4535 : }
4536 : }
4537 :
4538 : gboolean
4539 549 : xaccAccountIsPriced(const Account *acc)
4540 : {
4541 : AccountPrivate *priv;
4542 :
4543 549 : g_return_val_if_fail(GNC_IS_ACCOUNT(acc), FALSE);
4544 :
4545 549 : priv = GET_PRIVATE(acc);
4546 1035 : return (priv->type == ACCT_TYPE_STOCK || priv->type == ACCT_TYPE_MUTUAL ||
4547 1035 : priv->type == ACCT_TYPE_CURRENCY);
4548 : }
4549 :
4550 : /********************************************************************\
4551 : \********************************************************************/
4552 :
4553 : gboolean
4554 1 : xaccAccountGetReconcileLastDate (const Account *acc, time64 *last_date)
4555 : {
4556 1 : gboolean retval = FALSE;
4557 4 : auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"});
4558 :
4559 1 : if (date)
4560 : {
4561 1 : if (last_date)
4562 1 : *last_date = *date;
4563 1 : retval = TRUE;
4564 : }
4565 1 : return retval;
4566 2 : }
4567 :
4568 : /********************************************************************\
4569 : \********************************************************************/
4570 :
4571 : void
4572 1 : xaccAccountSetReconcileLastDate (Account *acc, time64 last_date)
4573 : {
4574 5 : set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-date"}, last_date);
4575 3 : }
4576 :
4577 : /********************************************************************\
4578 : \********************************************************************/
4579 :
4580 : gboolean
4581 2 : xaccAccountGetReconcileLastInterval (const Account *acc,
4582 : int *months, int *days)
4583 : {
4584 2 : if (!acc) return FALSE;
4585 10 : auto m{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"})};
4586 10 : auto d{get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"})};
4587 2 : if (m && d)
4588 : {
4589 1 : if (months)
4590 1 : *months = *m;
4591 1 : if (days)
4592 1 : *days = *d;
4593 1 : return true;
4594 : }
4595 1 : return false;
4596 8 : }
4597 :
4598 : /********************************************************************\
4599 : \********************************************************************/
4600 :
4601 : void
4602 1 : xaccAccountSetReconcileLastInterval (Account *acc, int months, int days)
4603 : {
4604 6 : set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "months"}, months);
4605 6 : set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, "last-interval", "days"}, days);
4606 5 : }
4607 :
4608 : /********************************************************************\
4609 : \********************************************************************/
4610 :
4611 : gboolean
4612 1 : xaccAccountGetReconcilePostponeDate (const Account *acc, time64 *postpone_date)
4613 : {
4614 6 : if (auto date = get_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}))
4615 : {
4616 1 : if (postpone_date)
4617 1 : *postpone_date = *date;
4618 1 : return true;
4619 : }
4620 0 : return false;
4621 2 : }
4622 :
4623 : /********************************************************************\
4624 : \********************************************************************/
4625 :
4626 : void
4627 1 : xaccAccountSetReconcilePostponeDate (Account *acc, time64 postpone_date)
4628 : {
4629 6 : set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "date"}, postpone_date);
4630 3 : }
4631 :
4632 : /********************************************************************\
4633 : \********************************************************************/
4634 :
4635 : gboolean
4636 3 : xaccAccountGetReconcilePostponeBalance (const Account *acc,
4637 : gnc_numeric *balance)
4638 : {
4639 18 : if (auto bal = get_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}))
4640 : {
4641 1 : if (balance)
4642 1 : *balance = *bal;
4643 1 : return true;
4644 : }
4645 2 : return false;
4646 6 : }
4647 :
4648 : /********************************************************************\
4649 : \********************************************************************/
4650 :
4651 : void
4652 1 : xaccAccountSetReconcilePostponeBalance (Account *acc, gnc_numeric balance)
4653 : {
4654 6 : set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE, "balance"}, balance);
4655 3 : }
4656 :
4657 : /********************************************************************\
4658 :
4659 : \********************************************************************/
4660 :
4661 : void
4662 1 : xaccAccountClearReconcilePostpone (Account *acc)
4663 : {
4664 5 : set_kvp_gnc_numeric_path (acc, {KEY_RECONCILE_INFO, KEY_POSTPONE}, {});
4665 2 : }
4666 :
4667 : /********************************************************************\
4668 : \********************************************************************/
4669 :
4670 : const char *
4671 6 : xaccAccountGetLastNum (const Account *acc)
4672 : {
4673 18 : return get_kvp_string_path (acc, {"last-num"});
4674 : }
4675 :
4676 : /********************************************************************\
4677 : \********************************************************************/
4678 :
4679 : void
4680 385 : xaccAccountSetLastNum (Account *acc, const char *num)
4681 : {
4682 770 : set_kvp_string_path (acc, {"last-num"}, num);
4683 385 : }
4684 :
4685 :
4686 : /********************************************************************\
4687 : \********************************************************************/
4688 :
4689 : static bool
4690 8 : get_balance_limit (const Account* acc, const std::string& key, gnc_numeric* balance)
4691 : {
4692 32 : auto limit = get_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key});
4693 8 : if (limit)
4694 4 : *balance = gnc_numeric_create (limit->num, limit->denom);
4695 16 : return limit.has_value();
4696 8 : }
4697 :
4698 : static void
4699 6 : set_balance_limit (Account *acc, const std::string& key, std::optional<gnc_numeric> balance)
4700 : {
4701 6 : if (balance && gnc_numeric_check (*balance))
4702 2 : return;
4703 16 : set_kvp_gnc_numeric_path (acc, {KEY_BALANCE_LIMIT, key}, balance);
4704 4 : }
4705 :
4706 : gboolean
4707 4 : xaccAccountGetHigherBalanceLimit (const Account *acc,
4708 : gnc_numeric *balance)
4709 : {
4710 4 : return get_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4711 : }
4712 :
4713 : gboolean
4714 4 : xaccAccountGetLowerBalanceLimit (const Account *acc,
4715 : gnc_numeric *balance)
4716 : {
4717 4 : return get_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4718 : }
4719 :
4720 : void
4721 2 : xaccAccountSetHigherBalanceLimit (Account *acc, gnc_numeric balance)
4722 : {
4723 2 : set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, balance);
4724 2 : }
4725 :
4726 : void
4727 2 : xaccAccountSetLowerBalanceLimit (Account *acc, gnc_numeric balance)
4728 : {
4729 2 : set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, balance);
4730 2 : }
4731 :
4732 : void
4733 1 : xaccAccountClearHigherBalanceLimit (Account *acc)
4734 : {
4735 1 : set_balance_limit (acc, KEY_BALANCE_HIGHER_LIMIT_VALUE, {});
4736 1 : }
4737 :
4738 : void
4739 1 : xaccAccountClearLowerBalanceLimit (Account *acc)
4740 : {
4741 1 : set_balance_limit (acc, KEY_BALANCE_LOWER_LIMIT_VALUE, {});
4742 1 : }
4743 :
4744 : gboolean
4745 3 : xaccAccountGetIncludeSubAccountBalances (const Account *acc)
4746 : {
4747 12 : return get_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS});
4748 3 : }
4749 :
4750 : void
4751 2 : xaccAccountSetIncludeSubAccountBalances (Account *acc, gboolean inc_sub)
4752 : {
4753 8 : set_kvp_boolean_path (acc, {KEY_BALANCE_LIMIT, KEY_BALANCE_INCLUDE_SUB_ACCTS}, inc_sub);
4754 4 : }
4755 :
4756 : /********************************************************************\
4757 : \********************************************************************/
4758 :
4759 : static Account *
4760 5 : GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency)
4761 : {
4762 : char * accname;
4763 : Account * acc;
4764 :
4765 5 : g_return_val_if_fail (root, nullptr);
4766 :
4767 : /* build the account name */
4768 5 : if (!currency)
4769 : {
4770 0 : PERR ("No currency specified!");
4771 0 : return nullptr;
4772 : }
4773 :
4774 5 : accname = g_strconcat (_("Orphaned Gains"), "-",
4775 : gnc_commodity_get_mnemonic (currency), nullptr);
4776 :
4777 : /* See if we've got one of these going already ... */
4778 5 : acc = gnc_account_lookup_by_name(root, accname);
4779 :
4780 5 : if (acc == nullptr)
4781 : {
4782 : /* Guess not. We'll have to build one. */
4783 3 : acc = xaccMallocAccount (gnc_account_get_book(root));
4784 3 : xaccAccountBeginEdit (acc);
4785 3 : xaccAccountSetName (acc, accname);
4786 3 : xaccAccountSetCommodity (acc, currency);
4787 3 : xaccAccountSetType (acc, ACCT_TYPE_INCOME);
4788 3 : xaccAccountSetDescription (acc, _("Realized Gain/Loss"));
4789 3 : xaccAccountSetNotes (acc,
4790 3 : _("Realized Gains or Losses from "
4791 : "Commodity or Trading Accounts "
4792 : "that haven't been recorded elsewhere."));
4793 :
4794 : /* Hang the account off the root. */
4795 3 : gnc_account_append_child (root, acc);
4796 3 : xaccAccountCommitEdit (acc);
4797 : }
4798 :
4799 5 : g_free (accname);
4800 :
4801 5 : return acc;
4802 : }
4803 :
4804 : Account *
4805 6 : xaccAccountGainsAccount (Account *acc, gnc_commodity *curr)
4806 : {
4807 30 : Path path {KEY_LOT_MGMT, "gains-acct", gnc_commodity_get_unique_name (curr)};
4808 6 : auto gains_account = get_kvp_account_path (acc, path);
4809 :
4810 6 : if (gains_account == nullptr) /* No gains account for this currency */
4811 : {
4812 5 : gains_account = GetOrMakeOrphanAccount (gnc_account_get_root (acc), curr);
4813 5 : set_kvp_account_path (acc, path, gains_account);
4814 : }
4815 :
4816 6 : return gains_account;
4817 18 : }
4818 :
4819 : /********************************************************************\
4820 : \********************************************************************/
4821 :
4822 : void
4823 16 : dxaccAccountSetPriceSrc(Account *acc, const char *src)
4824 : {
4825 16 : if (!acc) return;
4826 :
4827 16 : if (xaccAccountIsPriced(acc))
4828 15 : set_kvp_string_path (acc, {"old-price-source"}, src);
4829 : }
4830 :
4831 : /********************************************************************\
4832 : \********************************************************************/
4833 :
4834 : const char*
4835 428 : dxaccAccountGetPriceSrc(const Account *acc)
4836 : {
4837 428 : if (!acc) return nullptr;
4838 :
4839 428 : if (!xaccAccountIsPriced(acc)) return nullptr;
4840 :
4841 57 : return get_kvp_string_path (acc, {"old-price-source"});
4842 : }
4843 :
4844 : /********************************************************************\
4845 : \********************************************************************/
4846 :
4847 : void
4848 16 : dxaccAccountSetQuoteTZ(Account *acc, const char *tz)
4849 : {
4850 16 : if (!acc) return;
4851 16 : if (!xaccAccountIsPriced(acc)) return;
4852 15 : set_kvp_string_path (acc, {"old-quote-tz"}, tz);
4853 : }
4854 :
4855 : /********************************************************************\
4856 : \********************************************************************/
4857 :
4858 : const char*
4859 4 : dxaccAccountGetQuoteTZ(const Account *acc)
4860 : {
4861 4 : if (!acc) return nullptr;
4862 4 : if (!xaccAccountIsPriced(acc)) return nullptr;
4863 12 : return get_kvp_string_path (acc, {"old-quote-tz"});
4864 : }
4865 :
4866 : /********************************************************************\
4867 : \********************************************************************/
4868 :
4869 : void
4870 1 : xaccAccountSetReconcileChildrenStatus(Account *acc, gboolean status)
4871 : {
4872 : /* Would have been nice to use G_TYPE_BOOLEAN, but the other
4873 : * boolean kvps save the value as "true" or "false" and that would
4874 : * be file-incompatible with this.
4875 : */
4876 5 : set_kvp_int64_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN}, status);
4877 2 : }
4878 :
4879 : /********************************************************************\
4880 : \********************************************************************/
4881 :
4882 : gboolean
4883 2 : xaccAccountGetReconcileChildrenStatus(const Account *acc)
4884 : {
4885 : /* access the account's kvp-data for status and return that, if no value
4886 : * is found then we can assume not to include the children, that being
4887 : * the default behaviour
4888 : */
4889 8 : return get_kvp_boolean_path (acc, {KEY_RECONCILE_INFO, KEY_INCLUDE_CHILDREN});
4890 2 : }
4891 :
4892 : /********************************************************************\
4893 : \********************************************************************/
4894 :
4895 : Split *
4896 2 : xaccAccountFindSplitByDesc(const Account *acc, const char *description)
4897 : {
4898 10 : auto has_description = [description](const Split* s) -> bool
4899 10 : { return !g_strcmp0 (description, xaccTransGetDescription (xaccSplitGetParent (s))); };
4900 2 : return gnc_account_find_split (acc, has_description, true);
4901 : }
4902 :
4903 : /* This routine is for finding a matching transaction in an account by
4904 : * matching on the description field. [CAS: The rest of this comment
4905 : * seems to belong somewhere else.] This routine is used for
4906 : * auto-filling in registers with a default leading account. The
4907 : * dest_trans is a transaction used for currency checking. */
4908 : Transaction *
4909 1 : xaccAccountFindTransByDesc(const Account *acc, const char *description)
4910 : {
4911 1 : auto split = xaccAccountFindSplitByDesc (acc, description);
4912 1 : return split ? xaccSplitGetParent (split) : nullptr;
4913 : }
4914 :
4915 : /* ================================================================ */
4916 : /* Concatenation, Merging functions */
4917 :
4918 : void
4919 1 : gnc_account_join_children (Account *to_parent, Account *from_parent)
4920 : {
4921 :
4922 : /* errors */
4923 1 : g_return_if_fail(GNC_IS_ACCOUNT(to_parent));
4924 1 : g_return_if_fail(GNC_IS_ACCOUNT(from_parent));
4925 :
4926 : /* optimizations */
4927 1 : auto from_priv = GET_PRIVATE(from_parent);
4928 1 : if (from_priv->children.empty())
4929 0 : return;
4930 :
4931 1 : ENTER (" ");
4932 1 : auto children = from_priv->children;
4933 4 : for (auto child : children)
4934 2 : gnc_account_append_child(to_parent, child);
4935 1 : LEAVE (" ");
4936 1 : }
4937 : /********************************************************************\
4938 : \********************************************************************/
4939 :
4940 : void
4941 3 : gnc_account_merge_children (Account *parent)
4942 : {
4943 3 : g_return_if_fail(GNC_IS_ACCOUNT(parent));
4944 :
4945 3 : auto ppriv = GET_PRIVATE(parent);
4946 24 : for (auto it_a = ppriv->children.begin(); it_a != ppriv->children.end(); it_a++)
4947 : {
4948 9 : auto acc_a = *it_a;
4949 9 : auto priv_a = GET_PRIVATE(acc_a);
4950 46 : for (auto it_b = std::next(it_a); it_b != ppriv->children.end(); it_b++)
4951 : {
4952 14 : auto acc_b = *it_b;
4953 14 : auto priv_b = GET_PRIVATE(acc_b);
4954 14 : if (0 != null_strcmp(priv_a->accountName, priv_b->accountName))
4955 13 : continue;
4956 1 : if (0 != null_strcmp(priv_a->accountCode, priv_b->accountCode))
4957 0 : continue;
4958 1 : if (0 != null_strcmp(priv_a->description, priv_b->description))
4959 0 : continue;
4960 1 : if (0 != null_strcmp(xaccAccountGetColor(acc_a),
4961 : xaccAccountGetColor(acc_b)))
4962 0 : continue;
4963 1 : if (!gnc_commodity_equiv(priv_a->commodity, priv_b->commodity))
4964 0 : continue;
4965 1 : if (0 != null_strcmp(xaccAccountGetNotes(acc_a),
4966 : xaccAccountGetNotes(acc_b)))
4967 0 : continue;
4968 1 : if (priv_a->type != priv_b->type)
4969 0 : continue;
4970 :
4971 : /* consolidate children */
4972 1 : if (!priv_b->children.empty())
4973 : {
4974 1 : auto work = priv_b->children;
4975 4 : for (auto w : work)
4976 2 : gnc_account_append_child (acc_a, w);
4977 :
4978 1 : qof_event_gen (&acc_a->inst, QOF_EVENT_MODIFY, nullptr);
4979 1 : qof_event_gen (&acc_b->inst, QOF_EVENT_MODIFY, nullptr);
4980 1 : }
4981 :
4982 : /* recurse to do the children's children */
4983 1 : gnc_account_merge_children (acc_a);
4984 :
4985 : /* consolidate transactions */
4986 1 : while (!priv_b->splits.empty())
4987 0 : xaccSplitSetAccount (priv_b->splits.front(), acc_a);
4988 :
4989 : /* move back one before removal. next iteration around the loop
4990 : * will get the node after node_b */
4991 : it_b--;
4992 :
4993 : /* The destroy function will remove from list -- node_a is ok,
4994 : * it's before node_b */
4995 1 : xaccAccountBeginEdit (acc_b);
4996 1 : xaccAccountDestroy (acc_b);
4997 : }
4998 : }
4999 : }
5000 :
5001 : /* ================================================================ */
5002 : /* Transaction Traversal functions */
5003 :
5004 :
5005 : static void
5006 23 : xaccSplitsBeginStagedTransactionTraversals (SplitsVec& splits)
5007 : {
5008 80 : for (auto s : splits)
5009 : {
5010 34 : Transaction *trans = s->parent;
5011 :
5012 34 : if (trans)
5013 34 : trans->marker = 0;
5014 : }
5015 23 : }
5016 :
5017 : /* original function */
5018 : void
5019 23 : xaccAccountBeginStagedTransactionTraversals (const Account *account)
5020 : {
5021 23 : if (!account)
5022 0 : return;
5023 23 : xaccSplitsBeginStagedTransactionTraversals(GET_PRIVATE (account)->splits);
5024 : }
5025 :
5026 : gboolean
5027 0 : xaccTransactionTraverse (Transaction *trans, int stage)
5028 : {
5029 0 : if (trans == nullptr) return FALSE;
5030 :
5031 0 : if (trans->marker < stage)
5032 : {
5033 0 : trans->marker = stage;
5034 0 : return TRUE;
5035 : }
5036 :
5037 0 : return FALSE;
5038 : }
5039 :
5040 : /* Replacement for xaccGroupBeginStagedTransactionTraversals */
5041 : void
5042 55 : gnc_account_tree_begin_staged_transaction_traversals (Account *account)
5043 : {
5044 1260 : auto do_one_account = [](auto acc)
5045 1260 : { gnc_account_foreach_split (acc, [](auto s){ s->parent->marker = 0; }); };
5046 55 : gnc_account_foreach_descendant (account, do_one_account);
5047 55 : }
5048 :
5049 : int
5050 23 : xaccAccountStagedTransactionTraversal (const Account *acc,
5051 : unsigned int stage,
5052 : TransactionCallback thunk,
5053 : void *cb_data)
5054 : {
5055 23 : if (!acc) return 0;
5056 :
5057 : // iterate on copy of splits. some callers modify the splitsvec.
5058 23 : auto splits = GET_PRIVATE(acc)->splits;
5059 73 : for (auto s : splits)
5060 : {
5061 28 : auto trans = s->parent;
5062 28 : if (trans && (trans->marker < stage))
5063 : {
5064 20 : trans->marker = stage;
5065 20 : if (thunk)
5066 : {
5067 20 : auto retval = thunk(trans, cb_data);
5068 20 : if (retval) return retval;
5069 : }
5070 : }
5071 : }
5072 :
5073 22 : return 0;
5074 23 : }
5075 :
5076 : int
5077 1307 : gnc_account_tree_staged_transaction_traversal (const Account *acc,
5078 : unsigned int stage,
5079 : TransactionCallback thunk,
5080 : void *cb_data)
5081 : {
5082 : const AccountPrivate *priv;
5083 : Transaction *trans;
5084 : int retval;
5085 :
5086 1307 : if (!acc) return 0;
5087 :
5088 : /* depth first traversal */
5089 1307 : priv = GET_PRIVATE(acc);
5090 3863 : for (auto acc_p : priv->children)
5091 : {
5092 1252 : retval = gnc_account_tree_staged_transaction_traversal(acc_p, stage, thunk, cb_data);
5093 1252 : if (retval) return retval;
5094 : }
5095 :
5096 : /* Now this account */
5097 4697 : for (auto s : priv->splits)
5098 : {
5099 2090 : trans = s->parent;
5100 2090 : if (trans && (trans->marker < stage))
5101 : {
5102 1013 : trans->marker = stage;
5103 1013 : if (thunk)
5104 : {
5105 1013 : retval = thunk(trans, cb_data);
5106 1013 : if (retval) return retval;
5107 : }
5108 : }
5109 : }
5110 :
5111 1303 : return 0;
5112 : }
5113 :
5114 : time64
5115 0 : gnc_account_get_earliest_date (const Account* account)
5116 : {
5117 0 : g_return_val_if_fail (GNC_IS_ACCOUNT(account), INT64_MAX);
5118 0 : const auto& splits = xaccAccountGetSplits (account);
5119 0 : return splits.empty() ? INT64_MAX : xaccTransGetDate (xaccSplitGetParent (splits.front()));
5120 : }
5121 :
5122 : /********************************************************************\
5123 : \********************************************************************/
5124 :
5125 : int
5126 55 : xaccAccountTreeForEachTransaction (Account *acc,
5127 : int (*proc)(Transaction *t, void *data),
5128 : void *data)
5129 : {
5130 55 : if (!acc || !proc) return 0;
5131 :
5132 55 : gnc_account_tree_begin_staged_transaction_traversals (acc);
5133 55 : return gnc_account_tree_staged_transaction_traversal (acc, 42, proc, data);
5134 : }
5135 :
5136 :
5137 : gint
5138 23 : xaccAccountForEachTransaction(const Account *acc, TransactionCallback proc,
5139 : void *data)
5140 : {
5141 23 : if (!acc || !proc) return 0;
5142 23 : xaccAccountBeginStagedTransactionTraversals (acc);
5143 23 : return xaccAccountStagedTransactionTraversal(acc, 42, proc, data);
5144 : }
5145 :
5146 : /* ================================================================ */
5147 : /* The following functions are used by
5148 : * src/import-export/import-backend.c to manipulate the contra-account
5149 : * matching data. See src/import-export/import-backend.c for explanations.
5150 : */
5151 :
5152 : #define IMAP_FRAME "import-map"
5153 : #define IMAP_FRAME_BAYES "import-map-bayes"
5154 :
5155 : /* Look up an Account in the map */
5156 : Account*
5157 7 : gnc_account_imap_find_account (Account *acc,
5158 : const char *category,
5159 : const char *key)
5160 : {
5161 7 : if (!acc || !key) return nullptr;
5162 30 : std::vector<std::string> path {IMAP_FRAME};
5163 6 : if (category)
5164 8 : path.push_back (category);
5165 6 : path.push_back (key);
5166 6 : return get_kvp_account_path (acc, path);
5167 6 : }
5168 :
5169 : Account*
5170 0 : gnc_account_imap_find_any (QofBook *book, const char* category, const char *key)
5171 : {
5172 0 : Account *account = nullptr;
5173 :
5174 : /* Get list of Accounts */
5175 0 : auto root = gnc_book_get_root_account (book);
5176 0 : auto accts = gnc_account_get_descendants_sorted (root);
5177 :
5178 : /* Go through list of accounts */
5179 0 : for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5180 : {
5181 0 : auto tmp_acc = static_cast<Account*> (ptr->data);
5182 :
5183 0 : if (gnc_account_imap_find_account (tmp_acc, category, key))
5184 : {
5185 0 : account = tmp_acc;
5186 0 : break;
5187 : }
5188 : }
5189 0 : g_list_free (accts);
5190 :
5191 0 : return account;
5192 : }
5193 :
5194 : /* Store an Account in the map */
5195 : void
5196 9 : gnc_account_imap_add_account (Account *acc,
5197 : const char *category,
5198 : const char *key,
5199 : Account *added_acc)
5200 : {
5201 9 : if (!acc || !key || !added_acc || !*key) return;
5202 :
5203 42 : auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5204 :
5205 7 : set_kvp_account_path (acc, path, added_acc);
5206 21 : }
5207 :
5208 : /* Remove a reference to an Account in the map */
5209 : void
5210 4 : gnc_account_imap_delete_account (Account *acc,
5211 : const char *category,
5212 : const char *key)
5213 : {
5214 4 : if (!acc || !key) return;
5215 :
5216 18 : auto path = category ? Path{IMAP_FRAME, category, key} : Path{IMAP_FRAME, key};
5217 3 : if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5218 : {
5219 3 : qof_instance_slot_path_delete (QOF_INSTANCE (acc), path);
5220 3 : if (category)
5221 10 : qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME, category});
5222 9 : qof_instance_slot_path_delete_if_empty (QOF_INSTANCE (acc), {IMAP_FRAME});
5223 : }
5224 3 : qof_instance_set_dirty (QOF_INSTANCE (acc));
5225 13 : }
5226 :
5227 : /*--------------------------------------------------------------------------
5228 : Below here is the bayes transaction to account matching system
5229 : --------------------------------------------------------------------------*/
5230 :
5231 :
5232 : /** intermediate values used to calculate the bayes probability of a given account
5233 : where p(AB) = (a*b)/[a*b + (1-a)(1-b)], product is (a*b),
5234 : product_difference is (1-a) * (1-b)
5235 : */
5236 : struct AccountProbability
5237 : {
5238 : double product; /* product of probabilities */
5239 : double product_difference; /* product of (1-probabilities) */
5240 : };
5241 :
5242 : struct AccountTokenCount
5243 : {
5244 : std::string account_guid;
5245 : int64_t token_count; /** occurrences of a given token for this account_guid */
5246 : };
5247 :
5248 : /** total_count and the token_count for a given account let us calculate the
5249 : * probability of a given account with any single token
5250 : */
5251 : struct TokenAccountsInfo
5252 : {
5253 : std::vector<AccountTokenCount> accounts;
5254 : int64_t total_count;
5255 : };
5256 :
5257 : /** holds an account guid and its corresponding integer probability
5258 : the integer probability is some factor of 10
5259 : */
5260 : struct AccountInfo
5261 : {
5262 : std::string account_guid;
5263 : int32_t probability;
5264 : };
5265 :
5266 : static void
5267 11 : build_token_info(char const * suffix, KvpValue * value, TokenAccountsInfo & tokenInfo)
5268 : {
5269 11 : if (strlen(suffix) == GUID_ENCODING_LENGTH)
5270 : {
5271 10 : tokenInfo.total_count += value->get<int64_t>();
5272 : /*By convention, the key ends with the account GUID.*/
5273 30 : tokenInfo.accounts.emplace_back(AccountTokenCount{std::string{suffix}, value->get<int64_t>()});
5274 : }
5275 11 : }
5276 :
5277 : /** We scale the probability values by probability_factor.
5278 : ie. with probability_factor of 100000, 10% would be
5279 : 0.10 * 100000 = 10000 */
5280 : static constexpr int probability_factor = 100000;
5281 :
5282 : static FinalProbabilityVec
5283 8 : build_probabilities(ProbabilityVec const & first_pass)
5284 : {
5285 8 : FinalProbabilityVec ret;
5286 24 : for (auto const & first_pass_prob : first_pass)
5287 : {
5288 8 : auto const & account_probability = first_pass_prob.second;
5289 : /* P(AB) = A*B / [A*B + (1-A)*(1-B)]
5290 : * NOTE: so we only keep track of a running product(A*B*C...)
5291 : * and product difference ((1-A)(1-B)...)
5292 : */
5293 8 : int32_t probability = (account_probability.product /
5294 8 : (account_probability.product + account_probability.product_difference)) * probability_factor;
5295 8 : ret.push_back({first_pass_prob.first, probability});
5296 : }
5297 8 : return ret;
5298 0 : }
5299 :
5300 : static AccountInfo
5301 8 : highest_probability(FinalProbabilityVec const & probabilities)
5302 : {
5303 16 : AccountInfo ret {"", std::numeric_limits<int32_t>::min()};
5304 24 : for (auto const & prob : probabilities)
5305 8 : if (prob.second > ret.probability)
5306 8 : ret = AccountInfo {prob.first, prob.second};
5307 8 : return ret;
5308 0 : }
5309 :
5310 : static ProbabilityVec
5311 9 : get_first_pass_probabilities(Account* acc, GList * tokens)
5312 : {
5313 9 : ProbabilityVec ret;
5314 : /* find the probability for each account that contains any of the tokens
5315 : * in the input tokens list. */
5316 21 : for (auto current_token = tokens; current_token; current_token = current_token->next)
5317 : {
5318 12 : TokenAccountsInfo tokenInfo{};
5319 24 : auto path = std::string{IMAP_FRAME_BAYES "/"} + static_cast <char const *> (current_token->data) + "/";
5320 12 : qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), path, &build_token_info, tokenInfo);
5321 34 : for (auto const & current_account_token : tokenInfo.accounts)
5322 : {
5323 10 : auto item = std::find_if(ret.begin(), ret.end(), [¤t_account_token]
5324 : (std::pair<std::string, AccountProbability> const & a) {
5325 2 : return current_account_token.account_guid == a.first;
5326 : });
5327 20 : if (item != ret.end())
5328 : {/* This account is already in the map */
5329 4 : item->second.product = ((double)current_account_token.token_count /
5330 4 : (double)tokenInfo.total_count) * item->second.product;
5331 2 : item->second.product_difference = ((double)1 - ((double)current_account_token.token_count /
5332 4 : (double)tokenInfo.total_count)) * item->second.product_difference;
5333 : }
5334 : else
5335 : {
5336 : /* add a new entry */
5337 : AccountProbability new_probability;
5338 8 : new_probability.product = ((double)current_account_token.token_count /
5339 8 : (double)tokenInfo.total_count);
5340 8 : new_probability.product_difference = 1 - (new_probability.product);
5341 16 : ret.push_back({current_account_token.account_guid, std::move(new_probability)});
5342 : }
5343 : } /* for all accounts in tokenInfo */
5344 12 : }
5345 9 : return ret;
5346 0 : }
5347 :
5348 : static std::string
5349 5 : look_for_old_separator_descendants (Account *root, std::string const & full_name, const gchar *separator)
5350 : {
5351 : GList *top_accounts, *ptr;
5352 5 : gint found_len = 0;
5353 : gchar found_sep;
5354 5 : top_accounts = gnc_account_get_descendants (root);
5355 5 : PINFO("Incoming full_name is '%s', current separator is '%s'", full_name.c_str (), separator);
5356 : /* Go through list of top level accounts */
5357 75 : for (ptr = top_accounts; ptr; ptr = g_list_next (ptr))
5358 : {
5359 35 : const gchar *name = xaccAccountGetName (static_cast <Account const *> (ptr->data));
5360 : // we are looking for the longest top level account that matches
5361 35 : if (g_str_has_prefix (full_name.c_str (), name))
5362 : {
5363 6 : gint name_len = strlen (name);
5364 6 : const gchar old_sep = full_name[name_len];
5365 6 : if (!g_ascii_isalnum (old_sep)) // test for non alpha numeric
5366 : {
5367 6 : if (name_len > found_len)
5368 : {
5369 6 : found_sep = full_name[name_len];
5370 6 : found_len = name_len;
5371 : }
5372 : }
5373 : }
5374 : }
5375 5 : g_list_free (top_accounts); // Free the List
5376 5 : std::string new_name {full_name};
5377 5 : if (found_len > 1)
5378 5 : std::replace (new_name.begin (), new_name.end (), found_sep, *separator);
5379 5 : PINFO ("Return full_name is '%s'", new_name.c_str ());
5380 5 : return new_name;
5381 0 : }
5382 :
5383 : static std::string
5384 6 : get_guid_from_account_name (Account * root, std::string const & name)
5385 : {
5386 6 : auto map_account = gnc_account_lookup_by_full_name (root, name.c_str ());
5387 6 : if (!map_account)
5388 : {
5389 : auto temp_account_name = look_for_old_separator_descendants (root, name,
5390 5 : gnc_get_account_separator_string ());
5391 5 : map_account = gnc_account_lookup_by_full_name (root, temp_account_name.c_str ());
5392 5 : }
5393 6 : auto temp_guid = gnc::GUID {*xaccAccountGetGUID (map_account)};
5394 12 : return temp_guid.to_string ();
5395 : }
5396 :
5397 : static FlatKvpEntry
5398 7 : convert_entry (KvpEntry entry, Account* root)
5399 : {
5400 : /*We need to make a copy here.*/
5401 7 : auto account_name = entry.first.back();
5402 7 : if (!gnc::GUID::is_valid_guid (account_name))
5403 : {
5404 : /* Earlier version stored the account name in the import map, and
5405 : * there were early beta versions of 2.7 that stored a GUID.
5406 : * If there is no GUID, we assume it's an account name. */
5407 : /* Take off the account name and replace it with the GUID */
5408 6 : entry.first.pop_back();
5409 6 : auto guid_str = get_guid_from_account_name (root, account_name);
5410 6 : entry.first.emplace_back (guid_str);
5411 6 : }
5412 7 : std::string new_key {std::accumulate (entry.first.begin(), entry.first.end(), std::string {})};
5413 7 : new_key = IMAP_FRAME_BAYES + new_key;
5414 14 : return {new_key, entry.second};
5415 7 : }
5416 :
5417 : static std::vector<FlatKvpEntry>
5418 2 : get_flat_imap (Account * acc)
5419 : {
5420 2 : auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5421 4 : auto slot = frame->get_slot ({IMAP_FRAME_BAYES});
5422 2 : if (!slot)
5423 1 : return {};
5424 1 : auto imap_frame = slot->get<KvpFrame*> ();
5425 1 : auto flat_kvp = imap_frame->flatten_kvp ();
5426 1 : auto root = gnc_account_get_root (acc);
5427 1 : std::vector <FlatKvpEntry> ret;
5428 9 : for (auto const & flat_entry : flat_kvp)
5429 : {
5430 7 : auto converted_entry = convert_entry (flat_entry, root);
5431 : /*If the entry was invalid, we don't perpetuate it.*/
5432 7 : if (converted_entry.first.size())
5433 7 : ret.emplace_back (converted_entry);
5434 7 : }
5435 1 : return ret;
5436 1 : }
5437 :
5438 : static bool
5439 14 : convert_imap_account_bayes_to_flat (Account *acc)
5440 : {
5441 14 : auto frame = qof_instance_get_slots (QOF_INSTANCE (acc));
5442 14 : if (!frame->get_keys().size())
5443 12 : return false;
5444 2 : auto flat_imap = get_flat_imap(acc);
5445 2 : if (!flat_imap.size ())
5446 1 : return false;
5447 1 : xaccAccountBeginEdit(acc);
5448 2 : frame->set({IMAP_FRAME_BAYES}, nullptr);
5449 1 : std::for_each(flat_imap.begin(), flat_imap.end(),
5450 7 : [&frame] (FlatKvpEntry const & entry) {
5451 21 : frame->set({entry.first.c_str()}, entry.second);
5452 21 : });
5453 1 : qof_instance_set_dirty (QOF_INSTANCE (acc));
5454 1 : xaccAccountCommitEdit(acc);
5455 1 : return true;
5456 2 : }
5457 :
5458 : /*
5459 : * Checks for import map data and converts them when found.
5460 : */
5461 : static bool
5462 2 : imap_convert_bayes_to_flat (QofBook * book)
5463 : {
5464 2 : auto root = gnc_book_get_root_account (book);
5465 2 : auto accts = gnc_account_get_descendants_sorted (root);
5466 2 : bool ret = false;
5467 30 : for (auto ptr = accts; ptr; ptr = g_list_next (ptr))
5468 : {
5469 14 : Account *acc = static_cast <Account*> (ptr->data);
5470 14 : if (convert_imap_account_bayes_to_flat (acc))
5471 : {
5472 1 : ret = true;
5473 1 : gnc_features_set_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN);
5474 : }
5475 : }
5476 2 : g_list_free (accts);
5477 2 : return ret;
5478 : }
5479 :
5480 : void
5481 1 : gnc_account_reset_convert_bayes_to_flat (void)
5482 : {
5483 1 : imap_convert_bayes_to_flat_run = false;
5484 1 : }
5485 :
5486 : /*
5487 : * Here we check to see the state of import map data.
5488 : *
5489 : * If the GUID_FLAT_BAYESIAN feature flag is set, everything
5490 : * should be fine.
5491 : *
5492 : * If it is not set, there are two possibilities: import data
5493 : * are present from a previous version or not. If they are,
5494 : * they are converted, and the feature flag set. If there are
5495 : * no previous data, nothing is done.
5496 : */
5497 : static void
5498 21 : check_import_map_data (QofBook *book)
5499 : {
5500 21 : if (gnc_features_check_used (book, GNC_FEATURE_GUID_FLAT_BAYESIAN) ||
5501 : imap_convert_bayes_to_flat_run)
5502 19 : return;
5503 :
5504 : /* This function will set GNC_FEATURE_GUID_FLAT_BAYESIAN if necessary.*/
5505 2 : imap_convert_bayes_to_flat (book);
5506 2 : imap_convert_bayes_to_flat_run = true;
5507 : }
5508 :
5509 : static constexpr double threshold = .90 * probability_factor; /* 90% */
5510 :
5511 : /** Look up an Account in the map */
5512 : Account*
5513 9 : gnc_account_imap_find_account_bayes (Account *acc, GList *tokens)
5514 : {
5515 9 : if (!acc)
5516 0 : return nullptr;
5517 9 : auto book = gnc_account_get_book(acc);
5518 9 : check_import_map_data (book);
5519 9 : auto first_pass = get_first_pass_probabilities(acc, tokens);
5520 9 : if (!first_pass.size())
5521 1 : return nullptr;
5522 8 : auto final_probabilities = build_probabilities(first_pass);
5523 8 : if (!final_probabilities.size())
5524 0 : return nullptr;
5525 8 : auto best = highest_probability(final_probabilities);
5526 8 : if (best.account_guid == "")
5527 0 : return nullptr;
5528 8 : if (best.probability < threshold)
5529 0 : return nullptr;
5530 8 : gnc::GUID guid;
5531 : try {
5532 8 : guid = gnc::GUID::from_string(best.account_guid);
5533 0 : } catch (gnc::guid_syntax_exception&) {
5534 0 : return nullptr;
5535 0 : }
5536 8 : auto account = xaccAccountLookup (reinterpret_cast<GncGUID*>(&guid), book);
5537 8 : return account;
5538 9 : }
5539 :
5540 : static void
5541 14 : change_imap_entry (Account *acc, std::string const & path, int64_t token_count)
5542 : {
5543 14 : PINFO("Source Account is '%s', Count is '%" G_GINT64_FORMAT "'",
5544 : xaccAccountGetName (acc), token_count);
5545 :
5546 : // check for existing guid entry
5547 42 : if (auto existing_token_count = get_kvp_int64_path (acc, {path}))
5548 : {
5549 5 : PINFO("found existing value of '%" G_GINT64_FORMAT "'", *existing_token_count);
5550 5 : token_count += *existing_token_count;
5551 : }
5552 :
5553 : // Add or Update the entry based on guid
5554 56 : set_kvp_int64_path (acc, {path}, token_count);
5555 42 : }
5556 :
5557 : /** Updates the imap for a given account using a list of tokens */
5558 : void
5559 11 : gnc_account_imap_add_account_bayes (Account *acc,
5560 : GList *tokens,
5561 : Account *added_acc)
5562 : {
5563 : GList *current_token;
5564 : gint64 token_count;
5565 : char *account_fullname;
5566 : char *guid_string;
5567 :
5568 11 : ENTER(" ");
5569 11 : if (!acc)
5570 : {
5571 0 : LEAVE(" ");
5572 0 : return;
5573 : }
5574 11 : check_import_map_data (gnc_account_get_book(acc));
5575 :
5576 11 : g_return_if_fail (added_acc != nullptr);
5577 10 : account_fullname = gnc_account_get_full_name(added_acc);
5578 10 : xaccAccountBeginEdit (acc);
5579 :
5580 10 : PINFO("account name: '%s'", account_fullname);
5581 :
5582 10 : guid_string = guid_to_string (xaccAccountGetGUID (added_acc));
5583 :
5584 : /* process each token in the list */
5585 24 : for (current_token = g_list_first(tokens); current_token;
5586 14 : current_token = current_token->next)
5587 : {
5588 14 : char* token = static_cast<char*>(current_token->data);
5589 : /* Jump to next iteration if the pointer is not valid or if the
5590 : string is empty. In HBCI import we almost always get an empty
5591 : string, which doesn't work in the kvp loopkup later. So we
5592 : skip this case here. */
5593 14 : if (!token || !token[0])
5594 0 : continue;
5595 : /* start off with one token for this account */
5596 14 : token_count = 1;
5597 14 : PINFO("adding token '%s'", token);
5598 14 : auto path = std::string {IMAP_FRAME_BAYES} + '/' + token + '/' + guid_string;
5599 : /* change the imap entry for the account */
5600 14 : change_imap_entry (acc, path, token_count);
5601 14 : }
5602 : /* free up the account fullname and guid string */
5603 10 : xaccAccountCommitEdit (acc);
5604 10 : gnc_features_set_used (gnc_account_get_book(acc), GNC_FEATURE_GUID_FLAT_BAYESIAN);
5605 10 : g_free (account_fullname);
5606 10 : g_free (guid_string);
5607 10 : LEAVE(" ");
5608 : }
5609 :
5610 : /*******************************************************************************/
5611 :
5612 : static void
5613 0 : build_non_bayes (const char *key, const GValue *value, gpointer user_data)
5614 : {
5615 0 : if (!G_VALUE_HOLDS_BOXED (value))
5616 0 : return;
5617 : QofBook *book;
5618 0 : GncGUID *guid = nullptr;
5619 0 : gchar *guid_string = nullptr;
5620 0 : auto imapInfo = (GncImapInfo*)user_data;
5621 : // Get the book
5622 0 : book = qof_instance_get_book (imapInfo->source_account);
5623 :
5624 0 : guid = (GncGUID*)g_value_get_boxed (value);
5625 0 : guid_string = guid_to_string (guid);
5626 :
5627 0 : PINFO("build_non_bayes: match string '%s', match account guid: '%s'",
5628 : (char*)key, guid_string);
5629 :
5630 0 : auto imapInfo_node = static_cast <GncImapInfo*> (g_malloc(sizeof(GncImapInfo)));
5631 :
5632 0 : imapInfo_node->source_account = imapInfo->source_account;
5633 0 : imapInfo_node->map_account = xaccAccountLookup (guid, book);
5634 0 : imapInfo_node->head = g_strdup (imapInfo->head);
5635 0 : imapInfo_node->match_string = g_strdup (key);
5636 0 : imapInfo_node->category = g_strdup (imapInfo->category);
5637 0 : imapInfo_node->count = g_strdup (" ");
5638 :
5639 0 : imapInfo->list = g_list_prepend (imapInfo->list, imapInfo_node);
5640 :
5641 0 : g_free (guid_string);
5642 : }
5643 :
5644 : static void
5645 1 : build_bayes (const char *suffix, KvpValue * value, GncImapInfo & imapInfo)
5646 : {
5647 1 : size_t guid_start = strlen(suffix) - GUID_ENCODING_LENGTH;
5648 2 : std::string account_guid {&suffix[guid_start]};
5649 : GncGUID guid;
5650 : try
5651 : {
5652 1 : guid = gnc::GUID::from_string (account_guid);
5653 : }
5654 0 : catch (const gnc::guid_syntax_exception& err)
5655 : {
5656 0 : PWARN("Invalid GUID string from %s%s", IMAP_FRAME_BAYES, suffix);
5657 0 : }
5658 1 : auto map_account = xaccAccountLookup (&guid, gnc_account_get_book (imapInfo.source_account));
5659 1 : auto imap_node = static_cast <GncImapInfo*> (g_malloc (sizeof (GncImapInfo)));
5660 1 : auto count = value->get <int64_t> ();
5661 1 : imap_node->source_account = imapInfo.source_account;
5662 1 : imap_node->map_account = map_account;
5663 1 : imap_node->head = g_strdup_printf ("%s%s", IMAP_FRAME_BAYES, suffix);
5664 1 : imap_node->match_string = g_strndup (&suffix[1], guid_start - 2);
5665 1 : imap_node->category = g_strdup(" ");
5666 1 : imap_node->count = g_strdup_printf ("%" G_GINT64_FORMAT, count);
5667 1 : imapInfo.list = g_list_prepend (imapInfo.list, imap_node);
5668 1 : }
5669 :
5670 1 : void gnc_account_imap_info_destroy (GncImapInfo* imapInfo)
5671 : {
5672 1 : g_free (imapInfo->head);
5673 1 : g_free (imapInfo->category);
5674 1 : g_free (imapInfo->match_string);
5675 1 : g_free (imapInfo->count);
5676 1 : g_free (imapInfo);
5677 1 : }
5678 :
5679 : GList *
5680 1 : gnc_account_imap_get_info_bayes (Account *acc)
5681 : {
5682 1 : check_import_map_data (gnc_account_get_book (acc));
5683 : /* A dummy object which is used to hold the specified account, and the list
5684 : * of data about which we care. */
5685 1 : GncImapInfo imapInfo {acc, nullptr};
5686 2 : qof_instance_foreach_slot_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES, &build_bayes, imapInfo);
5687 2 : return g_list_reverse(imapInfo.list);
5688 : }
5689 :
5690 : GList *
5691 0 : gnc_account_imap_get_info (Account *acc, const char *category)
5692 : {
5693 0 : GList *list = nullptr;
5694 :
5695 : GncImapInfo imapInfo;
5696 :
5697 0 : std::vector<std::string> path {IMAP_FRAME};
5698 0 : if (category)
5699 0 : path.emplace_back (category);
5700 :
5701 0 : imapInfo.source_account = acc;
5702 0 : imapInfo.list = list;
5703 :
5704 0 : imapInfo.head = g_strdup (IMAP_FRAME);
5705 0 : imapInfo.category = g_strdup (category);
5706 :
5707 0 : if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5708 : {
5709 0 : qof_instance_foreach_slot (QOF_INSTANCE(acc), IMAP_FRAME, category,
5710 : build_non_bayes, &imapInfo);
5711 : }
5712 0 : g_free (imapInfo.head);
5713 0 : g_free (imapInfo.category);
5714 0 : return g_list_reverse(imapInfo.list);
5715 0 : }
5716 :
5717 : /*******************************************************************************/
5718 :
5719 : gchar *
5720 9 : gnc_account_get_map_entry (Account *acc, const char *head, const char *category)
5721 : {
5722 60 : return g_strdup (category ?
5723 : get_kvp_string_path (acc, {head, category}) :
5724 : get_kvp_string_path (acc, {head}));
5725 18 : }
5726 :
5727 : GncGUID *
5728 2 : gnc_account_get_map_guid_entry (Account *acc, const char *head, const char *category)
5729 : {
5730 2 : return category ?
5731 2 : get_kvp_guid_path (acc, {head, category}) :
5732 14 : get_kvp_guid_path (acc, {head});
5733 4 : }
5734 :
5735 : void
5736 1 : gnc_account_delete_map_entry (Account *acc, char *head, char *category,
5737 : char *match_string, gboolean empty)
5738 : {
5739 1 : if (acc != nullptr)
5740 : {
5741 3 : std::vector<std::string> path {head};
5742 1 : if (category)
5743 0 : path.emplace_back (category);
5744 1 : if (match_string)
5745 0 : path.emplace_back (match_string);
5746 :
5747 1 : if (qof_instance_has_path_slot (QOF_INSTANCE (acc), path))
5748 : {
5749 1 : xaccAccountBeginEdit (acc);
5750 1 : if (empty)
5751 0 : qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), path);
5752 : else
5753 1 : qof_instance_slot_path_delete (QOF_INSTANCE(acc), path);
5754 1 : PINFO("Account is '%s', head is '%s', category is '%s', match_string is'%s'",
5755 : xaccAccountGetName (acc), head, category, match_string);
5756 1 : qof_instance_set_dirty (QOF_INSTANCE(acc));
5757 1 : xaccAccountCommitEdit (acc);
5758 : }
5759 1 : }
5760 3 : }
5761 :
5762 : void
5763 0 : gnc_account_delete_all_bayes_maps (Account *acc)
5764 : {
5765 0 : if (acc != nullptr)
5766 : {
5767 0 : auto slots = qof_instance_get_slots_prefix (QOF_INSTANCE (acc), IMAP_FRAME_BAYES);
5768 0 : if (!slots.size()) return;
5769 0 : xaccAccountBeginEdit (acc);
5770 0 : for (auto const & entry : slots)
5771 : {
5772 0 : qof_instance_slot_path_delete (QOF_INSTANCE (acc), {entry.first});
5773 : }
5774 0 : qof_instance_set_dirty (QOF_INSTANCE(acc));
5775 0 : xaccAccountCommitEdit (acc);
5776 0 : }
5777 0 : }
5778 :
5779 : /* ================================================================ */
5780 : /* QofObject function implementation and registration */
5781 :
5782 : static void
5783 1418 : destroy_all_child_accounts (Account *acc, gpointer data)
5784 : {
5785 1418 : xaccAccountBeginEdit (acc);
5786 1418 : xaccAccountDestroy (acc);
5787 1418 : }
5788 :
5789 : static void
5790 179 : gnc_account_book_end(QofBook* book)
5791 : {
5792 179 : Account *root_account = gnc_book_get_root_account (book);
5793 : GList *accounts;
5794 :
5795 179 : if (!root_account)
5796 73 : return;
5797 :
5798 106 : accounts = gnc_account_get_descendants (root_account);
5799 :
5800 106 : if (accounts)
5801 : {
5802 104 : accounts = g_list_reverse (accounts);
5803 104 : g_list_foreach (accounts, (GFunc)destroy_all_child_accounts, nullptr);
5804 104 : g_list_free (accounts);
5805 : }
5806 106 : xaccAccountBeginEdit (root_account);
5807 106 : xaccAccountDestroy (root_account);
5808 : }
5809 :
5810 : #ifdef _MSC_VER
5811 : /* MSVC compiler doesn't have C99 "designated initializers"
5812 : * so we wrap them in a macro that is empty on MSVC. */
5813 : # define DI(x) /* */
5814 : #else
5815 : # define DI(x) x
5816 : #endif
5817 : static QofObject account_object_def =
5818 : {
5819 : DI(.interface_version = ) QOF_OBJECT_VERSION,
5820 : DI(.e_type = ) GNC_ID_ACCOUNT,
5821 : DI(.type_label = ) "Account",
5822 : DI(.create = ) (void*(*)(QofBook*)) xaccMallocAccount,
5823 : DI(.book_begin = ) nullptr,
5824 : DI(.book_end = ) gnc_account_book_end,
5825 : DI(.is_dirty = ) qof_collection_is_dirty,
5826 : DI(.mark_clean = ) qof_collection_mark_clean,
5827 : DI(.foreach = ) qof_collection_foreach,
5828 : DI(.printable = ) (const char * (*)(gpointer)) xaccAccountGetName,
5829 : DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
5830 : };
5831 :
5832 83 : gboolean xaccAccountRegister (void)
5833 : {
5834 : static QofParam params[] =
5835 : {
5836 : {
5837 : ACCOUNT_NAME_, QOF_TYPE_STRING,
5838 : (QofAccessFunc) xaccAccountGetName,
5839 : (QofSetterFunc) xaccAccountSetName
5840 : },
5841 : {
5842 : ACCOUNT_CODE_, QOF_TYPE_STRING,
5843 : (QofAccessFunc) xaccAccountGetCode,
5844 : (QofSetterFunc) xaccAccountSetCode
5845 : },
5846 : {
5847 : ACCOUNT_DESCRIPTION_, QOF_TYPE_STRING,
5848 : (QofAccessFunc) xaccAccountGetDescription,
5849 : (QofSetterFunc) xaccAccountSetDescription
5850 : },
5851 : {
5852 : ACCOUNT_COLOR_, QOF_TYPE_STRING,
5853 : (QofAccessFunc) xaccAccountGetColor,
5854 : (QofSetterFunc) xaccAccountSetColor
5855 : },
5856 : {
5857 : ACCOUNT_FILTER_, QOF_TYPE_STRING,
5858 : (QofAccessFunc) xaccAccountGetFilter,
5859 : (QofSetterFunc) xaccAccountSetFilter
5860 : },
5861 : {
5862 : ACCOUNT_SORT_ORDER_, QOF_TYPE_STRING,
5863 : (QofAccessFunc) xaccAccountGetSortOrder,
5864 : (QofSetterFunc) xaccAccountSetSortOrder
5865 : },
5866 : {
5867 : ACCOUNT_SORT_REVERSED_, QOF_TYPE_BOOLEAN,
5868 : (QofAccessFunc) xaccAccountGetSortReversed,
5869 : (QofSetterFunc) xaccAccountSetSortReversed
5870 : },
5871 : {
5872 : ACCOUNT_NOTES_, QOF_TYPE_STRING,
5873 : (QofAccessFunc) xaccAccountGetNotes,
5874 : (QofSetterFunc) xaccAccountSetNotes
5875 : },
5876 : {
5877 : ACCOUNT_PRESENT_, QOF_TYPE_NUMERIC,
5878 : (QofAccessFunc) xaccAccountGetPresentBalance, nullptr
5879 : },
5880 : {
5881 : ACCOUNT_BALANCE_, QOF_TYPE_NUMERIC,
5882 : (QofAccessFunc) xaccAccountGetBalance, nullptr
5883 : },
5884 : {
5885 : ACCOUNT_CLEARED_, QOF_TYPE_NUMERIC,
5886 : (QofAccessFunc) xaccAccountGetClearedBalance, nullptr
5887 : },
5888 : {
5889 : ACCOUNT_RECONCILED_, QOF_TYPE_NUMERIC,
5890 : (QofAccessFunc) xaccAccountGetReconciledBalance, nullptr
5891 : },
5892 : {
5893 : ACCOUNT_TYPE_, QOF_TYPE_STRING,
5894 : (QofAccessFunc) qofAccountGetTypeString,
5895 : (QofSetterFunc) qofAccountSetType
5896 : },
5897 : {
5898 : ACCOUNT_FUTURE_MINIMUM_, QOF_TYPE_NUMERIC,
5899 : (QofAccessFunc) xaccAccountGetProjectedMinimumBalance, nullptr
5900 : },
5901 : {
5902 : ACCOUNT_TAX_RELATED, QOF_TYPE_BOOLEAN,
5903 : (QofAccessFunc) xaccAccountGetTaxRelated,
5904 : (QofSetterFunc) xaccAccountSetTaxRelated
5905 : },
5906 : {
5907 : ACCOUNT_OPENING_BALANCE_, QOF_TYPE_BOOLEAN,
5908 : (QofAccessFunc) xaccAccountGetIsOpeningBalance,
5909 : (QofSetterFunc) xaccAccountSetIsOpeningBalance
5910 : },
5911 : {
5912 : ACCOUNT_SCU, QOF_TYPE_INT32,
5913 : (QofAccessFunc) xaccAccountGetCommoditySCU,
5914 : (QofSetterFunc) xaccAccountSetCommoditySCU
5915 : },
5916 : {
5917 : ACCOUNT_NSCU, QOF_TYPE_BOOLEAN,
5918 : (QofAccessFunc) xaccAccountGetNonStdSCU,
5919 : (QofSetterFunc) xaccAccountSetNonStdSCU
5920 : },
5921 : {
5922 : ACCOUNT_PARENT, GNC_ID_ACCOUNT,
5923 : (QofAccessFunc) gnc_account_get_parent,
5924 : (QofSetterFunc) qofAccountSetParent
5925 : },
5926 : {
5927 : QOF_PARAM_BOOK, QOF_ID_BOOK,
5928 : (QofAccessFunc) qof_instance_get_book, nullptr
5929 : },
5930 : {
5931 : QOF_PARAM_GUID, QOF_TYPE_GUID,
5932 : (QofAccessFunc) qof_instance_get_guid, nullptr
5933 : },
5934 : { nullptr },
5935 : };
5936 :
5937 83 : qof_class_register (GNC_ID_ACCOUNT, (QofSortFunc) qof_xaccAccountOrder, params);
5938 :
5939 83 : return qof_object_register (&account_object_def);
5940 : }
5941 :
5942 : /* ======================= UNIT TESTING ACCESS =======================
5943 : * The following functions are for unit testing use only.
5944 : */
5945 : static AccountPrivate*
5946 11 : utest_account_get_private (Account *acc)
5947 : {
5948 11 : return GET_PRIVATE (acc);
5949 : }
5950 :
5951 : AccountTestFunctions*
5952 45 : _utest_account_fill_functions(void)
5953 : {
5954 45 : AccountTestFunctions* func = g_new(AccountTestFunctions, 1);
5955 :
5956 45 : func->get_private = utest_account_get_private;
5957 45 : func->coll_get_root_account = gnc_coll_get_root_account;
5958 45 : func->xaccFreeAccountChildren = xaccFreeAccountChildren;
5959 45 : func->xaccFreeAccount = xaccFreeAccount;
5960 45 : func->qofAccountSetParent = qofAccountSetParent;
5961 45 : func->gnc_account_lookup_by_full_name_helper =
5962 : gnc_account_lookup_by_full_name_helper;
5963 :
5964 45 : return func;
5965 : }
5966 : /* ======================= END OF FILE =========================== */
|