diff --git a/api/BusinessLogic/Security/UserContext.php b/api/BusinessLogic/Security/UserContext.php index ec99ccf9..1f781023 100644 --- a/api/BusinessLogic/Security/UserContext.php +++ b/api/BusinessLogic/Security/UserContext.php @@ -29,4 +29,53 @@ class UserContext { public $rating; public $totalNumberOfReplies; public $active; + + /** + * Builds a user context based on the current session. **The session must be active!** + * @param $dataRow array the $_SESSION superglobal or the hesk_users result set + * @return UserContext the built user context + */ + static function fromDataRow($dataRow) { + $userContext = new UserContext(); + $userContext->id = $dataRow['id']; + $userContext->username = $dataRow['user']; + $userContext->admin = $dataRow['isadmin']; + $userContext->name = $dataRow['name']; + $userContext->email = $dataRow['email']; + $userContext->signature = $dataRow['signature']; + $userContext->language = $dataRow['language']; + $userContext->categories = explode(',', $dataRow['categories']); + $userContext->permissions = explode(',', $dataRow['heskprivileges']); + $userContext->autoAssign = $dataRow['autoassign']; + $userContext->ratingNegative = $dataRow['ratingneg']; + $userContext->ratingPositive = $dataRow['ratingpos']; + $userContext->rating = $dataRow['rating']; + $userContext->totalNumberOfReplies = $dataRow['replies']; + $userContext->active = $dataRow['active']; + + $preferences = new UserContextPreferences(); + $preferences->afterReply = $dataRow['afterreply']; + $preferences->autoStartTimeWorked = $dataRow['autostart']; + $preferences->autoreload = $dataRow['autoreload']; + $preferences->defaultNotifyCustomerNewTicket = $dataRow['notify_customer_new']; + $preferences->defaultNotifyCustomerReply = $dataRow['notify_customer_reply']; + $preferences->showSuggestedKnowledgebaseArticles = $dataRow['show_suggested']; + $preferences->defaultCalendarView = $dataRow['default_calendar_view']; + $preferences->defaultTicketView = $dataRow['default_list']; + $userContext->preferences = $preferences; + + $notifications = new UserContextNotifications(); + $notifications->newUnassigned = $dataRow['notify_new_unassigned']; + $notifications->newAssignedToMe = $dataRow['notify_new_my']; + $notifications->replyUnassigned = $dataRow['notify_reply_unassigned']; + $notifications->replyToMe = $dataRow['notify_reply_my']; + $notifications->ticketAssignedToMe = $dataRow['notify_assigned']; + $notifications->privateMessage = $dataRow['notify_pm']; + $notifications->noteOnTicketAssignedToMe = $dataRow['notify_note']; + $notifications->noteOnTicketNotAssignedToMe = $dataRow['notify_note_unassigned']; + $notifications->overdueTicketUnassigned = $dataRow['notify_overdue_unassigned']; + $userContext->notificationSettings = $notifications; + + return $userContext; + } } \ No newline at end of file diff --git a/api/BusinessLogic/Tickets/Autoassigner.php b/api/BusinessLogic/Tickets/Autoassigner.php index 514a9dda..4b8f654c 100644 --- a/api/BusinessLogic/Tickets/Autoassigner.php +++ b/api/BusinessLogic/Tickets/Autoassigner.php @@ -3,13 +3,35 @@ namespace BusinessLogic\Tickets; +use BusinessLogic\Security\UserContext; +use DataAccess\Categories\CategoryGateway; +use DataAccess\Security\UserGateway; + class Autoassigner { + /* @var $categoryGateway CategoryGateway */ + private $categoryGateway; + + /* @var $userGateway UserGateway */ + private $userGateway; + + function __construct($categoryGateway, $userGateway) { + $this->categoryGateway = $categoryGateway; + $this->userGateway = $userGateway; + } + /** * @param $categoryId int * @param $heskSettings array - * @return int|null The user ID, or null if no user found + * @return UserContext the user who is assigned, or null if no user should be assigned */ function getNextUserForTicket($categoryId, $heskSettings) { - return 0; + if (!$heskSettings['autoassign']) { + return null; + } + + $potentialUsers = $this->userGateway->getUsersByNumberOfOpenTickets($heskSettings); + + + return $potentialUsers[0]; } } \ No newline at end of file diff --git a/api/DataAccess/Security/UserGateway.php b/api/DataAccess/Security/UserGateway.php index be6e43ff..b32053b0 100644 --- a/api/DataAccess/Security/UserGateway.php +++ b/api/DataAccess/Security/UserGateway.php @@ -2,6 +2,7 @@ namespace DataAccess\Security; +use BusinessLogic\Security\UserContext; use BusinessLogic\Security\UserContextBuilder; use DataAccess\CommonDao; use Exception; @@ -61,4 +62,29 @@ class UserGateway extends CommonDao { return $row['email']; } + + function getUsersByNumberOfOpenTickets($heskSettings) { + $this->init(); + + $rs = hesk_dbQuery("SELECT `t1`.`id`,`t1`.`user`,`t1`.`name`, `t1`.`email`, `t1`.`language`, `t1`.`isadmin`, + `t1`.`categories`, `t1`.`notify_assigned`, `t1`.`heskprivileges`, + (SELECT COUNT(*) FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "tickets` + WHERE `owner`=`t1`.`id` + AND `status` IN ( + SELECT `ID` FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "statuses` + WHERE `IsClosed` = 0 + ) + ) AS `open_tickets` + FROM `" . hesk_dbEscape($heskSettings['db_pfix']) . "users` AS `t1` + WHERE `t1`.`autoassign` = '1' ORDER BY `open_tickets` ASC, RAND()"); + + $users = array(); + + while ($row = hesk_dbFetchAssoc($rs)) { + $user = UserContext::fromDataRow($row); + $users[] = $user; + } + + return $users; + } } \ No newline at end of file diff --git a/api/Tests/BusinessLogic/Tickets/AutoassignerTest.php b/api/Tests/BusinessLogic/Tickets/AutoassignerTest.php new file mode 100644 index 00000000..215adfee --- /dev/null +++ b/api/Tests/BusinessLogic/Tickets/AutoassignerTest.php @@ -0,0 +1,73 @@ +categoryGateway = $this->createMock(CategoryGateway::class); + $this->userGateway = $this->createMock(UserGateway::class); + $this->autoassigner = new Autoassigner($this->categoryGateway, $this->userGateway); + $this->heskSettings = array( + 'autoassign' => 1 + ); + } + + function testItReturnsNullWhenAutoassignIsDisabled() { + //-- Arrange + $this->heskSettings['autoassign'] = 0; + + //-- Act + $owner = $this->autoassigner->getNextUserForTicket(0, $this->heskSettings); + + //-- Assert + self::assertThat($owner, self::isNull()); + } + + function testItReturnsTheUsersWithLeastOpenTickets() { + //-- Arrange + $userWithNoOpenTickets = new UserContext(); + $userWithNoOpenTickets->id = 1; + $userWithNoOpenTickets->categories = array(1); + $userWithOneOpenTicket = new UserContext(); + $userWithOneOpenTicket->id = 2; + $userWithOneOpenTicket->categories = array(1); + $usersToReturn = array( + $userWithNoOpenTickets, + $userWithOneOpenTicket + ); + + $this->userGateway->method('getUsersByNumberOfOpenTickets') + ->with($this->heskSettings) + ->willReturn($usersToReturn); + + //-- Act + $actual = $this->autoassigner->getNextUserForTicket(1, $this->heskSettings); + + //-- Assert + self::assertThat($actual, self::equalTo($userWithNoOpenTickets)); + } +} diff --git a/api/Tests/BusinessLogic/Tickets/TicketCreatorTests/CreateTicketForCustomerTest.php b/api/Tests/BusinessLogic/Tickets/TicketCreatorTests/CreateTicketForCustomerTest.php index 23ab848c..f3072816 100644 --- a/api/Tests/BusinessLogic/Tickets/TicketCreatorTests/CreateTicketForCustomerTest.php +++ b/api/Tests/BusinessLogic/Tickets/TicketCreatorTests/CreateTicketForCustomerTest.php @@ -19,6 +19,7 @@ use BusinessLogic\Tickets\TrackingIdGenerator; use BusinessLogic\Tickets\VerifiedEmailChecker; use BusinessLogic\ValidationModel; use Core\Constants\Priority; +use DataAccess\Security\UserGateway; use DataAccess\Statuses\StatusGateway; use DataAccess\Tickets\TicketGateway; use PHPUnit\Framework\TestCase; @@ -90,6 +91,11 @@ class CreateTicketTest extends TestCase { */ private $emailSenderHelper; + /** + * @var $userGateway \PHPUnit_Framework_MockObject_MockObject + */ + private $userGateway; + protected function setUp() { $this->ticketGateway = $this->createMock(TicketGateway::class); $this->newTicketValidator = $this->createMock(NewTicketValidator::class); @@ -98,9 +104,11 @@ class CreateTicketTest extends TestCase { $this->statusGateway = $this->createMock(StatusGateway::class); $this->verifiedEmailChecker = $this->createMock(VerifiedEmailChecker::class); $this->emailSenderHelper = $this->createMock(EmailSenderHelper::class); + $this->userGateway = $this->createMock(UserGateway::class); $this->ticketCreator = new TicketCreator($this->newTicketValidator, $this->trackingIdGenerator, - $this->autoassigner, $this->statusGateway, $this->ticketGateway, $this->verifiedEmailChecker, $this->emailSenderHelper); + $this->autoassigner, $this->statusGateway, $this->ticketGateway, $this->verifiedEmailChecker, + $this->emailSenderHelper, $this->userGateway); $this->ticketRequest = new CreateTicketByCustomerModel(); $this->ticketRequest->name = 'Name';