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.
1462 lines
52 KiB
1462 lines
52 KiB
/***************************************************************************
|
|
kgloballedgerview.cpp - description
|
|
-------------------
|
|
begin : Wed Jul 26 2006
|
|
copyright : (C) 2006 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. *
|
|
* *
|
|
***************************************************************************/
|
|
|
|
#include <typeinfo>
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// QT Includes
|
|
|
|
#include <tqframe.h>
|
|
#include <tqlayout.h>
|
|
#include <tqtimer.h>
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// KDE Includes
|
|
|
|
#include "kdecompat.h"
|
|
#include <klocale.h>
|
|
#include <kcombobox.h>
|
|
#include <kpushbutton.h>
|
|
#include <kmessagebox.h>
|
|
#include <kiconloader.h>
|
|
#include <ktoolbar.h>
|
|
#include <ktoolbarbutton.h>
|
|
#include <kpassivepopup.h>
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Project Includes
|
|
|
|
#include "kgloballedgerview.h"
|
|
|
|
#include <kmymoney/mymoneyaccount.h>
|
|
#include <kmymoney/mymoneyfile.h>
|
|
#include <kmymoney/kmymoneyaccountcombo.h>
|
|
#include <kmymoney/kmymoneytitlelabel.h>
|
|
#include <kmymoney/register.h>
|
|
#include <kmymoney/transactioneditor.h>
|
|
#include <kmymoney/selectedtransaction.h>
|
|
|
|
#include <kmymoney/kmymoneyglobalsettings.h>
|
|
|
|
#include "../widgets/registersearchline.h"
|
|
#include "../dialogs/ksortoptiondlg.h"
|
|
#include "../kmymoney2.h"
|
|
|
|
#include "../widgets/scheduledtransaction.h"
|
|
|
|
class KGlobalLedgerView::Private
|
|
{
|
|
public:
|
|
Private();
|
|
|
|
MousePressFilter* m_mousePressFilter;
|
|
KMyMoneyRegister::RegisterSearchLineWidget* m_registerSearchLine;
|
|
TQPoint m_startPoint;
|
|
TQString m_reconciliationAccount;
|
|
TQDate m_reconciliationDate;
|
|
MyMoneyMoney m_endingBalance;
|
|
int m_precision;
|
|
bool m_inLoading;
|
|
bool m_recursion;
|
|
bool m_showDetails;
|
|
KMyMoneyRegister::Action m_action;
|
|
TQTimer m_viewPosTimer;
|
|
};
|
|
|
|
MousePressFilter::MousePressFilter(TQWidget* parent, const char* name) :
|
|
TQObject(parent, name),
|
|
m_lastMousePressEvent(0),
|
|
m_filterActive(true)
|
|
{
|
|
}
|
|
|
|
void MousePressFilter::addWidget(TQWidget* w)
|
|
{
|
|
m_parents.append(w);
|
|
}
|
|
|
|
void MousePressFilter::setFilterActive(bool state)
|
|
{
|
|
m_filterActive = state;
|
|
}
|
|
|
|
bool MousePressFilter::isChildOf( TQWidget* child, TQWidget *parent )
|
|
{
|
|
while(child) {
|
|
if(child == parent)
|
|
return true;
|
|
// If one of the ancestors is a KPassivePopup then it's as
|
|
// if it is a child of our own
|
|
if(dynamic_cast<KPassivePopup*>(child))
|
|
return true;
|
|
child = child->parentWidget();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool MousePressFilter::eventFilter(TQObject* o, TQEvent* e)
|
|
{
|
|
if(m_filterActive) {
|
|
if(e->type() == TQEvent::MouseButtonPress && !m_lastMousePressEvent) {
|
|
TQValueList<TQWidget*>::const_iterator it_w;
|
|
for(it_w = m_parents.begin(); it_w != m_parents.end(); ++it_w) {
|
|
if(isChildOf((TQWidget*)o, (*it_w))) {
|
|
m_lastMousePressEvent = e;
|
|
break;
|
|
}
|
|
}
|
|
if(it_w == m_parents.end()) {
|
|
m_lastMousePressEvent = e;
|
|
bool rc = false;
|
|
emit mousePressedOnExternalWidget(rc);
|
|
}
|
|
}
|
|
|
|
if(e->type() != TQEvent::MouseButtonPress) {
|
|
m_lastMousePressEvent = 0;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
KGlobalLedgerView::Private::Private() :
|
|
m_mousePressFilter(0),
|
|
m_registerSearchLine(0),
|
|
m_inLoading(false),
|
|
m_recursion(false),
|
|
m_showDetails(false)
|
|
{
|
|
}
|
|
|
|
TQDate KGlobalLedgerView::m_lastPostDate;
|
|
|
|
KGlobalLedgerView::KGlobalLedgerView(TQWidget *parent, const char *name )
|
|
: KMyMoneyViewBase(parent, name, i18n("Ledgers")),
|
|
d(new Private),
|
|
m_needReload(false),
|
|
m_newAccountLoaded(true),
|
|
m_inEditMode(false)
|
|
{
|
|
d->m_mousePressFilter = new MousePressFilter((TQWidget*)this);
|
|
d->m_action = KMyMoneyRegister::ActionNone;;
|
|
|
|
// create the toolbar frame at the top of the view
|
|
m_toolbarFrame = new TQFrame(this);
|
|
TQVBoxLayout* toolbarLayout = new TQVBoxLayout(m_toolbarFrame, 0, 0);
|
|
|
|
m_toolbar = new KToolBar(m_toolbarFrame, 0, true);
|
|
toolbarLayout->addWidget(m_toolbar);
|
|
m_toolbar->setIconText(KToolBar::IconTextRight);
|
|
|
|
m_accountComboBox = new KMyMoneyAccountCombo(m_toolbar, "AccountCombo");
|
|
m_toolbar->insertWidget(1, 100, m_accountComboBox);
|
|
|
|
#if 0
|
|
// the account button at the right of the toolbar
|
|
// I leave the code commented here for a while, so that I see
|
|
// how I can add other widgets at this point
|
|
KIconLoader *il = KGlobal::iconLoader();
|
|
m_toolbar->insertButton(il->loadIcon("document", KIcon::Small, KIcon::SizeSmall),
|
|
1,true,i18n("Account"));
|
|
//m_toolbar->setMaximumSize(50,20);
|
|
m_toolbar->alignItemRight(1);
|
|
#endif
|
|
m_toolbar->tqsetSizePolicy(TQSizePolicy::Minimum, TQSizePolicy::Fixed);
|
|
tqlayout()->addWidget(m_toolbarFrame);
|
|
|
|
// create the register frame
|
|
m_registerFrame = new TQFrame(this);
|
|
TQVBoxLayout* registerFrameLayout = new TQVBoxLayout(m_registerFrame, 0, 0);
|
|
tqlayout()->addWidget(m_registerFrame);
|
|
tqlayout()->setStretchFactor(m_registerFrame, 2);
|
|
m_register = new KMyMoneyRegister::Register(m_registerFrame);
|
|
registerFrameLayout->addWidget(m_register);
|
|
m_register->installEventFilter(this);
|
|
connect(m_register, TQT_SIGNAL(openContextMenu()), this, TQT_SIGNAL(openContextMenu()));
|
|
connect(m_register, TQT_SIGNAL(headerClicked()), this, TQT_SLOT(slotSortOptions()));
|
|
connect(m_register, TQT_SIGNAL(reconcileStateColumnClicked(KMyMoneyRegister::Transaction*)), this, TQT_SLOT(slotToggleTransactionMark(KMyMoneyRegister::Transaction*)));
|
|
connect(&d->m_viewPosTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotUpdateViewPos()));
|
|
|
|
// insert search line widget
|
|
|
|
d->m_registerSearchLine = new KMyMoneyRegister::RegisterSearchLineWidget(m_register, m_toolbar);
|
|
m_toolbar->setStretchableWidget(d->m_registerSearchLine);
|
|
|
|
// create the summary frame
|
|
m_summaryFrame = new TQFrame(this);
|
|
TQHBoxLayout* summaryFrameLayout = new TQHBoxLayout(m_summaryFrame, 0, 0);
|
|
m_leftSummaryLabel = new TQLabel(m_summaryFrame);
|
|
m_centerSummaryLabel = new TQLabel(m_summaryFrame);
|
|
m_rightSummaryLabel = new TQLabel(m_summaryFrame);
|
|
summaryFrameLayout->addWidget(m_leftSummaryLabel);
|
|
TQSpacerItem* spacer = new TQSpacerItem( 20, 1, TQSizePolicy::Expanding, TQSizePolicy::Minimum );
|
|
summaryFrameLayout->addItem(spacer);
|
|
summaryFrameLayout->addWidget(m_centerSummaryLabel);
|
|
spacer = new TQSpacerItem( 20, 1, TQSizePolicy::Expanding, TQSizePolicy::Minimum );
|
|
summaryFrameLayout->addItem(spacer);
|
|
summaryFrameLayout->addWidget(m_rightSummaryLabel);
|
|
tqlayout()->addWidget(m_summaryFrame);
|
|
|
|
// create the button frame
|
|
m_buttonFrame = new TQFrame(this);
|
|
TQVBoxLayout* buttonLayout = new TQVBoxLayout(m_buttonFrame, 0, 0);
|
|
tqlayout()->addWidget(m_buttonFrame);
|
|
m_buttonbar = new KToolBar(m_buttonFrame, 0, true);
|
|
m_buttonbar->setIconText(KToolBar::IconTextRight);
|
|
buttonLayout->addWidget(m_buttonbar);
|
|
|
|
kmymoney2->action("transaction_new")->plug(m_buttonbar);
|
|
kmymoney2->action("transaction_delete")->plug(m_buttonbar);
|
|
kmymoney2->action("transaction_edit")->plug(m_buttonbar);
|
|
kmymoney2->action("transaction_enter")->plug(m_buttonbar);
|
|
kmymoney2->action("transaction_cancel")->plug(m_buttonbar);
|
|
kmymoney2->action("transaction_accept")->plug(m_buttonbar);
|
|
kmymoney2->action("transaction_match")->plug(m_buttonbar);
|
|
|
|
// create the transaction form frame
|
|
m_formFrame = new TQFrame(this);
|
|
TQVBoxLayout* frameLayout = new TQVBoxLayout(m_formFrame, 5, 0);
|
|
m_form = new KMyMoneyTransactionForm::TransactionForm(m_formFrame);
|
|
frameLayout->addWidget(m_form->tabBar(m_formFrame));
|
|
frameLayout->addWidget(m_form);
|
|
m_formFrame->setFrameShape( TQFrame::Panel );
|
|
m_formFrame->setFrameShadow( TQFrame::Raised );
|
|
tqlayout()->addWidget(m_formFrame);
|
|
|
|
connect(MyMoneyFile::instance(), TQT_SIGNAL(dataChanged()), this, TQT_SLOT(slotLoadView()));
|
|
connect(m_register, TQT_SIGNAL(focusChanged(KMyMoneyRegister::Transaction*)), m_form, TQT_SLOT(slotSetTransaction(KMyMoneyRegister::Transaction*)));
|
|
connect(m_register, TQT_SIGNAL(focusChanged()), kmymoney2, TQT_SLOT(slotUpdateActions()));
|
|
connect(m_accountComboBox, TQT_SIGNAL(accountSelected(const TQString&)), this, TQT_SLOT(slotSelectAccount(const TQString&)));
|
|
connect(m_register, TQT_SIGNAL(selectionChanged(const KMyMoneyRegister::SelectedTransactions&)), this, TQT_SIGNAL(transactionsSelected(const KMyMoneyRegister::SelectedTransactions&)));
|
|
connect(m_register, TQT_SIGNAL(editTransaction()), this, TQT_SIGNAL(startEdit()));
|
|
connect(m_register, TQT_SIGNAL(emptyItemSelected()), this, TQT_SLOT(slotNewTransaction()));
|
|
connect(m_register, TQT_SIGNAL(aboutToSelectItem(KMyMoneyRegister::RegisterItem*, bool&)), this, TQT_SLOT(slotAboutToSelectItem(KMyMoneyRegister::RegisterItem*, bool&)));
|
|
connect(d->m_mousePressFilter, TQT_SIGNAL(mousePressedOnExternalWidget(bool&)), this, TQT_SIGNAL(cancelOrEndEdit(bool&)));
|
|
|
|
connect(m_form, TQT_SIGNAL(newTransaction(KMyMoneyRegister::Action)), this, TQT_SLOT(slotNewTransaction(KMyMoneyRegister::Action)));
|
|
|
|
// setup mouse press filter
|
|
d->m_mousePressFilter->addWidget(m_formFrame);
|
|
d->m_mousePressFilter->addWidget(m_buttonFrame);
|
|
d->m_mousePressFilter->addWidget(m_summaryFrame);
|
|
d->m_mousePressFilter->addWidget(m_registerFrame);
|
|
}
|
|
|
|
KGlobalLedgerView::~KGlobalLedgerView()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void KGlobalLedgerView::slotAboutToSelectItem(KMyMoneyRegister::RegisterItem* item, bool& okToSelect)
|
|
{
|
|
Q_UNUSED(item);
|
|
emit cancelOrEndEdit(okToSelect);
|
|
}
|
|
|
|
void KGlobalLedgerView::slotLoadView(void)
|
|
{
|
|
m_needReload = true;
|
|
if(isVisible()) {
|
|
if(!m_inEditMode) {
|
|
loadView();
|
|
m_needReload = false;
|
|
// force a new account if the current one is empty
|
|
m_newAccountLoaded = m_account.id().isEmpty();
|
|
}
|
|
}
|
|
}
|
|
|
|
void KGlobalLedgerView::clear(void)
|
|
{
|
|
// clear current register contents
|
|
m_register->clear();
|
|
|
|
// setup header font
|
|
TQFont font = KMyMoneyGlobalSettings::listHeaderFont();
|
|
TQFontMetrics fm( font );
|
|
int height = fm.lineSpacing()+6;
|
|
m_register->horizontalHeader()->setMinimumHeight(height);
|
|
m_register->horizontalHeader()->setMaximumHeight(height);
|
|
m_register->horizontalHeader()->setFont(font);
|
|
|
|
// setup cell font
|
|
font = KMyMoneyGlobalSettings::listCellFont();
|
|
m_register->setFont(font);
|
|
|
|
// clear the form
|
|
m_form->clear();
|
|
|
|
// the selected transactions list
|
|
m_transactionList.clear();
|
|
|
|
// and the selected account in the combo box
|
|
m_accountComboBox->setSelected(TQString());
|
|
|
|
// fraction defaults to two digits
|
|
d->m_precision = 2;
|
|
}
|
|
|
|
void KGlobalLedgerView::loadView(void)
|
|
{
|
|
MYMONEYTRACER(tracer);
|
|
|
|
// setup form visibility
|
|
m_formFrame->setShown(KMyMoneyGlobalSettings::transactionForm());
|
|
|
|
// no account selected
|
|
emit accountSelected(MyMoneyAccount());
|
|
// no transaction selected
|
|
KMyMoneyRegister::SelectedTransactions list;
|
|
emit transactionsSelected(list);
|
|
|
|
TQMap<TQString, bool> isSelected;
|
|
TQString focusItemId;
|
|
TQString anchorItemId;
|
|
|
|
if(!d->m_inLoading)
|
|
d->m_startPoint = TQPoint(-1, -1);
|
|
|
|
if(!m_newAccountLoaded) {
|
|
// remember the current selected transactions
|
|
KMyMoneyRegister::RegisterItem* item = m_register->firstItem();
|
|
for(; item; item = item->nextItem()) {
|
|
if(item->isSelected()) {
|
|
isSelected[item->id()] = true;
|
|
}
|
|
}
|
|
// remember the item that has the focus
|
|
if(m_register->focusItem())
|
|
focusItemId = m_register->focusItem()->id();
|
|
// and the one that has the selection anchor
|
|
if(m_register->anchorItem())
|
|
anchorItemId = m_register->anchorItem()->id();
|
|
|
|
// remember the upper left corner of the viewport
|
|
if(!d->m_inLoading && d->m_showDetails == KMyMoneyGlobalSettings::showRegisterDetailed())
|
|
d->m_startPoint = TQPoint(m_register->contentsX(), m_register->contentsY());
|
|
} else {
|
|
if(d->m_viewPosTimer.isActive())
|
|
d->m_viewPosTimer.stop();
|
|
d->m_startPoint = TQPoint(-1, -1);
|
|
d->m_inLoading = false;
|
|
d->m_registerSearchLine->searchLine()->reset();
|
|
}
|
|
|
|
// clear the current contents ...
|
|
clear();
|
|
|
|
// ... load the combobox widget and select current account ...
|
|
loadAccounts();
|
|
|
|
// ... setup the register columns ...
|
|
m_register->setupRegister(m_account);
|
|
|
|
// ... setup the form ...
|
|
m_form->setupForm(m_account);
|
|
|
|
if(m_account.id().isEmpty()) {
|
|
// if we don't have an account we bail out
|
|
setEnabled(false);
|
|
return;
|
|
}
|
|
setEnabled(true);
|
|
|
|
m_register->setUpdatesEnabled(false);
|
|
|
|
// ... and recreate it
|
|
KMyMoneyRegister::RegisterItem* focusItem = 0;
|
|
KMyMoneyRegister::RegisterItem* anchorItem = 0;
|
|
TQMap<TQString, MyMoneyMoney> actBalance, clearedBalance, futureBalance;
|
|
TQMap<TQString, MyMoneyMoney>::iterator it_b;
|
|
try {
|
|
// setup the filter to select the transactions we want to display
|
|
// and update the sort order
|
|
TQString sortOrder;
|
|
TQString key;
|
|
TQDate reconciliationDate = d->m_reconciliationDate;
|
|
|
|
MyMoneyTransactionFilter filter(m_account.id());
|
|
// if it's an investment account, we also take care of
|
|
// the sub-accounts (stock accounts)
|
|
if(m_account.accountType() == MyMoneyAccount::Investment)
|
|
filter.addAccount(m_account.accountList());
|
|
|
|
if(isReconciliationAccount()) {
|
|
key = "kmm-sort-reconcile";
|
|
sortOrder = KMyMoneyGlobalSettings::sortReconcileView();
|
|
filter.addState(MyMoneyTransactionFilter::notReconciled);
|
|
filter.addState(MyMoneyTransactionFilter::cleared);
|
|
} else {
|
|
filter.setDateFilter(KMyMoneyGlobalSettings::startDate().date(), TQDate());
|
|
key = "kmm-sort-std";
|
|
sortOrder = KMyMoneyGlobalSettings::sortNormalView();
|
|
if (KMyMoneyGlobalSettings::hideReconciledTransactions()
|
|
&& !m_account.isIncomeExpense()) {
|
|
filter.addState(MyMoneyTransactionFilter::notReconciled);
|
|
filter.addState(MyMoneyTransactionFilter::cleared);
|
|
}
|
|
}
|
|
filter.setReportAllSplits(true);
|
|
|
|
// check if we have an account override of the sort order
|
|
if(!m_account.value(key).isEmpty())
|
|
sortOrder = m_account.value(key);
|
|
|
|
// setup sort order
|
|
m_register->setSortOrder(sortOrder);
|
|
|
|
// retrieve the list from the engine
|
|
MyMoneyFile::instance()->transactionList(m_transactionList, filter);
|
|
|
|
kmymoney2->slotStatusProgressBar(0, m_transactionList.count());
|
|
|
|
// create the elements for the register
|
|
TQValueList<TQPair<MyMoneyTransaction, MyMoneySplit> >::const_iterator it;
|
|
TQMap<TQString, int>uniqueMap;
|
|
int i = 0;
|
|
for(it = m_transactionList.begin(); it != m_transactionList.end(); ++it) {
|
|
uniqueMap[(*it).first.id()]++;
|
|
KMyMoneyRegister::Transaction* t = KMyMoneyRegister::Register::transactionFactory(m_register, (*it).first, (*it).second, uniqueMap[(*it).first.id()]);
|
|
actBalance[t->split().accountId()] = MyMoneyMoney(0,1);
|
|
kmymoney2->slotStatusProgressBar(++i, 0);
|
|
// if we're in reconciliation and the state is cleared, we
|
|
// force the item to show in dimmed intensity to get a visual focus
|
|
// on those items, that we need to work on
|
|
if(isReconciliationAccount() && (*it).second.reconcileFlag() == MyMoneySplit::Cleared) {
|
|
t->setReducedIntensity(true);
|
|
}
|
|
}
|
|
|
|
// create dummy entries for the scheduled transactions if sorted by postdate
|
|
int period = KMyMoneyGlobalSettings::schedulePreview();
|
|
if(m_register->primarySortKey() == KMyMoneyRegister::PostDateSort) {
|
|
// show scheduled transactions which have a scheduled postdate
|
|
// within the next 'period' days. In reconciliation mode, the
|
|
// period starts on the statement date.
|
|
TQDate endDate = TQDate::tqcurrentDate().addDays(period);
|
|
if(isReconciliationAccount())
|
|
endDate = reconciliationDate.addDays(period);
|
|
TQValueList<MyMoneySchedule> scheduleList = MyMoneyFile::instance()->scheduleList(m_account.id());
|
|
while(scheduleList.count() > 0){
|
|
MyMoneySchedule& s = scheduleList.first();
|
|
for(;;) {
|
|
if(s.isFinished() || s.adjustedNextDueDate() > endDate) {
|
|
break;
|
|
}
|
|
|
|
MyMoneyTransaction t(s.id(), KMyMoneyUtils::scheduledTransaction(s));
|
|
// if the transaction is scheduled and overdue, it can't
|
|
// certainly be posted in the past. So we take todays date
|
|
// as the alternative
|
|
if(s.isOverdue())
|
|
t.setPostDate(TQDate::tqcurrentDate());
|
|
else
|
|
t.setPostDate(s.adjustedNextDueDate());
|
|
const TQValueList<MyMoneySplit>& splits = t.splits();
|
|
TQValueList<MyMoneySplit>::const_iterator it_s;
|
|
for(it_s = splits.begin(); it_s != splits.end(); ++it_s) {
|
|
if((*it_s).accountId() == m_account.id()) {
|
|
new KMyMoneyRegister::StdTransactionScheduled(m_register, t, *it_s, uniqueMap[t.id()]);
|
|
}
|
|
}
|
|
// keep track of this payment locally (not in the engine)
|
|
if(s.isOverdue())
|
|
s.setLastPayment(TQDate::tqcurrentDate());
|
|
else
|
|
s.setLastPayment(s.nextDueDate());
|
|
|
|
// if this is a one time schedule, we can bail out here as we're done
|
|
if(s.occurence() == MyMoneySchedule::OCCUR_ONCE)
|
|
break;
|
|
|
|
// for all others, we check if the next payment date is still 'in range'
|
|
s.setNextDueDate(s.nextPayment(s.nextDueDate()));
|
|
}
|
|
scheduleList.pop_front();
|
|
}
|
|
}
|
|
|
|
// add the group markers
|
|
m_register->addGroupMarkers();
|
|
|
|
// sort the transactions according to the sort setting
|
|
m_register->sortItems();
|
|
|
|
// remove trailing and adjacent markers
|
|
m_register->removeUnwantedGroupMarkers();
|
|
|
|
// add special markers for reconciliation now so that they do not get
|
|
// removed by m_register->removeUnwantedGroupMarkers(). Needs resorting
|
|
// of items but that's ok.
|
|
|
|
KMyMoneyRegister::StatementGroupMarker* statement = 0;
|
|
KMyMoneyRegister::StatementGroupMarker* dStatement = 0;
|
|
KMyMoneyRegister::StatementGroupMarker* pStatement = 0;
|
|
|
|
if(isReconciliationAccount()) {
|
|
switch(m_register->primarySortKey()) {
|
|
case KMyMoneyRegister::PostDateSort:
|
|
statement = new KMyMoneyRegister::StatementGroupMarker(m_register, KMyMoneyRegister::Deposit, reconciliationDate, i18n("Statement Details"));
|
|
m_register->sortItems();
|
|
break;
|
|
case KMyMoneyRegister::TypeSort:
|
|
dStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, KMyMoneyRegister::Deposit, reconciliationDate, i18n("Statement Deposit Details"));
|
|
pStatement = new KMyMoneyRegister::StatementGroupMarker(m_register, KMyMoneyRegister::Payment, reconciliationDate, i18n("Statement Payment Details"));
|
|
m_register->sortItems();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// we need at least the balance for the account we currently show
|
|
actBalance[m_account.id()] = MyMoneyMoney();
|
|
|
|
if(m_account.accountType() == MyMoneyAccount::Investment) {
|
|
TQValueList<TQString>::const_iterator it_a;
|
|
for(it_a = m_account.accountList().begin(); it_a != m_account.accountList().end(); ++it_a) {
|
|
actBalance[*it_a] = MyMoneyMoney();
|
|
}
|
|
}
|
|
|
|
// determine balances (actual, cleared). We do this by getting the actual
|
|
// balance of all entered transactions from the engine and walk the list
|
|
// of transactions backward. Also re-select a transaction if it was
|
|
// selected before and setup the focus item.
|
|
|
|
MyMoneyMoney factor(1,1);
|
|
if(m_account.accountGroup() == MyMoneyAccount::Liability
|
|
|| m_account.accountGroup() == MyMoneyAccount::Equity)
|
|
factor = -factor;
|
|
|
|
TQMap<TQString, int> deposits;
|
|
TQMap<TQString, int> payments;
|
|
TQMap<TQString, MyMoneyMoney> depositAmount;
|
|
TQMap<TQString, MyMoneyMoney> paymentAmount;
|
|
for(it_b = actBalance.begin(); it_b != actBalance.end(); ++it_b) {
|
|
MyMoneyMoney balance = MyMoneyFile::instance()->balance(it_b.key());
|
|
balance = balance * factor;
|
|
clearedBalance[it_b.key()] =
|
|
futureBalance[it_b.key()] =
|
|
(*it_b) = balance;
|
|
deposits[it_b.key()] = payments[it_b.key()] = 0;
|
|
depositAmount[it_b.key()] = MyMoneyMoney();
|
|
paymentAmount[it_b.key()] = MyMoneyMoney();
|
|
}
|
|
|
|
tracer.printf("total balance of %s = %s", m_account.name().data(), actBalance[m_account.id()].formatMoney("", 2).data());
|
|
tracer.printf("future balance of %s = %s", m_account.name().data(), futureBalance[m_account.id()].formatMoney("", 2).data());
|
|
tracer.printf("cleared balance of %s = %s", m_account.name().data(), clearedBalance[m_account.id()].formatMoney("", 2).data());
|
|
|
|
KMyMoneyRegister::RegisterItem* p = m_register->lastItem();
|
|
focusItem = 0;
|
|
|
|
// take care of possibly trailing scheduled transactions (bump up the future balance)
|
|
while(p) {
|
|
if(p->isSelectable()) {
|
|
KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
|
|
if(t && t->isScheduled()) {
|
|
MyMoneyMoney balance = futureBalance[t->split().accountId()];
|
|
const MyMoneySplit& split = t->split();
|
|
// if this split is a stock split, we can't just add the amount of shares
|
|
if(t->transaction().isStockSplit()) {
|
|
balance = balance * split.shares();
|
|
} else {
|
|
balance += split.shares() * factor;
|
|
}
|
|
futureBalance[split.accountId()] = balance;
|
|
} else if(t && !focusItem)
|
|
focusItem = p;
|
|
}
|
|
p = p->prevItem();
|
|
}
|
|
|
|
p = m_register->lastItem();
|
|
while(p) {
|
|
KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
|
|
if(t) {
|
|
if(isSelected.contains(t->id()))
|
|
t->setSelected(true);
|
|
|
|
if(t->id() == focusItemId)
|
|
focusItem = t;
|
|
if(t->id() == anchorItemId)
|
|
anchorItem = t;
|
|
|
|
const MyMoneySplit& split = t->split();
|
|
MyMoneyMoney balance = futureBalance[split.accountId()];
|
|
t->setBalance(balance);
|
|
|
|
// if this split is a stock split, we can't just add the amount of shares
|
|
if(t->transaction().isStockSplit()) {
|
|
balance /= split.shares();
|
|
} else {
|
|
balance -= split.shares() * factor;
|
|
}
|
|
|
|
if(!t->isScheduled()) {
|
|
if(split.reconcileFlag() == MyMoneySplit::NotReconciled) {
|
|
tracer.printf("Reducing cleared balance by %s because %s/%s(%s) is not reconciled", (split.shares() * factor).formatMoney("", 2).data(), t->transaction().id().data(), split.id().data(), t->transaction().postDate().toString(Qt::ISODate).data());
|
|
clearedBalance[split.accountId()] -= split.shares() * factor;
|
|
}
|
|
if(isReconciliationAccount() && t->transaction().postDate() > reconciliationDate && split.reconcileFlag() == MyMoneySplit::Cleared) {
|
|
tracer.printf("Reducing cleared balance by %s because we are in reconciliation, %s/%s(%s)'s date is after or on reconciliation date (%s) and is cleared", (split.shares() * factor).formatMoney("", 2).data(), t->transaction().id().data(), split.id().data(), t->transaction().postDate().toString(Qt::ISODate).data(), reconciliationDate.toString(Qt::ISODate).data());
|
|
|
|
clearedBalance[split.accountId()] -= split.shares() * factor;
|
|
}
|
|
if(isReconciliationAccount() && t->transaction().postDate() <= reconciliationDate && split.reconcileFlag() == MyMoneySplit::Cleared) {
|
|
if(split.shares().isNegative()) {
|
|
payments[split.accountId()]++;
|
|
paymentAmount[split.accountId()] += split.shares();
|
|
} else {
|
|
deposits[split.accountId()]++;
|
|
depositAmount[split.accountId()] += split.shares();
|
|
}
|
|
}
|
|
|
|
if(t->transaction().postDate() > TQDate::tqcurrentDate()) {
|
|
tracer.printf("Reducing actual balance by %s because %s/%s(%s) is in the future", (split.shares() * factor).formatMoney("", 2).data(), t->transaction().id().data(), split.id().data(), t->transaction().postDate().toString(Qt::ISODate).data());
|
|
actBalance[split.accountId()] -= split.shares() * factor;
|
|
}
|
|
}
|
|
futureBalance[split.accountId()] = balance;
|
|
}
|
|
p = p->prevItem();
|
|
}
|
|
|
|
tracer.printf("total balance of %s = %s", m_account.name().data(), actBalance[m_account.id()].formatMoney("", 2).data());
|
|
tracer.printf("future balance of %s = %s", m_account.name().data(), futureBalance[m_account.id()].formatMoney("", 2).data());
|
|
tracer.printf("cleared balance of %s = %s", m_account.name().data(), clearedBalance[m_account.id()].formatMoney("", 2).data());
|
|
|
|
// update statement information
|
|
if(statement) {
|
|
statement->setText(i18n("%1 deposits (%3), %2 payments (%4)").
|
|
arg(deposits[m_account.id()]).
|
|
arg(payments[m_account.id()]).
|
|
arg(depositAmount[m_account.id()].abs().formatMoney(m_account.fraction())).
|
|
arg(paymentAmount[m_account.id()].abs().formatMoney(m_account.fraction())) );
|
|
}
|
|
if(pStatement) {
|
|
pStatement->setText(i18n("%1 payments (%2)").tqarg(payments[m_account.id()]).
|
|
arg(paymentAmount[m_account.id()].abs().formatMoney(m_account.fraction())) );
|
|
}
|
|
if(dStatement) {
|
|
dStatement->setText(i18n("%1 deposits (%2)").tqarg(deposits[m_account.id()]).
|
|
arg(depositAmount[m_account.id()].abs().formatMoney(m_account.fraction())) );
|
|
}
|
|
|
|
// add a last empty entry for new transactions
|
|
// leave some information about the current account
|
|
MyMoneySplit split;
|
|
split.setReconcileFlag(MyMoneySplit::NotReconciled);
|
|
// make sure to use the value specified in the option during reconciliation
|
|
if(isReconciliationAccount())
|
|
split.setReconcileFlag(static_cast<MyMoneySplit::reconcileFlagE>(KMyMoneyGlobalSettings::defaultReconciliationState()));
|
|
KMyMoneyRegister::Register::transactionFactory(m_register, MyMoneyTransaction(), split, 0);
|
|
|
|
m_register->updateRegister(true);
|
|
|
|
if(focusItem) {
|
|
// in case we have some selected items we just set the focus item
|
|
// in other cases, we make the focusitem also the selected item
|
|
if(isSelected.count() > 1) {
|
|
m_register->setFocusItem(focusItem);
|
|
m_register->setAnchorItem(anchorItem);
|
|
} else
|
|
m_register->selectItem(focusItem, true);
|
|
} else {
|
|
// just use the empty line at the end if nothing else exists in the ledger
|
|
p = m_register->lastItem();
|
|
m_register->setFocusItem(p);
|
|
m_register->selectItem(p);
|
|
focusItem = p;
|
|
}
|
|
|
|
updateSummaryLine(actBalance, clearedBalance);
|
|
kmymoney2->slotStatusProgressBar(-1, -1);
|
|
|
|
} catch(MyMoneyException *e) {
|
|
delete e;
|
|
m_account = MyMoneyAccount();
|
|
clear();
|
|
}
|
|
|
|
// (re-)position viewport
|
|
if(m_newAccountLoaded) {
|
|
if(focusItem) {
|
|
d->m_startPoint = TQPoint(-1, -1);
|
|
} else {
|
|
d->m_startPoint = TQPoint(0, 0);
|
|
}
|
|
}
|
|
if(!d->m_inLoading) {
|
|
d->m_viewPosTimer.start(30, true);
|
|
d->m_inLoading = true;
|
|
}
|
|
|
|
d->m_showDetails = KMyMoneyGlobalSettings::showRegisterDetailed();
|
|
|
|
// and tell everyone what's selected
|
|
emit accountSelected(m_account);
|
|
}
|
|
|
|
void KGlobalLedgerView::updateSummaryLine(const TQMap<TQString, MyMoneyMoney>& actBalance, const TQMap<TQString, MyMoneyMoney>& clearedBalance)
|
|
{
|
|
MyMoneyFile* file = MyMoneyFile::instance();
|
|
m_leftSummaryLabel->show();
|
|
m_centerSummaryLabel->show();
|
|
m_rightSummaryLabel->show();
|
|
|
|
if(isReconciliationAccount()) {
|
|
if(m_account.accountType() != MyMoneyAccount::Investment) {
|
|
m_leftSummaryLabel->setText(i18n("Statement: %1").tqarg(d->m_endingBalance.formatMoney("", d->m_precision)));
|
|
m_centerSummaryLabel->setText(i18n("Cleared: %1").tqarg(clearedBalance[m_account.id()].formatMoney("", d->m_precision)));
|
|
m_rightSummaryLabel->setText(i18n("Difference: %1").tqarg((clearedBalance[m_account.id()] - d->m_endingBalance).formatMoney("", d->m_precision)));
|
|
}
|
|
} else {
|
|
// update summary line in normal mode
|
|
TQDate reconcileDate = m_account.lastReconciliationDate();
|
|
|
|
if(reconcileDate.isValid()) {
|
|
m_leftSummaryLabel->setText(i18n("Last reconciled: %1").tqarg(KGlobal::locale()->formatDate(reconcileDate, true)));
|
|
} else {
|
|
m_leftSummaryLabel->setText(i18n("Never reconciled"));
|
|
}
|
|
|
|
m_rightSummaryLabel->setPaletteForegroundColor(m_leftSummaryLabel->paletteForegroundColor());
|
|
if(m_account.accountType() != MyMoneyAccount::Investment) {
|
|
m_centerSummaryLabel->setText(i18n("Cleared: %1").tqarg(clearedBalance[m_account.id()].formatMoney("", d->m_precision)));
|
|
m_rightSummaryLabel->setText(i18n("Balance: %1").tqarg(actBalance[m_account.id()].formatMoney("", d->m_precision)));
|
|
bool showNegative = actBalance[m_account.id()].isNegative();
|
|
if(m_account.accountGroup() == MyMoneyAccount::Liability && !actBalance[m_account.id()].isZero())
|
|
showNegative = !showNegative;
|
|
if(showNegative) {
|
|
m_rightSummaryLabel->setPaletteForegroundColor(KMyMoneyGlobalSettings::listNegativeValueColor());
|
|
}
|
|
} else {
|
|
m_centerSummaryLabel->hide();
|
|
MyMoneyMoney balance;
|
|
MyMoneySecurity base = file->baseCurrency();
|
|
TQMap<TQString, MyMoneyMoney>::const_iterator it_b;
|
|
bool approx = false;
|
|
for(it_b = actBalance.begin(); it_b != actBalance.end(); ++it_b) {
|
|
MyMoneyAccount stock = file->account(it_b.key());
|
|
TQString currencyId = stock.currencyId();
|
|
MyMoneySecurity sec = file->security(currencyId);
|
|
MyMoneyPrice priceInfo;
|
|
MyMoneyMoney rate(1,1);
|
|
|
|
if(stock.isInvest()) {
|
|
currencyId = sec.tradingCurrency();
|
|
priceInfo = file->price(sec.id(), currencyId);
|
|
approx |= !priceInfo.isValid();
|
|
rate = priceInfo.rate(sec.tradingCurrency());
|
|
}
|
|
|
|
if(currencyId != base.id()) {
|
|
priceInfo = file->price(sec.tradingCurrency(), base.id());
|
|
approx |= !priceInfo.isValid();
|
|
rate = (rate * priceInfo.rate(base.id())).convert(MyMoneyMoney::precToDenom(KMyMoneyGlobalSettings::pricePrecision()));
|
|
}
|
|
balance += ((*it_b) * rate).convert(base.smallestAccountFraction());
|
|
}
|
|
m_rightSummaryLabel->setText(i18n("Investment value: %1%2").tqarg(approx ? "~" : "").tqarg(balance.formatMoney(base.tradingSymbol(), d->m_precision)));
|
|
}
|
|
}
|
|
}
|
|
|
|
void KGlobalLedgerView::slotUpdateViewPos(void)
|
|
{
|
|
m_register->setUpdatesEnabled(true);
|
|
|
|
if(d->m_startPoint == TQPoint(-1, -1)) {
|
|
m_register->ensureItemVisible(m_register->focusItem());
|
|
m_register->updateContents();
|
|
} else {
|
|
m_register->setContentsPos(d->m_startPoint.x(), d->m_startPoint.y());
|
|
m_register->repaintContents();
|
|
}
|
|
d->m_inLoading = false;
|
|
}
|
|
|
|
|
|
void KGlobalLedgerView::resizeEvent(TQResizeEvent* ev)
|
|
{
|
|
m_register->resize(KMyMoneyRegister::DetailColumn);
|
|
m_form->resize(KMyMoneyTransactionForm::ValueColumn1);
|
|
KMyMoneyViewBase::resizeEvent(ev);
|
|
}
|
|
|
|
void KGlobalLedgerView::loadAccounts(void)
|
|
{
|
|
MyMoneyFile* file = MyMoneyFile::instance();
|
|
|
|
// check if the current account still exists and make it the
|
|
// current account
|
|
if(!m_account.id().isEmpty()) {
|
|
try {
|
|
m_account = file->account(m_account.id());
|
|
} catch(MyMoneyException *e) {
|
|
delete e;
|
|
m_account = MyMoneyAccount();
|
|
}
|
|
}
|
|
|
|
m_accountComboBox->loadList((KMyMoneyUtils::categoryTypeE)(KMyMoneyUtils::asset | KMyMoneyUtils::liability));
|
|
|
|
if(m_account.id().isEmpty()) {
|
|
TQStringList list = m_accountComboBox->accountList();
|
|
if(list.count()) {
|
|
TQStringList::Iterator it;
|
|
for(it = list.begin(); it != list.end(); ++it) {
|
|
MyMoneyAccount a = file->account(*it);
|
|
if(!a.isInvest()) {
|
|
if(a.value("PreferredAccount") == "Yes") {
|
|
m_account = a;
|
|
break;
|
|
} else if(m_account.id().isEmpty()) {
|
|
m_account = a;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!m_account.id().isEmpty()) {
|
|
m_accountComboBox->setSelected(m_account);
|
|
try {
|
|
d->m_precision = MyMoneyMoney::denomToPrec(m_account.fraction());
|
|
} catch(MyMoneyException *e) {
|
|
qDebug("Security %s for account %s not found", m_account.currencyId().data(), m_account.name().data());
|
|
delete e;
|
|
d->m_precision = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KGlobalLedgerView::selectTransaction(const TQString& id)
|
|
{
|
|
if(!id.isEmpty()) {
|
|
KMyMoneyRegister::RegisterItem* p = m_register->lastItem();
|
|
while(p) {
|
|
KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
|
|
if(t) {
|
|
if(t->transaction().id() == id) {
|
|
m_register->selectItem(t);
|
|
m_register->ensureItemVisible(t);
|
|
break;
|
|
}
|
|
}
|
|
p = p->prevItem();
|
|
}
|
|
}
|
|
}
|
|
|
|
void KGlobalLedgerView::slotSelectAllTransactions(void)
|
|
{
|
|
KMyMoneyRegister::RegisterItem* p = m_register->firstItem();
|
|
while(p) {
|
|
KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
|
|
if(t) {
|
|
if(t->isVisible() && t->isSelectable() && !t->isScheduled() && !t->id().isEmpty()) {
|
|
t->setSelected(true);
|
|
}
|
|
}
|
|
p = p->nextItem();
|
|
}
|
|
m_register->repaintItems();
|
|
|
|
// inform everyone else about the selected items
|
|
KMyMoneyRegister::SelectedTransactions list(m_register);
|
|
emit transactionsSelected(list);
|
|
}
|
|
|
|
void KGlobalLedgerView::slotSetReconcileAccount(const MyMoneyAccount& acc, const TQDate& reconciliationDate, const MyMoneyMoney& endingBalance)
|
|
{
|
|
if(d->m_reconciliationAccount != acc.id()) {
|
|
// make sure the account is selected
|
|
if(!acc.id().isEmpty())
|
|
slotSelectAccount(acc.id());
|
|
|
|
d->m_reconciliationAccount = acc.id();
|
|
d->m_reconciliationDate = reconciliationDate;
|
|
d->m_endingBalance = endingBalance;
|
|
if(acc.accountGroup() == MyMoneyAccount::Liability)
|
|
d->m_endingBalance = -endingBalance;
|
|
|
|
m_newAccountLoaded = true;
|
|
if(acc.id().isEmpty()) {
|
|
kmymoney2->action("account_reconcile_postpone")->unplug(m_buttonbar);
|
|
kmymoney2->action("account_reconcile_finish")->unplug(m_buttonbar);
|
|
} else {
|
|
kmymoney2->action("account_reconcile_postpone")->plug(m_buttonbar);
|
|
kmymoney2->action("account_reconcile_finish")->plug(m_buttonbar);
|
|
// when we start reconciliation, we need to reload the view
|
|
// because no data has been changed. When postponing or finishing
|
|
// reconciliation, the data change in the engine takes care of updateing
|
|
// the view.
|
|
slotLoadView();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool KGlobalLedgerView::isReconciliationAccount(void) const
|
|
{
|
|
return m_account.id() == d->m_reconciliationAccount;
|
|
}
|
|
|
|
bool KGlobalLedgerView::slotSelectAccount(const MyMoneyObject& obj)
|
|
{
|
|
if(typeid(obj) != typeid(MyMoneyAccount))
|
|
return false;
|
|
|
|
if(d->m_recursion)
|
|
return false;
|
|
|
|
d->m_recursion = true;
|
|
const MyMoneyAccount& acc = dynamic_cast<const MyMoneyAccount&>(obj);
|
|
bool rc = slotSelectAccount(acc.id());
|
|
d->m_recursion = false;
|
|
return rc;
|
|
}
|
|
|
|
bool KGlobalLedgerView::slotSelectAccount(const TQString& id, const TQString& transactionId)
|
|
{
|
|
bool rc = true;
|
|
|
|
if(!id.isEmpty()) {
|
|
if(m_account.id() != id) {
|
|
try {
|
|
m_account = MyMoneyFile::instance()->account(id);
|
|
// if a stock account is selected, we show the
|
|
// the corresponding parent (investment) account
|
|
if(m_account.isInvest()) {
|
|
m_account = MyMoneyFile::instance()->account(m_account.parentAccountId());
|
|
}
|
|
m_newAccountLoaded = true;
|
|
slotLoadView();
|
|
} catch(MyMoneyException* e) {
|
|
qDebug("Unable to retrieve account %s", id.data());
|
|
delete e;
|
|
rc = false;
|
|
}
|
|
} else {
|
|
// we need to refresh m_account.m_accountList, a child could have been deleted
|
|
m_account = MyMoneyFile::instance()->account(id);
|
|
emit accountSelected(m_account);
|
|
}
|
|
selectTransaction(transactionId);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
void KGlobalLedgerView::slotNewTransaction(KMyMoneyRegister::Action id)
|
|
{
|
|
if(!m_inEditMode) {
|
|
d->m_action = id;
|
|
emit newTransaction();
|
|
}
|
|
}
|
|
|
|
void KGlobalLedgerView::slotNewTransaction(void)
|
|
{
|
|
slotNewTransaction(KMyMoneyRegister::ActionNone);
|
|
}
|
|
|
|
void KGlobalLedgerView::setupDefaultAction(void)
|
|
{
|
|
switch(m_account.accountType()) {
|
|
// TODO if we want a different action for different account types
|
|
// we can add cases here
|
|
default:
|
|
d->m_action = KMyMoneyRegister::ActionWithdrawal;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool KGlobalLedgerView::selectEmptyTransaction(void)
|
|
{
|
|
bool rc = false;
|
|
|
|
if(!m_inEditMode) {
|
|
// in case we don't know the type of transaction to be created,
|
|
// have at least one selected transaction and the id of
|
|
// this transaction is not empty, we take it as template for the
|
|
// transaction to be created
|
|
KMyMoneyRegister::SelectedTransactions list(m_register);
|
|
if((d->m_action == KMyMoneyRegister::ActionNone) && (list.count() > 0) && (!list[0].transaction().id().isEmpty())) {
|
|
// the new transaction to be created will have the same type
|
|
// as the one that currently has the focus
|
|
KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(m_register->focusItem());
|
|
if(t)
|
|
d->m_action = t->actionType();
|
|
m_register->clearSelection();
|
|
}
|
|
|
|
// if we still don't have an idea which type of transaction
|
|
// to create, we use the default.
|
|
if(d->m_action == KMyMoneyRegister::ActionNone) {
|
|
setupDefaultAction();
|
|
}
|
|
|
|
m_register->selectItem(m_register->lastItem());
|
|
m_register->updateRegister();
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
TransactionEditor* KGlobalLedgerView::startEdit(const KMyMoneyRegister::SelectedTransactions& list)
|
|
{
|
|
// we use the warnlevel to keep track, if we have to warn the
|
|
// user that some or all splits have been reconciled or if the
|
|
// user cannot modify the transaction if at least one split
|
|
// has the status frozen. The following value are used:
|
|
//
|
|
// 0 - no sweat, user can modify
|
|
// 1 - user should be warned that at least one split has been reconciled
|
|
// already
|
|
// 2 - user will be informed, that this transaction cannot be changed anymore
|
|
|
|
int warnLevel = list.warnLevel();
|
|
Q_ASSERT(warnLevel<2); // otherwise the edit action should not be enabled
|
|
|
|
switch(warnLevel) {
|
|
case 0:
|
|
break;
|
|
|
|
case 1:
|
|
if(KMessageBox::warningContinueCancel(0,
|
|
i18n(
|
|
"At least one split of the selected transactions has been reconciled. "
|
|
"Do you wish to continue to edit the transactions anyway?"
|
|
),
|
|
i18n("Transaction already reconciled"), KStdGuiItem::cont(),
|
|
"EditReconciledTransaction") == KMessageBox::Cancel) {
|
|
warnLevel = 2;
|
|
}
|
|
break;
|
|
|
|
case 2:
|
|
KMessageBox::sorry(0,
|
|
i18n("At least one split of the selected transactions has been frozen. "
|
|
"Editing the transactions is therefore prohibited."),
|
|
i18n("Transaction already frozen"));
|
|
break;
|
|
|
|
case 3:
|
|
KMessageBox::sorry(0,
|
|
i18n("At least one split of the selected transaction references an account that has been closed. "
|
|
"Editing the transactions is therefore prohibited."),
|
|
i18n("Account closed"));
|
|
break;
|
|
}
|
|
|
|
if(warnLevel > 1)
|
|
return 0;
|
|
|
|
|
|
TransactionEditor* editor = 0;
|
|
KMyMoneyRegister::Transaction* item = dynamic_cast<KMyMoneyRegister::Transaction*>(m_register->focusItem());
|
|
|
|
if(item) {
|
|
// in case the current focus item is not selected, we move the focus to the first selected transaction
|
|
if(!item->isSelected()) {
|
|
KMyMoneyRegister::RegisterItem* p;
|
|
for(p = m_register->firstItem(); p; p = p->nextItem()) {
|
|
KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
|
|
if(t && t->isSelected()) {
|
|
m_register->setFocusItem(t);
|
|
item = t;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// decide, if we edit in the register or in the form
|
|
TransactionEditorContainer* parent;
|
|
if(m_formFrame->isVisible())
|
|
parent = m_form;
|
|
else {
|
|
parent = m_register;
|
|
}
|
|
|
|
editor = item->createEditor(parent, list, m_lastPostDate);
|
|
|
|
// 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) {
|
|
if(!editor->fixTransactionCommodity(m_account)) {
|
|
// if the user wants to quit, we need to destroy the editor
|
|
// and bail out
|
|
delete editor;
|
|
editor = 0;
|
|
}
|
|
}
|
|
|
|
if(editor) {
|
|
if(parent == m_register) {
|
|
// make sure, the height of the table is correct
|
|
m_register->updateRegister(KMyMoneyGlobalSettings::ledgerLens() | !KMyMoneyGlobalSettings::transactionForm());
|
|
}
|
|
|
|
m_inEditMode = true;
|
|
connect(editor, TQT_SIGNAL(transactionDataSufficient(bool)), kmymoney2->action("transaction_enter"), TQT_SLOT(setEnabled(bool)));
|
|
connect(editor, TQT_SIGNAL(returnPressed()), kmymoney2->action("transaction_enter"), TQT_SLOT(activate()));
|
|
connect(editor, TQT_SIGNAL(escapePressed()), kmymoney2->action("transaction_cancel"), TQT_SLOT(activate()));
|
|
|
|
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(objectCreation(bool)), d->m_mousePressFilter, TQT_SLOT(setFilterDeactive(bool)));
|
|
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(editor, TQT_SIGNAL(assignNumber(void)), kmymoney2, TQT_SLOT(slotTransactionAssignNumber()));
|
|
connect(editor, TQT_SIGNAL(lastPostDateUsed(const TQDate&)), this, TQT_SLOT(slotKeepPostDate(const TQDate&)));
|
|
|
|
// create the widgets, place them in the parent and load them with data
|
|
// setup tab order
|
|
m_tabOrderWidgets.clear();
|
|
editor->setup(m_tabOrderWidgets, m_account, d->m_action);
|
|
|
|
Q_ASSERT(!m_tabOrderWidgets.isEmpty());
|
|
|
|
// install event filter in all taborder widgets
|
|
for(TQWidget* w = m_tabOrderWidgets.first(); w; w = m_tabOrderWidgets.next()) {
|
|
w->installEventFilter(this);
|
|
}
|
|
|
|
// Install a filter that checks if a mouse press happened outside
|
|
// of one of our own widgets.
|
|
tqApp->installEventFilter(d->m_mousePressFilter);
|
|
|
|
// Check if the editor has some preference on where to set the focus
|
|
// If not, set the focus to the first widget in the tab order
|
|
TQWidget* tqfocusWidget = editor->firstWidget();
|
|
if(!tqfocusWidget)
|
|
tqfocusWidget = m_tabOrderWidgets.first();
|
|
|
|
// for some reason, this only works reliably if delayed a bit
|
|
TQTimer::singleShot(10, tqfocusWidget, TQT_SLOT(setFocus()));
|
|
|
|
// preset to 'I have no idea which type to create' for the next round.
|
|
d->m_action = KMyMoneyRegister::ActionNone;
|
|
}
|
|
}
|
|
return editor;
|
|
}
|
|
|
|
void KGlobalLedgerView::slotLeaveEditMode(const KMyMoneyRegister::SelectedTransactions& list)
|
|
{
|
|
m_inEditMode = false;
|
|
tqApp->removeEventFilter(d->m_mousePressFilter);
|
|
|
|
// a possible focusOut event may have removed the focus, so we
|
|
// install it back again.
|
|
m_register->focusItem()->setFocus(true);
|
|
|
|
// if we come back from editing a new item, we make sure that
|
|
// we always select the very last known transaction entry no
|
|
// matter if the transaction has been created or not.
|
|
|
|
if(list.count() && list[0].transaction().id().isEmpty()) {
|
|
// block signals to prevent some infinite loops that might occur here.
|
|
m_register->blockSignals(true);
|
|
m_register->clearSelection();
|
|
KMyMoneyRegister::RegisterItem* p = m_register->lastItem();
|
|
if(p && p->prevItem())
|
|
p = p->prevItem();
|
|
m_register->selectItem(p);
|
|
m_register->updateRegister(true);
|
|
m_register->blockSignals(false);
|
|
// we need to update the form manually as sending signals was blocked
|
|
KMyMoneyRegister::Transaction* t = dynamic_cast<KMyMoneyRegister::Transaction*>(p);
|
|
if(t)
|
|
m_form->slotSetTransaction(t);
|
|
}
|
|
|
|
if(m_needReload)
|
|
slotLoadView();
|
|
|
|
m_register->setFocus();
|
|
}
|
|
|
|
bool KGlobalLedgerView::focusNextPrevChild(bool next)
|
|
{
|
|
bool rc = false;
|
|
|
|
// qDebug("KGlobalLedgerView::focusNextPrevChild(editmode=%s)", m_inEditMode ? "true" : "false");
|
|
if(m_inEditMode) {
|
|
TQWidget *w = 0;
|
|
TQWidget *currentWidget;
|
|
|
|
w = tqApp->tqfocusWidget();
|
|
while(w && 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 = m_tabOrderWidgets.current();
|
|
w = next ? m_tabOrderWidgets.next() : m_tabOrderWidgets.prev();
|
|
|
|
do {
|
|
if(!w) {
|
|
w = next ? m_tabOrderWidgets.first() : 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 ? m_tabOrderWidgets.next() : m_tabOrderWidgets.prev();
|
|
} while(w != currentWidget);
|
|
|
|
} else
|
|
rc = KMyMoneyViewBase::focusNextPrevChild(next);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
void KGlobalLedgerView::show(void)
|
|
{
|
|
if(m_needReload) {
|
|
if(!m_inEditMode) {
|
|
loadView();
|
|
m_needReload = false;
|
|
m_newAccountLoaded = false;
|
|
}
|
|
|
|
} else {
|
|
emit accountSelected(m_account);
|
|
KMyMoneyRegister::SelectedTransactions list(m_register);
|
|
emit transactionsSelected(list);
|
|
}
|
|
|
|
// don't forget base class implementation
|
|
KMyMoneyViewBase::show();
|
|
}
|
|
|
|
bool KGlobalLedgerView::eventFilter(TQObject* o, TQEvent* e)
|
|
{
|
|
bool rc = false;
|
|
|
|
if(e->type() == TQEvent::KeyPress) {
|
|
TQKeyEvent *k = TQT_TQKEYEVENT(e);
|
|
if(m_inEditMode) {
|
|
// qDebug("object = %s, key = %d", o->className(), k->key());
|
|
if(TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_register)) {
|
|
// we hide all key press events from the register
|
|
// while editing a transaction
|
|
rc = true;
|
|
}
|
|
} else {
|
|
// qDebug("object = %s, key = %d", o->className(), k->key());
|
|
if(TQT_BASE_OBJECT(o) == TQT_BASE_OBJECT(m_register)) {
|
|
if((k->state() & TQt::KeyButtonMask) == 0) {
|
|
switch(k->key()) {
|
|
case TQt::Key_Return:
|
|
case TQt::Key_Enter:
|
|
kmymoney2->action("transaction_edit")->activate();
|
|
rc = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!rc)
|
|
rc = KMyMoneyViewBase::eventFilter(o, e);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void KGlobalLedgerView::slotSortOptions(void)
|
|
{
|
|
KSortOptionDlg* dlg = new KSortOptionDlg(this);
|
|
|
|
TQString key;
|
|
TQString sortOrder, def;
|
|
if(isReconciliationAccount()) {
|
|
key = "kmm-sort-reconcile";
|
|
def = KMyMoneyGlobalSettings::sortReconcileView();
|
|
} else {
|
|
key = "kmm-sort-std";
|
|
def = KMyMoneyGlobalSettings::sortNormalView();
|
|
}
|
|
|
|
// check if we have an account override of the sort order
|
|
if(!m_account.value(key).isEmpty())
|
|
sortOrder = m_account.value(key);
|
|
|
|
TQString oldOrder = sortOrder;
|
|
|
|
dlg->setSortOption(sortOrder, def);
|
|
|
|
if(dlg->exec() == TQDialog::Accepted) {
|
|
sortOrder = dlg->sortOption();
|
|
if(sortOrder != oldOrder) {
|
|
if(sortOrder.isEmpty()) {
|
|
m_account.deletePair(key);
|
|
} else {
|
|
m_account.setValue(key, sortOrder);
|
|
}
|
|
MyMoneyFileTransaction ft;
|
|
try {
|
|
MyMoneyFile::instance()->modifyAccount(m_account);
|
|
ft.commit();
|
|
} catch(MyMoneyException* e) {
|
|
qDebug("Unable to update sort order for account '%s': %s", m_account.name().latin1(), e->what().latin1());
|
|
delete e;
|
|
}
|
|
}
|
|
}
|
|
delete dlg;
|
|
}
|
|
|
|
void KGlobalLedgerView::slotToggleTransactionMark(KMyMoneyRegister::Transaction* /* t */)
|
|
{
|
|
if(!m_inEditMode) {
|
|
emit toggleReconciliationFlag();
|
|
}
|
|
}
|
|
|
|
void KGlobalLedgerView::slotKeepPostDate(const TQDate& date)
|
|
{
|
|
m_lastPostDate = date;
|
|
}
|
|
|
|
bool KGlobalLedgerView::canCreateTransactions(TQString& tooltip) const
|
|
{
|
|
bool rc = true;
|
|
if(m_account.id().isEmpty()) {
|
|
tooltip = i18n("Cannot create transactions when no account is selected.");
|
|
rc = false;
|
|
}
|
|
if(m_account.accountGroup() == MyMoneyAccount::Income
|
|
|| m_account.accountGroup() == MyMoneyAccount::Expense) {
|
|
tooltip = i18n("Cannot create transactions in the context of a category.");
|
|
rc = false;
|
|
}
|
|
if(m_account.isClosed()) {
|
|
tooltip = i18n("Cannot create transactions in a closed account.");
|
|
rc = false;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
bool KGlobalLedgerView::canProcessTransactions(const KMyMoneyRegister::SelectedTransactions& list, TQString& tooltip) const
|
|
{
|
|
if(m_register->focusItem() == 0)
|
|
return false;
|
|
|
|
if(!m_register->focusItem()->isSelected()) {
|
|
tooltip = i18n("Cannot process transaction with focus if it is not selected.");
|
|
return false;
|
|
}
|
|
return list.count() > 0;
|
|
}
|
|
|
|
bool KGlobalLedgerView::canModifyTransactions(const KMyMoneyRegister::SelectedTransactions& list, TQString& tooltip) const
|
|
{
|
|
return canProcessTransactions(list,tooltip) && list.canModify();
|
|
}
|
|
|
|
bool KGlobalLedgerView::canDuplicateTransactions(const KMyMoneyRegister::SelectedTransactions& list, TQString& tooltip) const
|
|
{
|
|
return canProcessTransactions(list,tooltip) && list.canDuplicate();
|
|
}
|
|
|
|
bool KGlobalLedgerView::canEditTransactions(const KMyMoneyRegister::SelectedTransactions& list, TQString& tooltip) const
|
|
{
|
|
// check if we can edit the list of transactions. We can edit, if
|
|
//
|
|
// a) no mix of standard and investment transactions exist
|
|
// b) if a split transaction is selected, this is the only selection
|
|
// c) none of the splits is frozen
|
|
// d) the transaction having the current focus is selected
|
|
|
|
// check for d)
|
|
if(!canProcessTransactions(list, tooltip))
|
|
return false;
|
|
// check for c)
|
|
if (list.warnLevel() == 2) {
|
|
tooltip = i18n("Cannot edit transactions with frozen splits.");
|
|
return false;
|
|
}
|
|
|
|
|
|
bool rc = true;
|
|
int investmentTransactions = 0;
|
|
int normalTransactions = 0;
|
|
|
|
if(m_account.accountGroup() == MyMoneyAccount::Income
|
|
|| m_account.accountGroup() == MyMoneyAccount::Expense) {
|
|
tooltip = i18n("Cannot edit transactions in the context of a category.");
|
|
rc = false;
|
|
}
|
|
|
|
KMyMoneyRegister::SelectedTransactions::const_iterator it_t;
|
|
for(it_t = list.begin(); rc && it_t != list.end(); ++it_t) {
|
|
if((*it_t).transaction().id().isEmpty()) {
|
|
tooltip = TQString();
|
|
rc = false;
|
|
continue;
|
|
}
|
|
|
|
if(KMyMoneyUtils::transactionType((*it_t).transaction()) == KMyMoneyUtils::InvestmentTransaction)
|
|
++investmentTransactions;
|
|
else
|
|
++normalTransactions;
|
|
|
|
// check for a)
|
|
if(investmentTransactions != 0 && normalTransactions != 0) {
|
|
tooltip = i18n("Cannot edit investment transactions and non-investment transactions together.");
|
|
rc = false;
|
|
break;
|
|
}
|
|
|
|
// check for b) but only for normalTransactions
|
|
if((*it_t).transaction().splitCount() > 2 && normalTransactions != 0) {
|
|
if(list.count() > 1) {
|
|
tooltip = i18n("Cannot edit multiple split transactions at once.");
|
|
rc = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now check that we have the correct account type for investment transactions
|
|
if(rc == true && investmentTransactions != 0) {
|
|
if(m_account.accountType() != MyMoneyAccount::Investment) {
|
|
tooltip = i18n("Cannot edit investment transactions in the context of this account.");
|
|
rc = false;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
#include "kgloballedgerview.moc"
|