Changeset View
Changeset View
Standalone View
Standalone View
kmymoney/models/splitmodel.cpp
- This file was added.
1 | /*************************************************************************** | ||||
---|---|---|---|---|---|
2 | ledgermodel.cpp | ||||
3 | ------------------- | ||||
4 | begin : Sat Aug 8 2015 | ||||
5 | copyright : (C) 2015 by Thomas Baumgart | ||||
6 | email : Thomas Baumgart <tbaumgart@kde.org> | ||||
7 | (C) 2017 by Łukasz Wojniłowicz <lukasz.wojnilowicz@gmail.com> | ||||
8 | ***************************************************************************/ | ||||
9 | | ||||
10 | /*************************************************************************** | ||||
11 | * * | ||||
12 | * This program is free software; you can redistribute it and/or modify * | ||||
13 | * it under the terms of the GNU General Public License as published by * | ||||
14 | * the Free Software Foundation; either version 2 of the License, or * | ||||
15 | * (at your option) any later version. * | ||||
16 | * * | ||||
17 | ***************************************************************************/ | ||||
18 | | ||||
19 | #include "splitmodel.h" | ||||
20 | | ||||
21 | // ---------------------------------------------------------------------------- | ||||
22 | // QT Includes | ||||
23 | | ||||
24 | #include <QString> | ||||
25 | #include <QDebug> | ||||
26 | | ||||
27 | // ---------------------------------------------------------------------------- | ||||
28 | // KDE Includes | ||||
29 | | ||||
30 | #include <KLocalizedString> | ||||
31 | | ||||
32 | // ---------------------------------------------------------------------------- | ||||
33 | // Project Includes | ||||
34 | | ||||
35 | #include "models.h" | ||||
36 | #include "costcentermodel.h" | ||||
37 | #include "mymoneysplit.h" | ||||
38 | #include "mymoneytransaction.h" | ||||
39 | #include "mymoneyfile.h" | ||||
40 | #include "mymoneyaccount.h" | ||||
41 | #include "mymoneypayee.h" | ||||
42 | #include "mymoneymoney.h" | ||||
43 | #include "kmymoneyutils.h" | ||||
44 | #include "modelenums.h" | ||||
45 | | ||||
46 | using namespace eLedgerModel; | ||||
47 | using namespace eMyMoney; | ||||
48 | | ||||
49 | class SplitModelPrivate | ||||
50 | { | ||||
51 | public: | ||||
52 | SplitModelPrivate() | ||||
53 | : m_invertValues(false) | ||||
54 | {} | ||||
55 | | ||||
56 | bool isCreateSplitEntry(const QString& id) const { | ||||
57 | return id.isEmpty(); | ||||
58 | } | ||||
59 | | ||||
60 | MyMoneyTransaction m_transaction; | ||||
61 | QVector<MyMoneySplit> m_splits; | ||||
62 | bool m_invertValues; | ||||
63 | }; | ||||
64 | | ||||
65 | SplitModel::SplitModel(QObject* parent) : | ||||
66 | QAbstractTableModel(parent), | ||||
67 | d_ptr(new SplitModelPrivate) | ||||
68 | { | ||||
69 | } | ||||
70 | | ||||
71 | SplitModel::~SplitModel() | ||||
72 | { | ||||
73 | } | ||||
74 | | ||||
75 | QString SplitModel::newSplitId() | ||||
76 | { | ||||
77 | return QLatin1String("New-ID"); | ||||
78 | } | ||||
79 | | ||||
80 | bool SplitModel::isNewSplitId(const QString& id) | ||||
81 | { | ||||
82 | return id.compare(newSplitId()) == 0; | ||||
83 | } | ||||
84 | | ||||
85 | | ||||
86 | int SplitModel::rowCount(const QModelIndex& parent) const | ||||
87 | { | ||||
88 | Q_D(const SplitModel); | ||||
89 | Q_UNUSED(parent); | ||||
90 | return d->m_splits.count(); | ||||
91 | } | ||||
92 | | ||||
93 | int SplitModel::columnCount(const QModelIndex& parent) const | ||||
94 | { | ||||
95 | Q_UNUSED(parent); | ||||
96 | return (int)Column::LastColumn; | ||||
97 | } | ||||
98 | | ||||
99 | | ||||
100 | void SplitModel::deepCopy(const SplitModel& right, bool revertSplitSign) | ||||
101 | { | ||||
102 | Q_D(SplitModel); | ||||
103 | beginInsertRows(QModelIndex(), 0, right.rowCount()); | ||||
104 | d->m_splits = right.d_func()->m_splits; | ||||
105 | d->m_transaction = right.d_func()->m_transaction; | ||||
106 | if(revertSplitSign) { | ||||
107 | for(int idx = 0; idx < d->m_splits.count(); ++idx) { | ||||
108 | MyMoneySplit& split = d->m_splits[idx]; | ||||
109 | split.setShares(-split.shares()); | ||||
110 | split.setValue(-split.value()); | ||||
111 | } | ||||
112 | } | ||||
113 | endInsertRows(); | ||||
114 | } | ||||
115 | | ||||
116 | QVariant SplitModel::headerData(int section, Qt::Orientation orientation, int role) const | ||||
117 | { | ||||
118 | if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { | ||||
119 | switch(section) { | ||||
120 | case (int)Column::CostCenter: | ||||
121 | return i18n("Cost Center"); | ||||
122 | case (int)Column::Detail: | ||||
123 | return i18n("Category"); | ||||
124 | case (int)Column::Number: | ||||
125 | return i18n("No"); | ||||
126 | case (int)Column::Date: | ||||
127 | return i18n("Date"); | ||||
128 | case (int)Column::Security: | ||||
129 | return i18n("Security"); | ||||
130 | case (int)Column::Reconciliation: | ||||
131 | return i18n("C"); | ||||
132 | case (int)Column::Payment: | ||||
133 | return i18n("Payment"); | ||||
134 | case (int)Column::Deposit: | ||||
135 | return i18n("Deposit"); | ||||
136 | case (int)Column::Quantity: | ||||
137 | return i18n("Quantity"); | ||||
138 | case (int)Column::Price: | ||||
139 | return i18n("Price"); | ||||
140 | case (int)Column::Amount: | ||||
141 | return i18n("Amount"); | ||||
142 | case (int)Column::Value: | ||||
143 | return i18n("Value"); | ||||
144 | case (int)Column::Balance: | ||||
145 | return i18n("Balance"); | ||||
146 | } | ||||
147 | } | ||||
148 | return QAbstractItemModel::headerData(section, orientation, role); | ||||
149 | } | ||||
150 | | ||||
151 | QVariant SplitModel::data(const QModelIndex& index, int role) const | ||||
152 | { | ||||
153 | Q_D(const SplitModel); | ||||
154 | if(!index.isValid()) | ||||
155 | return QVariant(); | ||||
156 | if(index.row() < 0 || index.row() >= d->m_splits.count()) | ||||
157 | return QVariant(); | ||||
158 | | ||||
159 | QVariant rc; | ||||
160 | MyMoneyAccount acc; | ||||
161 | MyMoneyMoney value; | ||||
162 | const MyMoneySplit& split = d->m_splits[index.row()]; | ||||
163 | QModelIndex subIndex; | ||||
164 | CostCenterModel* ccModel = Models::instance()->costCenterModel(); | ||||
165 | | ||||
166 | switch(role) { | ||||
167 | case Qt::DisplayRole: | ||||
168 | // make sure to never return any displayable text for the dummy entry | ||||
169 | if(!d->isCreateSplitEntry(split.id())) { | ||||
170 | switch(index.column()) { | ||||
171 | case (int)Column::Detail: | ||||
172 | rc = MyMoneyFile::instance()->accountToCategory(split.accountId()); | ||||
173 | break; | ||||
174 | case (int)Column::CostCenter: | ||||
175 | subIndex = Models::indexById(ccModel, CostCenterModel::CostCenterIdRole, split.costCenterId()); | ||||
176 | rc = ccModel->data(subIndex); | ||||
177 | break; | ||||
178 | case (int)Column::Number: | ||||
179 | rc = split.number(); | ||||
180 | break; | ||||
181 | case (int)Column::Reconciliation: | ||||
182 | rc = KMyMoneyUtils::reconcileStateToString(split.reconcileFlag(), false); | ||||
183 | break; | ||||
184 | case (int)Column::Payment: | ||||
185 | if(split.value().isNegative()) { | ||||
186 | acc = MyMoneyFile::instance()->account(split.accountId()); | ||||
187 | rc = (-split).value(d->m_transaction.commodity(), acc.currencyId()).formatMoney(acc.fraction()); | ||||
188 | } | ||||
189 | break; | ||||
190 | case (int)Column::Deposit: | ||||
191 | if(!split.value().isNegative()) { | ||||
192 | acc = MyMoneyFile::instance()->account(split.accountId()); | ||||
193 | rc = split.value(d->m_transaction.commodity(), acc.currencyId()).formatMoney(acc.fraction()); | ||||
194 | } | ||||
195 | break; | ||||
196 | default: | ||||
197 | break; | ||||
198 | } | ||||
199 | } | ||||
200 | break; | ||||
201 | | ||||
202 | case Qt::TextAlignmentRole: | ||||
203 | switch(index.column()) { | ||||
204 | case (int)Column::Payment: | ||||
205 | case (int)Column::Deposit: | ||||
206 | case (int)Column::Amount: | ||||
207 | case (int)Column::Balance: | ||||
208 | case (int)Column::Value: | ||||
209 | rc = QVariant(Qt::AlignRight| Qt::AlignTop); | ||||
210 | break; | ||||
211 | case (int)Column::Reconciliation: | ||||
212 | rc = QVariant(Qt::AlignHCenter | Qt::AlignTop); | ||||
213 | break; | ||||
214 | default: | ||||
215 | rc = QVariant(Qt::AlignLeft | Qt::AlignTop); | ||||
216 | break; | ||||
217 | } | ||||
218 | break; | ||||
219 | | ||||
220 | case (int)Role::AccountId: | ||||
221 | rc = split.accountId(); | ||||
222 | break; | ||||
223 | | ||||
224 | case (int)Role::Account: | ||||
225 | rc = MyMoneyFile::instance()->accountToCategory(split.accountId()); | ||||
226 | break; | ||||
227 | | ||||
228 | case (int)Role::TransactionId: | ||||
229 | rc = QString("%1").arg(d->m_transaction.id()); | ||||
230 | break; | ||||
231 | | ||||
232 | case (int)Role::TransactionSplitId: | ||||
233 | rc = QString("%1-%2").arg(d->m_transaction.id()).arg(split.id()); | ||||
234 | break; | ||||
235 | | ||||
236 | case (int)Role::SplitId: | ||||
237 | rc = split.id(); | ||||
238 | break; | ||||
239 | | ||||
240 | case (int)Role::Memo: | ||||
241 | case (int)Role::SingleLineMemo: | ||||
242 | rc = split.memo(); | ||||
243 | if(role == (int)Role::SingleLineMemo) { | ||||
244 | QString txt = rc.toString(); | ||||
245 | // remove empty lines | ||||
246 | txt.replace("\n\n", "\n"); | ||||
247 | // replace '\n' with ", " | ||||
248 | txt.replace('\n', ", "); | ||||
249 | rc = txt; | ||||
250 | } | ||||
251 | break; | ||||
252 | | ||||
253 | case (int)Role::SplitShares: | ||||
254 | rc = QVariant::fromValue<MyMoneyMoney>(split.shares()); | ||||
255 | break; | ||||
256 | | ||||
257 | case (int)Role::SplitValue: | ||||
258 | acc = MyMoneyFile::instance()->account(split.accountId()); | ||||
259 | rc = QVariant::fromValue<MyMoneyMoney>(split.value(d->m_transaction.commodity(), acc.currencyId())); | ||||
260 | break; | ||||
261 | | ||||
262 | case (int)Role::PayeeName: | ||||
263 | try { | ||||
264 | rc = MyMoneyFile::instance()->payee(split.payeeId()).name(); | ||||
265 | } catch(MyMoneyException&e) { | ||||
266 | } | ||||
267 | break; | ||||
268 | | ||||
269 | case (int)Role::CostCenterId: | ||||
270 | rc = split.costCenterId(); | ||||
271 | break; | ||||
272 | | ||||
273 | case (int)Role::TransactionCommodity: | ||||
274 | return d->m_transaction.commodity(); | ||||
275 | break; | ||||
276 | | ||||
277 | case (int)Role::Number: | ||||
278 | rc = split.number(); | ||||
279 | break; | ||||
280 | | ||||
281 | case (int)Role::PayeeId: | ||||
282 | rc = split.payeeId(); | ||||
283 | break; | ||||
284 | | ||||
285 | default: | ||||
286 | if(role >= Qt::UserRole) { | ||||
287 | qWarning() << "Undefined role" << role << "(" << role-Qt::UserRole << ") in SplitModel::data"; | ||||
288 | } | ||||
289 | break; | ||||
290 | } | ||||
291 | return rc; | ||||
292 | } | ||||
293 | | ||||
294 | bool SplitModel::setData(const QModelIndex& index, const QVariant& value, int role) | ||||
295 | { | ||||
296 | Q_D(SplitModel); | ||||
297 | bool rc = false; | ||||
298 | if(index.isValid()) { | ||||
299 | MyMoneySplit& split = d->m_splits[index.row()]; | ||||
300 | if(split.id().isEmpty()) { | ||||
301 | split = MyMoneySplit(newSplitId(), split); | ||||
302 | } | ||||
303 | QString val; | ||||
304 | rc = true; | ||||
305 | switch(role) { | ||||
306 | case (int)Role::PayeeId: | ||||
307 | split.setPayeeId(value.toString()); | ||||
308 | break; | ||||
309 | | ||||
310 | case (int)Role::AccountId: | ||||
311 | split.setAccountId(value.toString()); | ||||
312 | break; | ||||
313 | | ||||
314 | case (int)Role::Memo: | ||||
315 | split.setMemo(value.toString()); | ||||
316 | break; | ||||
317 | | ||||
318 | case (int)Role::CostCenterId: | ||||
319 | val = value.toString(); | ||||
320 | split.setCostCenterId(value.toString()); | ||||
321 | break; | ||||
322 | | ||||
323 | case (int)Role::Number: | ||||
324 | split.setNumber(value.toString()); | ||||
325 | break; | ||||
326 | | ||||
327 | case (int)Role::SplitShares: | ||||
328 | split.setShares(value.value<MyMoneyMoney>()); | ||||
329 | break; | ||||
330 | | ||||
331 | case (int)Role::SplitValue: | ||||
332 | split.setValue(value.value<MyMoneyMoney>()); | ||||
333 | break; | ||||
334 | | ||||
335 | case (int)Role::EmitDataChanged: | ||||
336 | { | ||||
337 | // the whole row changed | ||||
338 | QModelIndex topLeft = this->index(index.row(), 0); | ||||
339 | QModelIndex bottomRight = this->index(index.row(), this->columnCount()-1); | ||||
340 | emit dataChanged(topLeft, bottomRight); | ||||
341 | } | ||||
342 | break; | ||||
343 | | ||||
344 | default: | ||||
345 | rc = false; | ||||
346 | break; | ||||
347 | } | ||||
348 | } | ||||
349 | | ||||
350 | return rc; | ||||
351 | } | ||||
352 | | ||||
353 | | ||||
354 | void SplitModel::addSplit(const QString& transactionSplitId) | ||||
355 | { | ||||
356 | Q_D(SplitModel); | ||||
357 | QRegExp transactionSplitIdExp("^(\\w+)-(\\w+)$"); | ||||
358 | if(transactionSplitIdExp.exactMatch(transactionSplitId)) { | ||||
359 | const QString transactionId = transactionSplitIdExp.cap(1); | ||||
360 | const QString splitId = transactionSplitIdExp.cap(2); | ||||
361 | if(transactionId != d->m_transaction.id()) { | ||||
362 | try { | ||||
363 | d->m_transaction = MyMoneyFile::instance()->transaction(transactionId); | ||||
364 | } catch(MyMoneyException& e) { | ||||
365 | d->m_transaction = MyMoneyTransaction(); | ||||
366 | } | ||||
367 | } | ||||
368 | try { | ||||
369 | beginInsertRows(QModelIndex(), rowCount(), rowCount()); | ||||
370 | d->m_splits.append(d->m_transaction.splitById(splitId)); | ||||
371 | endInsertRows(); | ||||
372 | } catch(MyMoneyException& e) { | ||||
373 | d->m_transaction = MyMoneyTransaction(); | ||||
374 | } | ||||
375 | } | ||||
376 | } | ||||
377 | | ||||
378 | void SplitModel::addEmptySplitEntry() | ||||
379 | { | ||||
380 | Q_D(SplitModel); | ||||
381 | QModelIndexList list = match(index(0, 0), (int)Role::SplitId, QString(), -1, Qt::MatchExactly); | ||||
382 | if(list.count() == 0) { | ||||
383 | beginInsertRows(QModelIndex(), rowCount(), rowCount()); | ||||
384 | // d->m_splits.append(MyMoneySplit(d->newSplitEntryId(), MyMoneySplit())); | ||||
385 | d->m_splits.append(MyMoneySplit()); | ||||
386 | endInsertRows(); | ||||
387 | } | ||||
388 | } | ||||
389 | | ||||
390 | void SplitModel::removeEmptySplitEntry() | ||||
391 | { | ||||
392 | Q_D(SplitModel); | ||||
393 | // QModelIndexList list = match(index(0, 0), SplitIdRole, d->newSplitEntryId(), -1, Qt::MatchExactly); | ||||
394 | QModelIndexList list = match(index(0, 0), (int)Role::SplitId, QString(), -1, Qt::MatchExactly); | ||||
395 | if(list.count()) { | ||||
396 | QModelIndex index = list.at(0); | ||||
397 | beginRemoveRows(QModelIndex(), index.row(), index.row()); | ||||
398 | d->m_splits.remove(index.row(), 1); | ||||
399 | endRemoveRows(); | ||||
400 | } | ||||
401 | } | ||||
402 | | ||||
403 | bool SplitModel::removeRows(int row, int count, const QModelIndex& parent) | ||||
404 | { | ||||
405 | Q_D(SplitModel); | ||||
406 | bool rc = false; | ||||
407 | if(count > 0) { | ||||
408 | beginRemoveRows(parent, row, row + count - 1); | ||||
409 | d->m_splits.remove(row, count); | ||||
410 | endRemoveRows(); | ||||
411 | rc = true; | ||||
412 | } | ||||
413 | return rc; | ||||
414 | } | ||||
415 | | ||||
416 | Qt::ItemFlags SplitModel::flags(const QModelIndex& index) const | ||||
417 | { | ||||
418 | Q_D(const SplitModel); | ||||
419 | Qt::ItemFlags flags; | ||||
420 | | ||||
421 | if(!index.isValid()) | ||||
422 | return flags; | ||||
423 | if(index.row() < 0 || index.row() >= d->m_splits.count()) | ||||
424 | return flags; | ||||
425 | | ||||
426 | return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; | ||||
427 | } | ||||
428 | | ||||
429 | | ||||
430 | #if 0 | ||||
431 | void SplitModel::removeSplit(const LedgerTransaction& t) | ||||
432 | { | ||||
433 | Q_D(SplitModel); | ||||
434 | QModelIndexList list = match(index(0, 0), TransactionSplitIdRole, t.transactionSplitId(), -1, Qt::MatchExactly); | ||||
435 | if(list.count()) { | ||||
436 | QModelIndex index = list.at(0); | ||||
437 | beginRemoveRows(QModelIndex(), index.row(), index.row()); | ||||
438 | delete d->m_ledgerItems[index.row()]; | ||||
439 | d->m_ledgerItems.remove(index.row(), 1); | ||||
440 | endRemoveRows(); | ||||
441 | | ||||
442 | // just make sure we're in sync | ||||
443 | Q_ASSERT(d->m_ledgerItems.count() == rowCount()); | ||||
444 | } | ||||
445 | } | ||||
446 | #endif |