/***************************************************************************
 *   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 defines classes SKGObjectModel.
*
* @author Stephane MANKOWSKI / Guillaume DE BURE
 */
#include "skgobjectmodel.h"
#include "skgdocumentbank.h"
#include "skgaccountobject.h"
#include "skgcategoryobject.h"
#include "skgpayeeobject.h"
#include "skgoperationobject.h"
#include "skgtrackerobject.h"
#include "skgrecurrentoperationobject.h"
#include "skgunitobject.h"
#include "skgunitvalueobject.h"
#include "skgbudgetruleobject.h"
#include "skgbudgetobject.h"
#include "skgtransactionmng.h"
#include "skgtraces.h"
#include "skgmainpanel.h"

#include <kicon.h>
#include <kstandarddirs.h>
#include <klocalizedstring.h>
#include <klocale.h>
#include <kconfiggroup.h>

#include <QApplication>
#include <QDir>
#include <QMimeData>
#include <QFont>
#include <qcolor.h>

SKGObjectModel::SKGObjectModel(SKGDocumentBank* iDocument,
                               const QString& iTable,
                               const QString& iWhereClause,
                               QWidget* parent,
                               const QString& iParentAttribute,
                               bool iResetOnCreation)
    : SKGObjectModelBase(iDocument, iTable, iWhereClause, parent, iParentAttribute, false)
{
    SKGTRACEIN(1, "SKGObjectModel::SKGObjectModel");


    m_isResetRealyNeeded = iResetOnCreation;
    refresh();
}

void SKGObjectModel::buidCache()
{
    SKGObjectModelBase::buidCache();
    m_operationTable = (getRealTable() == "operation");
    m_payeeTable = (getRealTable() == "payee");
    m_trackerTable = (getRealTable() == "refund");
    m_recurrentoperationTable = (getRealTable() == "recurrentoperation");
    m_accountTable = (getRealTable() == "account");
    m_unitTable = (getRealTable() == "unit");
    m_unitvalueTable = (getRealTable() == "unitvalue");
    m_suboperationTable = (getTable() == "v_operation_consolidated");
    m_ruleTable = (getRealTable() == "rule");
    m_categoryTable = (getRealTable() == "category");
    m_interestTable = (getRealTable() == "interest");
    m_budgetTable = (getRealTable() == "budget");
    m_budgetRuleTable = (getRealTable() == "budgetrule");

    if (m_unitvalueTable) {
        SKGUnitValueObject unitValObject = getObject(index(0, 0));
        SKGUnitObject unitObject;
        unitValObject.getUnit(unitObject);
        SKGUnitObject parent;
        unitObject.getUnit(parent);
        if (parent.exist()) {
            m_cacheUnit.Name = parent.getName();
            m_cacheUnit.Symbol = parent.getSymbol();
            m_cacheUnit.Value = 1;
            m_cacheUnit.NbDecimal = unitObject.getNumberDecimal();
        } else {
            m_cacheUnit.Name = "";
            m_cacheUnit.Symbol = "";
            m_cacheUnit.Value = 1;
            m_cacheUnit.NbDecimal = unitObject.getNumberDecimal();
        }
        if (m_cacheUnit.Symbol.isEmpty()) m_cacheUnit.Symbol = ' ';

        //Bug 209672 vvvv
        if (unitObject.getType() == SKGUnitObject::INDEX) {
            m_cacheUnit.Symbol = ' ';
            m_cacheUnit.Name = ' ';
        }
        //Bug 209672 ^^^^
    }

    if (m_operationTable) {
        //Read Setting
        KSharedConfigPtr config = KSharedConfig::openConfig();
        KConfigGroup pref = config->group("skrooge_operation");
        m_fontFutureOperationsColor = qVariantFromValue(pref.readEntry("fontFutureColor", QColor(Qt::gray)));
        m_fontNotValidatedOperationsColor = qVariantFromValue(pref.readEntry("fontNotValidatedColor", QColor(Qt::blue)));
        m_fontSubOperationsColor = qVariantFromValue(pref.readEntry("fontSubOperationColor", QColor(Qt::darkGreen)));
    }

    if (m_recurrentoperationTable) {
        //Read Setting
        KSharedConfigPtr config = KSharedConfig::openConfig();
        KConfigGroup pref = config->group("skrooge_scheduled");
        m_fontDisabledScheduleColor = qVariantFromValue(pref.readEntry("fontFutureColor", QColor(Qt::gray)));
    }

    if (m_operationTable || m_recurrentoperationTable || m_accountTable || m_ruleTable || m_categoryTable || m_payeeTable) {
        m_iconFavorite = KIcon("rating");
    }

    if (m_operationTable || m_recurrentoperationTable) {
        m_iconTransfer = KIcon("view-financial-transfer");
        QStringList overlays;
        overlays.push_back("list-remove");
        m_iconGroup = KIcon("view-financial-transfer", NULL, overlays);
        m_iconSplit = KIcon("split");
        m_iconImported = KIcon("utilities-file-archiver");
        {
            QStringList overlay;
            overlay.push_back("dialog-ok");
            m_iconImportedChecked = KIcon("utilities-file-archiver", NULL, overlay);
        }

        m_iconRecurrent = KIcon("chronometer");
        {
            QStringList overlay;
            overlay.push_back("rating");
            m_iconRecurrentMaster = KIcon("chronometer", NULL, overlay);
        }
    }

    if (m_budgetTable) {
        m_iconGreen = KIcon("security-high");
        m_iconRed = KIcon("security-low");
        m_iconAnber = KIcon("security-medium");
    }

    if (m_unitTable) {

        m_iconMuchMore = KIcon("skrooge_much_more");
        m_iconMuchLess = KIcon("skrooge_much_less");
        m_iconMore = KIcon("skrooge_more");
        m_iconLess = KIcon("skrooge_less");
    }

    if (m_ruleTable) {
        m_iconSearch = KIcon("edit-find");
        m_iconUpdate = KIcon("view-refresh");
        m_iconAlarm = KIcon("dialog-warning");
    }
    m_iconClosed = KIcon("dialog-close");

    if (m_categoryTable) {
        m_iconCategory = KIcon("skrooge_category");
        {
            QStringList overlay;
            overlay.push_back("list-remove");
            m_iconCategoryMoins = KIcon("skrooge_category", NULL, overlay);
        }
        {
            QStringList overlay;
            overlay.push_back("list-add");
            m_iconCategoryPlus = KIcon("skrooge_category", NULL, overlay);
        }
    }
}

