|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * Scrub.c -- convert single-entry accounts into clean double-entry * 00003 * * 00004 * This program is free software; you can redistribute it and/or * 00005 * modify it under the terms of the GNU General Public License as * 00006 * published by the Free Software Foundation; either version 2 of * 00007 * the License, or (at your option) any later version. * 00008 * * 00009 * This program is distributed in the hope that it will be useful, * 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00012 * GNU General Public License for more details. * 00013 * * 00014 * You should have received a copy of the GNU General Public License* 00015 * along with this program; if not, contact: * 00016 * * 00017 * Free Software Foundation Voice: +1-617-542-5942 * 00018 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00019 * Boston, MA 02110-1301, USA gnu@gnu.org * 00020 * * 00021 \********************************************************************/ 00022 00023 /* 00024 * FILE: 00025 * Scrub.c 00026 * 00027 * FUNCTION: 00028 * Provides a set of functions and utilities for scrubbing clean 00029 * single-entry accounts so that they can be promoted into 00030 * self-consistent, clean double-entry accounts. 00031 * 00032 * HISTORY: 00033 * Created by Linas Vepstas December 1998 00034 * Copyright (c) 1998-2000, 2003 Linas Vepstas <linas@linas.org> 00035 * Copyright (c) 2002 Christian Stimming 00036 * Copyright (c) 2006 David Hampton 00037 */ 00038 00039 #include "config.h" 00040 00041 #include <glib.h> 00042 #include <glib/gi18n.h> 00043 #include <stdio.h> 00044 #include <string.h> 00045 00046 #include "Account.h" 00047 #include "AccountP.h" 00048 #include "Scrub.h" 00049 #include "ScrubP.h" 00050 #include "Transaction.h" 00051 #include "TransactionP.h" 00052 #include "gnc-commodity.h" 00053 00054 #undef G_LOG_DOMAIN 00055 #define G_LOG_DOMAIN "gnc.engine.scrub" 00056 00057 static QofLogModule log_module = G_LOG_DOMAIN; 00058 00059 /* ================================================================ */ 00060 00061 void 00062 xaccAccountTreeScrubOrphans (Account *acc) 00063 { 00064 if (!acc) return; 00065 00066 xaccAccountScrubOrphans (acc); 00067 gnc_account_foreach_descendant(acc, 00068 (AccountCb)xaccAccountScrubOrphans, NULL); 00069 } 00070 00071 static void 00072 TransScrubOrphansFast (Transaction *trans, Account *root) 00073 { 00074 GList *node; 00075 gchar *accname; 00076 00077 if (!trans) return; 00078 g_return_if_fail (root); 00079 00080 for (node = trans->splits; node; node = node->next) 00081 { 00082 Split *split = node->data; 00083 Account *orph; 00084 00085 if (split->acc) continue; 00086 00087 DEBUG ("Found an orphan \n"); 00088 00089 accname = g_strconcat (_("Orphan"), "-", 00090 gnc_commodity_get_mnemonic (trans->common_currency), 00091 NULL); 00092 orph = xaccScrubUtilityGetOrMakeAccount (root, trans->common_currency, 00093 accname, ACCT_TYPE_BANK, FALSE); 00094 g_free (accname); 00095 if (!orph) continue; 00096 00097 xaccSplitSetAccount(split, orph); 00098 } 00099 } 00100 00101 void 00102 xaccAccountScrubOrphans (Account *acc) 00103 { 00104 GList *node; 00105 const char *str; 00106 00107 if (!acc) return; 00108 00109 str = xaccAccountGetName (acc); 00110 str = str ? str : "(null)"; 00111 PINFO ("Looking for orphans in account %s \n", str); 00112 00113 for (node = xaccAccountGetSplitList(acc); node; node = node->next) 00114 { 00115 Split *split = node->data; 00116 00117 TransScrubOrphansFast (xaccSplitGetParent (split), 00118 gnc_account_get_root (acc)); 00119 } 00120 } 00121 00122 00123 void 00124 xaccTransScrubOrphans (Transaction *trans) 00125 { 00126 SplitList *node; 00127 QofBook *book = NULL; 00128 Account *root = NULL; 00129 for (node = trans->splits; node; node = node->next) 00130 { 00131 Split *split = node->data; 00132 00133 if (split->acc) 00134 { 00135 TransScrubOrphansFast (trans, gnc_account_get_root(split->acc)); 00136 return; 00137 } 00138 } 00139 00140 /* If we got to here, then *none* of the splits belonged to an 00141 * account. Not a happy situation. We should dig an account 00142 * out of the book the transaction belongs to. 00143 * XXX we should probably *always* to this, instead of the above loop! 00144 */ 00145 PINFO ("Free Floating Transaction!"); 00146 book = xaccTransGetBook (trans); 00147 root = gnc_book_get_root_account (book); 00148 TransScrubOrphansFast (trans, root); 00149 } 00150 00151 /* ================================================================ */ 00152 00153 void 00154 xaccAccountTreeScrubSplits (Account *account) 00155 { 00156 if (!account) return; 00157 00158 xaccAccountScrubSplits (account); 00159 gnc_account_foreach_descendant(account, 00160 (AccountCb)xaccAccountScrubSplits, NULL); 00161 } 00162 00163 void 00164 xaccAccountScrubSplits (Account *account) 00165 { 00166 GList *node; 00167 00168 for (node = xaccAccountGetSplitList (account); node; node = node->next) 00169 xaccSplitScrub (node->data); 00170 } 00171 00172 void 00173 xaccSplitScrub (Split *split) 00174 { 00175 Account *account; 00176 Transaction *trans; 00177 gnc_numeric value, amount; 00178 gnc_commodity *currency, *acc_commodity; 00179 int scu; 00180 00181 if (!split) return; 00182 ENTER ("(split=%p)", split); 00183 00184 trans = xaccSplitGetParent (split); 00185 if (!trans) 00186 { 00187 LEAVE("no trans"); 00188 return; 00189 } 00190 00191 account = xaccSplitGetAccount (split); 00192 00193 /* If there's no account, this split is an orphan. 00194 * We need to fix that first, before proceeding. 00195 */ 00196 if (!account) 00197 { 00198 xaccTransScrubOrphans (trans); 00199 account = xaccSplitGetAccount (split); 00200 } 00201 00202 /* Grrr... the register gnc_split_register_load() line 203 of 00203 * src/register/ledger-core/split-register-load.c will create 00204 * free-floating bogus transactions. Ignore these for now ... 00205 */ 00206 if (!account) 00207 { 00208 PINFO ("Free Floating Transaction!"); 00209 LEAVE ("no account"); 00210 return; 00211 } 00212 00213 /* Split amounts and values should be valid numbers */ 00214 value = xaccSplitGetValue (split); 00215 if (gnc_numeric_check (value)) 00216 { 00217 value = gnc_numeric_zero(); 00218 xaccSplitSetValue (split, value); 00219 } 00220 00221 amount = xaccSplitGetAmount (split); 00222 if (gnc_numeric_check (amount)) 00223 { 00224 amount = gnc_numeric_zero(); 00225 xaccSplitSetAmount (split, amount); 00226 } 00227 00228 currency = xaccTransGetCurrency (trans); 00229 00230 /* If the account doesn't have a commodity, 00231 * we should attempt to fix that first. 00232 */ 00233 acc_commodity = xaccAccountGetCommodity(account); 00234 if (!acc_commodity) 00235 { 00236 xaccAccountScrubCommodity (account); 00237 } 00238 if (!acc_commodity || !gnc_commodity_equiv(acc_commodity, currency)) 00239 { 00240 LEAVE ("(split=%p) inequiv currency", split); 00241 return; 00242 } 00243 00244 scu = MIN (xaccAccountGetCommoditySCU (account), 00245 gnc_commodity_get_fraction (currency)); 00246 00247 if (gnc_numeric_same (amount, value, scu, GNC_HOW_RND_ROUND_HALF_UP)) 00248 { 00249 LEAVE("(split=%p) different values", split); 00250 return; 00251 } 00252 00253 /* 00254 * This will be hit every time you answer yes to the dialog "The 00255 * current transaction has changed. Would you like to record it. 00256 */ 00257 PINFO ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" 00258 " old amount %s %s, new amount %s", 00259 trans->description, split->memo, 00260 gnc_num_dbg_to_string (xaccSplitGetAmount(split)), 00261 gnc_commodity_get_mnemonic (currency), 00262 gnc_num_dbg_to_string (xaccSplitGetValue(split))); 00263 00264 xaccTransBeginEdit (trans); 00265 xaccSplitSetAmount (split, value); 00266 xaccTransCommitEdit (trans); 00267 LEAVE ("(split=%p)", split); 00268 } 00269 00270 /* ================================================================ */ 00271 00272 void 00273 xaccAccountTreeScrubImbalance (Account *acc) 00274 { 00275 xaccAccountScrubImbalance (acc); 00276 gnc_account_foreach_descendant(acc, 00277 (AccountCb)xaccAccountScrubImbalance, NULL); 00278 } 00279 00280 void 00281 xaccAccountScrubImbalance (Account *acc) 00282 { 00283 GList *node; 00284 const char *str; 00285 00286 if (!acc) return; 00287 00288 str = xaccAccountGetName(acc); 00289 str = str ? str : "(null)"; 00290 PINFO ("Looking for imbalance in account %s \n", str); 00291 00292 for (node = xaccAccountGetSplitList(acc); node; node = node->next) 00293 { 00294 Split *split = node->data; 00295 Transaction *trans = xaccSplitGetParent(split); 00296 00297 xaccTransScrubCurrencyFromSplits(trans); 00298 00299 xaccTransScrubImbalance (trans, gnc_account_get_root (acc), NULL); 00300 } 00301 } 00302 00303 void 00304 xaccTransScrubCurrencyFromSplits(Transaction *trans) 00305 { 00306 GList *node; 00307 gnc_commodity *common_currency = NULL; 00308 00309 if (!trans) return; 00310 00311 for (node = xaccTransGetSplitList (trans); node; node = node->next) 00312 { 00313 Split *split = node->data; 00314 00315 if (!xaccTransStillHasSplit(trans, split)) continue; 00316 if (gnc_numeric_equal(xaccSplitGetAmount (split), 00317 xaccSplitGetValue (split))) 00318 { 00319 00320 Account *s_account = xaccSplitGetAccount (split); 00321 gnc_commodity *s_commodity = xaccAccountGetCommodity (s_account); 00322 00323 if (s_commodity) 00324 { 00325 if (gnc_commodity_is_currency(s_commodity)) 00326 { 00327 /* Found a split where the amount is the same as the value and 00328 the commodity is a currency. If all splits in the transaction 00329 that fit this description are in the same currency then the 00330 transaction should be in that currency too. */ 00331 00332 if (common_currency == NULL) 00333 /* First one we've found, save the currency */ 00334 common_currency = s_commodity; 00335 else if ( !gnc_commodity_equiv (common_currency, s_commodity)) 00336 { 00337 /* Splits are inconsistent, more than one has a value equal to 00338 the amount, but they aren't all in the same currency. */ 00339 common_currency = NULL; 00340 break; 00341 } 00342 } 00343 } 00344 } 00345 } 00346 00347 if (common_currency && 00348 !gnc_commodity_equiv (common_currency, xaccTransGetCurrency (trans))) 00349 { 00350 00351 /* Found a common currency for the splits, and the transaction is not 00352 in that currency */ 00353 gboolean trans_was_open; 00354 00355 PINFO ("transaction in wrong currency"); 00356 00357 trans_was_open = xaccTransIsOpen (trans); 00358 00359 if (!trans_was_open) 00360 xaccTransBeginEdit (trans); 00361 00362 xaccTransSetCurrency (trans, common_currency); 00363 00364 if (!trans_was_open) 00365 xaccTransCommitEdit (trans); 00366 } 00367 } 00368 00369 static Split * 00370 get_balance_split (Transaction *trans, Account *root, Account *account, 00371 gnc_commodity *commodity) 00372 { 00373 Split *balance_split; 00374 gchar *accname; 00375 00376 if (!account || 00377 !gnc_commodity_equiv (commodity, xaccAccountGetCommodity(account))) 00378 { 00379 if (!root) 00380 { 00381 root = gnc_book_get_root_account (xaccTransGetBook (trans)); 00382 if (NULL == root) 00383 { 00384 /* This can't occur, things should be in books */ 00385 PERR ("Bad data corruption, no root account in book"); 00386 return NULL; 00387 } 00388 } 00389 accname = g_strconcat (_("Imbalance"), "-", 00390 gnc_commodity_get_mnemonic (commodity), NULL); 00391 account = xaccScrubUtilityGetOrMakeAccount (root, commodity, 00392 accname, ACCT_TYPE_BANK, FALSE); 00393 g_free (accname); 00394 if (!account) 00395 { 00396 PERR ("Can't get balancing account"); 00397 return NULL; 00398 } 00399 } 00400 00401 balance_split = xaccTransFindSplitByAccount(trans, account); 00402 00403 /* Put split into account before setting split value */ 00404 if (!balance_split) 00405 { 00406 balance_split = xaccMallocSplit (qof_instance_get_book(trans)); 00407 00408 xaccTransBeginEdit (trans); 00409 xaccSplitSetParent(balance_split, trans); 00410 xaccSplitSetAccount(balance_split, account); 00411 xaccTransCommitEdit (trans); 00412 } 00413 00414 return balance_split; 00415 } 00416 00417 /* Get the trading split for a given commodity, creating it (and the 00418 necessary accounts) if it doesn't exist. */ 00419 static Split * 00420 get_trading_split (Transaction *trans, Account *root, 00421 gnc_commodity *commodity) 00422 { 00423 Split *balance_split; 00424 Account *trading_account; 00425 Account *ns_account; 00426 Account *account; 00427 gnc_commodity *default_currency = NULL; 00428 00429 if (!root) 00430 { 00431 root = gnc_book_get_root_account (xaccTransGetBook (trans)); 00432 if (NULL == root) 00433 { 00434 /* This can't occur, things should be in books */ 00435 PERR ("Bad data corruption, no root account in book"); 00436 return NULL; 00437 } 00438 } 00439 00440 /* Get the default currency. This is harder than it seems. It's not 00441 possible to call gnc_default_currency() since it's a UI function. One 00442 might think that the currency of the root account would do, but the root 00443 account has no currency. Instead look for the Income placeholder account 00444 and use its currency. */ 00445 default_currency = xaccAccountGetCommodity(gnc_account_lookup_by_name(root, 00446 _("Income"))); 00447 if (! default_currency) 00448 { 00449 default_currency = commodity; 00450 } 00451 00452 trading_account = xaccScrubUtilityGetOrMakeAccount (root, 00453 default_currency, 00454 _("Trading"), 00455 ACCT_TYPE_TRADING, TRUE); 00456 if (!trading_account) 00457 { 00458 PERR ("Can't get trading account"); 00459 return NULL; 00460 } 00461 00462 ns_account = xaccScrubUtilityGetOrMakeAccount (trading_account, 00463 default_currency, 00464 gnc_commodity_get_namespace(commodity), 00465 ACCT_TYPE_TRADING, TRUE); 00466 if (!ns_account) 00467 { 00468 PERR ("Can't get namespace account"); 00469 return NULL; 00470 } 00471 00472 account = xaccScrubUtilityGetOrMakeAccount (ns_account, commodity, 00473 gnc_commodity_get_mnemonic(commodity), 00474 ACCT_TYPE_TRADING, FALSE); 00475 if (!account) 00476 { 00477 PERR ("Can't get commodity account"); 00478 return NULL; 00479 } 00480 00481 00482 balance_split = xaccTransFindSplitByAccount(trans, account); 00483 00484 /* Put split into account before setting split value */ 00485 if (!balance_split) 00486 { 00487 balance_split = xaccMallocSplit (qof_instance_get_book(trans)); 00488 00489 xaccTransBeginEdit (trans); 00490 xaccSplitSetParent(balance_split, trans); 00491 xaccSplitSetAccount(balance_split, account); 00492 xaccTransCommitEdit (trans); 00493 } 00494 00495 return balance_split; 00496 } 00497 00498 /* Find the trading split for a commodity, but don't create any splits 00499 or accounts if they don't already exist. */ 00500 static Split * 00501 find_trading_split (Transaction *trans, Account *root, 00502 gnc_commodity *commodity) 00503 { 00504 Account *trading_account; 00505 Account *ns_account; 00506 Account *account; 00507 00508 if (!root) 00509 { 00510 root = gnc_book_get_root_account (xaccTransGetBook (trans)); 00511 if (NULL == root) 00512 { 00513 /* This can't occur, things should be in books */ 00514 PERR ("Bad data corruption, no root account in book"); 00515 return NULL; 00516 } 00517 } 00518 00519 trading_account = gnc_account_lookup_by_name (root, _("Trading")); 00520 if (!trading_account) 00521 { 00522 return NULL; 00523 } 00524 00525 ns_account = gnc_account_lookup_by_name (trading_account, 00526 gnc_commodity_get_namespace(commodity)); 00527 if (!ns_account) 00528 { 00529 return NULL; 00530 } 00531 00532 account = gnc_account_lookup_by_name (ns_account, 00533 gnc_commodity_get_mnemonic(commodity)); 00534 if (!account) 00535 { 00536 return NULL; 00537 } 00538 00539 return xaccTransFindSplitByAccount(trans, account); 00540 } 00541 00542 static void 00543 add_balance_split (Transaction *trans, gnc_numeric imbalance, 00544 Account *root, Account *account) 00545 { 00546 const gnc_commodity *commodity; 00547 gnc_numeric old_value, new_value; 00548 Split *balance_split; 00549 gnc_commodity *currency = xaccTransGetCurrency (trans); 00550 00551 balance_split = get_balance_split(trans, root, account, currency); 00552 if (!balance_split) 00553 { 00554 /* Error already logged */ 00555 LEAVE(""); 00556 return; 00557 } 00558 account = xaccSplitGetAccount(balance_split); 00559 00560 xaccTransBeginEdit (trans); 00561 00562 old_value = xaccSplitGetValue (balance_split); 00563 00564 /* Note: We have to round for the commodity's fraction, NOT any 00565 * already existing denominator (bug #104343), because either one 00566 * of the denominators might already be reduced. */ 00567 new_value = gnc_numeric_sub (old_value, imbalance, 00568 gnc_commodity_get_fraction(currency), 00569 GNC_HOW_RND_ROUND_HALF_UP); 00570 00571 xaccSplitSetValue (balance_split, new_value); 00572 00573 commodity = xaccAccountGetCommodity (account); 00574 if (gnc_commodity_equiv (currency, commodity)) 00575 { 00576 xaccSplitSetAmount (balance_split, new_value); 00577 } 00578 00579 xaccSplitScrub (balance_split); 00580 xaccTransCommitEdit (trans); 00581 } 00582 00583 void 00584 xaccTransScrubImbalance (Transaction *trans, Account *root, 00585 Account *account) 00586 { 00587 const gnc_commodity *currency; 00588 00589 if (!trans) return; 00590 00591 ENTER ("()"); 00592 00593 /* Must look for orphan splits even if there is no imbalance. */ 00594 xaccTransScrubSplits (trans); 00595 00596 /* Return immediately if things are balanced. */ 00597 if (xaccTransIsBalanced (trans)) 00598 return; 00599 00600 currency = xaccTransGetCurrency (trans); 00601 00602 if (! xaccTransUseTradingAccounts (trans)) 00603 { 00604 gnc_numeric imbalance; 00605 00606 /* Make the value sum to zero */ 00607 imbalance = xaccTransGetImbalanceValue (trans); 00608 if (! gnc_numeric_zero_p (imbalance)) 00609 { 00610 PINFO ("Value unbalanced transaction"); 00611 00612 add_balance_split (trans, imbalance, root, account); 00613 } 00614 } 00615 else 00616 { 00617 MonetaryList *imbal_list; 00618 MonetaryList *imbalance_commod; 00619 GList *splits; 00620 gnc_numeric imbalance; 00621 Split *balance_split = NULL; 00622 00623 /* If there are existing trading splits, adjust the price or exchange 00624 rate in each of them to agree with the non-trading splits for the 00625 same commodity. If there are multiple non-trading splits for the 00626 same commodity in the transaction this will use the exchange rate in 00627 the last such split. This shouldn't happen, and if it does then there's 00628 not much we can do about it anyway. 00629 00630 While we're at it, compute the value imbalance ignoring existing 00631 trading splits. */ 00632 00633 imbalance = gnc_numeric_zero(); 00634 00635 for (splits = trans->splits; splits; splits = splits->next) 00636 { 00637 Split *split = splits->data; 00638 gnc_numeric value, amount; 00639 gnc_commodity *commodity; 00640 00641 if (! xaccTransStillHasSplit (trans, split)) continue; 00642 00643 commodity = xaccAccountGetCommodity (xaccSplitGetAccount(split)); 00644 if (!commodity) 00645 { 00646 PERR("Split has no commodity"); 00647 continue; 00648 } 00649 00650 balance_split = find_trading_split (trans, root, commodity); 00651 00652 if (balance_split != split) 00653 /* this is not a trading split */ 00654 imbalance = gnc_numeric_add(imbalance, xaccSplitGetValue (split), 00655 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); 00656 00657 /* Ignore splits where value or amount is zero */ 00658 value = xaccSplitGetValue (split); 00659 amount = xaccSplitGetAmount (split); 00660 if (gnc_numeric_zero_p(amount) || gnc_numeric_zero_p(value)) 00661 continue; 00662 00663 if (balance_split && balance_split != split) 00664 { 00665 gnc_numeric convrate = gnc_numeric_div (amount, value, 00666 GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); 00667 gnc_numeric old_value, new_value; 00668 old_value = xaccSplitGetValue(balance_split); 00669 new_value = gnc_numeric_div (xaccSplitGetAmount(balance_split), 00670 convrate, 00671 gnc_commodity_get_fraction(currency), 00672 GNC_HOW_RND_ROUND_HALF_UP); 00673 if (! gnc_numeric_equal (old_value, new_value)) 00674 { 00675 xaccTransBeginEdit (trans); 00676 xaccSplitSetValue (balance_split, new_value); 00677 xaccSplitScrub (balance_split); 00678 xaccTransCommitEdit (trans); 00679 } 00680 } 00681 } 00682 00683 /* Balance the value, ignoring existing trading splits */ 00684 if (! gnc_numeric_zero_p (imbalance)) 00685 { 00686 PINFO ("Value unbalanced transaction"); 00687 00688 add_balance_split (trans, imbalance, root, account); 00689 } 00690 00691 /* If the transaction is balanced, nothing more to do */ 00692 imbal_list = xaccTransGetImbalance (trans); 00693 if (!imbal_list) 00694 { 00695 LEAVE("()"); 00696 return; 00697 } 00698 00699 PINFO ("Currency unbalanced transaction"); 00700 00701 for (imbalance_commod = imbal_list; imbalance_commod; 00702 imbalance_commod = imbalance_commod->next) 00703 { 00704 gnc_monetary *imbal_mon = imbalance_commod->data; 00705 gnc_commodity *commodity; 00706 gnc_numeric old_amount, new_amount; 00707 gnc_numeric old_value, new_value, val_imbalance; 00708 GList *splits; 00709 00710 commodity = gnc_monetary_commodity (*imbal_mon); 00711 00712 balance_split = get_trading_split(trans, root, commodity); 00713 if (!balance_split) 00714 { 00715 /* Error already logged */ 00716 gnc_monetary_list_free(imbal_list); 00717 LEAVE(""); 00718 return; 00719 } 00720 00721 account = xaccSplitGetAccount(balance_split); 00722 00723 if (! gnc_commodity_equal (currency, commodity)) 00724 { 00725 /* Find the value imbalance in this commodity */ 00726 val_imbalance = gnc_numeric_zero(); 00727 for (splits = trans->splits; splits; splits = splits->next) 00728 { 00729 Split *split = splits->data; 00730 if (xaccTransStillHasSplit (trans, split) && 00731 gnc_commodity_equal (commodity, 00732 xaccAccountGetCommodity(xaccSplitGetAccount(split)))) 00733 val_imbalance = gnc_numeric_add (val_imbalance, xaccSplitGetValue (split), 00734 GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT); 00735 } 00736 } 00737 00738 xaccTransBeginEdit (trans); 00739 00740 old_amount = xaccSplitGetAmount (balance_split); 00741 new_amount = gnc_numeric_sub (old_amount, gnc_monetary_value(*imbal_mon), 00742 gnc_commodity_get_fraction(commodity), 00743 GNC_HOW_RND_ROUND_HALF_UP); 00744 00745 xaccSplitSetAmount (balance_split, new_amount); 00746 00747 if (gnc_commodity_equal (currency, commodity)) 00748 { 00749 /* Imbalance commodity is the transaction currency, value in the 00750 split must be the same as the amount */ 00751 xaccSplitSetValue (balance_split, new_amount); 00752 } 00753 else 00754 { 00755 old_value = xaccSplitGetValue (balance_split); 00756 new_value = gnc_numeric_sub (old_value, val_imbalance, 00757 gnc_commodity_get_fraction(currency), 00758 GNC_HOW_RND_ROUND_HALF_UP); 00759 00760 xaccSplitSetValue (balance_split, new_value); 00761 } 00762 00763 xaccSplitScrub (balance_split); 00764 xaccTransCommitEdit (trans); 00765 } 00766 00767 gnc_monetary_list_free(imbal_list); 00768 00769 if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans))) 00770 { 00771 /* This is probably because there are splits with zero amount 00772 and non-zero value. These are usually realized gain/loss 00773 splits. Add a reversing split for each of them to balance 00774 the value. */ 00775 00776 /* Copy the split list so we don't see the splits we're adding */ 00777 GList *splits_dup = g_list_copy(trans->splits); 00778 for (splits = splits_dup; splits; splits = splits->next) 00779 { 00780 Split *split = splits->data; 00781 if (! xaccTransStillHasSplit(trans, split)) continue; 00782 if (!gnc_numeric_zero_p(xaccSplitGetValue(split)) && 00783 gnc_numeric_zero_p(xaccSplitGetAmount(split))) 00784 { 00785 gnc_commodity *commodity; 00786 gnc_numeric old_value, new_value; 00787 00788 commodity = xaccAccountGetCommodity(xaccSplitGetAccount(split)); 00789 if (!commodity) 00790 { 00791 PERR("Split has no commodity"); 00792 continue; 00793 } 00794 balance_split = get_trading_split(trans, root, commodity); 00795 if (!balance_split) 00796 { 00797 /* Error already logged */ 00798 gnc_monetary_list_free(imbal_list); 00799 LEAVE(""); 00800 return; 00801 } 00802 account = xaccSplitGetAccount(balance_split); 00803 00804 xaccTransBeginEdit (trans); 00805 00806 old_value = xaccSplitGetValue (balance_split); 00807 new_value = gnc_numeric_sub (old_value, xaccSplitGetValue(split), 00808 gnc_commodity_get_fraction(currency), 00809 GNC_HOW_RND_ROUND_HALF_UP); 00810 xaccSplitSetValue (balance_split, new_value); 00811 00812 /* Don't change the balance split's amount since the amount 00813 is zero in the split we're working on */ 00814 00815 xaccSplitScrub (balance_split); 00816 xaccTransCommitEdit (trans); 00817 } 00818 } 00819 00820 g_list_free(splits_dup); 00821 00822 if (!gnc_numeric_zero_p(xaccTransGetImbalanceValue(trans))) 00823 PERR("Balancing currencies unbalanced value"); 00824 } 00825 } 00826 LEAVE ("()"); 00827 } 00828 00829 /* ================================================================ */ 00830 /* The xaccTransFindCommonCurrency () method returns 00831 * a gnc_commodity indicating a currency denomination that all 00832 * of the splits in this transaction have in common, using the 00833 * old/obsolete currency/security fields of the split accounts. 00834 */ 00835 00836 static gnc_commodity * 00837 FindCommonExclSCurrency (SplitList *splits, 00838 gnc_commodity * ra, gnc_commodity * rb, 00839 Split *excl_split) 00840 { 00841 GList *node; 00842 00843 if (!splits) return NULL; 00844 00845 for (node = splits; node; node = node->next) 00846 { 00847 Split *s = node->data; 00848 gnc_commodity * sa, * sb; 00849 00850 if (s == excl_split) continue; 00851 00852 g_return_val_if_fail (s->acc, NULL); 00853 00854 sa = DxaccAccountGetCurrency (s->acc); 00855 sb = xaccAccountGetCommodity (s->acc); 00856 00857 if (ra && rb) 00858 { 00859 int aa = !gnc_commodity_equiv(ra, sa); 00860 int ab = !gnc_commodity_equiv(ra, sb); 00861 int ba = !gnc_commodity_equiv(rb, sa); 00862 int bb = !gnc_commodity_equiv(rb, sb); 00863 00864 if ( (!aa) && bb) rb = NULL; 00865 else if ( (!ab) && ba) rb = NULL; 00866 else if ( (!ba) && ab) ra = NULL; 00867 else if ( (!bb) && aa) ra = NULL; 00868 else if ( aa && bb && ab && ba ) 00869 { 00870 ra = NULL; 00871 rb = NULL; 00872 } 00873 00874 if (!ra) 00875 { 00876 ra = rb; 00877 rb = NULL; 00878 } 00879 } 00880 else if (ra && !rb) 00881 { 00882 int aa = !gnc_commodity_equiv(ra, sa); 00883 int ab = !gnc_commodity_equiv(ra, sb); 00884 if ( aa && ab ) ra = NULL; 00885 } 00886 else if (!ra && rb) 00887 { 00888 int aa = !gnc_commodity_equiv(rb, sa); 00889 int ab = !gnc_commodity_equiv(rb, sb); 00890 ra = ( aa && ab ) ? NULL : rb; 00891 } 00892 00893 if ((!ra) && (!rb)) return NULL; 00894 } 00895 00896 return (ra); 00897 } 00898 00899 /* This is the wrapper for those calls (i.e. the older ones) which 00900 * don't exclude one split from the splitlist when looking for a 00901 * common currency. 00902 */ 00903 static gnc_commodity * 00904 FindCommonCurrency (GList *splits, gnc_commodity * ra, gnc_commodity * rb) 00905 { 00906 return FindCommonExclSCurrency(splits, ra, rb, NULL); 00907 } 00908 00909 static gnc_commodity * 00910 xaccTransFindOldCommonCurrency (Transaction *trans, QofBook *book) 00911 { 00912 gnc_commodity *ra, *rb, *retval; 00913 Split *split; 00914 00915 if (!trans) return NULL; 00916 00917 if (trans->splits == NULL) return NULL; 00918 00919 g_return_val_if_fail (book, NULL); 00920 00921 split = trans->splits->data; 00922 00923 if (!split || NULL == split->acc) return NULL; 00924 00925 ra = DxaccAccountGetCurrency (split->acc); 00926 rb = xaccAccountGetCommodity (split->acc); 00927 00928 retval = FindCommonCurrency (trans->splits, ra, rb); 00929 00930 /* Compare this value to what we think should be the 'right' value */ 00931 if (!trans->common_currency) 00932 { 00933 trans->common_currency = retval; 00934 } 00935 else if (!gnc_commodity_equiv (retval, trans->common_currency)) 00936 { 00937 char guid_str[GUID_ENCODING_LENGTH + 1]; 00938 guid_to_string_buff(xaccTransGetGUID(trans), guid_str); 00939 PWARN ("expected common currency %s but found %s in txn %s\n", 00940 gnc_commodity_get_unique_name (trans->common_currency), 00941 gnc_commodity_get_unique_name (retval), guid_str); 00942 } 00943 00944 if (NULL == retval) 00945 { 00946 /* In every situation I can think of, this routine should return 00947 * common currency. So make note of this ... */ 00948 char guid_str[GUID_ENCODING_LENGTH + 1]; 00949 guid_to_string_buff(xaccTransGetGUID(trans), guid_str); 00950 PWARN ("unable to find a common currency in txn %s, and that is strange.", 00951 guid_str); 00952 } 00953 00954 return retval; 00955 } 00956 00957 /* Test the currency of the splits and find the most common and return 00958 * it, or NULL if there is no currency more common than the 00959 * others -- or none at all. 00960 */ 00961 typedef struct 00962 { 00963 gnc_commodity *commodity; 00964 unsigned int count; 00965 } CommodityCount; 00966 00967 static gint 00968 commodity_equal (gconstpointer a, gconstpointer b) 00969 { 00970 CommodityCount *cc = (CommodityCount*)a; 00971 gnc_commodity *com = (gnc_commodity*)b; 00972 if ( cc == NULL || cc->commodity == NULL || 00973 !GNC_IS_COMMODITY( cc->commodity ) ) return -1; 00974 if ( com == NULL || !GNC_IS_COMMODITY( com ) ) return 1; 00975 if ( gnc_commodity_equal(cc->commodity, com) ) 00976 return 0; 00977 return 1; 00978 } 00979 00980 static gint 00981 commodity_compare( gconstpointer a, gconstpointer b) 00982 { 00983 CommodityCount *ca = (CommodityCount*)a, *cb = (CommodityCount*)b; 00984 if (ca == NULL || ca->commodity == NULL || 00985 !GNC_IS_COMMODITY( ca->commodity ) ) 00986 { 00987 if (cb == NULL || cb->commodity == NULL || 00988 !GNC_IS_COMMODITY( cb->commodity ) ) 00989 return 0; 00990 return -1; 00991 } 00992 if (cb == NULL || cb->commodity == NULL || 00993 !GNC_IS_COMMODITY( cb->commodity ) ) 00994 return 1; 00995 if (ca->count == cb->count) 00996 return 0; 00997 return ca->count > cb->count ? 1 : -1; 00998 } 00999 01000 /* Find the commodities in the account of each of the splits of a 01001 * transaction, and rank them by how many splits in which they 01002 * occur. Commodities which are currencies count more than those which 01003 * aren't, because for simple buy and sell transactions it makes 01004 * slightly more sense for the transaction commodity to be the 01005 * currency -- to the extent that it makes sense for a transaction to 01006 * have a currency at all. jralls, 2010-11-02 */ 01007 01008 static gnc_commodity * 01009 xaccTransFindCommonCurrency (Transaction *trans, QofBook *book) 01010 { 01011 gnc_commodity *com_first, *com_scratch; 01012 GList *node = NULL; 01013 GSList *comlist = NULL, *found = NULL; 01014 int score = 0; 01015 01016 if (!trans) return NULL; 01017 01018 if (trans->splits == NULL) return NULL; 01019 01020 g_return_val_if_fail (book, NULL); 01021 01022 for (node = trans->splits; node; node = node->next) 01023 { 01024 Split *s = node->data; 01025 if (s == NULL || s->acc == NULL) continue; 01026 com_scratch = xaccAccountGetCommodity(s->acc); 01027 if ( comlist ) 01028 { 01029 found = g_slist_find_custom(comlist, com_scratch, commodity_equal); 01030 } 01031 if (comlist == NULL || found == NULL) 01032 { 01033 CommodityCount *count = g_slice_new0(CommodityCount); 01034 count->commodity = com_scratch; 01035 count->count = ( gnc_commodity_is_currency( com_scratch ) ? 3 : 2 ); 01036 comlist = g_slist_append(comlist, count); 01037 } 01038 else 01039 { 01040 CommodityCount *count = (CommodityCount*)(found->data); 01041 count->count += ( gnc_commodity_is_currency( com_scratch ) ? 3 : 2 ); 01042 } 01043 } 01044 found = g_slist_sort( comlist, commodity_compare); 01045 01046 if ( ((CommodityCount*)(found->data))->commodity != NULL) 01047 { 01048 return ((CommodityCount*)(found->data))->commodity; 01049 } 01050 /* We didn't find a currency in the current account structure, so try 01051 * an old one. */ 01052 return xaccTransFindOldCommonCurrency( trans, book ); 01053 } 01054 01055 /* ================================================================ */ 01056 01057 void 01058 xaccTransScrubCurrency (Transaction *trans) 01059 { 01060 SplitList *node; 01061 gnc_commodity *currency; 01062 01063 if (!trans) return; 01064 01065 /* If there are any orphaned splits in a transaction, then the 01066 * this routine will fail. Therefore, we want to make sure that 01067 * there are no orphans (splits without parent account). 01068 */ 01069 xaccTransScrubOrphans (trans); 01070 01071 currency = xaccTransGetCurrency (trans); 01072 if (currency) return; 01073 01074 currency = xaccTransFindCommonCurrency (trans, qof_instance_get_book(trans)); 01075 if (currency) 01076 { 01077 xaccTransBeginEdit (trans); 01078 xaccTransSetCurrency (trans, currency); 01079 xaccTransCommitEdit (trans); 01080 } 01081 else 01082 { 01083 if (NULL == trans->splits) 01084 { 01085 PWARN ("Transaction \"%s\" has no splits in it!", trans->description); 01086 } 01087 else 01088 { 01089 SplitList *node; 01090 char guid_str[GUID_ENCODING_LENGTH + 1]; 01091 guid_to_string_buff(xaccTransGetGUID(trans), guid_str); 01092 PWARN ("no common transaction currency found for trans=\"%s\" (%s)", 01093 trans->description, guid_str); 01094 01095 for (node = trans->splits; node; node = node->next) 01096 { 01097 Split *split = node->data; 01098 if (NULL == split->acc) 01099 { 01100 PWARN (" split=\"%s\" is not in any account!", split->memo); 01101 } 01102 else 01103 { 01104 PWARN (" split=\"%s\" account=\"%s\" commodity=\"%s\"", 01105 split->memo, xaccAccountGetName(split->acc), 01106 gnc_commodity_get_mnemonic(xaccAccountGetCommodity(split->acc))); 01107 } 01108 } 01109 } 01110 } 01111 01112 for (node = trans->splits; node; node = node->next) 01113 { 01114 Split *sp = node->data; 01115 01116 if (!gnc_numeric_equal(xaccSplitGetAmount (sp), 01117 xaccSplitGetValue (sp))) 01118 { 01119 gnc_commodity *acc_currency; 01120 01121 acc_currency = sp->acc ? xaccAccountGetCommodity(sp->acc) : NULL; 01122 if (acc_currency == currency) 01123 { 01124 /* This Split needs fixing: The transaction-currency equals 01125 * the account-currency/commodity, but the amount/values are 01126 * inequal i.e. they still correspond to the security 01127 * (amount) and the currency (value). In the new model, the 01128 * value is the amount in the account-commodity -- so it 01129 * needs to be set to equal the amount (since the 01130 * account-currency doesn't exist anymore). 01131 * 01132 * Note: Nevertheless we lose some information here. Namely, 01133 * the information that the 'amount' in 'account-old-security' 01134 * was worth 'value' in 'account-old-currency'. Maybe it would 01135 * be better to store that information in the price database? 01136 * But then, for old currency transactions there is still the 01137 * 'other' transaction, which is going to keep that 01138 * information. So I don't bother with that here. -- cstim, 01139 * 2002/11/20. */ 01140 /* But if the commodity *isn't* a currency, then it's 01141 * the value that should be changed to the 01142 * amount. jralls, 2010-11-02 */ 01143 01144 PWARN ("Adjusted split with mismatched values, desc=\"%s\" memo=\"%s\"" 01145 " old amount %s %s, new amount %s", 01146 trans->description, sp->memo, 01147 gnc_num_dbg_to_string (xaccSplitGetAmount(sp)), 01148 gnc_commodity_get_mnemonic (currency), 01149 gnc_num_dbg_to_string (xaccSplitGetValue(sp))); 01150 xaccTransBeginEdit (trans); 01151 if ( gnc_commodity_is_currency( currency)) 01152 { 01153 xaccSplitSetAmount (sp, xaccSplitGetValue(sp)); 01154 } 01155 else 01156 { 01157 xaccSplitSetValue(sp, xaccSplitGetAmount(sp)); 01158 } 01159 xaccTransCommitEdit (trans); 01160 } 01161 /*else 01162 { 01163 PINFO ("Ok: Split '%s' Amount %s %s, value %s %s", 01164 xaccSplitGetMemo (sp), 01165 gnc_num_dbg_to_string (amount), 01166 gnc_commodity_get_mnemonic (currency), 01167 gnc_num_dbg_to_string (value), 01168 gnc_commodity_get_mnemonic (acc_currency)); 01169 }*/ 01170 } 01171 } 01172 01173 } 01174 01175 /* ================================================================ */ 01176 01177 void 01178 xaccAccountScrubCommodity (Account *account) 01179 { 01180 gnc_commodity *commodity; 01181 01182 if (!account) return; 01183 if (xaccAccountGetType(account) == ACCT_TYPE_ROOT) return; 01184 01185 commodity = xaccAccountGetCommodity (account); 01186 if (commodity) return; 01187 01188 /* Use the 'obsolete' routines to try to figure out what the 01189 * account commodity should have been. */ 01190 commodity = xaccAccountGetCommodity (account); 01191 if (commodity) 01192 { 01193 xaccAccountSetCommodity (account, commodity); 01194 return; 01195 } 01196 01197 commodity = DxaccAccountGetCurrency (account); 01198 if (commodity) 01199 { 01200 xaccAccountSetCommodity (account, commodity); 01201 return; 01202 } 01203 01204 PERR ("Account \"%s\" does not have a commodity!", 01205 xaccAccountGetName(account)); 01206 } 01207 01208 /* ================================================================ */ 01209 01210 static void 01211 xaccAccountDeleteOldData (Account *account) 01212 { 01213 if (!account) return; 01214 xaccAccountBeginEdit (account); 01215 01216 kvp_frame_set_slot_nc (account->inst.kvp_data, "old-currency", NULL); 01217 kvp_frame_set_slot_nc (account->inst.kvp_data, "old-security", NULL); 01218 kvp_frame_set_slot_nc (account->inst.kvp_data, "old-currency-scu", NULL); 01219 kvp_frame_set_slot_nc (account->inst.kvp_data, "old-security-scu", NULL); 01220 qof_instance_set_dirty (QOF_INSTANCE (account)); 01221 xaccAccountCommitEdit (account); 01222 } 01223 01224 static int 01225 scrub_trans_currency_helper (Transaction *t, gpointer data) 01226 { 01227 xaccTransScrubCurrency (t); 01228 return 0; 01229 } 01230 01231 static void 01232 scrub_account_commodity_helper (Account *account, gpointer data) 01233 { 01234 xaccAccountScrubCommodity (account); 01235 xaccAccountDeleteOldData (account); 01236 } 01237 01238 void 01239 xaccAccountTreeScrubCommodities (Account *acc) 01240 { 01241 if (!acc) return; 01242 01243 xaccAccountTreeForEachTransaction (acc, scrub_trans_currency_helper, NULL); 01244 01245 scrub_account_commodity_helper (acc, NULL); 01246 gnc_account_foreach_descendant (acc, scrub_account_commodity_helper, NULL); 01247 } 01248 01249 /* ================================================================ */ 01250 01251 static gboolean 01252 check_quote_source (gnc_commodity *com, gpointer data) 01253 { 01254 gboolean *commodity_has_quote_src = (gboolean *)data; 01255 if (com && !gnc_commodity_is_iso(com)) 01256 *commodity_has_quote_src |= gnc_commodity_get_quote_flag(com); 01257 return TRUE; 01258 } 01259 01260 static void 01261 move_quote_source (Account *account, gpointer data) 01262 { 01263 gnc_commodity *com; 01264 gnc_quote_source *quote_source; 01265 gboolean new_style = GPOINTER_TO_INT(data); 01266 const char *source, *tz; 01267 01268 com = xaccAccountGetCommodity(account); 01269 if (!com) 01270 return; 01271 01272 if (!new_style) 01273 { 01274 source = dxaccAccountGetPriceSrc(account); 01275 if (!source || !*source) 01276 return; 01277 tz = dxaccAccountGetQuoteTZ(account); 01278 01279 PINFO("to %8s from %s", gnc_commodity_get_mnemonic(com), 01280 xaccAccountGetName(account)); 01281 gnc_commodity_set_quote_flag(com, TRUE); 01282 quote_source = gnc_quote_source_lookup_by_internal(source); 01283 if (!quote_source) 01284 quote_source = gnc_quote_source_add_new(source, FALSE); 01285 gnc_commodity_set_quote_source(com, quote_source); 01286 gnc_commodity_set_quote_tz(com, tz); 01287 } 01288 01289 dxaccAccountSetPriceSrc(account, NULL); 01290 dxaccAccountSetQuoteTZ(account, NULL); 01291 return; 01292 } 01293 01294 01295 void 01296 xaccAccountTreeScrubQuoteSources (Account *root, gnc_commodity_table *table) 01297 { 01298 gboolean new_style = FALSE; 01299 ENTER(" "); 01300 01301 if (!root || !table) 01302 { 01303 LEAVE("Oops"); 01304 return; 01305 } 01306 01307 gnc_commodity_table_foreach_commodity (table, check_quote_source, &new_style); 01308 01309 move_quote_source(root, GINT_TO_POINTER(new_style)); 01310 gnc_account_foreach_descendant (root, move_quote_source, 01311 GINT_TO_POINTER(new_style)); 01312 LEAVE("Migration done"); 01313 } 01314 01315 /* ================================================================ */ 01316 01317 void 01318 xaccAccountScrubKvp (Account *account) 01319 { 01320 const gchar *str; 01321 gchar *str2; 01322 kvp_frame *frame; 01323 01324 if (!account) return; 01325 01326 str = kvp_frame_get_string(account->inst.kvp_data, "notes"); 01327 if (str) 01328 { 01329 str2 = g_strstrip(g_strdup(str)); 01330 if (strlen(str2) == 0) 01331 kvp_frame_set_slot_nc (account->inst.kvp_data, "notes", NULL); 01332 g_free(str2); 01333 } 01334 01335 str = kvp_frame_get_string(account->inst.kvp_data, "placeholder"); 01336 if (str && strcmp(str, "false") == 0) 01337 kvp_frame_set_slot_nc (account->inst.kvp_data, "placeholder", NULL); 01338 01339 frame = kvp_frame_get_frame(account->inst.kvp_data, "hbci"); 01340 if (frame && kvp_frame_is_empty(frame)) 01341 { 01342 kvp_frame_set_frame_nc(account->inst.kvp_data, "hbci", NULL); 01343 } 01344 } 01345 01346 /* ================================================================ */ 01347 01348 Account * 01349 xaccScrubUtilityGetOrMakeAccount (Account *root, gnc_commodity * currency, 01350 const char *accname, GNCAccountType acctype, 01351 gboolean placeholder) 01352 { 01353 Account * acc; 01354 01355 g_return_val_if_fail (root, NULL); 01356 01357 /* build the account name */ 01358 if (!currency) 01359 { 01360 PERR ("No currency specified!"); 01361 return NULL; 01362 } 01363 01364 /* See if we've got one of these going already ... */ 01365 acc = gnc_account_lookup_by_name(root, accname); 01366 01367 if (acc == NULL) 01368 { 01369 /* Guess not. We'll have to build one. */ 01370 acc = xaccMallocAccount(gnc_account_get_book (root)); 01371 xaccAccountBeginEdit (acc); 01372 xaccAccountSetName (acc, accname); 01373 xaccAccountSetCommodity (acc, currency); 01374 xaccAccountSetType (acc, acctype); 01375 xaccAccountSetPlaceholder (acc, placeholder); 01376 01377 /* Hang the account off the root. */ 01378 gnc_account_append_child (root, acc); 01379 xaccAccountCommitEdit (acc); 01380 } 01381 01382 return acc; 01383 } 01384 01385 /* ==================== END OF FILE ==================== */
1.7.4