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