0009768: Use ModelConfig for Timetracker models
authorPaul Mehrer <p.mehrer@metaways.de>
Wed, 25 May 2016 14:30:58 +0000 (16:30 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Wed, 10 Aug 2016 16:10:03 +0000 (18:10 +0200)
https://forge.tine20.org/view.php?id=9768

Change-Id: I8e5d3c77a8c8ba61d5f8e4015a62fe0a1fb24e82
Reviewed-on: http://gerrit.tine20.com/customers/3178
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
25 files changed:
tests/tine20/Timetracker/ControllerTest.php
tests/tine20/Timetracker/FilterTest.php
tests/tine20/Timetracker/JsonTest.php
tine20/Timetracker/Controller.php
tine20/Timetracker/Controller/Timeaccount.php
tine20/Timetracker/Controller/Timesheet.php
tine20/Timetracker/Convert/Timeaccount/Json.php [new file with mode: 0644]
tine20/Timetracker/Frontend/Json.php
tine20/Timetracker/Model/Timeaccount.php
tine20/Timetracker/Model/TimeaccountFilter.php
tine20/Timetracker/Model/Timesheet.php
tine20/Timetracker/Model/TimesheetFilter.php
tine20/Timetracker/Setup/DemoData.php
tine20/Timetracker/Timetracker.jsb2
tine20/Timetracker/js/Models.js
tine20/Timetracker/js/TimeAccountBilledFilterModel.js
tine20/Timetracker/js/TimeAccountFilterModel.js
tine20/Timetracker/js/TimeaccountEditDialog.js
tine20/Timetracker/js/TimeaccountGridPanel.js
tine20/Timetracker/js/TimeaccountResponsibleFilterModel.js [new file with mode: 0644]
tine20/Timetracker/js/TimesheetEditDialog.js
tine20/Timetracker/js/TimesheetGridPanel.js
tine20/Timetracker/js/Timetracker.js
tine20/Timetracker/translations/de.po
tine20/Tinebase/js/widgets/dialog/EditDialog.js

index c68bd19..e09f979 100644 (file)
@@ -277,6 +277,15 @@ class Timetracker_ControllerTest extends TestCase
         )));
         
         $this->_grantTestHelper($grants, 'searchTS', 1);
+
+        $filter = $this->_getTimesheetFilter();
+
+               $be = new Timetracker_Backend_Timesheet();
+        $result = $be->search($filter)->toArray();
+        $this->assertArrayHasKey('is_billable_combined', $result[0]);
+
+        $result = $this->_timesheetController->search($filter)->toArray();
+        $this->assertArrayHasKey('is_billable_combined', $result[0]);
     }
 
     /**
@@ -423,7 +432,8 @@ class Timetracker_ControllerTest extends TestCase
             'account_id'        => Tinebase_Core::getUser()->getId(),
             'timeaccount_id'    => $timeaccount->getId(),
             'description'       => 'blabla',
-            'start_date'        => Tinebase_DateTime::now()->toString('Y-m-d')
+            'start_date'        => Tinebase_DateTime::now()->toString('Y-m-d'),
+            'duration'          => 30,
         ), TRUE);
     }
 
index 0ff0a5c..39726d2 100644 (file)
@@ -164,6 +164,7 @@ class Timetracker_FilterTest extends Timetracker_AbstractTest
             'account_id' => Tinebase_Core::getUser()->getId(),
             'description' => 'lazy boring',
             'start_date' => $date,
+            'duration' => 30,
         ));
         
         $r->setTimezone('UTC');
index 7825e9d..177eea9 100644 (file)
@@ -58,7 +58,7 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
 
         // checks
         $this->assertEquals($timeaccount->description, $timeaccountData['description']);
-        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountData['created_by']);
+        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountData['created_by']['accountId']);
         $this->assertTrue(is_array($timeaccountData['container_id']));
         $this->assertEquals(Tinebase_Model_Container::TYPE_SHARED, $timeaccountData['container_id']['type']);
         $this->assertGreaterThan(0, count($timeaccountData['grants']));
@@ -83,7 +83,7 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
 
         // checks
         $this->assertEquals($timeaccount->description, $timeaccountData['description']);
-        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountData['created_by']);
+        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountData['created_by']['accountId']);
         $this->assertTrue(is_array($timeaccountData['container_id']));
         $this->assertEquals(Tinebase_Model_Container::TYPE_SHARED, $timeaccountData['container_id']['type']);
 
@@ -107,7 +107,7 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         // check
         $this->assertEquals($timeaccountData['id'], $timeaccountUpdated['id']);
         $this->assertEquals($timeaccountData['description'], $timeaccountUpdated['description']);
-        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountUpdated['last_modified_by']);
+        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timeaccountUpdated['last_modified_by']['accountId']);
 
         // cleanup
         $this->_json->deleteTimeaccounts($timeaccountData['id']);
@@ -206,9 +206,9 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
 
         // checks
         $this->assertEquals($timesheet->description, $timesheetData['description']);
-        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['created_by']);
+        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['created_by']['accountId']);
         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['account_id']['accountId'], 'account is not resolved');
-        $this->assertEquals(Tinebase_DateTime::now()->toString('Y-m-d'),  $timesheetData['start_date']);
+        $this->assertEquals(Tinebase_DateTime::now()->toString('Y-m-d') . ' 00:00:00',  $timesheetData['start_date']);
 
         // cleanup
         $this->_json->deleteTimeaccounts($timesheetData['timeaccount_id']['id']);
@@ -381,7 +381,7 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
 
         // checks
         $this->assertEquals($timesheet->description, $timesheetData['description']);
-        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['created_by']);
+        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['created_by']['accountId']);
         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetData['account_id']['accountId'], 'account is not resolved');
         $this->assertEquals($timesheet['timeaccount_id'], $timesheetData['timeaccount_id']['id'], 'timeaccount is not resolved');
 
@@ -409,7 +409,7 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         // check
         $this->assertEquals($timesheetData['id'], $timesheetUpdated['id']);
         $this->assertEquals($timesheetData['description'], $timesheetUpdated['description']);
-        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetUpdated['last_modified_by']);
+        $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetUpdated['last_modified_by']['accountId']);
         $this->assertEquals(Tinebase_Core::getUser()->getId(), $timesheetUpdated['account_id']['accountId'], 'account is not resolved');
         $this->assertEquals($timesheetData['timeaccount_id'], $timesheetUpdated['timeaccount_id']['id'], 'timeaccount is not resolved');
 
index 4f7ce0f..a3f95dc 100644 (file)
@@ -61,7 +61,7 @@ class Timetracker_Controller extends Tinebase_Controller_Abstract
     public static function getInstance() 
     {
         if (self::$_instance === NULL) {
-            self::$_instance = new Timetracker_Controller();
+            self::$_instance = new self();
         }
         return self::$_instance;
     }
index ddef9c1..1877502 100644 (file)
@@ -56,7 +56,7 @@ class Timetracker_Controller_Timeaccount extends Tinebase_Controller_Record_Abst
     public static function getInstance() 
     {
         if (self::$_instance === NULL) {
-            self::$_instance = new Timetracker_Controller_Timeaccount();
+            self::$_instance = new self();
         }
         
         return self::$_instance;
index aa6efe8..974a095 100644 (file)
@@ -88,7 +88,7 @@ class Timetracker_Controller_Timesheet extends Tinebase_Controller_Record_Abstra
     public static function getInstance() 
     {
         if (self::$_instance === NULL) {
-            self::$_instance = new Timetracker_Controller_Timesheet();
+            self::$_instance = new self();
         }
         
         return self::$_instance;
diff --git a/tine20/Timetracker/Convert/Timeaccount/Json.php b/tine20/Timetracker/Convert/Timeaccount/Json.php
new file mode 100644 (file)
index 0000000..2e07120
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * convert functions for records from/to json (array) format
+ *
+ * @package     Timetracker
+ * @subpackage  Convert
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+/**
+ * convert functions for records from/to json (array) format
+ *
+ * @package     Timetracker
+ * @subpackage  Convert
+ */
+class Timetracker_Convert_Timeaccount_Json extends Tinebase_Convert_Json
+{
+    /**
+     * resolve multiple record fields (Tinebase_ModelConfiguration._recordsFields)
+     *
+     * @param Tinebase_Record_RecordSet $_records
+     * @param Tinebase_ModelConfiguration $modelConfiguration
+     */
+    protected function _resolveMultipleRecordFields(Tinebase_Record_RecordSet $_records, $modelConfiguration = NULL)
+    {
+        // grants cannnot be resolved the default way, other records fields must not be resolved
+    }
+}
index ac0d06b..57aca6c 100644 (file)
@@ -38,6 +38,18 @@ class Timetracker_Frontend_Json extends Tinebase_Frontend_Json_Abstract
     protected $_relatableModels = array('Timetracker_Model_Timeaccount');
 
     /**
+     * default model (needed for application starter -> defaultContentType)
+     * @var string
+     */
+    protected $_defaultModel = 'Timesheet';
+
+    /**
+     * All configured models
+     * @var array
+     */
+       protected $_configuredModels = array('Timesheet', 'Timeaccount');
+
+    /**
      * the constructor
      *
      */
