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