0012018: closed TimeAccount warning
authorPaul Mehrer <p.mehrer@metaways.de>
Mon, 9 May 2016 11:46:25 +0000 (13:46 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Wed, 13 Jul 2016 13:38:31 +0000 (15:38 +0200)
reworked Timesheet edit/save for closed Timeaccounts

Admins and Timeaccount Manager can now skip that check after confirmation.
Everybody else can not and is not able to save Timesheets for closed
Timeaccounts.

https://forge.tine20.org/view.php?id=12018

Change-Id: I5c73f4e1390d53eae156be33546fe38d8d8733d1
Reviewed-on: http://gerrit.tine20.com/customers/3302
Tested-by: Jenkins CI (http://ci.tine20.com/)
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
tests/tine20/Timetracker/AbstractTest.php
tests/tine20/Timetracker/JsonTest.php
tine20/Timetracker/Controller/Timesheet.php
tine20/Timetracker/Exception/ClosedTimeaccount.php [new file with mode: 0644]
tine20/Timetracker/Frontend/Json.php
tine20/Timetracker/js/TimesheetEditDialog.js
tine20/Tinebase/Controller/Abstract.php

index 4aae724..2f2366d 100644 (file)
@@ -144,7 +144,7 @@ abstract class Timetracker_AbstractTest extends PHPUnit_Framework_TestCase
         $ts = new Timetracker_Model_Timesheet($data, TRUE);
 
         if ($_forceCreation) {
-            $tsRec = $this->_json->saveTimesheet($ts->toArray(), $_forceCreation);
+            $tsRec = $this->_json->saveTimesheet($ts->toArray());
             $this->_lastCreatedRecord = $tsRec;
         }
 
index 819a47b..7825e9d 100644 (file)
@@ -1269,11 +1269,11 @@ class Timetracker_JsonTest extends Timetracker_AbstractTest
         $timesheet = $this->_getTimesheet(array(
              'timeaccount_id'    => $timeaccount['id'],
         ));
-        $timesheetData = $this->_json->saveTimesheet($timesheet->toArray());
+        $timesheetData = $this->_json->saveTimesheet($timesheet->toArray(), array('skipClosedCheck' => true));
         
         Timetracker_ControllerTest::removeManageAllRight();
         
-        $this->setExpectedException('Tinebase_Exception_AccessDenied');
+        $this->setExpectedException('Timetracker_Exception_ClosedTimeaccount');
         
         // update Timesheet
         $timesheetData['description'] = "blubbblubb";
index 876f79c..d0d8f98 100644 (file)
@@ -243,20 +243,28 @@ class Timetracker_Controller_Timesheet extends Tinebase_Controller_Record_Abstra
      */
     protected function _checkGrant($_record, $_action, $_throw = TRUE, $_errorMessage = 'No Permission.', $_oldRecord = NULL)
     {
+        $isAdmin = false;
         // users with MANAGE_TIMEACCOUNTS have all grants here
         if ( $this->checkRight(Timetracker_Acl_Rights::MANAGE_TIMEACCOUNTS, FALSE)
-             || Timetracker_Model_TimeaccountGrants::hasGrant($_record->timeaccount_id, Tinebase_Model_Grants::GRANT_ADMIN)) {
-            return TRUE;
+            || Timetracker_Model_TimeaccountGrants::hasGrant($_record->timeaccount_id, Tinebase_Model_Grants::GRANT_ADMIN)) {
+            $isAdmin = true;
         }
-        
-        // only TA managers are allowed to alter TS of closed TAs
+
+        // only TA managers are allowed to alter TS of closed TAs, but they have to confirm first that they really want to do it
         if ($_action != 'get') {
             $timeaccount = Timetracker_Controller_Timeaccount::getInstance()->get($_record->timeaccount_id);
             if (! $timeaccount->is_open) {
                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
                         . ' This Timeaccount is already closed!');
+
+                if ($isAdmin === true) {
+                    if (is_array($this->_requestContext) && isset($this->_requestContext['skipClosedCheck']) && $this->_requestContext['skipClosedCheck']) {
+                        return true;
+                    }
+                }
+
                 if ($_throw) {
-                    throw new Tinebase_Exception_AccessDenied('This Timeaccount is already closed!');
+                    throw new Timetracker_Exception_ClosedTimeaccount();
                 }
                 return FALSE;
             }
@@ -267,6 +275,11 @@ class Timetracker_Controller_Timesheet extends Tinebase_Controller_Record_Abstra
                 $this->_fieldGrants['is_billable']['requiredGrant'] = Tinebase_Model_Grants::GRANT_ADMIN;
             }
         }
+
+        if ($isAdmin === true) {
+            return true;
+        }
+
         
         $hasGrant = FALSE;
         
diff --git a/tine20/Timetracker/Exception/ClosedTimeaccount.php b/tine20/Timetracker/Exception/ClosedTimeaccount.php
new file mode 100644 (file)
index 0000000..adb7e62
--- /dev/null
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Timetracker
+ * @subpackage  Exception
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2016 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Paul Mehrer <p.mehrer@metaways.de>
+ *
+ */
+
+/**
+ * Deadline exception
+ *
+ * @package     Timetracker
+ * @subpackage  Exception
+ */
+class Timetracker_Exception_ClosedTimeaccount extends Tinebase_Exception_AccessDenied
+{
+    /**
+     * create new Deadline exception
+     *
+     * @param string $_message
+     * @param integer $_code
+     * @return void
+     */
+    public function __construct($_message = 'This Timeaccount is already closed!', $_code = 403) {
+        parent::__construct($_message, $_code);
+    }
+}
index a26d63c..ac0d06b 100644 (file)
@@ -255,10 +255,12 @@ class Timetracker_Frontend_Json extends Tinebase_Frontend_Json_Abstract
      * creates/updates a record
      *
      * @param  array $recordData
+     * @param  array $context
      * @return array created/updated record
      */
-    public function saveTimesheet($recordData)
+    public function saveTimesheet($recordData, array $context = array())
     {
+        $this->_timesheetController->setRequestContext($context);
         return $this->_save($recordData, $this->_timesheetController, 'Timesheet');
     }
 
index ec8c133..6f4b4ac 100644 (file)
@@ -26,6 +26,7 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
     evalGrants: false,
     useInvoice: false,
     displayNotes: true,
+    context: { 'skipClosedCheck': false },
     
     /**
      * overwrite update toolbars function (we don't have record grants yet)
@@ -279,6 +280,17 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
     },
     
     /**
+     * returns additional save params
+     *
+     * @returns {{checkBusyConflicts: boolean}}
+     */
+    getAdditionalSaveParams: function() {
+        return {
+            context: this.context
+        };
+    },
+    
+    /**
      * show error if request fails
      * 
      * @param {} response
@@ -295,11 +307,33 @@ Tine.Timetracker.TimesheetEditDialog = Ext.extend(Tine.widgets.dialog.EditDialog
                 String.format(this.app.i18n._('Could not save {0}.'), this.i18nRecordName) 
                     + ' ( ' + this.app.i18n._('Booking deadline for this Timeaccount has been exceeded.') /* + ' ' + response.message  */ + ')'
             );
