diff --git a/vendor/magento/module-rma/Model/ItemCountValidator.php b/vendor/magento/module-rma/Model/ItemCountValidator.php
new file mode 100644
index 000000000000..150367206c7f
--- /dev/null
+++ b/vendor/magento/module-rma/Model/ItemCountValidator.php
@@ -0,0 +1,265 @@
+dataHelper = $dataHelper;
+ $this->itemFactory = $itemFactory;
+ $this->escaper = $escaper;
+ }
+
+ /**
+ * Validate Return items quantity
+ *
+ * @param Rma $value
+ * @return bool
+ * @throws LocalizedException
+ */
+ public function isValid($value)
+ {
+ if ($value->getStatus() == Status::STATE_CLOSED) {
+ return true;
+ }
+
+ if ($value->getIsUpdate() || $value->getEntityId()) {
+ $items = $value->getItems();
+ foreach ($items as $item) {
+ $this->checkQuantities($item);
+ $this->checkQuantityStatuses($item);
+ }
+ } else {
+ if (false === in_array($value->getStatus(), Status::STATE_ALL)) {
+ $this->_addMessages([__('Invalid status provided: %1', $value->getStatus())]);
+ return false;
+ }
+ }
+
+ $this->checkAvailability($value);
+
+ if (!empty($this->getMessages())) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Checks Rma items against initial order items
+ *
+ * @param Rma $value
+ * @return void
+ * @throws LocalizedException
+ */
+ private function checkAvailability(Rma $value): void
+ {
+ $errors = $errorKeys = [];
+ $availableItemsArray = $this->getAvailableItems($value);
+ $itemsArray = $this->getItemsTotals($value);
+ foreach ($itemsArray as $key => $info) {
+ if (!array_key_exists($key, $availableItemsArray)) {
+ $errors['return_item_not_allowed'] = __('You cannot return %1.', $key);
+ continue;
+ }
+ if ($availableItemsArray[$key]['qty'] < $info['quantity']) {
+ $escapedProductName = $this->escaper->escapeHtml($availableItemsArray[$key]['name']);
+ $errors['return_item_quantity_not_allowed'] =
+ __('A quantity of %1 is greater than you can return.', $escapedProductName);
+ $errorKeys[$key] = 'qty_requested';
+ $errorKeys['tabs'] = 'items_section';
+ }
+ }
+
+ if ($errors || $errorKeys) {
+ $this->_addMessages(array_merge($errors, ['error_keys' => $errorKeys]));
+ }
+ }
+
+ /**
+ * Extract total quantities
+ *
+ * @param Rma $value
+ * @return array
+ */
+ private function getItemsTotals(Rma $value): array
+ {
+ $itemsArray = [];
+ $items = $value->getItems();
+ foreach ($items as $item) {
+ if (!isset($itemsArray[$item->getOrderItemId()])) {
+ $itemsArray[$item->getOrderItemId()]['quantity'] = $item->getQtyRequested();
+ } else {
+ $itemsArray[$item->getOrderItemId()]['quantity'] += $item->getQtyRequested();
+ }
+ $itemsArray[$item->getOrderItemId()]['status'] = $item->getStatus();
+ }
+ ksort($itemsArray);
+
+ return $itemsArray;
+ }
+
+ /**
+ * Validate item quantity status
+ *
+ * @param ItemInterface $item
+ * @return void
+ */
+ private function checkQuantityStatuses(ItemInterface $item): void
+ {
+ $errors = $errorKeys = [];
+ $escapedProductName = $this->escaper->escapeHtml($item->getProductName());
+
+ //if we change item status i.e. to authorized, then qty_authorized must be non-empty and so on.
+ foreach ($this->getQtyToStatus() as $qtyKey => $qtyValue) {
+ if ($item->getStatus() === $qtyValue['status']
+ && $item->getOrigData('status') !== $qtyValue['status']
+ && !$item->getData($qtyKey)
+ ) {
+ $errors[] = __('%1 for item %2 cannot be empty.', $qtyValue['name'], $escapedProductName);
+ $errorKeys[$item->getId()] = $qtyKey;
+ $errorKeys['tabs'] = 'items_section';
+ }
+ }
+
+ if ($errors || $errorKeys) {
+ $this->_addMessages(array_merge($errors, ['error_keys' => $errorKeys]));
+ }
+ }
+
+ /**
+ * Check if there are enough items in the order to perform a return
+ *
+ * @param ItemInterface $item
+ * @return void
+ */
+ private function checkQuantities(ItemInterface $item): void
+ {
+ $validation = $errors = $errorKeys = [];
+ foreach ([Rma::QTY_REQUESTED, Rma::QTY_AUTHORIZED, Rma::QTY_RETURNED, Rma::QTY_APPROVED] as $tempQty) {
+ $quantity = $item->getData($tempQty);
+ if ($quantity === null) {
+ if ($item->getOrigData($tempQty) !== null) {
+ $validation[$tempQty] = (double)$item->getOrigData($tempQty);
+ }
+ } else {
+ $validation[$tempQty] = (double)$quantity;
+ }
+ }
+ $validation['dummy'] = -1;
+ $previousValue = null;
+ $escapedProductName = $this->escaper->escapeHtml($item->getProductName());
+ foreach ($validation as $key => $val) {
+ if (isset($previousValue) && $val > $previousValue) {
+ $errors[] = __('There is an error in quantities for item %1.', $escapedProductName);
+ $errorKeys[$item->getId()] = $key;
+ $errorKeys['tabs'] = 'items_section';
+ break;
+ }
+ $previousValue = $val;
+ }
+
+ if ($errors || $errorKeys) {
+ $this->_addMessages(array_merge($errors, ['error_keys' => $errorKeys]));
+ }
+ }
+
+ /**
+ * Extract all order items
+ *
+ * @param Rma $value
+ * @return array
+ * @throws LocalizedException
+ */
+ private function getAvailableItems(Rma $value): array
+ {
+ $order = $value->getOrder();
+ if (!$value->getEntityId()) {
+ $availableItems = $this->dataHelper->getOrderItems($order->getId())->getItems();
+ } else {
+ $itemResource = $this->itemFactory->create();
+ $availableItems = $itemResource->getOrderItemsCollection($order->getId());
+ }
+
+ $availableItemsArray = [];
+ foreach ($availableItems as $item) {
+ $availableItemsArray[$item->getId()] = [
+ 'name' => $item->getName(),
+ 'qty' => $item->getAvailableQty()
+ ];
+ }
+
+ return $availableItemsArray;
+ }
+
+ /**
+ * Get relevant Rma statuses
+ *
+ * @return array[]
+ */
+ private function getQtyToStatus(): array
+ {
+ return [
+ 'qty_authorized' => [
+ 'name' => __('Authorized Qty'),
+ 'status' => Status::STATE_AUTHORIZED,
+ ],
+ 'qty_returned' => [
+ 'name' => __('Returned Qty'),
+ 'status' => Status::STATE_RECEIVED,
+ ],
+ 'qty_approved' => [
+ 'name' => __('Approved Qty'),
+ 'status' => Status::STATE_APPROVED,
+ ],
+ ];
+ }
+}
diff --git a/vendor/magento/module-rma/Model/Rma.php b/vendor/magento/module-rma/Model/Rma.php
index ed1b9bdcfc9b..7fbc6891f1b5 100644
--- a/vendor/magento/module-rma/Model/Rma.php
+++ b/vendor/magento/module-rma/Model/Rma.php
@@ -7,6 +7,7 @@
namespace Magento\Rma\Model;
+use Magento\Framework\Validator\ValidatorInterface;
use Magento\Framework\Api\AttributeValueFactory;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Exception\LocalizedException;
@@ -20,7 +21,6 @@
use Magento\Store\Model\Store;
/**
- * RMA model
* @SuppressWarnings(PHPMD.TooManyFields)
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -56,6 +56,14 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
public const TRACKS = 'tracks';
+ public const QTY_AUTHORIZED = 'qty_authorized';
+
+ public const QTY_RETURNED = 'qty_returned';
+
+ public const QTY_APPROVED = 'qty_approved';
+
+ public const QTY_REQUESTED = 'qty_requested';
+
/**#@-*/
/**
@@ -114,13 +122,6 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
*/
protected $_storeManager;
- /**
- * Eav configuration model
- *
- * @var \Magento\Eav\Model\Config
- */
- protected $_eavConfig;
-
/**
* Rma item factory model
*
@@ -135,13 +136,6 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
*/
protected $_attrSourceFactory;
- /**
- * Rma grid factory model
- *
- * @var \Magento\Rma\Model\GridFactory
- */
- protected $_rmaGridFactory;
-
/**
* Rma source status factory
*
@@ -219,11 +213,6 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
*/
protected $_shippingFactory;
- /**
- * @var \Magento\Framework\Escaper
- */
- protected $_escaper;
-
/**
* @var \Magento\Framework\Message\ManagerInterface
*/
@@ -262,6 +251,16 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
*/
protected $_eventObject = 'rma';
+ /**
+ * @var ValidatorInterface
+ */
+ private ValidatorInterface $validator;
+
+ /**
+ * @var array
+ */
+ private array $errorKeys = [];
+
/**
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Registry $registry
@@ -270,10 +269,8 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
* @param \Magento\Rma\Helper\Data $rmaData
* @param \Magento\Framework\Session\Generic $session
* @param \Magento\Store\Model\StoreManagerInterface $storeManager
- * @param \Magento\Eav\Model\Config $eavConfig
* @param ItemFactory $rmaItemFactory
* @param Item\Attribute\Source\StatusFactory $attrSourceFactory
- * @param GridFactory $rmaGridFactory
* @param Rma\Source\StatusFactory $statusFactory
* @param \Magento\Rma\Model\ResourceModel\ItemFactory $itemFactory
* @param \Magento\Rma\Model\ResourceModel\Item\CollectionFactory $itemsFactory
@@ -285,7 +282,6 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
* @param \Magento\Sales\Model\ResourceModel\Order\Item\CollectionFactory $ordersFactory
* @param \Magento\Quote\Model\Quote\Address\RateRequestFactory $rateRequestFactory
* @param \Magento\Shipping\Model\ShippingFactory $shippingFactory
- * @param \Magento\Framework\Escaper $escaper
* @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
* @param \Magento\Framework\Message\ManagerInterface $messageManager
* @param RmaAttributesManagementInterface $metadataService
@@ -294,6 +290,7 @@ class Rma extends \Magento\Sales\Model\AbstractModel implements \Magento\Rma\Api
* @param array $data
* @param Json|null $serializer
* @param EntityAttributesLoader|null $attributesLoader
+ * @param ValidatorInterface|null $validator
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
@@ -304,10 +301,8 @@ public function __construct(
\Magento\Rma\Helper\Data $rmaData,
\Magento\Framework\Session\Generic $session,
\Magento\Store\Model\StoreManagerInterface $storeManager,
- \Magento\Eav\Model\Config $eavConfig,
\Magento\Rma\Model\ItemFactory $rmaItemFactory,
\Magento\Rma\Model\Item\Attribute\Source\StatusFactory $attrSourceFactory,
- \Magento\Rma\Model\GridFactory $rmaGridFactory,
\Magento\Rma\Model\Rma\Source\StatusFactory $statusFactory,
\Magento\Rma\Model\ResourceModel\ItemFactory $itemFactory,
\Magento\Rma\Model\ResourceModel\Item\CollectionFactory $itemsFactory,
@@ -319,7 +314,6 @@ public function __construct(
\Magento\Sales\Model\ResourceModel\Order\Item\CollectionFactory $ordersFactory,
\Magento\Quote\Model\Quote\Address\RateRequestFactory $rateRequestFactory,
\Magento\Shipping\Model\ShippingFactory $shippingFactory,
- \Magento\Framework\Escaper $escaper,
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
\Magento\Framework\Message\ManagerInterface $messageManager,
RmaAttributesManagementInterface $metadataService,
@@ -327,16 +321,15 @@ public function __construct(
\Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
array $data = [],
Json $serializer = null,
- EntityAttributesLoader $attributesLoader = null
+ EntityAttributesLoader $attributesLoader = null,
+ ?ValidatorInterface $validator = null
) {
$objectManager = ObjectManager::getInstance();
$this->_rmaData = $rmaData;
$this->_session = $session;
$this->_storeManager = $storeManager;
- $this->_eavConfig = $eavConfig;
$this->_rmaItemFactory = $rmaItemFactory;
$this->_attrSourceFactory = $attrSourceFactory;
- $this->_rmaGridFactory = $rmaGridFactory;
$this->_statusFactory = $statusFactory;
$this->_itemFactory = $itemFactory;
$this->_itemsFactory = $itemsFactory;
@@ -348,12 +341,12 @@ public function __construct(
$this->_ordersFactory = $ordersFactory;
$this->_rateRequestFactory = $rateRequestFactory;
$this->_shippingFactory = $shippingFactory;
- $this->_escaper = $escaper;
$this->_localeDate = $localeDate;
$this->messageManager = $messageManager;
$this->metadataService = $metadataService;
$this->serializer = $serializer ?: $objectManager->get(Json::class);
$this->attributesLoader = $attributesLoader ?: $objectManager->get(EntityAttributesLoader::class);
+ $this->validator = $validator ?: $objectManager->get(ValidatorInterface::class);
parent::__construct(
$context,
$registry,
@@ -722,11 +715,11 @@ public function close()
*
* @param array $data
* @return bool|$this
+ * @throws \Exception
*/
public function saveRma($data)
{
// TODO: move errors adding to controller
- $errors = 0;
$this->messageManager->getMessages(true);
if ($this->getCustomerCustomEmail()) {
$validateEmail = $this->_validateEmail($this->getCustomerCustomEmail());
@@ -735,17 +728,16 @@ public function saveRma($data)
$this->messageManager->addError($error);
}
$this->_session->setRmaFormData($data);
- $errors = 1;
}
}
- $itemModels = $this->_createItemsCollection($data);
- if (!$itemModels || $errors) {
+ try {
+ $this->_createItemsCollection($data);
+ } catch (\Throwable $e) {
return false;
}
- $this->save();
- return $this;
+ return $this->save();
}
/**
@@ -753,332 +745,80 @@ public function saveRma($data)
*
* @param array $item
* @return array
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
*/
protected function _preparePost($item)
{
- $errors = false;
$preparePost = [];
- $qtyKeys = ['qty_authorized', 'qty_returned', 'qty_approved'];
-
ksort($item);
- foreach ($item as $key => $value) {
- if ($key == 'order_item_id') {
- $preparePost['order_item_id'] = (int)$value;
- } elseif ($key == 'qty_requested') {
- $preparePost['qty_requested'] = is_numeric($value) ? $value : 0;
- } elseif (in_array($key, $qtyKeys)) {
- if (is_numeric($value)) {
- $preparePost[$key] = (double)$value;
- } else {
- $preparePost[$key] = '';
- }
- } elseif ($key == 'resolution') {
- $preparePost['resolution'] = (int)$value;
- } elseif ($key == 'condition') {
- $preparePost['condition'] = (int)$value;
- } elseif ($key == 'reason') {
- $preparePost['reason'] = (int)$value;
- } elseif ($key == 'reason_other' && !empty($value)) {
- $preparePost['reason_other'] = $value;
- } else {
- $preparePost[$key] = $value;
- }
- }
-
- $order = $this->getOrder();
- $realItem = $order->getItemById($preparePost['order_item_id']);
-
- $stat = Status::STATE_PENDING;
- if (!empty($preparePost['status'])) {
- /** @var $status Status */
- $status = $this->_attrSourceFactory->create();
- if ($status->checkStatus($preparePost['status'])) {
- $stat = $preparePost['status'];
- }
- }
-
- $preparePost['status'] = $stat;
-
- $preparePost['product_name'] = $realItem->getName();
- $preparePost['product_sku'] = $realItem->getSku();
- $preparePost['product_admin_name'] = $this->_rmaData->getAdminProductName($realItem);
- $preparePost['product_admin_sku'] = $this->_rmaData->getAdminProductSku($realItem);
- $preparePost['product_options'] = $this->serializer->serialize($realItem->getProductOptions());
- $preparePost['is_qty_decimal'] = $realItem->getIsQtyDecimal();
-
- if ($preparePost['is_qty_decimal']) {
- $preparePost['qty_requested'] = (double)$preparePost['qty_requested'];
- } else {
- $preparePost['qty_requested'] = (int)$preparePost['qty_requested'];
-
- foreach ($qtyKeys as $key) {
- if (!empty($preparePost[$key])) {
- $preparePost[$key] = (int)$preparePost[$key];
- }
- }
- }
-
- if (isset($preparePost['qty_requested']) && $preparePost['qty_requested'] <= 0) {
- $errors = true;
- }
-
- foreach ($qtyKeys as $key) {
- if (isset($preparePost[$key]) && !is_string($preparePost[$key]) && $preparePost[$key] <= 0) {
- $errors = true;
- }
- }
-
- if ($errors) {
- $this->messageManager->addError(
- __('There is an error in quantities for item %1.', $preparePost['product_name'])
- );
- }
- return $preparePost;
- }
-
- /**
- * Checks Items Quantity in Return
- *
- * @param Item $itemModels
- * @param int $orderId
- * @return array|bool
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
- */
- protected function _checkPost($itemModels, $orderId)
- {
- $errors = [];
- $errorKeys = [];
- if (!$this->getIsUpdate()) {
- $availableItems = $this->_rmaData->getOrderItems($orderId);
- } else {
- /** @var $itemResource \Magento\Rma\Model\ResourceModel\Item */
- $itemResource = $this->_itemFactory->create();
- $availableItems = $itemResource->getOrderItemsCollection($orderId);
- }
-
- $itemsArray = [];
- foreach ($itemModels as $item) {
- if (!isset($itemsArray[$item->getOrderItemId()])) {
- $itemsArray[$item->getOrderItemId()] = $item->getQtyRequested();
- } else {
- $itemsArray[$item->getOrderItemId()] += $item->getQtyRequested();
- }
-
- if ($this->getIsUpdate()) {
- $validation = [];
- foreach (['qty_requested', 'qty_authorized', 'qty_returned', 'qty_approved'] as $tempQty) {
- if ($item->getData($tempQty) === null) {
- if ($item->getOrigData($tempQty) !== null) {
- $validation[$tempQty] = (double)$item->getOrigData($tempQty);
- }
- } else {
- $validation[$tempQty] = (double)$item->getData($tempQty);
- }
- }
- $validation['dummy'] = -1;
- $previousValue = null;
- $escapedProductName = $this->_escaper->escapeHtml($item->getProductName());
- foreach ($validation as $key => $value) {
- if (isset($previousValue) && $value > $previousValue) {
- $errors[] = __('There is an error in quantities for item %1.', $escapedProductName);
- $errorKeys[$item->getId()] = $key;
- $errorKeys['tabs'] = 'items_section';
- break;
- }
- $previousValue = $value;
- }
-
- //if we change item status i.e. to authorized, then qty_authorized must be non-empty and so on.
- $qtyToStatus = [
- 'qty_authorized' => [
- 'name' => __('Authorized Qty'),
- 'status' => \Magento\Rma\Model\Rma\Source\Status::STATE_AUTHORIZED,
- ],
- 'qty_returned' => [
- 'name' => __('Returned Qty'),
- 'status' => \Magento\Rma\Model\Rma\Source\Status::STATE_RECEIVED,
- ],
- 'qty_approved' => [
- 'name' => __('Approved Qty'),
- 'status' => \Magento\Rma\Model\Rma\Source\Status::STATE_APPROVED,
- ],
- ];
- foreach ($qtyToStatus as $qtyKey => $qtyValue) {
- if ($item->getStatus() === $qtyValue['status']
- && $item->getOrigData(
- 'status'
- ) !== $qtyValue['status']
- && !$item->getData(
- $qtyKey
- )
- ) {
- $errors[] = __('%1 for item %2 cannot be empty.', $qtyValue['name'], $escapedProductName);
- $errorKeys[$item->getId()] = $qtyKey;
- $errorKeys['tabs'] = 'items_section';
- }
- }
+ foreach ($item as $key => $value) {
+ switch ($key) {
+ case 'order_item_id':
+ $preparePost['order_item_id'] = (int)$value;
+ break;
+ case 'qty_requested':
+ $preparePost['qty_requested'] = is_numeric($value) ? $value : 0;
+ break;
+ case 'resolution':
+ $preparePost['resolution'] = (int)$value;
+ break;
+ case 'condition':
+ $preparePost['condition'] = (int)$value;
+ break;
+ case 'reason':
+ $preparePost['reason'] = (int)$value;
+ break;
+ case 'reason_other':
+ $preparePost['reason_other'] = $value;
+ break;
+ default:
+ $preparePost[$key] = $value;
}
}
- ksort($itemsArray);
-
- $availableItemsArray = [];
- foreach ($availableItems as $item) {
- $availableItemsArray[$item->getId()] = [
- 'name' => $item->getName(),
- 'qty' => $item->getAvailableQty(),
- ];
- }
- foreach ($itemsArray as $key => $qty) {
- $escapedProductName = $this->_escaper->escapeHtml($availableItemsArray[$key]['name']);
- if (!array_key_exists($key, $availableItemsArray)) {
- $errors[] = __('You cannot return %1.', $escapedProductName);
- }
- if (isset($availableItemsArray[$key]) && $availableItemsArray[$key]['qty'] < $qty) {
- $errors[] = __('A quantity of %1 is greater than you can return.', $escapedProductName);
- $errorKeys[$key] = 'qty_requested';
- $errorKeys['tabs'] = 'items_section';
- }
- }
+ $preparePost = $this->preparePostItemStatus($preparePost);
+ $preparePost = $this->preparePostItemProductDetails($preparePost);
- if (count($errors)) {
- return [$errors, $errorKeys];
- }
- return true;
+ return $this->preparePostItemQuantities($preparePost);
}
/**
* Creates rma items collection by passed data
*
- * @param array $data
+ * @param mixed $data
* @return Item[]
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
+ * @throws \Exception
*/
- protected function _createItemsCollection($data)
+ protected function _createItemsCollection($data): array
{
if (!is_array($data)) {
$data = (array)$data;
}
- $order = $this->getOrder();
- $itemModels = [];
- $errors = [];
- $errorKeys = [];
+ $itemModels = [];
foreach ($data['items'] as $key => $item) {
- if (isset($item['items'])) {
- $itemModel = $firstModel = false;
- $files = $f = [];
- foreach ($item['items'] as $id => $qty) {
- if ($itemModel) {
- $firstModel = $itemModel;
- }
- /** @var $itemModel Item */
- $itemModel = $this->_rmaItemFactory->create();
- $subItem = $item;
- unset($subItem['items']);
- $subItem['order_item_id'] = $id;
- $subItem['qty_requested'] = $qty;
-
- $itemPost = $this->_preparePost($subItem);
-
- $f = $itemModel->setData($itemPost)->prepareAttributes($itemPost, $key);
-
- /* Copy image(s) to another bundle items */
- if (!empty($f)) {
- $files = $f;
- }
- if (!empty($files) && $firstModel) {
- foreach ($files as $code) {
- $itemModel->setData($code, $firstModel->getData($code));
- }
- }
- // @codingStandardsIgnoreStart
- $errors = array_merge($itemModel->getErrors(), $errors);
- // @codingStandardsIgnoreEnd
-
- $itemModels[] = $itemModel;
- }
- } else {
- /** @var $itemModel Item */
- $itemModel = $this->_rmaItemFactory->create();
- if (isset($item['entity_id']) && $item['entity_id']) {
- $itemModel->load($item['entity_id']);
- if ($itemModel->getEntityId()) {
- if (empty($item['reason'])) {
- $item['reason'] = $itemModel->getReason();
- }
-
- if (empty($item['reason_other'])) {
- $item['reason_other'] =
- $itemModel->getReasonOther() === null ? '' : $itemModel->getReasonOther();
- }
-
- if (empty($item['condition'])) {
- $item['condition'] = $itemModel->getCondition();
- }
-
- if (empty($item['qty_requested'])) {
- $item['qty_requested'] = $itemModel->getQtyRequested();
- }
- }
- }
-
- $itemPost = $this->_preparePost($item);
-
- $itemModel->setData($itemPost)->prepareAttributes($itemPost, $key);
- //check if entity_id present and its a split return. Reset the entity
- if ($itemModel->getEntityId() &&
- !is_numeric($itemModel->getEntityId())) {
- $itemModel->setEntityId(null);
- }
- // @codingStandardsIgnoreStart
- $errors = array_merge($itemModel->getErrors(), $errors);
- // @codingStandardsIgnoreEnd
- if ($errors) {
- $errorKeys['tabs'] = 'items_section';
- }
-
- $itemModels[] = $itemModel;
-
- if ($this->isStatusNeedsAuthEmail($itemModel->getStatus())
- && $itemModel->getOrigData(
- 'status'
- ) !== $itemModel->getStatus()
- ) {
- $this->setIsSendAuthEmail(1);
- }
- }
+ $itemModels = $this->mergeModels($itemModels, $this->createItemModel($item, (string)$key));
}
-
- $result = $this->_checkPost($itemModels, $order->getId());
+ $this->setItems($itemModels);
+ $result = $this->validator->isValid($this);
if ($result !== true) {
- list($result, $errorKey) = $result;
- $errors = array_merge($result, $errors);
- $errorKeys = array_merge($errorKey, $errorKeys);
+ $errors = $this->validator->getMessages();
+ $this->errorKeys = array_merge($this->errorKeys, $errors['error_keys'] ?: []);
+ unset($errors['error_keys']);
+ $this->addErrors($errors);
}
- $eMessages = $this->messageManager->getMessages()->getErrors();
- if (!empty($errors) || !empty($eMessages)) {
+ if (!empty($this->messageManager->getMessages()->getErrors())) {
$this->_session->setRmaFormData($data);
- if (!empty($errorKeys)) {
- $this->_session->setRmaErrorKeys($errorKeys);
- }
- if (!empty($errors)) {
- foreach ($errors as $message) {
- $this->messageManager->addError($message);
- }
+ if (!empty($this->errorKeys)) {
+ $this->_session->setRmaErrorKeys($this->errorKeys);
}
- return false;
}
- $this->setItems($itemModels);
+
+ if (empty($itemModels) || !empty($this->messageManager->getMessages()->getErrors())) {
+ throw new \LogicException('Faulty RMA items');
+ }
return $this->getItems();
}
@@ -1527,4 +1267,221 @@ public function validateOrderItems(): void
}
}
}
+
+ /**
+ * @inheritdoc
+ */
+ protected function _getValidationRulesBeforeSave()
+ {
+ return $this->validator;
+ }
+
+ /**
+ * Adds items to existing list
+ *
+ * @param array $itemModels
+ * @param array $newModels
+ * @return array
+ */
+ private function mergeModels(array $itemModels, array $newModels): array
+ {
+ return array_merge($itemModels, $newModels);
+ }
+
+ /**
+ * Set item quantities to proper type
+ *
+ * @param array $itemDetails
+ * @return array
+ */
+ private function preparePostItemQuantities(array $itemDetails): array
+ {
+ $quantityKeys = [self::QTY_AUTHORIZED, self::QTY_APPROVED, self::QTY_RETURNED];
+
+ foreach ($itemDetails as $key => $val) {
+ if (in_array($key, $quantityKeys)) {
+ if (is_numeric($val)) {
+ $preparePost[$key] = (double)$val;
+ } else {
+ $preparePost[$key] = '';
+ }
+ }
+ }
+
+ if ($itemDetails['is_qty_decimal']) {
+ $itemDetails['qty_requested'] = (double)$itemDetails['qty_requested'];
+ } else {
+ $itemDetails['qty_requested'] = (int)$itemDetails['qty_requested'];
+
+ foreach ($quantityKeys as $key) {
+ if (!empty($preparePost[$key])) {
+ $preparePost[$key] = (int)$preparePost[$key];
+ }
+ }
+ }
+
+ return $itemDetails;
+ }
+
+ /**
+ * Adds real product information to item details
+ *
+ * @param array $itemDetails
+ * @return array
+ */
+ private function preparePostItemProductDetails(array $itemDetails): array
+ {
+ $realItem = $this->getOrder()->getItemById($itemDetails['order_item_id']);
+
+ $itemDetails['product_name'] = $realItem->getName();
+ $itemDetails['product_sku'] = $realItem->getSku();
+ $itemDetails['product_admin_name'] = $this->_rmaData->getAdminProductName($realItem);
+ $itemDetails['product_admin_sku'] = $this->_rmaData->getAdminProductSku($realItem);
+ $itemDetails['product_options'] = $this->serializer->serialize($realItem->getProductOptions());
+ $itemDetails['is_qty_decimal'] = $realItem->getIsQtyDecimal();
+
+ return $itemDetails;
+ }
+
+ /**
+ * Add messages to existing message hub
+ *
+ * @param array $errors
+ */
+ private function addErrors(array $errors): void
+ {
+ foreach ($errors as $message) {
+ $this->messageManager->addError($message);
+ }
+ }
+
+ /**
+ * Normalize item post status
+ *
+ * @param array $itemDetails
+ * @return array
+ */
+ private function preparePostItemStatus(array $itemDetails): array
+ {
+ $stat = Status::STATE_PENDING;
+ if (!empty($itemDetails['status'])) {
+ $status = $this->_attrSourceFactory->create();
+ if ($status->checkStatus($itemDetails['status'])) {
+ $stat = $itemDetails['status'];
+ }
+ }
+
+ $itemDetails['status'] = $stat;
+
+ return $itemDetails;
+ }
+
+ /**
+ * Generate model with provided item details
+ *
+ * @param array $item
+ * @param string $key
+ * @return array
+ */
+ private function createItemModel(array $item, string $key): array
+ {
+ if (isset($item['items'])) {
+ $itemModel = $this->createSubItems($item['items'], $key);
+ } else {
+ $itemModel = $this->hydrateItemEntity($item, $key);
+ //check if entity_id present, and it's a split return. Reset the entity
+ if ($itemModel->getEntityId() &&
+ !is_numeric($itemModel->getEntityId())) {
+ $itemModel->setEntityId(null);
+ }
+
+ if ($this->isStatusNeedsAuthEmail($itemModel->getStatus())
+ && $itemModel->getOrigData('status') !== $itemModel->getStatus()
+ ) {
+ $this->setIsSendAuthEmail(1);
+ }
+
+ $this->addErrors($itemModel->getErrors());
+ if ($itemModel->getErrors()) {
+ $this->errorKeys['tabs'] = 'items_section';
+ }
+ $itemModel = [$itemModel];
+ }
+
+ return $itemModel;
+ }
+
+ /**
+ * Adds data to existing entity, if available
+ *
+ * @param array $itemDetails
+ * @param string $key
+ * @return Item
+ */
+ private function hydrateItemEntity(array $itemDetails, string $key): Item
+ {
+ $itemModel = $this->_rmaItemFactory->create();
+ if (isset($itemDetails['entity_id']) && $itemDetails['entity_id']) {
+ $itemModel->load($itemDetails['entity_id']);
+ if ($itemModel->getEntityId()) {
+ if (empty($itemDetails['reason'])) {
+ $itemDetails['reason'] = $itemModel->getReason();
+ }
+
+ if (empty($itemDetails['reason_other'])) {
+ $itemDetails['reason_other'] =
+ $itemModel->getReasonOther() === null ? '' : $itemModel->getReasonOther();
+ }
+
+ if (empty($itemDetails['condition'])) {
+ $itemDetails['condition'] = $itemModel->getCondition();
+ }
+
+ if (empty($itemDetails['qty_requested'])) {
+ $itemDetails['qty_requested'] = $itemModel->getQtyRequested();
+ }
+ }
+ }
+
+ $itemPost = $this->_preparePost($itemDetails);
+ $itemModel->setData($itemPost)->prepareAttributes($itemPost, $key);
+
+ return $itemModel;
+ }
+
+ /**
+ * Creates sub-items for returned product
+ *
+ * @param array $item
+ * @param string $key
+ * @return array
+ */
+ private function createSubItems(array $item, string $key): ?array
+ {
+ if (empty($item['items'])) {
+ return [];
+ }
+
+ $items = [];
+ foreach ($item['items'] as $id => $qty) {
+ $itemModel = $this->_rmaItemFactory->create();
+ $subItem = $item;
+ unset($subItem['items']);
+ $subItem['order_item_id'] = $id;
+ $subItem['qty_requested'] = $qty;
+
+ $itemPost = $this->_preparePost($subItem);
+
+ $files = $itemModel->setData($itemPost)->prepareAttributes($itemPost, $key);
+ if (!empty($files)) {
+ foreach ($files as $code) {
+ $itemModel->setData($code, $itemModel->getData($code));
+ }
+ }
+ $this->addErrors($itemModel->getErrors());
+ $items[] = $itemModel;
+ }
+
+ return $items;
+ }
}
diff --git a/vendor/magento/module-rma/Model/Rma/Source/Status.php b/vendor/magento/module-rma/Model/Rma/Source/Status.php
index bce8181801b1..ea1f9356f0bf 100644
--- a/vendor/magento/module-rma/Model/Rma/Source/Status.php
+++ b/vendor/magento/module-rma/Model/Rma/Source/Status.php
@@ -13,29 +13,36 @@ class Status extends \Magento\Rma\Model\Rma\Source\AbstractSource
/**
* Status constants
*/
- const STATE_PENDING = 'pending';
+ public const STATE_PENDING = 'pending';
- const STATE_AUTHORIZED = 'authorized';
+ public const STATE_AUTHORIZED = 'authorized';
- const STATE_PARTIAL_AUTHORIZED = 'partially_authorized';
+ public const STATE_PARTIAL_AUTHORIZED = 'partially_authorized';
- const STATE_RECEIVED = 'received';
+ public const STATE_RECEIVED = 'received';
- const STATE_RECEIVED_ON_ITEM = 'received_on_item';
+ public const STATE_RECEIVED_ON_ITEM = 'received_on_item';
- const STATE_APPROVED = 'approved';
+ public const STATE_APPROVED = 'approved';
- const STATE_APPROVED_ON_ITEM = 'approved_on_item';
+ public const STATE_APPROVED_ON_ITEM = 'approved_on_item';
- const STATE_REJECTED = 'rejected';
+ public const STATE_REJECTED = 'rejected';
- const STATE_REJECTED_ON_ITEM = 'rejected_on_item';
+ public const STATE_REJECTED_ON_ITEM = 'rejected_on_item';
- const STATE_DENIED = 'denied';
+ public const STATE_DENIED = 'denied';
- const STATE_CLOSED = 'closed';
+ public const STATE_CLOSED = 'closed';
- const STATE_PROCESSED_CLOSED = 'processed_closed';
+ public const STATE_PROCESSED_CLOSED = 'processed_closed';
+
+ public const STATE_ALL = [
+ self::STATE_PENDING, self::STATE_AUTHORIZED, self::STATE_PARTIAL_AUTHORIZED, self::STATE_RECEIVED,
+ self::STATE_RECEIVED_ON_ITEM, self::STATE_APPROVED, self::STATE_APPROVED_ON_ITEM,
+ self::STATE_REJECTED, self::STATE_REJECTED_ON_ITEM, self::STATE_DENIED, self::STATE_CLOSED,
+ self::STATE_PROCESSED_CLOSED
+ ];
/**
* Rma item attribute status factory
diff --git a/vendor/magento/module-rma/etc/di.xml b/vendor/magento/module-rma/etc/di.xml
index 2493a21b4bac..d22cd85aba70 100644
--- a/vendor/magento/module-rma/etc/di.xml
+++ b/vendor/magento/module-rma/etc/di.xml
@@ -36,7 +36,7 @@
- Magento\Framework\Translate
+ Magento\Rma\Model\ItemCountValidator
diff --git a/vendor/magento/module-rma/i18n/en_US.csv b/vendor/magento/module-rma/i18n/en_US.csv
index d8856f42cc64..c22a1e7b7f50 100644
--- a/vendor/magento/module-rma/i18n/en_US.csv
+++ b/vendor/magento/module-rma/i18n/en_US.csv
@@ -353,3 +353,4 @@ Comma-separated,Comma-separated
"RMA Comment Email Recipient","RMA Comment Email Recipient"
"Enabled for RMA","Enabled for RMA"
"The label cannot be created for '%1' because the product does not exist in the system.", "The label cannot be created for '%1' because the product does not exist in the system."
+"Invalid status provided: %s","Invalid status provided: %s"