Списание со складов — различия между версиями

Материал из Umicms
Перейти к:навигация, поиск
 
(не показано 13 промежуточных версий 6 участников)
Строка 1: Строка 1:
−
В системе UMI.CMS склады введены в основном для наглядности и для удобства менеджерам, обрабатывающим заказ. В этой статье мы добавим данным параметрам функциональности и организуем списание со складов.
+
'''Актуально для версии 18 ревизия 85081'''<br><br>
 +
В системе UMI.CMS склады введены в основном для наглядности и для удобства менеджерам, обрабатывающим заказ. В этой статье мы добавим данным параметрам функциональности и организуем списание со склада.<br><br>
  
−
За основу возмём оригинальный метод order_edit, который выполняется при редактировании заказа. Поместим его в файл __custom_adm.php модуля emarket и переименуем, например в order_edit_custom.
+
'''Задача:''' Реализовать функционал автоматического списания или возврата товара со склада.<br><br>
−
Добавляем свой код, который будет производить взаимодействие со складами, листинг метода будет выглядеть примерно следующим образом:
+
'''Внимание:''' Данная реализация списывает количество товаров со склада только в условии, если "Статус заказа" изменяется в самом "Свойстве заказа"<br><br>
 +
'''Условия работы макроса:'''
 +
<ol>
 +
<li>В модуле "Шаблоны данных" в типе данных "Заказ" должно присутствовать невидимое поле типа "Кнопка-флажок" с идентификатором ''current_mode''</li>
 +
<li>В модуле "Шаблоны данных" в типе данных "Объекты каталога" должно присутствовать поле типа "Составное" с идентификатором ''stores_state'' ("Состояние на складах")</li>
 +
</ol>
 +
<br>
 +
'''Реализация:''' Напишем обработчик события для точки вызова systemModifyObject, которое вызывается при сохранении объекта в административной панели. Вся логика списания или возврата будет находиться в обработчике события. Списание происходит, если статус заказа был изменен на "Готов" (статус 'ready'); возврат товара со склада, если статус заказа был изменен на "Отменен" (статус 'canceled').<br>
 +
Код состоит из четырех методов, описание которых представлено ниже:
 +
<ul>
 +
<li>public function '''storesReact ($event)''': обработчик события для точки вызова systemModifyObject <ul><li>$event - параметр, экземпляр iUmiEventPoint </li></ul></li>
 +
<li>protected function '''changeAmount($mode = 'w', $itemId, $storeId, $amount = 1):''' Метод списания или возврата на склад
 +
<ul><li>$mode - режим, списание - 'w', возврат 'b'</li>
 +
<li>$itemId - id объекта каталога</li>
 +
<li>$storeId - id элемента склада</li>
 +
<li>$amount - количество товара для списания или возврата</li>
 +
</ul></li>
 +
</li>
 +
<li>protected function '''changeAllAmounts($mode='w', $data):''' метод списания или возврата всех товаров со складов
 +
<ul><li>$mode - режим, списание - 'w', возврат 'b'</li>
 +
<li>$data - массив, ключами которого являются id объекта каталога, а значениями количество товара для списания или возврата</li>
 +
 
 +
</ul>
 +
</li>
 +
<li>protected function '''getStoreId ($stores):''' метод определения id элемента склада, то есть с какого склада списывать
 +
<ul><li>$stores - массив, предоставляющий результат работы метода getValue('stores_state') </li></ul></li>
 +
</ul>
 +
<br><br>
 +
Добавим в директорию classes\modules\emarket файл с названием custom_events.php или если у Вас новая реализация модулей то в директорию classes\components\emarket файл с названием custom_events.php и содержанием:
 
<source lang="php">
 