+        } else if (response.code && response.code == 403) {
+            //Time Account is closed
+            console.warn(this.grants);
+            if(Tine.Tinebase.common.hasRight('manage', 'Timetracker', 'timeaccounts')) {
+                this.onClosedWarning.apply(this, arguments);
+            } else {
+                Ext.MessageBox.alert(
+                    this.app.i18n._('Closed Timeaccount Warning!'), 
+                    String.format(this.app.i18n._('The selected Time Account is already closed.'))
+                );
+            }
         } else {
             // call default exception handler
             Tine.Tinebase.ExceptionHandler.handleRequestException(response);
         }
         this.loadMask.hide();
+    },
+    
+    onClosedWarning: function() {
+        Ext.Msg.confirm(this.app.i18n._('Closed Timeaccount Warning!'),
+            this.app.i18n._('The selected Time Account is already closed. Do you wish to continue anyway?'),
+            function(btn) {
+                if (btn == 'yes') {
+                    this.context = { 'skipClosedCheck': true };
+                    this.onApplyChanges(true);
+                }
+            }, this);
     }
 });
 
index 1db235e..6414133 100755 (executable)
@@ -58,6 +58,26 @@ abstract class Tinebase_Controller_Abstract extends Tinebase_Pluggable_Abstract
      * @var array|null
      */
     protected $_modelsUsingPath = null;
+
+    /**
+     * request context information
+     *
+     * @var array|null
+     */
+    protected $_requestContext = null;
+
+    public function setRequestContext(array $context)
+    {
+        $this->_requestContext = $context;
+    }
+
+    /**
+     * @return array|null
+     */
+    public function getRequestContext()
+    {
+        return $this->_requestContext;
+    }
     
     /**
      * generic check admin rights function