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;
|
width: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mailpoet-max-content-width {
|
||||||
|
max-width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
.mailpoet-nowrap {
|
.mailpoet-nowrap {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ type Props = SelectHTMLAttributes<HTMLSelectElement> & {
|
|||||||
dimension?: 'small';
|
dimension?: 'small';
|
||||||
isFullWidth?: boolean;
|
isFullWidth?: boolean;
|
||||||
isMinWidth?: boolean;
|
isMinWidth?: boolean;
|
||||||
|
isMaxContentWidth?: boolean;
|
||||||
iconStart?: JSX.Element;
|
iconStart?: JSX.Element;
|
||||||
automationId?: string;
|
automationId?: string;
|
||||||
};
|
};
|
||||||
@@ -17,6 +18,7 @@ export const Select = forwardRef(
|
|||||||
dimension,
|
dimension,
|
||||||
isFullWidth,
|
isFullWidth,
|
||||||
isMinWidth,
|
isMinWidth,
|
||||||
|
isMaxContentWidth,
|
||||||
iconStart,
|
iconStart,
|
||||||
automationId,
|
automationId,
|
||||||
...attributes
|
...attributes
|
||||||
@@ -29,6 +31,7 @@ export const Select = forwardRef(
|
|||||||
'mailpoet-disabled': attributes.disabled,
|
'mailpoet-disabled': attributes.disabled,
|
||||||
'mailpoet-full-width': isFullWidth,
|
'mailpoet-full-width': isFullWidth,
|
||||||
'mailpoet-min-width': isMinWidth,
|
'mailpoet-min-width': isMinWidth,
|
||||||
|
'mailpoet-max-content-width': isMaxContentWidth,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{iconStart}
|
{iconStart}
|
||||||
|
@@ -14,6 +14,7 @@ import {
|
|||||||
WindowProducts,
|
WindowProducts,
|
||||||
WindowWooCommerceCountries,
|
WindowWooCommerceCountries,
|
||||||
WooCommerceFormItem,
|
WooCommerceFormItem,
|
||||||
|
WooPaymentMethod,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { DateFields, validateDateField } from './date_fields';
|
import { DateFields, validateDateField } from './date_fields';
|
||||||
import { storeName } from '../store';
|
import { storeName } from '../store';
|
||||||
@@ -87,6 +88,17 @@ export function validateWooCommerce(formItems: WooCommerceFormItem): boolean {
|
|||||||
) {
|
) {
|
||||||
return false;
|
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) {
|
if (formItems.action === WooCommerceActionTypes.PURCHASE_DATE) {
|
||||||
return validateDateField(formItems);
|
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 = {
|
const componentsMap = {
|
||||||
[WooCommerceActionTypes.CUSTOMER_IN_COUNTRY]: CustomerInCountryFields,
|
[WooCommerceActionTypes.CUSTOMER_IN_COUNTRY]: CustomerInCountryFields,
|
||||||
[WooCommerceActionTypes.NUMBER_OF_ORDERS]: NumberOfOrdersFields,
|
[WooCommerceActionTypes.NUMBER_OF_ORDERS]: NumberOfOrdersFields,
|
||||||
@@ -630,6 +731,7 @@ const componentsMap = {
|
|||||||
[WooCommerceActionTypes.SINGLE_ORDER_VALUE]: SingleOrderValueFields,
|
[WooCommerceActionTypes.SINGLE_ORDER_VALUE]: SingleOrderValueFields,
|
||||||
[WooCommerceActionTypes.TOTAL_SPENT]: TotalSpentFields,
|
[WooCommerceActionTypes.TOTAL_SPENT]: TotalSpentFields,
|
||||||
[WooCommerceActionTypes.AVERAGE_SPENT]: AverageSpentFields,
|
[WooCommerceActionTypes.AVERAGE_SPENT]: AverageSpentFields,
|
||||||
|
[WooCommerceActionTypes.USED_PAYMENT_METHOD]: UsedPaymentMethodFields,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function WooCommerceFields({ filterIndex }: Props): JSX.Element {
|
export function WooCommerceFields({ filterIndex }: Props): JSX.Element {
|
||||||
|
@@ -11,6 +11,7 @@ export enum WooCommerceActionTypes {
|
|||||||
AVERAGE_SPENT = 'averageSpent',
|
AVERAGE_SPENT = 'averageSpent',
|
||||||
CUSTOMER_IN_COUNTRY = 'customerInCountry',
|
CUSTOMER_IN_COUNTRY = 'customerInCountry',
|
||||||
SINGLE_ORDER_VALUE = 'singleOrderValue',
|
SINGLE_ORDER_VALUE = 'singleOrderValue',
|
||||||
|
USED_PAYMENT_METHOD = 'usedPaymentMethod',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WooCommerceOptions = [
|
export const WooCommerceOptions = [
|
||||||
@@ -54,6 +55,11 @@ export const WooCommerceOptions = [
|
|||||||
label: MailPoet.I18n.t('wooTotalSpent'),
|
label: MailPoet.I18n.t('wooTotalSpent'),
|
||||||
group: SegmentTypes.WooCommerce,
|
group: SegmentTypes.WooCommerce,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: WooCommerceActionTypes.USED_PAYMENT_METHOD,
|
||||||
|
label: MailPoet.I18n.t('wooUsedPaymentMethod'),
|
||||||
|
group: SegmentTypes.WooCommerce,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// WooCommerce Memberships
|
// WooCommerce Memberships
|
||||||
|
@@ -21,6 +21,7 @@ export const getInitialState = (): StateType => ({
|
|||||||
canUseWooSubscriptions: window.mailpoet_can_use_woocommerce_subscriptions,
|
canUseWooSubscriptions: window.mailpoet_can_use_woocommerce_subscriptions,
|
||||||
wooCurrencySymbol: window.mailpoet_woocommerce_currency_symbol,
|
wooCurrencySymbol: window.mailpoet_woocommerce_currency_symbol,
|
||||||
wooCountries: window.mailpoet_woocommerce_countries,
|
wooCountries: window.mailpoet_woocommerce_countries,
|
||||||
|
wooPaymentMethods: window.mailpoet_woocommerce_payment_methods,
|
||||||
customFieldsList: window.mailpoet_custom_fields,
|
customFieldsList: window.mailpoet_custom_fields,
|
||||||
tags: window.mailpoet_tags,
|
tags: window.mailpoet_tags,
|
||||||
signupForms: window.mailpoet_signup_forms,
|
signupForms: window.mailpoet_signup_forms,
|
||||||
|
@@ -17,6 +17,7 @@ import {
|
|||||||
WindowProducts,
|
WindowProducts,
|
||||||
WindowSubscriptionProducts,
|
WindowSubscriptionProducts,
|
||||||
WindowWooCommerceCountries,
|
WindowWooCommerceCountries,
|
||||||
|
WooPaymentMethod,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
export const getProducts = (state: StateType): WindowProducts => state.products;
|
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 getTags = (state: StateType): Tag[] => state.tags;
|
||||||
export const getSignupForms = (state: StateType): SignupForm[] =>
|
export const getSignupForms = (state: StateType): SignupForm[] =>
|
||||||
state.signupForms;
|
state.signupForms;
|
||||||
|
export const getPaymentMethods = (state: StateType): WooPaymentMethod[] =>
|
||||||
|
state.wooPaymentMethods;
|
||||||
export const getSegmentFilter = (
|
export const getSegmentFilter = (
|
||||||
state: StateType,
|
state: StateType,
|
||||||
index: number,
|
index: number,
|
||||||
|
@@ -100,6 +100,8 @@ export interface WooCommerceFormItem extends FormItem {
|
|||||||
average_spent_type?: string;
|
average_spent_type?: string;
|
||||||
average_spent_amount?: string;
|
average_spent_amount?: string;
|
||||||
average_spent_days?: string;
|
average_spent_days?: string;
|
||||||
|
payment_methods?: string[];
|
||||||
|
used_payment_method_days?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WooCommerceMembershipFormItem extends FormItem {
|
export interface WooCommerceMembershipFormItem extends FormItem {
|
||||||
@@ -204,6 +206,7 @@ export interface SegmentFormDataWindow extends Window {
|
|||||||
mailpoet_subscription_products: WindowSubscriptionProducts;
|
mailpoet_subscription_products: WindowSubscriptionProducts;
|
||||||
mailpoet_product_categories: WindowProductCategories;
|
mailpoet_product_categories: WindowProductCategories;
|
||||||
mailpoet_woocommerce_countries: WindowWooCommerceCountries;
|
mailpoet_woocommerce_countries: WindowWooCommerceCountries;
|
||||||
|
mailpoet_woocommerce_payment_methods: WooPaymentMethod[];
|
||||||
mailpoet_newsletters_list: WindowNewslettersList;
|
mailpoet_newsletters_list: WindowNewslettersList;
|
||||||
mailpoet_custom_fields: WindowCustomFields;
|
mailpoet_custom_fields: WindowCustomFields;
|
||||||
mailpoet_can_use_woocommerce_memberships: boolean;
|
mailpoet_can_use_woocommerce_memberships: boolean;
|
||||||
@@ -225,6 +228,7 @@ export interface StateType {
|
|||||||
canUseWooSubscriptions: boolean;
|
canUseWooSubscriptions: boolean;
|
||||||
wooCurrencySymbol: string;
|
wooCurrencySymbol: string;
|
||||||
wooCountries: WindowWooCommerceCountries;
|
wooCountries: WindowWooCommerceCountries;
|
||||||
|
wooPaymentMethods: WooPaymentMethod[];
|
||||||
customFieldsList: WindowCustomFields;
|
customFieldsList: WindowCustomFields;
|
||||||
segment: Segment;
|
segment: Segment;
|
||||||
subscriberCount: SubscriberCount;
|
subscriberCount: SubscriberCount;
|
||||||
@@ -280,3 +284,8 @@ export type SignupForm = {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WooPaymentMethod = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
@@ -136,6 +136,18 @@ class Segments {
|
|||||||
'name' => $form->getName(),
|
'name' => $form->getName(),
|
||||||
];
|
];
|
||||||
}, $this->formsRepository->findAll());
|
}, $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);
|
$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\WooCommerceSingleOrderValue::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent::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\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\Filters\WooFilterHelper::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Segments\DynamicSegments\SegmentSaveController::class)->setPublic(true);
|
$container->autowire(\MailPoet\Segments\DynamicSegments\SegmentSaveController::class)->setPublic(true);
|
||||||
$container->autowire(\MailPoet\Segments\DynamicSegments\FilterDataMapper::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\WooCommerceSingleOrderValue;
|
||||||
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
|
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
|
||||||
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
|
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
|
||||||
|
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedPaymentMethod;
|
||||||
use MailPoet\WP\Functions as WPFunctions;
|
use MailPoet\WP\Functions as WPFunctions;
|
||||||
|
|
||||||
class FilterDataMapper {
|
class FilterDataMapper {
|
||||||
@@ -309,6 +310,19 @@ class FilterDataMapper {
|
|||||||
$filterData['average_spent_days'] = $data['average_spent_days'];
|
$filterData['average_spent_days'] = $data['average_spent_days'];
|
||||||
$filterData['average_spent_amount'] = $data['average_spent_amount'];
|
$filterData['average_spent_amount'] = $data['average_spent_amount'];
|
||||||
$filterData['average_spent_type'] = $data['average_spent_type'];
|
$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 {
|
} else {
|
||||||
throw new InvalidFilterException("Unknown action " . $data['action'], InvalidFilterException::MISSING_ACTION);
|
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\WooCommerceSingleOrderValue;
|
||||||
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
|
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceSubscription;
|
||||||
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
|
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceTotalSpent;
|
||||||
|
use MailPoet\Segments\DynamicSegments\Filters\WooCommerceUsedPaymentMethod;
|
||||||
|
|
||||||
class FilterFactory {
|
class FilterFactory {
|
||||||
/** @var EmailAction */
|
/** @var EmailAction */
|
||||||
@@ -92,6 +93,9 @@ class FilterFactory {
|
|||||||
/** @var SubscriberTextField */
|
/** @var SubscriberTextField */
|
||||||
private $subscriberTextField;
|
private $subscriberTextField;
|
||||||
|
|
||||||
|
/** @var WooCommerceUsedPaymentMethod */
|
||||||
|
private $wooCommerceUsedPaymentMethod;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
EmailAction $emailAction,
|
EmailAction $emailAction,
|
||||||
EmailActionClickAny $emailActionClickAny,
|
EmailActionClickAny $emailActionClickAny,
|
||||||
@@ -113,6 +117,7 @@ class FilterFactory {
|
|||||||
SubscriberSubscribedViaForm $subscribedViaForm,
|
SubscriberSubscribedViaForm $subscribedViaForm,
|
||||||
WooCommerceSingleOrderValue $wooCommerceSingleOrderValue,
|
WooCommerceSingleOrderValue $wooCommerceSingleOrderValue,
|
||||||
WooCommerceAverageSpent $wooCommerceAverageSpent,
|
WooCommerceAverageSpent $wooCommerceAverageSpent,
|
||||||
|
WooCommerceUsedPaymentMethod $wooCommerceUsedPaymentMethod,
|
||||||
SubscriberTextField $subscriberTextField
|
SubscriberTextField $subscriberTextField
|
||||||
) {
|
) {
|
||||||
$this->emailAction = $emailAction;
|
$this->emailAction = $emailAction;
|
||||||
@@ -136,6 +141,7 @@ class FilterFactory {
|
|||||||
$this->subscriberTextField = $subscriberTextField;
|
$this->subscriberTextField = $subscriberTextField;
|
||||||
$this->subscribedViaForm = $subscribedViaForm;
|
$this->subscribedViaForm = $subscribedViaForm;
|
||||||
$this->wooCommerceAverageSpent = $wooCommerceAverageSpent;
|
$this->wooCommerceAverageSpent = $wooCommerceAverageSpent;
|
||||||
|
$this->wooCommerceUsedPaymentMethod = $wooCommerceUsedPaymentMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFilterForFilterEntity(DynamicSegmentFilterEntity $filter): Filter {
|
public function getFilterForFilterEntity(DynamicSegmentFilterEntity $filter): Filter {
|
||||||
@@ -223,6 +229,8 @@ class FilterFactory {
|
|||||||
return $this->wooCommercePurchaseDate;
|
return $this->wooCommercePurchaseDate;
|
||||||
} elseif ($action === WooCommerceAverageSpent::ACTION) {
|
} elseif ($action === WooCommerceAverageSpent::ACTION) {
|
||||||
return $this->wooCommerceAverageSpent;
|
return $this->wooCommerceAverageSpent;
|
||||||
|
} elseif ($action === WooCommerceUsedPaymentMethod::ACTION) {
|
||||||
|
return $this->wooCommerceUsedPaymentMethod;
|
||||||
}
|
}
|
||||||
return $this->wooCommerceCategory;
|
return $this->wooCommerceCategory;
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@ class FilterHelper {
|
|||||||
return $this->entityManager
|
return $this->entityManager
|
||||||
->getConnection()
|
->getConnection()
|
||||||
->createQueryBuilder()
|
->createQueryBuilder()
|
||||||
->select('id')
|
->select($this->getSubscribersTable() . '.id')
|
||||||
->from($this->getSubscribersTable());
|
->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
|
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_can_use_woocommerce_subscriptions = <%= json_encode(can_use_woocommerce_subscriptions) %>;
|
||||||
var mailpoet_woocommerce_currency_symbol = <%= json_encode(woocommerce_currency_symbol) %>;
|
var mailpoet_woocommerce_currency_symbol = <%= json_encode(woocommerce_currency_symbol) %>;
|
||||||
var mailpoet_woocommerce_countries = <%= json_encode(woocommerce_countries) %>;
|
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) %>;
|
var mailpoet_signup_forms = <%= json_encode(signup_forms) %>;
|
||||||
</script>
|
</script>
|
||||||
<% endblock %>
|
<% endblock %>
|
||||||
@@ -195,6 +196,7 @@
|
|||||||
'selectWooMembership': __('Search membership plans'),
|
'selectWooMembership': __('Search membership plans'),
|
||||||
'segmentsActiveSubscription': __('has active subscription'),
|
'segmentsActiveSubscription': __('has active subscription'),
|
||||||
'woocommerceSubscriptions': _x('WooCommerce Subscriptions', 'Dynamic segment creation: User selects this to use any WooCommerce Subscriptions filters'),
|
'woocommerceSubscriptions': _x('WooCommerce Subscriptions', 'Dynamic segment creation: User selects this to use any WooCommerce Subscriptions filters'),
|
||||||
|
'wooUsedPaymentMethod': __('used payment method'),
|
||||||
'selectWooSubscription': __('Search subscriptions'),
|
'selectWooSubscription': __('Search subscriptions'),
|
||||||
'searchLists': __('Search lists'),
|
'searchLists': __('Search lists'),
|
||||||
'subscriberTag': _x('tag', 'Subscriber tag'),
|
'subscriberTag': _x('tag', 'Subscriber tag'),
|
||||||
@@ -233,6 +235,7 @@
|
|||||||
'selectWooPurchasedCategory': __('Search categories'),
|
'selectWooPurchasedCategory': __('Search categories'),
|
||||||
'selectWooPurchasedProduct': __('Search products'),
|
'selectWooPurchasedProduct': __('Search products'),
|
||||||
'selectWooCountry': __('Search countries'),
|
'selectWooCountry': __('Search countries'),
|
||||||
|
'selectWooPaymentMethods': __('Search payment methods'),
|
||||||
'woocommerce': _x('WooCommerce', 'Dynamic segment creation: User selects this to use any woocommerce filters'),
|
'woocommerce': _x('WooCommerce', 'Dynamic segment creation: User selects this to use any woocommerce filters'),
|
||||||
|
|
||||||
'dynamicSegmentSizeIsCalculated': __('Calculating segment size…'),
|
'dynamicSegmentSizeIsCalculated': __('Calculating segment size…'),
|
||||||
|
Reference in New Issue
Block a user