<source lang="php">
−
public function order_edit_custom() {
+
<?php
−
$object = $this->expectObject("param0", true);
+
    new umiEventListener("systemModifyObject", "emarket", "storesReact");
−
$mode = (string) getRequest('param1');
+
</source>
−
$objectId = $object->getId();
 
−
$this->setHeaderLabel("header-users-edit-" . $this->getObjectTypeMethod($object));
 
−
$this->checkSv($objectId);
 
−
$inputData = Array( "object" => $object,
 
−
"allowed-element-types" => Array('emarket', 'order')
 
−
);
 
−
if($mode == "do") {
 
−
$oldDeliveryPrice = $object->getValue('delivery_price');
 
  
−
//----------Наш код--------------------------------------------------------------------------
+
В файл classes\modules\emarket\__custom_adm.php или classes\components\emarket\customAdmin.php в зависимости от реализации модулей, добавим код обработчика события:
−
$old_state=$object->getValue('status_id');
+
<source lang="php">
−
$new_state=getRequest('data');
+
public function storesReact($event){
−
$new_state=$new_state[$objectId]['status_id'];
+
$mode = $event->getMode();
−
if(($new_state!=$old_state)&&(($new_state==91)||($new_state==85))){
+
−
$items=$object->getValue('order_items');
+
if ($mode == 'after'){
−
$items_count=count($items);
+
$subject = $event->getRef('object');
−
$objectsCollection = umiObjectsCollection::getInstance();
+
$typesCollection = umiObjectTypesCollection::getInstance();
−
$hierarchy = umiHierarchy::getInstance();
+
$umiHierarchy = umiHierarchy::getInstance();
−
$variablja=Array();
+
$subjectTypeId = $subject->getTypeId();
−
$var_arr=Array();
+
$subjectType = $typesCollection->getType($subjectTypeId);
−
for($i=0;$i<$items_count;$i++){
+
$subjectModule = $subjectType->getModule();
−
$item=$objectsCollection ->getObject($items[$i]);
+
$subjectMethod = $subjectType->getMethod();
−
$amo=$item->item_amount;
+
−
$link_from_item_to_catalog=$item->getValue('item_link');
+
if ($subjectModule == 'emarket' && $subjectMethod == 'order'){
−
$link_to_catalog=$link_from_item_to_catalog[0];
+
                                        $subjectOrder = order::get($subject->id);
−
$id_tovara=$link_to_catalog->id;
+
$orderStatusId = $subject->getValue('status_id');
−
$page_in_catalog = $hierarchy->getElement($id_tovara);  
+
$orderStatus = order::getCodeByStatus($orderStatusId);
−
$store=$page_in_catalog->getValue('stores_state');
+
$currentSwitchMode = $subject->getValue('current_mode');
−
$item_options=$item->getValue('options');
+
−
$store_location='';
+
if (is_null($currentSwitchMode))
−
$tempo=count($item_options);
+
$currentSwitchMode = false;
−
for($j=0;$j<$tempo;$j++){
+
−
if($item_options[$j]['varchar']=="stores_state"){
+
$itemsArray = $subjectOrder->getItems();
−
$store_location=$item_options[$j]['rel'];  
+
 +
$data = array();
 +
 +
foreach($itemsArray as $item){
 +
$catalogObject = $item->getItemElement();
 +
$catalogObjectId = $catalogObject->id;
 +
$itemAmount = $item->getAmount();
 +
$data[$catalogObjectId] = $itemAmount;
 
}
 
}
−
}
+
−
for($j=0;$j<$tempo;$j++){
+
if ($orderStatus == 'ready'){
−
if($store_location==$store[$j]['rel']){
+
−
if($new_state==91){
+
if (!$currentSwitchMode){
−
$store[$j]['int']-=$amo;
+
$changed = self::changeAllAmounts('w', $data);
−
settype($store[$j]['int'], "string");
+
−
}else{
+
if ($changed)
−
$store[$j]['int']+=$amo;
+
$subject->setValue('current_mode', 1);
−
}
+
}
 +
}
 +
 +
if ($orderStatus == 'canceled'){
 +
 
 +
if ($currentSwitchMode){
 +
$changed = self::changeAllAmounts('b', $data);
 +
 +
if ($changed)
 +
$subject->setValue('current_mode', 0);
 +
}
 
}
 
}
 +
 
}
 
}
−
$page_in_catalog->setValue('stores_state',$store);
+
−
$page_in_catalog->commit();
 
 
}
 
}
 
}
 
}
−
//==========================================================================================
 
−
$object = $this->saveEditedObjectData($inputData);
 
 
 
−
$newDeliveryPrice = $object->getValue('delivery_price');
+
protected function changeAmount($mode = 'w', $itemId, $storeId, $amount = 1){
−
+
// mode 'w' - write off
−
$order = order::get($object->id);
+
// mode 'b' - get back
−
+
$amount = intval($amount);
−
$amounts = getRequest('order-amount-item');
+
−
$dels = getRequest('order-del-item');
+
$umiHierarchy = umiHierarchy::getInstance();
−
+
$item = $umiHierarchy->getElement($itemId);
−
$isChanged = false;
+
−
if(is_array($amounts)) foreach($amounts as $itemId => $amount) {
+
$data = array();
−
$item = $order->getItem($itemId);
+
−
if($item instanceof orderItem) {
+
if ($item instanceof umiHierarchyElement){
−
if($item->getAmount() != $amount) {
+
$stores = $item->getValue('stores_state');
−
$item->setAmount($amount);
+
−
$item->commit();
+
foreach ($stores as $key => $store){
−
$isChanged = true;
+
 +
if (in_array($storeId, $store)){
 +
$currentIndex = $key;
 +
$currentValue = $store['int'];
 +
}
 +
}
 +
 +
if (!isset($currentValue) || !isset($key))
 +
return false;
 +
 +
$amountNow = $currentValue;
 +
 +
if ($mode == 'w')
 +
$amountNow = $currentValue - $amount;
 +
else
 +
$amountNow = $currentValue + $amount;
 +
 +
$storesNow = array();
 +
$stores[$currentIndex]['int'] = $amountNow;
 +
 +
foreach ($stores as $store){
 +
$storesNow[] = array('int' => $store['int'], 'rel' => $store['rel']);
 +
}
 +
 +
$item->setValue('stores_state', $storesNow);
 +
return true;
 +
}
 +
 +
 
}
 
}
 +
protected function changeAllAmounts($mode='w', $data){
 +
$umiHierarchy = umiHierarchy::getInstance();
 +
 +
foreach ($data as $id => $amount){
 +
$catalogItem = $umiHierarchy->getElement($id);
 +
$stores = $catalogItem->getValue('stores_state');
 +
 +
$storeId = self::getStoreId($stores);
 +
$changed = self::changeAmount($mode, $id, $storeId, $amount);
 +
}
 +
 +
if ($changed)
 +
return true;
 
}
 
}
 +
 +
protected function getStoreId($stores){
 +
if (!is_array($stores))
 +
return false;
 +
 +
// Insert Your store id choosing logic
 +
 +
$currentKey = 0;
 +
foreach ($stores as $key => $store){
 +
if (intval($store['int']) > 0){
 +
$currentKey = $key;
 +
break;
 +
}
 +
}
 +
if (is_numeric($stores[$currentKey]['rel']))
 +
return $stores[$currentKey]['rel'];
 
}
 
}
−
 
−
if(is_array($dels)) foreach($dels as $itemId) {
 
−
$item = orderItem::get($itemId);
 
−
if($item instanceof orderItem) {
 
−
$order->removeItem($item);
 
−
$isChanged = true;
 
−
}
 
−
}
 
−
 
−
if($isChanged) {
 
−
$order->refresh();
 
−
$order->commit();
 
−
}
 
−
 
−
if ($oldDeliveryPrice != $newDeliveryPrice && !$isChanged) {
 
−
$originalPrice = $object->getValue('total_original_price');
 
−
$totalPrice = $originalPrice;
 
−
 
−
$discount = $order->getDiscount();
 
−
if($discount instanceof discount) {
 
−
$totalPrice = $discount->recalcPrice($originalPrice);
 
−
}
 
−
$totalPrice += $newDeliveryPrice;
 
−
$object->setValue('total_price', $totalPrice);
 
−
$object->commit();
 
−
}
 
−
 
−
$this->chooseRedirect();
 
−
}
 
−
 
−
$this->setDataType("form");
 
−
$this->setActionType("modify");
 
−
 
−
$data = $this->prepareData($inputData, "object");
 
−
 
−
$this->setData($data);
 
−
return $this->doData();
 
−
}
 
