Add filter for used payment method

MAILPOET-4993
This commit is contained in:
John Oleksowicz
2023-05-10 12:23:09 -05:00
committed by Aschepikov
parent a976006a2c
commit ec68818c1a
16 changed files with 427 additions and 1 deletions

View File

@ -2,6 +2,10 @@
width: min-content;
}
.mailpoet-max-content-width {
max-width: max-content;
}
.mailpoet-nowrap {
white-space: nowrap;
}

View File

@ -6,6 +6,7 @@ type Props = SelectHTMLAttributes<HTMLSelectElement> & {
dimension?: 'small';
isFullWidth?: boolean;
isMinWidth?: boolean;
isMaxContentWidth?: boolean;
iconStart?: JSX.Element;
automationId?: string;
};
@ -17,6 +18,7 @@ export const Select = forwardRef(
dimension,
isFullWidth,
isMinWidth,
isMaxContentWidth,
iconStart,
automationId,
...attributes
@ -29,6 +31,7 @@ export const Select = forwardRef(
'mailpoet-disabled': attributes.disabled,
'mailpoet-full-width': isFullWidth,
'mailpoet-min-width': isMinWidth,
'mailpoet-max-content-width': isMaxContentWidth,
})}
>
{iconStart}

View File

@ -14,6 +14,7 @@ import {
WindowProducts,
WindowWooCommerceCountries,
WooCommerceFormItem,
WooPaymentMethod,
} from '../types';
import { DateFields, validateDateField } from './date_fields';
import { storeName } from '../store';
@ -87,6 +88,17 @@ export function validateWooCommerce(formItems: WooCommerceFormItem): boolean {
) {
return false;
}
if (formItems.action === WooCommerceActionTypes.USED_PAYMENT_METHOD) {
if (
!formItems.payment_methods ||
formItems.payment_methods.length < 1 ||
!formItems.operator ||
!formItems.used_payment_method_days ||
parseInt(formItems.used_payment_method_days, 10) < 1
) {
return false;
}
}
if (formItems.action === WooCommerceActionTypes.PURCHASE_DATE) {
return validateDateField(formItems);
}
@ -621,6 +633,95 @@ function CustomerInCountryFields({ filterIndex }: Props): JSX.Element {
);
}
function UsedPaymentMethodFields({ filterIndex }: Props): JSX.Element {
const segment: WooCommerceFormItem = useSelect(
(select) => select(storeName).getSegmentFilter(filterIndex),
[filterIndex],
);
const { updateSegmentFilter, updateSegmentFilterFromEvent } =
useDispatch(storeName);
const paymentMethods: WooPaymentMethod[] = useSelect(
(select) => select(storeName).getPaymentMethods(),
[],
);
const paymentMethodOptions = paymentMethods.map((method) => ({
value: method.id,
label: method.name,
}));
useEffect(() => {
if (
segment.operator !== AnyValueTypes.ANY &&
segment.operator !== AnyValueTypes.ALL &&
segment.operator !== AnyValueTypes.NONE
) {
void updateSegmentFilter({ operator: AnyValueTypes.ANY }, filterIndex);
}
}, [updateSegmentFilter, segment, filterIndex]);
return (
<>
<Grid.CenteredRow>
<Select
isMaxContentWidth
key="select-operator-used-payment-methods"
value={segment.operator}
onChange={(e): void => {
void updateSegmentFilter({ operator: e.target.value }, filterIndex);
}}
automationId="select-operator-used-payment-methods"
>
<option value={AnyValueTypes.ANY}>{MailPoet.I18n.t('anyOf')}</option>
<option value={AnyValueTypes.ALL}>{MailPoet.I18n.t('allOf')}</option>
<option value={AnyValueTypes.NONE}>
{MailPoet.I18n.t('noneOf')}
</option>
</Select>
<ReactSelect
key="select-payment-methods"
isFullWidth
isMulti
placeholder={MailPoet.I18n.t('selectWooPaymentMethods')}
options={paymentMethodOptions}
value={filter((option) => {
if (!segment.payment_methods) return undefined;
return segment.payment_methods.indexOf(option.value) !== -1;
}, paymentMethodOptions)}
onChange={(options: SelectOption[]): void => {
void updateSegmentFilter(
{
payment_methods: (options || []).map(
(x: SelectOption) => x.value,
),
},
filterIndex,
);
}}
automationId="select-payment-methods"
/>
</Grid.CenteredRow>
<Grid.CenteredRow>
<div>{MailPoet.I18n.t('inTheLast')}</div>
<Input
data-automation-id="input-used-payment-days"
type="number"
min={1}
value={segment.used_payment_method_days || ''}
placeholder={MailPoet.I18n.t('daysPlaceholder')}
onChange={(e): void => {
void updateSegmentFilterFromEvent(
'used_payment_method_days',
filterIndex,
e,
);
}}
/>
<div>{MailPoet.I18n.t('days')}</div>
</Grid.CenteredRow>
</>
);
}
const componentsMap = {
[WooCommerceActionTypes.CUSTOMER_IN_COUNTRY]: CustomerInCountryFields,
[WooCommerceActionTypes.NUMBER_OF_ORDERS]: NumberOfOrdersFields,
@ -630,6 +731,7 @@ const componentsMap = {
[WooCommerceActionTypes.SINGLE_ORDER_VALUE]: SingleOrderValueFields,
[WooCommerceActionTypes.TOTAL_SPENT]: TotalSpentFields,
[WooCommerceActionTypes.AVERAGE_SPENT]: AverageSpentFields,
[WooCommerceActionTypes.USED_PAYMENT_METHOD]: UsedPaymentMethodFields,
};
export function WooCommerceFields({ filterIndex }: Props): JSX.Element {

View File

@ -11,6 +11,7 @@ export enum WooCommerceActionTypes {
AVERAGE_SPENT = 'averageSpent',
CUSTOMER_IN_COUNTRY = 'customerInCountry',
SINGLE_ORDER_VALUE = 'singleOrderValue',
USED_PAYMENT_METHOD = 'usedPaymentMethod',
}
export const WooCommerceOptions = [
@ -54,6 +55,11 @@ export const WooCommerceOptions = [
label: MailPoet.I18n.t('wooTotalSpent'),
group: SegmentTypes.WooCommerce,
},
{
value: WooCommerceActionTypes.USED_PAYMENT_METHOD,
label: MailPoet.I18n.t('wooUsedPaymentMethod'),
group: SegmentTypes.WooCommerce,
},
];
// WooCommerce Memberships

View File

@ -21,6 +21,7 @@ export const getInitialState = (): StateType => ({
canUseWooSubscriptions: window.mailpoet_can_use_woocommerce_subscriptions,
wooCurrencySymbol: window.mailpoet_woocommerce_currency_symbol,
wooCountries: window.mailpoet_woocommerce_countries,
wooPaymentMethods: window.mailpoet_woocommerce_payment_methods,
customFieldsList: window.mailpoet_custom_fields,
tags: window.mailpoet_tags,
signupForms: window.mailpoet_signup_forms,

View File

@ -17,6 +17,7 @@ import {
WindowProducts,
WindowSubscriptionProducts,
WindowWooCommerceCountries,
WooPaymentMethod,
} from '../types';
export const getProducts = (state: StateType): WindowProducts => state.products;
@ -49,6 +50,8 @@ export const getSubscriberCount = (state: StateType): SubscriberCount =>
export const getTags = (state: StateType): Tag[] => state.tags;
export const getSignupForms = (state: StateType): SignupForm[] =>
state.signupForms;
export const getPaymentMethods = (state: StateType): WooPaymentMethod[] =>
state.wooPaymentMethods;
export const getSegmentFilter = (
state: StateType,
index: number,

View File

@ -100,6 +100,8 @@ export interface WooCommerceFormItem extends FormItem {
average_spent_type?: string;
average_spent_amount?: string;
average_spent_days?: string;
payment_methods?: string[];
used_payment_method_days?: string;
}
export interface WooCommerceMembershipFormItem extends FormItem {
@ -204,6 +206,7 @@ export interface SegmentFormDataWindow extends Window {
mailpoet_subscription_products: WindowSubscriptionProducts;
mailpoet_product_categories: WindowProductCategories;
mailpoet_woocommerce_countries: WindowWooCommerceCountries;
mailpoet_woocommerce_payment_methods: WooPaymentMethod[];
mailpoet_newsletters_list: WindowNewslettersList;
mailpoet_custom_fields: WindowCustomFields;
mailpoet_can_use_woocommerce_memberships: boolean;
@ -225,6 +228,7 @@ export interface StateType {
canUseWooSubscriptions: boolean;
wooCurrencySymbol: string;
wooCountries: WindowWooCommerceCountries;
wooPaymentMethods: WooPaymentMethod[];
customFieldsList: WindowCustomFields;
segment: Segment;
subscriberCount: SubscriberCount;
@ -280,3 +284,8 @@ export type SignupForm = {
id: string;
name: string;
};
export type WooPaymentMethod = {
id: string;
name: string;
};

View File

@ -136,6 +136,18 @@ class Segments {
'name' => $form->getName(),
];
}, $this->formsRepository->findAll());
$data['woocommerce_payment_methods'] = [];
if ($this->woocommerceHelper->isWooCommerceActive()) {
$allGateways = $this->woocommerceHelper->getPaymentGateways()->payment_gateways();
$paymentMethods = [];
foreach ($allGateways as $gatewayId => $gateway) {
$paymentMethods[] = [
'id' => $gatewayId,
'name' => $gateway->get_method_title(),
];
}
$data['woocommerce_payment_methods'] = $paymentMethods;
}
$this->pageRenderer->displayPage('segments.html', $data);
}

View File

@ -428,6 +428,7 @@ class ContainerConfigurator implements IContainerConfigurator {
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceSingleOrderValue::class)->setPublic(true);
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent::class)->setPublic(true);
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription::class)->setPublic(true);
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedPaymentMethod::class)->setPublic(true);
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooFilterHelper::class)->setPublic(true);
$container->autowire(\MailPoet\Segments\DynamicSegments\SegmentSaveController::class)->setPublic(true);
$container->autowire(\MailPoet\Segments\DynamicSegments\FilterDataMapper::class)->setPublic(true);

View File

@ -25,6 +25,7 @@ use MailPoet\Segments\DynamicSegments\Filters\WooCommercePurchaseDate;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSingleOrderValue;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedPaymentMethod;
use MailPoet\WP\Functions as WPFunctions;
class FilterDataMapper {
@ -309,6 +310,19 @@ class FilterDataMapper {
$filterData['average_spent_days'] = $data['average_spent_days'];
$filterData['average_spent_amount'] = $data['average_spent_amount'];
$filterData['average_spent_type'] = $data['average_spent_type'];
} elseif ($data['action'] === WooCommerceUsedPaymentMethod::ACTION) {
if (!isset($data['operator']) || !in_array($data['operator'], WooCommerceUsedPaymentMethod::VALID_OPERATORS, true)) {
throw new InvalidFilterException('Missing operator', InvalidFilterException::MISSING_OPERATOR);
}
if (!isset($data['payment_methods']) || !is_array($data['payment_methods']) || empty($data['payment_methods'])) {
throw new InvalidFilterException('Missing payment gateways', InvalidFilterException::MISSING_VALUE);
}
if (!isset($data['used_payment_method_days']) || intval($data['used_payment_method_days']) < 1) {
throw new InvalidFilterException('Missing days', InvalidFilterException::MISSING_VALUE);
}
$filterData['operator'] = $data['operator'];
$filterData['payment_methods'] = $data['payment_methods'];
$filterData['used_payment_method_days'] = intval($data['used_payment_method_days']);
} else {
throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
}

View File

@ -27,6 +27,7 @@ use MailPoet\Segments\DynamicSegments\Filters\WooCommercePurchaseDate;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSingleOrderValue;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedPaymentMethod;
class FilterFactory {
/** @var EmailAction */
@ -92,6 +93,9 @@ class FilterFactory {
/** @var SubscriberTextField */
private $subscriberTextField;
/** @var WooCommerceUsedPaymentMethod */
private $wooCommerceUsedPaymentMethod;
public function __construct(
EmailAction $emailAction,
EmailActionClickAny $emailActionClickAny,
@ -113,6 +117,7 @@ class FilterFactory {
SubscriberSubscribedViaForm $subscribedViaForm,
WooCommerceSingleOrderValue $wooCommerceSingleOrderValue,
WooCommerceAverageSpent $wooCommerceAverageSpent,
WooCommerceUsedPaymentMethod $wooCommerceUsedPaymentMethod,
SubscriberTextField $subscriberTextField
) {
$this->emailAction = $emailAction;
@ -136,6 +141,7 @@ class FilterFactory {
$this->subscriberTextField = $subscriberTextField;
$this->subscribedViaForm = $subscribedViaForm;
$this->wooCommerceAverageSpent = $wooCommerceAverageSpent;
$this->wooCommerceUsedPaymentMethod = $wooCommerceUsedPaymentMethod;
}
public function getFilterForFilterEntity(DynamicSegmentFilterEntity $filter): Filter {
@ -223,6 +229,8 @@ class FilterFactory {
return $this->wooCommercePurchaseDate;
} elseif ($action === WooCommerceAverageSpent::ACTION) {
return $this->wooCommerceAverageSpent;
} elseif ($action === WooCommerceUsedPaymentMethod::ACTION) {
return $this->wooCommerceUsedPaymentMethod;
}
return $this->wooCommerceCategory;
}

View File

@ -26,7 +26,7 @@ class FilterHelper {
return $this->entityManager
->getConnection()
->createQueryBuilder()
->select('id')
->select($this->getSubscribersTable() . '.id')
->from($this->getSubscribersTable());
}

View File

@ -0,0 +1,134 @@
<?php declare(strict_types = 1);
namespace MailPoet\Segments\DynamicSegments\Filters;
use MailPoet\Entities\DynamicSegmentFilterData;
use MailPoet\Entities\DynamicSegmentFilterEntity;
use MailPoet\Segments\DynamicSegments\Exceptions\InvalidFilterException;
use MailPoet\WooCommerce\Helper;
use MailPoetVendor\Carbon\Carbon;
use MailPoetVendor\Doctrine\DBAL\Connection;
use MailPoetVendor\Doctrine\DBAL\Query\QueryBuilder;
class WooCommerceUsedPaymentMethod implements Filter {
const ACTION = 'usedPaymentMethod';
const VALID_OPERATORS = [
DynamicSegmentFilterData::OPERATOR_NONE,
DynamicSegmentFilterData::OPERATOR_ANY,
DynamicSegmentFilterData::OPERATOR_ALL,
];
/** @var WooFilterHelper */
private $wooFilterHelper;
/** @var Helper */
private $wooHelper;
/** @var FilterHelper */
private $filterHelper;
public function __construct(
FilterHelper $filterHelper,
WooFilterHelper $wooFilterHelper,
Helper $wooHelper
) {
$this->wooFilterHelper = $wooFilterHelper;
$this->wooHelper = $wooHelper;
$this->filterHelper = $filterHelper;
}
public function apply(QueryBuilder $queryBuilder, DynamicSegmentFilterEntity $filter): QueryBuilder {
$filterData = $filter->getFilterData();
$operator = $filterData->getParam('operator');
$paymentMethods = $filterData->getParam('payment_methods');
$days = $filterData->getParam('used_payment_method_days');
if (!is_string($operator) || !in_array($operator, self::VALID_OPERATORS, true)) {
throw new InvalidFilterException('Invalid operator', InvalidFilterException::MISSING_OPERATOR);
}
if (!is_array($paymentMethods) || count($paymentMethods) < 1) {
throw new InvalidFilterException('Missing payment methods', InvalidFilterException::MISSING_VALUE);
}
if (!is_int($days) || $days < 1) {
throw new InvalidFilterException('Missing days', InvalidFilterException::MISSING_VALUE);
}
$includedStatuses = array_keys($this->wooHelper->getOrderStatuses());
$failedKey = array_search('wc-failed', $includedStatuses, true);
if ($failedKey !== false) {
unset($includedStatuses[$failedKey]);
}
$date = Carbon::now()->subDays($days);
switch ($operator) {
case DynamicSegmentFilterData::OPERATOR_ANY:
$this->applyForAnyOperator($queryBuilder, $includedStatuses, $paymentMethods, $date);
break;
case DynamicSegmentFilterData::OPERATOR_ALL:
$this->applyForAllOperator($queryBuilder, $includedStatuses, $paymentMethods, $date);
break;
case DynamicSegmentFilterData::OPERATOR_NONE:
$subQuery = $this->filterHelper->getNewSubscribersQueryBuilder();
$this->applyForAnyOperator($subQuery, $includedStatuses, $paymentMethods, $date);
$subscribersTable = $this->filterHelper->getSubscribersTable();
$queryBuilder->andWhere($queryBuilder->expr()->notIn("$subscribersTable.id", $this->filterHelper->getInterpolatedSQL($subQuery)));
break;
}
return $queryBuilder;
}
private function applyForAnyOperator(QueryBuilder $queryBuilder, array $includedStatuses, array $paymentMethods, Carbon $date): void {
if ($this->wooHelper->isWooCommerceCustomOrdersTableEnabled()) {
$this->applyCustomOrderTableJoin($queryBuilder, $includedStatuses, $paymentMethods, $date);
} else {
$this->applyPostmetaOrderJoin($queryBuilder, $includedStatuses, $paymentMethods, $date);
}
}
private function applyForAllOperator(QueryBuilder $queryBuilder, array $includedStatuses, array $paymentMethods, Carbon $date): void {
if ($this->wooHelper->isWooCommerceCustomOrdersTableEnabled()) {
$ordersAlias = $this->applyCustomOrderTableJoin($queryBuilder, $includedStatuses, $paymentMethods, $date);
$queryBuilder->groupBy('inner_subscriber_id')
->having("COUNT(DISTINCT $ordersAlias.payment_method) = " . count($paymentMethods));
} else {
$postmetaAlias = $this->applyPostmetaOrderJoin($queryBuilder, $includedStatuses, $paymentMethods, $date);
$queryBuilder->groupBy('inner_subscriber_id')->having("COUNT(DISTINCT $postmetaAlias.meta_value) = " . count($paymentMethods));
}
}
private function applyPostmetaOrderJoin(QueryBuilder $queryBuilder, array $includedStatuses, array $paymentMethods, Carbon $date, string $postmetaAlias = 'postmeta'): string {
$dateParam = $this->filterHelper->getUniqueParameterName('date');
$paymentMethodParam = $this->filterHelper->getUniqueParameterName('paymentMethod');
$paymentMethodMetaKeyParam = $this->filterHelper->getUniqueParameterName('paymentMethod');
$postMetaTable = $this->filterHelper->getPrefixedTable('postmeta');
$orderStatsAlias = $this->wooFilterHelper->applyOrderStatusFilter($queryBuilder, $includedStatuses);
$queryBuilder
->innerJoin($orderStatsAlias, $postMetaTable, $postmetaAlias, "$orderStatsAlias.order_id = $postmetaAlias.post_id")
->andWhere("$orderStatsAlias.date_created >= :$dateParam")
->andWhere("postmeta.meta_key = :$paymentMethodMetaKeyParam")
->andWhere("postmeta.meta_value IN (:$paymentMethodParam)")
->setParameter($paymentMethodMetaKeyParam, '_payment_method')
->setParameter($dateParam, $date->toDateTimeString())
->setParameter($paymentMethodParam, $paymentMethods, Connection::PARAM_STR_ARRAY);
return $postmetaAlias;
}
private function applyCustomOrderTableJoin(QueryBuilder $queryBuilder, array $includedStatuses, array $paymentMethods, Carbon $date, string $ordersAlias = 'orders'): string {
$dateParam = $this->filterHelper->getUniqueParameterName('date');
$paymentMethodParam = $this->filterHelper->getUniqueParameterName('paymentMethod');
$ordersTable = $this->wooHelper->getOrdersTableName();
$orderStatsAlias = $this->wooFilterHelper->applyOrderStatusFilter($queryBuilder, $includedStatuses);
$queryBuilder
->innerJoin($orderStatsAlias, $ordersTable, 'orders', "$orderStatsAlias.order_id = orders.id")
->andWhere("$orderStatsAlias.date_created >= :$dateParam")
->andWhere("$ordersAlias.payment_method IN (:$paymentMethodParam)")
->setParameter($dateParam, $date->toDateTimeString())
->setParameter($paymentMethodParam, $paymentMethods, Connection::PARAM_STR_ARRAY);
return $ordersAlias;
}
}

View File

@ -223,4 +223,8 @@ class Helper {
return $coupon ? $coupon->post_title : null; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
}
public function getPaymentGateways() {
return $this->WC()->payment_gateways();
}
}

View File

@ -0,0 +1,122 @@
<?php declare(strict_types = 1);
namespace integration\Segments\DynamicSegments\Filters;
use MailPoet\Entities\DynamicSegmentFilterData;
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedPaymentMethod;
use MailPoet\Test\DataFactories\Subscriber;
use MailPoetVendor\Carbon\Carbon;
/**
* @group woo
*/
class WooCommerceUsedPaymentMethodTest extends \MailPoetTest {
/** @var WooCommerceUsedPaymentMethod */
private $filter;
public function _before(): void {
$this->filter = $this->diContainer->get(WooCommerceUsedPaymentMethod::class);
}
public function testItWorksWithAnyOperator(): void {
$customerId1 = $this->tester->createCustomer('c1@e.com');
$customerId2 = $this->tester->createCustomer('c2@e.com');
$customerId3 = $this->tester->createCustomer('c3@e.com');
$this->createOrder($customerId1, Carbon::now(), 'paypal');
$this->createOrder($customerId2, Carbon::now(), 'cheque');
$this->createOrder($customerId3, Carbon::now(), 'cheque');
$this->createOrder($customerId3, Carbon::now(), 'paypal');
$this->assertFilterReturnsEmails('any', ['paypal'], 1, ['c1@e.com', 'c3@e.com']);
$this->assertFilterReturnsEmails('any', ['cheque'], 1, ['c2@e.com', 'c3@e.com']);
$this->assertFilterReturnsEmails('any', ['doge'], 1000, []);
}
public function testItWorksWithAllOperator(): void {
$customerId1 = $this->tester->createCustomer('c1@e.com');
$this->createOrder($customerId1, Carbon::now(), 'paypal');
$this->createOrder($customerId1, Carbon::now(), 'paypal');
$customerId2 = $this->tester->createCustomer('c2@e.com');
$this->createOrder($customerId2, Carbon::now(), 'cheque');
$customerId3 = $this->tester->createCustomer('c3@e.com');
$this->createOrder($customerId3, Carbon::now(), 'cheque');
$this->createOrder($customerId3, Carbon::now(), 'paypal');
$this->assertfilterreturnsemails('all', ['paypal'], 1, ['c1@e.com', 'c3@e.com']);
$this->assertFilterReturnsEmails('all', ['cheque'], 1, ['c2@e.com', 'c3@e.com']);
$this->assertFilterReturnsEmails('all', ['cheque', 'paypal'], 1, ['c3@e.com']);
$this->assertFilterReturnsEmails('all', ['doge'], 1000, []);
}
public function testItWorksWithNoneOperator(): void {
$customerId1 = $this->tester->createCustomer('c1@e.com');
$this->createOrder($customerId1, Carbon::now(), 'paypal');
$this->createOrder($customerId1, Carbon::now(), 'paypal');
$customerId2 = $this->tester->createCustomer('c2@e.com');
$this->createOrder($customerId2, Carbon::now(), 'cheque');
$customerId3 = $this->tester->createCustomer('c3@e.com');
$this->createOrder($customerId3, Carbon::now(), 'cheque');
$this->createOrder($customerId3, Carbon::now(), 'paypal');
(new Subscriber)->withEmail('sub@e.com')->create();
$this->assertFilterReturnsEmails('none', ['paypal'], 1, ['sub@e.com', 'c2@e.com']);
$this->assertFilterReturnsEmails('none', ['cheque'], 1, ['sub@e.com', 'c1@e.com']);
$this->assertFilterReturnsEmails('none', ['doge'], 1000, ['sub@e.com', 'c1@e.com', 'c2@e.com', 'c3@e.com']);
$this->assertFilterReturnsEmails('none', ['paypal', 'cheque'], 1, ['sub@e.com']);
}
public function testItWorksWithDateRanges(): void {
$customerId1 = $this->tester->createCustomer('c1@e.com');
$this->createOrder($customerId1, Carbon::now()->subDays(2)->addMinute(), 'paypal');
$this->createOrder($customerId1, Carbon::now()->subDays(5)->addMinute(), 'cheque');
$customerId2 = $this->tester->createCustomer('c2@e.com');
$this->createOrder($customerId2, Carbon::now()->subDays(100)->addMinute(), 'cash');
$this->assertFilterReturnsEmails('any', ['paypal'], 1, []);
$this->assertFilterReturnsEmails('any', ['paypal'], 2, ['c1@e.com']);
$this->assertFilterReturnsEmails('any', ['cheque'], 4, []);
$this->assertFilterReturnsEmails('any', ['cheque'], 5, ['c1@e.com']);
$this->assertFilterReturnsEmails('any', ['cash'], 99, []);
$this->assertFilterReturnsEmails('any', ['cash'], 100, ['c2@e.com']);
$this->assertFilterReturnsEmails('any', ['cash', 'paypal'], 100, ['c1@e.com', 'c2@e.com']);
$this->assertFilterReturnsEmails('all', ['paypal'], 1, []);
$this->assertFilterReturnsEmails('all', ['paypal'], 2, ['c1@e.com']);
$this->assertFilterReturnsEmails('all', ['paypal', 'cheque'], 2, []);
$this->assertFilterReturnsEmails('all', ['paypal', 'cheque'], 5, ['c1@e.com']);
$this->assertFilterReturnsEmails('none', ['paypal'], 1, ['c1@e.com', 'c2@e.com']);
$this->assertFilterReturnsEmails('none', ['paypal'], 2, ['c2@e.com']);
$this->assertFilterReturnsEmails('none', ['cheque'], 2, ['c1@e.com', 'c2@e.com']);
$this->assertFilterReturnsEmails('none', ['cheque'], 5, ['c2@e.com']);
}
private function assertFilterReturnsEmails(string $operator, array $paymentMethods, int $days, array $expectedEmails): void {
$filterData = new DynamicSegmentFilterData(DynamicSegmentFilterData::TYPE_WOOCOMMERCE, WooCommerceUsedPaymentMethod::ACTION, [
'operator' => $operator,
'payment_methods' => $paymentMethods,
'used_payment_method_days' => $days,
]);
$emails = $this->tester->getSubscriberEmailsMatchingDynamicFilter($filterData, $this->filter);
$this->assertEqualsCanonicalizing($expectedEmails, $emails);
}
private function createOrder(int $customerId, Carbon $createdAt, string $paymentMethod): int {
$order = $this->tester->createWooCommerceOrder();
$order->set_customer_id($customerId);
$order->set_date_created($createdAt->toDateTimeString());
$order->set_status('wc-completed');
$order->set_payment_method($paymentMethod);
$order->save();
$this->tester->updateWooOrderStats($order->get_id());
return $order->get_id();
}
}

View File

@ -27,6 +27,7 @@
var mailpoet_can_use_woocommerce_subscriptions = <%= json_encode(can_use_woocommerce_subscriptions) %>;
var mailpoet_woocommerce_currency_symbol = <%= json_encode(woocommerce_currency_symbol) %>;
var mailpoet_woocommerce_countries = <%= json_encode(woocommerce_countries) %>;
var mailpoet_woocommerce_payment_methods = <%= json_encode(woocommerce_payment_methods) %>;
var mailpoet_signup_forms = <%= json_encode(signup_forms) %>;
</script>
<% endblock %>
@ -195,6 +196,7 @@
'selectWooMembership': __('Search membership plans'),
'segmentsActiveSubscription': __('has active subscription'),
'woocommerceSubscriptions': _x('WooCommerce Subscriptions', 'Dynamic segment creation: User selects this to use any WooCommerce Subscriptions filters'),
'wooUsedPaymentMethod': __('used payment method'),
'selectWooSubscription': __('Search subscriptions'),
'searchLists': __('Search lists'),
'subscriberTag': _x('tag', 'Subscriber tag'),
@ -233,6 +235,7 @@
'selectWooPurchasedCategory': __('Search categories'),
'selectWooPurchasedProduct': __('Search products'),
'selectWooCountry': __('Search countries'),
'selectWooPaymentMethods': __('Search payment methods'),
'woocommerce': _x('WooCommerce', 'Dynamic segment creation: User selects this to use any woocommerce filters'),
'dynamicSegmentSizeIsCalculated': __('Calculating segment size…'),