Skip to content

Commit 8a80ff4

Browse files
committed
Merge remote-tracking branch 'origin/MC-36405' into 2.4-develop-pr42
2 parents 910c3ff + b09c85a commit 8a80ff4

File tree

6 files changed

+175
-5
lines changed

6 files changed

+175
-5
lines changed

app/code/Magento/Catalog/Model/Product/Option/Type/Date.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ public function validateUserValue($values)
7272
$dateValid = true;
7373
if ($this->_dateExists()) {
7474
if ($this->useCalendar()) {
75+
/* Fixed validation if the date was not saved correctly after re-saved the order
76+
for example: "09\/24\/2020,2020-09-24 00:00:00" */
77+
if (is_string($value) && preg_match('/^\d{1,4}.+\d{1,4}.+\d{1,4},+(\w|\W)*$/', $value)) {
78+
$value = [
79+
'date' => preg_replace('/,([^,]+),?$/', '', $value),
80+
];
81+
}
7582
$dateValid = isset($value['date']) && preg_match('/^\d{1,4}.+\d{1,4}.+\d{1,4}$/', $value['date']);
7683
} else {
7784
$dateValid = isset(
@@ -184,8 +191,10 @@ public function prepareForCart()
184191
$date = (new \DateTime())->setTimestamp($timestamp);
185192
$result = $date->format('Y-m-d H:i:s');
186193

194+
$originDate = (isset($value['date']) && $value['date'] != '') ? $value['date'] : null;
195+
187196
// Save date in internal format to avoid locale date bugs
188-
$this->_setInternalInRequest($result);
197+
$this->_setInternalInRequest($result, $originDate);
189198

190199
return $result;
191200
} else {
@@ -352,9 +361,10 @@ public function getYearEnd()
352361
* Save internal value of option in infoBuy_request
353362
*
354363
* @param string $internalValue Datetime value in internal format
364+
* @param string|null $originDate date value in origin format
355365
* @return void
356366
*/
357-
protected function _setInternalInRequest($internalValue)
367+
protected function _setInternalInRequest($internalValue, $originDate = null)
358368
{
359369
$requestOptions = $this->getRequest()->getOptions();
360370
if (!isset($requestOptions[$this->getOption()->getId()])) {
@@ -364,6 +374,9 @@ protected function _setInternalInRequest($internalValue)
364374
$requestOptions[$this->getOption()->getId()] = [];
365375
}
366376
$requestOptions[$this->getOption()->getId()]['date_internal'] = $internalValue;
377+
if ($originDate) {
378+
$requestOptions[$this->getOption()->getId()]['date'] = $originDate;
379+
}
367380
$this->getRequest()->setOptions($requestOptions);
368381
}
369382

app/code/Magento/Catalog/Model/ProductOptionProcessor.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
*/
66
namespace Magento\Catalog\Model;
77

8-
use Magento\Catalog\Api\Data\ProductOptionExtensionFactory;
98
use Magento\Catalog\Api\Data\ProductOptionInterface;
109
use Magento\Catalog\Model\CustomOptions\CustomOption;
1110
use Magento\Catalog\Model\CustomOptions\CustomOptionFactory;
1211
use Magento\Framework\DataObject;
1312
use Magento\Framework\DataObject\Factory as DataObjectFactory;
1413

14+
/**
15+
* Processor for product options
16+
*/
1517
class ProductOptionProcessor implements ProductOptionProcessorInterface
1618
{
1719
/**
@@ -88,7 +90,8 @@ public function convertToProductOption(DataObject $request)
8890
if (!empty($options) && is_array($options)) {
8991
$data = [];
9092
foreach ($options as $optionId => $optionValue) {
91-
if (is_array($optionValue)) {
93+
94+
if (is_array($optionValue) && !$this->isDateWithDateInternal($optionValue)) {
9295
$optionValue = $this->processFileOptionValue($optionValue);
9396
$optionValue = implode(',', $optionValue);
9497
}
@@ -126,6 +129,8 @@ private function processFileOptionValue(array $optionValue)
126129
}
127130

128131
/**
132+
* Get url builder
133+
*
129134
* @return \Magento\Catalog\Model\Product\Option\UrlBuilder
130135
*
131136
* @deprecated 101.0.0
@@ -138,4 +143,15 @@ private function getUrlBuilder()
138143
}
139144
return $this->urlBuilder;
140145
}
146+
147+
/**
148+
* Check if the option has a date_internal and date
149+
*
150+
* @param array $optionValue
151+
* @return bool
152+
*/
153+
private function isDateWithDateInternal(array $optionValue): bool
154+
{
155+
return array_key_exists('date_internal', $optionValue) && array_key_exists('date', $optionValue);
156+
}
141157
}

app/code/Magento/Sales/Model/AdminOrder/Create.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ protected function _initShippingAddressFromOrder(\Magento\Sales\Model\Order $ord
642642
* @param \Magento\Sales\Model\Order\Item $orderItem
643643
* @param int $qty
644644
* @return \Magento\Quote\Model\Quote\Item|string|$this
645+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
645646
*/
646647
public function initFromOrderItem(\Magento\Sales\Model\Order\Item $orderItem, $qty = null)
647648
{
@@ -666,10 +667,17 @@ public function initFromOrderItem(\Magento\Sales\Model\Order\Item $orderItem, $q
666667
$productOptions = $orderItem->getProductOptions();
667668
if ($productOptions !== null && !empty($productOptions['options'])) {
668669
$formattedOptions = [];
670+
$useFrontendCalendar = $this->useFrontendCalendar();
669671
foreach ($productOptions['options'] as $option) {
672+
if (in_array($option['option_type'], ['date', 'date_time']) && $useFrontendCalendar) {
673+
$product->setSkipCheckRequiredOption(false);
674+
break;
675+
}
670676
$formattedOptions[$option['option_id']] = $option['option_value'];
671677
}
672-
$buyRequest->setData('options', $formattedOptions);
678+
if (!empty($formattedOptions)) {
679+
$buyRequest->setData('options', $formattedOptions);
680+
}
673681
}
674682
$item = $this->getQuote()->addProduct($product, $buyRequest);
675683
if (is_string($item)) {
@@ -2115,4 +2123,17 @@ private function isAddressesAreEqual(Order $order)
21152123

21162124
return $shippingData == $billingData;
21172125
}
2126+
2127+
/**
2128+
* Use Calendar on frontend or not
2129+
*
2130+
* @return bool
2131+
*/
2132+
private function useFrontendCalendar(): bool
2133+
{
2134+
return (bool)$this->_scopeConfig->getValue(
2135+
'catalog/custom_options/use_calendar',
2136+
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
2137+
);
2138+
}
21182139
}

dev/tests/integration/testsuite/Magento/Sales/Controller/Order/ReorderTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,33 @@ public function testReorderByAnotherCustomer(): void
136136
}
137137
}
138138

139+
/**
140+
* Reorder with JS calendar options
141+
*
142+
* @magentoDataFixture Magento/Sales/_files/order_with_js_date_option_product.php
143+
* @magentoConfigFixture current_store catalog/custom_options/use_calendar 1
144+
*
145+
* @return void
146+
*/
147+
public function testReorderWithJSCalendar(): void
148+
{
149+
$order = $this->orderFactory->create()->loadByIncrementId('100000001');
150+
$items = $order->getItems();
151+
$orderItem = array_pop($items);
152+
$orderRequestOptions = $orderItem->getProductOptionByCode('info_buyRequest')['options'];
153+
$order->save();
154+
$this->customerSession->setCustomerId($order->getCustomerId());
155+
$this->dispatchReorderRequest((int)$order->getId());
156+
$this->assertRedirect($this->stringContains('checkout/cart'));
157+
$this->quote = $this->checkoutSession->getQuote();
158+
$quoteItemsCollection = $this->quote->getItemsCollection();
159+
$this->assertCount(1, $quoteItemsCollection);
160+
$items = $quoteItemsCollection->getItems();
161+
$quoteItem = array_pop($items);
162+
$quoteRequestOptions = $quoteItem->getBuyRequest()->getOptions();
163+
$this->assertEquals($orderRequestOptions, $quoteRequestOptions);
164+
}
165+
139166
/**
140167
* Dispatch reorder request.
141168
*
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
9+
10+
Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer.php');
11+
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple.php');
12+
13+
$objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
14+
15+
$addressData = include __DIR__ . '/../../../Magento/Sales/_files/address_data.php';
16+
17+
$billingAddress = $objectManager->create(\Magento\Sales\Model\Order\Address::class, ['data' => $addressData]);
18+
$billingAddress->setAddressType('billing');
19+
20+
$shippingAddress = clone $billingAddress;
21+
$shippingAddress->setId(null)->setAddressType('shipping');
22+
23+
$payment = $objectManager->create(\Magento\Sales\Model\Order\Payment::class);
24+
$payment->setMethod('checkmo');
25+
26+
/** @var $product \Magento\Catalog\Model\Product */
27+
$product = $objectManager->create(\Magento\Catalog\Model\Product::class);
28+
$repository = $objectManager->create(\Magento\Catalog\Model\ProductRepository::class);
29+
$product = $repository->get('simple');
30+
31+
$optionValuesByType = [
32+
'field' => 'Test value',
33+
'date_time' => [
34+
'date' => '09/30/2022',
35+
'hour' => '2',
36+
'minute' => '15',
37+
'day_part' => 'am',
38+
'date_internal' => '2020-09-30 02:15:00'
39+
],
40+
'drop_down' => '3-1-select',
41+
'radio' => '4-1-radio',
42+
];
43+
44+
$requestInfo = ['options' => []];
45+
$productOptions = $product->getOptions();
46+
foreach ($productOptions as $option) {
47+
$requestInfo['options'][$option->getOptionId()] = $optionValuesByType[$option->getType()];
48+
}
49+
50+
/** @var \Magento\Sales\Model\Order\Item $orderItem */
51+
$orderItem = $objectManager->create(\Magento\Sales\Model\Order\Item::class);
52+
$orderItem->setProductId($product->getId());
53+
$orderItem->setSku($product->getSku());
54+
$orderItem->setQtyOrdered(1);
55+
$orderItem->setBasePrice($product->getPrice());
56+
$orderItem->setPrice($product->getPrice());
57+
$orderItem->setRowTotal($product->getPrice());
58+
$orderItem->setProductType($product->getTypeId());
59+
$orderItem->setProductOptions(['info_buyRequest' => $requestInfo]);
60+
61+
/** @var \Magento\Sales\Model\Order $order */
62+
$order = $objectManager->create(\Magento\Sales\Model\Order::class);
63+
$order->setIncrementId('100000001');
64+
$order->setState(\Magento\Sales\Model\Order::STATE_NEW);
65+
$order->setStatus($order->getConfig()->getStateDefaultStatus(\Magento\Sales\Model\Order::STATE_NEW));
66+
$order->setCustomerIsGuest(true);
67+
$order->setCustomerEmail('customer@null.com');
68+
$order->setCustomerFirstname('firstname');
69+
$order->setCustomerLastname('lastname');
70+
$order->setBillingAddress($billingAddress);
71+
$order->setShippingAddress($shippingAddress);
72+
$order->setAddresses([$billingAddress, $shippingAddress]);
73+
$order->setPayment($payment);
74+
$order->addItem($orderItem);
75+
$order->setStoreId($objectManager->get(\Magento\Store\Model\StoreManagerInterface::class)->getStore()->getId());
76+
$order->setSubtotal(100);
77+
$order->setBaseSubtotal(100);
78+
$order->setBaseGrandTotal(100);
79+
$order->setCustomerId(1)
80+
->setCustomerIsGuest(false)
81+
->save();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
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+
use Magento\TestFramework\Workaround\Override\Fixture\Resolver;
9+
10+
Resolver::getInstance()->requireDataFixture('Magento/Customer/_files/customer_rollback.php');
11+
Resolver::getInstance()->requireDataFixture('Magento/Catalog/_files/product_simple_rollback.php');
12+
Resolver::getInstance()->requireDataFixture('Magento/Sales/_files/default_rollback.php');

0 commit comments

Comments
 (0)