Check sent emails for products when schedulig an email
[MAILPOET-2335] [MAILPOET-2334]
This commit is contained in:
@ -118,7 +118,10 @@ class PurchasedInCategory {
|
||||
$meta = $automaticEmail->getMeta();
|
||||
|
||||
if (empty($meta['option'])) return false;
|
||||
if ($automaticEmail->wasScheduledForSubscriber($subscriber->id)) return false;
|
||||
if ($automaticEmail->wasScheduledForSubscriber($subscriber->id)) {
|
||||
$sentAllProducts = $automaticEmail->alreadySentAllProducts($subscriber->id, 'orderedProductCategories', $orderedProductCategories);
|
||||
if ($sentAllProducts) return false;
|
||||
}
|
||||
|
||||
$metaCategories = array_column($meta['option'], 'id');
|
||||
$matchedCategories = array_intersect($metaCategories, $orderedProductCategories);
|
||||
@ -138,7 +141,7 @@ class PurchasedInCategory {
|
||||
self::SLUG,
|
||||
$schedulingCondition,
|
||||
$subscriber->id,
|
||||
['orderedProducts' => $orderedProductCategories]
|
||||
['orderedProductCategories' => $orderedProductCategories]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace MailPoet\AutomaticEmails\WooCommerce\Events;
|
||||
|
||||
use MailPoet\AutomaticEmails\WooCommerce\WooCommerce;
|
||||
use MailPoet\Logging\LoggerFactory;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Newsletter\Scheduler\AutomaticEmailScheduler;
|
||||
use MailPoet\WooCommerce\Helper as WCHelper;
|
||||
@ -120,11 +121,14 @@ class PurchasedProduct {
|
||||
}, $orderDetails->get_items());
|
||||
$orderedProducts = array_values(array_filter($orderedProducts));
|
||||
|
||||
$schedulingCondition = function($automaticEmail) use ($orderedProducts, $subscriber) {
|
||||
$schedulingCondition = function(Newsletter $automaticEmail) use ($orderedProducts, $subscriber) {
|
||||
$meta = $automaticEmail->getMeta();
|
||||
|
||||
if (empty($meta['option'])) return false;
|
||||
if ($automaticEmail->wasScheduledForSubscriber($subscriber->id)) return false;
|
||||
if ($automaticEmail->wasScheduledForSubscriber($subscriber->id)) {
|
||||
$sentAllProducts = $automaticEmail->alreadySentAllProducts($subscriber->id, 'orderedProducts', $orderedProducts);
|
||||
if ($sentAllProducts) return false;
|
||||
}
|
||||
|
||||
$metaProducts = array_column($meta['option'], 'id');
|
||||
$matchedProducts = array_intersect($metaProducts, $orderedProducts);
|
||||
|
@ -369,19 +369,41 @@ class Newsletter extends Model {
|
||||
}
|
||||
|
||||
public function wasScheduledForSubscriber($subscriberId) {
|
||||
$query = SendingQueue::selectExpr('COUNT(*)', 'count');
|
||||
$query = $this->getAllQueuesForSubscscriberQuery($query, $subscriberId);
|
||||
/** @var \stdClass */
|
||||
$queue = SendingQueue::rawQuery(
|
||||
"SELECT COUNT(*) as count
|
||||
FROM `" . SendingQueue::$_table . "`
|
||||
JOIN `" . ScheduledTask::$_table . "` ON " . SendingQueue::$_table . ".task_id = " . ScheduledTask::$_table . ".id
|
||||
JOIN `" . ScheduledTaskSubscriber::$_table . "` ON " . ScheduledTask::$_table . ".id = " . ScheduledTaskSubscriber::$_table . ".task_id
|
||||
WHERE " . ScheduledTaskSubscriber::$_table . ".subscriber_id = " . $subscriberId . "
|
||||
AND " . SendingQueue::$_table . ".newsletter_id = " . $this->id
|
||||
)->findOne();
|
||||
$queue = $query->findOne();
|
||||
|
||||
return ((int)$queue->count) > 0;
|
||||
}
|
||||
|
||||
private function getAllQueuesForSubscscriberQuery($orm, $subscriberId) {
|
||||
return $orm->tableAlias('queue')
|
||||
->join(ScheduledTask::$_table, ['queue.task_id', '=', 'task.id'], 'task')
|
||||
->join(ScheduledTaskSubscriber::$_table, ['subscriber.task_id', '=', 'task.id'], 'subscriber')
|
||||
->where('queue.newsletter_id', $this->id)
|
||||
->where('subscriber.subscriber_id', $subscriberId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for automatic emails.
|
||||
* Search products/categories in meta if all of the ordered products have already been sent to the subscriber.
|
||||
*/
|
||||
public function alreadySentAllProducts(int $subscriberId, string $orderedKey, array $ordered): bool {
|
||||
$query = SendingQueue::select('queue.*');
|
||||
$queues = $this->getAllQueuesForSubscscriberQuery($query, $subscriberId)->findMany();
|
||||
$sent = [];
|
||||
foreach ($queues as $queue) {
|
||||
$meta = $queue->getMeta();
|
||||
if (isset($meta[$orderedKey])) {
|
||||
$sent = array_merge($sent, $meta[$orderedKey]);
|
||||
}
|
||||
}
|
||||
$notSentProducts = array_diff($ordered, $sent);
|
||||
|
||||
return empty($notSentProducts);
|
||||
}
|
||||
|
||||
public static function filterWithOptions($orm, $type) {
|
||||
$orm = $orm->select(MP_NEWSLETTERS_TABLE . '.*');
|
||||
$optionFields = NewsletterOptionField::findArray();
|
||||
|
@ -145,11 +145,6 @@ parameters:
|
||||
count: 2
|
||||
path: ../../tests/integration/AutomaticEmails/WooCommerce/Events/AbandonedCartTest.php
|
||||
|
||||
-
|
||||
message: "#^Property MailPoet\\\\AutomaticEmails\\\\WooCommerce\\\\Events\\\\PurchasedInCategoryTest\\:\\:\\$woocommerceHelper \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept MailPoet\\\\WooCommerce\\\\Helper\\.$#"
|
||||
count: 1
|
||||
path: ../../tests/integration/AutomaticEmails/WooCommerce/Events/PurchasedInCategoryTest.php
|
||||
|
||||
-
|
||||
message: "#^Cannot call method has_cap\\(\\) on WP_Role\\|null\\.$#"
|
||||
count: 6
|
||||
|
@ -21,7 +21,7 @@ use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class PurchasedInCategoryTest extends \MailPoetTest {
|
||||
|
||||
/** @var MockObject */
|
||||
/** @var MockObject&WCHelper */
|
||||
private $woocommerceHelper;
|
||||
|
||||
/** @var PurchasedInCategory */
|
||||
@ -37,7 +37,7 @@ class PurchasedInCategoryTest extends \MailPoetTest {
|
||||
ORM::raw_execute('TRUNCATE ' . ScheduledTaskSubscriber::$_table);
|
||||
WPFunctions::set(new WPFunctions);
|
||||
WPFunctions::get()->removeAllFilters('woocommerce_payment_complete');
|
||||
$this->woocommerceHelper = $this->makeEmpty(WCHelper::class, []);
|
||||
$this->woocommerceHelper = $this->createMock(WCHelper::class);
|
||||
$this->event = new PurchasedInCategory($this->woocommerceHelper);
|
||||
}
|
||||
|
||||
@ -69,29 +69,7 @@ class PurchasedInCategoryTest extends \MailPoetTest {
|
||||
}
|
||||
|
||||
public function testItSchedules() {
|
||||
$newsletter = Newsletter::createOrUpdate(
|
||||
[
|
||||
'subject' => 'WooCommerce',
|
||||
'preheader' => 'preheader',
|
||||
'type' => Newsletter::TYPE_AUTOMATIC,
|
||||
'status' => Newsletter::STATUS_ACTIVE,
|
||||
]
|
||||
);
|
||||
$this->_createNewsletterOption(
|
||||
[
|
||||
'group' => WooCommerce::SLUG,
|
||||
'event' => PurchasedInCategory::SLUG,
|
||||
'afterTimeType' => 'days',
|
||||
'afterTimeNumber' => 1,
|
||||
'meta' => json_encode(
|
||||
[
|
||||
'option' => [
|
||||
['id' => '15'],
|
||||
],
|
||||
]),
|
||||
],
|
||||
$newsletter->id
|
||||
);
|
||||
$newsletter = $this->_createNewsletter();
|
||||
|
||||
$customerEmail = 'email@example.com';
|
||||
$order = $this->getOrderMock(['15', '16']);
|
||||
@ -104,27 +82,53 @@ class PurchasedInCategoryTest extends \MailPoetTest {
|
||||
->method('get_billing_email')
|
||||
->will($this->returnValue($customerEmail));
|
||||
|
||||
$subscriber = Subscriber::createOrUpdate(Fixtures::get('subscriber_template'));
|
||||
$subscriber->email = $customerEmail;
|
||||
$subscriber->isWoocommerceUser = 1;
|
||||
$subscriber->status = Subscriber::STATUS_SUBSCRIBED;
|
||||
$subscriber->save();
|
||||
|
||||
$subscriberSegment = SubscriberSegment::create();
|
||||
$subscriberSegment->hydrate([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
'segment_id' => Segment::getWooCommerceSegment()->id,
|
||||
'status' => Subscriber::STATUS_SUBSCRIBED,
|
||||
]);
|
||||
$subscriberSegment->save();
|
||||
$this->_createSubscriber($customerEmail);
|
||||
|
||||
$this->event->scheduleEmail(3);
|
||||
$scheduledTask = Sending::getByNewsletterId($newsletter->id);
|
||||
$queue = $scheduledTask->queue();
|
||||
expect($queue->getMeta())->equals(['orderedProducts' => ['15', '16']]);
|
||||
expect($queue->getMeta())->equals(['orderedProductCategories' => ['15', '16']]);
|
||||
expect($scheduledTask)->notEmpty();
|
||||
}
|
||||
|
||||
public function testItSchedulesOnlyOnce() {
|
||||
$newsletter = $this->_createNewsletter();
|
||||
|
||||
$customerEmail = 'email@example.com';
|
||||
$order = $this->getOrderMock(['15', '16']);
|
||||
$this->woocommerceHelper = $this->createMock(WCHelper::class);
|
||||
$this->woocommerceHelper
|
||||
->expects($this->any())
|
||||
->method('wcGetOrder')
|
||||
->will($this->returnValue($order));
|
||||
$order
|
||||
->expects($this->any())
|
||||
->method('get_billing_email')
|
||||
->will($this->returnValue($customerEmail));
|
||||
|
||||
$this->_createSubscriber($customerEmail);
|
||||
|
||||
$this->event = new PurchasedInCategory($this->woocommerceHelper);
|
||||
$this->event->scheduleEmail(3);
|
||||
$queue1 = SendingQueue::where('newsletter_id', $newsletter->id)->findMany();
|
||||
expect($queue1)->notEmpty();
|
||||
|
||||
$order = $this->getOrderMock(['15']);
|
||||
$this->woocommerceHelper = $this->createMock(WCHelper::class);
|
||||
$this->woocommerceHelper
|
||||
->expects($this->any())
|
||||
->method('wcGetOrder')
|
||||
->will($this->returnValue($order));
|
||||
$order
|
||||
->expects($this->any())
|
||||
->method('get_billing_email')
|
||||
->will($this->returnValue($customerEmail));
|
||||
$this->event = new PurchasedInCategory($this->woocommerceHelper);
|
||||
$this->event->scheduleEmail(4);
|
||||
$queue2 = SendingQueue::where('newsletter_id', $newsletter->id)->findMany();
|
||||
expect($queue1)->count(count($queue2));
|
||||
}
|
||||
|
||||
private function getOrderMock($categories = ['123']) {
|
||||
$productMock = $this->getMockBuilder(\WC_Product::class)
|
||||
->disableOriginalConstructor()
|
||||
@ -156,6 +160,51 @@ class PurchasedInCategoryTest extends \MailPoetTest {
|
||||
return $orderMock;
|
||||
}
|
||||
|
||||
private function _createNewsletter() {
|
||||
$newsletter = Newsletter::createOrUpdate(
|
||||
[
|
||||
'subject' => 'WooCommerce',
|
||||
'preheader' => 'preheader',
|
||||
'type' => Newsletter::TYPE_AUTOMATIC,
|
||||
'status' => Newsletter::STATUS_ACTIVE,
|
||||
]
|
||||
);
|
||||
$this->_createNewsletterOption(
|
||||
[
|
||||
'sendTo' => 'user',
|
||||
'group' => WooCommerce::SLUG,
|
||||
'event' => PurchasedInCategory::SLUG,
|
||||
'afterTimeType' => 'days',
|
||||
'afterTimeNumber' => 1,
|
||||
'meta' => json_encode(
|
||||
[
|
||||
'option' => [
|
||||
['id' => '15'],
|
||||
],
|
||||
]),
|
||||
],
|
||||
$newsletter->id
|
||||
);
|
||||
return $newsletter;
|
||||
}
|
||||
|
||||
private function _createSubscriber($customerEmail) {
|
||||
$subscriber = Subscriber::createOrUpdate(Fixtures::get('subscriber_template'));
|
||||
$subscriber->email = $customerEmail;
|
||||
$subscriber->isWoocommerceUser = 1;
|
||||
$subscriber->status = Subscriber::STATUS_SUBSCRIBED;
|
||||
$subscriber->save();
|
||||
|
||||
$subscriberSegment = SubscriberSegment::create();
|
||||
$subscriberSegment->hydrate([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
'segment_id' => Segment::getWooCommerceSegment()->id,
|
||||
'status' => Subscriber::STATUS_SUBSCRIBED,
|
||||
]);
|
||||
$subscriberSegment->save();
|
||||
return $subscriber;
|
||||
}
|
||||
|
||||
public function _createNewsletterOption(array $options, $newsletterId) {
|
||||
foreach ($options as $option => $value) {
|
||||
$newsletterOptionField = NewsletterOptionField::where('name', $option)
|
||||
|
@ -90,6 +90,83 @@ class PurchasedProductTest extends \MailPoetTest {
|
||||
expect($result)->isEmpty();
|
||||
}
|
||||
|
||||
public function testItDoesNotScheduleEmailWhenAlreadySent() {
|
||||
$newsletter = Newsletter::createOrUpdate(
|
||||
[
|
||||
'subject' => 'WooCommerce',
|
||||
'preheader' => 'preheader',
|
||||
'type' => Newsletter::TYPE_AUTOMATIC,
|
||||
'status' => Newsletter::STATUS_ACTIVE,
|
||||
]
|
||||
);
|
||||
$productId = 1000;
|
||||
$this->_createNewsletterOption(
|
||||
[
|
||||
'sendTo' => 'user',
|
||||
'group' => WooCommerce::SLUG,
|
||||
'event' => PurchasedProduct::SLUG,
|
||||
'afterTimeType' => 'days',
|
||||
'afterTimeNumber' => 1,
|
||||
'meta' => json_encode(
|
||||
[
|
||||
'option' => [
|
||||
['id' => $productId],
|
||||
],
|
||||
]),
|
||||
],
|
||||
$newsletter->id
|
||||
);
|
||||
$customerEmail = 'test@example.com';
|
||||
$subscriber = Subscriber::createOrUpdate(Fixtures::get('subscriber_template'));
|
||||
$subscriber->email = $customerEmail;
|
||||
$subscriber->isWoocommerceUser = 1;
|
||||
$subscriber->status = Subscriber::STATUS_SUBSCRIBED;
|
||||
$subscriber->save();
|
||||
|
||||
$subscriberSegment = SubscriberSegment::create();
|
||||
$subscriberSegment->hydrate([
|
||||
'subscriber_id' => $subscriber->id,
|
||||
'segment_id' => Segment::getWooCommerceSegment()->id,
|
||||
'status' => Subscriber::STATUS_SUBSCRIBED,
|
||||
]);
|
||||
$subscriberSegment->save();
|
||||
|
||||
$orderDetails = Stub::make(
|
||||
new OrderDetails(),
|
||||
[
|
||||
'get_billing_email' => 'test@example.com',
|
||||
'get_items' => function() use ($productId) {
|
||||
return [
|
||||
Stub::make(
|
||||
\WC_Order_Item_Product::class,
|
||||
[
|
||||
'get_product_id' => $productId,
|
||||
]
|
||||
),
|
||||
];
|
||||
},
|
||||
]
|
||||
);
|
||||
$orderDetails->total = 'order_total';
|
||||
$orderId = 12;
|
||||
$helper = Stub::make(WCHelper::class, [
|
||||
'wcGetOrder' => $orderDetails,
|
||||
]);
|
||||
|
||||
$event = new PurchasedProduct($helper);
|
||||
|
||||
// ensure there are no existing scheduled tasks
|
||||
$scheduledTask = Sending::getByNewsletterId($newsletter->id);
|
||||
expect($scheduledTask)->false();
|
||||
|
||||
$event->scheduleEmailWhenProductIsPurchased($orderId);
|
||||
$queue1 = SendingQueue::where('newsletter_id', $newsletter->id)->findMany();
|
||||
|
||||
$event->scheduleEmailWhenProductIsPurchased($orderId);
|
||||
$queue2 = SendingQueue::where('newsletter_id', $newsletter->id)->findMany();
|
||||
expect($queue1)->count(count($queue2));
|
||||
}
|
||||
|
||||
public function testItDoesNotScheduleEmailWhenPurchasedProductDoesNotMatchConfiguredProductIds() {
|
||||
WPFunctions::get()->removeAllFilters('woocommerce_order_status_completed');
|
||||
$newsletter = Newsletter::createOrUpdate(
|
||||
@ -107,6 +184,7 @@ class PurchasedProductTest extends \MailPoetTest {
|
||||
];
|
||||
$this->_createNewsletterOption(
|
||||
[
|
||||
'sendTo' => 'user',
|
||||
'group' => WooCommerce::SLUG,
|
||||
'event' => PurchasedProduct::SLUG,
|
||||
'afterTimeType' => 'days',
|
||||
|
Reference in New Issue
Block a user