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

Материал из Umicms
Перейти к:навигация, поиск
 
(не показано 12 промежуточных версий 5 участников)
Строка 1: Строка 1:
<b style="color: red">не актуально для версии 2.9</b>
+
'''Актуально для версии 18 ревизия 85081'''<br><br>
 +
В системе UMI.CMS склады введены в основном для наглядности и для удобства менеджерам, обрабатывающим заказ. В этой статье мы добавим данным параметрам функциональности и организуем списание со склада.<br><br>
  
В системе UMI.CMS склады введены в основном для наглядности и для удобства менеджерам, обрабатывающим заказ. В этой статье мы добавим данным параметрам функциональности и организуем списание со складов.
+
'''Задача:''' Реализовать функционал автоматического списания или возврата товара со склада.<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>
  
За основу возмём оригинальный метод order_edit, который выполняется при редактировании заказа. Поместим его в файл __custom_adm.php модуля emarket и переименуем, например в order_edit_custom.
+
</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;
 +
}
 +
 +
if ($orderStatus == 'ready'){
 +
 +
if (!$currentSwitchMode){
 +
$changed = self::changeAllAmounts('w', $data);
 +
 +
if ($changed)
 +
$subject->setValue('current_mode', 1);
 +
}
 
}
 
}
}
+
for($j=0;$j<$tempo;$j++){
+
if ($orderStatus == 'canceled'){
if($store_location==$store[$j]['rel']){
+
 
if($new_state==91){
+
if ($currentSwitchMode){
$store[$j]['int']-=$amo;
+
$changed = self::changeAllAmounts('b', $data);
settype($store[$j]['int'], "string");
+
}else{
+
if ($changed)
$store[$j]['int']+=$amo;
+
$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 склада. По умолчанию списывание происходит с первого склада, товаров на котором больше нуля.