You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/kmymoney2/dialogs/keditscheduledlg.cpp

559 lines
21 KiB

/***************************************************************************
keditscheduledlg.cpp - description
-------------------
begin : Mon Sep 3 2007
copyright : (C) 2007 by Thomas Baumgart
email : Thomas Baumgart <ipwizard@users.sourceforge.net>
***************************************************************************/
/***************************************************************************
* *
* 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. *
* *
***************************************************************************/
// ----------------------------------------------------------------------------
// QT Includes
#include <tqtimer.h>
#include <tqwidgetlist.h>
#include <tqcheckbox.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqvaluevector.h>
// ----------------------------------------------------------------------------
// KDE Includes
#include <klocale.h>
#include <kmessagebox.h>
#include <kpushbutton.h>
#include <kstdguiitem.h>
#include <klineedit.h>
#include <knuminput.h>
// ----------------------------------------------------------------------------
// Project Includes
#include <kmymoney/register.h>
#include <kmymoney/transactionform.h>
#include <kmymoney/transaction.h>
#include <kmymoney/transactioneditor.h>
#include <kmymoney/kmymoneylineedit.h>
#include <kmymoney/kmymoneydateinput.h>
#include <kmymoney/kmymoneycombo.h>
#include <kmymoney/kguiutils.h>
#include <kmymoney/kmymoneyutils.h>
#include "keditscheduledlg.h"
#include "../kmymoney2.h"
class KEditScheduleDlg::Private {
public:
MyMoneySchedule m_schedule;
KMyMoneyRegister::Transaction* m_item;
TQWidgetList m_tabOrderWidgets;
TransactionEditor* m_editor;
kMandatoryFieldGroup* m_requiredFields;
};
KEditScheduleDlg::KEditScheduleDlg(const MyMoneySchedule& schedule, TQWidget *parent, const char *name) :
KEditScheduleDlgDecl(parent, name, true),
d(new Private)
{
d->m_schedule = schedule;
d->m_editor = 0;
buttonOk->setGuiItem(KStdGuiItem::ok());
buttonCancel->setGuiItem(KStdGuiItem::cancel());
buttonHelp->setGuiItem(KStdGuiItem::help());
d->m_requiredFields = new kMandatoryFieldGroup (TQT_TQOBJECT(this));
d->m_requiredFields->setOkButton(buttonOk); // button to be enabled when all fields present
// make sure, we have a tabbar with the form
// insert it after the horizontal line
m_paymentInformationLayout->insertWidget(2, m_form->tabBar(m_form->parentWidget()));
// we never need to see the register
m_register->hide();
// ... setup the form ...
m_form->setupForm(d->m_schedule.account());
// ... and the register ...
m_register->clear();
// ... now add the transaction to register and form ...
MyMoneyTransaction t = transaction();
d->m_item = KMyMoneyRegister::Register::transactionFactory(m_register, t, d->m_schedule.transaction().splits()[0], 0);
m_register->selectItem(d->m_item);
// show the account row
d->m_item->setShowRowInForm(0, true);
m_form->slotSetTransaction(d->m_item);
// setup widget contents
m_nameEdit->setText(d->m_schedule.name());
m_frequencyEdit->setCurrentItem(d->m_schedule.occurencePeriod());
if(m_frequencyEdit->currentItem() == -1)
m_frequencyEdit->setCurrentItem(MyMoneySchedule::OCCUR_MONTHLY);
slotFrequencyChanged(m_frequencyEdit->currentItem());
m_frequencyNoEdit->setValue(d->m_schedule.occurenceMultiplier());
// load option widgets
m_paymentMethodEdit->insertItem(i18n("Direct deposit"), MyMoneySchedule::STYPE_DIRECTDEPOSIT);
m_paymentMethodEdit->insertItem(i18n("Manual deposit"), MyMoneySchedule::STYPE_MANUALDEPOSIT);
m_paymentMethodEdit->insertItem(i18n("Direct debit"), MyMoneySchedule::STYPE_DIRECTDEBIT);
m_paymentMethodEdit->insertItem(i18n("Standing order"), MyMoneySchedule::STYPE_STANDINGORDER);
m_paymentMethodEdit->insertItem(i18n("Bank transfer"), MyMoneySchedule::STYPE_BANKTRANSFER);
m_paymentMethodEdit->insertItem(i18n("Write check"), MyMoneySchedule::STYPE_WRITECHEQUE);
m_paymentMethodEdit->insertItem(i18n("Other"), MyMoneySchedule::STYPE_OTHER);
MyMoneySchedule::paymentTypeE method = d->m_schedule.paymentType();
if(method == MyMoneySchedule::STYPE_ANY)
method = MyMoneySchedule::STYPE_OTHER;
m_paymentMethodEdit->setCurrentItem(method);
switch(d->m_schedule.weekendOption()) {
case MyMoneySchedule::MoveNothing:
m_weekendOptionEdit->setCurrentItem(0);
break;
case MyMoneySchedule::MoveFriday:
m_weekendOptionEdit->setCurrentItem(1);
break;
case MyMoneySchedule::MoveMonday:
m_weekendOptionEdit->setCurrentItem(2);
break;
}
m_estimateEdit->setChecked(!d->m_schedule.isFixed());
m_autoEnterEdit->setChecked(d->m_schedule.autoEnter());
m_endSeriesEdit->setChecked(d->m_schedule.willEnd());
m_endOptionsFrame->setEnabled(d->m_schedule.willEnd());
if(d->m_schedule.willEnd()) {
m_RemainingEdit->setValue(d->m_schedule.transactionsRemaining());
m_FinalPaymentEdit->setDate(d->m_schedule.endDate());
}
connect(m_RemainingEdit, TQT_SIGNAL(valueChanged(int)),
this, TQT_SLOT(slotRemainingChanged(int)));
connect(m_FinalPaymentEdit, TQT_SIGNAL(dateChanged(const TQDate&)),
this, TQT_SLOT(slotEndDateChanged(const TQDate&)));
connect(m_frequencyEdit, TQT_SIGNAL(itemSelected(int)),
this, TQT_SLOT(slotFrequencyChanged(int)));
connect(m_frequencyNoEdit, TQT_SIGNAL(valueChanged(int)),
this, TQT_SLOT(slotOccurenceMultiplierChanged(int)));
connect(buttonHelp, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotShowHelp()));
// force the initial height to be as small as possible
TQTimer::singleShot(0, this, TQT_SLOT(slotSetupSize()));
// we just hide the variation field for now and enable the logic
// once we have a respective member in the MyMoneySchedule object
m_variation->hide();
}
KEditScheduleDlg::~KEditScheduleDlg()
{
delete d;
}
void KEditScheduleDlg::slotSetupSize(void)
{
resize(width(), minimumSizeHint().height());
}
TransactionEditor* KEditScheduleDlg::startEdit(void)
{
KMyMoneyRegister::SelectedTransactions list(m_register);
TransactionEditor* editor = d->m_item->createEditor(m_form, list, TQDate());
// check that we use the same transaction commodity in all selected transactions
// if not, we need to update this in the editor's list. The user can also bail out
// of this operation which means that we have to stop editing here.
if(editor && !d->m_schedule.account().id().isEmpty()) {
if(!editor->fixTransactionCommodity(d->m_schedule.account())) {
// if the user wants to quit, we need to destroy the editor
// and bail out
delete editor;
editor = 0;
}
}
if(editor) {
connect(editor, TQT_SIGNAL(transactionDataSufficient(bool)), buttonOk, TQT_SLOT(setEnabled(bool)));
connect(editor, TQT_SIGNAL(escapePressed()), buttonCancel, TQT_SLOT(animateClick()));
connect(editor, TQT_SIGNAL(returnPressed()), buttonOk, TQT_SLOT(animateClick()));
connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), editor, TQT_SLOT(slotReloadEditWidgets()));
// connect(editor, TQT_SIGNAL(finishEdit(const KMyMoneyRegister::SelectedTransactions&)), this, TQT_SLOT(slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions&)));
connect(editor, TQT_SIGNAL(createPayee(const TQString&, TQString&)), kmymoney2, TQT_SLOT(slotPayeeNew(const TQString&, TQString&)));
connect(editor, TQT_SIGNAL(createCategory(MyMoneyAccount&, const MyMoneyAccount&)), kmymoney2, TQT_SLOT(slotCategoryNew(MyMoneyAccount&, const MyMoneyAccount&)));
connect(editor, TQT_SIGNAL(createSecurity(MyMoneyAccount&, const MyMoneyAccount&)), kmymoney2, TQT_SLOT(slotInvestmentNew(MyMoneyAccount&, const MyMoneyAccount&)));
connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), editor, TQT_SLOT(slotReloadEditWidgets()));
// create the widgets, place them in the parent and load them with data
// setup tab order
d->m_tabOrderWidgets.clear();
KMyMoneyRegister::Action action = KMyMoneyRegister::ActionWithdrawal;
switch(d->m_schedule.type()) {
case MyMoneySchedule::TYPE_DEPOSIT:
action = KMyMoneyRegister::ActionDeposit;
break;
case MyMoneySchedule::TYPE_BILL:
action = KMyMoneyRegister::ActionWithdrawal;
break;
case MyMoneySchedule::TYPE_TRANSFER:
action = KMyMoneyRegister::ActionTransfer;
break;
default:
// if we end up here, we don't have a known schedule type (yet). in this case, we just glimpse
// into the transaction and determine the type. in case we don't have a transaction with splits
// we stick with the default action already set up
if(d->m_schedule.transaction().splits().count() > 0) {
TQValueList<MyMoneySplit>::const_iterator it_s;
bool isDeposit = false;
bool isTransfer = false;
for(it_s = d->m_schedule.transaction().splits().begin(); it_s != d->m_schedule.transaction().splits().end(); ++it_s) {
if((*it_s).accountId() == d->m_schedule.account().id()) {
isDeposit = !((*it_s).shares().isNegative());
} else {
MyMoneyAccount acc = MyMoneyFile::instance()->account((*it_s).accountId());
if(acc.isAssetLiability() && d->m_schedule.transaction().splits().count() == 2) {
isTransfer = true;
}
}
}
if(isTransfer)
action = KMyMoneyRegister::ActionTransfer;
else if(isDeposit)
action = KMyMoneyRegister::ActionDeposit;
}
break;
}
editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action);
// if it's not a check, then we need to clear
// a possibly assigned check number
if(d->m_schedule.paymentType() != MyMoneySchedule::STYPE_WRITECHEQUE) {
TQWidget* w = editor->haveWidget("number");
if(w)
dynamic_cast<kMyMoneyLineEdit*>(w)->loadText(TQString());
}
Q_ASSERT(!d->m_tabOrderWidgets.isEmpty());
// don't forget our three buttons and additional widgets
d->m_tabOrderWidgets.append(m_weekendOptionEdit);
d->m_tabOrderWidgets.append(m_estimateEdit);
d->m_tabOrderWidgets.append(m_variation);
d->m_tabOrderWidgets.append(m_autoEnterEdit);
d->m_tabOrderWidgets.append(m_endSeriesEdit);
d->m_tabOrderWidgets.append(m_RemainingEdit);
d->m_tabOrderWidgets.append(m_FinalPaymentEdit);
d->m_tabOrderWidgets.append(buttonOk);
d->m_tabOrderWidgets.append(buttonCancel);
d->m_tabOrderWidgets.append(buttonHelp);
d->m_tabOrderWidgets.append(m_nameEdit);
d->m_tabOrderWidgets.append(m_frequencyNoEdit);
d->m_tabOrderWidgets.append(m_frequencyEdit);
d->m_tabOrderWidgets.append(m_paymentMethodEdit);
d->m_tabOrderWidgets.append(m_form);
// install event filter in all taborder widgets
TQWidget* w;
for(w = d->m_tabOrderWidgets.first(); w; w = d->m_tabOrderWidgets.next()) {
w->installEventFilter(this);
w->installEventFilter(editor);
}
// connect the postdate modification signal to our update routine
kMyMoneyDateInput* dateEdit = dynamic_cast<kMyMoneyDateInput*>(editor->haveWidget("postdate"));
if(dateEdit)
connect(dateEdit, TQT_SIGNAL(dateChanged(const TQDate&)), this, TQT_SLOT(slotPostDateChanged(const TQDate&)));
m_nameEdit->setFocus();
// add the required fields to the mandatory group
d->m_requiredFields->add(m_nameEdit);
d->m_requiredFields->add(editor->haveWidget("account"));
d->m_requiredFields->add(editor->haveWidget("category"));
// fix labels
TQLabel* label = dynamic_cast<TQLabel*>(editor->haveWidget("date-label"));
if(label) {
label->setText(i18n("Next due date"));
}
d->m_editor = editor;
slotSetPaymentMethod(d->m_schedule.paymentType());
connect(m_paymentMethodEdit, TQT_SIGNAL(itemSelected(int)), this, TQT_SLOT(slotSetPaymentMethod(int)));
}
return editor;
}
void KEditScheduleDlg::accept(void)
{
// Force the focus to be on the OK button. This will trigger creation
// of any unknown objects (payees, categories etc.)
buttonOk->setFocus();
// only accept if the button is really still enabled. We could end
// up here, if the user filled all fields, the focus is on the category
// field, but the category is not yet existant. When the user presses the
// OK button in this context, he will be asked if he wants to create
// the category or not. In case he decides no, we end up here with no
// category filled in, so we don't run through the final acceptance.
if(buttonOk->isEnabled())
KEditScheduleDlgDecl::accept();
}
const MyMoneySchedule& KEditScheduleDlg::schedule(void) const
{
if(d->m_editor) {
MyMoneyTransaction t = transaction();
if(d->m_schedule.nextDueDate() != t.postDate())
d->m_schedule.setNextDueDate(t.postDate());
d->m_schedule.setTransaction(t);
d->m_schedule.setName(m_nameEdit->text());
d->m_schedule.setFixed(!m_estimateEdit->isChecked());
d->m_schedule.setOccurencePeriod(static_cast<MyMoneySchedule::occurenceE>(m_frequencyEdit->currentItem()));
d->m_schedule.setOccurenceMultiplier( m_frequencyNoEdit->value() );
switch(m_weekendOptionEdit->currentItem()) {
case 0:
d->m_schedule.setWeekendOption(MyMoneySchedule::MoveNothing);
break;
case 1:
d->m_schedule.setWeekendOption(MyMoneySchedule::MoveFriday);
break;
case 2:
d->m_schedule.setWeekendOption(MyMoneySchedule::MoveMonday);
break;
}
d->m_schedule.setType(MyMoneySchedule::TYPE_BILL);
KMyMoneyTransactionForm::TabBar* tabbar = dynamic_cast<KMyMoneyTransactionForm::TabBar*>(d->m_editor->haveWidget("tabbar"));
if(tabbar) {
switch(static_cast<KMyMoneyRegister::Action>(tabbar->currentTab())) {
case KMyMoneyRegister::ActionDeposit:
d->m_schedule.setType(MyMoneySchedule::TYPE_DEPOSIT);
break;
default:
case KMyMoneyRegister::ActionWithdrawal:
d->m_schedule.setType(MyMoneySchedule::TYPE_BILL);
break;
case KMyMoneyRegister::ActionTransfer:
d->m_schedule.setType(MyMoneySchedule::TYPE_TRANSFER);
break;
}
} else {
qDebug("No tabbar found in KEditScheduleDlg::schedule(). Defaulting type to BILL");
}
d->m_schedule.setAutoEnter(m_autoEnterEdit->isChecked());
d->m_schedule.setPaymentType(static_cast<MyMoneySchedule::paymentTypeE>(m_paymentMethodEdit->currentItem()));
if(m_endSeriesEdit->isEnabled() && m_endSeriesEdit->isChecked()) {
d->m_schedule.setEndDate(m_FinalPaymentEdit->date());
} else {
d->m_schedule.setEndDate(TQDate());
}
}
return d->m_schedule;
}
MyMoneyTransaction KEditScheduleDlg::transaction(void) const
{
MyMoneyTransaction t = d->m_schedule.transaction();
if(d->m_editor) {
d->m_editor->createTransaction(t, d->m_schedule.transaction(), d->m_schedule.transaction().splits()[0], false);
}
t.clearId();
t.setEntryDate(TQDate());
return t;
}
bool KEditScheduleDlg::focusNextPrevChild(bool next)
{
bool rc = false;
// qDebug("KEditScheduleDlg::focusNextPrevChild(editmode=%s)", m_inEditMode ? "true" : "false");
TQWidget *w = 0;
TQWidget *currentWidget;
w = tqApp->focusWidget();
while(w && d->m_tabOrderWidgets.find(w) == -1) {
// qDebug("'%s' not in list, use parent", w->className());
w = w->parentWidget();
}
// if(w) qDebug("tab order is at '%s'", w->className());
currentWidget = d->m_tabOrderWidgets.current();
w = next ? d->m_tabOrderWidgets.next() : d->m_tabOrderWidgets.prev();
do {
if(!w) {
w = next ? d->m_tabOrderWidgets.first() : d->m_tabOrderWidgets.last();
}
if(w != currentWidget
&& ((w->focusPolicy() & TQ_TabFocus) == TQ_TabFocus)
&& w->isVisible() && w->isEnabled()) {
// qDebug("Selecting '%s' as focus", w->className());
w->setFocus();
rc = true;
break;
}
w = next ? d->m_tabOrderWidgets.next() : d->m_tabOrderWidgets.prev();
} while(w != currentWidget);
return rc;
}
void KEditScheduleDlg::resizeEvent(TQResizeEvent* ev)
{
m_register->resize(KMyMoneyRegister::DetailColumn);
m_form->resize(KMyMoneyTransactionForm::ValueColumn1);
KEditScheduleDlgDecl::resizeEvent(ev);
}
void KEditScheduleDlg::slotRemainingChanged(int value)
{
// Make sure the required fields are set
kMyMoneyDateInput* dateEdit = dynamic_cast<kMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"));
d->m_schedule.setNextDueDate(dateEdit->date());
d->m_schedule.setOccurencePeriod(static_cast<MyMoneySchedule::occurenceE>(m_frequencyEdit->currentItem()));
d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value());
if(d->m_schedule.transactionsRemaining() != value) {
m_FinalPaymentEdit->blockSignals(true);
m_FinalPaymentEdit->setDate(d->m_schedule.dateAfter(value));
m_FinalPaymentEdit->blockSignals(false);
}
}
void KEditScheduleDlg::slotEndDateChanged(const TQDate& date)
{
// Make sure the required fields are set
kMyMoneyDateInput* dateEdit = dynamic_cast<kMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"));
d->m_schedule.setNextDueDate(dateEdit->date());
d->m_schedule.setOccurencePeriod(static_cast<MyMoneySchedule::occurenceE>(m_frequencyEdit->currentItem()));
d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value());
if(d->m_schedule.endDate() != date) {
d->m_schedule.setEndDate(date);
updateTransactionsRemaining();
}
}
void KEditScheduleDlg::slotPostDateChanged(const TQDate& date)
{
if(d->m_schedule.nextDueDate() != date) {
if (m_endOptionsFrame->isEnabled()) {
d->m_schedule.setNextDueDate(date);
d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value());
d->m_schedule.setOccurencePeriod(static_cast<MyMoneySchedule::occurenceE>(m_frequencyEdit->currentItem()));
d->m_schedule.setEndDate(m_FinalPaymentEdit->date());
updateTransactionsRemaining();
}
}
}
void KEditScheduleDlg::slotSetPaymentMethod(int item)
{
kMyMoneyLineEdit* dateEdit = dynamic_cast<kMyMoneyLineEdit*>(d->m_editor->haveWidget("number"));
if(dateEdit) {
dateEdit->setShown(item == MyMoneySchedule::STYPE_WRITECHEQUE);
// hiding the label does not work, because the label underneath will shine
// through. So we either write the label or a blank
TQLabel* label = dynamic_cast<TQLabel *>(d->m_editor->haveWidget("number-label"));
if(label) {
label->setText((item == MyMoneySchedule::STYPE_WRITECHEQUE) ? i18n("Number") : " ");
}
}
}
void KEditScheduleDlg::slotFrequencyChanged(int item)
{
m_endSeriesEdit->setEnabled(item != MyMoneySchedule::OCCUR_ONCE);
bool isEndSeries = m_endSeriesEdit->isChecked();
if(isEndSeries )
m_endOptionsFrame->setEnabled(item != MyMoneySchedule::OCCUR_ONCE);
switch( item )
{
case MyMoneySchedule::OCCUR_DAILY:
case MyMoneySchedule::OCCUR_WEEKLY:
case MyMoneySchedule::OCCUR_EVERYHALFMONTH:
case MyMoneySchedule::OCCUR_MONTHLY:
case MyMoneySchedule::OCCUR_YEARLY:
// Supports Frequency Number
m_frequencyNoEdit->setEnabled(true);
break;
default:
// Multiplier is always 1
m_frequencyNoEdit->setEnabled(false);
m_frequencyNoEdit->setValue(1);
break;
}
if ( isEndSeries && ( item != MyMoneySchedule::OCCUR_ONCE ) )
{
// Changing the frequency changes the number
// of remaining transactions
kMyMoneyDateInput* dateEdit = dynamic_cast<kMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"));
d->m_schedule.setNextDueDate(dateEdit->date());
d->m_schedule.setOccurenceMultiplier(m_frequencyNoEdit->value());
d->m_schedule.setOccurencePeriod(static_cast<MyMoneySchedule::occurenceE>(item));
d->m_schedule.setEndDate(m_FinalPaymentEdit->date());
updateTransactionsRemaining();
}
}
void KEditScheduleDlg::slotOccurenceMultiplierChanged(int multiplier)
{
// Make sure the required fields are set
int oldOccurenceMultiplier = d->m_schedule.occurenceMultiplier();
if ( multiplier != oldOccurenceMultiplier )
{
if (m_endOptionsFrame->isEnabled())
{
kMyMoneyDateInput* dateEdit = dynamic_cast<kMyMoneyDateInput*>(d->m_editor->haveWidget("postdate"));
d->m_schedule.setNextDueDate(dateEdit->date());
d->m_schedule.setOccurenceMultiplier(multiplier);
d->m_schedule.setOccurencePeriod(static_cast<MyMoneySchedule::occurenceE>(m_frequencyEdit->currentItem()));
d->m_schedule.setEndDate(m_FinalPaymentEdit->date());
updateTransactionsRemaining();
}
}
}
void KEditScheduleDlg::updateTransactionsRemaining(void)
{
int remain = d->m_schedule.transactionsRemaining();
if ( remain != m_RemainingEdit->value() )
{
m_RemainingEdit->blockSignals(true);
m_RemainingEdit->setValue(remain);
m_RemainingEdit->blockSignals(false);
}
}
void KEditScheduleDlg::slotShowHelp(void)
{
kapp->invokeHelp("details.schedules.intro");
}
#include <keditscheduledlg.moc>