@@ -230,7 +242,7 @@ class Timetracker_Frontend_Json extends Tinebase_Frontend_Json_Abstract
      */
     public function searchTimesheets($filter, $paging)
     {
-        $result = $this->_search($filter, $paging, $this->_timesheetController, 'Timetracker_Model_TimesheetFilter');
+        $result = $this->_search($filter, $paging, $this->_timesheetController, 'Timetracker_Model_TimesheetFilter', true);
         
         $result['totalcountbillable'] = $result['totalcount']['sum_is_billable_combined'];
         $result['totalsum']           = $result['totalcount']['sum_duration'];
index 86a6671..e350c3f 100644 (file)
@@ -6,7 +6,7 @@
  * @subpackage  Model
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schüle <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  * 
  * @todo        update validators (default values, mandatory fields)
  * @todo        add setFromJson with relation handling
@@ -36,6 +36,192 @@ class Timetracker_Model_Timeaccount extends Sales_Model_Accountable_Abstract
     protected $_application = 'Timetracker';
 
     /**
+     * holds the configuration object (must be declared in the concrete class)
+     *
+     * @var Tinebase_ModelConfiguration
+     */
+    protected static $_configurationObject = NULL;
+
+    /**
+     * Holds the model configuration (must be assigned in the concrete class)
+     *
+     * @var array
+     */
+    protected static $_modelConfiguration = array(
+        'containerName'     => 'Timeaccount',
+        'containersName'    => 'Timeaccounts',
+        'recordName'        => 'Timeaccount',
+        'recordsName'       => 'Timeaccounts', // ngettext('Timeaccount', 'Timeaccounts', n)
+        'hasRelations'      => TRUE,
+        'hasCustomFields'   => TRUE,
+        'hasNotes'          => TRUE,
+        'hasTags'           => TRUE,
+        'modlogActive'      => TRUE,
+        'hasAttachments'    => TRUE,
+        'createModule'      => TRUE,
+        'containerProperty' => 'container_id',
+        'multipleEdit'      => TRUE,
+        'requiredRight'     => 'manage',
+
+        'titleProperty'     => 'title',
+        'appName'           => 'Timetracker',
+        'modelName'         => 'Timeaccount',
+
+        'filterModel'       => array(
+            'contract'          => array(
+                'filter'            => 'Tinebase_Model_Filter_ExplicitRelatedRecord',
+                'title'             => 'Contract', // _('Contract')
+                'options'           => array(
+                    'controller'        => 'Sales_Controller_Contract',
+                    'filtergroup'       => 'Sales_Model_ContractFilter',
+                    'own_filtergroup'   => 'Timetracker_Model_TimeaccountFilter',
+                    'own_controller'    => 'Timetracker_Controller_Timeaccount',
+                    'related_model'     => 'Sales_Model_Contract',
+                ),
+                'jsConfig'          => array('filtertype' => 'timetracker.timeaccountcontract')
+            ),
+            'responsible'       => array(
+                'filter'            => 'Tinebase_Model_Filter_ExplicitRelatedRecord',
+                'title'             => 'Responsible',
+                'options'           => array(
+                    'controller'        => 'Addressbook_Controller_Contact',
+                    'filtergroup'       => 'Addressbook_Model_ContactFilter',
+                    'own_filtergroup'   => 'Timetracker_Model_TimeaccountFilter',
+                    'own_controller'    => 'Timetracker_Controller_Timeaccount',
+                    'related_model'     => 'Addressbook_Model_Contact',
+                ),
+                'jsConfig'          => array('filtertype' => 'timetracker.timeaccountresponsible')
+            )
+        ),
+
+        'fields'            => array(
+            'account_grants'    => array(
+                'label'                 => NULL,
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'title'             => array(
+                'label'                 => 'Title', //_('Title')
+                'duplicateCheckGroup'   => 'title',
+                'queryFilter'           => TRUE,
+                'showInDetailsPanel'    => TRUE,
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
+            ),
+            'number'            => array(
+                'label'                 => 'Number', //_('Number')
+                'duplicateCheckGroup'   => 'number',
+                'queryFilter'           => TRUE,
+                'showInDetailsPanel'    => TRUE,
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'description'       => array(
+                'label'                 => 'Description', // _('Description')
+                'type'                  => 'text',
+                'showInDetailsPanel'    => TRUE,
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'budget'            => array(
+                'type'                  => 'float',
+                'inputFilters'          => array('Zend_Filter_Digits', 'Zend_Filter_Empty' => NULL),
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'budget_unit'       => array(
+                'shy'                   => TRUE,
+                'default'               => 'hours',
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 'hours'),
+            ),
+            'price'             => array(
+                'type'                  => 'integer',
+                'inputFilters'          => array('Zend_Filter_PregReplace' => array('/,/', '.'), 'Zend_Filter_Empty' => NULL),
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 0),
+            ),
+            'price_unit'        => array(
+                'shy'                   => TRUE,
+                'default'               => 'hours',
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 'hours'),
+            ),
+            'is_open'           => array(
+                // is_open = Status, status = Billed
+                'label'                 => 'Status', //_('Status')
+                'type'                  => 'boolean',
+                'default'               => 1,
+                'inputFilters'          => array('Zend_Filter_Empty' => 0),
+                'filterDefinition'      => array(
+                    'filter'                => 'Tinebase_Model_Filter_Bool',
+                    'jsConfig'              => array('filtertype' => 'timetracker.timeaccountstatus')
+                ),
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 1),
+            ),
+            'is_billable'       => array(
+                'type'                  => 'boolean',
+                'default'               => TRUE,
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 1),
+            ),
+            'billed_in'         => array(
+                'label'                 => "Cleared in", // _("Cleared in"),
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'invoice_id'        => array(
+                'label'                 => 'Invoice', // _('Invoice')
+                'type'                  => 'record',
+                'inputFilters'          => array('Zend_Filter_Empty' => NULL),
+                'config'                => array(
+                    'appName'               => 'Sales',
+                    'modelName'             => 'Invoice',
+                    'idProperty'            => 'id'
+                ),
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'status'            => array(
+                // is_open = Status, status = Billed
+                'label'                 => 'Billed', //_('Billed')
+                'type'                  => 'string',
+                'filterDefinition'      => array(
+                    'filter'                => 'Tinebase_Model_Filter_Text',
+                    'jsConfig'              => array('filtertype' => 'timetracker.timeaccountbilled')
+                ),
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 'not yet billed'),
+            ),
+            'cleared_at'        => array(
+                'label'                 => "Cleared at", // _("Cleared at")
+                'type'                  => 'datetime',
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'deadline'          => array(
+                'label'                 => 'Booking deadline', // _('Booking deadline')
+                'type'                  => 'string',
+                'validators'            => array(
+                                            Zend_Filter_Input::ALLOW_EMPTY      => true,
+                                            Zend_Filter_Input::DEFAULT_VALUE    => self::DEADLINE_NONE,
+                                                array('InArray', array(self::DEADLINE_NONE, self::DEADLINE_LASTWEEK)),
+                                            )
+            ),
+            'grants'            => array(
+                'label'                 => NULL,
+                'type'                  => 'records',
+                'config'                => array(
+                    'appName'               => 'Timetracker',
+                    'modelName'             => 'TimeaccountGrants',
+                    'idProperty'            => 'id'
+                ),
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+            ),
+            'responsible'       => array(
+                'type'                  => 'virtual',
+                'config'                => array(
+                    'type'                  => 'relation',
+                    'label'                 => 'Responsible',    // _('Responsible')
+                    'config'                => array(
+                        'appName'               => 'Addressbook',
+                        'modelName'             => 'Contact',
+                        'type'                  => 'RESPONSIBLE'
+                    )
+                )
+            ),
+
+        )
+    );
+
+    /**
      * @see Tinebase_Record_Abstract
      */
     protected static $_relatableConfig = array(
@@ -80,84 +266,6 @@ class Timetracker_Model_Timeaccount extends Sales_Model_Accountable_Abstract
      * = booking timesheets allowed until monday midnight for the last week
      */
     const DEADLINE_LASTWEEK = 'lastweek';
-    
-    /**
-     * list of zend validator
-     * 
-     * this validators get used when validating user generated content with Zend_Input_Filter
-     *
-     * @var array
-     */
-    protected $_validators = array(
-        'id'                    => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'container_id'          => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'account_grants'        => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'title'                 => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
-        'number'                => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'description'           => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'budget'                => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'budget_unit'           => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 'hours'),
-        'price'                 => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 0),
-        'price_unit'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 'hours'),
-        'is_open'               => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 1),
-        'is_billable'           => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 1),
-        'billed_in'             => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'invoice_id'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'status'                => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 'not yet billed'),
-        'cleared_at'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-    // how long can users book timesheets for this timeaccount 
-        'deadline'              => array(
-            Zend_Filter_Input::ALLOW_EMPTY      => true, 
-            Zend_Filter_Input::DEFAULT_VALUE    => self::DEADLINE_NONE,
-            array('InArray', array(self::DEADLINE_NONE, self::DEADLINE_LASTWEEK)),
-        ),    
-    // modlog information
-        'created_by'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'creation_time'         => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'last_modified_by'      => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'last_modified_time'    => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'is_deleted'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'deleted_time'          => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'deleted_by'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'seq'                   => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-    // relations (linked Timetracker_Model_Timeaccount records) and other metadata
-        'relations'             => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => NULL),
-        'tags'                  => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'notes'                 => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'grants'                => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'customfields'          => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-       
-        'attachments'           => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-    );
-
-    /**
-     * name of fields containing datetime or an array of datetime information
-     *
-     * @var array list of datetime fields
-     */
-    protected $_datetimeFields = array(
-        'creation_time',
-        'last_modified_time',
-        'deleted_time',
-        'cleared_at'
-    );
-    
-    /**
-     * overwrite constructor to add more filters
-     *
-     * @param mixed $_data
-     * @param bool $_bypassFilters
-     * @param mixed $_convertDates
-     */
-    public function __construct($_data = NULL, $_bypassFilters = false, $_convertDates = true)
-    {
-        $this->_filters['budget']  = new Zend_Filter_Empty(0);
-        $this->_filters['price'] = array(new Zend_Filter_PregReplace('/,/', '.'), new Zend_Filter_Empty(NULL));
-        $this->_filters['is_open'] = new Zend_Filter_Empty(0);
-        $this->_filters['invoice_id']  = array(new Zend_Filter_Empty(NULL));
-        
-        return parent::__construct($_data, $_bypassFilters, $_convertDates);
-    }
 
     /**
      * set from array data
index 5129f20..495e39a 100644 (file)
 class Timetracker_Model_TimeaccountFilter extends Tinebase_Model_Filter_FilterGroup implements Tinebase_Model_Filter_AclFilter 
 {
     /**
-     * @var string application of this filter group
-     */
-    protected $_applicationName = 'Timetracker';
-    
-    /**
-     * @var string name of model this filter group is designed for
-     */
-    protected $_modelName = 'Timetracker_Model_Timeaccount';
-    
-    /**
-     * @var string class name of this filter group
-     *      this is needed to overcome the static late binding
-     *      limitation in php < 5.3
-     */
-    protected $_className = 'Timetracker_Model_TimeaccountFilter';
-        
-    /**
-     * @var array filter model fieldName => definition
+     * if this is set, the filtergroup will be created using the configurationObject for this model
+     *
+     * @var string
      */
