123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- <?php
- namespace Dotdigitalgroup\Email\Model\Sync;
- /**
- * Sync automation by type.
- *
- * @SuppressWarnings(PHPMD.TooManyFields)
- */
- class Automation
- {
- const AUTOMATION_TYPE_NEW_CUSTOMER = 'customer_automation';
- const AUTOMATION_TYPE_NEW_SUBSCRIBER = 'subscriber_automation';
- const AUTOMATION_TYPE_NEW_ORDER = 'order_automation';
- const AUTOMATION_TYPE_NEW_GUEST_ORDER = 'guest_order_automation';
- const AUTOMATION_TYPE_NEW_REVIEW = 'review_automation';
- const AUTOMATION_TYPE_NEW_WISHLIST = 'wishlist_automation';
- const AUTOMATION_STATUS_PENDING = 'pending';
- const ORDER_STATUS_AUTOMATION = 'order_automation_';
- const AUTOMATION_TYPE_CUSTOMER_FIRST_ORDER = 'first_order_automation';
- const CONTACT_STATUS_PENDING = "PendingOptIn";
- const CONTACT_STATUS_CONFIRMED = "Confirmed";
- const CONTACT_STATUS_EXPIRED = "Expired";
- /**
- * @var array
- */
- public $automationTypes = [
- self::AUTOMATION_TYPE_NEW_CUSTOMER =>
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_CUSTOMER,
- self::AUTOMATION_TYPE_NEW_SUBSCRIBER =>
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_SUBSCRIBER,
- self::AUTOMATION_TYPE_NEW_ORDER =>
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_ORDER,
- self::AUTOMATION_TYPE_NEW_GUEST_ORDER =>
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_GUEST_ORDER,
- self::AUTOMATION_TYPE_NEW_REVIEW =>
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_REVIEW,
- self::AUTOMATION_TYPE_NEW_WISHLIST =>
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_WISHLIST,
- self::AUTOMATION_TYPE_CUSTOMER_FIRST_ORDER =>
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_FIRST_ORDER
- ];
- /**
- * @var int
- */
- private $limit = 100;
- /**
- * @var string
- */
- private $typeId;
- /**
- * @var string
- */
- private $storeName;
- /**
- * @var string
- */
- private $programId;
- /**
- * @var string
- */
- private $programStatus = 'Active';
- /**
- * @var string
- */
- private $programMessage;
- /**
- * @var \Dotdigitalgroup\Email\Helper\Data
- */
- private $helper;
- /**
- * @var \Magento\Framework\App\ResourceConnection
- */
- private $resource;
- /**
- * @var \Magento\Framework\Stdlib\DateTime
- */
- private $dateTime;
- /**
- * @var \Dotdigitalgroup\Email\Model\ResourceModel\Automation\CollectionFactory
- */
- private $automationFactory;
- /**
- * @var \Dotdigitalgroup\Email\Model\ResourceModel\Automation
- */
- private $automationResource;
- /**
- * @var \Magento\Sales\Model\OrderFactory
- */
- private $orderFactory;
- /**
- * @var \Dotdigitalgroup\Email\Model\DateIntervalFactory
- */
- private $dateIntervalFactory;
- /**
- * @var \Magento\Framework\Stdlib\DateTime\TimezoneInterface
- */
- private $timeZone;
- /**
- * Automation constructor.
- *
- * @param \Dotdigitalgroup\Email\Model\ResourceModel\Automation\CollectionFactory $automationFactory
- * @param \Magento\Framework\App\ResourceConnection $resource
- * @param \Dotdigitalgroup\Email\Helper\Data $helper
- * @param \Magento\Framework\Stdlib\DateTime $dateTime
- * @param \Magento\Sales\Model\OrderFactory $orderFactory
- * @param \Dotdigitalgroup\Email\Model\ResourceModel\Automation $automationResource
- * @param \Dotdigitalgroup\Email\Model\DateIntervalFactory $dateIntervalFactory,
- * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timeZone
- */
- public function __construct(
- \Dotdigitalgroup\Email\Model\ResourceModel\Automation\CollectionFactory $automationFactory,
- \Magento\Framework\App\ResourceConnection $resource,
- \Dotdigitalgroup\Email\Helper\Data $helper,
- \Magento\Framework\Stdlib\DateTime $dateTime,
- \Magento\Sales\Model\OrderFactory $orderFactory,
- \Dotdigitalgroup\Email\Model\ResourceModel\Automation $automationResource,
- \Dotdigitalgroup\Email\Model\DateIntervalFactory $dateIntervalFactory,
- \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timeZone
- ) {
- $this->automationFactory = $automationFactory;
- $this->helper = $helper;
- $this->resource = $resource;
- $this->dateTime = $dateTime;
- $this->orderFactory = $orderFactory;
- $this->automationResource = $automationResource;
- $this->dateIntervalFactory = $dateIntervalFactory;
- $this->timeZone = $timeZone;
- }
- /**
- * Sync.
- *
- * @throws \Magento\Framework\Exception\LocalizedException
- *
- * @return null
- */
- public function sync()
- {
- $this->checkStatusForPendingContacts();
- $this->setupAutomationTypes();
- //send the campaign by each types
- foreach ($this->automationTypes as $type => $config) {
- $contacts = $this->buildFirstDimensionOfContactsArray($type, $config);
- //get collection from type
- $automationCollection = $this->automationFactory->create()
- ->getCollectionByType($type, $this->limit);
- foreach ($automationCollection as $automation) {
- $type = $automation->getAutomationType();
- $email = $automation->getEmail();
- $this->typeId = $automation->getTypeId();
- $websiteId = $automation->getWebsiteId();
- $this->storeName = $automation->getStoreName();
- $typeDouble = $type;
- //Set type to generic automation status if type contains constant value
- if (strpos($typeDouble, self::ORDER_STATUS_AUTOMATION) !== false) {
- $typeDouble = self::ORDER_STATUS_AUTOMATION;
- }
- $contact = $this->helper->getContact($email, $websiteId);
- //contact id is valid, can update datafields
- if ($contact && isset($contact->id)) {
- if ($contact->status === self::CONTACT_STATUS_PENDING) {
- $automation->setEnrolmentStatus(self::CONTACT_STATUS_PENDING);
- $this->automationResource->save($automation);
- continue;
- }
- //need to update datafields
- $this->updateDatafieldsByType(
- $typeDouble,
- $email,
- $websiteId
- );
- $contacts[$automation->getWebsiteId()]['contacts'][$automation->getId()] = $contact->id;
- } else {
- // the contact is suppressed or the request failed
- $automation->setEnrolmentStatus('Suppressed');
- $this->automationResource->save($automation);
- }
- }
- $this->sendAutomationEnrolements($contacts, $type);
- }
- }
- /**
- * check automation entries for pending contacts
- */
- private function checkStatusForPendingContacts()
- {
- $updatedAt = $this->dateTime->formatDate(true);
- if ($this->isItTimeToCheckPendingContact()) {
- $collection = $this->automationFactory->create()
- ->getCollectionByPendingStatus();
- $idsToUpdateStatus = [];
- $idsToUpdateDate = [];
- foreach ($collection as $item) {
- $contact = $this->helper->getContact($item->getEmail(), $item->getWebsiteId());
- if (isset($contact->id) && $contact->status !== self::CONTACT_STATUS_PENDING) {
- //add to array for update status
- $idsToUpdateStatus[] = $item->getId();
- } else {
- //add to array for update date
- $idsToUpdateDate[] = $item->getId();
- }
- }
- if (! empty($idsToUpdateStatus)) {
- $this->automationResource
- ->update(
- $idsToUpdateStatus,
- $updatedAt,
- self::CONTACT_STATUS_CONFIRMED
- );
- }
- if (! empty($idsToUpdateDate)) {
- $this->automationResource
- ->update(
- $idsToUpdateDate,
- $updatedAt
- );
- }
- }
- //Get pending with 24 house delay and expire it
- $collection = $this->automationFactory->create()
- ->getCollectionByPendingStatus($this->getDateTimeForExpiration());
- $ids = $collection->getColumnValues('id');
- if (! empty($ids)) {
- $this->automationResource
- ->update(
- $ids,
- $updatedAt,
- self::CONTACT_STATUS_EXPIRED
- );
- }
- }
- /**
- * @return string
- */
- private function getDateTimeForExpiration()
- {
- $hours = (int) $this->helper->getWebsiteConfig(
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AC_AUTOMATION_EXPIRE_TIME
- );
- $interval = $this->dateIntervalFactory->create(
- ['interval_spec' => sprintf('PT%sH', $hours)]
- );
- $dateTime = $this->timeZone->date();
- $dateTime->sub($interval);
- return $dateTime->format('Y-m-d H:i:s');
- }
- /**
- * @return boolean
- */
- private function isItTimeToCheckPendingContact()
- {
- $dateTimeFromDb = $this->automationFactory->create()->getLastPendingStatusCheckTime();
- if (! $dateTimeFromDb) {
- return false;
- }
- $lastCheckTime = $this->timeZone->date($dateTimeFromDb);
- $interval = $this->dateIntervalFactory->create(['interval_spec' => 'PT30M']);
- $lastCheckTime->add($interval);
- $now = $this->timeZone->date();
- return ($now->format('Y-m-d H:i:s') > $lastCheckTime->format('Y-m-d H:i:s'));
- }
- /**
- * Update single contact datafields for this automation type.
- *
- * @param string $type
- * @param string $email
- * @param int $websiteId
- *
- * @return null
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- private function updateDatafieldsByType($type, $email, $websiteId)
- {
- switch ($type) {
- case self::AUTOMATION_TYPE_NEW_ORDER:
- case self::AUTOMATION_TYPE_NEW_GUEST_ORDER:
- case self::ORDER_STATUS_AUTOMATION:
- case self::AUTOMATION_TYPE_CUSTOMER_FIRST_ORDER:
- $this->updateNewOrderDatafields($websiteId);
- break;
- default:
- $this->updateDefaultDatafields($email, $websiteId);
- break;
- }
- }
- /**
- * Update config datafield.
- *
- * @param string $email
- * @param int $websiteId
- *
- * @return null
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- private function updateDefaultDatafields($email, $websiteId)
- {
- $website = $this->helper->storeManager->getWebsite($websiteId);
- $this->helper->updateDataFields($email, $website, $this->storeName);
- }
- /**
- * Update new order default datafields.
- *
- * @param int $websiteId
- *
- * @return null
- * @throws \Magento\Framework\Exception\LocalizedException
- */
- private function updateNewOrderDatafields($websiteId)
- {
- $website = $this->helper->storeManager->getWebsite($websiteId);
- $orderModel = $this->orderFactory->create()
- ->loadByIncrementId($this->typeId);
- //data fields
- if ($lastOrderId = $website->getConfig(
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_CUSTOMER_LAST_ORDER_ID
- )
- ) {
- $data[] = [
- 'Key' => $lastOrderId,
- 'Value' => $orderModel->getId(),
- ];
- }
- if ($orderIncrementId = $website->getConfig(
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_CUSTOMER_LAST_ORDER_INCREMENT_ID
- )
- ) {
- $data[] = [
- 'Key' => $orderIncrementId,
- 'Value' => $orderModel->getIncrementId(),
- ];
- }
- if ($storeName = $website->getConfig(
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_CUSTOMER_STORE_NAME
- )
- ) {
- $data[] = [
- 'Key' => $storeName,
- 'Value' => $this->storeName,
- ];
- }
- if ($websiteName = $website->getConfig(
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_CUSTOMER_WEBSITE_NAME
- )
- ) {
- $data[] = [
- 'Key' => $websiteName,
- 'Value' => $website->getName(),
- ];
- }
- if ($lastOrderDate = $website->getConfig(
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_CUSTOMER_LAST_ORDER_DATE
- )
- ) {
- $data[] = [
- 'Key' => $lastOrderDate,
- 'Value' => $orderModel->getCreatedAt(),
- ];
- }
- if (($customerId = $website->getConfig(
- \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_CUSTOMER_ID
- ))
- && $orderModel->getCustomerId()
- ) {
- $data[] = [
- 'Key' => $customerId,
- 'Value' => $orderModel->getCustomerId(),
- ];
- }
- if (!empty($data)) {
- //update data fields
- $client = $this->helper->getWebsiteApiClient($website);
- $client->updateContactDatafieldsByEmail(
- $orderModel->getCustomerEmail(),
- $data
- );
- }
- }
- /**
- * Program check if is valid and active.
- *
- * @param int $programId
- * @param int $websiteId
- *
- * @return bool
- * @throws \Exception
- */
- private function checkCampignEnrolmentActive($programId, $websiteId)
- {
- //program is not set
- if (!$programId) {
- return false;
- }
- $client = $this->helper->getWebsiteApiClient($websiteId);
- $program = $client->getProgramById($programId);
- //program status
- if (isset($program->status)) {
- $this->programStatus = $program->status;
- }
- if (isset($program->status) && $program->status == 'Active') {
- return true;
- }
- return false;
- }
- /**
- * Enrol contacts for a program.
- *
- * @param array $contacts
- * @param int $websiteId
- *
- * @return mixed
- * @throws \Exception
- */
- private function sendContactsToAutomation($contacts, $websiteId)
- {
- $client = $this->helper->getWebsiteApiClient($websiteId);
- $data = [
- 'Contacts' => $contacts,
- 'ProgramId' => $this->programId,
- 'AddressBooks' => [],
- ];
- //api add contact to automation enrolment
- $result = $client->postProgramsEnrolments($data);
- return $result;
- }
- /**
- * Setup automation types
- *
- * @return null
- */
- private function setupAutomationTypes()
- {
- $statusTypes = $this->automationFactory->create()
- ->getAutomationStatusType();
- foreach ($statusTypes as $type) {
- $this->automationTypes[$type]
- = \Dotdigitalgroup\Email\Helper\Config::XML_PATH_CONNECTOR_AUTOMATION_STUDIO_ORDER_STATUS;
- }
- }
- /**
- * @param string $type
- * @param string $config
- *
- * @return array
- */
- private function buildFirstDimensionOfContactsArray($type, $config)
- {
- $contacts = [];
- $websites = $this->helper->getWebsites(true);
- foreach ($websites as $website) {
- if (strpos($type, self::ORDER_STATUS_AUTOMATION) !== false) {
- $configValue = $this->helper->serializer->unserialize(
- $this->helper->getWebsiteConfig($config, $website)
- );
- if (is_array($configValue) && !empty($configValue)) {
- foreach ($configValue as $one) {
- if (strpos($type, $one['status']) !== false) {
- $contacts[$website->getId()]['programId']
- = $one['automation'];
- }
- }
- }
- } else {
- $contacts[$website->getId()]['programId']
- = $this->helper->getWebsiteConfig($config, $website);
- }
- }
- return $contacts;
- }
- /**
- * @param array $contactsArray
- * @param int $websiteId
- *
- * @return null
- * @throws \Exception
- */
- private function sendSubscribedContactsToAutomation($contactsArray, $websiteId)
- {
- if (!empty($contactsArray) &&
- $this->checkCampignEnrolmentActive($this->programId, $websiteId)
- ) {
- $result = $this->sendContactsToAutomation(
- array_values($contactsArray),
- $websiteId
- );
- //check for error message
- if (isset($result->message)) {
- $this->programStatus = 'Failed';
- $this->programMessage = $result->message;
- }
- //program is not active
- } elseif ($this->programMessage
- == 'Error: ERROR_PROGRAM_NOT_ACTIVE '
- ) {
- $this->programStatus = 'Deactivated';
- }
- }
- /**
- * @param $contacts
- * @param $type
- */
- private function sendAutomationEnrolements($contacts, $type)
- {
- foreach ($contacts as $websiteId => $websiteContacts) {
- if (isset($websiteContacts['contacts'])) {
- $this->programId = $websiteContacts['programId'];
- $contactsArray = $websiteContacts['contacts'];
- //only for subscribed contacts
- $this->sendSubscribedContactsToAutomation($contactsArray, $websiteId);
- //update contacts with the new status, and log the error message if fails
- $contactIds = array_keys($contactsArray);
- $updatedAt = $this->dateTime->formatDate(true);
- $this->automationResource
- ->updateStatus(
- $contactIds,
- $this->programStatus,
- $this->programMessage,
- $updatedAt,
- $type
- );
- }
- }
- }
- }
|