−
</source>
 
−
 
−
Здесь мы добавили проверку на изменение статуса заказа и в зависимости от этого условия выполянется код. В данном случае, при изменении статуса заказа на "Готов" (id статуса 91) будет происходить списание со склада, а при изменении на статус "Отменён" (id статуса 85) возврат товара на стклад. Можно поставить эти параметры на своё усмотрение.
 
−
Теперь ещё один очень важный момент, этот метод должен каким то образом запускаться, для этого изменим action формы редактирования заказа. Для этого отредактируем файл ~/styles/skins/mac/data/modules/emarket/form.modify.xsl добавив в него шаблон
 
−
<source lang="xml">
 
−
<xsl:template match="data[@type = 'form' and (@action = 'modify' or @action = 'create')]">
 
−
 
−
<form method="post" action="/admin/emarket/order_edit_custom/{object/@id}/do/" enctype="multipart/form-data">
 
−
<input type="hidden" name="referer" value="{/result/@referer-uri}" id="form-referer" />
 
−
<input type="hidden" name="domain" value="{$domain-floated}"/>
 
−
 
−
<xsl:apply-templates mode="form-modify" />
 
−
<xsl:apply-templates select="page" mode="permissions" />
 
−
 
−
<xsl:if test="@action = 'modify' and count(page) = 1">
 
