GnuCash 2.4.99
Scrub.c
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 ==================== */
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines