1/* This file is part of the KDE project
2 Copyright (C) 2007, 2012 Dag Andersen <danders@get2net>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17* Boston, MA 02110-1301, USA.
18*/
19
20#include "kptaccountsmodel.h"
21
22#include "kptglobal.h"
23#include "kptcommonstrings.h"
24#include "kptcommand.h"
25#include "kptduration.h"
26#include "kptnode.h"
27#include "kptproject.h"
28#include "kpttask.h"
29#include "kptaccount.h"
30#include "kptdatetime.h"
31#include "kptschedule.h"
32#include "kptdebug.h"
33
34#include <KoIcon.h>
35
36#include <QList>
37#include <QObject>
38
39#include <kcalendarsystem.h>
40#include <kdeversion.h>
41#include <kglobal.h>
42#include <klocale.h>
43
44namespace KPlato
45{
46
47//--------------------------------------
48AccountModel::AccountModel()
49 : QObject(),
50 m_project( 0 )
51{
52}
53
54const QMetaEnum AccountModel::columnMap() const
55{
56 return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") );
57}
58
59int AccountModel::propertyCount() const
60{
61 return columnMap().keyCount();
62}
63
64QVariant AccountModel::data( const Account *a, int property, int role ) const
65{
66 QVariant result;
67 if ( a == 0 ) {
68 return QVariant();
69 }
70 switch ( property ) {
71 case AccountModel::Name: result = name( a, role ); break;
72 case AccountModel::Description: result = description( a, role ); break;
73 default:
74 kDebug(planDbg())<<"data: invalid display value column"<<property;
75 return QVariant();
76 }
77 return result;
78}
79
80QVariant AccountModel::name( const Account *a, int role ) const
81{
82 //kDebug(planDbg())<<a->name()<<","<<role;
83 switch ( role ) {
84 case Qt::DisplayRole:
85 case Qt::EditRole:
86 return a->name();
87 case Qt::ToolTipRole:
88 if ( a->isDefaultAccount() ) {
89 return i18nc( "1=account name", "%1 (Default account)", a->name() );
90 }
91 return a->name();
92 case Qt::StatusTipRole:
93 case Qt::WhatsThisRole:
94 return QVariant();
95 case Qt::CheckStateRole:
96 if ( a->isDefaultAccount() ) {
97 return m_project && m_project->isBaselined() ? Qt::PartiallyChecked : Qt::Checked;
98 }
99 return m_project && m_project->isBaselined() ? QVariant() : Qt::Unchecked;
100 case Qt::DecorationRole:
101 if ( a->isBaselined() ) {
102 return koIcon("view-time-schedule-baselined");
103 }
104 break;
105 }
106 return QVariant();
107}
108
109QVariant AccountModel::description( const Account *a, int role ) const
110{
111 //kDebug(planDbg())<<res->name()<<","<<role;
112 switch ( role ) {
113 case Qt::DisplayRole:
114 case Qt::EditRole:
115 case Qt::ToolTipRole:
116 return a->description();
117 break;
118 case Qt::StatusTipRole:
119 case Qt::WhatsThisRole:
120 return QVariant();
121 }
122 return QVariant();
123}
124
125QVariant AccountModel::headerData( int property, int role ) const
126{
127 if ( role == Qt::DisplayRole ) {
128 switch ( property ) {
129 case AccountModel::Name: return i18n( "Name" );
130 case AccountModel::Description: return i18n( "Description" );
131 default: return QVariant();
132 }
133 }
134 if ( role == Qt::TextAlignmentRole ) {
135 return QVariant();
136 }
137 if ( role == Qt::ToolTipRole ) {
138 switch ( property ) {
139 case AccountModel::Name: return ToolTip::accountName();
140 case AccountModel::Description: return ToolTip::accountDescription();
141 default: return QVariant();
142 }
143 }
144 return QVariant();
145}
146
147//----------------------------------------
148AccountItemModel::AccountItemModel( QObject *parent )
149 : ItemModelBase( parent ),
150 m_account( 0 )
151{
152}
153
154AccountItemModel::~AccountItemModel()
155{
156}
157
158const QMetaEnum AccountItemModel::columnMap() const
159{
160 return m_model.columnMap();
161}
162
163void AccountItemModel::slotAccountToBeInserted( const Account *parent, int row )
164{
165 //kDebug(planDbg())<<parent->name();
166 Q_ASSERT( m_account == 0 );
167 m_account = const_cast<Account*>(parent);
168 beginInsertRows( index( parent ), row, row );
169}
170
171void AccountItemModel::slotAccountInserted( const Account *account )
172{
173 //kDebug(planDbg())<<account->name();
174 Q_ASSERT( account->parent() == m_account ); Q_UNUSED( account );
175 endInsertRows();
176 m_account = 0;
177}
178
179void AccountItemModel::slotAccountToBeRemoved( const Account *account )
180{
181 //kDebug(planDbg())<<account->name();
182 Q_ASSERT( m_account == 0 );
183 m_account = const_cast<Account*>(account);
184 int row = index( account ).row();
185 beginRemoveRows( index( account->parent() ), row, row );
186}
187
188void AccountItemModel::slotAccountRemoved( const Account *account )
189{
190 //kDebug(planDbg())<<account->name();
191 Q_ASSERT( account == m_account ); Q_UNUSED( account );
192 endRemoveRows();
193 m_account = 0;
194}
195
196void AccountItemModel::setProject( Project *project )
197{
198 if ( m_project ) {
199 Accounts *acc = &( m_project->accounts() );
200 disconnect( acc , SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) );
201
202 disconnect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) );
203 disconnect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) );
204
205 disconnect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) );
206 disconnect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) );
207 }
208 m_project = project;
209 m_model.m_project = project;
210 if ( project ) {
211 Accounts *acc = &( project->accounts() );
212 kDebug(planDbg())<<acc;
213 connect( acc, SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) );
214
215 connect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) );
216 connect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) );
217
218 connect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) );
219 connect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) );
220 }
221}
222
223Qt::ItemFlags AccountItemModel::flags( const QModelIndex &index ) const
224{
225 Qt::ItemFlags flags = ItemModelBase::flags( index );
226 if ( ! m_readWrite ) {
227 return flags &= ~Qt::ItemIsEditable;
228 }
229 if ( ! index.isValid() || ! m_project ) {
230 return flags;
231 }
232 Account *a = account( index );
233 if ( a ) {
234 switch ( index.column() ) {
235 case AccountModel::Name: {
236 if ( ! a->isBaselined() ) {
237 flags |= Qt::ItemIsEditable;
238 flags |= Qt::ItemIsUserCheckable;
239 }
240 break;
241 }
242 default: flags |= Qt::ItemIsEditable; break;
243 }
244 }
245 return flags;
246}
247
248
249QModelIndex AccountItemModel::parent( const QModelIndex &index ) const
250{
251 if ( !index.isValid() || m_project == 0 ) {
252 return QModelIndex();
253 }
254 //kDebug(planDbg())<<index.internalPointer()<<":"<<index.row()<<","<<index.column();
255 Account *a = account( index );
256 if ( a == 0 ) {
257 return QModelIndex();
258 }
259 Account *par = a->parent();
260 if ( par ) {
261 a = par->parent();
262 int row = -1;
263 if ( a ) {
264 row = a->accountList().indexOf( par );
265 } else {
266 row = m_project->accounts().accountList().indexOf( par );
267 }
268 //kDebug(planDbg())<<par->name()<<":"<<row;
269 return createIndex( row, 0, par );
270 }
271 return QModelIndex();
272}
273
274QModelIndex AccountItemModel::index( int row, int column, const QModelIndex &parent ) const
275{
276 if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) {
277 return QModelIndex();
278 }
279 Account *par = account( parent );
280 if ( par == 0 ) {
281 if ( row < m_project->accounts().accountList().count() ) {
282 return createIndex( row, column, m_project->accounts().accountList().at( row ) );
283 }
284 } else if ( row < par->accountList().count() ) {
285 return createIndex( row, column, par->accountList().at( row ) );
286 }
287 return QModelIndex();
288}
289
290QModelIndex AccountItemModel::index( const Account *account, int column ) const
291{
292 Account *a = const_cast<Account*>(account);
293 if ( m_project == 0 || account == 0 ) {
294 return QModelIndex();
295 }
296 int row = -1;
297 Account *par = a->parent();
298 if ( par == 0 ) {
299 row = m_project->accounts().accountList().indexOf( a );
300 } else {
301 row = par->accountList().indexOf( a );
302 }
303 if ( row == -1 ) {
304 return QModelIndex();
305 }
306 return createIndex( row, column, a );
307
308}
309
310int AccountItemModel::columnCount( const QModelIndex & ) const
311{
312 return m_model.propertyCount();
313}
314
315int AccountItemModel::rowCount( const QModelIndex &parent ) const
316{
317 if ( m_project == 0 ) {
318 return 0;
319 }
320 Account *par = account( parent );
321 if ( par == 0 ) {
322 return m_project->accounts().accountList().count();
323 }
324 return par->accountList().count();
325}
326
327bool AccountItemModel::setName( Account *a, const QVariant &value, int role )
328{
329 switch ( role ) {
330 case Qt::EditRole:
331 if ( value.toString() != a->name() ) {
332 emit executeCommand( new RenameAccountCmd( a, value.toString(), kundo2_i18n( "Modify account name" ) ) );
333 }
334 return true;
335 case Qt::CheckStateRole: {
336 switch ( value.toInt() ) {
337 case Qt::Unchecked:
338 if ( a->isDefaultAccount() ) {
339 emit executeCommand( new ModifyDefaultAccountCmd( m_project->accounts(), a, 0, kundo2_i18n( "De-select as default account" ) ) );
340 return true;
341 }
342 break;
343 case Qt::Checked:
344 if ( ! a->isDefaultAccount() ) {
345 emit executeCommand( new ModifyDefaultAccountCmd( m_project->accounts(), m_project->accounts().defaultAccount(), a, kundo2_i18n( "Select as default account" ) ) );
346 return true;
347 }
348 break;
349 default: break;
350 }
351 }
352 default: break;
353 }
354 return false;
355}
356
357bool AccountItemModel::setDescription( Account *a, const QVariant &value, int role )
358{
359 switch ( role ) {
360 case Qt::EditRole:
361 if ( value.toString() != a->description() ) {
362 emit executeCommand( new ModifyAccountDescriptionCmd( a, value.toString(), kundo2_i18n( "Modify account description" ) ) );
363 }
364 return true;
365 }
366 return false;
367}
368
369QVariant AccountItemModel::data( const QModelIndex &index, int role ) const
370{
371 QVariant result;
372 Account *a = account( index );
373 if ( a == 0 ) {
374 return QVariant();
375 }
376 result = m_model.data( a, index.column(), role );
377 return result;
378}
379
380bool AccountItemModel::setData( const QModelIndex &index, const QVariant &value, int role )
381{
382 if ( ! index.isValid() ) {
383 return ItemModelBase::setData( index, value, role );
384 }
385 if ( ( flags( index ) &( Qt::ItemIsEditable | Qt::CheckStateRole ) ) == 0 ) {
386 Q_ASSERT( true );
387 return false;
388 }
389 Account *a = account( index );
390 kDebug(planDbg())<<a->name()<<value<<role;
391 switch (index.column()) {
392 case AccountModel::Name: return setName( a, value, role );
393 case AccountModel::Description: return setDescription( a, value, role );
394 default:
395 qWarning("data: invalid display value column %d", index.column());
396 return false;
397 }
398 return false;
399}
400
401QVariant AccountItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
402{
403 if ( orientation == Qt::Horizontal ) {
404 return m_model.headerData( section, role );
405 }
406 return ItemModelBase::headerData(section, orientation, role);
407}
408
409Account *AccountItemModel::account( const QModelIndex &index ) const
410{
411 return static_cast<Account*>( index.internalPointer() );
412}
413
414void AccountItemModel::slotAccountChanged( Account *account )
415{
416 Account *par = account->parent();
417 if ( par ) {
418 int row = par->accountList().indexOf( account );
419 emit dataChanged( createIndex( row, 0, account ), createIndex( row, columnCount() - 1, account ) );
420 } else {
421 int row = m_project->accounts().accountList().indexOf( account );
422 emit dataChanged( createIndex( row, 0, account ), createIndex( row, columnCount() - 1, account ) );
423 }
424}
425
426QModelIndex AccountItemModel::insertAccount( Account *account, Account *parent, int index )
427{
428 kDebug(planDbg());
429 if ( account->name().isEmpty() || m_project->accounts().findAccount( account->name() ) ) {
430 QString s = parent == 0 ? account->name() : parent->name();
431 account->setName( m_project->accounts().uniqueId( s ) );
432 //m_project->accounts().insertId( account );
433 }
434 emit executeCommand( new AddAccountCmd( *m_project, account, parent, index, kundo2_i18n( "Add account" ) ) );
435 int row = -1;
436 if ( parent ) {
437 row = parent->accountList().indexOf( account );
438 } else {
439 row = m_project->accounts().accountList().indexOf( account );
440 }
441 if ( row != -1 ) {
442 //kDebug(planDbg())<<"Inserted:"<<account->name();
443 return createIndex( row, 0, account );
444 }
445 kDebug(planDbg())<<"Can't find"<<account->name();
446 return QModelIndex();
447}
448
449void AccountItemModel::removeAccounts( QList<Account*> lst )
450{
451 MacroCommand *cmd = 0;
452 KUndo2MagicString c = kundo2_i18np( "Delete Account", "Delete %1 Accounts", lst.count() );
453 while ( ! lst.isEmpty() ) {
454 bool del = true;
455 Account *acc = lst.takeFirst();
456 foreach ( Account *a, lst ) {
457 if ( acc->isChildOf( a ) ) {
458 del = false; // acc will be deleted when a is deleted
459 break;
460 }
461 }
462 if ( del ) {
463 if ( cmd == 0 ) cmd = new MacroCommand( c );
464 cmd->addCommand( new RemoveAccountCmd( *m_project, acc ) );
465 }
466 }
467 if ( cmd )
468 emit executeCommand( cmd );
469}
470
471//----------------------------------------
472CostBreakdownItemModel::CostBreakdownItemModel( QObject *parent )
473 : ItemModelBase( parent ),
474 m_manager( 0 ),
475 m_cumulative( false ),
476 m_periodtype( Period_Day ),
477 m_startmode( StartMode_Project ),
478 m_endmode( EndMode_Project ),
479 m_showmode( ShowMode_Both )
480{
481 m_format = QString( "%1 [%2]" );
482}
483
484CostBreakdownItemModel::~CostBreakdownItemModel()
485{
486}
487
488void CostBreakdownItemModel::slotAccountToBeInserted( const Account *parent, int row )
489{
490 //kDebug(planDbg())<<parent->name();
491 beginInsertRows( index( parent ), row, row );
492}
493
494void CostBreakdownItemModel::slotAccountInserted( const Account *account )
495{
496 Q_UNUSED(account);
497 //kDebug(planDbg())<<account->name();
498 endInsertRows();
499}
500
501void CostBreakdownItemModel::slotAccountToBeRemoved( const Account *account )
502{
503
504 //kDebug(planDbg())<<account->name();
505 int row = index( account ).row();
506 beginRemoveRows( index( account->parent() ), row, row );
507}
508
509void CostBreakdownItemModel::slotAccountRemoved( const Account *account )
510{
511 Q_UNUSED(account);
512 //kDebug(planDbg())<<account->name();
513 endRemoveRows();
514}
515
516void CostBreakdownItemModel::slotDataChanged()
517{
518 fetchData();
519 foreach ( Account *a, m_plannedCostMap.keys() ) {
520 QModelIndex idx1 = index( a );
521 QModelIndex idx2 = index( idx1.row(), columnCount() - 1, parent( idx1 ) );
522 //kDebug(planDbg())<<a->name()<<idx1<<idx2;
523 emit dataChanged( idx1, idx2 );
524 }
525}
526
527void CostBreakdownItemModel::setProject( Project *project )
528{
529 if ( m_project ) {
530 Accounts *acc = &( m_project->accounts() );
531 disconnect( acc , SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) );
532
533 disconnect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) );
534 disconnect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) );
535
536 disconnect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) );
537 disconnect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) );
538
539 disconnect( m_project , SIGNAL(nodeChanged(Node*)), this, SLOT(slotDataChanged()) );
540 disconnect( m_project , SIGNAL(nodeAdded(Node*)), this, SLOT(slotDataChanged()) );
541 disconnect( m_project , SIGNAL(nodeRemoved(Node*)), this, SLOT(slotDataChanged()) );
542
543 disconnect( m_project , SIGNAL(resourceChanged(Resource*)), this, SLOT(slotDataChanged()) );
544 disconnect( m_project , SIGNAL(resourceAdded(const Resource*)), this, SLOT(slotDataChanged()) );
545 disconnect( m_project , SIGNAL(resourceRemoved(const Resource*)), this, SLOT(slotDataChanged()) );
546 }
547 m_project = project;
548 if ( project ) {
549 Accounts *acc = &( project->accounts() );
550 kDebug(planDbg())<<acc;
551 connect( acc, SIGNAL(changed(Account*)), this, SLOT(slotAccountChanged(Account*)) );
552
553 connect( acc, SIGNAL(accountAdded(const Account*)), this, SLOT(slotAccountInserted(const Account*)) );
554 connect( acc, SIGNAL(accountToBeAdded(const Account*,int)), this, SLOT(slotAccountToBeInserted(const Account*,int)) );
555
556 connect( acc, SIGNAL(accountRemoved(const Account*)), this, SLOT(slotAccountRemoved(const Account*)) );
557 connect( acc, SIGNAL(accountToBeRemoved(const Account*)), this, SLOT(slotAccountToBeRemoved(const Account*)) );
558
559 connect( m_project , SIGNAL(nodeChanged(Node*)), this, SLOT(slotDataChanged()) );
560 connect( m_project , SIGNAL(nodeAdded(Node*)), this, SLOT(slotDataChanged()) );
561 connect( m_project , SIGNAL(nodeRemoved(Node*)), this, SLOT(slotDataChanged()) );
562
563 connect( m_project , SIGNAL(resourceChanged(Resource*)), this, SLOT(slotDataChanged()) );
564 connect( m_project , SIGNAL(resourceAdded(const Resource*)), this, SLOT(slotDataChanged()) );
565 connect( m_project , SIGNAL(resourceRemoved(const Resource*)), this, SLOT(slotDataChanged()) );
566 }
567}
568
569void CostBreakdownItemModel::setScheduleManager( ScheduleManager *sm )
570{
571 kDebug(planDbg())<<m_project<<m_manager<<sm;
572 if ( m_manager != sm ) {
573 m_manager = sm;
574 fetchData();
575 reset();
576 }
577}
578
579long CostBreakdownItemModel::id() const
580{
581 return m_manager == 0 ? -1 : m_manager->scheduleId();
582}
583
584EffortCostMap CostBreakdownItemModel::fetchPlannedCost( Account *account )
585{
586 EffortCostMap ec;
587 ec = account->plannedCost( id() );
588 m_plannedCostMap.insert( account, ec );
589 QDate s = ec.startDate();
590 if ( ! m_plannedStart.isValid() || s < m_plannedStart ) {
591 m_plannedStart = s;
592 }
593 QDate e = ec.endDate();
594 if ( ! m_plannedEnd.isValid() || e > m_plannedEnd ) {
595 m_plannedEnd = e;
596 }
597 return ec;
598}
599
600EffortCostMap CostBreakdownItemModel::fetchActualCost( Account *account )
601{
602 kDebug(planDbg())<<account->name();
603 EffortCostMap ec;
604 ec = account->actualCost( id() );
605 m_actualCostMap.insert( account, ec );
606 QDate s = ec.startDate();
607 if ( ! m_actualStart.isValid() || s < m_actualStart ) {
608 m_actualStart = s;
609 }
610 QDate e = ec.endDate();
611 if ( ! m_actualEnd.isValid() || e > m_actualEnd ) {
612 m_actualEnd = e;
613 }
614 kDebug(planDbg())<<account->name()<<ec.totalEffort().toDouble(Duration::Unit_h)<<ec.totalCost();
615 return ec;
616}
617
618void CostBreakdownItemModel::fetchData()
619{
620 //kDebug(planDbg())<<m_start<<m_end;
621 m_plannedCostMap.clear();
622 m_plannedStart = m_plannedEnd = QDate();
623 m_actualStart = m_actualEnd = QDate();
624 if ( m_project == 0 || m_manager == 0 ) {
625 return;
626 }
627 foreach ( Account *a, m_project->accounts().allAccounts() ) {
628 fetchPlannedCost( a );
629 fetchActualCost( a );
630 }
631}
632
633QModelIndex CostBreakdownItemModel::parent( const QModelIndex &index ) const
634{
635 if ( !index.isValid() || m_project == 0 ) {
636 return QModelIndex();
637 }
638 //kDebug(planDbg())<<index.internalPointer()<<":"<<index.row()<<","<<index.column();
639 Account *a = account( index );
640 if ( a == 0 ) {
641 return QModelIndex();
642 }
643 Account *par = a->parent();
644 if ( par ) {
645 a = par->parent();
646 int row = -1;
647 if ( a ) {
648 row = a->accountList().indexOf( par );
649 } else {
650 row = m_project->accounts().accountList().indexOf( par );
651 }
652 //kDebug(planDbg())<<par->name()<<":"<<row;
653 return createIndex( row, 0, par );
654 }
655 return QModelIndex();
656}
657
658QModelIndex CostBreakdownItemModel::index( int row, int column, const QModelIndex &parent ) const
659{
660 if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) {
661 return QModelIndex();
662 }
663 Account *par = account( parent );
664 if ( par == 0 ) {
665 if ( row < m_project->accounts().accountList().count() ) {
666 return createIndex( row, column, m_project->accounts().accountList().at( row ) );
667 }
668 } else if ( row < par->accountList().count() ) {
669 return createIndex( row, column, par->accountList().at( row ) );
670 }
671 return QModelIndex();
672}
673
674QModelIndex CostBreakdownItemModel::index( const Account *account ) const
675{
676 Account *a = const_cast<Account*>(account);
677 if ( m_project == 0 || account == 0 ) {
678 return QModelIndex();
679 }
680 int row = -1;
681 Account *par = a->parent();
682 if ( par == 0 ) {
683 row = m_project->accounts().accountList().indexOf( a );
684 } else {
685 row = par->accountList().indexOf( a );
686 }
687 if ( row == -1 ) {
688 return QModelIndex();
689 }
690 return createIndex( row, 0, a );
691
692}
693
694int CostBreakdownItemModel::columnCount( const QModelIndex & ) const
695{
696 int c = 3;
697 if ( startDate().isValid() && endDate().isValid() ) {
698 switch ( m_periodtype ) {
699 case Period_Day: {
700 c += startDate().daysTo( endDate()) + 1;
701 break;
702 }
703 case Period_Week: {
704 // ISO Week numbering always uses Monday as first day of week
705 const int firstWeekDay = (KGlobal::locale()->weekNumberSystem() == KLocale::IsoWeekNumber)
706 ? Qt::Monday
707 : KGlobal::locale()->weekStartDay();
708
709 int days = firstWeekDay - startDate().dayOfWeek();
710 if ( days > 0 ) {
711 days -= 7;
712 }
713 QDate start = startDate().addDays( days );
714 c += (start.daysTo( endDate() ) / 7) + 1;
715 break;
716 }
717 case Period_Month: {
718 int days = startDate().daysInMonth() - startDate().day() + 1;
719 for ( QDate d = startDate(); d < endDate(); d = d.addDays( days ) ) {
720 ++c;
721 days = qMin( d.daysTo( endDate() ), d.daysInMonth() );
722 }
723 break;
724 }
725 }
726 }
727 return c;
728}
729
730int CostBreakdownItemModel::rowCount( const QModelIndex &parent ) const
731{
732 if ( m_project == 0 ) {
733 return 0;
734 }
735 Account *par = account( parent );
736 if ( par == 0 ) {
737 return m_project->accounts().accountList().count();
738 }
739 return par->accountList().count();
740}
741
742QString CostBreakdownItemModel::formatMoney( double cost1, double cost2 ) const
743{
744 if ( m_showmode == ShowMode_Planned ) {
745 return m_project->locale()->formatMoney( cost1, "", 0 );
746 }
747 if ( m_showmode == ShowMode_Actual ) {
748 return m_project->locale()->formatMoney( cost2, "", 0 );
749 }
750 if ( m_showmode == ShowMode_Both ) {
751 return QString(m_format).arg( m_project->locale()->formatMoney( cost2, "", 0 ) ).arg( m_project->locale()->formatMoney( cost1, "", 0 ) );
752 }
753 if ( m_showmode == ShowMode_Deviation ) {
754 return m_project->locale()->formatMoney( cost1 - cost2, "", 0 );
755 }
756 return "";
757}
758
759QVariant CostBreakdownItemModel::data( const QModelIndex &index, int role ) const
760{
761 QVariant result;
762 Account *a = account( index );
763 if ( a == 0 ) {
764 return QVariant();
765 }
766 if ( role == Qt::DisplayRole ) {
767 switch ( index.column() ) {
768 case 0: return a->name();
769 case 1: return a->description();
770 case 2: {
771 return formatMoney( m_plannedCostMap.value( a ).totalCost(), m_actualCostMap.value( a ).totalCost() );
772 }
773 default: {
774 int col = index.column() - 3;
775 EffortCostMap pc = m_plannedCostMap.value( a );
776 EffortCostMap ac = m_actualCostMap.value( a );
777 switch ( m_periodtype ) {
778 case Period_Day: {
779 double planned = 0.0;
780 if ( m_cumulative ) {
781 planned = pc.costTo( startDate().addDays( col ) );
782 } else {
783 planned = pc.costOnDate( startDate().addDays( col ) );
784 }
785 double actual = 0.0;
786 if ( m_cumulative ) {
787 actual = ac.costTo( startDate().addDays( col ) );
788 } else {
789 actual = ac.costOnDate( startDate().addDays( col ) );
790 }
791 return formatMoney( planned, actual );
792 }
793 case Period_Week: {
794 // ISO Week numbering always uses Monday as first day of week
795 const int firstWeekDay = (KGlobal::locale()->weekNumberSystem() == KLocale::IsoWeekNumber)
796 ? Qt::Monday
797 : KGlobal::locale()->weekStartDay();
798
799 int days = firstWeekDay - startDate().dayOfWeek();
800 if ( days > 0 ) {
801 days -= 7; ;
802 }
803 QDate start = startDate().addDays( days );
804 int week = col;
805 double planned = 0.0;
806 if ( m_cumulative ) {
807 planned = pc.costTo( start.addDays( ++week * 7 ) );
808 } else {
809 planned = week == 0 ? pc.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : pc.cost( start.addDays( week * 7 ) );
810 }
811 double actual = 0.0;
812 if ( m_cumulative ) {
813 actual = ac.costTo( start.addDays( ++week * 7 ) );
814 } else {
815 actual = week == 0 ? ac.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : ac.cost( start.addDays( week * 7 ) );
816 }
817 return formatMoney( planned, actual );
818 }
819 case Period_Month: {
820 int days = startDate().daysInMonth() - startDate().day() + 1;
821 QDate start = startDate();
822 for ( int i = 0; i < col; ++i ) {
823 start = start.addDays( days );
824 days = start.daysInMonth();
825 }
826 int planned = 0.0;
827 if ( m_cumulative ) {
828 planned = pc.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) );
829 } else {
830 planned = pc.cost( start, start.daysInMonth() - start.day() + 1);
831 }
832 int actual = 0.0;
833 if ( m_cumulative ) {
834 actual = ac.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) );
835 } else {
836 actual = ac.cost( start, start.daysInMonth() - start.day() + 1);
837 }
838 return formatMoney( planned, actual );
839 }
840 default:
841 return 0.0;
842 break;
843 }
844 }
845 }
846 } else if ( role == Qt::ToolTipRole ) {
847 switch ( index.column() ) {
848 case 0: return a->name();
849 case 1: return a->description();
850 case 2: {
851 double act = m_actualCostMap.value( a ).totalCost();
852 double pl = m_plannedCostMap.value( a ).totalCost();
853 return i18n( "Actual total cost: %1, planned total cost: %2", m_project->locale()->formatMoney( act, "", 0 ), m_project->locale()->formatMoney( pl, "", 0 ) );
854 }
855 default: break;
856 }
857 } else if ( role == Qt::TextAlignmentRole ) {
858 return headerData( index.column(), Qt::Horizontal, role );
859 } else {
860 switch ( index.column() ) {
861 case 0:
862 case 1: return QVariant();
863 default: {
864 return cost( a, index.column() - 3, role );
865 }
866 }
867 }
868 return QVariant();
869}
870
871QVariant CostBreakdownItemModel::cost( const Account *a, int offset, int role ) const
872{
873 EffortCostMap costmap;
874 if ( role == Role::Planned ) {
875 costmap = m_plannedCostMap.value( const_cast<Account*>( a ) );
876 } else if ( role == Role::Actual ) {
877 costmap = m_actualCostMap.value( const_cast<Account*>( a ) );
878 } else {
879 return QVariant();
880 }
881 double cost = 0.0;
882 switch ( m_periodtype ) {
883 case Period_Day: {
884 if ( m_cumulative ) {
885 cost = costmap.costTo( startDate().addDays( offset ) );
886 } else {
887 cost = costmap.costOnDate( startDate().addDays( offset ) );
888 }
889 break;
890 }
891 case Period_Week: {
892 // ISO Week numbering always uses Monday as first day of week
893 const int firstWeekDay = (KGlobal::locale()->weekNumberSystem() == KLocale::IsoWeekNumber)
894 ? Qt::Monday
895 : KGlobal::locale()->weekStartDay();
896
897 int days = firstWeekDay - startDate().dayOfWeek();
898 if ( days > 0 ) {
899 days -= 7; ;
900 }
901 QDate start = startDate().addDays( days );
902 int week = offset;
903 if ( m_cumulative ) {
904 cost = costmap.costTo( start.addDays( ++week * 7 ) );
905 } else {
906 cost = week == 0 ? costmap.cost( startDate(), startDate().daysTo( start.addDays( 7 ) ) ) : costmap.cost( start.addDays( week * 7 ) );
907 }
908 break;
909 }
910 case Period_Month: {
911 int days = startDate().daysInMonth() - startDate().day() + 1;
912 QDate start = startDate();
913 for ( int i = 0; i < offset; ++i ) {
914 start = start.addDays( days );
915 days = start.daysInMonth();
916 }
917 if ( m_cumulative ) {
918 cost = costmap.costTo( start.addDays( start.daysInMonth() - start.day() + 1 ) );
919 } else {
920 cost = costmap.cost( start, start.daysInMonth() - start.day() + 1);
921 }
922 break;
923 }
924 default:
925 break;
926 }
927 return cost;
928}
929
930int CostBreakdownItemModel::periodType() const
931{
932 return m_periodtype;
933}
934
935void CostBreakdownItemModel::setPeriodType( int period )
936{
937 if ( m_periodtype != period ) {
938 m_periodtype = period;
939 reset();
940 }
941}
942
943int CostBreakdownItemModel::startMode() const
944{
945 return m_startmode;
946}
947
948void CostBreakdownItemModel::setStartMode( int mode )
949{
950 m_startmode = mode;
951 reset();
952}
953
954int CostBreakdownItemModel::endMode() const
955{
956 return m_endmode;
957}
958
959void CostBreakdownItemModel::setEndMode( int mode )
960{
961 m_endmode = mode;
962 reset();
963}
964
965QDate CostBreakdownItemModel::startDate() const
966{
967 if ( m_project == 0 || m_manager == 0 ) {
968 return m_start;
969 }
970 switch ( m_startmode ) {
971 case StartMode_Project: {
972 QDate d = m_project->startTime( id() ).date();
973 if ( m_plannedStart.isValid() && m_plannedStart < d ) {
974 d = m_plannedStart;
975 }
976 if ( m_actualStart.isValid() && m_actualStart < d ) {
977 d = m_actualStart;
978 }
979 return d;
980 }
981 default: break;
982 }
983 return m_start;
984}
985
986
987void CostBreakdownItemModel::setStartDate( const QDate &date )
988{
989 m_start = date;
990 reset();
991}
992
993QDate CostBreakdownItemModel::endDate() const
994{
995 if ( m_project == 0 || m_manager == 0 ) {
996 return m_end;
997 }
998 switch ( m_endmode ) {
999 case EndMode_Project: {
1000 QDate d = m_project->endTime( id() ).date();
1001 if ( m_plannedEnd.isValid() && m_plannedEnd > d ) {
1002 d = m_plannedEnd;
1003 }
1004 if ( m_actualEnd.isValid() && m_actualEnd > d ) {
1005 d = m_actualEnd;
1006 }
1007 return d;
1008 }
1009 case EndMode_CurrentDate: return QDate::currentDate();
1010 default: break;
1011 }
1012 return m_end;
1013}
1014
1015void CostBreakdownItemModel::setEndDate( const QDate &date )
1016{
1017 m_end = date;
1018 reset();
1019}
1020
1021bool CostBreakdownItemModel::cumulative() const
1022{
1023 return m_cumulative;
1024}
1025
1026void CostBreakdownItemModel::setCumulative( bool on )
1027{
1028 m_cumulative = on;
1029 reset();
1030}
1031
1032int CostBreakdownItemModel::showMode() const
1033{
1034 return m_showmode;
1035}
1036void CostBreakdownItemModel::setShowMode( int show )
1037{
1038 m_showmode = show;
1039}
1040
1041QVariant CostBreakdownItemModel::headerData( int section, Qt::Orientation orientation, int role ) const
1042{
1043 if ( orientation == Qt::Horizontal ) {
1044 if ( role == Qt::DisplayRole ) {
1045 if ( section == 0 ) {
1046 return i18n( "Name" );
1047 }
1048 if ( section == 1 ) {
1049 return i18n( "Description" );
1050 }
1051 if ( section == 2 ) {
1052 return i18n( "Total" );
1053 }
1054 int col = section - 3;
1055 switch ( m_periodtype ) {
1056 case Period_Day: {
1057 return startDate().addDays( col ).toString( Qt::ISODate );
1058 }
1059 case Period_Week: {
1060 const KCalendarSystem * calendar = KGlobal::locale()->calendar();
1061#if KDE_IS_VERSION(4,7,0)
1062 return calendar->week(startDate().addDays( ( col ) * 7 ));
1063#else
1064 return calendar->weekNumber(startDate().addDays( ( col ) * 7 ));
1065#endif
1066 }
1067 case Period_Month: {
1068 int days = startDate().daysInMonth() - startDate().day() + 1;
1069 QDate start = startDate();
1070 for ( int i = 0; i < col; ++i ) {
1071 start = start.addDays( days );
1072 days = start.daysInMonth();
1073 }
1074 return QDate::shortMonthName( start.month() );
1075 }
1076 default:
1077 return section;
1078 break;
1079 }
1080 return QVariant();
1081 }
1082 if ( role == Qt::EditRole ) {
1083 if ( section == 0 ) {
1084 return "Name";
1085 }
1086 if ( section == 1 ) {
1087 return "Description";
1088 }
1089 if ( section == 2 ) {
1090 return "Total";
1091 }
1092 int col = section - 3;
1093 switch ( m_periodtype ) {
1094 case Period_Day: {
1095 return startDate().addDays( col );
1096 }
1097 case Period_Week: {
1098 const KCalendarSystem * calendar = KGlobal::locale()->calendar();
1099#if KDE_IS_VERSION(4,7,0)
1100 return calendar->week(startDate().addDays( ( col ) * 7 ));
1101#else
1102 return calendar->weekNumber(startDate().addDays( ( col ) * 7 ));
1103#endif
1104 }
1105 case Period_Month: {
1106 int days = startDate().daysInMonth() - startDate().day() + 1;
1107 QDate start = startDate();
1108 for ( int i = 0; i < col; ++i ) {
1109 start = start.addDays( days );
1110 days = start.daysInMonth();
1111 }
1112 return start.month();
1113 }
1114 default:
1115 return section;
1116 break;
1117 }
1118 return QVariant();
1119 }
1120 if ( role == Qt::ToolTipRole ) {
1121 switch ( section ) {
1122 case 0: return ToolTip::accountName();
1123 case 1: return ToolTip::accountDescription();
1124 case 2: return i18n( "The total cost for the account shown as: Actual cost [ Planned cost ]" );
1125 default: return QVariant();
1126 }
1127 }
1128 if ( role == Qt::TextAlignmentRole ) {
1129 switch ( section ) {
1130 case 0: return QVariant();
1131 case 1: return QVariant();
1132 default: return (int)(Qt::AlignRight|Qt::AlignVCenter);
1133 }
1134 return QVariant();
1135 }
1136 }
1137 return ItemModelBase::headerData(section, orientation, role);
1138}
1139
1140Account *CostBreakdownItemModel::account( const QModelIndex &index ) const
1141{
1142 return static_cast<Account*>( index.internalPointer() );
1143}
1144
1145void CostBreakdownItemModel::slotAccountChanged( Account *account )
1146{
1147 Q_UNUSED(account);
1148 fetchData();
1149 foreach ( Account *a, m_plannedCostMap.keys() ) {
1150 QModelIndex idx1 = index( a );
1151 QModelIndex idx2 = index( idx1.row(), columnCount() - 1, parent( idx1 ) );
1152 //kDebug(planDbg())<<a->name()<<idx1<<idx2;
1153 emit dataChanged( idx1, idx2 );
1154 }
1155}
1156
1157
1158} // namespace KPlato
1159
1160#include "kptaccountsmodel.moc"
1161