−
<xsl:apply-templates select="document(concat('udata://backup/backup_panel/', page/@id))/udata" />
 
−
</xsl:if>
 
−
</form>
 
−
</xsl:template>
 
 
</source>
 
</source>
  
−
без него форма редактирования выводится по стандартному шаблону.
+
В метод getStoreId() Вы можете добавить свой код для определения id склада. По умолчанию списывание происходит с первого склада, товаров на котором больше нуля.
  
−
Теперь при изменении статуса заказа, если он меняется на готов, со склада списывается количество товаров, указанное в заказе, а при отмене заказа, возвращается.
+
[[category:Написание кастомных макросов]]
−
[[Категория:Модуль Интернет магазин]][[Категория:Написание кастомных макросов]]
 

Текущая версия на 08:27, 22 ноября 2019

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

В системе UMI.CMS склады введены в основном для наглядности и для удобства менеджерам, обрабатывающим заказ. В этой статье мы добавим данным параметрам функциональности и организуем списание со склада.

Задача: Реализовать функционал автоматического списания или возврата товара со склада.

Внимание: Данная реализация списывает количество товаров со склада только в условии, если "Статус заказа" изменяется в самом "Свойстве заказа"

Условия работы макроса:

  1. В модуле "Шаблоны данных" в типе данных "Заказ" должно присутствовать невидимое поле типа "Кнопка-флажок" с идентификатором current_mode
  2. В модуле "Шаблоны данных" в типе данных "Объекты каталога" должно присутствовать поле типа "Составное" с идентификатором stores_state ("Состояние на складах")


Реализация: Напишем обработчик события для точки вызова systemModifyObject, которое вызывается при сохранении объекта в административной панели. Вся логика списания или возврата будет находиться в обработчике события. Списание происходит, если статус заказа был изменен на "Готов" (статус 'ready'); возврат товара со склада, если статус заказа был изменен на "Отменен" (статус 'canceled').
Код состоит из четырех методов, описание которых представлено ниже:

  • public function storesReact ($event): обработчик события для точки вызова systemModifyObject
    • $event - параметр, экземпляр iUmiEventPoint
  • protected function changeAmount($mode = 'w', $itemId, $storeId, $amount = 1): Метод списания или возврата на склад
    • $mode - режим, списание - 'w', возврат 'b'
    • $itemId - id объекта каталога
    • $storeId - id элемента склада
    • $amount - количество товара для списания или возврата
  • protected function changeAllAmounts($mode='w', $data): метод списания или возврата всех товаров со складов
    • $mode - режим, списание - 'w', возврат 'b'
    • $data - массив, ключами которого являются id объекта каталога, а значениями количество товара для списания или возврата
  • protected function getStoreId ($stores): метод определения id элемента склада, то есть с какого склада списывать
    • $stores - массив, предоставляющий результат работы метода getValue('stores_state')



Добавим в директорию classes\modules\emarket файл с названием custom_events.php или если у Вас новая реализация модулей то в директорию classes\components\emarket файл с названием custom_events.php и содержанием:

