3 * Tine 2.0 - http://www.tine20.org
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>
14 require_once dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php';
17 * Test class for Timetracker_Frontent_Json
19 class Timetracker_JsonTest extends Timetracker_AbstractTest
21 protected $_testUser = NULL;
24 * Sets up the fixture.
25 * This method is called before a test is executed.
29 protected function setUp()
35 * Tears down the fixture
36 * This method is called after a test is executed.
40 protected function tearDown()
42 // switch back to admin user
43 if ($this->_testUser) {
44 Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
51 * try to add a Timeaccount
54 public function testAddTimeaccount()
56 $timeaccount = $this->_getTimeaccount();
57 $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
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']));
67 $this->_json->deleteTimeaccounts($timeaccountData['id']);
69 // check if it got deleted
70 $this->setExpectedException('Tinebase_Exception_NotFound');
71 Timetracker_Controller_Timeaccount::getInstance()->get($timeaccountData['id']);
75 * try to get a Timeaccount
78 public function testGetTimeaccount()
80 $timeaccount = $this->_getTimeaccount();
81 $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
82 $timeaccountData = $this->_json->getTimeaccount($timeaccountData['id']);
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']);
91 $this->_json->deleteTimeaccounts($timeaccountData['id']);
95 * try to update a Timeaccount
98 public function testUpdateTimeaccount()
100 $timeaccount = $this->_getTimeaccount();
101 $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
103 // update Timeaccount
104 $timeaccountData['description'] = "blubbblubb";
105 $timeaccountUpdated = $this->_json->saveTimeaccount($timeaccountData);
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']);
113 $this->_json->deleteTimeaccounts($timeaccountData['id']);
117 * try to get a Timeaccount
119 public function testSearchTimeaccounts()
122 $timeaccount = $this->_getTimeaccount();
123 $timeaccount->is_open = 0;
124 $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
127 $timeaccountFilter = $this->_getTimeaccountFilter();
128 $search = $this->_json->searchTimeaccounts($timeaccountFilter, $this->_getPaging());
129 $this->assertEquals(0, $search['totalcount'], 'is_open filter not working');
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']);
137 * try to get a Timeaccount with a TA filter
139 * @see 0007946: error when searching for single timeaccount
141 public function testSearchTimeaccountsWithTAFilter()
143 $timeaccount = $this->_getTimeaccount();
144 $timeaccountData = $this->_json->saveTimeaccount($timeaccount->toArray());
148 "id": "ext-record-869",
150 "operator": "equals",
151 "value": "' . $timeaccountData['id'] . '"
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']);
168 * try to add a Timeaccount with grants
170 public function testAddTimeaccountWithGrants()
172 $grants = $this->_getGrants();
173 $timeaccountData = $this->_saveTimeaccountWithGrants($grants);
176 $this->assertGreaterThan(0, count($timeaccountData['grants']));
177 $this->assertEquals($grants[0]['account_type'], $timeaccountData['grants'][0]['account_type']);
180 $this->_json->deleteTimeaccounts($timeaccountData['id']);
182 // check if it got deleted
183 $this->setExpectedException('Tinebase_Exception_NotFound');
184 Timetracker_Controller_Timeaccount::getInstance()->get($timeaccountData['id']);
188 * save TA with grants
192 protected function _saveTimeaccountWithGrants($grants = NULL)
194 $timeaccount = $this->_getTimeaccount();
195 $timeaccountData = $timeaccount->toArray();
196 $timeaccountData['grants'] = ($grants !== NULL) ? $grants : $this->_getGrants();
197 return $this->_json->saveTimeaccount($timeaccountData);
201 * try to add a Timesheet
204 public function testAddTimesheet()
206 $timesheet = $this->_getTimesheet();
207 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
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']);
216 $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']['id']);
218 // check if everything got deleted
219 $this->setExpectedException('Tinebase_Exception_NotFound');
220 Timetracker_Controller_Timesheet::getInstance()->get($timesheetData['id']);
224 * try to add a Timesheet with custom fields
227 public function testAddTimesheetWithCustomFields()
231 $cf = $this->_getCustomField();
233 // create two timesheets with customfields
234 $this->_addTsWithCf($cf, $value1);
235 $this->_addTsWithCf($cf, $value2);
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');
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));
248 * search Timesheet with empty custom fields
250 public function testSearchTimesheetWithEmptyCustomField()
252 $cf = $this->_getCustomField();
254 $timesheet = $this->_getTimesheet();
255 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
257 $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
258 'field' => 'customfield',
259 'operator' => 'equals',
261 'cfId' => $cf->getId(),
264 )), $this->_getPaging());
265 $this->assertEquals(1, $search['totalcount']);
269 * try to add a Timesheet with custom fields (check grants)
271 public function testAddTimesheetWithCustomFieldGrants()
274 $cf = $this->_getCustomField();
276 $timesheetArray = $this->_getTimesheet()->toArray();
277 $timesheetArray[$cf->name] = $value;
278 $ts = $this->_json->saveTimesheet($timesheetArray);
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]);
285 Tinebase_CustomField::getInstance()->setGrants($cf, array());
286 $ts = $this->_json->getTimesheet($ts['id']);
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');
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');
301 * update timesheet customfields and return saved ts
304 * @param string $_cfName
305 * @param string $_cfValue
308 protected function _updateCfOfTs($_ts, $_cfName, $_cfValue)
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);
321 * this test is for Tinebase_Frontend_Json updateMultipleRecords with timesheet data in the timetracker app
323 public function testUpdateMultipleRecords()
325 $durations = array(75,90,105);
326 $timeAccount = $this->_getTimeaccount(array('description' => 'blablub'),true);
327 $lr = $this->_getLastCreatedRecord();
331 // create customfield
332 $cf = $this->_getCustomField()->toArray();
334 $changes = array( array('name' => 'duration', 'value' => '111'),
335 array('name' => 'description', 'value' => 'PHPUNIT_multipleUpdate'),
336 array('name' => 'customfield_' . $cf['name'], 'value' => 'PHPUNIT_multipleUpdate' )
339 foreach ($durations as $duration) {
340 $timeSheet = $this->_getTimesheet(array('timeaccount_id' => $taId, 'duration' => $duration),true);
341 $lr = $this->_getLastCreatedRecord();
342 $timesheetIds[] = $lr['id'];
345 $filter = array( array('field' => 'id', 'operator' => 'in', 'value' => $timesheetIds),
346 array('field' => 'account_id', 'operator' => 'equals', 'value' => Tinebase_Core::getUser()->getId())
349 $json = new Tinebase_Frontend_Json();
351 $result = $json->updateMultipleRecords('Timetracker', 'Timesheet', $changes, $filter);
353 // look if all 3 contacts are updated
354 $this->assertEquals(3, $result['totalcount'],'Could not update the correct number of records');
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())
360 $searchResult = $this->_json->searchTimesheets($sFilter,$this->_getPaging());
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');
365 $record = array_pop($searchResult['results']);
367 // check if customfieldvalue was updated properly
368 $this->assertEquals($record['customfields'][$cf['name']],'PHPUNIT_multipleUpdate','Customfield was not updated as expected');
370 // check if other default field value was updated properly
371 $this->assertEquals($record['duration'],'111','DefaultField "duration" was not updated as expected');
375 * try to get a Timesheet
378 public function testGetTimesheet()
380 $timesheet = $this->_getTimesheet();
381 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
382 $timesheetData = $this->_json->getTimesheet($timesheetData['id']);
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');
391 $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']['id']);
395 * try to update a Timesheet (with relations)
398 public function testUpdateTimesheet()
400 $timesheet = $this->_getTimesheet();
401 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
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'];
409 $timesheetUpdated = $this->_json->saveTimesheet($timesheetData);
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');
419 $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']);
423 * try to get a Timesheet
426 public function testDeleteTimesheet()
428 $timesheet = $this->_getTimesheet();
429 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
432 $this->_json->deleteTimesheets($timesheetData['id']);
434 $timesheets = Timetracker_Controller_Timesheet::getInstance()->getTimesheetsByTimeaccountId($timesheetData['timeaccount_id']['id']);
437 $this->assertEquals(0, count($timesheets));
440 $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']['id']);
444 * try to search for Timesheets
447 public function testSearchTimesheets()
450 $timesheet = $this->_getTimesheet();
452 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
455 $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(), $this->_getPaging());
456 $this->assertEquals($timesheet->description, $search['results'][0]['description']);
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');
461 $this->assertEquals(1, $search['totalcount']);
462 $this->assertEquals(1, count($search['results']));
463 $this->assertEquals(30, $search['totalsum'], 'totalsum mismatch');
467 * try to search for Timesheets with date filtering (using 'weekThis' filter)
470 public function testSearchTimesheetsWithDateFilterWeekThis()
472 $this->_dateFilterTest();
476 * try to search for Timesheets with date filtering (using inweek operator)
479 public function testSearchTimesheetsWithDateFilterInWeek()
481 $this->_dateFilterTest('inweek');
485 * try to search for Timesheets with date filtering (using monthLast operator)
487 public function testSearchTimesheetsWithDateMonthLast()
489 $today = Tinebase_DateTime::now();
490 $lastMonth = $today->setDate($today->get('Y'), $today->get('m') - 1, 1);
491 $this->_createTsAndSearch($lastMonth, 'monthLast');
495 * date filter test helper
497 * @param string $_type weekThis|inweek|monthLast
499 protected function _dateFilterTest($_type = 'weekThis')
501 $oldLocale = Tinebase_Core::getLocale();
502 Tinebase_Core::set(Tinebase_Core::LOCALE, new Zend_Locale('en_US'));
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);
509 $this->_createTsAndSearch($lastSunday, $_type);
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');
517 Tinebase_Core::set(Tinebase_Core::LOCALE, $oldLocale);
521 * create timesheet and search with filter
523 * @param Tinebase_DateTime $_startDate
524 * @param string $_filterType
526 protected function _createTsAndSearch($_startDate, $_filterType)
528 //$timesheet = $this->_getTimesheet(NULL, $_startDate);
529 $timesheet = $this->_getTimesheet(array('timeaccount_id' => null, 'start_date' => $_startDate));
530 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
532 $result = $this->_json->searchTimesheets($this->_getTimesheetDateFilter($_filterType), $this->_getPaging());
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');
541 * try to search for Timesheets (with combined is_billable + cleared)
543 public function testSearchTimesheetsWithCombinedIsBillableAndCleared()
546 $timesheet = $this->_getTimesheet();
547 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
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);
555 $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
556 'field' => 'is_cleared_combined',
557 'operator' => 'equals',
559 )), $this->_getPaging('is_billable_combined'));
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']);
568 // search again with is_billable filter
569 $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(array(
570 'field' => 'is_billable_combined',
571 'operator' => 'equals',
573 )), $this->_getPaging('is_billable_combined'));
574 $this->assertEquals(0, $search['results'][0]['is_billable_combined'], 'is_billable_combined mismatch');
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',
581 )), $this->_getPaging());
582 $this->assertEquals(0, $search['results'][0]['is_billable_combined'], 'is_billable_combined mismatch');
586 * testSearchTimesheetsSumBillable
588 public function testSearchTimesheetsSumBillable()
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());
597 $search = $search = $this->_json->searchTimesheets($this->_getTimesheetFilter(), $this->_getPaging());
598 $this->assertEquals(60, $search['totalsum']);
599 $this->assertEquals(30, $search['totalsumbillable']);
603 * try to save and search persistent filter
605 * @todo move this test to tinebase json tests?
607 public function testSavePersistentTimesheetFilter()
609 $persistentFiltersJson = new Tinebase_Frontend_Json_PersistentFilter();
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'
621 $persistentFilters = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
622 //print_r($persistentFilters);
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());
630 // cleanup / delete file
631 $persistentFiltersJson->deletePersistentFilters($persistentFilters['results'][0]['id']);
635 * try to save/update and search persistent filter
637 * @todo move this test to tinebase json tests?
639 public function testUpdatePersistentTimesheetFilter()
641 $persistentFiltersJson = new Tinebase_Frontend_Json_PersistentFilter();
642 $tsFilter = $this->_getTimesheetFilter();
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'
653 $persistentFilters = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
656 $updatedFilter = $persistentFilters['results'][0];
657 $updatedFilter[0]['value'] = 'blubb';
658 $persistentFiltersJson->savePersistentFilter($updatedFilter);
661 $persistentFiltersUpdated = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
662 //print_r($persistentFiltersUpdated);
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']);
671 // cleanup / delete file
672 $persistentFiltersJson->deletePersistentFilters($persistentFiltersUpdated['results'][0]['id']);
676 * try to search timesheets with saved persistent filter id
678 * @todo move this test to tinebase json tests?
680 public function testSearchTimesheetsWithPersistentFilter()
682 $persistentFiltersJson = new Tinebase_Frontend_Json_PersistentFilter();
683 $tsFilter = $this->_getTimesheetFilter();
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'
693 $timesheet = $this->_getTimesheet();
694 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
696 // search persistent filter
697 $persistentFilters = $persistentFiltersJson->searchPersistentFilter($this->_getPersistentFilterFilter($filterName), NULL);
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');
707 // cleanup / delete file
708 $persistentFiltersJson->deletePersistentFilters($persistentFilters['results'][0]['id']);
712 * create timesheet and search with explicite foreign filter
714 public function testSearchWithExpliciteForeignIdFilter()
716 $timesheet = $this->_getTimesheet();
717 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
720 array('field' => 'timeaccount_id', 'operator' => 'AND', 'value' => array(
721 array('field' => 'id', 'operator' => 'equals', 'value' => $timesheetData['timeaccount_id']['id'])
725 $result = $this->_json->searchTimesheets($filter, $this->_getPaging());
727 $this->assertEquals(1, $result['totalcount'], 'timesheet not found with ExpliciteForeignIdFilter filter');
731 * create timesheet and search with explicite foreign left hand filter
733 public function testSearchWithExpliciteForeignIdLeftFilter()
735 $timesheet = $this->_getTimesheet();
736 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
738 $anotherTimesheet = $this->_getTimesheet();
739 $anotherTimesheetData = $this->_json->saveTimesheet($anotherTimesheet->toArray());
742 array('field' => 'timeaccount_id', 'operator' => 'AND', 'value' => array(
743 array('field' => ':id', 'operator' => 'equals', 'value' => $timesheetData['timeaccount_id']['id'])
747 $result = $this->_json->searchTimesheets($filter, $this->_getPaging());
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');
755 * try to search timesheets with or filter
757 public function testSearchTimesheetsWithOrFilter()
759 $timesheet = $this->_getTimesheet();
760 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
762 $filterData = $this->_getTSFilterDataByUser(Tinebase_Core::getUser()->getId());
763 $search = $this->_json->searchTimesheets($filterData, array());
764 $this->assertEquals(1, $search['totalcount']);
768 * get ts filter array by user
770 * @param string $userId
773 protected function _getTSFilterDataByUser($userId)
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"}]}]'
783 * try to search timesheets with or filter + acl filtering (should find only 1 ts)
785 * @see 0005684: fix timesheet acl filtering
787 public function testSearchTimesheetsWithOrAndAclFilter()
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')
793 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
795 Timetracker_ControllerTest::removeManageAllRight();
796 $this->testSearchTimesheetsWithOrFilter();
800 * try to search timesheets of another user with account filter + acl filtering (should find 1 ts)
802 * @see 0006244: user filter does not work
804 public function testSearchTimesheetsOfAnotherUser()
806 $taData = $this->_saveTimeaccountWithGrants();
807 $scleverId = Tinebase_User::getInstance()->getFullUserByLoginName('sclever')->getId();
809 // add ts for sclever
810 $timesheet = $this->_getTimesheet(array(
811 'account_id' => $scleverId,
812 'timeaccount_id' => $taData['id'],
814 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
816 Timetracker_ControllerTest::removeManageAllRight();
818 $filterData = $this->_getTSFilterDataByUser($scleverId);
819 $search = $this->_json->searchTimesheets($filterData, array());
821 $this->assertEquals(1, $search['totalcount']);
822 $this->assertEquals($scleverId, $search['results'][0]['account_id']['accountId']);
826 * testUpdateMultipleTimesheets
830 * @see 0005878: multi update timeout and strange behaviour (server)
832 public function testUpdateMultipleTimesheets()
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
842 // update multi with filter
843 $filterArray = $this->_getTimesheetDateFilter();
844 $filterArray[] = array(
845 'field' => 'is_cleared_combined',
846 'operator' => 'equals',
849 $tinebaseJson = new Tinebase_Frontend_Json();
850 $result = $tinebaseJson->updateMultipleRecords('Timetracker', 'Timesheet', array(array('name' => 'is_cleared', 'value' => 1)), $filterArray);
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));
860 * test if relation record gets deleted on both sides on deleting the relation on one side
862 public function testDeleteTimeaccountWitContractRelation()
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()));
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);
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(),
884 $ta->relations = array($r);
886 $ta = Timetracker_Controller_Timeaccount::getInstance()->update($ta);
888 $feTa = new Timetracker_Frontend_Json();
889 $feCo = new Sales_Frontend_Json();
891 $jsonTa = $feTa->getTimeaccount($ta->getId());
892 $jsonCo = $feCo->getContract($contract->getId());
894 $this->assertEquals(1, count($jsonTa['relations']));
895 $this->assertEquals(1, count($jsonCo['relations']));
897 $feTa->deleteTimeaccounts(array($ta->getId()));
899 $jsonCo = $feCo->getContract($contract->getId());
900 $this->assertEquals(0, count($jsonCo['relations']));
904 * this test assures that relations, the user doesn't have the right to manage, won't be resolved anyway
906 public function testResolvingRelations()
908 $this->markTestSkipped('0010492: fix failing invoices and timetracker tests');
910 $ta = $this->_getTimeaccount()->toArray();
911 $ta['grants'] = $this->_getGrants(TRUE);
913 $contractController = Sales_Controller_Contract::getInstance();
914 $contactController = Addressbook_Controller_Contact::getInstance();
915 $taController = Timetracker_Controller_Timeaccount::getInstance();
918 $group = Tinebase_Group::getInstance()->getDefaultGroup();
919 $groupId = $group->getId();
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',
932 $user = Admin_Controller_User::getInstance()->create($user, 'pw', 'pw');
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']);
940 $roleRights = $fe->getRoleRights($userRole['id']);
941 $roleMembers = $fe->getRoleMembers($userRole['id']);
942 $roleMembers['results'][] = array('name' => 'testuser', 'type' => 'user', 'id' => $user->accountId);
944 $app = Tinebase_Application::getInstance()->getApplicationByName('Timetracker');
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);
949 $app = Tinebase_Application::getInstance()->getApplicationByName('Addressbook');
950 $roleRights['results'][] = array('application_id' => $app->getId(), 'right' => Tinebase_Acl_Rights::ADMIN);
952 $fe->saveRole($userRole, $roleMembers['results'], $roleRights['results']);
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,
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,
970 $container = Tinebase_Container::getInstance()->addContainer(
971 new Tinebase_Model_Container(
974 'type' => Tinebase_Model_Container::TYPE_SHARED,
977 'application_id' => $app->getId(),
978 'model' => 'Addressbook_Model_Contact',
983 // create timeaccount
984 $ta = $this->_json->saveTimeaccount($ta);
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')));
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'),
995 $ta = $this->_json->getTimeaccount($ta['id']);
996 $this->assertEquals(2, count($ta['relations']));
998 // switch to other user
999 $this->_testUser = Tinebase_Core::getUser();
1000 Tinebase_Core::set(Tinebase_Core::USER, $user);
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));
1006 // save timeaccount with reduced relations
1007 $ta = $this->_json->saveTimeaccount($ta);
1010 Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
1012 // get sure all relations will be returned
1013 $ta = $this->_json->getTimeaccount($ta['id']);
1014 $this->assertEquals(2, count($ta['relations']));
1020 public function testUpdateTimeaccountWithRelatedContact()
1022 $this->_getTimeaccount(array(), TRUE);
1023 $ta = $this->_lastCreatedRecord;
1025 $contactController = Addressbook_Controller_Contact::getInstance();
1026 $taController = Timetracker_Controller_Timeaccount::getInstance();
1028 $bday = new Tinebase_DateTime();
1029 $bday->setDate(2013, 12, 24);
1030 $bday->setTime(0,0,0);
1032 $contact = $contactController->create(new Addressbook_Model_Contact(array('n_given' => 'Test', 'n_family' => 'Unit', 'bday' => $bday)));
1033 $bday = $contact['bday'];
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'),
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']);
1046 $ajson = new Addressbook_Frontend_Json();
1047 $contactJson = $ajson->getContact($contact->getId());
1049 $this->assertEquals($bday->setTimezone(Tinebase_Core::getUserTimezone())->toString(), $contactJson['bday']);
1053 * here we search for all timeaccounts, which are related to an contract with a special
1054 * internal contact assigned
1056 * @see: 0009752: create contract - internal/external contact person filter
1058 public function testTimeaccountContractInternalContactFilter()
1060 $this->markTestSkipped('0010492: fix failing invoices and timetracker tests');
1062 $this->_getTimeaccount(array('title' => 'to find'), true);
1064 $taController = Timetracker_Controller_Timeaccount::getInstance();
1065 $taToFind = $taController->get($this->_lastCreatedRecord['id']);
1067 $this->_getTimeaccount(array('title' => 'not to find'), true);
1069 $contact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array('n_family' => 'Green')));
1071 $contractController = Sales_Controller_Contract::getInstance();
1073 $contract = new Sales_Model_Contract(array('title' => 'xtestunit', 'description' => 'nothing'));
1074 $contract = $contractController->create($contract);
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'
1088 $contract = $contractController->update($contract);
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'
1105 $taToFind = $taController->update($taToFind);
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}}}');
1114 $filter = $req['params']['filter'];
1115 $paging = $req['params']['paging'];
1117 $result = $this->_json->searchTimeaccounts($filter, $paging);
1119 $this->assertEquals(1, $result['totalcount']);
1120 $this->assertEquals($taToFind->getId(), $result['results'][0]['id']);
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}');
1125 $filter = $req['params']['filter'];
1126 $paging = $req['params']['paging'];
1128 $result = $this->_json->searchTimeaccounts($filter, $paging);
1130 $this->assertEquals(1, $result['totalcount']);
1131 $this->assertEquals($taToFind->getId(), $result['results'][0]['id']);
1135 * test if a user, who has no manage_invoices - right, is able tosave a timeaccount having an invoice linked
1137 public function testUpdateInvoiceLinkedTimeaccount()
1139 $this->markTestSkipped('0010492: fix failing invoices and timetracker tests');
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')));
1144 $customer = Sales_Controller_Customer::getInstance()->create(new Sales_Model_Customer(array(
1147 'description' => 'unittest',
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
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(),
1163 'type' => 'INVOICE',
1164 'start_date' => Tinebase_DateTime::now(),
1165 'end_date' => Tinebase_DateTime::now()->addMonth(1),
1166 'costcenter_id' => $cc->id
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',
1178 $group = Tinebase_Group::getInstance()->getDefaultGroup();
1179 $groupId = $group->getId();
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',
1192 $user = Admin_Controller_User::getInstance()->create($user, 'pw', 'pw');
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']);
1200 $roleRights = $fe->getRoleRights($userRole['id']);
1201 $roleMembers = $fe->getRoleMembers($userRole['id']);
1202 $roleMembers['results'][] = array('name' => 'testuser', 'type' => 'user', 'id' => $user->accountId);
1204 $app = Tinebase_Application::getInstance()->getApplicationByName('Timetracker');
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']);
1210 // switch to other user
1211 $this->_testUser = Tinebase_Core::getUser();
1212 Tinebase_Core::set(Tinebase_Core::USER, $user);
1214 $ta = $this->_json->getTimeaccount($ta->id);
1215 $this->assertTrue(empty($ta['relations']), 'relations are not empty: ' . print_r($ta['relations'], true));
1217 // this must be possible
1218 $ta = $this->_json->saveTimeaccount($ta);
1220 Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
1222 $ta = $this->_json->getTimeaccount($ta['id']);
1223 $this->assertTrue(count($ta['relations']) == 1);
1227 * try to add a Timesheet
1229 public function testTimesheetInvoiceId()
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']);
1241 * try to update a Timesheet with a closed TimeAccount
1244 public function testUpdateClosedTimeaccount()
1246 $timeaccountData = $this->_saveTimeaccountWithGrants();
1247 $timeaccountData['is_open'] = 0;
1248 $timeaccount = $this->_json->saveTimeaccount($timeaccountData);
1250 $timesheet = $this->_getTimesheet(array(
1251 'timeaccount_id' => $timeaccount['id'],
1253 $timesheetData = $this->_json->saveTimesheet($timesheet->toArray(), array('skipClosedCheck' => true));
1255 Timetracker_ControllerTest::removeManageAllRight();
1257 $this->setExpectedException('Timetracker_Exception_ClosedTimeaccount');
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);