SKGObjectModel::~SKGObjectModel()
{
    SKGTRACEIN(1, "SKGObjectModel::~SKGObjectModel");
}

QVariant SKGObjectModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    _SKGTRACEIN(10, "SKGObjectModelBase::headerData");

    if (orientation == Qt::Horizontal) {
        if (role == Qt::DisplayRole) {
            QString att;
            if (section >= 0 && section < m_listAttibutes.count()) att = m_listAttibutes[section];
            else att = SKGServices::intToString(section);

            if (att == "t_bookmarked" || att == "i_NBRECURRENT" || att == "t_status" || att == "t_close" || att == "t_imported") return "";
        }
    }
    return SKGObjectModelBase::headerData(section, orientation, role);
}

QString SKGObjectModel::getAttributeForGrouping(const SKGObjectBase& iObject, const QString& iAttribute) const
{
    if (m_recurrentoperationTable && iAttribute == "i_nb_times") {
        if (iObject.getAttribute("t_times") != "Y") return QChar(8734);
    } else if (m_ruleTable && iAttribute == "t_action_type") {
        QString val = iObject.getAttribute(iAttribute);
        if (val == "S") val = i18nc("Noun, a search", "Search");
        else if (val == "U") val = i18nc("Noun, a modification", "Update");
        else val = i18nc("Noun, an alarm", "Alarm");
        return val;
    } else if (iAttribute == "t_bookmarked" || iAttribute == "t_close") {
        QString val = iObject.getAttribute(iAttribute);
        return val == "Y" ? i18n("Yes") : i18n("No");
    } else if (iAttribute == "t_status") {
        QString val = iObject.getAttribute(iAttribute);
        return val == "N" ? i18n("None") : val == "P" ? i18n("Pointed") : i18n("Checked");
    }
    return SKGObjectModelBase::getAttributeForGrouping(iObject, iAttribute);
}