<?php
    new umiEventListener("systemModifyObject", "emarket", "storesReact");

В файл classes\modules\emarket\__custom_adm.php или classes\components\emarket\customAdmin.php в зависимости от реализации модулей, добавим код обработчика события:

		public function storesReact($event){
			$mode = $event->getMode();
			
			if ($mode == 'after'){
				$subject = $event->getRef('object');
				$typesCollection = umiObjectTypesCollection::getInstance();
				$umiHierarchy = umiHierarchy::getInstance();
				$subjectTypeId = $subject->getTypeId();
				$subjectType = $typesCollection->getType($subjectTypeId);
				$subjectModule = $subjectType->getModule();
				$subjectMethod = $subjectType->getMethod();
				
				if ($subjectModule == 'emarket' && $subjectMethod == 'order'){
                                        $subjectOrder = order::get($subject->id);
					$orderStatusId = $subject->getValue('status_id');
					$orderStatus = order::getCodeByStatus($orderStatusId);
					$currentSwitchMode = $subject->getValue('current_mode');
					
					if (is_null($currentSwitchMode))
						$currentSwitchMode = false;
					
					$itemsArray = $subjectOrder->getItems();
					
					$data = array();
					
					foreach($itemsArray as $item){
						$catalogObject = $item->getItemElement();
						$catalogObjectId = $catalogObject->id;
						$itemAmount = $item->getAmount();
						$data[$catalogObjectId] = $itemAmount;
					}
					
					if ($orderStatus == 'ready'){
					
						if (!$currentSwitchMode){
							$changed = self::changeAllAmounts('w', $data);
							
							if ($changed)
								$subject->setValue('current_mode', 1);
						}
					}
					
					if ($orderStatus == 'canceled'){

						if ($currentSwitchMode){
							$changed = self::changeAllAmounts('b', $data);
							
							if ($changed)
								$subject->setValue('current_mode', 0);
						}
					}
					
				}
				
			}
		}
		
		protected function changeAmount($mode = 'w', $itemId, $storeId, $amount = 1){
			// mode 'w' - write off
			// mode 'b' - get back
			$amount = intval($amount);
			
			$umiHierarchy = umiHierarchy::getInstance();
			$item = $umiHierarchy->getElement($itemId);
			
			$data = array();
			
			if ($item instanceof umiHierarchyElement){
				$stores = $item->getValue('stores_state');
				
				foreach ($stores as $key => $store){
					
					if (in_array($storeId, $store)){
						$currentIndex = $key;
						$currentValue = $store['int'];
					}
				}
				
				if (!isset($currentValue) || !isset($key))
					return false;
				
				$amountNow = $currentValue;
				
				if ($mode == 'w')
					$amountNow = $currentValue - $amount;
				else 
					$amountNow = $currentValue + $amount;
				
				$storesNow = array();
				$stores[$currentIndex]['int'] = $amountNow;
				
				foreach ($stores as $store){
					$storesNow[] = array('int' => $store['int'], 'rel' => $store['rel']);
				}
				
				$item->setValue('stores_state', $storesNow);
				return true;
			}
				
			
		}
		protected function changeAllAmounts($mode='w', $data){
			$umiHierarchy = umiHierarchy::getInstance();
			
			foreach ($data as $id => $amount){
				$catalogItem = $umiHierarchy->getElement($id);
				$stores = $catalogItem->getValue('stores_state');

				$storeId = self::getStoreId($stores);
				$changed = self::changeAmount($mode, $id, $storeId, $amount);
			}
			
			if ($changed)
				return true;
		}

		protected function getStoreId($stores){
			if (!is_array($stores))
				return false;
			
			// Insert Your store id choosing logic
			
			$currentKey = 0;
			foreach ($stores as $key => $store){
				if (intval($store['int']) > 0){
					$currentKey = $key;
					break;
				}
			}
			if (is_numeric($stores[$currentKey]['rel']))
				return $stores[$currentKey]['rel'];
		}

В метод getStoreId() Вы можете добавить свой код для определения id склада. По умолчанию списывание происходит с первого склада, товаров на котором больше нуля.