9namespace Bitrix\Sender\Posting;
12use Bitrix\Main\Application;
13use Bitrix\Main\Config\Option;
16use Bitrix\Main\Localization\Loc;
18use Bitrix\Sender\Consent\Consent;
19use Bitrix\Sender\ContactTable;
20use Bitrix\Sender\Entity\Campaign;
21use Bitrix\Sender\Entity\Letter;
22use Bitrix\Sender\Integration;
23use Bitrix\Sender\Integration\Bitrix24;
24use Bitrix\Sender\Internals\Model;
25use Bitrix\Sender\Internals\Model\PostingThreadTable;
26use Bitrix\Sender\MailingTable;
27use Bitrix\Sender\Message;
28use Bitrix\Sender\Message\Adapter;
29use Bitrix\Sender\Posting\ThreadStrategy\AbstractThreadStrategy;
30use Bitrix\Sender\Posting\ThreadStrategy\IThreadStrategy;
31use Bitrix\Sender\PostingRecipientTable;
32use Bitrix\Sender\PostingTable;
33use Bitrix\Sender\Recipient;
34use Bitrix\Sender\Runtime\TimeLineJob;
35use Bitrix\Sender\Transport\iLimiter;
37Loc::loadMessages(__FILE__);
110 $this->checkStatusStep = (int)Option::get(
'sender',
'send_check_status_step', $this->checkStatusStep);
112 $this->message =
$letter->getMessage();
113 $this->message->getConfiguration()->set(
'LETTER_ID', $this->letter->getId());
151 return $this->resultCode;
162 $this->
load($this->letter->get(
'POSTING_ID'));
164 if (!$this->postingId)
166 $this->resultCode = self::RESULT_ERROR;
171 if ($this->threadStrategy->isProcessLimited())
173 $this->resultCode = static::RESULT_CONTINUE;
177 $threadState = $this->threadStrategy->checkThreads();
181 $this->isConsentSupport = $this->letter
184 ->get(
'APPROVE_CONFIRMATION') ===
'Y';
186 $this->threadStrategy->setPostingId($this->postingId);
190 $this->threadStrategy->fillThreads();
193 $this->threadStrategy->lockThread();
194 $threadId = $this->threadStrategy->getThreadId();
197 if (is_null($threadId))
199 if ($this->letter->get(
'WAITING_RECIPIENT') ===
'Y')
202 $this->resultCode = static::RESULT_WAITING_RECIPIENT;
206 if (!$this->threadStrategy->hasUnprocessedThreads())
209 $status = self::updateActualStatus(
214 $this->finalizePosting(
$status);
218 $this->resultCode = static::RESULT_CONTINUE;
222 if (static::lock($this->postingId, $threadId) ===
false)
225 throw new DB\Exception(Loc::getMessage(
'SENDER_POSTING_MANAGER_ERR_LOCK'));
230 $this->resultCode = static::RESULT_CONTINUE;
232 static::unlock($this->postingId, $threadId);
239 $this->resultCode = static::RESULT_WAITING_RECIPIENT;
241 static::unlock($this->postingId, $threadId);
250 $this->resultCode = static::RESULT_SENT;
252 static::unlock($this->postingId, $threadId);
259 $this->resultCode = static::RESULT_ERROR;
261 static::unlock($this->postingId, $threadId);
267 $this->resultCode = static::RESULT_CONTINUE;
268 static::unlock($this->postingId, $threadId);
273 $threadRecipients = $this->threadStrategy->getRecipients($this->limit);
274 $recipients = static::getRecipientsToSend($threadRecipients);
277 $this->message->getTransport()->setSendCount(
$count);
278 if (!$this->message->getTransport()->start())
284 $this->sendToRecipients($recipients);
286 $this->message->getTransport()->end();
289 self::unlock($this->postingId, $threadId);
291 $status = self::updateActualStatus(
294 $this->threadStrategy->hasUnprocessedThreads()
298 $threadRecipients->getSelectedRowsCount() === 0 ?
303 $this->threadStrategy->updateStatus($threadStatus);
305 if ($threadId < $this->threadStrategy->lastThreadId())
307 $this->resultCode = static::RESULT_CONTINUE;
312 if ($this->threadStrategy->hasUnprocessedThreads())
314 $this->resultCode = static::RESULT_CONTINUE;
319 $this->finalizePosting(
$status);
338 'MAILING_CHAIN_REITERATE' =>
'MAILING_CHAIN.REITERATE',
339 'MAILING_CHAIN_IS_TRIGGER' =>
'MAILING_CHAIN.IS_TRIGGER',
344 '=MAILING.ACTIVE' =>
'Y',
345 '=MAILING_CHAIN.STATUS' => [
346 Model\LetterTable::STATUS_SEND,
347 Model\LetterTable::STATUS_PLAN,
352 if ($postingData = $postingDb->fetch())
354 $this->postingId = $postingData[
'ID'];
355 $this->status = $postingData[
'STATUS'];
357 $this->mailingId = $postingData[
'MAILING_ID'];
358 $this->letterId = $postingData[
'MAILING_CHAIN_ID'];
359 $this->sendCount = $postingData[
'COUNT_SEND_ALL'];
361 $this->isReiterate = $postingData[
'MAILING_CHAIN_REITERATE'] ==
'Y';
362 $this->isTrigger = $postingData[
'MAILING_CHAIN_IS_TRIGGER'] ==
'Y';
378 $this->timeAtStart = microtime(
true);
387 if (!$this->postingId)
392 if ($this->isTrigger)
407 if (!$this->postingId)
424 $onBeforeStartResult = $this->message->onBeforeStart();
425 if ($onBeforeStartResult->isSuccess())
428 Model\PostingTable::update($this->postingId, [
'STATUS' => $this->status]);
432 static::updateActualStatus($this->postingId,
true);
435 $errorMessage = implode(
', ', $onBeforeStartResult->getErrorMessages());
438 Model\LetterTable::update($this->letterId, [
'ERROR_MESSAGE' =>
$errorMessage]);
475 if ($posting[
'DATE_SENT'] < $weekAgo)
481 '@STATUS' => $statusesToCheck,
491 elseif (!$hasStatusNone && !$awaitThread && !$hasStatusWait)
500 $postingUpdateFields = [
503 'COUNT_SEND_ALL' => 0
507 foreach ($recipientStatusToPostingFieldMap as $recipientStatus => $postingFieldName)
509 if (!array_key_exists($recipientStatus,
$statusList))
513 $postingUpdateFields[$postingFieldName] =
$statusList[$recipientStatus];
515 $postingUpdateFields[
'COUNT_SEND_ALL'] = array_sum(
$statusList);
516 Model\PostingTable::update(
$postingId, $postingUpdateFields);
529 public static function lock($id, $threadId = 0)
532 $threadId = intval($threadId);
534 $lockName = self::getSendpostLockName($id, $threadId);
536 return Application::getInstance()->getConnection()->lock($lockName);
547 private static function getSendpostLockName(
int $id,
int $threadId): string
549 return "sendpost_{$id}_{$threadId}";
559 return $this->message->getTransport()->isLimitsExceeded($this->message);
569 return $this->message->getTransport()->getExceededLimiter($this->message);
583 private function sendToRecipients($recipients)
589 foreach ($recipients as $recipient)
591 if ($this->isPrevented() || $this->isStoppedOnRun())
596 $this->setPostingDateSend();
598 if ($this->canDenySendToRecipient($recipient))
602 $eventData = $this->executeEvent($recipient, $sendResult);
604 elseif($this->canSendMessageToRecipient($recipient))
606 $sendResult = $this->sendToRecipient($recipient);
607 if ($this->isPrevented())
611 $sendResultStatus = ($sendResult ?
615 $this->updateRecipientStatus($recipient[
'ID'],$sendResultStatus);
616 $eventData = $this->executeEvent($recipient, $sendResult);
621 $sendResultStatus = (
629 $dataToInsert[] = $eventData;
631 if (Bitrix24\Service::isCloud() && $eventData[
'SEND_RESULT'] && $this->letter->getMessage()->getCode() === Message\iBase::CODE_MAIL)
633 Bitrix24\Limitation\DailyLimit::increment();
643 }
catch(\Exception $e)
650 Integration\EventHandler::onAfterPostingSendRecipientMultiple(
655 }
catch(\Exception $e)
665 return $this->isPrevented;
671 if (++$this->checkStatusCounter < $this->checkStatusStep)
676 $checkStatusDb = Model\LetterTable::getList(
680 '=ID' => $this->letterId,
681 '=STATUS' =>
Model\LetterTable::STATUS_SEND
685 if (!$checkStatusDb->fetch())
690 $this->checkStatusCounter = 0;
697 if ($this->letter->get(
'DATE_SEND'))
702 Model\PostingTable::update($this->postingId, [
'DATE_SEND' =>
new Type\
DateTime()]);
707 self::applyRecipientToMessage($this->message, $recipient);
711 'FIELDS' => $this->message->getFields(),
712 'TRACK_READ' => $this->message->getReadTracker()->getArray(),
713 'TRACK_CLICK' => $this->message->getClickTracker()->getArray(),
714 'MAILING_CHAIN_ID' => $this->letter->getId()
716 $linkDomain = $this->message->getReadTracker()->getLinkDomain();
719 $eventSendParams[
'LINK_DOMAIN'] = $linkDomain;
721 $event =
new Main\Event(
'sender',
'OnBeforePostingSendRecipient', [$eventSendParams]);
723 foreach (
$event->getResults() as $eventResult)
725 if ($eventResult->getType() ==
Main\EventResult::ERROR)
730 if (is_array($eventResult->getParameters()))
732 $eventSendParams = array_merge($eventSendParams, $eventResult->getParameters());
737 $this->message->setFields($eventSendParams[
'FIELDS']);
738 $this->message->getReadTracker()->setArray($eventSendParams[
'TRACK_READ']);
739 $this->message->getReadTracker()->setArray($eventSendParams[
'TRACK_CLICK']);
744 $sendResult = $this->message->send();
767 $message->getReadTracker()->setModuleId(
'sender')->setFields([
'RECIPIENT_ID' => $recipient[
"ID"]])
768 ->setHandlerUri(Option::get(
'sender',
'read_link'))->setSiteId(
$siteId);
769 $message->getClickTracker()->setModuleId(
'sender')->setFields([
'RECIPIENT_ID' => $recipient[
"ID"]])
770 ->setUriParameters([
'bx_sender_conversion_id' => $recipient[
"ID"]])->setHandlerUri(
771 Option::get(
'sender',
'click_link')
773 $message->getUnsubTracker()->setModuleId(
'sender')->setFields(
775 'RECIPIENT_ID' => $recipient[
'ID'],
776 'CONTACT_ID' => $recipient[
'CONTACT_ID'] ??
'',
777 'MAILING_ID' => $recipient[
'CAMPAIGN_ID'] ?? 0,
778 'EMAIL' =>
$message->getRecipientCode(),
779 'CODE' =>
$message->getRecipientCode(),
780 'TEST' => $isTest ?
'Y' :
'N'
782 )->setHandlerUri(Option::get(
'sender',
'unsub_link'))->setSiteId(
$siteId);
784 $fields = self::prepareRecipientFields($recipient);
786 $message->setRecipientId($recipient[
'ID']);
787 $message->setRecipientCode($recipient[
'CONTACT_CODE']);
789 $message->setRecipientData($recipient);
795 if (empty($recipient[
"NAME"]))
799 $recipient[
"MAILING_CHAIN_ID"] ??= 0;
800 $senderChainId = (int)$recipient[
"MAILING_CHAIN_ID"] > 0 ? (
int)$recipient[
"MAILING_CHAIN_ID"]
801 : (int)$recipient[
'CAMPAIGN_ID'];
805 'EMAIL_TO' => $recipient[
'CONTACT_CODE'] ??
'',
806 'NAME' => $recipient[
'NAME'] ??
'',
807 'USER_ID' => $recipient[
"USER_ID"] ??
'',
808 'SENDER_CHAIN_ID' => $senderChainId,
809 'SENDER_CHAIN_CODE' =>
'sender_chain_item_'.$senderChainId
812 if (is_array($recipient[
'FIELDS']) &&
count($recipient) > 0)
832 return (microtime(
true) - $this->timeAtStart >= $this->timeout);
847 return ($this->sentCount > $this->limit);
858 public static function unlock($id, $threadId = 0)
861 $threadId = intval($threadId);
863 $lockName = self::getSendpostLockName($id, $threadId);
865 return Application::getInstance()->getConnection()->unlock($lockName);
873 return $this->threadStrategy;
898 empty($recipient[
'CONTACT_CODE']) ||
899 $recipient[
'CONTACT_BLACKLISTED'] ===
'Y' ||
900 $recipient[
'CONTACT_UNSUBSCRIBED'] ===
'Y' ||
901 $recipient[
'CONTACT_MAILING_UNSUBSCRIBED'] ===
'Y' ||
903 $recipient[
'CONTACT_CONSENT_STATUS'],
904 $recipient[
'CONTACT_CONSENT_REQUEST'],
905 $this->message->getTransport()->getCode()
913 in_array($recipient[
'CONTACT_CONSENT_STATUS'], [
918 $recipient[
'CONTACT_CONSENT_REQUEST'],
919 $this->message->getTransport()->getCode()
934 $sendResult = $this->message->getTransport()->sendConsent(
935 $this->letter->getMessage(), $recipient + [
'RECIPIENT_ID' => $recipient[
'ID'],
'SITE_ID' =>
SITE_ID]
941 $recipient[
'CONTACT_ID'],
957 'RECIPIENT' => $recipient,
959 'ID' => $this->postingId,
961 'MAILING_ID' => $this->mailingId,
962 'MAILING_CHAIN_ID' => $this->letterId,
971 static $needConsentMessage;
972 if (!isset($needConsentMessage))
974 $needConsentMessage = $this->isConsentSupport;
976 return $needConsentMessage;
981 Model\Posting\RecipientTable::update(
992 return array_filter(iterator_to_array(
$result),
993 function ($recipient)
1004 private function finalizePosting(
string $status): void
1008 $onAfterEndResult = $this->message->onAfterEnd();
1009 if (!$onAfterEndResult->isSuccess())
1011 $this->resultCode = static::RESULT_CONTINUE;
1015 $errorMessage = implode(
', ', $onAfterEndResult->getErrorMessages());
1018 Model\LetterTable::update($this->letterId, [
'ERROR_MESSAGE' =>
$errorMessage]);
1024 $this->resultCode = $isContinue ? static::RESULT_CONTINUE : static::RESULT_SENT;
1026 if ($this->resultCode == static::RESULT_SENT)
1028 $this->resultCode = !$this->threadStrategy->finalize() ? static::RESULT_CONTINUE : static::RESULT_SENT;
1029 TimeLineJob::addEventAgent($this->letterId);
static getList(array $parameters=array())
static checkIfConsentRequestLimitExceeded(int $requests, string $code)
static isUnsub(string $status, ?int $requests, ?string $code)
static getDefaultId($siteId=null)
static getMailingSiteId($mailingId)
updateRecipientStatus($primary, $status)
executeEvent($recipient, $success)
setThreadStrategy(IThreadStrategy $threadStrategy)
isTransportLimitsExceeded()
static prepareRecipientFields($recipient)
canSendMessageToRecipient($recipient)
static updateActualStatus($postingId, $isPrevented=false, $awaitThread=false)
canDenySendToRecipient($recipient)
executeConsentToRecipient($recipient)
const RESULT_WAITING_RECIPIENT
canSendConsentToRecipient($recipient)
static unlock($id, $threadId=0)
__construct(Letter $letter)
sendToRecipient($recipient)
static applyRecipientToMessage(Adapter $message, array $recipient, $isTest=false)
static getRecipientsToSend(\Bitrix\Main\ORM\Query\Result $result)
static lock($id, $threadId=0)
const SEND_RESULT_WAIT_ACCEPT
const SEND_RESULT_SUCCESS
static hasUnprocessed($postingId, $threadId=null)
static getRecipientCountByStatus($id, ?array $customFilter=null)
static getRecipientStatusToPostingFieldMap()
const STATUS_SENT_WITH_ERRORS
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
while($arParentIBlockProperty=$dbParentIBlockProperty->Fetch()) $errorMessage
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
</p ></td >< td valign=top style='border-top:none;border-left:none;border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;padding:0cm 2.0pt 0cm 2.0pt;height:9.0pt'>< p class=Normal align=center style='margin:0cm;margin-bottom:.0001pt;text-align:center;line-height:normal'>< a name=ТекстовоеПоле54 ></a ><?=($taxRate > count( $arTaxList) > 0) ? $taxRate."%"
if( $guestStatuses !=='') if(!is_array($guestStatuses)) $statusList