From efe5cb23ed7857ee2774b2fcd1e27aa380ee6140 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Fri, 31 Jan 2014 17:45:38 +0200 Subject: [PATCH] inbound email 3 --- .../Business/CaseDistribution/LeastBusy.php | 55 ++++++++ .../Business/CaseDistribution/RoundRobin.php | 51 ++++++++ .../layouts/InboundEmail/detail.json | 7 + .../Resources/layouts/Lead/detailSmall.json | 20 ++- .../metadata/entityDefs/InboundEmail.json | 25 ++-- .../metadata/viewDefs/InboundEmail.json | 7 +- .../Modules/Crm/Services/InboundEmail.php | 122 ++++++++++++++---- application/Espo/ORM/Repositories/RDB.php | 2 +- application/Espo/Services/Stream.php | 3 + 9 files changed, 255 insertions(+), 37 deletions(-) create mode 100644 application/Espo/Modules/Crm/Business/CaseDistribution/LeastBusy.php create mode 100644 application/Espo/Modules/Crm/Business/CaseDistribution/RoundRobin.php diff --git a/application/Espo/Modules/Crm/Business/CaseDistribution/LeastBusy.php b/application/Espo/Modules/Crm/Business/CaseDistribution/LeastBusy.php new file mode 100644 index 0000000000..ef3ac89e9a --- /dev/null +++ b/application/Espo/Modules/Crm/Business/CaseDistribution/LeastBusy.php @@ -0,0 +1,55 @@ +entityManager = $entityManager; + } + + protected function getEntityManager() + { + return $this->entityManager; + } + + public function getUser($team) + { + $userList = $team->get('users'); + if (count($userList) == 0) { + return false; + } + + $countHash = array(); + + foreach ($userList as $user) { + $count = $this->getEntityManager()->getRepository('Case')->where(array( + 'assignedUserId' => $user->id, + 'status<>' => array('Closed', 'Rejected', 'Duplicated') + ))->count(); + $countHash[$user->id] = $count; + } + + $foundUserId = false; + $min = false; + foreach ($countHash as $userId => $count) { + if ($min === false) { + $min = $count; + $foundUserId = $userId; + } else { + if ($count < $min) { + $min = $clunt; + $foundUserId = $userId; + } + } + } + + if ($foundUserId !== false) { + return $this->getEntityManager()->getEntity('User', $foundUserId); + } + } +} + diff --git a/application/Espo/Modules/Crm/Business/CaseDistribution/RoundRobin.php b/application/Espo/Modules/Crm/Business/CaseDistribution/RoundRobin.php new file mode 100644 index 0000000000..eb243cd767 --- /dev/null +++ b/application/Espo/Modules/Crm/Business/CaseDistribution/RoundRobin.php @@ -0,0 +1,51 @@ +entityManager = $entityManager; + } + + protected function getEntityManager() + { + return $this->entityManager; + } + + public function getUser($team) + { + $userList = $team->get('users'); + if (count($userList) == 0) { + return false; + } + + $userIdList = array(); + + foreach ($userList as $user) { + $userIdList[] = $user->id; + } + + + $case = $this->getEntityManager()->getRepository('Case')->where(array( + 'assignedUserId' => $userIdList, + ))->order('createdAt', 'DESC')->findOne(); + + if (empty($case)) { + $num = 0; + } else { + $num = array_search($case->get('assignedUserId'), $userIdList); + if ($num === false || $num == count($userIdList) - 1) { + $num = 0; + } else { + $num++; + } + } + + return $this->getEntityManager()->getEntity('User', $userIdList[$num]); + } +} + diff --git a/application/Espo/Modules/Crm/Resources/layouts/InboundEmail/detail.json b/application/Espo/Modules/Crm/Resources/layouts/InboundEmail/detail.json index 6d44a66a34..dd4e286540 100644 --- a/application/Espo/Modules/Crm/Resources/layouts/InboundEmail/detail.json +++ b/application/Espo/Modules/Crm/Resources/layouts/InboundEmail/detail.json @@ -5,6 +5,13 @@ [ {"name":"name"}, {"name":"status"} + ], + [ + {"name":"team"} + + ], + [ + {"name":"assignToUser"} ] ] }, diff --git a/application/Espo/Modules/Crm/Resources/layouts/Lead/detailSmall.json b/application/Espo/Modules/Crm/Resources/layouts/Lead/detailSmall.json index c52329bf1b..6c52aebd62 100644 --- a/application/Espo/Modules/Crm/Resources/layouts/Lead/detailSmall.json +++ b/application/Espo/Modules/Crm/Resources/layouts/Lead/detailSmall.json @@ -1 +1,19 @@ -[{"label":"","rows":[[{"name":"name"}],[{"name":"emailAddress"}],[{"name":"phone"}]]}] +[ + { + "label":"", + "rows":[ + [ + {"name":"name"} + ], + [ + {"name":"emailAddress"} + ], + [ + {"name":"phone"} + ], + [ + {"name":"status"} + ] + ] + } +] diff --git a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/InboundEmail.json b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/InboundEmail.json index ed97dc6432..c73899704e 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/entityDefs/InboundEmail.json +++ b/application/Espo/Modules/Crm/Resources/metadata/entityDefs/InboundEmail.json @@ -30,17 +30,23 @@ "default": "INBOX" }, "trashFolder": { - "type": "varchar" + "type": "varchar", + "required": true, + "default": "INBOX.Trash" }, - "teams": { - "type": "linkMultiple" + "assignToUser": { + "type": "link" + }, + "team": { + "type": "link" }, "createCase": { "type": "bool" }, "caseDistribution": { "type": "enum", - "options": ["Round-Robin", "Least-Busy"] + "options": ["Direct-Assignment", "Round-Robin", "Least-Busy"], + "default": "Direct-Assignment" }, "reply": { "type": "bool" @@ -80,10 +86,13 @@ "type": "belongsTo", "entity": "User" }, - "teams": { - "type": "hasMany", - "entity": "Team", - "relationName": "EntityTeam" + "assignToUser": { + "type": "belongsTo", + "entity": "User" + }, + "team": { + "type": "belongsTo", + "entity": "Team" }, "replyEmailTemplate": { "type": "belongsTo", diff --git a/application/Espo/Modules/Crm/Resources/metadata/viewDefs/InboundEmail.json b/application/Espo/Modules/Crm/Resources/metadata/viewDefs/InboundEmail.json index 0967ef424b..f560d1f71b 100644 --- a/application/Espo/Modules/Crm/Resources/metadata/viewDefs/InboundEmail.json +++ b/application/Espo/Modules/Crm/Resources/metadata/viewDefs/InboundEmail.json @@ -1 +1,6 @@ -{} +{ + "recordViews":{ + "detail":"Crm:InboundEmail.Record.Detail", + "edit":"Crm:InboundEmail.Record.Edit" + } +} diff --git a/application/Espo/Modules/Crm/Services/InboundEmail.php b/application/Espo/Modules/Crm/Services/InboundEmail.php index d25291b14f..cf0445650b 100644 --- a/application/Espo/Modules/Crm/Services/InboundEmail.php +++ b/application/Espo/Modules/Crm/Services/InboundEmail.php @@ -28,7 +28,7 @@ class InboundEmail extends \Espo\Services\Record protected function findFolder($storage, $path) { - $arr = explode('/', $path); + $arr = explode('.', $path); $pointer = $storage->getFolders(); foreach ($arr as $folderName) { $pointer = $pointer->$folderName; @@ -40,7 +40,6 @@ class InboundEmail extends \Espo\Services\Record public function fetchFromMailServer($id) { $inboundEmail = $this->getEntity($id); - $inboundEmail->loadLinkMultipleField('teams'); if ($inboundEmail->get('status') != 'Active') { throw new Error(); @@ -58,11 +57,16 @@ class InboundEmail extends \Espo\Services\Record } $trash = null; - $trashFolder = $inboundEmail->get('trashFolder'); - if ($trashFolder) { - $trash = $this->findFolder($storage, $trashFolder); + $trashFolder = $inboundEmail->get('trashFolder'); + if (empty($trashFolder)) { + $trashFolder = 'INBOX.Trash'; } - + try { + $trash = $this->findFolder($storage, $trashFolder); + } catch (\Exception $e) { + throw new Error("No trash folder '{$trashFolder}' found for Inbound Email {$id}"); + } + $monitoredFolders = $inboundEmail->get('monitoredFolders'); if (empty($monitoredFolders)) { $monitoredFolders = 'INBOX'; @@ -82,9 +86,7 @@ class InboundEmail extends \Espo\Services\Record while ($storage->countMessages()) { if ($trash) { - $folder->moveMessage(1, $trash); - } else { - $storage->removeMessage(1); + $storage->moveMessage(1, $trash); } } } @@ -109,12 +111,19 @@ class InboundEmail extends \Espo\Services\Record try { $email = $this->getEntityManager()->getEntity('Email'); - $email->set('teamsIds', $inboundEmail->get('teamsIds')); + if ($inboundEmail->get('teamId')) { + $email->set('teamsIds', array($inboundEmail->get('teamId'))); + } + $email->set('isHtml', false); $email->set('name', $message->subject); $email->set('attachmentsIds', array()); - - $email->set('assignedUserId', $this->getUser()->id); + + $userId = $this->getUser()->id; + if ($inboundEmail->get('assignToUserId')) { + $userId = $inboundEmail->get('assignToUserId'); + } + $email->set('assignedUserId', $userId); $fromArr = $this->getAddressListFromMessage($message, 'from'); @@ -146,8 +155,6 @@ class InboundEmail extends \Espo\Services\Record $this->getEntityManager()->saveEntity($email); if ($inboundEmail->get('createCase')) { - // TODO check case exists - if (preg_match('/\[#([0-9]+)[^0-9]*\]/', $email->get('name'), $m)) { $caseNumber = $m[1]; $case = $this->getEntityManager()->getRepository('Case')->where(array( @@ -160,37 +167,96 @@ class InboundEmail extends \Espo\Services\Record $this->getServiceFactory()->create('Stream')->noteEmail($case, $email); } } else { - $case = $this->emailToCase($email, $inboundEmail->get('caseDistribution')); + $params = array( + 'caseDistribution' => $inboundEmail->get('caseDistribution'), + 'teamId' => $inboundEmail->get('teamId'), + 'userId' => $inboundEmail->get('assignToUserId'), + ); + $case = $this->emailToCase($email, $params); + $user = $this->getEntityManager()->getEntity('User', $case->get('assignedUserId')); if ($inboundEmail->get('reply')) { - $this->autoReply($inboundEmail, $email, $case); + $this->autoReply($inboundEmail, $email, $case, $user); } } } else { if ($inboundEmail->get('reply')) { - $this->autoReply($inboundEmail, $email); + $user = $this->getEntityManager()->getEntity('User', $userId); + $this->autoReply($inboundEmail, $email, $user); } } $result = true; } catch (\Exception $e){ - // TODO log - + // TODO log } return $result; } - protected function emailToCase(\Espo\Entities\Email $email, $caseDistribution = 'Round-Robin') + protected function assignRoundRobin($case, $team) + { + $roundRobin = new \Espo\Modules\Crm\Business\CaseDistribution\RoundRobin($this->getEntityManager()); + $user = $roundRobin->getUser($team); + if ($user) { + $case->set('assignedUserId', $user->id); + } + } + + protected function assignLeastBusy($case, $team) + { + $leastBusy = new \Espo\Modules\Crm\Business\CaseDistribution\LeastBusy($this->getEntityManager()); + $user = $leastBusy->getUser($team); + if ($user) { + $case->set('assignedUserId', $user->id); + } + } + + protected function emailToCase(\Espo\Entities\Email $email, array $params = array()) { $case = $this->getEntityManager()->getEntity('Case'); $case->populateDefaults(); $case->set('name', $email->get('name')); - $case->set('description', $email->get('bodyPlain')); - $case->set('teamsIds', $email->get('teamsIds')); + $case->set('description', $email->get('bodyPlain')); - // TODO distribution - $case->set('assignedUserId', $this->getUser()->id); + $userId = $this->getUser()->id; + if (!empty($params['userId'])) { + $userId = $params['userId']; + } + $case->set('assignedUserId', $userId); + + $teamId = false; + if (!empty($params['teamId'])) { + $teamId = $params['teamId']; + } + if ($teamId) { + $case->set('teamsIds', array($teamId)); + } + + $caseDistribution = 'Direct-Assignment'; + if (!empty($params['caseDistribution'])) { + $caseDistribution = $params['caseDistribution']; + } + + switch ($caseDistribution) { + case 'Round-Robin': + if ($teamId) { + $team = $this->getEntityManager()->getEntity('Team', $teamId); + if ($team) { + $this->assignRoundRobin($case, $team); + } + } + break; + case 'Least-Busy': + if ($teamId) { + $team = $this->getEntityManager()->getEntity('Team', $teamId); + if ($team) { + $this->assignLeastBusy($case, $team); + } + } + break; + } + $contact = $this->getEntityManager()->getRepository('Contact')->where(array( 'EmailAddress.id' => $email->get('fromEmailAddressId') @@ -251,7 +317,7 @@ class InboundEmail extends \Espo\Services\Record } } - protected function autoReply($inboundEmail, $email, $case = null) + protected function autoReply($inboundEmail, $email, $case = null, $user = null) { try { $replyEmailTemplateId = $inboundEmail->get('replyEmailTemplateId'); @@ -270,7 +336,11 @@ class InboundEmail extends \Espo\Services\Record $entityHash['Person'] = $contact; - $entityHash['Contact'] = $contact; + $entityHash['Contact'] = $contact; + + if ($user) { + $entityHash['User'] = $user; + } $emailTemplateService = $this->getServiceFactory()->create('EmailTemplate'); diff --git a/application/Espo/ORM/Repositories/RDB.php b/application/Espo/ORM/Repositories/RDB.php index ad2b58c7e0..28e3cd3cc1 100644 --- a/application/Espo/ORM/Repositories/RDB.php +++ b/application/Espo/ORM/Repositories/RDB.php @@ -152,7 +152,7 @@ class RDB extends \Espo\ORM\Repository public function findOne(array $params = array()) { - $collection = $this->find($params); + $collection = $this->limit(0, 1)->find($params); if (count($collection)) { return $collection[0]; } diff --git a/application/Espo/Services/Stream.php b/application/Espo/Services/Stream.php index 425505aebb..5e91d3cb95 100644 --- a/application/Espo/Services/Stream.php +++ b/application/Espo/Services/Stream.php @@ -92,6 +92,9 @@ class Stream extends \Espo\Core\Services\Base public function followEntity(Entity $entity, $userId) { + if ($userId == 'system') { + return; + } if (!$this->getMetadata()->get('scopes.' . $entity->getEntityName() . '.stream')) { throw new Error(); }