Commit dbd547a5 authored by Cristian Oneț's avatar Cristian Oneț
Browse files

Add a stage to the consistency check to fix transaction post dates.

If the transaction is moved to another post date the price information
is moved along with it for investments transactions. This fixes the
reported issue, saving the file once will fix all price issues.

Also add the possibility to save the consistency check log to a file
or copy it into the clipboard.

Make sure that the user enters a valid transaction date by checking
that the transaction date is after the opening date of each and every
account involved in the transaction. Until now we only checked that
the transaction date is after the opening date of the account in which
is being entered.

Remove the check for "Sell" action since there is no such action.

BUG: 313793
REVIEW: 122746
parent a5972be0
......@@ -675,7 +675,6 @@ bool TransactionEditor::enterTransactions(QString& newId, bool askForSchedule, b
QList<MyMoneySplit>::const_iterator it_t;
for (it_t = t.splits().constBegin(); it_t != t.splits().constEnd(); ++it_t) {
if (((*it_t).action() != "Buy") &&
((*it_t).action() != "Sell") &&
((*it_t).action() != "Reinvest")) {
continue;
}
......@@ -1827,7 +1826,22 @@ bool StdTransactionEditor::isComplete(QString& reason) const
kMyMoneyDateInput* postDate = dynamic_cast<kMyMoneyDateInput*>(m_editWidgets["postdate"]);
if (postDate) {
if (postDate->date().isValid() && (postDate->date() < m_account.openingDate())) {
QDate accountOpeningDate = m_account.openingDate();
for (QList<MyMoneySplit>::const_iterator it_s = m_splits.constBegin(); it_s != m_splits.constEnd(); ++it_s) {
const MyMoneyAccount& acc = MyMoneyFile::instance()->account((*it_s).accountId());
// compute the newest opening date of all accounts involved in the transaction
if (acc.openingDate() > accountOpeningDate)
accountOpeningDate = acc.openingDate();
}
// check the selected category in case m_splits hasn't been updated yet
KMyMoneyCategory* category = dynamic_cast<KMyMoneyCategory*>(m_editWidgets["category"]);
if (category && !category->selectedItem().isEmpty()) {
MyMoneyAccount cat = MyMoneyFile::instance()->account(category->selectedItem());
if (cat.openingDate() > accountOpeningDate)
accountOpeningDate = cat.openingDate();
}
if (postDate->date().isValid() && (postDate->date() < accountOpeningDate)) {
postDate->markAsBadDate(true, KMyMoneyGlobalSettings::listNegativeValueColor());
reason = i18n("Cannot enter transaction with postdate prior to account's opening date.");
postDate->setToolTip(reason);
......
......@@ -58,6 +58,7 @@
#include <KMenu>
#include <QProgressBar>
#include <QList>
#include <QClipboard>
// ----------------------------------------------------------------------------
// KDE Includes
......@@ -347,12 +348,14 @@ public:
KHolidays::HolidayRegion* m_holidayRegion;
QBitArray m_processingDays;
QMap<QDate, bool> m_holidayMap;
bool m_applicationIsReady;
QStringList m_consistencyCheckResult;
bool m_applicationIsReady;
// methods
void consistencyCheck(bool alwaysDisplayResults);
void setCustomColors();
void copyConsistencyCheckResults();
void saveConsistencyCheckResults();
};
KMyMoneyApp::KMyMoneyApp(QWidget* parent) :
......@@ -476,6 +479,41 @@ void KMyMoneyApp::slotObjectDestroyed(QObject* o)
}
}
void KMyMoneyApp::slotInstallConsistencyCheckContextMenu()
{
// this code relies on the implementation of KMessageBox::informationList to add a context menu to that list,
// please adjust it if it's necessary or rewrite the way the consistency check results are displayed
if (QWidget* dialog = QApplication::activeModalWidget()) {
if (QListWidget* widget = dialog->findChild<QListWidget *>()) {
// give the user a hint that the data can be saved
widget->setToolTip(i18n("This is the consistency check log, use the context menu to copy or save it."));
widget->setWhatsThis(widget->toolTip());
widget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(widget, SIGNAL(customContextMenuRequested(QPoint)), SLOT(slotShowContextMenuForConsistencyCheck(QPoint)));
}
}
}
void KMyMoneyApp::slotShowContextMenuForConsistencyCheck(const QPoint &pos)
{
// allow the user to save the consistency check results
if (QWidget* widget = qobject_cast< QWidget* >(sender())) {
QMenu contextMenu(widget);
QAction* copy = new QAction(i18n("Copy to clipboard"), widget);
QAction* save = new QAction(i18n("Save to file"), widget);
contextMenu.addAction(copy);
contextMenu.addAction(save);
QAction *result = contextMenu.exec(widget->mapToGlobal(pos));
if (result == copy) {
// copy the consistency check results to the clipboard
d->copyConsistencyCheckResults();
} else if (result == save) {
// save the consistency check results to a file
d->saveConsistencyCheckResults();
}
}
}
void KMyMoneyApp::createTransactionMoveMenu(void)
{
if (!d->m_moveToAccountSelector) {
......@@ -6932,23 +6970,49 @@ void KMyMoneyApp::Private::consistencyCheck(bool alwaysDisplayResult)
{
KMSTATUS(i18n("Running consistency check..."));
QStringList msg;
MyMoneyFileTransaction ft;
try {
msg = MyMoneyFile::instance()->consistencyCheck();
m_consistencyCheckResult = MyMoneyFile::instance()->consistencyCheck();
ft.commit();
} catch (const MyMoneyException &e) {
msg.append(i18n("Consistency check failed: %1", e.what()));
m_consistencyCheckResult.append(i18n("Consistency check failed: %1", e.what()));
// always display the result if the check failed
alwaysDisplayResult = true;
}
// in case the consistency check was OK, we get a single line as result
// in all errneous cases, we get more than one line and force the
// display of them.
if (msg.size() > 1) {
KMessageBox::informationList(0, i18n("The consistency check has found some issues in your data. Details are presented below. Those issues that could not be corrected automatically need to be solved by the user."), msg, i18n("Consistency check result"));
} else if (alwaysDisplayResult) {
KMessageBox::informationList(0, i18n("The consistency check has found no issues in your data. Details are presented below."), msg, i18n("Consistency check result"));
if (alwaysDisplayResult || m_consistencyCheckResult.size() > 1) {
QString msg = i18n("The consistency check has found no issues in your data. Details are presented below.");
if (m_consistencyCheckResult.size() > 1)
msg = i18n("The consistency check has found some issues in your data. Details are presented below. Those issues that could not be corrected automatically need to be solved by the user.");
// install a context menu for the list after the dialog is displayed
QTimer::singleShot(500, q, SLOT(slotInstallConsistencyCheckContextMenu()));
KMessageBox::informationList(0, msg, m_consistencyCheckResult, i18n("Consistency check result"));
}
// this data is no longer needed
m_consistencyCheckResult.clear();
}
void KMyMoneyApp::Private::copyConsistencyCheckResults() {
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(m_consistencyCheckResult.join(QLatin1String("\n")));
}
void KMyMoneyApp::Private::saveConsistencyCheckResults() {
QPointer<KFileDialog> dialog = new KFileDialog(KUrl("kfiledialog:///kmymoney-consistency-check"), QString(), q);
dialog->setMode(KFile::File);
dialog->setOperationMode(KFileDialog::Saving);
if (dialog->exec() == QDialog::Accepted && dialog != 0) {
QFile file(dialog->selectedUrl().toLocalFile());
if (file.open(QFile::WriteOnly | QFile::Append | QFile::Text)) {
QTextStream out(&file);
out << m_consistencyCheckResult.join(QLatin1String("\n"));
file.close();
}
}
}
......
......@@ -88,6 +88,16 @@ private slots:
*/
void slotObjectDestroyed(QObject* o);
/**
* Add a context menu to the list used by KMessageBox::informationList to display the consistency check results.
*/
void slotInstallConsistencyCheckContextMenu();
/**
* Handle the context menu of the list used by KMessageBox::informationList to display the consistency check results.
*/
void slotShowContextMenuForConsistencyCheck(const QPoint &);
protected slots:
void slotFileSaveAsFilterChanged(const QString& filter);
......
......@@ -2067,6 +2067,7 @@ const QStringList MyMoneyFile::consistencyCheck(void)
QList<MyMoneySplit> splits = t.splits();
QList<MyMoneySplit>::const_iterator it_s;
bool tChanged = false;
QDate accountOpeningDate;
for (it_s = splits.constBegin(); it_s != splits.constEnd(); ++it_s) {
bool sChanged = false;
MyMoneySplit s = (*it_s);
......@@ -2081,8 +2082,10 @@ const QStringList MyMoneyFile::consistencyCheck(void)
// represent the same currency.
try {
const MyMoneyAccount& acc = this->account(s.accountId());
if (t.commodity() == acc.currencyId()
&& s.shares().reduce() != s.value().reduce()) {
// compute the newest opening date of all accounts involved in the transaction
if (!accountOpeningDate.isValid() || acc.openingDate() > accountOpeningDate)
accountOpeningDate = acc.openingDate();
if (t.commodity() == acc.currencyId() && s.shares().reduce() != s.value().reduce()) {
// use the value as master if the transaction is balanced
if (t.splitSum().isZero()) {
s.setShares(s.value());
......@@ -2123,6 +2126,32 @@ const QStringList MyMoneyFile::consistencyCheck(void)
rc << i18n(" The post date was updated to '%1'.", KGlobal::locale()->formatDate(t.postDate(), KLocale::ShortDate));
++problemCount;
}
// make sure that the transaction's post date is after the opening date of all accounts involved in the transaction
if (accountOpeningDate.isValid() && t.postDate() < accountOpeningDate) {
tChanged = true;
QDate originalPostDate = t.postDate();
t.setPostDate(accountOpeningDate);
// copy the price information for investments to the new date
QList<MyMoneySplit>::const_iterator it_t;
for (it_t = t.splits().constBegin(); it_t != t.splits().constEnd(); ++it_t) {
if (((*it_t).action() != "Buy") &&
((*it_t).action() != "Reinvest")) {
continue;
}
QString id = (*it_t).accountId();
MyMoneyAccount acc = this->account(id);
MyMoneySecurity sec = this->security(acc.currencyId());
MyMoneyPrice price(acc.currencyId(),
sec.tradingCurrency(),
t.postDate(),
(*it_t).price(), "Transaction");
this->addPrice(price);
break;
}
rc << i18n(" * Transaction '%1' has a post date '%2' before one of the referenced account's opening date.", t.id(), KGlobal::locale()->formatDate(originalPostDate, KLocale::ShortDate));
rc << i18n(" The post date was updated to '%1'.", KGlobal::locale()->formatDate(t.postDate(), KLocale::ShortDate));
++problemCount;
}
if (tChanged) {
d->m_storage->modifyTransaction(t);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment