Skip to content

Commit ed2ffad

Browse files
authored
Merge pull request #6901 from magento-tsg-csl3/MC-41917
MC-41917: Issue with 'catalog_product_alert' cron
2 parents 6a0db3b + c414098 commit ed2ffad

File tree

13 files changed

+999
-834
lines changed

13 files changed

+999
-834
lines changed

app/code/Magento/Catalog/Block/Product/ImageFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ public function create(Product $product, string $imageId, array $attributes = nu
165165
'image_url' => $imageAsset->getUrl(),
166166
'width' => $imageMiscParams['image_width'],
167167
'height' => $imageMiscParams['image_height'],
168-
'label' => $this->getLabel($product, $imageMiscParams['image_type']),
168+
'label' => $this->getLabel($product, $imageMiscParams['image_type'] ?? ''),
169169
'ratio' => $this->getRatio($imageMiscParams['image_width'] ?? 0, $imageMiscParams['image_height'] ?? 0),
170170
'custom_attributes' => $this->filterCustomAttributes($attributes),
171171
'class' => $this->getClass($attributes),
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\ProductAlert\Model\Mailing;
9+
10+
use Magento\Catalog\Api\Data\ProductInterface;
11+
use Magento\Catalog\Api\ProductRepositoryInterface;
12+
use Magento\Catalog\Helper\Data;
13+
use Magento\Customer\Api\CustomerRepositoryInterface;
14+
use Magento\Customer\Api\Data\CustomerInterface;
15+
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
16+
use Magento\Framework\Stdlib\DateTime;
17+
use Magento\ProductAlert\Model\Email;
18+
use Magento\ProductAlert\Model\EmailFactory;
19+
use Magento\ProductAlert\Model\Price;
20+
use Magento\ProductAlert\Model\ProductSalability;
21+
use Magento\ProductAlert\Model\ResourceModel\Stock\CollectionFactory as StockCollectionFactory;
22+
use Magento\ProductAlert\Model\ResourceModel\Price\CollectionFactory as PriceCollectionFactory;
23+
use Magento\ProductAlert\Model\Stock;
24+
use Magento\Store\Api\Data\WebsiteInterface;
25+
use Magento\Store\Model\StoreManagerInterface;
26+
use Magento\Store\Model\Website;
27+
28+
/**
29+
* Class for mailing Product Alerts
30+
*/
31+
class AlertProcessor
32+
{
33+
public const ALERT_TYPE_STOCK = 'stock';
34+
public const ALERT_TYPE_PRICE = 'price';
35+
36+
/**
37+
* @var EmailFactory
38+
*/
39+
private $emailFactory;
40+
41+
/**
42+
* @var PriceCollectionFactory
43+
*/
44+
private $priceCollectionFactory;
45+
46+
/**
47+
* @var StockCollectionFactory
48+
*/
49+
private $stockCollectionFactory;
50+
51+
/**
52+
* @var CustomerRepositoryInterface
53+
*/
54+
private $customerRepository;
55+
56+
/**
57+
* @var ProductRepositoryInterface
58+
*/
59+
private $productRepository;
60+
61+
/**
62+
* @var Data
63+
*/
64+
private $catalogData;
65+
66+
/**
67+
* @var ProductSalability
68+
*/
69+
private $productSalability;
70+
71+
/**
72+
* @var StoreManagerInterface
73+
*/
74+
private $storeManager;
75+
76+
/**
77+
* @var ErrorEmailSender
78+
*/
79+
private $errorEmailSender;
80+
81+
/**
82+
* @param EmailFactory $emailFactory
83+
* @param PriceCollectionFactory $priceCollectionFactory
84+
* @param StockCollectionFactory $stockCollectionFactory
85+
* @param CustomerRepositoryInterface $customerRepository
86+
* @param ProductRepositoryInterface $productRepository
87+
* @param Data $catalogData
88+
* @param ProductSalability $productSalability
89+
* @param StoreManagerInterface $storeManager
90+
* @param ErrorEmailSender $errorEmailSender
91+
*/
92+
public function __construct(
93+
EmailFactory $emailFactory,
94+
PriceCollectionFactory $priceCollectionFactory,
95+
StockCollectionFactory $stockCollectionFactory,
96+
CustomerRepositoryInterface $customerRepository,
97+
ProductRepositoryInterface $productRepository,
98+
Data $catalogData,
99+
ProductSalability $productSalability,
100+
StoreManagerInterface $storeManager,
101+
ErrorEmailSender $errorEmailSender
102+
) {
103+
$this->emailFactory = $emailFactory;
104+
$this->priceCollectionFactory = $priceCollectionFactory;
105+
$this->stockCollectionFactory = $stockCollectionFactory;
106+
$this->customerRepository = $customerRepository;
107+
$this->productRepository = $productRepository;
108+
$this->catalogData = $catalogData;
109+
$this->productSalability = $productSalability;
110+
$this->storeManager = $storeManager;
111+
$this->errorEmailSender = $errorEmailSender;
112+
}
113+
114+
/**
115+
* Process product alerts
116+
*
117+
* @param string $alertType
118+
* @param array $customerIds
119+
* @param int $websiteId
120+
* @throws \Exception
121+
*/
122+
public function process(string $alertType, array $customerIds, int $websiteId): void
123+
{
124+
$this->validateAlertType($alertType);
125+
$errors = $this->processAlerts($alertType, $customerIds, $websiteId);
126+
if (!empty($errors)) {
127+
/** @var Website $website */
128+
$website = $this->storeManager->getWebsite($websiteId);
129+
$defaultStoreId = $website->getDefaultStore()->getId();
130+
$this->errorEmailSender->execute($errors, $defaultStoreId);
131+
}
132+
}
133+
134+
/**
135+
* Process product alerts
136+
*
137+
* @param string $alertType
138+
* @param array $customerIds
139+
* @param int $websiteId
140+
* @return array
141+
* @throws \Exception
142+
*/
143+
private function processAlerts(string $alertType, array $customerIds, int $websiteId): array
144+
{
145+
/** @var Email $email */
146+
$email = $this->emailFactory->create();
147+
$email->setType($alertType);
148+
$email->setWebsiteId($websiteId);
149+
$errors = [];
150+
151+
try {
152+
$collection = $this->getAlertCollection($alertType, $customerIds, $websiteId);
153+
} catch (\Exception $e) {
154+
$errors[] = $e->getMessage();
155+
return $errors;
156+
}
157+
158+
/** @var CustomerInterface $customer */
159+
$customer = null;
160+
/** @var Website $website */
161+
$website = $this->storeManager->getWebsite($websiteId);
162+
$defaultStoreId = $website->getDefaultStore()->getId();
163+
164+
/** @var Price|Stock $alert */
165+
foreach ($collection as $alert) {
166+
try {
167+
if ($alert->getStoreId()) {
168+
$email->setStoreId($alert->getStoreId());
169+
}
170+
if ($customer === null) {
171+
$customer = $this->customerRepository->getById($alert->getCustomerId());
172+
} elseif ((int)$customer->getId() !== (int)$alert->getCustomerId()) {
173+
$this->sendEmail($customer, $email);
174+
$customer = $this->customerRepository->getById($alert->getCustomerId());
175+
}
176+
177+
$product = $this->productRepository->getById($alert->getProductId(), false, $defaultStoreId);
178+
179+
switch ($alertType) {
180+
case self::ALERT_TYPE_STOCK:
181+
$this->saveStockAlert($alert, $product, $website, $email);
182+
break;
183+
case self::ALERT_TYPE_PRICE:
184+
$this->savePriceAlert($alert, $product, $customer, $email);
185+
break;
186+
}
187+
} catch (\Exception $e) {
188+
$errors[] = $e->getMessage();
189+
}
190+
}
191+
192+
if ($customer !== null) {
193+
try {
194+
$this->sendEmail($customer, $email);
195+
} catch (\Exception $e) {
196+
$errors[] = $e->getMessage();
197+
}
198+
}
199+
200+
return $errors;
201+
}
202+
203+
/**
204+
* Validate Alert Type
205+
*
206+
* @param string $alertType
207+
* @throws \InvalidArgumentException
208+
*/
209+
private function validateAlertType(string $alertType): void
210+
{
211+
if (!in_array($alertType, [self::ALERT_TYPE_STOCK, self::ALERT_TYPE_PRICE])) {
212+
throw new \InvalidArgumentException('Invalid alert type');
213+
}
214+
}
215+
216+
/**
217+
* Get alert collection
218+
*
219+
* @param string $alertType
220+
* @param array $customerIds
221+
* @param int $websiteId
222+
* @return AbstractCollection
223+
* @throws \InvalidArgumentException
224+
*/
225+
private function getAlertCollection(string $alertType, array $customerIds, int $websiteId): AbstractCollection
226+
{
227+
switch ($alertType) {
228+
case self::ALERT_TYPE_STOCK:
229+
$collection = $this->stockCollectionFactory->create();
230+
$collection->addFieldToFilter('customer_id', ['in' => $customerIds])
231+
->addWebsiteFilter($websiteId)
232+
->addStatusFilter(0)
233+
->setCustomerOrder()
234+
->addOrder('product_id');
235+
break;
236+
case self::ALERT_TYPE_PRICE:
237+
$collection = $this->priceCollectionFactory->create();
238+
$collection->addFieldToFilter('customer_id', ['in' => $customerIds])
239+
->addWebsiteFilter($websiteId)
240+
->setCustomerOrder()
241+
->addOrder('product_id');
242+
break;
243+
default:
244+
throw new \InvalidArgumentException('Invalid alert type');
245+
}
246+
247+
return $collection;
248+
}
249+
250+
/**
251+
* Save Price Alert
252+
*
253+
* @param Price $alert
254+
* @param ProductInterface $product
255+
* @param CustomerInterface $customer
256+
* @param Email $email
257+
*/
258+
private function savePriceAlert(
259+
Price $alert,
260+
ProductInterface $product,
261+
CustomerInterface $customer,
262+
Email $email
263+
): void {
264+
$product->setCustomerGroupId($customer->getGroupId());
265+
$finalPrice = $product->getFinalPrice();
266+
if ($alert->getPrice() <= $finalPrice) {
267+
return;
268+
}
269+
270+
$product->setFinalPrice($this->catalogData->getTaxPrice($product, $finalPrice));
271+
$product->setPrice($this->catalogData->getTaxPrice($product, $product->getPrice()));
272+
273+
$alert->setPrice($finalPrice);
274+
$alert->setLastSendDate(date(DateTime::DATETIME_PHP_FORMAT));
275+
$alert->setSendCount($alert->getSendCount() + 1);
276+
$alert->setStatus(1);
277+
$alert->save();
278+
279+
$email->addPriceProduct($product);
280+
}
281+
282+
/**
283+
* Save stock alert
284+
*
285+
* @param Stock $alert
286+
* @param ProductInterface $product
287+
* @param WebsiteInterface $website
288+
* @param Email $email
289+
*/
290+
private function saveStockAlert(
291+
Stock $alert,
292+
ProductInterface $product,
293+
WebsiteInterface $website,
294+
Email $email
295+
): void {
296+
if (!$this->productSalability->isSalable($product, $website)) {
297+
return;
298+
}
299+
300+
$alert->setSendDate(date(DateTime::DATETIME_PHP_FORMAT));
301+
$alert->setSendCount($alert->getSendCount() + 1);
302+
$alert->setStatus(1);
303+
$alert->save();
304+
305+
$email->addStockProduct($product);
306+
}
307+
308+
/**
309+
* Send alert email
310+
*
311+
* @param CustomerInterface $customer
312+
* @param Email $email
313+
*/
314+
private function sendEmail(CustomerInterface $customer, Email $email): void
315+
{
316+
$email->setCustomerData($customer);
317+
$email->send();
318+
$email->clean();
319+
}
320+
}

0 commit comments

Comments
 (0)