Beispiel #1
0
 /**
  * Creates transactions from clockings.
  *
  * Plugin event "transaction.add" data:
  * - "user": { ... }      // The user object
  * - "clockings": [ ... ]
  * - "booking_types": { identifier: {...}, ... }
  * - "bookings": {}        // Receives the bookings to be created
  * - "transactions": {}    // Receives the transactions to be created
  *
  * @param array $clockingIds
  * @param bool $commit Optional. If FALSE, only data about the transaction
  *     will be returned without saving them to the database. That data can
  *     be used to manually create a transaction.
  *     Default is TRUE.
  * @return array An array with IDs of clocking that have been booked.
  * @throws Exception
  */
 public function do_add(array $clockingIds, $commit = true)
 {
     if (empty($clockingIds)) {
         throw new Exception('No clockings specified.');
     }
     $usedClockingIds = array();
     // Return value for $commit == FALSE:
     // An associative array mapping user IDs to associative arrays with the
     // items "bookings" and "transactions":
     // {
     //     [user-id-1]: {
     //         "bookings"    : {
     //             [ref-1]   : { ... }
     //         },
     //         "transactions": [
     //         ]
     //     }
     // }
     $resultData = array();
     $con = Propel::getConnection();
     if (!$con->beginTransaction()) {
         throw new Exception('Could not start transaction.');
     }
     try {
         $authUser = $this->requireUser();
         $authUserId = $authUser->getId();
         $isAdmin = $authUser->isAdmin();
         if (!$isAdmin) {
             throw new Exception('Non-administrative user "' . $authUser->getFQN($con) . '" cannot create transactions.');
         }
         $clockings = ClockingAPI::createClockingQuery($authUser, $con)->joinWith('Domain.Account')->joinWith('ClockingType')->filterById($clockingIds, Criteria::IN)->addAscendingOrderByColumn(ClockingPeer::START)->addAscendingOrderByColumn(ClockingPeer::END)->addAscendingOrderByColumn(ClockingTypePeer::IDENTIFIER)->addAscendingOrderByColumn(ClockingPeer::ID)->find($con);
         // Check for missing clockings
         $missingIds = array_diff_key(array_fill_keys($clockingIds, true), $clockings->getArrayCopy('Id'));
         if (!empty($missingIds)) {
             throw new Exception('Could not find clockings with the IDs ' . implode(', ', $missingIds) . '.');
         }
         // Lock clocking records by writing to them
         $this->freezeClockings(true, $clockings, $con);
         // Group clockings by user
         $clockingDataByUserId = array();
         foreach ($clockings as $clocking) {
             $clockingDataByUserId[$clocking->getUserId()][] = EntityArray::from($clocking) + array('Type' => EntityArray::from($clocking->getClockingType()));
         }
         $typeDataByAccount = array();
         foreach ($clockingDataByUserId as $userId => $userClockingData) {
             $user = UserQuery::create()->findPk($userId, $con);
             $userData = array_diff_key($user->toArray(), array('PasswordHash' => true));
             $account = $user->getAccount($con);
             $accountId = $account->getId();
             if (isset($typeDataByAccount[$accountId])) {
                 list($typesById, $typeData) = $typeDataByAccount[$accountId];
             } else {
                 $types = BookingTypeQuery::create()->findByAccountId($account->getId(), $con);
                 $typesById = $types->getArrayCopy('Id');
                 $typeData = $types->toArray('Identifier');
                 $typeDataByAccount[$accountId] = array($typesById, $typeData);
             }
             $data = PluginPeer::fireEvent($authUser, 'transaction', 'add', array('user' => $userData, 'clockings' => $userClockingData, 'booking_types' => $typeData, 'bookings' => array(), 'transactions' => array()), $con);
             if (!isset($data['bookings'])) {
                 $data['bookings'] = array();
             }
             if (empty($data['transactions'])) {
                 // Ignore if there are no bookings either, otherwise fail
                 if (!empty($data['bookings']) and is_array($data['bookings'])) {
                     throw new Exception('Plugins created ' . count($data['bookings']) . ' booking(s) not linked to any transactions.');
                 }
             } elseif (!is_array($data['bookings']) or !is_array($data['transactions'])) {
                 throw new Exception('Plugins must return array data in variables "bookings" and "transactions".');
             } else {
                 // Create bookings and transactions
                 list($userClockingIds, $transactions) = $this->createBookingsTransactions($authUser, $user, $data['bookings'], $typesById, $data['transactions'], $con);
                 $usedClockingIds += $userClockingIds;
                 $resultData[$userId] = array('bookings' => array(), 'transactions' => EntityArray::from($transactions, $con));
             }
         }
         $this->freezeClockings(false, $clockings, $con);
     } catch (Exception $e) {
         $con->rollBack();
         throw $e;
     }
     if (!$commit) {
         $con->rollBack();
         return $resultData;
     }
     if (!$con->commit()) {
         throw new Exception('Could not commit transaction.');
     }
     return array_keys($usedClockingIds);
 }
Beispiel #2
0
 /**
  * Removes a clocking entry
  *
  * @param int $id The clocking ID
  */
 public function do_remove($id)
 {
     $con = Propel::getConnection();
     if (!$con->beginTransaction()) {
         throw new Exception('Could not start transaction.');
     }
     try {
         $authUser = $this->requireUser($con);
         $clocking = $this->getClockingById($id, $con);
         if ($clocking->getBooked()) {
             throw new Exception('Cannot remove clocking entry #' . $id . ' because it already has bookings. Please remove the transactions instead.');
         }
         if (!$clocking->isOpen() and !$authUser->isAdmin()) {
             $account = $authUser->getAccount($con);
             if ($account === null) {
                 throw new Exception('Could not get account of user #' . $authUser->getId() . ' "' . $authUser->getFQN($con) . '".');
             }
             $type = $clocking->getClockingType($con);
             if ($type === null) {
                 throw new Exception('Could not get clocking type with ID #' . $clocking->getTypeId() . '.');
             }
             $this->validateTimeLimits($account, $authUser, $clocking, $con);
         }
         $clockingUser = $clocking->getUserRelatedByUserId($con);
         if ($clockingUser === null) {
             throw new Exception('Could not determine clocking\'s assigned user #' . $clocking->getUserId() . '.');
         }
         PluginPeer::fireEvent($clockingUser, 'clocking', 'remove', EntityArray::from($clocking, $con), $con);
         $clocking->setDeleted(1)->save($con);
     } catch (Exception $e) {
         $con->rollBack();
         throw $e;
     }
     if (!$con->commit()) {
         throw new Exception('Could not commit transaction.');
     }
     return true;
 }