Files
esun3d/app/code/Esun/CustomerCoupon/Plugin/GenerateAssignedCouponsPlugin.php
2026-09-15 10:35:56 +08:00

132 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
namespace Esun\CustomerCoupon\Plugin;
use Esun\CustomerCoupon\Model\AssignmentService;
use Magento\Customer\Model\ResourceModel\Customer\CollectionFactory as CustomerCollectionFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Escaper;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Json\Helper\Data as JsonHelper;
use Magento\SalesRule\Api\CouponManagementInterface;
use Magento\SalesRule\Api\Data\CouponGenerationSpecInterfaceFactory;
use Magento\SalesRule\Controller\Adminhtml\Promo\Quote\Generate;
use Magento\SalesRule\Model\Quote\GetCouponCodeLengthInterface;
use Magento\SalesRule\Model\Rule;
use Magento\SalesRule\Model\RuleFactory;
use Magento\Store\Model\ScopeInterface;
use Psr\Log\LoggerInterface;
class GenerateAssignedCouponsPlugin
{
private const XML_CONFIG_COUPON_QUANTITY_LIMIT = 'promo/auto_generated_coupon_codes/quantity_limit';
public function __construct(
private readonly CouponGenerationSpecInterfaceFactory $generationSpecFactory,
private readonly CouponManagementInterface $couponManagement,
private readonly AssignmentService $assignmentService,
private readonly RuleFactory $ruleFactory,
private readonly CustomerCollectionFactory $customerCollectionFactory,
private readonly GetCouponCodeLengthInterface $getCouponCodeLength,
private readonly ScopeConfigInterface $scopeConfig,
private readonly JsonHelper $jsonHelper,
private readonly Escaper $escaper,
private readonly LoggerInterface $logger
) {
}
public function aroundExecute(Generate $subject, callable $proceed): mixed
{
$customerIds = $this->parseCustomerIds($subject->getRequest()->getParam('customer_ids'));
if (!$customerIds) {
return $proceed();
}
try {
$data = $subject->getRequest()->getParams();
$ruleId = (int)($data['rule_id'] ?? 0);
$rule = $this->ruleFactory->create()->load($ruleId);
if (!$rule->getId()) {
throw new LocalizedException(__('Rule is not defined.'));
}
if ((int)$rule->getCouponType() !== Rule::COUPON_TYPE_AUTO && !$rule->getUseAutoGeneration()) {
throw new LocalizedException(
__('The rule coupon settings changed. Please save the rule before using auto-generation.')
);
}
$this->validateCustomers($customerIds, $rule);
$quantityLimit = (int)$this->scopeConfig->getValue(
self::XML_CONFIG_COUPON_QUANTITY_LIMIT,
ScopeInterface::SCOPE_STORE
);
if ($quantityLimit > 0 && count($customerIds) > $quantityLimit) {
throw new LocalizedException(__('The selected customer count exceeds the configured coupon limit.'));
}
$data['qty'] = count($customerIds);
$data['quantity'] = count($customerIds);
if ((int)($data['length'] ?? 0) !== $this->getCouponCodeLength->fetchCouponCodeLength($data)) {
throw new LocalizedException(
__('The coupon code length is too short for the selected customer count.')
);
}
$specification = $this->generationSpecFactory->create(['data' => $data]);
$codes = $this->couponManagement->generate($specification);
try {
$this->assignmentService->assignCodes($codes, $customerIds, $ruleId);
} catch (\Throwable $exception) {
$this->couponManagement->deleteByCodes($codes);
throw $exception;
}
return $this->respond($subject, [
'messages' => '<div class="messages"><div class="message message-success success"><div>'
. $this->escaper->escapeHtml(
__('Generated and assigned %1 coupon(s).', count($customerIds))
)
. '</div></div></div>',
]);
} catch (LocalizedException $exception) {
return $this->respond($subject, ['error' => $exception->getMessage()]);
} catch (\Throwable $exception) {
$this->logger->critical($exception);
return $this->respond($subject, [
'error' => (string)__('Something went wrong while generating assigned coupons.'),
]);
}
}
/** @return int[] */
private function parseCustomerIds(mixed $value): array
{
$values = is_array($value) ? $value : explode(',', (string)$value);
$ids = array_values(array_unique(array_filter(array_map('intval', $values))));
return $ids;
}
/** @param int[] $customerIds */
private function validateCustomers(array $customerIds, Rule $rule): void
{
$collection = $this->customerCollectionFactory->create();
$collection->addFieldToFilter('entity_id', ['in' => $customerIds]);
$collection->addFieldToFilter('website_id', ['in' => array_map('intval', $rule->getWebsiteIds())]);
$collection->addFieldToFilter('group_id', ['in' => array_map('intval', $rule->getCustomerGroupIds())]);
if ((int)$collection->getSize() !== count($customerIds)) {
throw new LocalizedException(
__('One or more selected customers are not eligible for this rule website or customer group.')
);
}
}
private function respond(Generate $subject, array $data): mixed
{
return $subject->getResponse()->representJson($this->jsonHelper->jsonEncode($data));
}
}