-    protected $_filterModel = array(
-        'id'             => array('filter' => 'Tinebase_Model_Filter_Id', 'options' => array('modelName' => 'Timetracker_Model_Timeaccount')),
-        'query'          => array('filter' => 'Tinebase_Model_Filter_Query', 'options' => array('fields' => array('number', 'title'))),
-        'title'          => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'number'         => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'budget'         => array('filter' => 'Tinebase_Model_Filter_Int'),
-        'description'    => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'status'         => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'deadline'       => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'tag'            => array('filter' => 'Tinebase_Model_Filter_Tag', 'options' => array(
-            'idProperty' => 'timetracker_timeaccount.id',
-            'applicationName' => 'Timetracker',
-        )),
-        'invoice_id' => array('filter' => 'Tinebase_Model_Filter_ForeignId',
-            'options' => array(
-                'filtergroup'       => 'Sales_Model_InvoiceFilter',
-                'controller'        => 'Sales_Controller_Invoice',
-            )
-        ),
-        'created_by'     => array('filter' => 'Tinebase_Model_Filter_User'),
-        'last_modified_time'   => array('filter' => 'Tinebase_Model_Filter_Date'),
-        'deleted_time'         => array('filter' => 'Tinebase_Model_Filter_DateTime'),
-        'creation_time'        => array('filter' => 'Tinebase_Model_Filter_Date'),
-        'cleared_at'           => array('filter' => 'Tinebase_Model_Filter_Date'),
-        'last_modified_by'     => array('filter' => 'Tinebase_Model_Filter_User'),
-        'is_open'              => array('filter' => 'Tinebase_Model_Filter_Bool'),
-        'contract'    => array('filter' => 'Tinebase_Model_Filter_ExplicitRelatedRecord', 'options' => array(
-            'controller' => 'Sales_Controller_Contract',
-            'filtergroup' => 'Sales_Model_ContractFilter',
-            'own_filtergroup' => 'Timetracker_Model_TimeaccountFilter',
-            'own_controller' => 'Timetracker_Controller_Timeaccount',
-            'related_model' => 'Sales_Model_Contract',
-        ))
-    );
+       protected $_configuredModel = 'Timetracker_Model_Timeaccount';
     
     /**
      * @var array one of these grants must be met
index 6da3932..7805ce9 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schuele <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2008 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  * 
  */
 
 class Timetracker_Model_Timesheet extends Tinebase_Record_Abstract implements Sales_Model_Billable_Interface
 {
     /**
-     * key in $_validators/$_properties array for the filed which 
-     * represents the identifier
-     * 
-     * @var string
-     */
-    protected $_identifier = 'id';
-    
-    /**
-     * application the record belongs to
-     *
-     * @var string
-     */
-    protected $_application = 'Timetracker';
-    
-    /**
-     * list of zend validator
-     * 
-     * this validators get used when validating user generated content with Zend_Input_Filter
+     * holds the configuration object (must be declared in the concrete class)
      *
-     * @var array
+     * @var Tinebase_ModelConfiguration
      */
-    protected $_validators = array(
-        'id'                    => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-    // record fields
-        'account_id'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
-        'timeaccount_id'        => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
-        'start_date'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
-        'start_time'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'duration'              => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'description'           => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
-        'is_billable'           => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 1),
-        'is_billable_combined'  => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'billed_in'             => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'invoice_id'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'is_cleared'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 0),
-        'is_cleared_combined'   => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-    // custom fields array
-        'customfields'          => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => array()),    
-    // modlog information
-        'created_by'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'creation_time'         => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'last_modified_by'      => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'last_modified_time'    => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'is_deleted'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'deleted_time'          => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'deleted_by'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'seq'                   => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-    // other related data
-        'relations'             => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => NULL),
-        'notes'                 => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        'tags'                  => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-        
-        'attachments'           => array(Zend_Filter_Input::ALLOW_EMPTY => true),
-    );
+    protected static $_configurationObject = NULL;
 
     /**
-     * name of fields containing datetime or an array of datetime information
-     *
-     * @var array list of datetime fields
-     */    
-    protected $_datetimeFields = array(
-        'creation_time',
-        'last_modified_time',
-        'deleted_time',
-    );
-    
-    /**
-     * name of fields containing time information
-     *
-     * @var array list of time fields
-     */
-    protected $_timeFields = array(
-        'start_time'
-    );
-    
-    /**
-     * if foreign Id fields should be resolved on search and get from json
-     * should have this format:
-     *     array('Calendar_Model_Contact' => 'contact_id', ...)
-     * or for more fields:
-     *     array('Calendar_Model_Contact' => array('contact_id', 'customer_id), ...)
-     * (e.g. resolves contact_id with the corresponding Model)
+     * Holds the model configuration (must be assigned in the concrete class)
      *
      * @var array
      */
