/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>  *
 ***************************************************************************/
/** @file
 * This file implements classes SKGAccountObject.
 *
 * @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgaccountobject.h"
#include "skgbankobject.h"
#include "skgoperationobject.h"
#include "skgsuboperationobject.h"
#include "skgunitobject.h"
#include "skginterestobject.h"
#include "skgdocumentbank.h"
#include "skgtraces.h"

#include <klocale.h>

SKGAccountObject::SKGAccountObject(SKGDocument* iDocument, int iID) : SKGNamedObject(iDocument, "v_account", iID) {}

SKGAccountObject::~SKGAccountObject() {}

SKGAccountObject::SKGAccountObject(const SKGAccountObject& iObject)
    : SKGNamedObject(iObject)
{}

SKGAccountObject::SKGAccountObject(const SKGNamedObject& iObject)
    : SKGNamedObject(iObject.getDocument(), "v_account", iObject.getID())
{
    if (iObject.getRealTable() == "account") {
        copyFrom(iObject);
    } else {
        *this = SKGNamedObject(iObject.getDocument(), "v_account", iObject.getID());
    }
}

SKGAccountObject::SKGAccountObject(const SKGObjectBase& iObject)
{
    if (iObject.getRealTable() == "account") {
        copyFrom(iObject);
    } else {
        *this = SKGNamedObject(iObject.getDocument(), "v_account", iObject.getID());
    }
}

const SKGAccountObject& SKGAccountObject::operator= (const SKGObjectBase& iObject)
{
    copyFrom(iObject);
    return *this;
}

SKGError SKGAccountObject::setInitialBalance(double iBalance, const SKGUnitObject& iUnit)
{
    SKGError err;
    SKGTRACEINRC(10, "SKGAccountObject::setInitialBalance", err);
    if (getDocument()) {
        //Delete previous initial balance for this account
        err = getDocument()->executeSqliteOrder("DELETE FROM operation  WHERE d_date='0000-00-00' AND rd_account_id=" % SKGServices::intToString(getID()));

        //Creation of new initial balance
        if (!err) {
            SKGOperationObject initialBalanceOp;
            err = addOperation(initialBalanceOp);
            if (!err) err = initialBalanceOp.setAttribute("d_date", "0000-00-00");
            if (!err) err = initialBalanceOp.setUnit(iUnit);
            if (!err) err = initialBalanceOp.setStatus(SKGOperationObject::CHECKED);
            if (!err) err = initialBalanceOp.save();

            SKGSubOperationObject initialBalanceSubOp;
            if (!err) err = initialBalanceOp.addSubOperation(initialBalanceSubOp);
            if (!err) err = initialBalanceSubOp.setQuantity(iBalance);
            if (!err) err = initialBalanceSubOp.save();
        }
    }
    return err;
}

SKGError SKGAccountObject::getInitialBalance(double& oBalance, SKGUnitObject& oUnit)
{
    SKGError err;
    SKGTRACEINRC(10, "SKGAccountObject::getInitialBalance", err);
    //Initialisation
    oBalance = 0;
    oUnit = SKGUnitObject();
    QString unitName = static_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit().Symbol;

    //Get initial balance
    SKGStringListList listTmp;
    err = getDocument()->executeSelectSqliteOrder("SELECT f_QUANTITY, t_UNIT FROM  v_operation_consolidated  WHERE d_date='0000-00-00' AND rd_account_id=" % SKGServices::intToString(getID()), listTmp);
    if (!err && listTmp.count() > 1) {
        oBalance = SKGServices::stringToDouble(listTmp.at(1).at(0));
        unitName = listTmp.at(1).at(1);

        oUnit = SKGUnitObject(getDocument());
        err = oUnit.setSymbol(unitName);
        if (!err) err = oUnit.load();
    }
    return err;
}

SKGError SKGAccountObject::setBank(const SKGBankObject& iBank)
{
    return setAttribute("rd_bank_id", SKGServices::intToString(iBank.getID()));
}

SKGError SKGAccountObject::getBank(SKGBankObject& oBank) const
{
    SKGError err = getDocument()->getObject("v_bank", "id=" % getAttribute("rd_bank_id"), oBank);
    return err;
}

SKGError SKGAccountObject::setNumber(const QString& iNumber)
{
    return setAttribute("t_number", iNumber);
}

QString SKGAccountObject::getNumber() const
{
    return getAttribute("t_number");
}

SKGError SKGAccountObject::setComment(const QString& iComment)
{
    return setAttribute("t_comment", iComment);
}

QString SKGAccountObject::getComment() const
{
    return getAttribute("t_comment");
}

SKGError SKGAccountObject::setAgencyNumber(const QString& iNumber)
{
    return setAttribute("t_agency_number", iNumber);
}

QString SKGAccountObject::getAgencyNumber() const
{
    return getAttribute("t_agency_number");
}

SKGError SKGAccountObject::setAgencyAddress(const QString& iAddress)
{
    return setAttribute("t_agency_address", iAddress);
}

QString SKGAccountObject::getAgencyAddress() const
{
    return getAttribute("t_agency_address");
}

SKGError SKGAccountObject::addOperation(SKGOperationObject& oOperation)
{
    SKGError err;
    if (getID() == 0) err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QString("SKGAccountObject::addOperation")));
    else {
        oOperation = SKGOperationObject(getDocument());
        err = oOperation.setParentAccount(*this);
    }
    return err;
}

int SKGAccountObject::getNbOperation() const
{
    int nb = 0;
    if (getDocument()) getDocument()->getNbObjects("operation", "rd_account_id=" % SKGServices::intToString(getID()), nb);
    return nb;
}

SKGError SKGAccountObject::getOperations(SKGListSKGObjectBase& oOperations) const
{
    SKGError err;
    if (getDocument()) {
        err = getDocument()->getObjects("v_operation",
                                        "rd_account_id=" % SKGServices::intToString(getID()) ,
                                        oOperations);
    }
    return err;
}

double SKGAccountObject::getCurrentAmount() const
{
    return SKGServices::stringToDouble(getAttribute("f_CURRENTAMOUNT"));
}

double SKGAccountObject::getAmount(const QDate& iDate) const
{
    double output = 0;
    if (getDocument()) {
        SKGStringListList listTmp;
        SKGError err = getDocument()->executeSelectSqliteOrder("SELECT TOTAL(f_QUANTITY), rc_unit_id FROM v_operation  WHERE "
                       "d_date<='" % SKGServices::dateToSqlString(QDateTime(iDate)) % "' AND t_template='N' AND rd_account_id=" % SKGServices::intToString(getID()) %
                       " GROUP BY rc_unit_id",
                       listTmp);
        int nb = listTmp.count();
        for (int i = 1; !err && i < nb ; ++i) {
            QString quantity = listTmp.at(i).at(0);
            QString unitid = listTmp.at(i).at(1);

            double coef = 1;
            QString val = getDocument()->getCachedValue("unitvalue-" % unitid);
            if (!val.isEmpty()) {
                //Yes
                coef = SKGServices::stringToDouble(val);
            } else {
                //No
                SKGUnitObject unit(getDocument(), SKGServices::stringToInt(unitid));
                if (unit.getType() != SKGUnitObject::PRIMARY) coef = unit.getAmount(iDate);
            }

            output += coef * SKGServices::stringToDouble(quantity);
        }
    }
    return output;
}

SKGError SKGAccountObject::setType(SKGAccountObject::AccountType iType)
{
    SKGError err;
    if (!err) err = setAttribute("t_type", (iType == CURRENT ? "C" : (iType == CREDITCARD ? "D" : (iType == ASSETS ? "A" : (iType == INVESTMENT ? "I" : (iType == WALLET ? "W" : (iType == LOAN ? "L" : "O")))))));
    return err;
}

SKGAccountObject::AccountType SKGAccountObject::getType() const
{
    QString typeString = getAttribute("t_type");
    return (typeString == "C" ? CURRENT : (typeString == "D" ? CREDITCARD : (typeString == "A" ? ASSETS : (typeString == "I" ? INVESTMENT : (typeString == "W" ? (typeString == "L" ? LOAN : OTHER) : OTHER)))));
}

SKGError SKGAccountObject::setClosed(bool iClosed)
{
    return setAttribute("t_close", iClosed ? "Y" : "N");
}

bool SKGAccountObject::isClosed() const
{
    return (getAttribute("t_close") == "Y" ? true : false);
}

SKGError SKGAccountObject::bookmark(bool iBookmark)
{
    return setAttribute("t_bookmarked", iBookmark ? "Y" : "N");

}

bool SKGAccountObject::isBookmarked() const
{
    return (getAttribute("t_bookmarked") == "Y" ? true : false);
}

SKGError SKGAccountObject::getUnit(SKGUnitObject& oUnit) const
{
    //Get initial amount
    SKGStringListList listTmp;
    SKGError err = getDocument()->executeSelectSqliteOrder("SELECT t_UNIT FROM  v_operation_consolidated  WHERE d_date='0000-00-00' AND rd_account_id=" % SKGServices::intToString(getID()), listTmp);
    if (!err) {
        //Is initial amount existing ?
        if (listTmp.count() > 1) {
            //Yes ==> then the amount is the amount of the initial value
            oUnit = SKGUnitObject(getDocument());
            err = oUnit.setSymbol(listTmp.at(1).at(0));
            if (!err) err = oUnit.load();
        } else {
            //No ==> we get the preferred unit
            SKGObjectBase::SKGListSKGObjectBase units;
            err = getDocument()->getObjects("v_unit",
                                            "t_type IN ('1', '2', 'C') AND EXISTS(SELECT 1 FROM operation WHERE rc_unit_id=v_unit.id AND rd_account_id=" % SKGServices::intToString(getID()) % ") ORDER BY t_type", units);
            int nb = units.count();
            if (nb) oUnit = units.at(0);
        }
    }
    return err;
}

SKGError SKGAccountObject::addInterest(SKGInterestObject& oInterest)
{
    SKGError err;
    if (getID() == 0) err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QString("SKGAccountObject::addInterest")));
    else {
        oInterest = SKGInterestObject(static_cast<SKGDocumentBank*>(getDocument()));
        err = oInterest.setAccount(*this);
    }
    return err;
}

SKGError SKGAccountObject::getInterests(SKGListSKGObjectBase& oInterestList) const
{
    SKGError err = getDocument()->getObjects("v_interest",
                   "rd_account_id=" % SKGServices::intToString(getID()),
                   oInterestList);
    return err;
}

SKGError SKGAccountObject::getInterest(const QDate& iDate, SKGInterestObject& oInterest) const
{
    QString ids = SKGServices::intToString(getID());
    QString dates = SKGServices::dateToSqlString(QDateTime(iDate));
    SKGError err = SKGObjectBase::getDocument()->getObject("v_interest",
                   "rd_account_id=" % ids % " AND d_date<='" % dates %
                   "' AND  ABS(strftime('%s','" % dates %
                   "')-strftime('%s',d_date))=(SELECT MIN(ABS(strftime('%s','" % dates %
                   "')-strftime('%s',u2.d_date))) FROM interest u2 WHERE u2.rd_account_id=" % ids %
                   " AND u2.d_date<='" % dates % "')",
                   oInterest);

    //If not found then get first
    if (err) err = SKGObjectBase::getDocument()->getObject("v_interest",
                       "rd_account_id=" % SKGServices::intToString(getID()) % " AND d_date=(SELECT MIN(d_date) FROM interest WHERE rd_account_id=" %
                       SKGServices::intToString(getID()) % ')',
                       oInterest);
    return err;
}

SKGError SKGAccountObject::getInterestItems(QList<SKGAccountObject::SKGInterestItem>& oInterestList, double& oInterests, int iYear) const
{
    oInterestList.clear();
    SKGError err;

    //Initial date
    int y = iYear;
    if (y == 0) y = QDate::currentDate().year();
    QDate initialDate = QDate(y, 1, 1);
    QDate lastDate = QDate(y, 12, 31);

    oInterests = 0;
    bool computationNeeded = false;

    //Add operations
    SKGObjectBase::SKGListSKGObjectBase items;
    err = getDocument()->getObjects("v_operation", "rd_account_id=" % SKGServices::intToString(getID()) %
                                    " AND d_date>='" % SKGServices::dateToSqlString(QDateTime(initialDate)) % "' "
                                    " AND d_date<='" % SKGServices::dateToSqlString(QDateTime(lastDate)) % "' ORDER BY d_date", items);
    int nb = items.count();
    for (int i = 0; !err && i < nb; ++i) {
        SKGOperationObject ob = items.at(i);

        SKGInterestItem itemI;
        itemI.object = ob;
        itemI.date = ob.getDate();
        itemI.valueDate = itemI.date;
        itemI.rate = 0;
        itemI.base = 0;
        itemI.coef = 0;
        itemI.annualInterest = 0;
        itemI.accruedInterest = 0;
        itemI.amount = ob.getCurrentAmount();

        oInterestList.push_back(itemI);
    }

    //Add interest
    if (!err) {
        err = getDocument()->getObjects("v_interest", "rd_account_id=" % SKGServices::intToString(getID()) %
                                        " AND d_date>='" % SKGServices::dateToSqlString(QDateTime(initialDate)) % "' "
                                        " AND d_date<='" % SKGServices::dateToSqlString(QDateTime(lastDate)) % "' ORDER BY d_date", items);

        int pos = 0;
        int nb = items.count();
        for (int i = 0; !err && i < nb; ++i) {
            SKGInterestObject ob = items.at(i);

            SKGInterestItem itemI;
            itemI.object = ob;
            itemI.date = ob.getDate();
            itemI.valueDate = itemI.date;
            itemI.rate = ob.getRate();
            itemI.base = SKGServices::stringToInt(ob.getAttribute("t_base"));
            itemI.coef = 0;
            itemI.annualInterest = 0;
            itemI.accruedInterest = 0;
            itemI.amount = 0;

            int nb2 = oInterestList.count();
            for (int j = pos; !err && j < nb2; ++j) {
                if (itemI.date <= oInterestList.at(j).date) {
                    break;
                } else {
                    ++pos;
                }
            }

            oInterestList.insert(pos, itemI);
            computationNeeded = true;
        }
    }

    //Get first interest
    if (!err) {
        SKGInterestObject firstInterest;
        if (getInterest(initialDate, firstInterest).isSucceeded()) {
            if (firstInterest.getDate() < initialDate) {
                SKGInterestItem itemI;
                itemI.object = firstInterest;
                itemI.date = initialDate;
                itemI.valueDate = initialDate;
                itemI.rate = firstInterest.getRate();
                itemI.base = 0;
                itemI.coef = 0;
                itemI.annualInterest = 0;
                itemI.accruedInterest = 0;
                itemI.amount = 0;

                oInterestList.insert(0, itemI);
                computationNeeded = true;
            }
        }
    }

    //Launch computation
    if (!err) {
        if (computationNeeded) {

            err = computeInterestItems(oInterestList, oInterests, y);
        } else {
            //Drop temporary table
            if (!err) {
                err = getDocument()->executeSqliteOrder("DROP TABLE IF EXISTS interest_result");
            }
        }
    }
    return err;
}

SKGError SKGAccountObject::computeInterestItems(QList<SKGAccountObject::SKGInterestItem>& ioInterestList, double& oInterests, int iYear) const
{
    SKGError err;

    //Sum annual interest
    oInterests = 0;

    //Initial date
    int y = iYear;
    if (y == 0) y = QDate::currentDate().year();
    QDate initialDate = QDate(y, 1, 1);

    //Default interest item
    SKGInterestItem currentInterest;
    currentInterest.date = initialDate;
    currentInterest.valueDate = currentInterest.date;
    currentInterest.rate = 0;
    currentInterest.coef = 0;
    currentInterest.annualInterest = 0;
    currentInterest.accruedInterest = 0;

    int nb = ioInterestList.count();
    for (int i = 0; !err && i < nb; ++i) {
        SKGInterestItem tmp = ioInterestList.at(i);
        SKGObjectBase object = tmp.object;
        if (object.getRealTable() == "operation") {
            //Get operations
            SKGOperationObject op = object;

            //Get current amount
            tmp.amount = op.getCurrentAmount();

            //Get value date computation mode
            SKGInterestObject::ValueDateMode valueMode = SKGInterestObject::FIFTEEN;
            SKGInterestObject::InterestMode baseMode = SKGInterestObject::FIFTEEN24;
            if (currentInterest.object.getRealTable() == "interest") {
                SKGInterestObject interestObj = currentInterest.object;
                valueMode = (tmp.amount >= 0 ? interestObj.getIncomeValueDateMode() : interestObj.getExpenditueValueDateMode());
                baseMode = interestObj.getInterestComputationMode();

                tmp.rate = interestObj.getRate();
            }

            //Compute value date
            if (object.getRealTable() == "operation") {
                if (valueMode == SKGInterestObject::FIFTEEN) {
                    if (tmp.amount >= 0) {
                        if (tmp.date.day() <= 15) tmp.valueDate = tmp.date.addDays(16 - tmp.date.day());
                        else tmp.valueDate = tmp.date.addMonths(1).addDays(1 - tmp.date.day());
                    } else {
                        if (tmp.date.day() <= 15) tmp.valueDate = tmp.date.addDays(1 - tmp.date.day());
                        else tmp.valueDate = tmp.date.addDays(16 - tmp.date.day());
                    }
                } else {
                    tmp.valueDate = tmp.date.addDays(tmp.amount >= 0 ? ((int) valueMode) - 1 : - ((int) valueMode) + 1);
                }
            }

            //Compute coef
            if (baseMode == SKGInterestObject::DAYS365) {
                QDate last(tmp.date.year(), 12, 31);
                tmp.coef = tmp.valueDate.daysTo(last) + 1;
                tmp.coef /= 365;
            } else if (baseMode == SKGInterestObject::DAYS360) {
                QDate last(tmp.date.year(), 12, 31);
                tmp.coef = 360 * (last.year() - tmp.valueDate.year()) + 30 * (last.month() - tmp.valueDate.month()) + (last.day() - tmp.valueDate.day()); //TODO must be confirmed
                tmp.coef /= 360;
            } else {
                tmp.coef = 2 * (12 - tmp.valueDate.month()) + (tmp.valueDate.day() <= 15 ? 2 : 1);
                tmp.coef /= 24;
            }
            if (tmp.valueDate.year() != iYear) tmp.coef = 0;

            //Compute annual interest
            tmp.annualInterest = tmp.amount * tmp.coef * tmp.rate / 100;

        } else if (object.getRealTable() == "interest") {
            //Compute coef
            if (tmp.base == 365) {
                QDate last(tmp.date.year(), 12, 31);
                tmp.coef = tmp.valueDate.daysTo(last) + 1;
                tmp.coef /= 365;
            } else if (tmp.base == 360) {
                QDate last(tmp.date.year(), 12, 31);
                tmp.coef = 360 * (last.year() - tmp.valueDate.year()) + 30 * (last.month() - tmp.valueDate.month()) + (last.day() - tmp.valueDate.day()); //TODO must be confirmed
                tmp.coef /= 360;
            } else {
                tmp.coef = 2 * (12 - tmp.valueDate.month()) + (tmp.valueDate.day() <= 15 ? 2 : 1);
                tmp.coef /= 24;
            }
            if (tmp.valueDate.year() != iYear) tmp.coef = 0;

            //Compute annual interest
            tmp.amount = getAmount(tmp.valueDate);
            tmp.annualInterest = tmp.amount * tmp.coef * (tmp.rate - currentInterest.rate) / 100;

            currentInterest = tmp;
        }

        //Compute sum
        oInterests += tmp.annualInterest;

        //Compute accrued interest
        tmp.accruedInterest = oInterests - getAmount(tmp.date) * tmp.coef * tmp.rate / 100;

        ioInterestList[i] = tmp;

    }

    //Create temporary table
    if (!err) {
        QStringList sqlOrders;
        sqlOrders << "DROP TABLE IF EXISTS interest_result"
                  << "CREATE TEMP TABLE interest_result("
                  "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
                  "d_date DATE NOT NULL,"
                  "d_valuedate DATE NOT NULL,"
                  "t_comment TEXT NOT NULL DEFAULT '',"
                  "f_currentamount FLOAT NOT NULL DEFAULT 0,"
                  "f_coef FLOAT NOT NULL DEFAULT 0,"
                  "f_rate FLOAT NOT NULL DEFAULT 0,"
                  "f_annual_interest FLOAT NOT NULL DEFAULT 0,"
                  "f_accrued_interest FLOAT NOT NULL DEFAULT 0"
                  ")";
        err = getDocument()->executeSqliteOrders(sqlOrders);

        //Fill table
        int nb = ioInterestList.count();
        for (int i = 0; !err && i < nb; ++i) {
            SKGInterestItem interest = ioInterestList.at(i);
            SKGObjectBase object = interest.object;
            QString sqlinsert =
                "INSERT INTO interest_result (d_date,d_valuedate,t_comment,f_currentamount,f_coef,f_rate,f_annual_interest,f_accrued_interest) "
                " VALUES ('" % SKGServices::dateToSqlString(QDateTime(interest.date)) %
                "','" % SKGServices::dateToSqlString(QDateTime(interest.valueDate)) %
                "','" % SKGServices::stringToSqlString(object.getRealTable() == "operation" ? i18nc("Noun", "Relative to operation '%1'", SKGOperationObject(object).getDisplayName()) : i18nc("Noun", "Rate change")) %
                "'," % SKGServices::doubleToString(interest.amount) %
                ',' % SKGServices::doubleToString(interest.coef) %
                ',' % SKGServices::doubleToString(interest.rate) %
                ',' % SKGServices::doubleToString(interest.annualInterest) %
                ',' % SKGServices::doubleToString(interest.accruedInterest) %
                ")";

            err = getDocument()->executeSqliteOrder(sqlinsert);

        }
    }
    return err;
}

SKGError SKGAccountObject::autoReconcile(double iBalance)
{
    SKGError err;
    SKGTRACEINRC(10, "SKGAccountObject::autoReconcile", err);
    //
    SKGDocumentBank* doc = qobject_cast<SKGDocumentBank*>(getDocument());
    if (doc) {
        //Get unit
        SKGServices::SKGUnitInfo unit1 = doc->getPrimaryUnit();
        SKGUnitObject unitAccount;
        if (getUnit(unitAccount).isSucceeded()) {
            if (!unitAccount.getSymbol().isEmpty()) {
                unit1.Symbol = unitAccount.getSymbol();
                unit1.Value = SKGServices::stringToDouble(unitAccount.getAttribute("f_CURRENTAMOUNT"));
            }
        }

        //Get balance of checked operations
        double balance = 0;
        SKGStringListList listTmp2;
        err = getDocument()->executeSelectSqliteOrder(
                  "SELECT TOTAL(f_CHECKED) from v_account_display WHERE id=" % SKGServices::intToString(getID()),
                  listTmp2);
        if (listTmp2.count() == 2) balance = SKGServices::stringToDouble(listTmp2.at(1).at(0)) / unit1.Value;


        //Get all imported operation
        double diff = balance - iBalance;
        SKGObjectBase::SKGListSKGObjectBase operations;
        if (!err) err = getDocument()->getObjects("v_operation", "rd_account_id=" % SKGServices::intToString(getID()) % " AND t_status='N' AND t_template='N' AND t_imported IN ('Y','P') ORDER BY d_date, id", operations);


        //Comparison
        QString zero = doc->formatMoney(0, unit1);
        QString negativezero = doc->formatMoney(-EPSILON, unit1);

        //Try to find the soluce
        int nb = operations.count();

        QString sdiff = doc->formatMoney(diff, unit1);
        int limit = -1;
        bool moreThanOneSoluceFound = false;
        for (int i = 0; !moreThanOneSoluceFound && !err && i < nb; ++i) {
            SKGOperationObject op = operations.at(i);
            double amount = op.getCurrentAmount();
            diff += amount;
            sdiff = doc->formatMoney(diff, unit1);
            SKGTRACEL(5) << (i + 1) << "/" << nb << ": Amount=" << amount << " / New diff=" << sdiff << endl;
            if (sdiff == zero || sdiff == negativezero) {
                if (limit != -1) {
                    err = getDocument()->sendMessage(i18nc("An information message",  "More than one solution is possible for this auto reconciliation."));
                    moreThanOneSoluceFound = true;
                } else limit = i;
            }
        }

        //Check all
        if (limit != -1) {
            for (int i = 0; !err && i <= limit; ++i) {
                SKGOperationObject op = operations.at(i);
                err = op.setStatus(SKGOperationObject::POINTED);
                if (!err) err = op.save(true, false);
            }
        } else {
            err = SKGError(ERR_FAIL, i18nc("Error message",  "Can not find the imported operations for obtaining the expected final balance"));
        }
    }

    return err;
}

SKGError SKGAccountObject::merge(const SKGAccountObject& iAccount)
{
    SKGError err;
    SKGTRACEINRC(10, "SKGAccountObject::merge", err);

    //Get initial balances
    double balance1;
    SKGUnitObject unit1;
    err = getInitialBalance(balance1, unit1);

    double balance2;
    SKGUnitObject unit2;
    if (!err) err = const_cast<SKGAccountObject*>(&iAccount)->getInitialBalance(balance2, unit2);

    //Transfert operations
    SKGObjectBase::SKGListSKGObjectBase ops;
    if (!err) err = iAccount.getOperations(ops);
    int nb = ops.count();
    for (int i = 0; !err && i < nb; ++i) {
        SKGOperationObject op = ops.at(i);
        err = op.setParentAccount(*this);
        if (!err) err = op.save(true, false);
    }

    //Set initial balance
    SKGUnitObject unit = unit1;
    if (!unit1.exist()) unit = unit2;
    if (unit.exist()) {
        double balance = balance1 + SKGUnitObject::convert(balance2, unit2, unit);
        if (!err) err = setInitialBalance(balance, unit);
    }
    //Remove account
    if (!err) err = iAccount.remove();
    return err;
}

#include "skgaccountobject.moc"
