Add filter for used payment method
MAILPOET-4993
This commit is contained in:
committed by
Aschepikov
parent
a976006a2c
commit
ec68818c1a
@ -2,6 +2,10 @@
|
||||
width: min-content;
|
||||
}
|
||||
|
||||
.mailpoet-max-content-width {
|
||||
max-width: max-content;
|
||||
}
|
||||
|
||||
.mailpoet-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class FilterHelper {
|
||||
return $this->entityManager
|
||||
->getConnection()
|
||||
->createQueryBuilder()
|
||||
->select('id')
|
||||
->select($this->getSubscribersTable() . '.id')
|
||||
->from($this->getSubscribersTable());
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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…'),
|
||||
|
Reference in New Issue
Block a user