QVariant SKGObjectModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid()) return QVariant();
    _SKGTRACEIN(10, "SKGObjectModel::data");
    SKGObjectBase* obj = getObjectPointer(index);
    if (obj->getTable().isEmpty()) return SKGObjectModelBase::data(index, role);

    switch (role) {
    case Qt::DisplayRole:
    case Qt::EditRole:
    case Qt::UserRole: {
        QString att = m_listAttibutes[index.column()];
        QString val = obj->getAttribute(att);
        if (((m_operationTable || m_recurrentoperationTable || m_trackerTable || m_accountTable || m_ruleTable || m_categoryTable || m_payeeTable) &&
                (att == "t_bookmarked" || att == "t_status" || att == "i_NBRECURRENT" || att == "t_imported"))
                || (att == "t_close")
                || (att == "t_action_type")
           ) {
            if (role == Qt::UserRole) {
                if (m_ruleTable && att == "t_action_type") {
                    SKGObjectBase* obj = getObjectPointer(index);
                    if (obj) {
                        QString val = obj->getAttribute(att);
                        if (val == "S") return i18nc("Noun, a search", "Search");
                        else if (val == "U") return i18nc("Noun, a modification", "Update");
                        else return i18nc("Noun, an alarm", "Alarm");
                    }
                }
                return val;
            } else return "";
        } else if (m_accountTable && att == "t_type") {
            if (val == "C") return i18nc("Noun, a type of account", "Current");
            else if (val == "D") return i18nc("Noun, a type of account", "Credit card");
            else if (val == "A") return i18nc("Noun, a type of account", "Assets");
            else if (val == "I") return i18nc("Noun, a type of account", "Investment");
            else if (val == "W") return i18nc("Noun, a type of account", "Wallet");
            else return i18nc("Noun", "Other");
        } else if (m_unitTable && att == "t_type") {
            if (val == "1") return i18nc("Noun", "Primary currency");
            else if (val == "2") return i18nc("Noun", "Secondary currency");
            else if (val == "C") return i18nc("Noun", "Currency");
            else if (val == "S") return i18nc("Noun", "Share");
            else if (val == "I") return i18nc("Noun, example CAC40, DOWJONES", "Index");
            else return i18nc("Noun", "Object");
        } else if (m_interestTable && att == "t_expenditure_value_date_mode") {
            if (val == "0") return i18nc("Noun", "Day -0");
            else if (val == "1") return i18nc("Noun", "Day -1");
            else if (val == "2") return i18nc("Noun", "Day -2");
            else if (val == "3") return i18nc("Noun", "Day -3");
            else if (val == "4") return i18nc("Noun", "Day -4");
            else if (val == "5") return i18nc("Noun", "Day -5");
            else return i18nc("Noun", "Fifteen");
        } else if (m_interestTable && att == "t_income_value_date_mode") {
            if (val == "0") return i18nc("Noun", "Day +0");
            else if (val == "1") return i18nc("Noun", "Day +1");
            else if (val == "2") return i18nc("Noun", "Day +2");
            else if (val == "3") return i18nc("Noun", "Day +3");
            else if (val == "4") return i18nc("Noun", "Day +4");
            else if (val == "5") return i18nc("Noun", "Day +5");
            else return i18nc("Noun", "Fifteen");
        } else if (getAttributeType(index.column()) == SKGServices::FLOAT) {
            double dval = SKGServices::stringToDouble(val);
            if (role == Qt::DisplayRole) {
                if (att.endsWith(QLatin1String("_INCOME")) ||
                        att.endsWith(QLatin1String("_EXPENSE")) ||
                        (m_categoryTable && (att == "f_REALCURRENTAMOUNT" ||
                                             att == "f_SUMCURRENTAMOUNT"))) {
                    double dval = SKGServices::stringToDouble(val);
                    if (dval == 0) return "";
                }

                SKGServices::SKGUnitInfo unit;
                unit.Symbol = "";
                unit.NbDecimal = 2;
                if (!att.contains("QUANTITY")) {
                    unit = static_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
                    if (m_unitvalueTable && !m_cacheUnit.Symbol.isEmpty()) {
                        unit = m_cacheUnit;
                    }
                } else {
                    if (att == "f_QUANTITYOWNED") {
                        unit.NbDecimal = SKGServices::stringToInt(obj->getAttribute("i_nbdecimal"));
                    } else {
                        unit.Symbol = obj->getAttribute("t_UNIT");
                    }
                }

                //Bug 209672 vvvv
                if (m_unitTable) {
                    if (obj->getAttribute("t_type") == "I") unit.Symbol = ' ';
                }
                //Bug 209672 ^^^

                if (QString::compare(att, "f_rate", Qt::CaseInsensitive) == 0) {
                    unit.Symbol = '%';
                    unit.NbDecimal = 2;
                } else if (att == "f_coef") {
                    unit.Symbol = "";
                    unit.NbDecimal = 2;
                }

                if (unit.Symbol.isEmpty()) unit.Symbol = ' ';

                return KGlobal::locale()->formatMoney(dval, unit.Symbol, unit.NbDecimal);
            } else {
                return dval;
            }
        } else if (getAttributeType(index.column()) == SKGServices::INTEGER) {
            if (m_recurrentoperationTable && att == "i_nb_times") {
                QString t_times = obj->getAttribute("t_times");
                if (t_times != "Y") return QChar(8734);
            } else if ((att == "i_number" || att == "i_NBOPERATIONS" || att == "i_SUMNBOPERATIONS") && val == "0") return "";

            return SKGServices::stringToInt(val);
        }
        break;
    }
    case Qt::DecorationRole: {
        //decoration
        QString att = m_listAttibutes[index.column()];
        if (m_operationTable || m_recurrentoperationTable) {
            if (att == "t_mode") {
                if (obj->getAttribute("t_TRANSFER") == "Y") return m_iconTransfer;
                if (obj->getAttribute("i_group_id") != "0") return m_iconGroup;
            } else if (att == "t_bookmarked") {
                if (obj->getAttribute("t_bookmarked") == "Y")  return m_iconFavorite;
            } else if (att == "t_CATEGORY") {
                if (SKGServices::stringToInt(obj->getAttribute("i_NBSUBCATEGORY")) > 1) return m_iconSplit;
            } else if (att == "i_NBRECURRENT" && m_operationTable) {
                if (obj->getAttribute("i_NBRECURRENT") != "0") return m_iconRecurrentMaster;
                else if (obj->getAttribute("r_recurrentoperation_id") != "0") return m_iconRecurrent;
            } else if (att == "t_imported") {
                QString impStatus = obj->getAttribute("t_imported");
                if (impStatus == "Y") return m_iconImported;
                else if (impStatus == "P")  return m_iconImportedChecked;
            } else if (att == "t_REFUND" || att == "t_REALREFUND") {
                SKGObjectBase* obj = getObjectPointer(index);
                QString trackerName;
                if (obj) trackerName = obj->getAttribute(att);
                if (!trackerName.isEmpty()) {
                    SKGTrackerObject tracker = SKGObjectBase(getDocument(), "refund"); //Better performance if v_refund is not used
                    tracker.setName(trackerName);
                    tracker.load();

                    if (tracker.isClosed()) return m_iconClosed;
                }
            }
        } else if (m_ruleTable) {
            if (att == "t_action_type") {
                QString val = obj->getAttribute(att);
                if (val == "S") return m_iconSearch;
                else if (val == "U") return m_iconUpdate;
                else return m_iconAlarm;
            } else if (att == "t_bookmarked") {
                if (obj->getAttribute("t_bookmarked") == "Y")  return m_iconFavorite;
            }
        } else if (m_unitTable) {
            if (att == "f_CURRENTAMOUNT") {
                SKGUnitObject unit = *obj;
                double annualchange = 365 * unit.getDailyChange();
                if (annualchange >= 15) return m_iconMuchMore;
                else if (annualchange <= -15) return m_iconMuchLess;
                else if (annualchange >= 5) return m_iconMore;
                else if (annualchange <= -5) return m_iconLess;
            }
        } else if (m_accountTable) {
            if (att == "t_BANK") {
                QString t_icon = obj->getAttribute("t_icon");
                if (t_icon.isEmpty()) t_icon = obj->getAttribute("t_ICON");
                if (!t_icon.isEmpty()) {
                    QDir dirLogo(KStandardDirs::locate("data", QString::fromLatin1("skrooge/images/logo/")));
                    return qVariantFromValue(QIcon(dirLogo.absoluteFilePath(t_icon)));
                }
            } /*else if(att == "t_TYPENLS") {
QString type = obj->getAttribute("t_type");
if(type == "C") return KIcon("view-bank-account-savings");
else if(type == "D")  return KIcon("view-credit-card-account");
else if(type == "I")  return KIcon("view-stock-account");
else if(type == "A")  return KIcon("view-loan-asset");
else if(type == "W")  return KIcon("kwalletmanager");
else return KIcon();
} */else if (att == "t_bookmarked") {
                if (obj->getAttribute("t_bookmarked") == "Y")  return m_iconFavorite;
            }
        } else if (m_categoryTable) {
            if (index.column() == 0) {
                QString t_TYPEEXPENSE = obj->getAttribute("t_TYPEEXPENSE");
                if (t_TYPEEXPENSE == "-") return m_iconCategoryMoins;
                else if (t_TYPEEXPENSE == "+") return m_iconCategoryPlus;
                return m_iconCategory;
            } else if (att == "t_bookmarked") {
                if (obj->getAttribute("t_bookmarked") == "Y")  return m_iconFavorite;
            }
        } else if (m_payeeTable) {
            if (att == "t_bookmarked") {
                if (obj->getAttribute("t_bookmarked") == "Y")  return m_iconFavorite;
            }
        } else if (m_budgetTable) {
            if (att == "f_DELTA" || att == "f_DELTABEFORETRANSFER") {
                double val = SKGServices::stringToDouble(obj->getAttribute(att));
                if (val < -EPSILON) {
                    return m_iconRed;
                } else if (val > EPSILON) {
                    return m_iconGreen;
                } else {
                    double transferred = SKGServices::stringToDouble(obj->getAttribute("f_transferred"));
                    if (transferred < -EPSILON) return m_iconAnber;
                    else return m_iconGreen;
                }
            }
        }
        break;
    }
    case Qt::TextColorRole: {
        //Text color
        QString att = m_listAttibutes[index.column()];
        if (m_recurrentoperationTable) {
            if (obj->getAttribute("i_nb_times") == "0") return  m_fontDisabledScheduleColor;
            else if (att == "d_date" && SKGServices::stringToTime(obj->getAttribute("d_date")).date() <= QDate::currentDate()) return m_fontNegativeColor;
        } else if (m_operationTable) {
            if (SKGServices::stringToTime(obj->getAttribute("d_date")).date() > QDate::currentDate()) return m_fontFutureOperationsColor;
            else if (getAttributeType(index.column()) != SKGServices::FLOAT) {
                if (obj->getAttribute("t_imported") == "P") return  m_fontNotValidatedOperationsColor;
                else if (m_suboperationTable) return m_fontSubOperationsColor;
            }
        }
        break;
    }
    case Qt::TextAlignmentRole: {
        //Text alignment
        if (m_recurrentoperationTable) {
            QString att = m_listAttibutes[index.column()];

            if (att == "i_auto_write_days" || att == "i_warn_days" || att == "i_nb_times") {
                return (int)(Qt::AlignVCenter | Qt::AlignLeft);
            }
        }
        break;
    }
    case Qt::CheckStateRole: {
        //CheckState
        QString att = m_listAttibutes[index.column()];
        if (m_operationTable && att == "t_status") {
            QString cond = obj->getAttribute("t_status");
            if (cond == "P") return Qt::PartiallyChecked;
            else if (cond == "Y") return Qt::Checked;
            else if (cond == "N") return Qt::Unchecked;
        } else if (att == "t_close") {
            QString cond = obj->getAttribute("t_close");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_recurrentoperationTable && att == "i_auto_write_days") {
            QString cond = obj->getAttribute("t_auto_write");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_recurrentoperationTable && att == "i_warn_days") {
            QString cond = obj->getAttribute("t_warn");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_recurrentoperationTable && att == "i_nb_times") {
            QString cond = obj->getAttribute("t_times");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_budgetRuleTable && att == "t_CATEGORYCONDITION") {
            QString cond = obj->getAttribute("t_category_condition");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_budgetRuleTable && att == "i_year") {
            QString cond = obj->getAttribute("t_year_condition");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_budgetRuleTable && att == "i_month") {
            QString cond = obj->getAttribute("t_month_condition");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_budgetRuleTable && att == "t_CATEGORY") {
            QString cond = obj->getAttribute("t_category_target");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        } else  if (m_budgetTable && att == "t_CATEGORY") {
            QString cond = obj->getAttribute("t_including_subcategories");
            if (cond == "Y") return Qt::Checked;
            else return Qt::Unchecked;
        }
        break;
    }
    case Qt::ToolTipRole: {
        //Tooltip
        QString toolTipString;
        QString att = m_listAttibutes[index.column()];
        if (getAttributeType(index.column()) == SKGServices::FLOAT) {
            //Add secondary unit
            if (!att.contains("QUANTITY") && att != "f_coef" && att != "f_rate") {
                SKGServices::SKGUnitInfo secondaryUnit = static_cast<SKGDocumentBank*>(getDocument())->getSecondaryUnit();
                if (!secondaryUnit.Symbol.isEmpty()) {
                    double val = SKGServices::stringToDouble(obj->getAttribute(att));
                    if ((!att.endsWith(QLatin1String("_INCOME")) && !att.endsWith(QLatin1String("_EXPENSE"))) || val != 0) {
                        if (secondaryUnit.Value)  toolTipString = KGlobal::locale()->formatMoney(val / secondaryUnit.Value, secondaryUnit.Symbol, secondaryUnit.NbDecimal);

                    }

                }
            }

            //Add original amount
            QString originalAmount = obj->getProperty("SKG_OP_ORIGINAL_AMOUNT");
            if (!originalAmount.isEmpty()) {
                if (!toolTipString.isEmpty()) toolTipString += "\n\n";
                double val1 = SKGServices::stringToDouble(obj->getAttribute("f_CURRENTAMOUNT"));
                double val2 = SKGServices::stringToDouble(originalAmount);
                double gain = (val2 != 0 ? 100.0 * (val1 - val2) / val2 : 0);
                SKGServices::SKGUnitInfo primaryUnit = static_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
                toolTipString += i18nc("Noun", "Original amount=%1 (%2 %)",
                                       KGlobal::locale()->formatMoney(val2, primaryUnit.Symbol, primaryUnit.NbDecimal),
                                       gain);
            }

            //Add balance
            if (m_operationTable) {
                SKGOperationObject op = *obj;
                if (!op.isTemplate()) {
                    if (!toolTipString.isEmpty()) toolTipString += "\n\n";
                    SKGServices::SKGUnitInfo primaryUnit = static_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
                    toolTipString += i18nc("Noun", "Account balance=%1",
                                           KGlobal::locale()->formatMoney(op.getBalance(), primaryUnit.Symbol, primaryUnit.NbDecimal));
                }
            }

            if (m_budgetTable) {
                if (att == "f_DELTA") {
                    double val = SKGServices::stringToDouble(obj->getAttribute("f_DELTABEFORETRANSFER"));

                    if (!toolTipString.isEmpty()) toolTipString += "\n\n";

                    SKGServices::SKGUnitInfo primaryUnit = static_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
                    toolTipString += i18nc("Noun", "Original delta=%1",
                                           KGlobal::locale()->formatMoney(val, primaryUnit.Symbol, primaryUnit.NbDecimal));
                }
            }
        } else if (m_operationTable || m_recurrentoperationTable) {
            if (att == "t_imported") {
                SKGOperationObject op = *obj;
                if (m_recurrentoperationTable) {
                    SKGRecurrentOperationObject rop = *obj;
                    rop.getParentOperation(op);
                }
                toolTipString = op.getImportID();
            } else if (att == "t_REFUND" || att == "t_REALREFUND") {
                QString trackerName;
                if (obj) trackerName = obj->getAttribute(att);
                if (!trackerName.isEmpty()) {
                    SKGTrackerObject tracker = SKGObjectBase(getDocument(), "v_refund");
                    tracker.setName(trackerName);
                    tracker.load();
                    SKGServices::SKGUnitInfo unit = static_cast<SKGDocumentBank*>(getDocument())->getPrimaryUnit();
                    toolTipString = KGlobal::locale()->formatMoney(tracker.getCurrentAmount(), unit.Symbol, unit.NbDecimal);
                }
            } else if (att == "t_CATEGORY") {
                SKGOperationObject op = *obj;
                if (m_recurrentoperationTable) {
                    SKGRecurrentOperationObject rop = *obj;
                    rop.getParentOperation(op);
                }
                if (SKGServices::stringToInt(op.getAttribute("i_NBSUBCATEGORY")) > 1) {
                    SKGObjectBase::SKGListSKGObjectBase subOps;
                    op.getSubOperations(subOps);
                    foreach(const SKGObjectBase & subOp, subOps) {
                        toolTipString += subOp.getDisplayName() % '\n';
                    }
                }
            } else if (att == "t_mode") {
                SKGOperationObject op = *obj;
                if (m_recurrentoperationTable) {
                    SKGRecurrentOperationObject rop = *obj;
                    rop.getParentOperation(op);
                }
                if (op.getAttribute("i_group_id") != "0") {
                    SKGOperationObject gop;
                    op.getGroupOperation(gop);

                    SKGObjectBase::SKGListSKGObjectBase gOps;
                    op.getGroupedOperations(gOps);
                    foreach(const SKGOperationObject & gOp, gOps) {
                        SKGAccountObject account;
                        gOp.getParentAccount(account);
                        toolTipString += account.getDisplayName() % '\n';
                    }
                }
            }

            if (m_operationTable && toolTipString.isEmpty()) {
                SKGOperationObject op = *obj;
                if (op.getStatus() == SKGOperationObject::POINTED) {
                    toolTipString = i18nc("A tool tip", "This operation is pointed but not checked yet.");
                    toolTipString += '\n';
                    toolTipString += i18nc("A tool tip", "You can use the reconciliation mode to validate pointed operations.");
                    toolTipString += '\n';
                    toolTipString += i18nc("A tool tip", "Click to switch back status.");
                    toolTipString += '\n';
                    toolTipString += i18nc("A tool tip", "Ctrl+click to force checked status.");
                } else if (op.getStatus() == SKGOperationObject::CHECKED) {
                    toolTipString = i18nc("A tool tip", "This operation is already checked.");
                } else if (op.getStatus() == SKGOperationObject::NONE) {
                    toolTipString = i18nc("A tool tip", "This operation is not pointed yet.");
                    toolTipString += '\n';
                    toolTipString += i18nc("A tool tip", "Click to set pointed status.");
                    toolTipString += '\n';
                    toolTipString += i18nc("A tool tip", "Ctrl+click to force checked status.");
                }
            }
        } else if (m_ruleTable && att == "t_action_type") {
            SKGObjectBase* obj = getObjectPointer(index);
            if (obj) {
                QString val = obj->getAttribute(att);
                if (val == "S") toolTipString = i18nc("Noun, a search", "Search");
                else if (val == "U") toolTipString = i18nc("Noun, a modification", "Update");
                else toolTipString = i18nc("Noun, an alarm", "Alarm");
            }
        }

        QString toolTipStringBase = SKGObjectModelBase::data(index, role).toString();
        if (!toolTipStringBase.isEmpty()) {
            if (!toolTipString.isEmpty()) toolTipString += "\n\n";
            toolTipString += toolTipStringBase;
        }
        return toolTipString;
        break;
    }
    default: {
    }
    }
    return SKGObjectModelBase::data(index, role);
}