-    protected static $_resolveForeignIdFields = array(
-        'Sales_Model_Invoice' => array('invoice_id'),
+    protected static $_modelConfiguration = array(
+        'recordName'        => 'Timesheet',
+        'recordsName'       => 'Timesheets', // ngettext('Timesheet', 'Timesheets', n)
+        'hasRelations'      => TRUE,
+        'hasCustomFields'   => TRUE,
+        'hasNotes'          => TRUE,
+        'hasTags'           => TRUE,
+        'modlogActive'      => TRUE,
+        'hasAttachments'    => TRUE,
+        'createModule'      => TRUE,
+        'containerProperty' => NULL,
+
+        'titleProperty'     => 'title',
+        'appName'           => 'Timetracker',
+        'modelName'         => 'Timesheet',
+        'multipleEdit'      => TRUE,
+        'splitButton'       => TRUE,
+
+        'defaultFilter' => 'start_date',
+
+        'fields'            => array(
+            'account_id'            => array(
+                'label'                 => 'Account', //_('Account')
+                'duplicateCheckGroup'   => 'account',
+                'type'                  => 'user',
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
+                // TODO add filter: 'inputFilters'          => array('Zend_Filter_Empty' => CURRENT USER ACCOUNT ID),
+            ),
+            'timeaccount_id'        => array(
+                'label'                 => 'Time Account (Number - Title)', //_('Time Account (Number - Title)')
+                'type'                  => 'record',
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
+                'config'                => array(
+                    'appName'               => 'Timetracker',
+                    'modelName'             => 'Timeaccount',
+                    'idProperty'            => 'id'
+                ),
+                // TODO ?????
+                //'default'               => array('account_grants' => array('bookOwnGrant' => true)),
+                'filterDefinition'      => array(
+                    'filter'                => 'Tinebase_Model_Filter_ForeignId',
+                    'options'               => array(
+                        'filtergroup'           => 'Timetracker_Model_TimeaccountFilter',
+                        'controller'            => 'Timetracker_Controller_Timeaccount',
+                        'useTimesheetAcl'       => TRUE,
+                        'showClosed'            => TRUE,
+                        'appName'               => 'Timetracker',
+                        'modelName'             => 'Timeaccount',
+                    ),
+                    'jsConfig'              => array('filtertype' => 'timetracker.timeaccount')
+                ),
+            ),
+            'is_billable'           => array(
+                'label'                 => NULL,
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 1),
+                'type'                  => 'boolean',
+                'default'               => 1,
+                'shy'                   => TRUE
+            ),
+            'is_billable_combined'  => array(
+                'label'                 => 'Billable', // _('Billable')
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+                'type'                  => 'boolean',
+                'filterDefinition'      => array(
+                    'filter'                => 'Tinebase_Model_Filter_Bool',
+                    'title'                 => 'Billable', // _('Billable')
+                    'options'               => array(
+                        'leftOperand'           => '(timetracker_timesheet.is_billable*timetracker_timeaccount.is_billable)',
+                        'requiredCols'          => array('is_billable_combined')
+                    ),
+                ),
+            ),
+            'billed_in'             => array(
+                'label'                 => 'Cleared in', // _('Cleared in')
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+                'copyOmit'              => TRUE,
+                'shy'                   => TRUE
+            ),
+            'invoice_id'            => array(
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+                'label'                 => 'Invoice', // _('Invoice')
+                'type'                  => 'record',
+                'inputFilters'          => array('Zend_Filter_Empty' => NULL),
+                'config'                => array(
+                    'appName'               => 'Sales',
+                    'modelName'             => 'Invoice',
+                    'idProperty'            => 'id'
+                ),
+            ),
+            'is_cleared'            => array(
+                'label'                 => NULL,
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true, Zend_Filter_Input::DEFAULT_VALUE => 0),
+                'type'                  => 'boolean',
+                'default'               => 0,
+                'copyOmit'              => TRUE,
+                'shy'                   => TRUE
+            ),
+            'is_cleared_combined'   => array(
+                'label'                 => 'Cleared', // _('Cleared')
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+                'type'                  => 'boolean',
+                'filterDefinition'      => array(
+                    'filter'                => 'Tinebase_Model_Filter_Bool',
+                    'options'               => array(
+                        'leftOperand'           => "( (CASE WHEN timetracker_timesheet.is_cleared = '1' THEN 1 ELSE 0 END) | (CASE WHEN timetracker_timeaccount.status = 'billed' THEN 1 ELSE 0 END) )",
+                        'requiredCols'          => array('is_cleared_combined'),
+                    ),
+                ),
+            ),
+            'start_date'            => array(
+                'label'                 => 'Date', // _('Date')
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
+                'type'                  => 'date',
+                'default'               => 'today',
+                // strip time information from datetime string
+                'inputFilters'          => array('Zend_Filter_PregReplace' => array('/(\d{4}-\d{2}-\d{2}).*/', '$1'))
+            ),
+            'start_time'            => array(
+                'label'                 => 'Start time', // _('Start time')
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+                'inputFilters'          => array('Zend_Filter_Empty' => NULL),
+                'type'                  => 'time',
+                'shy'                   => TRUE
+            ),
+            'duration'              => array(
+                'label'                 => 'Duration', // _('Duration')
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
+                'type'                  => 'integer',
+                'specialType'           => 'minutes',
+                'default'               => '30'
+            ),
+            'description'           => array(
+                'label'                 => 'Description', // _('Description')
+                'type'                  => 'text',
+                'validators'            => array(Zend_Filter_Input::ALLOW_EMPTY => false, 'presence'=>'required'),
+                'queryFilter'           => TRUE
+            ),
+            // TODO ?????
+            /*
+            'timeaccount_closed' => array(
+                'label' => 'Time Account closed', // _('Time Account closed')
+                'validators' => array(Zend_Filter_Input::ALLOW_EMPTY => true),
+                'type' => 'boolean',
+
+            ),*/
+        )
     );
     
     /**
-     * overwrite constructor to add more filters
-     *
-     * @param mixed $_data
-     * @param bool $_bypassFilters
-     * @param mixed $_convertDates
-     * @return void
-     */
-    public function __construct($_data = NULL, $_bypassFilters = false, $_convertDates = true)
-    {
-        // strip time information from datetime string
-        $this->_filters['start_date'] = new Zend_Filter_PregReplace('/(\d{4}-\d{2}-\d{2}).*/', '$1');
-        // set start_time to NULL if not set
-        $this->_filters['start_time'] = new Zend_Filter_Empty(NULL);
-        
-        $this->_filters['invoice_id'] = new Zend_Filter_Empty(NULL);
-        
-        return parent::__construct($_data, $_bypassFilters, $_convertDates);
-    }
-    
-    /**
      * returns the interval of this billable
      *
      * @return array
index a2f3d02..4c0533e 100644 (file)
 class Timetracker_Model_TimesheetFilter extends Tinebase_Model_Filter_FilterGroup implements Tinebase_Model_Filter_AclFilter 
 {
     /**
-     * @var string application of this filter group
-     */
-    protected $_applicationName = 'Timetracker';
-    
-    /**
-     * @var string name of model this filter group is designed for
-     */
-    protected $_modelName = 'Timetracker_Model_Timesheet';
-    
-    /**
-     * @var string class name of this filter group
-     *      this is needed to overcome the static late binding
-     *      limitation in php < 5.3
-     */
-    protected $_className = 'Timetracker_Model_TimesheetFilter';
-    
-    /**
-     * @var array filter model fieldName => definition
+     * if this is set, the filtergroup will be created using the configurationObject for this model
+     *
+     * @var string
      */
-    protected $_filterModel = array(
-        'id'             => array(
-            'filter'  => 'Tinebase_Model_Filter_Id',
-            'options' => array('modelName' => 'Timetracker_Model_Timesheet')
-        ),
-        'query'          => array(
-            'filter'  => 'Tinebase_Model_Filter_Query',
-            'options' => array('fields' => array('description'))
-        ),
-        'description'    => array('filter' => 'Tinebase_Model_Filter_Text'),
-        'duration'       => array('filter' => 'Tinebase_Model_Filter_Int'),
-        'timeaccount_id' => array('filter' => 'Tinebase_Model_Filter_ForeignId', 
-            'options' => array(
-                'filtergroup'       => 'Timetracker_Model_TimeaccountFilter', 
-                'controller'        => 'Timetracker_Controller_Timeaccount', 
-                'useTimesheetAcl'   => TRUE,
-                'showClosed'        => TRUE
-            )
-        ),
-        'invoice_id' => array('filter' => 'Tinebase_Model_Filter_ForeignId',
-            'options' => array(
-                'filtergroup'       => 'Sales_Model_InvoiceFilter',
-                'controller'        => 'Sales_Controller_Invoice',
-            )
-        ),
-        'account_id'     => array('filter' => 'Tinebase_Model_Filter_User'),
-        'start_date'     => array('filter' => 'Tinebase_Model_Filter_Date'),
-        
-        'billed_in'    => array('filter' => 'Tinebase_Model_Filter_Text'),
-        
-        'is_billable_combined'  => array(
-            'filter' => 'Tinebase_Model_Filter_Bool', 
-            'options' => array(
-                'leftOperand'   => '(timetracker_timesheet.is_billable*timetracker_timeaccount.is_billable)',
-                'requiredCols'  => array('is_billable_combined')
-            ),
-        ),
-        'is_billable'   => array('filter' => 'Tinebase_Model_Filter_Bool'),
-        'is_cleared'   => array('filter' => 'Tinebase_Model_Filter_Bool'),
-        'is_cleared_combined'   => array(
-            'filter' => 'Tinebase_Model_Filter_Bool', 
-            'options' => array(
-                'leftOperand' => "( (CASE WHEN timetracker_timesheet.is_cleared = '1' THEN 1 ELSE 0 END) | (CASE WHEN timetracker_timeaccount.status = 'billed' THEN 1 ELSE 0 END) )",
-                'requiredCols'  => array('is_cleared_combined'),
-            ),
-        ),
-        'tag'            => array('filter' => 'Tinebase_Model_Filter_Tag',          'options' => array(
-            'idProperty' => 'timetracker_timesheet.id',
-            'applicationName' => 'Timetracker',
-        )),
-        'customfield'    => array('filter' => 'Tinebase_Model_Filter_CustomField',  'options' => array('idProperty' => 'timetracker_timesheet.id')),
-    // modlog
-        'created_by'     => array('filter' => 'Tinebase_Model_Filter_User'),
-        'last_modified_time'   => array('filter' => 'Tinebase_Model_Filter_Date'),
-        'deleted_time'         => array('filter' => 'Tinebase_Model_Filter_DateTime'),
-        'creation_time'        => array('filter' => 'Tinebase_Model_Filter_Date'),
-        'last_modified_by'     => array('filter' => 'Tinebase_Model_Filter_User'),
-    );
+    protected $_configuredModel = 'Timetracker_Model_Timesheet';
     
     /**
      * is resolved
index b666893..bb6f215 100644 (file)
@@ -21,7 +21,7 @@ class Timetracker_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
      * 
      * @var array
      */
-    protected static $_requiredApplications = array('Admin', 'Sales');
+    protected static $_requiredApplications = array('Admin', 'Sales', 'HumanResources');
     
     /**
      * holds the instance of the singleton
@@ -308,11 +308,13 @@ class Timetracker_Setup_DemoData extends Tinebase_Setup_DemoData_Abstract
     
     /**
      * returns the cost center for the current account
+     *
+     * @return HumanResources_Model_CostCenter|Sales_Model_CostCenter
      */
     protected function _getCurrentUsersCostCenter()
     {
         $employee = $this->_getCurrentUsersEmployee();
-        $salesCC =  HumanResources_Controller_CostCenter::getInstance()->getValidCostCenter($employee->getId(), NULL, TRUE);
+        $salesCC = HumanResources_Controller_CostCenter::getInstance()->getValidCostCenter($employee->getId(), NULL, TRUE);
         return $salesCC;
     }
     
index 83ea8ef..8fcb2d8 100644 (file)
           "path": "js/"
         },
         {
-          "text": "TimeAccountFilterModel.js",
-          "path": "js/"
-        },
-        {
           "text": "TimeAccountStatusFilterModel.js",
           "path": "js/"
         },
           "path": "js/"
         },
         {
+          "text": "TimeaccountResponsibleFilterModel.js",
+          "path": "js/"
+        },
+        {
           "text": "TimeaccountGridPanel.js",
           "path": "js/"
         },
         {
+          "text": "TimeAccountFilterModel.js",
+          "path": "js/"
+        },
+        {
           "text": "TimeaccountEditDialog.js",
           "path": "js/"
         },
