diff --git a/application/Espo/Core/ControllerManager.php b/application/Espo/Core/ControllerManager.php index f80e7c86bd..a86e5c7f10 100644 --- a/application/Espo/Core/ControllerManager.php +++ b/application/Espo/Core/ControllerManager.php @@ -99,7 +99,7 @@ class ControllerManager } if (!method_exists($controller, $primaryActionMethodName)) { - throw new NotFound("Action '$actionName' (".$request->getMethod().") does not exist in controller '$controller'"); + throw new NotFound("Action '$actionName' (".$request->getMethod().") does not exist in controller '$controllerName'"); } if (method_exists($controller, $beforeMethodName)) { diff --git a/application/Espo/Core/Controllers/Record.php b/application/Espo/Core/Controllers/Record.php index 1c542206c7..bd432d3818 100644 --- a/application/Espo/Core/Controllers/Record.php +++ b/application/Espo/Core/Controllers/Record.php @@ -124,6 +124,9 @@ class Record extends Base $asc = $request->get('asc') === 'true'; $sortBy = $request->get('sortBy'); $q = $request->get('q'); + $primaryFilter = $request->get('primaryFilter'); + $textFilter = $request->get('textFilter'); + $boolFilterList = $request->get('boolFilterList'); if (empty($maxSize)) { $maxSize = self::MAX_SIZE_LIMIT; @@ -139,6 +142,9 @@ class Record extends Base 'asc' => $asc, 'sortBy' => $sortBy, 'q' => $q, + 'primaryFilter' => $primaryFilter, + 'textFilter' => $textFilter, + 'boolFilterList' => $boolFilterList ); if ($request->get('filter')) { $params['filter'] = $request->get('filter'); diff --git a/application/Espo/Core/SelectManagers/Base.php b/application/Espo/Core/SelectManagers/Base.php index 60012d5e27..358a219056 100644 --- a/application/Espo/Core/SelectManagers/Base.php +++ b/application/Espo/Core/SelectManagers/Base.php @@ -754,12 +754,6 @@ class Base } } - public function applyAccess(&$result) - { - $this->prepareResult($result); - $this->access($result); - } - public function applyTextFilter($textFilter, &$result) { $this->prepareResult($result); @@ -784,6 +778,12 @@ class Base ); } + public function applyAccess(&$result) + { + $this->prepareResult($result); + $this->access($result); + } + protected function boolFilters($params, &$result) { if (!empty($params['boolFilterList']) && is_array($params['boolFilterList'])) { @@ -795,8 +795,8 @@ class Base protected function primaryFilter($params, &$result) { - if (!empty($params['filter'])) { - $this->applyPrimaryFilter($params['filter'], $result); + if (!empty($params['primaryFilter'])) { + $this->applyPrimaryFilter($params['primaryFilter'], $result); } } diff --git a/application/Espo/Modules/Crm/Controllers/Activities.php b/application/Espo/Modules/Crm/Controllers/Activities.php index 9e8a92af3e..c5fdea2f23 100644 --- a/application/Espo/Modules/Crm/Controllers/Activities.php +++ b/application/Espo/Modules/Crm/Controllers/Activities.php @@ -52,6 +52,24 @@ class Activities extends \Espo\Core\Controllers\Base return $service->getEvents($userId, $from, $to); } + public function actionListUpcoming($params, $data, $request) + { + $service = $this->getService('Activities'); + + $userId = $request->get('userId'); + if (!$userId) { + $userId = $this->getUser()->id; + } + + $offset = intval($request->get('offset')); + $maxSize = intval($request->get('maxSize')); + + return $service->getUpcomingActivities($userId, array( + 'offset' => $offset, + 'maxSize' => $maxSize + )); + } + public function actionPopupNotifications() { $userId = $this->getUser()->id; diff --git a/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json b/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json index 11d3bd79ba..d16b89bcba 100644 --- a/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json +++ b/application/Espo/Modules/Crm/Resources/i18n/en_US/Global.json @@ -48,7 +48,8 @@ "OpportunitiesByStage": "Opportunities by Stage", "OpportunitiesByLeadSource": "Opportunities by Lead Source", "SalesByMonth": "Sales by Month", - "SalesPipeline": "Sales Pipeline" + "SalesPipeline": "Sales Pipeline", + "Activities": "My Activities" }, "labels": { "Create InboundEmail": "Create Inbound Email", diff --git a/application/Espo/Modules/Crm/Resources/metadata/dashlets/Activities.json b/application/Espo/Modules/Crm/Resources/metadata/dashlets/Activities.json new file mode 100644 index 0000000000..da278a93e5 --- /dev/null +++ b/application/Espo/Modules/Crm/Resources/metadata/dashlets/Activities.json @@ -0,0 +1,3 @@ +{ + "view":"crm:views/dashlets/activities" +} diff --git a/application/Espo/Modules/Crm/Services/Activities.php b/application/Espo/Modules/Crm/Services/Activities.php index 0d1deb5d32..4672ea7b66 100644 --- a/application/Espo/Modules/Crm/Services/Activities.php +++ b/application/Espo/Modules/Crm/Services/Activities.php @@ -34,7 +34,8 @@ class Activities extends \Espo\Core\Services\Base 'entityManager', 'user', 'metadata', - 'acl' + 'acl', + 'selectManagerFactory' ); protected function getPDO() @@ -62,6 +63,11 @@ class Activities extends \Espo\Core\Services\Base return $this->injections['metadata']; } + protected function getSelectManagerFactory() + { + return $this->getInjection('selectManagerFactory'); + } + protected function isPerson($scope) { return in_array($scope, ['Contact', 'Lead', 'User']); @@ -76,7 +82,7 @@ class Activities extends \Espo\Core\Services\Base meeting.parent_type AS 'parentType', meeting.parent_id AS 'parentId', meeting.status AS status, meeting.created_at AS createdAt FROM `meeting` LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = meeting.assigned_user_id - JOIN `meeting_user` AS `usersMiddle` ON usersMiddle.meeting_id = meeting.id AND usersMiddle.deleted = 0 + JOIN `meeting_user` AS `usersMiddle` ON usersMiddle.meeting_id = meeting.id AND usersMiddle.deleted = 0 AND usersMiddle.status <> 'Declined' WHERE meeting.deleted = 0 AND usersMiddle.user_id = ".$pdo->quote($id)." "; if (!empty($notIn)) { @@ -96,7 +102,7 @@ class Activities extends \Espo\Core\Services\Base call.parent_type AS 'parentType', call.parent_id AS 'parentId', call.status AS status, call.created_at AS createdAt FROM `call` LEFT JOIN `user` AS `assignedUser` ON assignedUser.id = call.assigned_user_id - JOIN `call_user` AS `usersMiddle` ON usersMiddle.call_id = call.id AND usersMiddle.deleted = 0 + JOIN `call_user` AS `usersMiddle` ON usersMiddle.call_id = call.id AND usersMiddle.deleted = 0 AND usersMiddle.status <> 'Declined' WHERE call.deleted = 0 AND usersMiddle.user_id = ".$pdo->quote($id)." "; if (!empty($notIn)) { @@ -428,8 +434,6 @@ class Activities extends \Espo\Core\Services\Base "; } - - $sth = $pdo->prepare($qu); if (!empty($params['maxSize'])) { @@ -508,10 +512,13 @@ class Activities extends \Espo\Core\Services\Base $fetchAll = empty($params['scope']); - $parts = array( - 'Meeting' => ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'NOT IN', ['Held', 'Not Held']) : [], - 'Call' => ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'NOT IN', ['Held', 'Not Held']) : [], - ); + $parts = array(); + if ($this->getAcl()->checkScope('Meeting')) { + $parts['Meeting'] = ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'NOT IN', ['Held', 'Not Held']) : []; + } + if ($this->getAcl()->checkScope('Call')) { + $parts['Call'] = ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'NOT IN', ['Held', 'Not Held']) : []; + } return $this->getResult($parts, $scope, $id, $params); } @@ -523,11 +530,16 @@ class Activities extends \Espo\Core\Services\Base $fetchAll = empty($params['scope']); - $parts = array( - 'Meeting' => ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'IN', ['Held', 'Not Held']) : [], - 'Call' => ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'IN', ['Held', 'Not Held']) : [], - 'Email' => ($fetchAll || $params['scope'] == 'Email') ? $this->getEmailQuery($scope, $id, 'IN', ['Archived', 'Sent']) : [], - ); + $parts = array(); + if ($this->getAcl()->checkScope('Meeting')) { + $parts['Meeting'] = ($fetchAll || $params['scope'] == 'Meeting') ? $this->getMeetingQuery($scope, $id, 'IN', ['Held', 'Not Held']) : []; + } + if ($this->getAcl()->checkScope('Call')) { + $parts['Call'] = ($fetchAll || $params['scope'] == 'Call') ? $this->getCallQuery($scope, $id, 'IN', ['Held', 'Not Held']) : []; + } + if ($this->getAcl()->checkScope('Email')) { + $parts['Email'] = ($fetchAll || $params['scope'] == 'Email') ? $this->getEmailQuery($scope, $id, 'IN', ['Archived', 'Sent']) : []; + } $result = $this->getResult($parts, $scope, $id, $params); foreach ($result['list'] as &$item) { @@ -680,5 +692,94 @@ class Activities extends \Espo\Core\Services\Base } return $result; } + + public function getUpcomingActivities($userId, $params) + { + $user = $this->getEntityManager()->getEntity('User', $userId); + $this->accessCheck($user); + + $entityTypeList = ['Meeting', 'Call']; + + $unionPartList = []; + foreach ($entityTypeList as $entityType) { + if (!$this->getAcl()->checkScope($entityType, 'read')) { + continue; + } + + + $selectParams = array( + 'select' => ['id', 'name', 'dateStart', ['VALUE:' . $entityType, 'entityType']], + ); + + $selectManager = $this->getSelectManagerFactory()->create($entityType); + + + $selectManager->applyAccess($selectParams); + $selectManager->applyTextFilter($query, $selectParams); + $selectManager->applyPrimaryFilter('planned', $selectParams); + $selectManager->applyBoolFilter('onlyMy', $selectParams); + $selectManager->applyWhere(array( + '1' => array( + 'type' => 'or', + 'value' => array( + '1' => array( + 'type' => 'today', + 'field' => 'dateStart', + 'dateTime' => true + ), + '2' => array( + 'type' => 'future', + 'field' => 'dateEnd', + 'dateTime' => true + ) + ) + ) + ), $selectParams); + + $sql = $this->getEntityManager()->getQuery()->createSelectQuery($entityType, $selectParams); + + $unionPartList[] = '' . $sql . ''; + } + if (empty($unionPartList)) { + return array( + 'total' => 0, + 'list' => [] + ); + } + + $pdo = $this->getEntityManager()->getPDO(); + + $unionSql = implode(' UNION ', $unionPartList); + + $countSql = "SELECT COUNT(*) AS 'COUNT' FROM ({$unionSql}) AS c"; + $sth = $pdo->prepare($countSql); + $sth->execute(); + $row = $sth->fetch(\PDO::FETCH_ASSOC); + $totalCount = $row['COUNT']; + + $unionSql .= " ORDER BY dateStart ASC"; + $unionSql .= " LIMIT :offset, :maxSize"; + + $sth = $pdo->prepare($unionSql); + + $sth->bindParam(':offset', intval($params['offset']), \PDO::PARAM_INT); + $sth->bindParam(':maxSize', intval($params['maxSize']), \PDO::PARAM_INT); + $sth->execute(); + $rows = $sth->fetchAll(\PDO::FETCH_ASSOC); + + $entityDataList = []; + + foreach ($rows as $row) { + $entity = $this->getEntityManager()->getEntity($row['entityType'], $row['id']); + $entityData = $entity->toArray(); + $entityData['_scope'] = $entity->getEntityType(); + $entityDataList[] = $entityData; + } + + return array( + 'total' => $totalCount, + 'list' => $entityDataList + ); + } } diff --git a/frontend/client/modules/crm/src/views/dashlets/activities.js b/frontend/client/modules/crm/src/views/dashlets/activities.js new file mode 100644 index 0000000000..f9f2f96310 --- /dev/null +++ b/frontend/client/modules/crm/src/views/dashlets/activities.js @@ -0,0 +1,185 @@ +/************************************************************************ + * This file is part of EspoCRM. + * + * EspoCRM - Open Source CRM application. + * Copyright (C) 2014-2015 Yuri Kuznetsov, Taras Machyshyn, Oleksiy Avramenko + * Website: http://www.espocrm.com + * + * EspoCRM is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EspoCRM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EspoCRM. If not, see http://www.gnu.org/licenses/. + ************************************************************************/ + +Espo.define('crm:views/dashlets/activities', ['views/dashlets/abstract/base', 'multi-collection'], function (Dep, MultiCollection) { + + return Dep.extend({ + + name: 'Activities', + + _template: '