Merge branch '2015.11-develop' into 2016.03
[tine20] / tests / tine20 / Timetracker / JsonTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  *
5  * @package     Timetracker
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2008-2014 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  */
10
11 /**
12  * Test helper
13  */
14 require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php';
15
16 /**
17  * Test class for Timetracker_Frontent_Json
18  */
19 class Timetracker_JsonTest extends Timetracker_AbstractTest
20 {
21     protected $_testUser = NULL;
22     
23     /**
24      * Sets up the fixture.
25      * This method is called before a test is executed.
26      *
27      * @access protected
28      */
29     protected function setUp()
30     {
31         parent::setUp();
32     }
33     
34     /**
35      * Tears down the fixture
36      * This method is called after a test is executed.
37      *
38      * @access protected
39      */
40     protected function tearDown()
41     {
42         // switch back to admin user
43         if ($this->_testUser) {
44             Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
45         }
46         
47         parent::tearDown();
48     }
49     
50     /**
51      * try to add a Timeaccount
52      *
53      */
54     public function testAddTimeaccount()
55     {
56         $timeaccount = $this->_getTimeaccount();
57         $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
58
59         // checks
60         $this->assertEquals($timeaccount->description, $timeaccountData['description']);
61         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountData['created_by']['accountId']);
62         $this->assertTrue(is_array($timeaccountData['container_id']));
63         $this->assertEquals(Tinebase_Model_Container::TYPE_SHARED, $timeaccountData['container_id']['type']);
64         $this->assertGreaterThan(0, count($timeaccountData['grants']));
65
66         // cleanup
67         $this->_json->deleteTimeaccounts($timeaccountData['id']);
68
69         // check if it got deleted
70         $this->setExpectedException('Tinebase_Exception_NotFound');
71         Timetracker_Controller_Timeaccount::getInstance()->get($timeaccountData['id']);
72     }
73
74     /**
75      * try to get a Timeaccount
76      *
77      */
78     public function testGetTimeaccount()
79     {
80         $timeaccount = $this->_getTimeaccount();
81         $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
82         $timeaccountData = $this->_json->getTimeaccount($timeaccountData['id']);
83
84         // checks
85         $this->assertEquals($timeaccount->description, $timeaccountData['description']);
86         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountData['created_by']['accountId']);
87         $this->assertTrue(is_array($timeaccountData['container_id']));
88         $this->assertEquals(Tinebase_Model_Container::TYPE_SHARED, $timeaccountData['container_id']['type']);
89
90         // cleanup
91         $this->_json->deleteTimeaccounts($timeaccountData['id']);
92     }
93
94     /**
95      * try to update a Timeaccount
96      *
97      */
98     public function testUpdateTimeaccount()
99     {
100         $timeaccount = $this->_getTimeaccount();
101         $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
102
103         // update Timeaccount
104         $timeaccountData['description'] = "blubbblubb";
105         $timeaccountUpdated = $this->_json->saveTimeaccount($timeaccountData);
106
107         // check
108         $this->assertEquals($timeaccountData['id'], $timeaccountUpdated['id']);
109         $this->assertEquals($timeaccountData['description'], $timeaccountUpdated['description']);
110         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountUpdated['last_modified_by']['accountId']);
111
112         // cleanup
113         $this->_json->deleteTimeaccounts($timeaccountData['id']);
114     }
115
116     /**
117      * try to get a Timeaccount
118      */
119     public function testSearchTimeaccounts()
120     {
121         // create
122         $timeaccount = $this->_getTimeaccount();
123         $timeaccount->is_open = 0;
124         $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
125
126         // search & check
127         $timeaccountFilter = $this->_getTimeaccountFilter();
128         $search = $this->_json->searchTimeaccounts($timeaccountFilter, $this->_getPaging());
129         $this->assertEquals(0, $search['totalcount'], 'is_open filter not working');
130
131         $search = $this->_json->searchTimeaccounts($this->_getTimeaccountFilter(TRUE), $this->_getPaging());
132         $this->assertEquals(1, $search['totalcount']);
133         $this->assertEquals($timeaccount->description, $search['results'][0]['description']);
134     }
135
136     /**
137      * try to get a Timeaccount with a TA filter
138      * 
139      * @see 0007946: error when searching for single timeaccount
140      */
141     public function testSearchTimeaccountsWithTAFilter()
142     {
143         $timeaccount = $this->_getTimeaccount();
144         $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
145         
146         $searchFilter = '[{
147             "field": "id",
148             "id": "ext-record-869",
149             "label": null,
150             "operator": "equals",
151             "value": "' . $timeaccountData['id'] . '"
152         }]';
153         $paging = '{
154         "paging": {
155             "sort": "number",
156             "dir": "DESC",
157             "start": 0,
158             "limit": 50
159         }}';
160
161         $searchResult = $this->_json->searchTimeaccounts(Zend_Json::decode($searchFilter), Zend_Json::decode($paging));
162         $this->assertEquals(1, $searchResult['totalcount']);
163         $this->assertEquals(1, count($searchResult['filter']), 'did not get ta filter: ' . print_r($searchResult, TRUE));
164         $this->assertEquals($timeaccountData['id'], $searchResult['filter'][0]['value']['id']);
165     }
166     
167     /**
168      * try to add a Timeaccount with grants
169      */
170     public function testAddTimeaccountWithGrants()
171     {
172         $grants = $this->_getGrants();
173         $timeaccountData = $this->_saveTimeaccountWithGrants($grants);
174
175         // checks
176         $this->assertGreaterThan(0, count($timeaccountData['grants']));
177         $this->assertEquals($grants[0]['account_type'], $timeaccountData['grants'][0]['account_type']);
178
179         // cleanup
180         $this->_json->deleteTimeaccounts($timeaccountData['id']);
181
182         // check if it got deleted
183         $this->setExpectedException('Tinebase_Exception_NotFound');
184         Timetracker_Controller_Timeaccount::getInstance()->get($timeaccountData['id']);
185     }
186     
187     /**
188      * save TA with grants
189      * 
190      * @return array
191      */
192     protected function _saveTimeaccountWithGrants($grants = NULL)
193     {
194         $timeaccount = $this->_getTimeaccount();
195         $timeaccountData = $timeaccount->toArray();
196         $timeaccountData['grants'] = ($grants !== NULL) ? $grants : $this->_getGrants();
197         return $this->_json->saveTimeaccount($timeaccountData);
198     }
199
200     /**
201      * try to add a Timesheet
202      *
203      */
204     public function testAddTimesheet()
205     {
206         $timesheet = $this->_getTimesheet();
207         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
208
209         // checks
210         $this->assertEquals($timesheet->description, $timesheetData['description']);
211         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['created_by']['accountId']);
212         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['account_id']['accountId'], 'account is not resolved');
213         $this->assertEquals(Tinebase_DateTime::now()->toString('Y-m-d') . ' 00:00:00',  $timesheetData['start_date']);
214
215         // cleanup
216         $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']['id']);
217
218         // check if everything got deleted
219         $this->setExpectedException('Tinebase_Exception_NotFound');
220         Timetracker_Controller_Timesheet::getInstance()->get($timesheetData['id']);
221     }
222
223     /**
224      * try to add a Timesheet with custom fields
225      *
226      */
227     public function testAddTimesheetWithCustomFields()
228     {
229         $value1 = 'abcd';
230         $value2 = 'efgh';
231         $cf = $this->_getCustomField();
232         
233         // create two timesheets with customfields
234         $this->_addTsWithCf($cf, $value1);
235         $this->_addTsWithCf($cf, $value2);
236
237         // search custom field values and check totalcount
238         $tinebaseJson = new Tinebase_Frontend_Json();
239         $cfValues = $tinebaseJson->searchCustomFieldValues(Zend_Json::encode($this->_getCfValueFilter($cf->getId())), '');
240         $this->assertEquals(2, $cfValues['totalcount'], 'wrong totalcount');
241
242         $cfValueArray = array($cfValues['results'][0]['value'], $cfValues['results'][1]['value']);
243         $this->assertTrue(in_array($value1, $cfValueArray));
244         $this->assertTrue(in_array($value2, $cfValueArray));
245     }
246
247     /**
248      * search Timesheet with empty custom fields
249      */
250     public function testSearchTimesheetWithEmptyCustomField()
251     {
252         $cf = $this->_getCustomField();
253
254         $timesheet = $this->_getTimesheet();
255         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
256
257         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
258             'field'     => 'customfield',
259             'operator'  => 'equals',
260             'value'     => array(
261                 'cfId'  => $cf->getId(),
262                 'value' => '',
263             )
264         )), $this->_getPaging());
265         $this->assertEquals(1, $search['totalcount']);
266     }
267
268     /**
269      * try to add a Timesheet with custom fields (check grants)
270      */
271     public function testAddTimesheetWithCustomFieldGrants()
272     {
273         $value = 'test';
274         $cf = $this->_getCustomField();
275
276         $timesheetArray = $this->_getTimesheet()->toArray();
277         $timesheetArray[$cf->name] = $value;
278         $ts = $this->_json->saveTimesheet($timesheetArray);
279
280         // test with default grants
281         $this->assertTrue((isset($ts['customfields'][$cf->name]) || array_key_exists($cf->name, $ts['customfields'])), 'customfield should be readable');
282         $this->assertEquals($value, $ts['customfields'][$cf->name]);
283
284         // remove all grants
285         Tinebase_CustomField::getInstance()->setGrants($cf, array());
286         $ts = $this->_json->getTimesheet($ts['id']);
287
288         $this->assertTrue(! (isset($ts['customfields']) || array_key_exists('customfields', $ts)), 'customfields should not be readable');
289         $ts = $this->_updateCfOfTs($ts, $cf->name, 'try to update');
290
291         // only read allowed
292         Tinebase_CustomField::getInstance()->setGrants($cf, array(Tinebase_Model_CustomField_Grant::GRANT_READ));
293         $ts = $this->_json->getTimesheet($ts['id']);
294         $this->assertTrue((isset($ts['customfields'][$cf->name]) || array_key_exists($cf->name, $ts['customfields'])), 'customfield should be readable again');
295         $this->assertEquals($value, $ts['customfields'][$cf->name], 'value should not have changed');
296         $ts = $this->_updateCfOfTs($ts, $cf->name, 'try to update');
297         $this->assertEquals($value, $ts['customfields'][$cf->name], 'value should still not have changed');
298     }
299
300     /**
301      * update timesheet customfields and return saved ts
302      *
303      * @param array $_ts
304      * @param string $_cfName
305      * @param string $_cfValue
306      * @return array
307      */
308     protected function _updateCfOfTs($_ts, $_cfName, $_cfValue)
309     {
310         $_ts[$_cfName] = $_cfValue;
311         $_ts['timeaccount_id'] = $_ts['timeaccount_id']['id'];
312         $_ts['account_id'] = $_ts['account_id']['accountId'];
313         unset($_ts['customfields']);
314         $ts = $this->_json->saveTimesheet($_ts);
315
316         return $ts;
317     }
318
319
320     /**
321      * this test is for Tinebase_Frontend_Json updateMultipleRecords with timesheet data in the timetracker app
322      */
323     public function testUpdateMultipleRecords()
324     {
325         $durations = array(75,90,105);
326         $timeAccount = $this->_getTimeaccount(array('description' => 'blablub'),true);
327         $lr = $this->_getLastCreatedRecord();
328
329         $taId = $lr['id'];
330
331         // create customfield
332         $cf = $this->_getCustomField()->toArray();
333
334         $changes = array( array('name' => 'duration',                   'value' => '111'),
335                           array('name' => 'description',                'value' => 'PHPUNIT_multipleUpdate'),
336                           array('name' => 'customfield_' . $cf['name'], 'value' => 'PHPUNIT_multipleUpdate' )
337         );
338
339         foreach ($durations as $duration) {
340             $timeSheet = $this->_getTimesheet(array('timeaccount_id' => $taId, 'duration' => $duration),true);
341             $lr = $this->_getLastCreatedRecord();
342             $timesheetIds[] = $lr['id'];
343         }
344
345         $filter = array( array('field' => 'id',         'operator' => 'in',     'value' => $timesheetIds),
346                          array('field' => 'account_id', 'operator' => 'equals', 'value' => Tinebase_Core::getUser()->getId())
347         );
348
349         $json = new Tinebase_Frontend_Json();
350
351         $result = $json->updateMultipleRecords('Timetracker', 'Timesheet', $changes, $filter);
352
353         // look if all 3 contacts are updated
354         $this->assertEquals(3, $result['totalcount'],'Could not update the correct number of records');
355
356         // check if default field duration value was found
357         $sFilter = array( array('field' => 'duration',   'operator' => 'equals', 'value' => '111'),
358                           array('field' => 'account_id', 'operator' => 'equals', 'value' => Tinebase_Core::getUser()->getId())
359         );
360         $searchResult = $this->_json->searchTimesheets($sFilter,$this->_getPaging());
361
362         // look if all 3 contacts are found again by default field, and check if default field got properly updated
363         $this->assertEquals(3, $searchResult['totalcount'],'Could not find the correct number of records by duration');
364
365         $record = array_pop($searchResult['results']);
366
367         // check if customfieldvalue was updated properly
368         $this->assertEquals($record['customfields'][$cf['name']],'PHPUNIT_multipleUpdate','Customfield was not updated as expected');
369
370         // check if other default field value was updated properly
371         $this->assertEquals($record['duration'],'111','DefaultField "duration" was not updated as expected');
372     }
373
374     /**
375      * try to get a Timesheet
376      *
377      */
378     public function testGetTimesheet()
379     {
380         $timesheet = $this->_getTimesheet();
381         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
382         $timesheetData = $this->_json->getTimesheet($timesheetData['id']);
383
384         // checks
385         $this->assertEquals($timesheet->description, $timesheetData['description']);
386         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['created_by']['accountId']);
387         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['account_id']['accountId'], 'account is not resolved');
388         $this->assertEquals($timesheet['timeaccount_id'], $timesheetData['timeaccount_id']['id'], 'timeaccount is not resolved');
389
390         // cleanup
391         $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']['id']);
392     }
393
394     /**
395      * try to update a Timesheet (with relations)
396      *
397      */
398     public function testUpdateTimesheet()
399     {
400         $timesheet = $this->_getTimesheet();
401         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
402
403         // update Timesheet
404         $timesheetData['description'] = "blubbblubb";
405         //$timesheetData['container_id'] = $timesheetData['container_id']['id'];
406         $timesheetData['account_id'] = $timesheetData['account_id']['accountId'];
407         $timesheetData['timeaccount_id'] = $timesheetData['timeaccount_id']['id'];
408
409         $timesheetUpdated = $this->_json->saveTimesheet($timesheetData);
410
411         // check
412         $this->assertEquals($timesheetData['id'], $timesheetUpdated['id']);
413         $this->assertEquals($timesheetData['description'], $timesheetUpdated['description']);
414         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetUpdated['last_modified_by']['accountId']);
415         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetUpdated['account_id']['accountId'], 'account is not resolved');
416         $this->assertEquals($timesheetData['timeaccount_id'], $timesheetUpdated['timeaccount_id']['id'], 'timeaccount is not resolved');
417
418         // cleanup
419         $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']);
420     }
421
422     /**
423      * try to get a Timesheet
424      *
425      */
426     public function testDeleteTimesheet()
427     {
428         $timesheet = $this->_getTimesheet();
429         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
430
431         // delete
432         $this->_json->deleteTimesheets($timesheetData['id']);
433
434         $timesheets = Timetracker_Controller_Timesheet::getInstance()->getTimesheetsByTimeaccountId($timesheetData['timeaccount_id']['id']);
435
436         // checks
437         $this->assertEquals(0, count($timesheets));
438
439         // cleanup
440         $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']['id']);
441     }
442
443     /**
444      * try to search for Timesheets
445      *
446      */
447     public function testSearchTimesheets()
448     {
449         // create
450         $timesheet = $this->_getTimesheet();
451
452         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
453
454         // search & check
455         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(), $this->_getPaging());
456         $this->assertEquals($timesheet->description, $search['results'][0]['description']);
457
458         $this->assertEquals('array', gettype($search['results'][0]['timeaccount_id']), 'timeaccount_id is not resolved');
459         $this->assertEquals('array', gettype($search['results'][0]['account_id']), 'account_id is not resolved');
460
461         $this->assertEquals(1, $search['totalcount']);
462         $this->assertEquals(1, count($search['results']));
463         $this->assertEquals(30, $search['totalsum'], 'totalsum mismatch');
464     }
465
466     /**
467      * try to search for Timesheets with date filtering (using 'weekThis' filter)
468      *
469      */
470     public function testSearchTimesheetsWithDateFilterWeekThis()
471     {
472         $this->_dateFilterTest();
473     }
474
475     /**
476      * try to search for Timesheets with date filtering (using inweek operator)
477      *
478      */
479     public function testSearchTimesheetsWithDateFilterInWeek()
480     {
481         $this->_dateFilterTest('inweek');
482     }
483
484     /**
485      * try to search for Timesheets with date filtering (using monthLast operator)
486      */
487     public function testSearchTimesheetsWithDateMonthLast()
488     {
489         $today = Tinebase_DateTime::now();
490         $lastMonth = $today->setDate($today->get('Y'), $today->get('m') - 1, 1);
491         $this->_createTsAndSearch($lastMonth, 'monthLast');
492     }
493
494     /**
495      * date filter test helper
496      *
497      * @param string $_type weekThis|inweek|monthLast
498      */
499     protected function _dateFilterTest($_type = 'weekThis')
500     {
501         $oldLocale = Tinebase_Core::getLocale();
502         Tinebase_Core::set(Tinebase_Core::LOCALE, new Zend_Locale('en_US'));
503
504         // date is last/this sunday (1. day of week in the US)
505         $today = Tinebase_DateTime::now();
506         $dayOfWeek = $today->get('w');
507         $lastSunday = $today->subDay($dayOfWeek);
508
509         $this->_createTsAndSearch($lastSunday, $_type);
510
511         // change locale to de_DE -> timesheet should no longer be found because monday is the first day of the week
512         Tinebase_Core::set(Tinebase_Core::LOCALE, new Zend_Locale('de_DE'));
513         $search = $this->_json->searchTimesheets($this->_getTimesheetDateFilter($_type), $this->_getPaging());
514         // if today is sunday -> ts should be found in german locale!
515         $this->assertEquals(($dayOfWeek == 0) ? 1 : 0, $search['totalcount'], 'filter not working in german locale');
516
517         Tinebase_Core::set(Tinebase_Core::LOCALE, $oldLocale);
518     }
519
520     /**
521      * create timesheet and search with filter
522      *
523      * @param Tinebase_DateTime $_startDate
524      * @param string $_filterType
525      */
526     protected function _createTsAndSearch($_startDate, $_filterType)
527     {
528         //$timesheet = $this->_getTimesheet(NULL, $_startDate);
529         $timesheet = $this->_getTimesheet(array('timeaccount_id' => null, 'start_date' => $_startDate));
530         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
531
532         $result = $this->_json->searchTimesheets($this->_getTimesheetDateFilter($_filterType), $this->_getPaging());
533
534         $this->assertEquals(1, $result['totalcount'], 'timesheet not found with ' . $_filterType . ' filter');
535         $this->assertEquals($timesheet->description, $result['results'][0]['description']);
536         $this->assertEquals('array', gettype($result['results'][0]['timeaccount_id']), 'timeaccount_id is not resolved');
537         $this->assertEquals('array', gettype($result['results'][0]['account_id']), 'account_id is not resolved');
538     }
539
540     /**
541      * try to search for Timesheets (with combined is_billable + cleared)
542      */
543     public function testSearchTimesheetsWithCombinedIsBillableAndCleared()
544     {
545         // create
546         $timesheet = $this->_getTimesheet();
547         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
548
549         // update timeaccount -> is_billable = false
550         $ta = Timetracker_Controller_Timeaccount::getInstance()->get($timesheetData['timeaccount_id']['id']);
551         $ta->is_billable = 0;
552         Timetracker_Controller_Timeaccount::getInstance()->update($ta);
553
554         // search & check
555         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
556             'field' => 'is_cleared_combined',
557             'operator' => 'equals',
558             'value' => FALSE
559         )), $this->_getPaging('is_billable_combined'));
560         
561         $this->assertGreaterThanOrEqual(1, count($search['results']));
562         $this->assertEquals(0, $search['results'][0]['is_billable_combined'], 'is_billable_combined mismatch');
563         $this->assertEquals(0, $search['results'][0]['is_cleared_combined'], 'is_cleared_combined mismatch');
564         $this->assertEquals(1, $search['totalcount']);
565         $this->assertEquals(30, $search['totalsum']);
566         $this->assertEquals(0, $search['totalsumbillable']);
567
568         // search again with is_billable filter
569         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
570             'field' => 'is_billable_combined',
571             'operator' => 'equals',
572             'value' => FALSE,
573         )), $this->_getPaging('is_billable_combined'));
574         $this->assertEquals(0, $search['results'][0]['is_billable_combined'], 'is_billable_combined mismatch');
575
576         // search again with is_billable filter and no sorting
577         $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
578             'field' => 'is_billable_combined',
579             'operator' => 'equals',
580             'value' => FALSE,
581         )), $this->_getPaging());
582         $this->assertEquals(0, $search['results'][0]['is_billable_combined'], 'is_billable_combined mismatch');
583     }
584     
585     /**
586      * testSearchTimesheetsSumBillable
587      */
588     public function testSearchTimesheetsSumBillable()
589     {
590         $timesheet = $this->_getTimesheet();
591         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
592         $timesheet = $this->_getTimesheet();
593         $timesheet->is_billable = false;
594         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
595         
596         // search & check
597         $search = $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(), $this->_getPaging());
598         $this->assertEquals(60, $search['totalsum']);
599         $this->assertEquals(30, $search['totalsumbillable']);
600     }
601     
602     /**
603      * try to save and search persistent filter
604      *
605      * @todo move this test to tinebase json tests?
606      */
607     public function testSavePersistentTimesheetFilter()
608     {
609         $persistentFiltersJson = new Tinebase_Frontend_Json_PersistentFilter();
610
611         // create
612         $filterName = Tinebase_Record_Abstract::generateUID();
613         $persistentFiltersJson->savePersistentFilter(array(
614             'application_id'    => Tinebase_Application::getInstance()->getApplicationById('Timetracker')->getId(),
615             'filters'           => $this->_getTimesheetFilter(),
616             'name'              => $filterName,
617             'model'             => 'Timetracker_Model_TimesheetFilter'
618         ));
619
620         // get
621         $persistentFilters = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
622         //print_r($persistentFilters);
623
624         //check
625         $this->assertEquals(1, $persistentFilters['totalcount']);
626         $this->assertEquals($filterName, $persistentFilters['results'][0]['name']);
627         $this->assertEquals(Tinebase_Core::getUser()->getId(), $persistentFilters['results'][0]['created_by']);
628         $this->assertEquals($persistentFilters['results'][0]['filters'], $this->_getTimesheetFilter());
629
630         // cleanup / delete file
631         $persistentFiltersJson->deletePersistentFilters($persistentFilters['results'][0]['id']);
632     }
633
634     /**
635      * try to save/update and search persistent filter
636      *
637      * @todo move this test to tinebase json tests?
638      */
639     public function testUpdatePersistentTimesheetFilter()
640     {
641         $persistentFiltersJson = new Tinebase_Frontend_Json_PersistentFilter();
642         $tsFilter = $this->_getTimesheetFilter();
643
644         // create
645         $filterName = Tinebase_Record_Abstract::generateUID();
646         $persistentFiltersJson->savePersistentFilter(array(
647             'application_id'    => Tinebase_Application::getInstance()->getApplicationById('Timetracker')->getId(),
648             'filters'           => $tsFilter,
649             'name'              => $filterName,
650             'model'             => 'Timetracker_Model_TimesheetFilter'
651         ));
652
653         $persistentFilters = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
654
655         // update
656         $updatedFilter = $persistentFilters['results'][0];
657         $updatedFilter[0]['value'] = 'blubb';
658         $persistentFiltersJson->savePersistentFilter($updatedFilter);
659
660         // get
661         $persistentFiltersUpdated = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
662         //print_r($persistentFiltersUpdated);
663
664         //check
665         $this->assertEquals(1, $persistentFiltersUpdated['totalcount']);
666         $this->assertEquals($filterName, $persistentFiltersUpdated['results'][0]['name']);
667         $this->assertEquals(Tinebase_Core::getUser()->getId(), $persistentFiltersUpdated['results'][0]['last_modified_by']);
668         //$this->assertEquals($persistentFiltersUpdated['results'][0]['filters'], $updatedFilter);
669         $this->assertEquals($persistentFilters['results'][0]['id'], $persistentFiltersUpdated['results'][0]['id']);
670
671         // cleanup / delete file
672         $persistentFiltersJson->deletePersistentFilters($persistentFiltersUpdated['results'][0]['id']);
673     }
674
675     /**
676      * try to search timesheets with saved persistent filter id
677      *
678      * @todo move this test to tinebase json tests?
679      */
680     public function testSearchTimesheetsWithPersistentFilter()
681     {
682         $persistentFiltersJson = new Tinebase_Frontend_Json_PersistentFilter();
683         $tsFilter = $this->_getTimesheetFilter();
684
685         // create
686         $filterName = Tinebase_Record_Abstract::generateUID();
687         $persistentFiltersJson->savePersistentFilter(array(
688             'application_id'    => Tinebase_Application::getInstance()->getApplicationById('Timetracker')->getId(),
689             'filters'           => $tsFilter,
690             'name'              => $filterName,
691             'model'             => 'Timetracker_Model_TimesheetFilter'
692         ));
693         $timesheet = $this->_getTimesheet();
694         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
695
696         // search persistent filter
697         $persistentFilters = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
698         //check
699         $search = $this->_json->searchTimesheets($persistentFilters['results'][0]['id'], $this->_getPaging());
700         $this->assertEquals($timesheet->description, $search['results'][0]['description']);
701         $this->assertEquals('array', gettype($search['results'][0]['timeaccount_id']), 'timeaccount_id is not resolved');
702         $this->assertEquals('array', gettype($search['results'][0]['account_id']), 'account_id is not resolved');
703         $this->assertEquals(1, $search['totalcount']);
704         $this->assertEquals(30, $search['totalsum']);
705         $this->assertEquals($tsFilter, $search['filter'], 'filters do not match');
706
707         // cleanup / delete file
708         $persistentFiltersJson->deletePersistentFilters($persistentFilters['results'][0]['id']);
709     }
710
711     /**
712      * create timesheet and search with explicite foreign filter
713      */
714     public function testSearchWithExpliciteForeignIdFilter()
715     {
716         $timesheet = $this->_getTimesheet();
717         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
718
719         $filter = array(
720             array('field' => 'timeaccount_id', 'operator' => 'AND', 'value' => array(
721                 array('field' => 'id', 'operator' => 'equals', 'value' => $timesheetData['timeaccount_id']['id'])
722             ))
723         );
724
725         $result = $this->_json->searchTimesheets($filter, $this->_getPaging());
726
727         $this->assertEquals(1, $result['totalcount'], 'timesheet not found with ExpliciteForeignIdFilter filter');
728     }
729
730     /**
731      * create timesheet and search with explicite foreign left hand filter
732      */
733     public function testSearchWithExpliciteForeignIdLeftFilter()
734     {
735         $timesheet = $this->_getTimesheet();
736         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
737
738         $anotherTimesheet = $this->_getTimesheet();
739         $anotherTimesheetData = $this->_json->saveTimesheet($anotherTimesheet->toArray());
740
741         $filter = array(
742             array('field' => 'timeaccount_id', 'operator' => 'AND', 'value' => array(
743                 array('field' => ':id', 'operator' => 'equals', 'value' => $timesheetData['timeaccount_id']['id'])
744             ))
745         );
746
747         $result = $this->_json->searchTimesheets($filter, $this->_getPaging());
748
749         $this->assertEquals(1, $result['totalcount'], 'timesheet not found with ExpliciteForeignIdFilter filter');
750         $this->assertEquals(':id', $result['filter'][0]['value'][0]['field']);
751         $this->assertTrue(is_array($result['filter'][0]['value'][0]['value']), 'timeaccount should be resolved');
752     }
753
754     /**
755      * try to search timesheets with or filter
756      */
757     public function testSearchTimesheetsWithOrFilter()
758     {
759         $timesheet = $this->_getTimesheet();
760         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
761
762         $filterData = $this->_getTSFilterDataByUser(Tinebase_Core::getUser()->getId());
763         $search = $this->_json->searchTimesheets($filterData, array());
764         $this->assertEquals(1, $search['totalcount']);
765     }
766     
767     /**
768      * get ts filter array by user
769      * 
770      * @param string $userId
771      * @return array
772      */
773     protected function _getTSFilterDataByUser($userId)
774     {
775         return $filterData = Zend_Json::decode('[{"condition":"OR","filters":[{"condition":"AND","filters":'
776             . '[{"field":"start_date","operator":"within","value":"weekThis","id":"ext-record-1"},'
777             . '{"field":"account_id","operator":"equals","value":"' . $userId . '","id":"ext-record-2"}]'
778             . ',"id":"ext-comp-1076","label":"Stundenzettel"}]}]'
779         );
780     }
781     
782     /**
783     * try to search timesheets with or filter + acl filtering (should find only 1 ts)
784     * 
785     * @see 0005684: fix timesheet acl filtering
786     */
787     public function testSearchTimesheetsWithOrAndAclFilter()
788     {
789         // add another ts that does not match the filter
790         $timesheet = $this->_getTimesheet(array(
791             'start_date' => Tinebase_DateTime::now()->subWeek(2)->toString('Y-m-d')
792         ));
793         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
794         
795         Timetracker_ControllerTest::removeManageAllRight();
796         $this->testSearchTimesheetsWithOrFilter();
797     }
798
799     /**
800     * try to search timesheets of another user with account filter + acl filtering (should find 1 ts)
801     * 
802     * @see 0006244: user filter does not work
803     */
804     public function testSearchTimesheetsOfAnotherUser()
805     {
806         $taData = $this->_saveTimeaccountWithGrants();
807         $scleverId = Tinebase_User::getInstance()->getFullUserByLoginName('sclever')->getId();
808         
809         // add ts for sclever
810         $timesheet = $this->_getTimesheet(array(
811             'account_id'     => $scleverId,
812             'timeaccount_id' => $taData['id'],
813         ));
814         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
815     
816         Timetracker_ControllerTest::removeManageAllRight();
817         
818         $filterData = $this->_getTSFilterDataByUser($scleverId);
819         $search = $this->_json->searchTimesheets($filterData, array());
820         
821         $this->assertEquals(1, $search['totalcount']);
822         $this->assertEquals($scleverId, $search['results'][0]['account_id']['accountId']);
823     }
824     
825     /**
826      * testUpdateMultipleTimesheets
827      * 
828      * @group longrunning
829      * 
830      * @see 0005878: multi update timeout and strange behaviour (server)
831      */
832     public function testUpdateMultipleTimesheets()
833     {
834         // create 100+ timesheets
835         $first = $this->_getTimesheet(array(), TRUE);
836         for ($i = 0; $i < 122; $i++) {
837             $this->_getTimesheet(array(
838                 'timeaccount_id' => $first->timeaccount_id
839             ), TRUE);
840         }
841         
842         // update multi with filter
843         $filterArray = $this->_getTimesheetDateFilter();
844         $filterArray[] = array(
845             'field'     => 'is_cleared_combined',
846             'operator'  => 'equals',
847             'value'     => 0
848         );
849         $tinebaseJson = new Tinebase_Frontend_Json();
850         $result = $tinebaseJson->updateMultipleRecords('Timetracker', 'Timesheet', array(array('name' => 'is_cleared', 'value' => 1)), $filterArray);
851         
852         // check if all got updated
853         $this->assertEquals($result['totalcount'], 123);
854         $this->assertEquals($result['failcount'], 0);
855         $this->assertEquals(1, $result['results'][0]['is_cleared'], print_r($result['results'][0], TRUE));
856         $this->assertEquals(1, $result['results'][122]['is_cleared'], print_r($result['results'][122], TRUE));
857     }
858     
859     /**
860      * test if relation record gets deleted on both sides on deleting the relation on one side
861      */
862     public function testDeleteTimeaccountWitContractRelation()
863     {
864         $taContainer = Tinebase_Container::getInstance()->getDefaultContainer('Timetracker_Model_Timeaccount');
865         $cContainer  = Tinebase_Container::getInstance()->getDefaultContainer('Sales_Model_Contract');
866         $ta = new Timetracker_Model_Timeaccount(array('number' => 83209, 'title' => 'unitttest', 'container_id' => $taContainer->getId()));
867         
868         $contract = new Sales_Model_Contract(array('number' => 83209, 'title' => 'unittest', 'container_id' => $cContainer->getId()));
869         $contract = Sales_Controller_Contract::getInstance()->create($contract);
870         $ta = Timetracker_Controller_Timeaccount::getInstance()->create($ta);
871         
872         $r = new Tinebase_Model_Relation(array(
873             'own_model' => 'Timetracker_Model_Timeaccount',
874             'own_backend' => 'Sql',
875             'related_degree' => 'sibling',
876             'own_id' => $ta->getId(),
877             'remark' => 'PHP UNITTEST',
878             'related_model' => 'Sales_Model_Contract',
879             'related_backend' => 'Sql',
880             'related_id' => $contract->getId(),
881             'type' => 'CONTRACT'
882         ));
883         
884         $ta->relations = array($r);
885         
886         $ta = Timetracker_Controller_Timeaccount::getInstance()->update($ta);
887         
888         $feTa = new Timetracker_Frontend_Json();
889         $feCo = new Sales_Frontend_Json();
890         
891         $jsonTa = $feTa->getTimeaccount($ta->getId());
892         $jsonCo = $feCo->getContract($contract->getId());
893         
894         $this->assertEquals(1, count($jsonTa['relations']));
895         $this->assertEquals(1, count($jsonCo['relations']));
896         
897         $feTa->deleteTimeaccounts(array($ta->getId()));
898         
899         $jsonCo = $feCo->getContract($contract->getId());
900         $this->assertEquals(0, count($jsonCo['relations']));
901     }
902     
903     /**
904      * this test assures that relations, the user doesn't have the right to manage, won't be resolved anyway
905      */
906     public function testResolvingRelations()
907     {
908         $this->markTestSkipped('0010492: fix failing invoices and timetracker tests');
909         
910         $ta = $this->_getTimeaccount()->toArray();
911         $ta['grants'] = $this->_getGrants(TRUE);
912         
913         $contractController = Sales_Controller_Contract::getInstance();
914         $contactController  = Addressbook_Controller_Contact::getInstance();
915         $taController       = Timetracker_Controller_Timeaccount::getInstance();
916         
917         // fetch user group
918         $group   = Tinebase_Group::getInstance()->getDefaultGroup();
919         $groupId = $group->getId();
920         
921         // create new user
922         $user = new Tinebase_Model_FullUser(array(
923             'accountLoginName'      => 'testuser',
924             'accountPrimaryGroup'   => $groupId,
925             'accountDisplayName'    => 'Test User',
926             'accountLastName'       => 'User',
927             'accountFirstName'      => 'Test',
928             'accountFullName'       => 'Test User',
929             'accountEmailAddress'   => 'unittestx8@tine20.org',
930         ));
931         
932         $user = Admin_Controller_User::getInstance()->create($user, 'pw', 'pw');
933         
934         // add tt-ta admin right to user role to allow user to update (manage) timeaccounts
935         // user has no right to see sales contracts
936         $fe = new Admin_Frontend_Json();
937         $userRoles = $fe->getRoles('user', array(), array(), 0, 1);
938         $userRole = $fe->getRole($userRoles['results'][0]['id']);
939         
940         $roleRights = $fe->getRoleRights($userRole['id']);
941         $roleMembers = $fe->getRoleMembers($userRole['id']);
942         $roleMembers['results'][] = array('name' => 'testuser', 'type' => 'user', 'id' => $user->accountId);
943         
944         $app = Tinebase_Application::getInstance()->getApplicationByName('Timetracker');
945         
946         $roleRights['results'][] = array('application_id' => $app->getId(), 'right' => Timetracker_Acl_Rights::MANAGE_TIMEACCOUNTS);
947         $roleRights['results'][] = array('application_id' => $app->getId(), 'right' => Tinebase_Acl_Rights::ADMIN);
948         
949         $app = Tinebase_Application::getInstance()->getApplicationByName('Addressbook');
950         $roleRights['results'][] = array('application_id' => $app->getId(), 'right' => Tinebase_Acl_Rights::ADMIN);
951         
952         $fe->saveRole($userRole, $roleMembers['results'], $roleRights['results']);
953         
954         $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants');
955         $grants->addRecord(new Tinebase_Model_Grants(array(
956             'account_type' => 'user', 'account_id' => Tinebase_Core::getUser()->getId(),
957             Tinebase_Model_Grants::GRANT_READ      => true,
958             Tinebase_Model_Grants::GRANT_ADD       => true,
959             Tinebase_Model_Grants::GRANT_EDIT      => true,
960             Tinebase_Model_Grants::GRANT_DELETE    => true,
961         )));
962         $grants->addRecord(new Tinebase_Model_Grants(array(
963             'account_type' => 'user', 'account_id' => $user->getId(),
964             Tinebase_Model_Grants::GRANT_READ      => true,
965             Tinebase_Model_Grants::GRANT_ADD       => true,
966             Tinebase_Model_Grants::GRANT_EDIT      => true,
967             Tinebase_Model_Grants::GRANT_DELETE    => true,
968         )));
969         
970         $container = Tinebase_Container::getInstance()->addContainer(
971             new Tinebase_Model_Container(
972                 array(
973                     'name'           => 'shared',
974                     'type'           => Tinebase_Model_Container::TYPE_SHARED,
975                     'owner_id'       => $user,
976                     'backend'        => 'SQL',
977                     'application_id' => $app->getId(),
978                     'model'          => 'Addressbook_Model_Contact',
979                     'color'          => '#000000'
980                 )), $grants, TRUE
981         );
982         
983         // create timeaccount
984         $ta = $this->_json->saveTimeaccount($ta);
985         
986         $contact  = $contactController->create(new Addressbook_Model_Contact(array('container_id' => $container->getId(), 'n_given' => 'Test', 'n_family' => 'Unit')));
987         $contract = $contractController->create(new Sales_Model_Contract(array('number' => '123', 'title' => 'UnitTest')));
988         
989         Tinebase_Relations::getInstance()->setRelations('Timetracker_Model_Timeaccount', 'Sql', $ta['id'], array(
990             array('related_backend' => 'Sql', 'type' => 'RESPONSIBLE', 'related_model' => 'Addressbook_Model_Contact', 'related_id' => $contact->getId(), 'related_degree' => 'sibling'),
991             array('related_backend' => 'Sql', 'type' => 'TIME_ACCOUNT', 'related_model' => 'Sales_Model_Contract', 'related_id' => $contract->getId(), 'related_degree' => 'sibling'),
992         ));
993         
994         // add 2 relations
995         $ta = $this->_json->getTimeaccount($ta['id']);
996         $this->assertEquals(2, count($ta['relations']));
997
998         // switch to other user
999         $this->_testUser = Tinebase_Core::getUser();
1000         Tinebase_Core::set(Tinebase_Core::USER, $user);
1001         
1002         // get sure the user doesn't get relations not having the right for
1003         $ta = $this->_json->getTimeaccount($ta['id']);
1004         $this->assertEquals(1, count($ta['relations']), 'user should exactly get one related contact: ' . print_r($ta['relations'], true));
1005         
1006         // save timeaccount with reduced relations
1007         $ta = $this->_json->saveTimeaccount($ta);
1008         
1009         // switch user back
1010         Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
1011         
1012         // get sure all relations will be returned
1013         $ta = $this->_json->getTimeaccount($ta['id']);
1014         $this->assertEquals(2, count($ta['relations']));
1015     }
1016     
1017     /**
1018      * on saving 
1019      */
1020     public function testUpdateTimeaccountWithRelatedContact()
1021     {
1022         $this->_getTimeaccount(array(), TRUE);
1023         $ta = $this->_lastCreatedRecord;
1024         
1025         $contactController  = Addressbook_Controller_Contact::getInstance();
1026         $taController       = Timetracker_Controller_Timeaccount::getInstance();
1027         
1028         $bday = new Tinebase_DateTime();
1029         $bday->setDate(2013, 12, 24);
1030         $bday->setTime(0,0,0);
1031         
1032         $contact  = $contactController->create(new Addressbook_Model_Contact(array('n_given' => 'Test', 'n_family' => 'Unit', 'bday' => $bday)));
1033         $bday = $contact['bday'];
1034         
1035         Tinebase_Relations::getInstance()->setRelations('Timetracker_Model_Timeaccount', 'Sql', $ta['id'], array(
1036             array('related_backend' => 'Sql', 'type' => 'RESPONSIBLE', 'related_model' => 'Addressbook_Model_Contact', 'related_id' => $contact->getId(), 'related_degree' => 'sibling'),
1037         ));
1038         
1039         // update a few times, bday of contract should not change
1040         $tajson = $this->_json->getTimeaccount($ta['id']);
1041         $this->_json->saveTimeaccount($tajson);
1042         $tajson = $this->_json->getTimeaccount($ta['id']);
1043         $this->_json->saveTimeaccount($tajson);
1044         $tajson = $this->_json->getTimeaccount($ta['id']);
1045         
1046         $ajson = new Addressbook_Frontend_Json();
1047         $contactJson = $ajson->getContact($contact->getId());
1048         
1049         $this->assertEquals($bday->setTimezone(Tinebase_Core::getUserTimezone())->toString(), $contactJson['bday']);
1050     }
1051
1052     /**
1053      * here we search for all timeaccounts, which are related to an contract with a special
1054      * internal contact assigned
1055      * 
1056      * @see: 0009752: create contract - internal/external contact person filter
1057      */
1058     public function testTimeaccountContractInternalContactFilter()
1059     {
1060         $this->markTestSkipped('0010492: fix failing invoices and timetracker tests');
1061         
1062         $this->_getTimeaccount(array('title' => 'to find'), true);
1063         
1064         $taController = Timetracker_Controller_Timeaccount::getInstance();
1065         $taToFind = $taController->get($this->_lastCreatedRecord['id']);
1066         
1067         $this->_getTimeaccount(array('title' => 'not to find'), true);
1068         
1069         $contact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array('n_family' => 'Green')));
1070         
1071         $contractController = Sales_Controller_Contract::getInstance();
1072         
1073         $contract = new Sales_Model_Contract(array('title' => 'xtestunit', 'description' => 'nothing'));
1074         $contract = $contractController->create($contract);
1075         
1076         $contract->relations = array(new Tinebase_Model_Relation(array(
1077                 'own_backend' => 'Sql',
1078                 'own_id' => $contract->getId(),
1079                 'own_model' => 'Sales_Model_Contract',
1080                 'related_degree' => 'sibling',
1081                 'remark' => 'PHP UNITTEST',
1082                 'related_model' => 'Addressbook_Model_Contact',
1083                 'related_backend' => 'Sql',
1084                 'related_id' => $contact->getId(),
1085                 'type' => 'RESPONSIBLE'
1086         ))); 
1087         
1088         $contract = $contractController->update($contract);
1089         
1090         $taToFind->relations = array(
1091             new Tinebase_Model_Relation(array(
1092                 'own_backend' => 'Sql',
1093                 'related_degree' => 'sibling',
1094                 'own_id' => $taToFind->getId(),
1095                 'own_model' => 'Timetracker_Model_Timeaccount',
1096                 'remark' => 'PHP UNITTEST',
1097                 'related_model' => 'Sales_Model_Contract',
1098                 'related_backend' => 'Sql',
1099                 'related_id' => $contract->getId(),
1100                 'type' => 'CONTRACT'
1101             ))
1102         );
1103         
1104         
1105         $taToFind = $taController->update($taToFind);
1106         
1107         // build request with direct id
1108         $req = Zend_Json::decode('{"params":{"filter":[{"condition":"OR","filters":[{"condition":"AND","filters":
1109             [{"field":"contract","operator":"AND","value":[{"field":"contact_external","operator":"AND","value":
1110                 [{"field":":id","operator":"equals","value":"' . $contact->getId() . '"}],"id":"ext-record-266"},
1111                  {"field":":id","operator":"AND"}],"id":"ext-record-181"}],"id":"ext-comp-1350","label":"Zeitkonten"}]}],
1112             "paging":{"sort":"creation_time","dir":"DESC","start":0,"limit":50}}}');
1113         
1114         $filter = $req['params']['filter'];
1115         $paging = $req['params']['paging'];
1116         
1117         $result = $this->_json->searchTimeaccounts($filter, $paging);
1118         
1119         $this->assertEquals(1, $result['totalcount']);
1120         $this->assertEquals($taToFind->getId(), $result['results'][0]['id']);
1121         
1122         // build request with query=Green
1123         $req = Zend_Json::decode('{"jsonrpc":"2.0","method":"Timetracker.searchTimeaccounts","params":{"filter":[{"condition":"OR","filters":[{"condition":"AND","filters":[{"field":"contract","operator":"AND","value":[{"field":"foreignRecord","operator":"AND","value":{"appName":"Addressbook","modelName":"Contact","linkType":"relation","filters":[{"field":"query","operator":"contains","value":"Green","id":"ext-record-546"}]},"id":"ext-record-480"},{"field":":id","operator":"AND"}],"id":"ext-record-181"}],"id":"ext-comp-1350","label":"Zeitkonten"}]}],"paging":{"sort":"creation_time","dir":"DESC","start":0,"limit":50}},"id":62}');
1124         
1125         $filter = $req['params']['filter'];
1126         $paging = $req['params']['paging'];
1127         
1128         $result = $this->_json->searchTimeaccounts($filter, $paging);
1129         
1130         $this->assertEquals(1, $result['totalcount']);
1131         $this->assertEquals($taToFind->getId(), $result['results'][0]['id']);
1132     }
1133     
1134     /**
1135      * test if a user, who has no manage_invoices - right, is able tosave a timeaccount having an invoice linked
1136      */
1137     public function testUpdateInvoiceLinkedTimeaccount()
1138     {
1139         $this->markTestSkipped('0010492: fix failing invoices and timetracker tests');
1140         
1141         $ta = $this->_getTimeaccount(array('title' => 'to find'), true);
1142         $cc = Sales_Controller_CostCenter::getInstance()->create(new Sales_Model_CostCenter(array('number' => 1, 'title' => 'test')));
1143         
1144         $customer = Sales_Controller_Customer::getInstance()->create(new Sales_Model_Customer(array(
1145             'number' => 100,
1146             'name' => 'test',
1147             'description' => 'unittest',
1148             'credit_term' => 1
1149         )));
1150         
1151         $address = Sales_Controller_Address::getInstance()->create(new Sales_Model_Address(array(
1152             'street' => 'teststreet',
1153             'locality' => 'testcity',
1154             'customer_id' => $customer->id,
1155             'postalcode' => 12345
1156         )));
1157         
1158         $invoice = Sales_Controller_Invoice::getInstance()->create(new Sales_Model_Invoice(array(
1159             'description' => 'test',
1160             'address_id' => $address->id,
1161             'date' => Tinebase_DateTime::now(),
1162             'credit_term' => 1,
1163             'type' => 'INVOICE',
1164             'start_date' => Tinebase_DateTime::now(),
1165             'end_date' => Tinebase_DateTime::now()->addMonth(1),
1166             'costcenter_id' => $cc->id
1167         )));
1168         
1169         Tinebase_Relations::getInstance()->setRelations('Sales_Model_Invoice', 'Sql', $invoice->id, array(array(
1170             'related_id' => $ta->id,
1171             'related_model' => 'Timetracker_Model_Timeaccount',
1172             'related_record' => $ta,
1173             'related_degree' => 'sibling',
1174             'type' => 'INVOICE'
1175         )));
1176         
1177         // fetch user group 
1178         $group   = Tinebase_Group::getInstance()->getDefaultGroup();
1179         $groupId = $group->getId();
1180         
1181         // create new user 
1182         $user = new Tinebase_Model_FullUser(array(
1183             'accountLoginName'      => 'testuser',
1184             'accountPrimaryGroup'   => $groupId,
1185             'accountDisplayName'    => 'Test User',
1186             'accountLastName'       => 'User',
1187             'accountFirstName'      => 'Test',
1188             'accountFullName'       => 'Test User',
1189             'accountEmailAddress'   => 'unittestx8@tine20.org',
1190         ));
1191         
1192         $user = Admin_Controller_User::getInstance()->create($user, 'pw', 'pw');
1193
1194         // add tt-ta admin right to user role to allow user to update (manage) timeaccounts
1195         // user has no right to see sales contracts
1196         $fe = new Admin_Frontend_Json();
1197         $userRoles = $fe->getRoles('user', array(), array(), 0, 1);
1198         $userRole = $fe->getRole($userRoles['results'][0]['id']);
1199         
1200         $roleRights = $fe->getRoleRights($userRole['id']);
1201         $roleMembers = $fe->getRoleMembers($userRole['id']);
1202         $roleMembers['results'][] = array('name' => 'testuser', 'type' => 'user', 'id' => $user->accountId);
1203         
1204         $app = Tinebase_Application::getInstance()->getApplicationByName('Timetracker');
1205         
1206         $roleRights['results'][] = array('application_id' => $app->getId(), 'right' => Timetracker_Acl_Rights::MANAGE_TIMEACCOUNTS);
1207         $roleRights['results'][] = array('application_id' => $app->getId(), 'right' => Tinebase_Acl_Rights::ADMIN);
1208         $fe->saveRole($userRole, $roleMembers['results'], $roleRights['results']);
1209         
1210         // switch to other user
1211         $this->_testUser = Tinebase_Core::getUser();
1212         Tinebase_Core::set(Tinebase_Core::USER, $user);
1213         
1214         $ta = $this->_json->getTimeaccount($ta->id);
1215         $this->assertTrue(empty($ta['relations']), 'relations are not empty: ' . print_r($ta['relations'], true));
1216         
1217         // this must be possible
1218         $ta = $this->_json->saveTimeaccount($ta);
1219         
1220         Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
1221         
1222         $ta = $this->_json->getTimeaccount($ta['id']);
1223         $this->assertTrue(count($ta['relations']) == 1);
1224     }
1225     
1226     /**
1227      * try to add a Timesheet
1228      */
1229     public function testTimesheetInvoiceId()
1230     {
1231         $timesheet = $this->_getTimesheet();
1232         $tsData = $timesheet->toArray();
1233         $tsData['invoice_id'] = '';
1234         $tsData = $this->_json->saveTimesheet($tsData);
1235         $this->assertSame(NULL,  $tsData['invoice_id']);
1236         $tsData = $this->_json->getTimesheet($tsData['id']);
1237         $this->assertSame(NULL,  $tsData['invoice_id']);
1238     }
1239     
1240     /**
1241      * try to update a Timesheet with a closed TimeAccount
1242      *
1243      */
1244     public function testUpdateClosedTimeaccount()
1245     {
1246         $timeaccountData = $this->_saveTimeaccountWithGrants();
1247         $timeaccountData['is_open'] = 0;
1248         $timeaccount = $this->_json->saveTimeaccount($timeaccountData);
1249         
1250         $timesheet = $this->_getTimesheet(array(
1251              'timeaccount_id'    => $timeaccount['id'],
1252         ));
1253         $timesheetData = $this->_json->saveTimesheet($timesheet->toArray(), array('skipClosedCheck' => true));
1254         
1255         Timetracker_ControllerTest::removeManageAllRight();
1256         
1257         $this->setExpectedException('Timetracker_Exception_ClosedTimeaccount');
1258         
1259         // update Timesheet
1260         $timesheetData['description'] = "blubbblubb";
1261         $timesheetData['account_id'] = $timesheetData['account_id']['accountId'];
1262         $timesheetData['timeaccount_id'] = $timesheetData['timeaccount_id']['id'];
1263         $timesheetUpdated = $this->_json->saveTimesheet($timesheetData);
1264     }
1265 }