Add an option to restrict coupon to current subscriber

[MAILPOET-6384]
This commit is contained in:
alex-mpoet
2025-02-20 15:00:47 +03:00
committed by Aschepikov
parent 1d28e8d7c6
commit a3ec922ca5
5 changed files with 57 additions and 9 deletions

View File

@@ -19,6 +19,7 @@ Module.CouponBlockModel = base.BlockModel.extend({
return this._getDefaults( return this._getDefaults(
{ {
isStandardEmail: App.getNewsletter().isStandardEmail(), isStandardEmail: App.getNewsletter().isStandardEmail(),
isAutomationEmail: App.getNewsletter().isAutomationEmail(),
productIds: [], // selected product ids, productIds: [], // selected product ids,
excludedProductIds: [], excludedProductIds: [],
productCategoryIds: [], // selected categories id productCategoryIds: [], // selected categories id
@@ -33,6 +34,10 @@ Module.CouponBlockModel = base.BlockModel.extend({
minimumAmount: '', minimumAmount: '',
maximumAmount: '', maximumAmount: '',
emailRestrictions: '', emailRestrictions: '',
restrictToSubscriber: false,
showRestrictToSubscriber:
App.getNewsletter().isAutomationEmail() ||
App.getNewsletter().isWelcomeEmail(),
styles: { styles: {
block: { block: {
backgroundColor: '#ffffff', backgroundColor: '#ffffff',

View File

@@ -31,6 +31,7 @@ type State = {
productCategoryIds: Post[]; productCategoryIds: Post[];
excludedProductCategoryIds: Post[]; excludedProductCategoryIds: Post[];
emailRestrictions: string; emailRestrictions: string;
restrictToSubscriber: boolean;
}; };
class UsageRestriction extends Component<Props, State> { class UsageRestriction extends Component<Props, State> {
@@ -62,6 +63,8 @@ class UsageRestriction extends Component<Props, State> {
'excludedProductCategoryIds', 'excludedProductCategoryIds',
).toJSON() as Post[], ).toJSON() as Post[],
emailRestrictions: this.getValueCallback('emailRestrictions') as string, emailRestrictions: this.getValueCallback('emailRestrictions') as string,
restrictToSubscriber:
(this.getValueCallback('restrictToSubscriber') as boolean) || false,
}; };
} }
@@ -262,6 +265,25 @@ class UsageRestriction extends Component<Props, State> {
)} )}
/> />
</PanelRow> </PanelRow>
{this.getValueCallback('showRestrictToSubscriber') && (
<PanelRow>
<ToggleControl
checked={this.state.restrictToSubscriber}
label={__('Restrict to subscriber email', 'mailpoet')}
onChange={(restrictToSubscriber) => {
this.setValueCallback(
'restrictToSubscriber',
restrictToSubscriber,
);
this.setState({ restrictToSubscriber });
}}
help={__(
'Restrict coupon usage to the subscriber receiving this email.',
'mailpoet',
)}
/>
</PanelRow>
)}
</PanelBody> </PanelBody>
</Panel> </Panel>
); );

View File

@@ -35,6 +35,9 @@ Module.NewsletterModel = SuperModel.extend({
isStandardEmail: function isStandardEmail() { isStandardEmail: function isStandardEmail() {
return this.get('type') === NewsletterType.Standard; return this.get('type') === NewsletterType.Standard;
}, },
isWelcomeEmail: function isWelcomeEmail() {
return this.get('type') === NewsletterType.Welcome;
},
}); });
// Content block view and model handlers for different content types // Content block view and model handlers for different content types

View File

@@ -53,7 +53,7 @@ class Preprocessor {
return $content; return $content;
} }
$contentBlocks = $content['blocks']; $contentBlocks = $content['blocks'];
$contentBlocks = $this->couponPreProcessor->processCoupons($newsletter, $contentBlocks, $preview); $contentBlocks = $this->couponPreProcessor->processCoupons($newsletter, $contentBlocks, $preview, $sendingQueue);
$content['blocks'] = $this->processContainer($newsletter, $contentBlocks, $preview, $sendingQueue); $content['blocks'] = $this->processContainer($newsletter, $contentBlocks, $preview, $sendingQueue);
return $content; return $content;
} }

View File

