Как реализовать резервирование товаров

Материал из Umicms
Перейти к:навигация, поиск

Актуально для версии 2.9.6

Задача

Допустим у Вас есть ряд товаров, количество которых ограничено и Вам необходимо создать для них резервирование, то есть, нужно реализовать примерно следующие возможности:

  • Включить резервирование товара;
  • Задать количество зарезервированного товара;
  • Указать дату, до которой этот товар должен быть оплачен;
  • Сделать, чтобы было невозможно совершить заказов, в котором количество товаров будет превышать резерв;
  • Все действия с корзиной перед покупкой (изменения количества, удаление и добавление позиций) должны сразу отражаться на количество резерва;
  • Если заказ с зарезервированным товаром не был переведен в статус "Готов" до даты, заданной в 3 пункте, то товарная позиция должна быть удалена из заказа;
  • Если заказ был отмене или отклонен, то у зарезервированного товара должно быть восполнено его количество.

Все эти задачи можно решить только кастомно, ниже даются примеры.

Решение

Управление резервирование у товара

Сначала создадим поля, в которых будут храниться настройки резервирования для товаров. Для этого, создадим в типе данных "объект каталога" следующие поля:

Reservation fields.png

Теперь у товаров появились настройки:

Reservation page.png

Резервирование при работе с корзиной

К сожалению, стандартный метод emarket basket() совершенно не подойдет для решения функциональности, поэтому придется создаваться кастомный, пример ниже:

/*
		*  Замена стандартного метода emarket basket()
		*
		*  Отличия от ориганала:
		*  1) в зависимости от $mode дополнительно вызываются:
		*     checkAmountAndPut()  для 'put'
		*     checkAmountAndRemove()  для 'remove'
		*     checkAmountAndRemoveAll()  для 'remove_all'
		*  2) нет тройного вызова метода refresh();
		*/
		public function smartBasket($mode = false, $itemType = false, $itemId = false){			
			$mode = $mode ? $mode : getRequest('param0');
			$order = emarket::getBasketOrder(!in_array($mode, array('put', 'remove')));
			$itemType = $itemType ? $itemType : getRequest('param1');
			$itemId = (int) ($itemId ? $itemId : getRequest('param2'));
			$amount = (int) getRequest('amount');
			$options = getRequest('options');

			if($mode == 'put') {
				$newElement = false;
				if ($itemType == 'element') {
					$orderItem = emarket::getBasketItem($itemId, false);
					if (!$orderItem) {
						$orderItem = emarket::getBasketItem($itemId);
						$newElement = true;
					}
				} else {
					$orderItem = order::getItem($itemId);
				}

				if (!$orderItem) {
					throw new publicException("Order item is not defined");
				}

				if(is_array($options)) {
					if($itemType != 'element') {
						throw new publicException("Put basket method required element id of optionedOrderItem");
					}

					// Get all orderItems
					$orderItems = order::getItems();

					foreach($orderItems as $tOrderItem) {
						if (!$tOrderItem instanceOf optionedOrderItem) {
							$itemOptions = null;
							$tOrderItem = null;
							continue;
						}

						$itemOptions = $tOrderItem->getOptions();

						if(sizeof($itemOptions) != sizeof($options)) {
							$itemOptions = null;
							$tOrderItem = null;
							continue;
						}

						if($tOrderItem->getItemElement()->id != $orderItem->getItemElement()->id) {
							$itemOptions = null;
							$tOrderItem = null;
							continue;
						}

						// Compare each tOrderItem with options list
						foreach($options as $optionName => $optionId) {
							$itemOption = getArrayKey($itemOptions, $optionName);

							if(getArrayKey($itemOption, 'option-id') != $optionId) {
								$tOrderItem = null;
								continue 2;		// If does not match, create new item using options specified
							}
						}

						break;	// If matches, stop loop and continue to amount change
					}

					if(!isset($tOrderItem) || is_null($tOrderItem)) {
						$tOrderItem = orderItem::create($itemId);
						$order->appendItem($tOrderItem);
						if ($newElement) {
							$orderItem->remove();
						}
					}

					if($tOrderItem instanceof optionedOrderItem) {
						foreach($options as $optionName => $optionId) {
							if($optionId) {
								$tOrderItem->appendOption($optionName, $optionId);
							} else {
								$tOrderItem->removeOption($optionName);
							}
						}
					}

					if($tOrderItem) {
						$orderItem = $tOrderItem;
					}
				}
				self::checkAmountAndPut($itemId, $amount, $orderItem);				

				if($itemType == 'element') {
					$order->appendItem($orderItem);
				}
				$order->refresh();
			}

			if($mode == 'remove') {
				
				$orderItem = ($itemType == 'element') ? emarket::getBasketItem($itemId, false) : orderItem::get($itemId);
				self::checkAmountAndRemove($itemId, $orderItem, $order, $itemType);
			}

			if ($mode == 'remove_all') {
				self::checkAmountAndRemoveAll($order);
			}

			$referer = getServer('HTTP_REFERER');
			$noRedirect = getRequest('no-redirect');

			if($redirectUri = getRequest('redirect-uri')) {
				$this->redirect($redirectUri);
			} else if (!defined('VIA_HTTP_SCHEME') && !$noRedirect && $referer) {
				$current = $_SERVER['REQUEST_URI'];
				if(substr($referer, -strlen($current)) == $current) {
					if($itemType == 'element') {
						$referer = umiHierarchy::getInstance()->getPathById($itemId);
					} else {
						$referer = "/";
					}
				}
				$this->redirect($referer);
			}
			return $this->order($order->getId());
		}

Код метода нужно поместить в файл /classes/modules/emarket/__custom.php и не забудьте про пермишены.