File indexing completed on 2024-05-12 16:42:20
0001 /* 0002 SPDX-FileCopyrightText: 2016-2018 Thomas Baumgart <tbaumgart@kde.org> 0003 SPDX-FileCopyrightText: 2017-2018 Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "splitmodel.h" 0008 0009 // ---------------------------------------------------------------------------- 0010 // QT Includes 0011 0012 #include <QString> 0013 #include <QDebug> 0014 0015 // ---------------------------------------------------------------------------- 0016 // KDE Includes 0017 0018 #include <KLocalizedString> 0019 0020 // ---------------------------------------------------------------------------- 0021 // Project Includes 0022 0023 #include "models.h" 0024 #include "costcentermodel.h" 0025 #include "mymoneysplit.h" 0026 #include "mymoneytransaction.h" 0027 #include "mymoneyfile.h" 0028 #include "mymoneyaccount.h" 0029 #include "mymoneypayee.h" 0030 #include "mymoneymoney.h" 0031 #include "mymoneyexception.h" 0032 #include "kmymoneyutils.h" 0033 #include "modelenums.h" 0034 0035 using namespace eLedgerModel; 0036 using namespace eMyMoney; 0037 0038 class SplitModelPrivate 0039 { 0040 public: 0041 SplitModelPrivate() 0042 : m_invertValues(false) 0043 {} 0044 0045 bool isCreateSplitEntry(const QString& id) const { 0046 return id.isEmpty(); 0047 } 0048 0049 MyMoneyTransaction m_transaction; 0050 QVector<MyMoneySplit> m_splits; 0051 bool m_invertValues; 0052 }; 0053 0054 SplitModel::SplitModel(QObject* parent) : 0055 QAbstractTableModel(parent), 0056 d_ptr(new SplitModelPrivate) 0057 { 0058 } 0059 0060 SplitModel::~SplitModel() 0061 { 0062 } 0063 0064 QString SplitModel::newSplitId() 0065 { 0066 return QLatin1String("New-ID"); 0067 } 0068 0069 bool SplitModel::isNewSplitId(const QString& id) 0070 { 0071 return id.compare(newSplitId()) == 0; 0072 } 0073 0074 0075 int SplitModel::rowCount(const QModelIndex& parent) const 0076 { 0077 Q_D(const SplitModel); 0078 Q_UNUSED(parent); 0079 return d->m_splits.count(); 0080 } 0081 0082 int SplitModel::columnCount(const QModelIndex& parent) const 0083 { 0084 Q_UNUSED(parent); 0085 return (int)Column::LastColumn; 0086 } 0087 0088 0089 void SplitModel::deepCopy(const SplitModel& right, bool revertSplitSign) 0090 { 0091 Q_D(SplitModel); 0092 beginInsertRows(QModelIndex(), 0, right.rowCount()); 0093 d->m_splits = right.d_func()->m_splits; 0094 d->m_transaction = right.d_func()->m_transaction; 0095 if(revertSplitSign) { 0096 for(int idx = 0; idx < d->m_splits.count(); ++idx) { 0097 MyMoneySplit& split = d->m_splits[idx]; 0098 split.setShares(-split.shares()); 0099 split.setValue(-split.value()); 0100 } 0101 } 0102 endInsertRows(); 0103 } 0104 0105 QVariant SplitModel::headerData(int section, Qt::Orientation orientation, int role) const 0106 { 0107 if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { 0108 switch(section) { 0109 case (int)Column::CostCenter: 0110 return i18n("Cost Center"); 0111 case (int)Column::Detail: 0112 return i18n("Category"); 0113 case (int)Column::Number: 0114 return i18n("No"); 0115 case (int)Column::Date: 0116 return i18n("Date"); 0117 case (int)Column::Security: 0118 return i18n("Security"); 0119 case (int)Column::Reconciliation: 0120 return i18n("C"); 0121 case (int)Column::Payment: 0122 return i18n("Payment"); 0123 case (int)Column::Deposit: 0124 return i18n("Deposit"); 0125 case (int)Column::Quantity: 0126 return i18n("Quantity"); 0127 case (int)Column::Price: 0128 return i18n("Price"); 0129 case (int)Column::Amount: 0130 return i18n("Amount"); 0131 case (int)Column::Value: 0132 return i18n("Value"); 0133 case (int)Column::Balance: 0134 return i18n("Balance"); 0135 } 0136 } 0137 return QAbstractItemModel::headerData(section, orientation, role); 0138 } 0139 0140 QVariant SplitModel::data(const QModelIndex& index, int role) const 0141 { 0142 Q_D(const SplitModel); 0143 if(!index.isValid()) 0144 return QVariant(); 0145 if(index.row() < 0 || index.row() >= d->m_splits.count()) 0146 return QVariant(); 0147 0148 QVariant rc; 0149 MyMoneyAccount acc; 0150 MyMoneyMoney value; 0151 const MyMoneySplit& split = d->m_splits[index.row()]; 0152 QModelIndex subIndex; 0153 CostCenterModel* ccModel = Models::instance()->costCenterModel(); 0154 0155 switch(role) { 0156 case Qt::DisplayRole: 0157 // make sure to never return any displayable text for the dummy entry 0158 if(!d->isCreateSplitEntry(split.id())) { 0159 switch(index.column()) { 0160 case (int)Column::Detail: 0161 rc = MyMoneyFile::instance()->accountToCategory(split.accountId()); 0162 break; 0163 case (int)Column::CostCenter: 0164 subIndex = Models::indexById(ccModel, CostCenterModel::CostCenterIdRole, split.costCenterId()); 0165 rc = ccModel->data(subIndex); 0166 break; 0167 case (int)Column::Number: 0168 rc = split.number(); 0169 break; 0170 case (int)Column::Reconciliation: 0171 rc = KMyMoneyUtils::reconcileStateToString(split.reconcileFlag(), false); 0172 break; 0173 case (int)Column::Payment: 0174 if(split.value().isNegative()) { 0175 acc = MyMoneyFile::instance()->account(split.accountId()); 0176 rc = (-split).value(d->m_transaction.commodity(), acc.currencyId()).formatMoney(acc.fraction()); 0177 } 0178 break; 0179 case (int)Column::Deposit: 0180 if(!split.value().isNegative()) { 0181 acc = MyMoneyFile::instance()->account(split.accountId()); 0182 rc = split.value(d->m_transaction.commodity(), acc.currencyId()).formatMoney(acc.fraction()); 0183 } 0184 break; 0185 default: 0186 break; 0187 } 0188 } 0189 break; 0190 0191 case Qt::TextAlignmentRole: 0192 switch(index.column()) { 0193 case (int)Column::Payment: 0194 case (int)Column::Deposit: 0195 case (int)Column::Amount: 0196 case (int)Column::Balance: 0197 case (int)Column::Value: 0198 rc = QVariant(Qt::AlignRight| Qt::AlignTop); 0199 break; 0200 case (int)Column::Reconciliation: 0201 rc = QVariant(Qt::AlignHCenter | Qt::AlignTop); 0202 break; 0203 default: 0204 rc = QVariant(Qt::AlignLeft | Qt::AlignTop); 0205 break; 0206 } 0207 break; 0208 0209 case (int)Role::AccountId: 0210 rc = split.accountId(); 0211 break; 0212 0213 case (int)Role::Account: 0214 rc = MyMoneyFile::instance()->accountToCategory(split.accountId()); 0215 break; 0216 0217 case (int)Role::TransactionId: 0218 rc = QString("%1").arg(d->m_transaction.id()); 0219 break; 0220 0221 case (int)Role::TransactionSplitId: 0222 rc = QString("%1-%2").arg(d->m_transaction.id(), split.id()); 0223 break; 0224 0225 case (int)Role::SplitId: 0226 rc = split.id(); 0227 break; 0228 0229 case (int)Role::Memo: 0230 case (int)Role::SingleLineMemo: 0231 rc = split.memo(); 0232 if(role == (int)Role::SingleLineMemo) { 0233 QString txt = rc.toString(); 0234 // remove empty lines 0235 txt.replace("\n\n", "\n"); 0236 // replace '\n' with ", " 0237 txt.replace('\n', ", "); 0238 rc = txt; 0239 } 0240 break; 0241 0242 case (int)Role::SplitShares: 0243 rc = QVariant::fromValue<MyMoneyMoney>(split.shares()); 0244 break; 0245 0246 case (int)Role::SplitValue: 0247 acc = MyMoneyFile::instance()->account(split.accountId()); 0248 rc = QVariant::fromValue<MyMoneyMoney>(split.value(d->m_transaction.commodity(), acc.currencyId())); 0249 break; 0250 0251 case (int)Role::PayeeName: 0252 try { 0253 rc = MyMoneyFile::instance()->payee(split.payeeId()).name(); 0254 } catch (const MyMoneyException &e) { 0255 } 0256 break; 0257 0258 case (int)Role::CostCenterId: 0259 rc = split.costCenterId(); 0260 break; 0261 0262 case (int)Role::TransactionCommodity: 0263 return d->m_transaction.commodity(); 0264 break; 0265 0266 case (int)Role::Number: 0267 rc = split.number(); 0268 break; 0269 0270 case (int)Role::PayeeId: 0271 rc = split.payeeId(); 0272 break; 0273 0274 default: 0275 if(role >= Qt::UserRole) { 0276 qWarning() << "Undefined role" << role << "(" << role-Qt::UserRole << ") in SplitModel::data"; 0277 } 0278 break; 0279 } 0280 return rc; 0281 } 0282 0283 bool SplitModel::setData(const QModelIndex& index, const QVariant& value, int role) 0284 { 0285 Q_D(SplitModel); 0286 bool rc = false; 0287 if(index.isValid()) { 0288 MyMoneySplit& split = d->m_splits[index.row()]; 0289 if(split.id().isEmpty()) { 0290 split = MyMoneySplit(newSplitId(), split); 0291 } 0292 QString val; 0293 rc = true; 0294 switch(role) { 0295 case (int)Role::PayeeId: 0296 split.setPayeeId(value.toString()); 0297 break; 0298 0299 case (int)Role::AccountId: 0300 split.setAccountId(value.toString()); 0301 break; 0302 0303 case (int)Role::Memo: 0304 split.setMemo(value.toString()); 0305 break; 0306 0307 case (int)Role::CostCenterId: 0308 val = value.toString(); 0309 split.setCostCenterId(value.toString()); 0310 break; 0311 0312 case (int)Role::Number: 0313 split.setNumber(value.toString()); 0314 break; 0315 0316 case (int)Role::SplitShares: 0317 split.setShares(value.value<MyMoneyMoney>()); 0318 break; 0319 0320 case (int)Role::SplitValue: 0321 split.setValue(value.value<MyMoneyMoney>()); 0322 break; 0323 0324 case (int)Role::EmitDataChanged: 0325 { 0326 // the whole row changed 0327 QModelIndex topLeft = this->index(index.row(), 0); 0328 QModelIndex bottomRight = this->index(index.row(), this->columnCount()-1); 0329 emit dataChanged(topLeft, bottomRight); 0330 } 0331 break; 0332 0333 default: 0334 rc = false; 0335 break; 0336 } 0337 } 0338 0339 return rc; 0340 } 0341 0342 0343 void SplitModel::addSplit(const QString& transactionSplitId) 0344 { 0345 Q_D(SplitModel); 0346 QRegExp transactionSplitIdExp("^(\\w+)-(\\w+)$"); 0347 if(transactionSplitIdExp.exactMatch(transactionSplitId)) { 0348 const QString transactionId = transactionSplitIdExp.cap(1); 0349 const QString splitId = transactionSplitIdExp.cap(2); 0350 if(transactionId != d->m_transaction.id()) { 0351 try { 0352 d->m_transaction = MyMoneyFile::instance()->transaction(transactionId); 0353 } catch (const MyMoneyException &e) { 0354 d->m_transaction = MyMoneyTransaction(); 0355 } 0356 } 0357 try { 0358 beginInsertRows(QModelIndex(), rowCount(), rowCount()); 0359 d->m_splits.append(d->m_transaction.splitById(splitId)); 0360 endInsertRows(); 0361 } catch (const MyMoneyException &e) { 0362 d->m_transaction = MyMoneyTransaction(); 0363 } 0364 } 0365 } 0366 0367 void SplitModel::addEmptySplitEntry() 0368 { 0369 Q_D(SplitModel); 0370 QModelIndexList list = match(index(0, 0), (int)Role::SplitId, QString(), -1, Qt::MatchExactly); 0371 if(list.count() == 0) { 0372 beginInsertRows(QModelIndex(), rowCount(), rowCount()); 0373 // d->m_splits.append(MyMoneySplit(d->newSplitEntryId(), MyMoneySplit())); 0374 d->m_splits.append(MyMoneySplit()); 0375 endInsertRows(); 0376 } 0377 } 0378 0379 void SplitModel::removeEmptySplitEntry() 0380 { 0381 Q_D(SplitModel); 0382 // QModelIndexList list = match(index(0, 0), SplitIdRole, d->newSplitEntryId(), -1, Qt::MatchExactly); 0383 QModelIndexList list = match(index(0, 0), (int)Role::SplitId, QString(), -1, Qt::MatchExactly); 0384 if(list.count()) { 0385 QModelIndex index = list.at(0); 0386 beginRemoveRows(QModelIndex(), index.row(), index.row()); 0387 d->m_splits.remove(index.row(), 1); 0388 endRemoveRows(); 0389 } 0390 } 0391 0392 bool SplitModel::removeRows(int row, int count, const QModelIndex& parent) 0393 { 0394 Q_D(SplitModel); 0395 bool rc = false; 0396 if(count > 0) { 0397 beginRemoveRows(parent, row, row + count - 1); 0398 d->m_splits.remove(row, count); 0399 endRemoveRows(); 0400 rc = true; 0401 } 0402 return rc; 0403 } 0404 0405 Qt::ItemFlags SplitModel::flags(const QModelIndex& index) const 0406 { 0407 Q_D(const SplitModel); 0408 Qt::ItemFlags flags; 0409 0410 if(!index.isValid()) 0411 return flags; 0412 if(index.row() < 0 || index.row() >= d->m_splits.count()) 0413 return flags; 0414 0415 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; 0416 } 0417 0418 0419 #if 0 0420 void SplitModel::removeSplit(const LedgerTransaction& t) 0421 { 0422 Q_D(SplitModel); 0423 QModelIndexList list = match(index(0, 0), TransactionSplitIdRole, t.transactionSplitId(), -1, Qt::MatchExactly); 0424 if(list.count()) { 0425 QModelIndex index = list.at(0); 0426 beginRemoveRows(QModelIndex(), index.row(), index.row()); 0427 delete d->m_ledgerItems[index.row()]; 0428 d->m_ledgerItems.remove(index.row(), 1); 0429 endRemoveRows(); 0430 0431 // just make sure we're in sync 0432 Q_ASSERT(d->m_ledgerItems.count() == rowCount()); 0433 } 0434 } 0435 #endif