@@ -3,6 +3,7 @@
namespace MailPoet\WooCommerce; namespace MailPoet\WooCommerce;
use MailPoet\Entities\NewsletterEntity; use MailPoet\Entities\NewsletterEntity;
use MailPoet\Entities\SendingQueueEntity;
use MailPoet\Newsletter\NewslettersRepository; use MailPoet\Newsletter\NewslettersRepository;
use MailPoet\Newsletter\Renderer\Blocks\Coupon; use MailPoet\Newsletter\Renderer\Blocks\Coupon;
use MailPoet\NewsletterProcessingException; use MailPoet\NewsletterProcessingException;
@@ -30,12 +31,12 @@ class CouponPreProcessor {
/** /**
* @throws NewsletterProcessingException * @throws NewsletterProcessingException
*/ */
public function processCoupons(NewsletterEntity $newsletter, array $blocks, bool $preview = false): array { public function processCoupons(NewsletterEntity $newsletter, array $blocks, bool $preview = false, ?SendingQueueEntity $sendingQueue = null): array {
if ($preview) { if ($preview) {
return $blocks; return $blocks;
} }
$generated = $this->ensureCouponForBlocks($blocks, $newsletter); $generated = $this->ensureCouponForBlocks($blocks, $newsletter, $sendingQueue);
$body = $newsletter->getBody(); $body = $newsletter->getBody();
if ($generated && $body && $this->shouldPersist($newsletter)) { if ($generated && $body && $this->shouldPersist($newsletter)) {
@@ -54,11 +55,10 @@ class CouponPreProcessor {
return $blocks; return $blocks;
} }
private function ensureCouponForBlocks(array &$blocks, NewsletterEntity $newsletter): bool { private function ensureCouponForBlocks(array &$blocks, NewsletterEntity $newsletter, ?SendingQueueEntity $sendingQueue): bool {
foreach ($blocks as &$innerBlock) { foreach ($blocks as &$innerBlock) {
if (isset($innerBlock['blocks']) && !empty($innerBlock['blocks'])) { if (isset($innerBlock['blocks']) && !empty($innerBlock['blocks'])) {
$this->ensureCouponForBlocks($innerBlock['blocks'], $newsletter); $this->ensureCouponForBlocks($innerBlock['blocks'], $newsletter, $sendingQueue);
} }
if (isset($innerBlock['type']) && $innerBlock['type'] === Coupon::TYPE) { if (isset($innerBlock['type']) && $innerBlock['type'] === Coupon::TYPE) {
if (!$this->wcHelper->isWooCommerceActive()) { if (!$this->wcHelper->isWooCommerceActive()) {
@@ -66,7 +66,7 @@ class CouponPreProcessor {
} }
if ($this->shouldGenerateCoupon($innerBlock)) { if ($this->shouldGenerateCoupon($innerBlock)) {
try { try {
$innerBlock['couponId'] = $this->addOrUpdateCoupon($innerBlock, $newsletter); $innerBlock['couponId'] = $this->addOrUpdateCoupon($innerBlock, $newsletter, $sendingQueue);
$this->generated = true; $this->generated = true;
} catch (\Exception $e) { } catch (\Exception $e) {
throw NewsletterProcessingException::create()->withMessage($e->getMessage())->withCode($e->getCode()); throw NewsletterProcessingException::create()->withMessage($e->getMessage())->withCode($e->getCode());
@@ -81,10 +81,11 @@ class CouponPreProcessor {
/** /**
* @param array $couponBlock * @param array $couponBlock
* @param NewsletterEntity $newsletter * @param NewsletterEntity $newsletter
* @param SendingQueueEntity|null $sendingQueue
* @return int * @return int
* @throws \WC_Data_Exception|\Exception * @throws \WC_Data_Exception|\Exception
*/ */
private function addOrUpdateCoupon(array $couponBlock, NewsletterEntity $newsletter) { private function addOrUpdateCoupon(array $couponBlock, NewsletterEntity $newsletter, ?SendingQueueEntity $sendingQueue) {
$coupon = $this->wcHelper->createWcCoupon($couponBlock['couponId'] ?? ''); $coupon = $this->wcHelper->createWcCoupon($couponBlock['couponId'] ?? '');
if ($this->shouldGenerateCoupon($couponBlock)) { if ($this->shouldGenerateCoupon($couponBlock)) {
$code = isset($couponBlock['code']) && $couponBlock['code'] !== Coupon::CODE_PLACEHOLDER ? $couponBlock['code'] : $this->generateRandomCode(); $code = isset($couponBlock['code']) && $couponBlock['code'] !== Coupon::CODE_PLACEHOLDER ? $couponBlock['code'] : $this->generateRandomCode();
@@ -128,7 +129,24 @@ class CouponPreProcessor {
$coupon->set_product_categories($this->getItemIds($couponBlock['productCategoryIds'] ?? [])); $coupon->set_product_categories($this->getItemIds($couponBlock['productCategoryIds'] ?? []));
$coupon->set_excluded_product_categories($this->getItemIds($couponBlock['excludedProductCategoryIds'] ?? [])); $coupon->set_excluded_product_categories($this->getItemIds($couponBlock['excludedProductCategoryIds'] ?? []));
$coupon->set_email_restrictions(explode(',', $couponBlock['emailRestrictions'] ?? '')); $emailRestrictions = [];
if (!empty($couponBlock['emailRestrictions'])) {
$emailRestrictions = explode(',', $couponBlock['emailRestrictions']);
}
if (!empty($couponBlock['restrictToSubscriber']) && $sendingQueue && $sendingQueue->getTask()) {
$subscribers = $sendingQueue->getTask()->getSubscribers();
if (is_iterable($subscribers) && count($subscribers) === 1) { // Only apply to single-subscriber sending queues
foreach ($subscribers as $taskSubscriber) {
$subscriber = $taskSubscriber->getSubscriber();
if ($subscriber && $subscriber->getEmail()) {
$emailRestrictions[] = $subscriber->getEmail();
}
}
}
}
$coupon->set_email_restrictions(array_unique(array_filter($emailRestrictions)));
// usage limit // usage limit
$coupon->set_usage_limit($couponBlock['usageLimit'] ?? 0); $coupon->set_usage_limit($couponBlock['usageLimit'] ?? 0);