From 5eb4d27a8365599201109e73a6d65ed59f18f622 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 31 Mar 2020 16:31:28 +0300 Subject: [PATCH 1/5] fix mass email smtp account field --- client/modules/crm/src/views/mass-email/fields/smtp-account.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/modules/crm/src/views/mass-email/fields/smtp-account.js b/client/modules/crm/src/views/mass-email/fields/smtp-account.js index 4b0bc7409a..d50dfe2527 100644 --- a/client/modules/crm/src/views/mass-email/fields/smtp-account.js +++ b/client/modules/crm/src/views/mass-email/fields/smtp-account.js @@ -73,7 +73,7 @@ define('crm:views/mass-email/fields/smtp-account', 'views/fields/enum', function }, getValueForDisplay: function () { - if (!this.model.has(this.name)) { + if (!this.model.has(this.name) && this.isReadMode()) { if (this.model.has('inboundEmailId')) { if (this.model.get('inboundEmailId')) { return 'inboundEmail:' + this.model.get('inboundEmailId'); From 107856f57db017b97a6c7e5491e6d55dc2c540e7 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 31 Mar 2020 17:29:08 +0300 Subject: [PATCH 2/5] target list changes --- .../Espo/Modules/Crm/Services/TargetList.php | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/application/Espo/Modules/Crm/Services/TargetList.php b/application/Espo/Modules/Crm/Services/TargetList.php index 321eca8340..148b1b575a 100644 --- a/application/Espo/Modules/Crm/Services/TargetList.php +++ b/application/Espo/Modules/Crm/Services/TargetList.php @@ -41,11 +41,13 @@ class TargetList extends \Espo\Services\Record protected $duplicatingLinkList = ['accounts', 'contacts', 'leads', 'users']; + protected $targetsLinkList = ['accounts', 'contacts', 'leads', 'users']; + protected $entityTypeLinkMap = array( 'Lead' => 'leads', 'Account' => 'accounts', 'Contact' => 'contacts', - 'User' => 'users' + 'User' => 'users', ); protected $linkSelectParams = [ @@ -95,10 +97,9 @@ class TargetList extends \Espo\Services\Record protected function loadEntryCountField(Entity $entity) { $count = 0; - $count += $this->getEntityManager()->getRepository('TargetList')->countRelated($entity, 'contacts'); - $count += $this->getEntityManager()->getRepository('TargetList')->countRelated($entity, 'leads'); - $count += $this->getEntityManager()->getRepository('TargetList')->countRelated($entity, 'users'); - $count += $this->getEntityManager()->getRepository('TargetList')->countRelated($entity, 'accounts'); + foreach ($this->targetsLinkList as $link) { + $count += $this->getEntityManager()->getRepository('TargetList')->countRelated($entity, $link); + } $entity->set('entryCount', $count); } @@ -106,25 +107,13 @@ class TargetList extends \Espo\Services\Record { $count = 0; - $count += $this->getEntityManager()->getRepository('Contact')->join(['targetLists'])->where([ - 'targetListsMiddle.targetListId' => $entity->id, - 'targetListsMiddle.optedOut' => 1 - ])->count(); - - $count += $this->getEntityManager()->getRepository('Lead')->join(['targetLists'])->where([ - 'targetListsMiddle.targetListId' => $entity->id, - 'targetListsMiddle.optedOut' => 1 - ])->count(); - - $count += $this->getEntityManager()->getRepository('Account')->join(['targetLists'])->where([ - 'targetListsMiddle.targetListId' => $entity->id, - 'targetListsMiddle.optedOut' => 1 - ])->count(); - - $count += $this->getEntityManager()->getRepository('User')->join(['targetLists'])->where([ - 'targetListsMiddle.targetListId' => $entity->id, - 'targetListsMiddle.optedOut' => 1 - ])->count(); + foreach ($this->targetsLinkList as $link) { + $foreignEntityType = $entity->getRelationParam($link, 'entity'); + $count += $this->getEntityManager()->getRepository($foreignEntityType)->join(['targetLists'])->where([ + 'targetListsMiddle.targetListId' => $entity->id, + 'targetListsMiddle.optedOut' => 1, + ])->count(); + } $entity->set('optedOutCount', $count); } @@ -237,7 +226,7 @@ class TargetList extends \Espo\Services\Record } if ($sql) { if ($pdo->query($sql)) { - $this->getInjection('hookManager')->process('TargetList', 'afterUnlinkAll', $entity, array(), array('link' => $link)); + $this->getInjection('hookManager')->process('TargetList', 'afterUnlinkAll', $entity, [], ['link' => $link]); return true; } } @@ -324,12 +313,12 @@ class TargetList extends \Espo\Services\Record if (!$target) { throw new NotFound(); } - $map = array( + $map = [ 'Account' => 'accounts', 'Contact' => 'contacts', 'Lead' => 'leads', - 'User' => 'users' - ); + 'User' => 'users', + ]; if (empty($map[$targetType])) { throw new Error(); @@ -363,12 +352,12 @@ class TargetList extends \Espo\Services\Record if (!$target) { throw new NotFound(); } - $map = array( + $map = [ 'Account' => 'accounts', 'Contact' => 'contacts', 'Lead' => 'leads', - 'User' => 'users' - ); + 'User' => 'users', + ]; if (empty($map[$targetType])) { throw new Error(); From 4dc289f2bced3a3bd2e726e9376f86b73adead3a Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 31 Mar 2020 17:38:49 +0300 Subject: [PATCH 3/5] css fix --- frontend/less/espo/custom.less | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/less/espo/custom.less b/frontend/less/espo/custom.less index 301875dac4..a86db15223 100644 --- a/frontend/less/espo/custom.less +++ b/frontend/less/espo/custom.less @@ -1646,6 +1646,7 @@ table.no-margin { .list-expanded > ul > li .list-row-buttons { margin-right: -12px; margin-top: -10px; + margin-bottom: -5px; } .show-more > .btn { From 2a0b8957fc46dcb3c8db3f0419d911820ee77966 Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Tue, 31 Mar 2020 17:45:42 +0300 Subject: [PATCH 4/5] mass email memory usage improvements --- .../Espo/Modules/Crm/Services/MassEmail.php | 172 ++++++------------ 1 file changed, 60 insertions(+), 112 deletions(-) diff --git a/application/Espo/Modules/Crm/Services/MassEmail.php b/application/Espo/Modules/Crm/Services/MassEmail.php index 2f461caaff..713ebf6475 100644 --- a/application/Espo/Modules/Crm/Services/MassEmail.php +++ b/application/Espo/Modules/Crm/Services/MassEmail.php @@ -52,6 +52,8 @@ class MassEmail extends \Espo\Services\Record protected $mandatorySelectAttributeList = ['campaignId']; + protected $targetsLinkList = ['accounts', 'contacts', 'leads', 'users']; + protected function init() { parent::init(); @@ -80,10 +82,16 @@ class MassEmail extends \Espo\Services\Record protected function afterDeleteEntity(Entity $massEmail) { parent::afterDeleteEntity($massEmail); - $existingQueueItemList = $this->getEntityManager()->getRepository('EmailQueueItem')->where(array( + + $this->cleanupQueueItems($massEmail); + } + + protected function cleanupQueueItems(Entity $massEmail) + { + $existingQueueItemList = $this->getEntityManager()->getRepository('EmailQueueItem')->select(['id'])->where([ 'status' => ['Pending', 'Failed'], - 'massEmailId' => $massEmail->id - ))->find(); + 'massEmailId' => $massEmail->id, + ])->find(); foreach ($existingQueueItemList as $existingQueueItem) { $this->getEntityManager()->getMapper('RDB')->deleteFromDb('EmailQueueItem', $existingQueueItem->id); } @@ -95,27 +103,27 @@ class MassEmail extends \Espo\Services\Record throw new Error("Mass Email '".$massEmail->id."' should be 'Pending'."); } + $em = $this->getEntityManager(); + $pdo = $this->getEntityManager()->getPDO(); + if (!$isTest) { - $existingQueueItemList = $this->getEntityManager()->getRepository('EmailQueueItem')->where(array( - 'status' => ['Pending', 'Failed'], - 'massEmailId' => $massEmail->id - ))->find(); - foreach ($existingQueueItemList as $existingQueueItem) { - $this->getEntityManager()->getMapper('RDB')->deleteFromDb('EmailQueueItem', $existingQueueItem->id); - } + $this->cleanupQueueItems($massEmail); } - $metTargetHash = array(); - $metEmailAddressHash = array(); - $entityList = []; - - $pdo = $this->getEntityManager()->getPDO(); + $metTargetHash = []; + $metEmailAddressHash = []; + $itemList = []; if (!$isTest) { $excludingTargetListList = $massEmail->get('excludingTargetLists'); foreach ($excludingTargetListList as $excludingTargetList) { - foreach (['accounts', 'contacts', 'leads', 'users'] as $link) { - foreach ($excludingTargetList->get($link) as $excludingTarget) { + foreach ($this->targetsLinkList as $link) { + $excludingList = $em->getRepository('TargetList')->select(['id', 'emailAddress'])->findRelated( + $excludingTargetList, + $link + ); + + foreach ($excludingList as $excludingTarget) { $hashId = $excludingTarget->getEntityType() . '-'. $excludingTarget->id; $metTargetHash[$hashId] = true; $emailAddress = $excludingTarget->get('emailAddress'); @@ -127,109 +135,49 @@ class MassEmail extends \Espo\Services\Record } $targetListCollection = $massEmail->get('targetLists'); + foreach ($targetListCollection as $targetList) { - $accountList = $targetList->get('accounts', array( - 'additionalColumnsConditions' => array( - 'optedOut' => false - ) - )); - foreach ($accountList as $account) { - $hashId = $account->getEntityType() . '-'. $account->id; - $emailAddress = $account->get('emailAddress'); - if (empty($emailAddress)) { - continue; - } - if (!empty($metEmailAddressHash[$emailAddress])) { - continue; - } - if (!empty($metTargetHash[$hashId])) { - continue; - } + foreach ($this->targetsLinkList as $link) { + $recordList = $em->getRepository('TargetList')->select(['id', 'emailAddress'])->findRelated( + $targetList, + $link, + [ + 'additionalColumnsConditions' => ['optedOut' => false], + 'returnSthCollection' => true, + ] + ); - $entityList[] = $account; - $metTargetHash[$hashId] = true; - $metEmailAddressHash[$emailAddress] = true; - } + foreach ($recordList as $record) { + $hashId = $record->getEntityType() . '-'. $record->id; + $emailAddress = $record->get('emailAddress'); - $contactList = $targetList->get('contacts', array( - 'additionalColumnsConditions' => array( - 'optedOut' => false - ) - )); - foreach ($contactList as $contact) { - $hashId = $contact->getEntityType() . '-'. $contact->id; - $emailAddress = $contact->get('emailAddress'); - if (empty($emailAddress)) { - continue; - } - if (!empty($metEmailAddressHash[$emailAddress])) { - continue; - } - if (!empty($metTargetHash[$hashId])) { - continue; - } + if (!$emailAddress) continue; + if (!empty($metEmailAddressHash[$emailAddress])) continue; + if (!empty($metTargetHash[$hashId])) continue; - $entityList[] = $contact; - $metTargetHash[$hashId] = true; - $metEmailAddressHash[$emailAddress] = true; - } + $item = $record->getValueMap(); + $item->entityType = $record->getEntityType(); - $leadList = $targetList->get('leads', array( - 'additionalColumnsConditions' => array( - 'optedOut' => false - ) - )); - foreach ($leadList as $lead) { - $hashId = $lead->getEntityType() . '-'. $lead->id; - $emailAddress = $lead->get('emailAddress'); - if (empty($emailAddress)) { - continue; - } - if (!empty($metEmailAddressHash[$emailAddress])) { - continue; - } - if (!empty($metTargetHash[$hashId])) { - continue; - } + $itemList[] = $item; - $entityList[] = $lead; - $metTargetHash[$hashId] = true; - $metEmailAddressHash[$emailAddress] = true; - } - - $userList = $targetList->get('users', array( - 'additionalColumnsConditions' => array( - 'optedOut' => false - ) - )); - foreach ($userList as $user) { - $hashId = $user->getEntityType() . '-'. $user->id; - $emailAddress = $user->get('emailAddress'); - if (empty($emailAddress)) { - continue; + $metTargetHash[$hashId] = true; + $metEmailAddressHash[$emailAddress] = true; } - if (!empty($metEmailAddressHash[$emailAddress])) { - continue; - } - if (!empty($metTargetHash[$hashId])) { - continue; - } - - $entityList[] = $user; - $metTargetHash[$hashId] = true; - $metEmailAddressHash[$emailAddress] = true; } } } - foreach ($additionalTargetList as $target) { - $entityList[] = $target; + foreach ($additionalTargetList as $record) { + $item = $record->getValueMap(); + $item->entityType = $record->getEntityType(); + $itemList[] = $item; } - foreach ($entityList as $target) { - $emailAddress = $target->get('emailAddress'); - if (!$target->get('emailAddress')) continue; + foreach ($itemList as $item) { + $emailAddress = $item->emailAddress ?? null; + if (!$emailAddress) continue; if (strpos($emailAddress, 'ERASED:') === 0) continue; + $emailAddressRecord = $this->getEntityManager()->getRepository('EmailAddress')->getByAddress($emailAddress); if ($emailAddressRecord) { if ($emailAddressRecord->get('invalid') || $emailAddressRecord->get('optOut')) { @@ -238,19 +186,19 @@ class MassEmail extends \Espo\Services\Record } $queueItem = $this->getEntityManager()->getEntity('EmailQueueItem'); - $queueItem->set(array( + $queueItem->set([ 'massEmailId' => $massEmail->id, 'status' => 'Pending', - 'targetId' => $target->id, - 'targetType' => $target->getEntityType(), - 'isTest' => $isTest - )); + 'targetId' => $item->id, + 'targetType' => $item->entityType, + 'isTest' => $isTest, + ]); $this->getEntityManager()->saveEntity($queueItem); } if (!$isTest) { $massEmail->set('status', 'In Process'); - if (empty($entityList)) { + if (empty($itemList)) { $massEmail->set('status', 'Complete'); } From 2ecc9ac10fd628cae0b5bf6a39dd20291a5004bf Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Wed, 1 Apr 2020 10:23:36 +0300 Subject: [PATCH 5/5] mass email fix --- application/Espo/Modules/Crm/Services/MassEmail.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/application/Espo/Modules/Crm/Services/MassEmail.php b/application/Espo/Modules/Crm/Services/MassEmail.php index 713ebf6475..cdbac55159 100644 --- a/application/Espo/Modules/Crm/Services/MassEmail.php +++ b/application/Espo/Modules/Crm/Services/MassEmail.php @@ -118,9 +118,12 @@ class MassEmail extends \Espo\Services\Record $excludingTargetListList = $massEmail->get('excludingTargetLists'); foreach ($excludingTargetListList as $excludingTargetList) { foreach ($this->targetsLinkList as $link) { - $excludingList = $em->getRepository('TargetList')->select(['id', 'emailAddress'])->findRelated( + $excludingList = $em->getRepository('TargetList')->findRelated( $excludingTargetList, - $link + $link, + [ + 'select' => ['id', 'emailAddress'], + ] ); foreach ($excludingList as $excludingTarget) { @@ -138,12 +141,13 @@ class MassEmail extends \Espo\Services\Record foreach ($targetListCollection as $targetList) { foreach ($this->targetsLinkList as $link) { - $recordList = $em->getRepository('TargetList')->select(['id', 'emailAddress'])->findRelated( + $recordList = $em->getRepository('TargetList')->findRelated( $targetList, $link, [ 'additionalColumnsConditions' => ['optedOut' => false], 'returnSthCollection' => true, + 'select' => ['id', 'emailAddress'], ] );