bool SKGObjectModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (!index.isValid()) return false;

    if (role == Qt::CheckStateRole) {
        SKGError err;
        {
            if (m_accountTable) {
                SKGAccountObject obj = getObject(index);
                SKGBEGINLIGHTTRANSACTION(*getDocument(),
                                         ((Qt::CheckState) value.toInt() == Qt::Checked ? i18nc("Noun, name of the user action", "Close account '%1'", obj.getName()) : i18nc("Noun, name of the user action", "Open account '%1'", obj.getName())), err);
                err = obj.setClosed((Qt::CheckState) value.toInt() == Qt::Checked);
                if (!err) err = obj.save();
            } else if (m_trackerTable) {
                SKGTrackerObject obj = getObject(index);
                SKGBEGINLIGHTTRANSACTION(*getDocument(),
                                         ((Qt::CheckState) value.toInt() == Qt::Checked ? i18nc("Noun, name of the user action", "Close tracker '%1'", obj.getName()) : i18nc("Noun, name of the user action", "Open tracker '%1'", obj.getName())), err);
                err = obj.setClosed((Qt::CheckState) value.toInt() == Qt::Checked);
                if (!err) err = obj.save();
            } else if (m_operationTable) {
                SKGOperationObject obj = getObject(index);
                SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Change operation status"), err);
                SKGOperationObject::OperationStatus statusinitial = obj.getStatus();
                SKGOperationObject::OperationStatus t_status = statusinitial;
                if (QApplication::keyboardModifiers() &Qt::ControlModifier) {
                    //2747379: NONE ==> CHECKED, POINTED ==> CHECKED, CHECKED ==> CHECKED
                    t_status = SKGOperationObject::CHECKED ;
                    //t_status= ( t_status==SKGOperationObject::POINTED ? SKGOperationObject::NONE : ( t_status==SKGOperationObject::CHECKED ? SKGOperationObject::POINTED : SKGOperationObject::NONE ) );
                } else {
                    //2747379: NONE ==> POINTED, POINTED ==> NONE, CHECKED ==> POINTED
                    t_status = (t_status == SKGOperationObject::NONE ? SKGOperationObject::POINTED : (t_status == SKGOperationObject::POINTED ? SKGOperationObject::NONE : SKGOperationObject::POINTED));
                    //t_status=(t_status==SKGOperationObject::POINTED ? SKGOperationObject::CHECKED : (t_status==SKGOperationObject::CHECKED ? SKGOperationObject::CHECKED : SKGOperationObject::POINTED ));
                }
                if (t_status != statusinitial) {
                    err = obj.setStatus(t_status);
                    if (!err) err = obj.save();
                }
            } else if (m_recurrentoperationTable) {
                QString att = m_listAttibutes[index.column()];

                //Get the real object, not the object from the view
                SKGObjectBase* objtmp = getObjectPointer(index);
                if (objtmp) {
                    SKGRecurrentOperationObject obj = SKGRecurrentOperationObject(objtmp->getDocument(), objtmp->getID());

                    SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Recurrent operation update"), err);
                    if (att == "i_warn_days") {
                        err = obj.warnEnabled(!obj.isWarnEnabled());
                        if (!err) err = obj.save();
                    } else if (att == "i_auto_write_days") {
                        err = obj.autoWriteEnabled(!obj.isAutoWriteEnabled());
                        if (!err) err = obj.save();
                    } else if (att == "i_nb_times") {
                        err = obj.timeLimit(!obj.hasTimeLimit());
                        if (!err) err = obj.save();
                    }
                }
            } else if (m_budgetRuleTable) {
                QString att = m_listAttibutes[index.column()];

                //Get the real object, not the object from the view
                SKGObjectBase* objtmp = getObjectPointer(index);
                if (objtmp) {
                    SKGBudgetRuleObject obj = SKGBudgetRuleObject(objtmp->getDocument(), objtmp->getID());

                    SKGBEGINLIGHTTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget rule update"), err);
                    if (att == "i_year") {
                        err = obj.enableYearCondition(!obj.isYearConditionEnabled());
                        if (!err) err = obj.save();
                    } else if (att == "i_month") {
                        err = obj.enableMonthCondition(!obj.isMonthConditionEnabled());
                        if (!err) err = obj.save();
                    } else if (att == "t_CATEGORYCONDITION") {
                        err = obj.enableCategoryCondition(!obj.isCategoryConditionEnabled());
                        if (!err) err = obj.save();
                    } else if (att == "t_CATEGORY") {
                        err = obj.enableCategoryChange(!obj.isCategoryChangeEnabled());
                        if (!err) err = obj.save();
                    }
                }
            } else if (m_budgetTable) {
                QString att = m_listAttibutes[index.column()];

                //Get the real object, not the object from the view
                SKGObjectBase* objtmp = getObjectPointer(index);
                if (objtmp) {
                    SKGBudgetObject obj = SKGBudgetObject(objtmp->getDocument(), objtmp->getID());

                    SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Budget update"), err);
                    if (att == "t_CATEGORY") {
                        err = obj.enableSubCategoriesInclusion(!obj.isSubCategoriesInclusionEnabled());
                        if (!err) err = obj.save();
                    }
                }
            }
        }

        SKGMainPanel::displayErrorMessage(err);
        return !err;
    }
    return SKGObjectModelBase::setData(index, value, role);
}