index 606346f..1145869 100644 (file)
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Cornelius Weiss <c.weiss@metaways.de>
- * @copyright   Copyright (c) 2007-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  */
-Ext.ns('Tine.Timetracker', 'Tine.Timetracker.Model');
 
-/**
- * @type {Array}
- * Timesheet model fields
- */
-Tine.Timetracker.Model.TimesheetArray = Tine.Tinebase.Model.modlogFields.concat([
-    { name: 'id' },
-    { name: 'account_id' },
-    { name: 'timeaccount_id' },
-    { name: 'start_date', type: 'date', dateFormat: Date.patterns.ISO8601Short},
-    { name: 'start_time', type: 'date', dateFormat: Date.patterns.ISO8601Time },
-    { name: 'duration' },
-    { name: 'description' },
-    { name: 'is_billable', type: 'bool' },
-    { name: 'is_billable_combined', type: 'bool' }, // ts & ta is_billable
-    { name: 'is_cleared', type: 'bool' },
-    { name: 'is_cleared_combined', type: 'bool' }, // ts is_cleared & ta status == 'billed'
-    { name: 'billed_in' },
-    { name: 'invoice_id' },
-    // tine 2.0 notes + tags
-    { name: 'notes'},
-    { name: 'tags' },
-    { name: 'customfields'},
-    
-    {name: 'attachments', omitDuplicateResolving: true}
-    // relations
-    // TODO fix this, relations do not work yet in TS edit dialog (without admin/manage privileges)
-    //{ name: 'relations'}
-]);
-
-/**
- * @type {Tine.Tinebase.data.Record}
- * Timesheet record definition
- */
-Tine.Timetracker.Model.Timesheet = Tine.Tinebase.data.Record.create(Tine.Timetracker.Model.TimesheetArray, {
-    appName: 'Timetracker',
-    modelName: 'Timesheet',
-    idProperty: 'id',
-    titleProperty: null,
-    // ngettext('Timesheet', 'Timesheets', n);
-    recordName: 'Timesheet',
-    recordsName: 'Timesheets',
-    containerProperty: 'timeaccount_id',
-    // ngettext('timesheets list', 'timesheets lists', n);
-    containerName: 'All Timesheets',
-    containersName: 'timesheets lists',
-    defaultFilter: 'start_date',
-    getTitle: function() {
-        var timeaccount = this.get('timeaccount_id'),
-            description = Ext.util.Format.ellipsis(this.get('description'), 30, true),
-            timeaccountTitle = '';
-        
-        if (timeaccount) {
-            if (typeof(timeaccount.get) !== 'function') {
-                timeaccount = new Tine.Timetracker.Model.Timeaccount(timeaccount);
-            }
-            timeaccountTitle = timeaccount.getTitle();
-        }
-        
-        timeaccountTitle = timeaccountTitle ? '[' + timeaccountTitle + '] ' : '';
-        return timeaccountTitle + description;
-    },
-    copyOmitFields: ['billed_in', 'is_cleared', 'invoice_id']
-});
-
-Tine.Timetracker.Model.Timesheet.getDefaultData = function() {
-    return {
-        account_id: Tine.Tinebase.registry.get('currentAccount'),
-        duration:   '00:30',
-        start_date: new Date(),
-        is_billable: true,
-        timeaccount_id: {account_grants: {bookOwnGrant: true}}
-    };
-};
-
-Tine.Timetracker.Model.Timesheet.getFilterModel = function() {
-    var app = Tine.Tinebase.appMgr.get('Timetracker');
-    
-    return [
-        {label: app.i18n._('Quick Search'), field: 'query',    operators: ['contains']}, // query only searches description
-        {label: app.i18n._('Account'),      field: 'account_id', valueType: 'user'},
-        {label: app.i18n._('Date'),         field: 'start_date', valueType: 'date', pastOnly: true},
-        {label: app.i18n._('Description'),  field: 'description', defaultOperator: 'contains'},
-        {label: app.i18n._('Billable'),     field: 'is_billable_combined', valueType: 'bool', defaultValue: true },
-        {label: app.i18n._('Cleared'),      field: 'is_cleared_combined',  valueType: 'bool', defaultValue: false },
-        {label: i18n._('Last Modified Time'),                                                field: 'last_modified_time', valueType: 'date'},
-        {label: i18n._('Last Modified By'),                                                  field: 'last_modified_by',   valueType: 'user'},
-        {label: i18n._('Creation Time'),                                                     field: 'creation_time',      valueType: 'date'},
-        {label: i18n._('Created By'),                                                        field: 'created_by',         valueType: 'user'},
-        {filtertype: 'tinebase.tag', app: app},
-        {filtertype: 'timetracker.timeaccount'}
-    ];
-};
-
-
-/**
- * @type {Array}
- * Timeaccount model fields
- */
-Tine.Timetracker.Model.TimeaccountArray = Tine.Tinebase.Model.genericFields.concat([
-    { name: 'id' },
-    { name: 'container_id' },
-    { name: 'title' },
-    { name: 'number' },
-    { name: 'description' },
-    { name: 'budget' },
-    { name: 'budget_unit' },
-    { name: 'price' },
-    { name: 'price_unit' },
-    { name: 'cleared_at'},
-    { name: 'is_open', type: 'bool'},
-    { name: 'is_billable', type: 'bool' },
-    { name: 'billed_in' },
-    { name: 'invoice_id' },
-    { name: 'status' },
-    { name: 'deadline' },
-    { name: 'account_grants'},
-    { name: 'grants'},
-    // tine 2.0 notes + tags
-    { name: 'notes'},
-    { name: 'tags' },
-    { name: 'customfields'},
-    // relations
-    { name: 'relations'},
-    
-    {name: 'attachments', omitDuplicateResolving: true}
-]);
-
-/**
- * @type {Tine.Tinebase.data.Record}
- * Timesheet record definition
- */
-Tine.Timetracker.Model.Timeaccount = Tine.Tinebase.data.Record.create(Tine.Timetracker.Model.TimeaccountArray, {
-    appName: 'Timetracker',
-    modelName: 'Timeaccount',
-    idProperty: 'id',
-    titleProperty: 'title',
-    // ngettext('Time Account', 'Time Accounts', n);
-    recordName: 'Time Account',
-    recordsName: 'Time Accounts',
-    containerProperty: 'container_id',
-    defaultFilter: 'query',
-    // ngettext('timeaccount list', 'timeaccount lists', n);
-    containerName: 'All Timeaccounts',
-    containersName: 'timeaccount lists',
-    
-    copyOmitFields: ['cleared_at', 'billed_in', 'invoice_id', 'status'],
-    
-    getTitle: function() {
-        var closedText = this.get('is_open') ? '' : (' (' + Tine.Tinebase.appMgr.get('Timetracker').i18n._('closed') + ')');
-        
-        return this.get('number') ? (this.get('number') + ' - ' + this.get('title') + closedText) : '';
-    }
-});
-
-Tine.Timetracker.Model.Timeaccount.getDefaultData = function() {
-    return {
-        is_open: 1,
-        is_billable: true
-    };
-};
-
-Tine.Timetracker.Model.Timeaccount.getFilterModel = function() {
-    var app = Tine.Tinebase.appMgr.get('Timetracker');
-
-    var filters = [
-        {label: i18n._('Quick Search'),              field: 'query',       operators: ['contains']},
-        {label: app.i18n._('Number'),           field: 'number'       },
-        {label: app.i18n._('Title'),            field: 'title'        },
-        {label: app.i18n._('Description'),      field: 'description', operators: ['contains']},
-        {label: app.i18n._('Billed'),           field: 'status',      filtertype: 'timetracker.timeaccountbilled'},
-        {label: app.i18n._('Status'),           field: 'is_open',     filtertype: 'timetracker.timeaccountstatus'},
-        {label: app.i18n._('Cleared at'),       field: 'cleared_at',  valueType: 'date'},
-        {label: i18n._('Last Modified Time'),        field: 'last_modified_time', valueType: 'date'},
-        {label: i18n._('Last Modified By'),          field: 'last_modified_by',   valueType: 'user'},
-        {label: i18n._('Creation Time'),             field: 'creation_time',      valueType: 'date'},
-        {label: i18n._('Created By'),                field: 'created_by',         valueType: 'user'},
-        {label: app.i18n._('Booking deadline'), field: 'deadline'},
-        {filtertype: 'tinebase.tag', app: app}
-    ];
-    
-    if(Tine.Tinebase.appMgr.get('Sales')) {
-        filters.push({filtertype: 'timetracker.timeaccountcontract'});
-    }
-    
-    return filters;
-};
+Ext.ns('Tine.Timetracker.Model');
 
 /**
  * Model of a grant
index b450290..c547b6c 100644 (file)
@@ -32,7 +32,6 @@ Tine.Timetracker.TimeAccountBilledFilterModel = Ext.extend(Tine.widgets.grid.Fil
      * @param {Ext.Element} element to render to 
      */
     valueRenderer: function(filter, el) {
-        // value
         var value = new Ext.form.ComboBox({
             filter: filter,
             width: 200,
index ebe1206..523b363 100644 (file)
@@ -4,7 +4,7 @@
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Cornelius Weiss <c.weiss@metaways.de>
- * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  */
  
 Ext.ns('Tine.Timetracker');
@@ -14,7 +14,7 @@ Tine.Timetracker.TimeAccountFilterModel = Ext.extend(Tine.widgets.grid.ForeignRe
     /**
      * @cfg {Record} foreignRecordClass needed for explicit defined filters
      */
-    foreignRecordClass : Tine.Timetracker.Model.Timeaccount,
+    foreignRecordClass : 'Timetracker.Timeaccount',
     
     /**
      * @cfg {String} linkType {relation|foreignId} needed for explicit defined filters
@@ -36,7 +36,7 @@ Tine.Timetracker.TimeAccountFilterModel = Ext.extend(Tine.widgets.grid.ForeignRe
      */
     initComponent: function() {
         this.app = Tine.Tinebase.appMgr.get('Timetracker');
-        this.label = this.app.i18n.n_hidden(this.foreignRecordClass.getMeta('recordName'), this.foreignRecordClass.getMeta('recordsName'), 1);
+        this.label = this.app.i18n.n_('Timeaccount', 'Timeaccounts', 1);
         
         this.pickerConfig = this.pickerConfig || {};
         
index 99679e9..4e8a834 100644 (file)
@@ -4,7 +4,7 @@
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Cornelius Weiss <c.weiss@metaways.de>
- * @copyright   Copyright (c) 2007-2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  */
  
@@ -21,12 +21,14 @@ Tine.Timetracker.TimeaccountEditDialog = Ext.extend(Tine.widgets.dialog.EditDial
     recordProxy: Tine.Timetracker.timeaccountBackend,
     useInvoice: false,
     displayNotes: true,
+
+    windowWidth: 800,
+    windowHeight: 500,
     
     /**
      * overwrite update toolbars function (we don't have record grants yet)
      */
     updateToolbars: function() {
-
     },
     
     initComponent: function() {
@@ -37,6 +39,15 @@ Tine.Timetracker.TimeaccountEditDialog = Ext.extend(Tine.widgets.dialog.EditDial
             && Tine.Sales.Model.Invoice;
         Tine.Timetracker.TimeaccountEditDialog.superclass.initComponent.call(this);
     },
+
+    isMultipleValid: function() {
+        return true;
+    },
+
+    /**
+     * containerProperty (all contracts in one container) exists, so overwrite creating selector here
+     */
+    initContainerSelector: Ext.emptyFn,
     
     onRecordLoad: function() {
         // make sure grants grid is initialized
@@ -335,18 +346,3 @@ Tine.Timetracker.TimeaccountEditDialog = Ext.extend(Tine.widgets.dialog.EditDial
         this.record.set('status', 'not yet billed');
     }
 });
-
-/**
- * Timetracker Edit Popup
- */
-Tine.Timetracker.TimeaccountEditDialog.openWindow = function (config) {
-    var id = (config.record && config.record.id) ? config.record.id : 0;
-    var window = Tine.WindowFactory.getWindow({
-        width: 800,
-        height: 500,
-        name: Tine.Timetracker.TimeaccountEditDialog.prototype.windowNamePrefix + id,
-        contentPanelConstructor: 'Tine.Timetracker.TimeaccountEditDialog',
-        contentPanelConstructorConfig: config
-    });
-    return window;
-};
index 3cdf6b3..a22e321 100644 (file)
@@ -4,7 +4,7 @@
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schüle <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  */
  
@@ -31,10 +31,6 @@ Ext.namespace('Tine.Timetracker');
  * Create a new Tine.Timetracker.TimeaccountGridPanel
  */
 Tine.Timetracker.TimeaccountGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
-    // model generics
-    recordClass: Tine.Timetracker.Model.Timeaccount,
-    
-    // grid specific
     defaultSortInfo: {field: 'creation_time', direction: 'DESC'},
     gridConfig: {
         autoExpandColumn: 'title'
@@ -51,113 +47,18 @@ Tine.Timetracker.TimeaccountGridPanel = Ext.extend(Tine.widgets.grid.GridPanel,
     }],
     
     initComponent: function() {
-        this.recordProxy = Tine.Timetracker.timeaccountBackend;
-        
-        this.actionToolbarItems = this.getToolbarItems();
-        this.gridConfig.cm = this.getColumnModel();
-        
         Tine.Timetracker.TimeaccountGridPanel.superclass.initComponent.call(this);
         
         this.action_addInNewWindow.setDisabled(! Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts'));
         this.action_editInNewWindow.requiredGrant = 'editGrant';
     },
-    
+
     /**
-     * returns cm
-     * 
-     * @return Ext.grid.ColumnModel
      * @private
      */
-    getColumnModel: function(){
-        return new Ext.grid.ColumnModel({
-            defaults: {
-                sortable: true,
-                resizable: true
-            },
-            columns: [
-            {   id: 'tags', header: this.app.i18n._('Tags'), width: 50,  dataIndex: 'tags', sortable: false, renderer: Tine.Tinebase.common.tagsRenderer },
-            {
-                id: 'number',
-                header: this.app.i18n._("Number"),
-                width: 100,
-                dataIndex: 'number'
-            },{
-                id: 'title',
-                header: this.app.i18n._("Title"),
-                width: 350,
-                dataIndex: 'title'
-            },{
-                id: 'status',
-                header: this.app.i18n._("Billed"),
-                width: 150,
-                dataIndex: 'status',
-                renderer: this.statusRenderer.createDelegate(this)
-            },{
-                id: 'budget',
-                header: this.app.i18n._("Budget"),
-                width: 100,
-                dataIndex: 'budget'
-            }, {
-                id: 'billed_in',
-                hidden: true,
-                header: this.app.i18n._("Cleared in"),
-                width: 150,
-                dataIndex: 'billed_in'
-            }, { 
-                id: 'invoice_id',
-                header: this.app.i18n._("Invoice"),
-                width: 150,
-                dataIndex: 'invoice_id',
-                hidden: true,
-                renderer: function(value, row, record) {
-                    if (! value) {
-                        return '';
-                    }
-                    var i = record.get('invoice_id');
-                    
-                    return (i.number ? i.number + ' - ' : '') + i.description;
-                  }
-            }, {
-                id: 'deadline',
-                hidden: true,
-                header: this.app.i18n._("Booking deadline"),
-                width: 100,
-                dataIndex: 'deadline'
-            },{
-                id: 'cleared_at',
-                header: this.app.i18n._("Cleared at"),
-                dataIndex: 'cleared_at',
-                renderer: Tine.Tinebase.common.dateRenderer
-            },{
-                id: 'is_open',
-                header: this.app.i18n._("Status"),
-                width: 150,
-                dataIndex: 'is_open',
-                renderer: function(value) {
-                    if(value) return this.app.i18n._('open');
-                    return this.app.i18n._('closed');
-                },
-                scope: this,
-                hidden: true
-            }]
-        });
-    },
-    
-    /**
-     * status column renderer
-     * @param {string} value
-     * @return {string}
-     */
-    statusRenderer: function(value) {
-        return this.app.i18n._hidden(value);
-    },
-    
-    /**
-     * return additional tb items
-     */
-    getToolbarItems: function(){
-        this.exportButton = new Ext.Action({
-            text: i18n._('Export'),
+    initActions: function() {
+        this.actions_exportTimeaccount = new Ext.Action({
+            text: this.app.i18n._('Export Timeaccounts'),
             iconCls: 'action_export',
             scope: this,
             requiredGrant: 'readGrant',
@@ -168,23 +69,30 @@ Tine.Timetracker.TimeaccountGridPanel = Ext.extend(Tine.widgets.grid.GridPanel,
                     new Tine.widgets.grid.ExportButton({
                         text: this.app.i18n._('Export as ODS'),
                         format: 'ods',
+                        iconCls: 'tinebase-action-export-ods',
                         exportFunction: 'Timetracker.exportTimeaccounts',
                         gridPanel: this
                     })
-                    /*,
-                    new Tine.widgets.grid.ExportButton({
-                        text: this.app.i18n._('Export as CSV'),
-                        format: 'csv',
-                        exportFunction: 'Timetracker.exportTimesheets',
-                        gridPanel: this
-                    })
-                    */
                 ]
             }
         });
-        
+
+        // register actions in updater
+        this.actionUpdater.addActions([
+            this.actions_exportTimeaccount
+        ]);
+
+        Tine.Timetracker.TimesheetGridPanel.superclass.initActions.call(this);
+    },
+
+    /**
+     * add custom items to action toolbar
+     *
+     * @return {Object}
+     */
+    getActionToolbarItems: function() {
         return [
-            Ext.apply(new Ext.Button(this.exportButton), {
+            Ext.apply(new Ext.Button(this.actions_exportTimeaccount), {
                 scale: 'medium',
                 rowspan: 2,
                 iconAlign: 'top'
diff --git a/tine20/Timetracker/js/TimeaccountResponsibleFilterModel.js b/tine20/Timetracker/js/TimeaccountResponsibleFilterModel.js
new file mode 100644 (file)
index 0000000..73c7c60
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+ * Tine 2.0
+ *
+ * @package     Timetracker
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ * @copyright   Copyright (c) 2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+Ext.ns('Tine.Timetracker');
+
+/**
+ * @namespace   Tine.Timetracker
+ * @class       Tine.Timetracker.TimeaccountResponsibleFilterModel
+ * @extends     Tine.widgets.grid.ForeignRecordFilter
+ *
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ */
+Tine.Timetracker.TimeaccountResponsibleFilterModel = Ext.extend(Tine.widgets.grid.ForeignRecordFilter, {
+
+    // private
+    field: 'responsible',
+    valueType: 'relation',
+
+    /**
+     * @private
+     */
+    initComponent: function() {
+        this.app = Tine.Tinebase.appMgr.get('Timetracker');
+        this.label = this.app.i18n._('Responsible');
+        this.foreignRecordClass = 'Addressbook.Contact',
+            this.pickerConfig = {emptyText: this.app.i18n._('without responsible'), allowBlank: true};
+
+        Tine.Timetracker.TimeaccountResponsibleFilterModel.superclass.initComponent.call(this);
+    }
+});
+
+Tine.widgets.grid.FilterToolbar.FILTERS['timetracker.timeaccountresponsible'] = Tine.Timetracker.TimeaccountResponsibleFilterModel;
\ No newline at end of file
index 6f4b4ac..264530e 100644 (file)
@@ -4,7 +4,7 @@
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schüle <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  */
  
@@ -27,6 +27,10 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
     useInvoice: false,
     displayNotes: true,
     context: { 'skipClosedCheck': false },
+
+    windowWidth: 800,
+    windowHeight: 500,
+
     
     /**
      * overwrite update toolbars function (we don't have record grants yet)
@@ -35,7 +39,29 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
         this.onTimeaccountUpdate();
         Tine.Timetracker.TimesheetEditDialog.superclass.updateToolbars.call(this, record, 'timeaccount_id');
     },
-    
+
+    onRecordLoad: function() {
+        // interrupt process flow until dialog is rendered
+        if (! this.rendered) {
+            this.onRecordLoad.defer(250, this);
+            return;
+        }
+
+        if (! this.record.id) {
+            // @todo: this should be handled by default values
+            this.record.set('account_id', Tine.Tinebase.registry.get('currentAccount'));
+            this.record.set('start_date', new Date());
+        }
+
+        Tine.Timetracker.TimesheetEditDialog.superclass.onRecordLoad.call(this);
+
+        // TODO get timeaccount from filter if set
+        var timeaccount = this.record.get('timeaccount_id');
+        if (timeaccount) {
+            this.onTimeaccountUpdate(null, new Tine.Timetracker.Model.Timeaccount(timeaccount));
+        }
+    },
+
     /**
      * this gets called when initializing and if a new timeaccount is chosen
      * 
@@ -67,10 +93,10 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
         }
 
         if (timeaccount) {
-            notBillable = notBillable || timeaccount.data.is_billable == "0" || this.record.get('timeaccount_id').is_billable == "0";
+            notBillable = notBillable || timeaccount.data.is_billable == "0" || timeaccount.get('is_billable') == "0";
             
             // clearable depends on timeaccount is_billable as well (changed by ps / 2009-09-01, behaviour was inconsistent)
-            notClearable = notClearable || timeaccount.data.is_billable == "0" || this.record.get('timeaccount_id').is_billable == "0";
+            notClearable = notClearable || timeaccount.data.is_billable == "0" || timeaccount.get('is_billable') == "0";
         }
         
         this.getForm().findField('is_billable').setDisabled(notBillable);
@@ -114,7 +140,7 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
             && Tine.Tinebase.common.hasRight('manage', 'Sales', 'invoices')
             && Tine.Sales.Model.Invoice;
         
-        Tine.Timetracker.TimesheetEditDialog.superclass.initComponent.apply(this, arguments);
+        Tine.Timetracker.TimesheetEditDialog.superclass.initComponent.call(this);
     },
 
     /**
@@ -336,18 +362,3 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
             }, this);
     }
 });
-
-/**
- * Timetracker Edit Popup
- */
-Tine.Timetracker.TimesheetEditDialog.openWindow = function (config) {
-    var id = (config.record && config.record.id) ? config.record.id : 0;
-    var window = Tine.WindowFactory.getWindow({
-        width: 800,
-        height: 500,
-        name: Tine.Timetracker.TimesheetEditDialog.prototype.windowNamePrefix + id,
-        contentPanelConstructor: 'Tine.Timetracker.TimesheetEditDialog',
-        contentPanelConstructorConfig: config
-    });
-    return window;
-};
index 80ae674..009b22a 100644 (file)
@@ -4,7 +4,7 @@
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schüle <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  */
  
@@ -29,34 +29,13 @@ Ext.namespace('Tine.Timetracker');
  * Create a new Tine.Timetracker.TimesheetGridPanel
  */
 Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
-    /**
-     * record class
-     * @cfg {Tine.Timetracker.Model.Timesheet} recordClass
-     */
-    recordClass: Tine.Timetracker.Model.Timesheet,
-    
-    /**
-     * @private grid cfg
-     */
-    defaultSortInfo: {field: 'start_date', direction: 'DESC'},
-    gridConfig: {
-        autoExpandColumn: 'description'
-    },
-    copyEditAction: true,
-    multipleEdit: true,
-    multipleEditRequiredRight: 'manage_timeaccounts',
-    
-    /**
-     * @private
-     */
-    initComponent: function() {
-        this.recordProxy = Tine.Timetracker.timesheetBackend;
 
+    initComponent: function() {
         this.defaultFilters = [
             {field: 'start_date', operator: 'within', value: 'weekThis'},
             {field: 'account_id', operator: 'equals', value: Tine.Tinebase.registry.get('currentAccount')}
         ];
-        this.gridConfig.cm = this.getColumnModel();
+
         this.initDetailsPanel();
         
         // only eval grants in action updater if user does not have the right to manage timeaccounts
@@ -64,79 +43,6 @@ Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
         
         Tine.Timetracker.TimesheetGridPanel.superclass.initComponent.call(this);
     },
-    
-    /**
-     * returns cm
-     * 
-     * @return Ext.grid.ColumnModel
-     * @private
-     */
-    getColumnModel: function(){
-        var columns = [
-            { id: 'tags',               header: this.app.i18n._('Tags'),                width: 50,  dataIndex: 'tags', sortable: false,
-                renderer: Tine.Tinebase.common.tagsRenderer },
-            { id: 'start_date',         header: this.app.i18n._("Date"),                width: 120, dataIndex: 'start_date',
-                renderer: Tine.Tinebase.common.dateRenderer },
-            { id: 'start_time',         header: this.app.i18n._("Start time"),          width: 100, dataIndex: 'start_time',            hidden: true,
-                renderer: Tine.Tinebase.common.timeRenderer },
-            { id: 'timeaccount_id',     header: this.app.i18n._('Time Account (Number - Title)'), width: 500, dataIndex: 'timeaccount_id',
-                renderer: this.rendererTimeaccountId },
-            { id: 'timeaccount_closed', header: this.app.i18n._("Time Account closed"), width: 100, dataIndex: 'timeaccount_closed',    hidden: true,
-                renderer: this.rendererTimeaccountClosed },
-            { id: 'description',        header: this.app.i18n._("Description"),         width: 400, dataIndex: 'description',           hidden: true },
-            { id: 'is_billable',        header: this.app.i18n._("Billable"),            width: 100, dataIndex: 'is_billable_combined',
-                renderer: Tine.Tinebase.common.booleanRenderer },
-            { id: 'is_cleared',         header: this.app.i18n._("Cleared"),             width: 100, dataIndex: 'is_cleared_combined',   hidden: true,
-                renderer: Tine.Tinebase.common.booleanRenderer },
-            { id: 'billed_in',          header: this.app.i18n._("Cleared in"),          width: 150, dataIndex: 'billed_in',             hidden: true },
-            { id: 'invoice_id',          header: this.app.i18n._("Invoice"),          width: 150, dataIndex: 'invoice_id', hidden: true,
-              renderer: function(value, row, record) {
-                if (! value) {
-                    return '';
-                }
-                var i = record.get('invoice_id');
-                
-                return (i.number ? i.number + ' - ' : '') + i.description;
-              }
-            },
-            { id: 'account_id',         header: this.app.i18n._("Account"),             width: 350, dataIndex: 'account_id',
-                renderer: Tine.Tinebase.common.usernameRenderer },
-            { id: 'duration',           header: this.app.i18n._("Duration"),            width: 150, dataIndex: 'duration',
-                renderer: Tine.Tinebase.common.minutesRenderer }
-        ].concat(this.getModlogColumns());
-        
-        return new Ext.grid.ColumnModel({
-            defaults: {
-                sortable: true,
-                resizable: true
-            },
-            // add custom fields
-            columns: columns.concat(this.getCustomfieldColumns())
-        });
-    },
-    
-    /**
-     * timeaccount renderer -> returns timeaccount title
-     * 
-     * @param {Array} timeaccount
-     * @return {String}
-     */
-    rendererTimeaccountId: function(timeaccount) {
-        return new Tine.Timetracker.Model.Timeaccount(timeaccount).getTitle();
-    },
-    
-    /**
-     * is timeaccount closed -> returns yes/no if timeaccount is closed
-     * 
-     * @param {} a
-     * @param {} b
-     * @param {Tine.Timetracker.Model.Timesheet} record
-     * @return {String}
-     */
-    rendererTimeaccountClosed: function(a, b, record) {
-        var isopen = (record.data.timeaccount_id.is_open == '1');
-        return Tine.Tinebase.common.booleanRenderer(!isopen);
-    },
 
     /**
      * @private
@@ -286,7 +192,7 @@ Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
                                     break;
                                 default:
                                     value += type;
-                            }                           
+                            }
                         } else {
                             value = Ext.util.Format.htmlEncode(value);
                         }
@@ -302,7 +208,7 @@ Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
             })
         });
     },
-    
+
     /**
      * @private
      */
@@ -314,6 +220,7 @@ Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
             requiredGrant: 'exportGrant',
             disabled: true,
             allowMultiple: true,
+            actionUpdater: this.updateExportAction,
             menu: {
                 items: [
                     new Tine.widgets.grid.ExportButton({
@@ -348,7 +255,24 @@ Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
         
         Tine.Timetracker.TimesheetGridPanel.superclass.initActions.call(this);
     },
-    
+
+    updateExportAction: function(action, grants, records) {
+        var exportGrant = true;
+        Ext.each(records, function(record) {
+            var c = record.get('timeaccount_id').container_id;
+            if (c.hasOwnProperty('account_grants')) {
+                if (! c.account_grants.exportGrant) {
+                    exportGrant = false;
+                    return false;
+                }
+            }
+        });
+
+        var disable = ! exportGrant;
+        action.setDisabled(disable);
+        return false;
+    },
+
     /**
      * add custom items to action toolbar
      * 
@@ -373,33 +297,6 @@ Tine.Timetracker.TimesheetGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
         var items = [
             '-',
             this.actions_exportTimesheet
-//            '-', {
-//            text: i18n._('Mass Update'),
-//            iconCls: 'action_edit',
-//            disabled: !Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts'),
-//            scope: this,
-//            menu: {
-//                items: [
-//                    '<b class="x-ux-menu-title">' + i18n._('Update field:') + '</b>',
-//                    {
-//                        text: this.app.i18n._('Billable'),
-//                        field: 'is_billable',
-//                        scope: this,
-//                        handler: this.onMassUpdate
-//                    }, {
-//                        text: this.app.i18n._('Cleared'),
-//                        field: 'is_cleared',
-//                        scope: this,
-//                        handler: this.onMassUpdate
-//                    }, {
-//                        text: this.app.i18n._('Cleared in'),
-//                        field: 'billed_in',
-//                        scope: this,
-//                        handler: this.onMassUpdate
-//                    }
-//                ]
-//            }
-//        }
         ];
         
         return items;
index 07ac025..cea1963 100644 (file)
@@ -4,93 +4,50 @@
  * @package     Timetracker
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Philipp Schüle <p.schuele@metaways.de>
- * @copyright   Copyright (c) 2007-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2016 Metaways Infosystems GmbH (http://www.metaways.de)
  */
  
 Ext.ns('Tine.Timetracker');
 
-/**
- * @namespace   Tine.Timetracker
- * @class       Tine.Timetracker.Application
- * @extends     Tine.Tinebase.Application
- * Timetracker Application Object <br>
- * 
- * @author      Cornelius Weiss <c.weiss@metaways.de>
- */
-Tine.Timetracker.Application = Ext.extend(Tine.Tinebase.Application, {
-    init: function() {
-        Tine.Timetracker.Application.superclass.init.apply(this, arguments);
-        
-        Ext.ux.ItemRegistry.registerItem('Tine.widgets.grid.GridPanel.addButton', {
-            text: this.i18n._('New Timesheet'), 
-            iconCls: 'TimetrackerTimesheet',
-            scope: this,
-            handler: function() {
-                var ms = this.getMainScreen(),
-                    cp = ms.getCenterPanel('Timesheet');
-                    
-                cp.onEditInNewWindow.call(cp, {});
-            }
-        });
-    }
+Tine.widgets.grid.RendererManager.register('Timetracker', 'Timesheet', 'timeaccount_closed', function(row, index, record) {
+    var isopen = (record.data.timeaccount_id.is_open == '1');
+    return Tine.Tinebase.common.booleanRenderer(!isopen);
 });
 
-/**
- * @namespace   Tine.Timetracker
- * @class       Tine.Timetracker.MainScreen
- * @extends     Tine.widgets.MainScreen
- * MainScreen of the Timetracker Application <br>
- * 
- * @author      Cornelius Weiss <c.weiss@metaways.de>
- * 
- * @constructor
- */
-Tine.Timetracker.MainScreen = Ext.extend(Tine.widgets.MainScreen, {
-    activeContentType: 'Timesheet',
-    contentTypes: [
-        {modelName: 'Timesheet',   requiredRight: null,     singularContainerMode: true},
-        {modelName: 'Timeaccount', requiredRight: 'manage', singularContainerMode: true}]
+Tine.widgets.grid.RendererManager.register('Timetracker', 'Timesheet', 'timeaccount_id', function(row, index, record) {
+    var record = new Tine.Timetracker.Model.Timeaccount(record.get('timeaccount_id'));
+    var closedText = record.get('is_open') ? '' : (' (' + Tine.Tinebase.appMgr.get('Timetracker').i18n._('closed') + ')');
+    return record.get('number') ? (record.get('number') + ' - ' + record.get('title') + closedText) : '';
 });
 
-/**
- * default filter panels
- */
-Tine.Timetracker.TimesheetFilterPanel = function(config) {
-    Ext.apply(this, config);
-    Tine.Timetracker.TimesheetFilterPanel.superclass.constructor.call(this);
-};
-
-Ext.extend(Tine.Timetracker.TimesheetFilterPanel, Tine.widgets.persistentfilter.PickerPanel, {
-    filter: [{field: 'model', operator: 'equals', value: 'Timetracker_Model_TimesheetFilter'}]
+Tine.Tinebase.data.TitleRendererManager.register('Timetracker', 'Timeaccount', function(record) {
+    var closedText = record.get('is_open') ? '' : (' (' + Tine.Tinebase.appMgr.get('Timetracker').i18n._('closed') + ')');
+    return record.get('number') ? (record.get('number') + ' - ' + record.get('title') + closedText) : '';
 });
 
-Tine.Timetracker.TimeaccountFilterPanel = function(config) {
-    Ext.apply(this, config);
-    Tine.Timetracker.TimeaccountFilterPanel.superclass.constructor.call(this);
-};
-
-Ext.extend(Tine.Timetracker.TimeaccountFilterPanel, Tine.widgets.persistentfilter.PickerPanel, {
-    filter: [{field: 'model', operator: 'equals', value: 'Timetracker_Model_TimeaccountFilter'}]
-});
+Tine.Tinebase.data.TitleRendererManager.register('Timetracker', 'Timesheet', function(record) {
+    var timeaccount = record.get('timeaccount_id'),
+        description = Ext.util.Format.ellipsis(record.get('description'), 30, true),
+        timeaccountTitle = '';
 
+    if (timeaccount) {
+        if (typeof(timeaccount.get) !== 'function') {
+            timeaccount = new Tine.Timetracker.Model.Timeaccount(timeaccount);
+        }
+        timeaccountTitle = timeaccount.getTitle();
+    }
 
+    timeaccountTitle = timeaccountTitle ? '[' + timeaccountTitle + '] ' : '';
+    return timeaccountTitle + description;
+});
 
-/**
- * default timesheets backend
- */
-Tine.Timetracker.timesheetBackend = new Tine.Tinebase.data.RecordProxy({
-    appName: 'Timetracker',
-    modelName: 'Timesheet',
-    recordClass: Tine.Timetracker.Model.Timesheet
+Tine.widgets.grid.RendererManager.register('Timetracker', 'Timeaccount', 'status', function(row, index, record) {
+    return Tine.Tinebase.appMgr.get('Timetracker').i18n._hidden(record.get('status'));
 });
 
-/**
- * default timeaccounts backend
- */
-Tine.Timetracker.timeaccountBackend = new Tine.Tinebase.data.RecordProxy({
-    appName: 'Timetracker',
-    modelName: 'Timeaccount',
-    recordClass: Tine.Timetracker.Model.Timeaccount
+Tine.widgets.grid.RendererManager.register('Timetracker', 'Timeaccount', 'is_open', function(row, index, record) {
+    var i18n = Tine.Tinebase.appMgr.get('Timetracker').i18n;
+    return record.get('is_open') ? i18n._('open') : i18n._('closed');
 });
 
 // add renderer for invoice position gridpanel
@@ -120,3 +77,8 @@ Tine.Timetracker.registerAccountables = function() {
 };
 
 Tine.Timetracker.registerAccountables();
+
+// disables container tree in WestPanel
+Tine.Timetracker.TimeaccountWestPanel = Ext.extend(Tine.widgets.mainscreen.WestPanel, {
+    hasContainerTreePanel: false
+});
\ No newline at end of file
index 169f508..5b3415f 100644 (file)
@@ -230,6 +230,9 @@ msgstr "Detail"
 msgid "Export Timesheets"
 msgstr "Stundenzettel exportieren"
 
+msgid "Export Timeaccounts"
+msgstr "Zeitkonten exportieren"
+
 #: js/TimesheetGridPanel.js:334
 msgid "Export as ..."
 msgstr "Als ... exportieren"
@@ -300,6 +303,11 @@ msgid_plural "Time Accounts"
 msgstr[0] "Zeitkonto"
 msgstr[1] "Zeitkonten"
 
+msgid "Timeaccount"
+msgid_plural "Timeaccounts"
+msgstr[0] "Zeitkonto"
+msgstr[1] "Zeitkonten"
+
 #: js/TimeaccountEditDialog.js:247
 msgid "Access"
 msgstr "Zugang"
index 366f241..7fdb0c5 100644 (file)
@@ -220,6 +220,8 @@ Tine.widgets.dialog.EditDialog = Ext.extend(Ext.FormPanel, {
      */
     displayNotes: false,
 
+    useMultiple: false,
+
     //private
     initComponent: function() {
         this.relationPanelRegistry = this.relationPanelRegistry ? this.relationPanelRegistry : [];