Qt::ItemFlags SKGObjectModel::flags(const QModelIndex& index) const
{
    _SKGTRACEIN(10, "SKGObjectModel::flags");

    Qt::ItemFlags flags = SKGObjectModelBase::flags(index);

    if (index.isValid()) {
        QString att = m_listAttibutes[index.column()];
        if (att == "t_bookmarked" || m_ruleTable || m_recurrentoperationTable) flags = flags & ~Qt::ItemIsEditable;
    }

    if (m_categoryTable || m_payeeTable || m_accountTable || m_unitTable || m_trackerTable) {
        if (index.isValid()) flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
        else flags |= Qt::ItemIsDropEnabled;
    }

    return flags;
}

Qt::DropActions SKGObjectModel::supportedDragActions() const
{
    if (m_categoryTable || m_payeeTable || m_accountTable || m_unitTable || m_trackerTable) return Qt::MoveAction;
    return SKGObjectModelBase::supportedDragActions();
}

Qt::DropActions SKGObjectModel::supportedDropActions() const
{
    if (m_categoryTable || m_payeeTable || m_accountTable || m_unitTable || m_trackerTable) return Qt::MoveAction;
    return SKGObjectModelBase::supportedDropActions();
}

bool SKGObjectModel::dropMimeData(const QMimeData* data,
                                  Qt::DropAction action,
                                  int row, int column,
                                  const QModelIndex& parent)
{
    if (SKGObjectModelBase::dropMimeData(data, action, row, column, parent)) return true;
    if (action == Qt::IgnoreAction) return true;
    if (!data || !(data->hasFormat("application/skg.category.ids") ||
                   data->hasFormat("application/skg.payee.ids") ||
                   data->hasFormat("application/skg.account.ids") ||
                   data->hasFormat("application/skg.refund.ids") ||
                   data->hasFormat("application/skg.unit.ids")
                  )) return false;
    if (column > 0) return false;

    SKGError err;
    if (data->hasFormat("application/skg.category.ids")) {
        QByteArray encodedData = data->data("application/skg.category.ids");
        QDataStream stream(&encodedData, QIODevice::ReadOnly);
        QStringList newItems;

        SKGCategoryObject parentCategory;
        if (parent.isValid()) parentCategory = getObject(parent);
        {
            SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Move category"), err);

            while (!stream.atEnd() && !err) {
                int o_id;
                QString o_table;
                stream >> o_table;
                stream >> o_id;

                SKGCategoryObject child(getDocument(), o_id);
                err = child.load();
                if (!err) {
                    if (parent.isValid()) err = child.setParentCategory(parentCategory);
                    else err = child.removeParentCategory();
                }
                if (!err) err = child.save();
            }
        }
    } else if (data->hasFormat("application/skg.payee.ids")) {
        QByteArray encodedData = data->data("application/skg.payee.ids");
        QDataStream stream(&encodedData, QIODevice::ReadOnly);
        QStringList newItems;

        if (parent.isValid()) {
            SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge payees"), err);
            SKGPayeeObject parentPayee = getObject(parent);
            while (!stream.atEnd() && !err) {
                int o_id;
                QString o_table;
                stream >> o_table;
                stream >> o_id;

                SKGPayeeObject child(getDocument(), o_id);
                err = child.load();
                if (!err) err = parentPayee.merge(child);
            }
        }
    } else if (data->hasFormat("application/skg.account.ids")) {
        QByteArray encodedData = data->data("application/skg.account.ids");
        QDataStream stream(&encodedData, QIODevice::ReadOnly);
        QStringList newItems;

        if (parent.isValid()) {
            SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge accounts"), err);
            SKGAccountObject parentAccount = getObject(parent);
            while (!stream.atEnd() && !err) {
                int o_id;
                QString o_table;
                stream >> o_table;
                stream >> o_id;

                SKGAccountObject child(getDocument(), o_id);
                err = child.load();
                if (!err) err = parentAccount.merge(child);
            }
        }
    } else if (data->hasFormat("application/skg.unit.ids")) {
        QByteArray encodedData = data->data("application/skg.unit.ids");
        QDataStream stream(&encodedData, QIODevice::ReadOnly);
        QStringList newItems;

        if (parent.isValid()) {
            SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge units"), err);
            SKGUnitObject parentUnit = getObject(parent);
            while (!stream.atEnd() && !err) {
                int o_id;
                QString o_table;
                stream >> o_table;
                stream >> o_id;

                SKGUnitObject child(getDocument(), o_id);
                err = child.load();
                if (!err) err = parentUnit.merge(child);
            }
        }
    } else if (data->hasFormat("application/skg.refund.ids")) {
        QByteArray encodedData = data->data("application/skg.refund.ids");
        QDataStream stream(&encodedData, QIODevice::ReadOnly);
        QStringList newItems;

        if (parent.isValid()) {
            SKGBEGINTRANSACTION(*getDocument(), i18nc("Noun, name of the user action", "Merge trackers"), err);
            SKGTrackerObject trackerUnit = getObject(parent);
            while (!stream.atEnd() && !err) {
                int o_id;
                QString o_table;
                stream >> o_table;
                stream >> o_id;

                SKGTrackerObject child(getDocument(), o_id);
                err = child.load();
                if (!err) err = trackerUnit.merge(child);
            }
        }
    }
    SKGMainPanel::displayErrorMessage(err);
    return !err;
}

void SKGObjectModel::dataModified(const QString& iTableName, int iIdTransaction)
{
    if (getRealTable() == iTableName || iTableName.isEmpty() || getRealTable() == "doctransaction") {
        SKGTRACEIN(1, "SKGObjectModel::dataModified");
        if (iTableName == "category") {
            //Full refresh
            m_isResetRealyNeeded = true;
            refresh();
        } else {
            SKGObjectModelBase::dataModified(iTableName, iIdTransaction);
        }
    } else SKGObjectModelBase::dataModified(iTableName, iIdTransaction);
}


#include "skgobjectmodel.moc"

