<?php
/**
* @version	$Id: orders_tag_processor.php 13578 2010-05-19 07:25:51Z alex $
* @package	In-Commerce
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license	Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	class OrdersTagProcessor extends kDBTagProcessor
	{

		/**
		 * Print location using only filled in fields
		 *
		 * @param Array $params
		 * @access public
		 */
		function PrintLocation($params)
		{
			$object =& $this->getObject($params);

			$type = getArrayValue($params,'type');
			if($type == 'Company')
			{
				return $this->PrintCompanyLocation($params);
			}
			$fields = Array('City','State','Zip','Country');

			$ret = '';
			foreach($fields as $field)
			{
				$value = $object->GetField($type.$field);
				if ($field == 'Country' && $value) $ret .= '<br/>';
				if($value) $ret .= $value.', ';
			}
			return rtrim($ret,', ');
		}

		function PrintCompanyLocation($params)
		{
			$ret = '';
			$fields = Array ('City','State','ZIP','Country');

			foreach ($fields as $field) {
				$value = $this->Application->ConfigValue('Comm_'.$field);
				if ($field == 'Country') {
					$current_language = $this->Application->GetVar('m_lang');
					$primary_language = $this->Application->GetDefaultLanguageId();

					$sql = 'SELECT IF(l' . $current_language . '_Name = "", l' . $primary_language . '_Name, l' . $current_language . '_Name)
							FROM ' . TABLE_PREFIX . 'CountryStates
							WHERE IsoCode = ' . $this->Conn->qstr($value);
					$value = $this->Conn->GetOne($sql);
				}

				if ($field == 'Country' && $value) {
					$ret .= '<br/>';
				}

				if ($value) {
					$ret .= $value.', ';
				}
			}

			return rtrim($ret,', ');
		}

		function Orditems_LinkRemoveFromCart($params)
		{
			return $this->Application->HREF($this->Application->GetVar('t'), '', Array('pass' => 'm,orditems,ord', 'ord_event' => 'OnRemoveFromCart', 'm_cat_id'=>0));
		}

		function Orderitems_ProductLink($params)
		{
			$object =& $this->Application->recallObject('orditems');

			$url_params = Array (
				'p_id' =>  $object->GetDBField('ProductId'),
				'pass' => 'm,p',
			);

			return $this->Application->HREF($params['template'], '', $url_params);
		}

		function Orderitems_ProductExists($params)
		{
			$object =& $this->Application->recallObject('orditems');
			return $object->GetDBField('ProductId') > 0;
		}

		function PrintCart($params)
		{
			$o = '';

			$params['render_as'] = $params['item_render_as'];
			$tag_params = array_merge($params, Array ('per_page' => -1));

			$o_items = $this->Application->ProcessParsedTag(rtrim('orditems.'.$this->Special, '.'), 'PrintList', $tag_params);

			if ($o_items) {
				$cart_params = array('name' => $params['header_render_as']);
				$o  = $this->Application->ParseBlock($cart_params);
				$o .= $o_items;
				$cart_params = array('name' => $params['footer_render_as']);
				$o .= $this->Application->ParseBlock($cart_params);
			} else {
				$cart_params = array('name' => $params['empty_cart_render_as']);
				$o = $this->Application->ParseBlock($cart_params);
			}

			return $o;
		}

		function ShopCartForm($params)
		{
			return $this->Application->ProcessParsedTag('m', 'ParseBlock', 	array_merge($params, Array(
					'name' => 'kernel_form', 'PrefixSpecial'=>'ord'
			)) );
		}

		function BackOrderFlag($params)
		{
			$object =& $this->Application->recallObject('orditems');
			return $object->GetDBField('BackOrderFlag');
		}

		function OrderIcon($params)
		{
			$object =& $this->Application->recallObject('orditems');
			if ($object->GetDBField('BackOrderFlag') == 0) {
				return $params['ordericon'];
			} else {
				return $params['backordericon'];
			}
		}

		function Status($params)
		{
			$status_map = Array(
				'incomplete' => ORDER_STATUS_INCOMPLETE,
				'pending' => ORDER_STATUS_PENDING,
				'backorder' => ORDER_STATUS_BACKORDERS,
				'toship' => ORDER_STATUS_TOSHIP,
				'processed' => ORDER_STATUS_PROCESSED,
				'denied' => ORDER_STATUS_DENIED,
				'archived' => ORDER_STATUS_ARCHIVED,
			);

			$object =& $this->getObject($params);
			$status = $object->GetDBField('Status');

			$result = true;
			if (isset($params['is'])) {
				$result = $result && ($status == $status_map[$params['is']]);
			}
			if (isset($params['is_not'])) {
				$result = $result && ($status != $status_map[$params['is_not']]);
			}
			return $result;
		}

		function ItemsInCart($params)
		{
			$object =& $this->getObject($params);
			if ($object->GetDBField('Status') != ORDER_STATUS_INCOMPLETE) {
				return 0;
			}

			$object =& $this->Application->recallObject('orditems', 'orditems_List');
			/* @var $object kDBList */

			return $object->RecordsCount;
		}

		function CartNotEmpty($params)
		{
			$object =& $this->getObject($params);

			if ($object->GetDBField('Status') != ORDER_STATUS_INCOMPLETE) {
				return 0;
			}

			$order_id = $this->Application->RecallVar('ord_id');
			if ($order_id) {
				$sql = 'SELECT COUNT(*)
						FROM ' . TABLE_PREFIX . 'OrderItems
						WHERE OrderId = ' . $order_id;
				return $this->Conn->GetOne($sql);
			}

			return 0;
		}

		function CartIsEmpty($params)
		{
			return $this->CartNotEmpty($params) ? false : true;
		}

		function CartHasBackorders($params)
		{
			$object =& $this->getObject($params);

			$sql = 'SELECT COUNT(*)
					FROM ' . TABLE_PREFIX . 'OrderItems
					WHERE OrderId = ' . $object->GetID() . '
					GROUP BY BackOrderFlag';
			$different_types = $this->Conn->GetCol($sql);

			return count($different_types) > 1;
		}

		function PrintShippings($params)
		{
			$o = '';
			$object =& $this->getObject($params);
			$ord_id = $object->GetId();

			$shipping_option = $object->GetDBField('ShippingOption');
			$backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag';

			$order_items =& $this->Application->recallObject('orditems', 'orditems_List', Array('skip_autoload' => true) );
			$oi_table = $order_items->TableName;

			list($split_shipments, $limit_types) = $this->GetShippingLimitations($ord_id);
			foreach ($split_shipments as $group => $data)
			{
				$query = 'UPDATE '.$oi_table.' SET SplitShippingGroup = '.$group.'
									WHERE ProductId IN ('.implode(',', $data['Products']).')';
				$this->Conn->Query($query);
				$limitations_cache[$group] = $data['Types'];
			}

			$shipping_group_option = $object->GetDBField('ShippingGroupOption');
			$shipping_group_select = $shipping_group_option == 0 ? '0 AS SplitShippingGroup' : 'SplitShippingGroup';
			if (count($split_shipments) > 1) {
				$this->Application->SetVar('shipping_limitations_apply', 1);
				// different shipping limitations apply
				if ($limit_types == 'NONE') {
					// order can't be shipped with single shipping type
					$this->Application->SetVar('shipping_limitations_apply', 2);
					$shipping_group_select = 'SplitShippingGroup';
					$shipping_group_option = 1;
				}
			}
			else {
				$this->Application->SetVar('shipping_limitations_apply', 0);
			}

			$weight_sql = 'IF(oi.Weight IS NULL, 0, oi.Weight * oi.Quantity)';

			$query = 'SELECT
									'.$backorder_select.',
									oi.ProductName,
									oi.ShippingTypeId,
									SUM(oi.Quantity) AS TotalItems,
									SUM('.$weight_sql.') AS TotalWeight,
									SUM(oi.Price * oi.Quantity) AS TotalAmount,'.
									 //	calculate free Totals => SUM(ALL) - SUM(PROMO) 								'
									'SUM(oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Quantity, 0)) AS TotalItemsPromo,
									SUM('.$weight_sql.') - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, '.$weight_sql.', 0)) AS TotalWeightPromo,
									SUM(oi.Price * oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Price * oi.Quantity, 0)) AS TotalAmountPromo,
									'.$shipping_group_select.'
								FROM '.$oi_table.' oi
								LEFT JOIN '.$this->Application->getUnitOption('p', 'TableName').' p
								ON oi.ProductId = p.ProductId
								WHERE oi.OrderId = '.$ord_id.' AND p.Type = 1
								GROUP BY BackOrderFlag, SplitShippingGroup
								ORDER BY BackOrderFlag ASC, SplitShippingGroup ASC';
			$shipments = $this->Conn->Query($query);

			$block_params = Array();
			$block_params['name'] = $this->SelectParam($params, 'render_as,block');
			$block_params['user_country_id'] = $object->GetDBField('ShippingCountry');
			$block_params['user_state_id'] = $object->GetDBField('ShippingState');
			$block_params['user_zip'] = $object->GetDBField('ShippingZip');
			$block_params['user_city'] = $object->GetDBField('ShippingCity');
			$block_params['user_addr1'] = $object->GetDBField('ShippingAddress1');
			$block_params['user_addr2'] = $object->GetDBField('ShippingAddress2');
			$block_params['user_name'] = $object->GetDBField('ShippingTo');

			if(	($block_params['user_addr1'] == '' || $block_params['user_city'] == '' ||
				$block_params['user_zip'] == '' || $block_params['user_country_id'] == '') &&
				getArrayValue($params, 'invalid_address_render_as'))
			{
				$block_params['name'] = $params['invalid_address_render_as'];
				return $this->Application->ParseBlock($block_params);
			}

			$group = 1;
			foreach ($shipments as $shipment) {
				$where = array('OrderId = '.$ord_id);
				if ($shipping_group_option != 0) {
					$where[] = 'SplitShippingGroup = '.$shipment['SplitShippingGroup'];
				}
				if ($shipping_option > 0) { // not all together
					$where[] = 'BackOrderFlag = '.$shipment['BackOrderFlag'];
				}

				$query = 'UPDATE '.$oi_table.' SET PackageNum = '.$group.'
									'.($where ? 'WHERE '.implode(' AND ', $where) : '');
				$this->Conn->Query($query);
				$group++;
			}

			$this->Application->RemoveVar('LastShippings');
			$group = 1;
			$this->Application->SetVar('ShipmentsExists', 1);
			foreach ($shipments as $shipment) {


				$block_params['package_num'] =  $group;

				$block_params['limit_types'] = strpos($shipping_group_select, '0 AS') !== false ? $limit_types : $limitations_cache[$shipment['SplitShippingGroup']];

				$this->Application->SetVar('ItemShipmentsExists', 1);

				switch ($shipment['BackOrderFlag']) {
					case 0:
						if ( $this->CartHasBackOrders(Array()) && $shipping_option == 0 ) {
							$block_params['shipment'] = $this->Application->Phrase('lu_all_available_backordered');
						}
						else {
							$block_params['shipment'] = $this->Application->Phrase('lu_ship_all_available');;
						}
						break;
					case 1:
						$block_params['shipment'] = $this->Application->Phrase('lu_ship_all_backordered');;
						break;
					default:
						$block_params['shipment'] = $this->Application->Phrase('lu_ship_backordered');
						break;
				}

				$block_params['promo_weight_metric'] = $shipment['TotalWeightPromo'];
				$block_params['promo_amount'] = $shipment['TotalAmountPromo'];
				$block_params['promo_items'] = $shipment['TotalItemsPromo'];

				$block_params['weight_metric'] = $shipment['TotalWeight'];
				$block_params['weight'] = $shipment['TotalWeight'];

				$regional =& $this->Application->recallObject('lang.current');
				if ($block_params['weight_metric'] == '')
				{
					$block_params['weight'] = $this->Application->Phrase('lu_NotAvailable');
				}
				elseif ($regional->GetDBField('UnitSystem') == 1)
				{
					$block_params['weight'] .= ' '.$this->Application->Phrase('lu_kg');
				}
				elseif ($regional->GetDBField('UnitSystem') == 2)
				{
					list($pounds, $ounces) = Kg2Pounds($block_params['weight']);
					$block_params['weight'] = 	$pounds.' '.$this->Application->Phrase('lu_pounds').' '.
												$ounces.' '.$this->Application->Phrase('lu_ounces');
				}

				$block_params['items'] = $shipment['TotalItems'];

				$iso = $this->GetISO($params['currency']);
				$amount = $this->ConvertCurrency($shipment['TotalAmount'], $iso);
				$amount = sprintf("%.2f", $amount);

//				$block_params['amount'] = $this->AddCurrencySymbol($amount, $iso);
				$block_params['amount'] = $shipment['TotalAmount'];
				$block_params['field_name'] = $this->InputName(Array('field' => 'ShippingTypeId')).'['.($group).']';

				$parsed_block = $this->Application->ParseBlock($block_params);

				if($this->Application->GetVar('ItemShipmentsExists'))
				{
					$o .= $parsed_block;
				}
				else
				{
					$this->Application->SetVar('ShipmentsExists', 0);
					if(getArrayValue($params, 'no_shipments_render_as'))
					{
						$block_params['name'] = $params['no_shipments_render_as'];
						return $this->Application->ParseBlock($block_params);
					}
				}
				$group++;
			}

			if(getArrayValue($params, 'table_header_render_as'))
			{
				$o = $this->Application->ParseBlock( Array('name' => $params['table_header_render_as']) ).$o;
			}
			if(getArrayValue($params, 'table_footer_render_as'))
			{
				$o .= $this->Application->ParseBlock( Array('name' => $params['table_footer_render_as']) );
			}

			return $o;
		}

		function GetShippingLimitations($ord_id)
		{
			/*$query = 'SELECT
										c.CachedShippingLimitation
									FROM '.TABLE_PREFIX.'OrderItems AS oi
									LEFT JOIN '.TABLE_PREFIX.'Products AS p
									ON p.ProductId = oi.ProductId
									LEFT JOIN '.TABLE_PREFIX.'CategoryItems AS ci
									ON ci.ItemResourceId = p.ResourceId
									LEFT JOIN '.TABLE_PREFIX.'Category AS c
									ON c.CategoryId = ci.CategoryId
									WHERE
										oi.OrderId = '.$ord_id.'
										AND
										ci.PrimaryCat = 1
										AND
										c.CachedShippingMode = 1;';
			$cat_limitations = $this->Conn->GetCol($query);*/
			$cat_limitations = array();

			$query = 'SELECT ShippingLimitation, ShippingMode, oi.ProductId as ProductId
									FROM '.TABLE_PREFIX.'Products AS p
									LEFT JOIN '.TABLE_PREFIX.'OrderItems AS oi ON
									oi.ProductId = p.ProductId
									WHERE oi.OrderId = '.$ord_id.' AND p.Type = 1'; // .' AND p.ShippingMode = 1';
			$limitations = $this->Conn->Query($query, 'ProductId');

			$split_shipments = array();

			$limit = false;

			$types_index = array();

			// group products by shipping type range and caculate intersection of all types available for ALL products
			// the intersaction caclulation is needed to determine if the order can be shipped with single type or not
			if ($limitations) {
				$limit_types = null;
				foreach ($limitations as $product_id => $row)
				{
					// if shipping types are limited - get the types
					$types = $row['ShippingLimitation'] != '' ? explode('|', substr($row['ShippingLimitation'], 1, -1)) : array('ANY');
					// if shipping is NOT limited to selected types (default - so products with no limitations at all also counts)
					if ($row['ShippingMode'] == 0) {
						array_push($types, 'ANY'); // can be shipped with ANY (literally) type
						$types = array_unique($types);
					}
					//adding product id to split_shipments group by types range
					$i = array_search(serialize($types), $types_index);
					if ($i === false) {
						$types_index[] = serialize($types);
						$i = count($types_index)-1;
					}
					$split_shipments[$i]['Products'][] = $product_id;
					$split_shipments[$i]['Types'] = serialize($types);
					if ($limit_types == null) { //it is null only when we process first item with limitations
						$limit_types = $types; //initial scope
					}

					// this is to avoid ANY intersect CUST_1 = (), but allows ANY intersect CUST_1,ANY = (ANY)
					if (in_array('ANY', $limit_types) && !in_array('ANY', $types)) {
						array_splice($limit_types, array_search('ANY', $limit_types), 1, $types);
					}
					// this is to avoid CUST_1 intersect ANY = (), but allows CUST_1 intersect CUST_1,ANY = (ANY)
					if (!in_array('ANY', $limit_types) && in_array('ANY', $types)) {
						array_splice($types, array_search('ANY', $types), 1, $limit_types);
					}
					$limit_types = array_intersect($limit_types, $types);
				}
				$limit_types = count($limit_types) > 0 ? serialize(array_unique($limit_types)) : 'NONE';
			}
			return array($split_shipments, $limit_types);
		}

		function PaymentTypeForm($params)
		{
			$object =& $this->getObject($params);

			$payment_type_id = $object->GetDBField('PaymentType');
			if($payment_type_id)
			{
				$this->Application->SetVar('pt_id', $payment_type_id);
				$block_params['name'] = $this->SelectParam($params, $this->UsingCreditCard($params) ? 'cc_render_as,block_cc' : 'default_render_as,block_default' );
				return $this->Application->ParseBlock($block_params);
			}
			return '';
		}

		/**
		 * Returns true in case if credit card was used as payment type for order
		 *
		 * @param Array $params
		 * @return bool
		 */
		function UsingCreditCard($params)
		{
			$object =& $this->getObject($params);

			$pt = $object->GetDBField('PaymentType');

			if (!$pt) {
				$pt = $this->Conn->GetOne('SELECT PaymentTypeId FROM '.TABLE_PREFIX.'PaymentTypes WHERE IsPrimary = 1');
				$object->SetDBField('PaymentType', $pt);
			}

			$pt_table = $this->Application->getUnitOption('pt','TableName');
			$sql = 'SELECT GatewayId FROM %s WHERE PaymentTypeId = %s';
			$gw_id = $this->Conn->GetOne( sprintf( $sql, $pt_table, $pt ) );

			$sql = 'SELECT RequireCCFields FROM %s WHERE GatewayId = %s';

			return $this->Conn->GetOne( sprintf($sql, TABLE_PREFIX.'Gateways', $gw_id) );
		}

		function PaymentTypeDescription($params)
		{
			return $this->Application->ProcessParsedTag('pt', 'Field', 	array_merge($params, Array(
					'field' => 'Description'
			)) );
		}

		function PaymentTypeInstructions($params)
		{
			return $this->Application->ProcessParsedTag('pt', 'Field', 	array_merge($params, Array(
					'field' => 'Instructions'
			)) );
		}

		function PrintMonthOptions($params)
		{
			$object =& $this->getObject($params);

			$date = explode('/', $object->GetDBField($params['date_field_name']));
			if (!$date || sizeof($date) != 2) {
				$date=array("", "");
			}
			$o = '';
			$params['name'] = $params['block'];
			for ($i = 1; $i <= 12; $i++) {
				$month_str = str_pad($i, 2, "0", STR_PAD_LEFT);
				if ($date[0] == $month_str) {
					$params['selected'] = ' selected';
				}else {
					$params['selected'] = '';
				}

				$params['mm'] = $month_str;
				$o .= $this->Application->ParseBlock($params);
			}
			return $o;
		}

		function PrintYearOptions($params)
		{
			$object =& $this->getObject($params);
			$value = $object->GetDBField( $params['field'] );

			$block_params = $this->prepareTagParams($params);
			$block_params['name'] = $this->SelectParam($params, 'render_as,block');

			$o = '';
			$this_year = adodb_date('y');

			for($i = $this_year; $i <= $this_year + 10; $i++)
			{
				$year_str = str_pad($i, 2, '0', STR_PAD_LEFT);
				$block_params['selected'] = ($value == $year_str) ? $params['selected'] : '';
				$block_params['key'] = $year_str;
				$block_params['option'] = $year_str;
				$o .= $this->Application->ParseBlock($block_params);
			}
			return $o;
		}

		function PrintMyOrders($params)
		{

		}

		/**
		 * Checks, that order data can be editied based on it's status
		 *
		 * @param Array $params
		 * @return bool
		 */
		function OrderEditable($params)
		{
			$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
			$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');

			if ($this->Application->IsTempMode($this->Prefix, $this->Special)) {
				$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix);
			}

			// use direct select here (not $this->getObject) because this tag is
			// used even before "combined_header" block is used (on "orders_edit_items" template)
			$sql = 'SELECT Status, PaymentType
					FROM ' . $table_name . '
					WHERE ' . $id_field . ' = ' . $this->Application->GetVar( $this->getPrefixSpecial() . '_id' );
			$order_data = $this->Conn->GetRow($sql);

			if (!$order_data) {
				// new order adding, when even not in database
				return true;
			}

			switch ($order_data['Status']) {
				case ORDER_STATUS_INCOMPLETE:
					$ret = true;
					break;

				case ORDER_STATUS_PENDING:
				case ORDER_STATUS_BACKORDERS:
					$sql = 'SELECT PlacedOrdersEdit
							FROM ' . $this->Application->getUnitOption('pt', 'TableName') . '
							WHERE ' . $this->Application->getUnitOption('pt', 'IDField') . ' = ' . $order_data['PaymentType'];
					$ret = $this->Conn->GetOne($sql);
					break;

				default:
					$ret = false;
					break;
			}

			return $ret;
		}

		function CheckoutSteps($params)
		{
			$steps = explode(',', $params['steps']);
			foreach ($steps as $key => $item)
			{
				$templates[$key] = trim($item);
			}

			$templates = explode(',', $params['templates']);
			foreach ($templates as $key => $item)
			{
				$templates[$key] = trim($item);
			}
			$total_steps = count($templates);
			$t = $this->Application->GetVar('t');

			$o = '';
			$block_params = array();
			$i = 0;
			$passed_current = preg_match("/".preg_quote($templates[count($templates)-1], '/')."/", $t);
			foreach ($steps as $step => $name)
			{
				if (preg_match("/".preg_quote($templates[$step], '/')."/", $t)) {
					$block_params['name'] = $this->SelectParam($params, 'current_step_render_as,block_current_step');
					$passed_current = true;
				}
				else {
					$block_params['name'] = $passed_current ? $this->SelectParam($params, 'render_as,block') : $this->SelectParam($params, 'passed_step_render_as,block_passed_step');
				}
				$block_params['title'] = $this->Application->Phrase($name);
				$block_params['template'] = $templates[$i];
				$block_params['template_link'] = $this->Application->HREF($templates[$step], '', Array('pass'=>'m'));
				$block_params['next_step_template'] = isset($templates[$i + 1]) ? $templates[$i + 1] : '';
				$block_params['number'] = $i + 1;
				$i++;
				$o.= $this->Application->ParseBlock($block_params, 1);
			}
			return $o;
		}

		function ShowOrder($params)
		{

			$order_params = $this->prepareTagParams($params);
//			$order_params['Special'] = 'myorders';
//			$order_params['PrefixSpecial'] = 'ord.myorders';
			$order_params['name'] = $this->SelectParam($order_params, 'render_as,block');
//			$this->Application->SetVar('ord.myorders_id', $this->Application->GetVar('ord_id'));

			$object =& $this->getObject($params);
			if (!$object->GetDBField('OrderId')) {
				return;
			}
			return $this->Application->ParseBlock($order_params);
		}

		function BuildListSpecial($params)
		{
			if ($this->Special != '') {
				return $this->Special;
			}

			$list_unique_key = $this->getUniqueListKey($params);
			if ($list_unique_key == '') {
				return parent::BuildListSpecial($params);
			}

			return crc32($list_unique_key);
		}

		function ListOrders($params)
		{
			$o = '';
			$params['render_as'] = $params['item_render_as'];

			$o_orders = $this->PrintList2($params);

			if ($o_orders) {
				$orders_params = array('name' => $params['header_render_as']);
				$o  = $this->Application->ParseBlock($orders_params);
				$o .= $o_orders;

			} else {
				$orders_params = array('name' => $params['empty_myorders_render_as']);
				$o = $this->Application->ParseBlock($orders_params);
			}

			return $o;

		}

		function HasRecentOrders($params)
		{
			$per_page = $this->SelectParam($params, 'per_page,max_items');
			if ($per_page !== false) {
				$params['per_page'] = $per_page;
			}

			return (int)$this->TotalRecords($params) > 0 ? 1 : 0;
		}

		function ListOrderItems($params)
		{
			$prefix_special = rtrim('orditems.'.$this->Special, '.');
			return $this->Application->ProcessParsedTag($prefix_special, 'PrintList', 	array_merge($params, Array(
					'per_page' => -1
			)) );
		}


		function OrdersLink(){
			$params['pass']='m,ord';
			$main_processor =& $this->Application->RecallObject('m_TagProcessor');
			return $main_processor->Link($params);
		}



		function PrintAddresses($params)
		{
			$object =& $this->getObject($params);

			$address_list =& $this->Application->recallObject('addr','addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
			$address_list->Query();

			$address_id = $this->Application->GetVar($params['type'].'_address_id');
			if (!$address_id) {
				$sql = 'SELECT '.$address_list->IDField.'
						FROM '.$address_list->TableName.'
						WHERE PortalUserId = '.$object->GetDBField('PortalUserId').' AND LastUsedAs'.ucfirst($params['type']).' = 1';
				$address_id = (int)$this->Conn->GetOne($sql);
			}

			$ret = '';
			$block_params = $this->prepareTagParams($params);
			$block_params['name'] = $this->SelectParam($params, 'render_as,block');

			$address_list->GoFirst();
			while (!$address_list->EOL()) {

				$selected = ($address_list->GetID() == $address_id);
				if ($selected && $address_list->GetDBField('IsProfileAddress')) {
					$this->Application->SetVar($this->Prefix.'_IsProfileAddress', true);
				}

				$block_params['key'] = $address_list->GetID();
				$block_params['value'] = $address_list->GetDBField('ShortAddress');
				$block_params['selected'] = $selected ? ' selected="selected"' : '';

				$ret .= $this->Application->ParseBlock($block_params, 1);
				$address_list->GoNext();
			}

			return $ret;
		}

		function PrefillRegistrationFields($params)
		{
			if ( $this->Application->GetVar('fields_prefilled') ) {
				return false;
			}

			$user =& $this->Application->recallObject('u', null, Array ('skip_autoload' => true));
			/* @var $user kDBItem */

			$order =& $this->Application->recallObject($this->Prefix . '.last');

			$order_prefix = $params['type'] == 'billing' ? 'Billing' : 'Shipping';
			$order_fields = Array (
				'To', 'Company', 'Phone', 'Fax', 'Email', 'Address1',
				'Address2', 'City', 'State', 'Zip', 'Country'
			);

			$names = explode(' ', $order->GetDBField($order_prefix.'To'), 2);
			if (!$user->GetDBField('FirstName')) $user->SetDBField('FirstName', getArrayValue($names, 0) );
			if (!$user->GetDBField('LastName')) $user->SetDBField('LastName', getArrayValue($names, 1) );
			if (!$user->GetDBField('Company')) $user->SetDBField('Company', $order->GetDBField($order_prefix.'Company') );
			if (!$user->GetDBField('Phone')) $user->SetDBField('Phone', $order->GetDBField($order_prefix.'Phone') );
			if (!$user->GetDBField('Fax')) $user->SetDBField('Fax', $order->GetDBField($order_prefix.'Fax') );
			if (!$user->GetDBField('Email')) $user->SetDBField('Email', $order->GetDBField($order_prefix.'Email') );
			if (!$user->GetDBField('Street')) $user->SetDBField('Street', $order->GetDBField($order_prefix.'Address1') );
			if (!$user->GetDBField('Street2')) $user->SetDBField('Street2', $order->GetDBField($order_prefix.'Address2') );
			if (!$user->GetDBField('City')) $user->SetDBField('City', $order->GetDBField($order_prefix.'City') );
			if (!$user->GetDBField('State')) $user->SetDBField('State', $order->GetDBField($order_prefix.'State') );
			if (!$user->GetDBField('Zip')) $user->SetDBField('Zip', $order->GetDBField($order_prefix.'Zip') );
			if (!$user->GetDBField('Country')) $user->SetDBField('Country', $order->GetDBField($order_prefix.'Country') );

			$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
			/* @var $cs_helper kCountryStatesHelper */

			$cs_helper->PopulateStates(new kEvent('u:OnBuild'), 'State', 'Country');
		}

		function UserLink($params)
		{
			$object =& $this->getObject($params);
			$user_id = $object->GetDBField( $params['user_field'] );

			if ($user_id) {
				$url_params =  Array (
					'm_opener' => 'd',
					'u_mode' => 't',
					'u_event' => 'OnEdit',
					'u_id' => $user_id,
					'pass' => 'all,u',
					'no_pass_through' => 1,
				);

				return $this->Application->HREF($params['edit_template'], '', $url_params);
			}
		}

		function UserFound($params)
		{
			$virtual_users = Array(USER_ROOT, USER_GUEST, 0);
			$object =& $this->getObject($params);
			return !in_array( $object->GetDBField( $params['user_field'] ) , $virtual_users );
		}

		/**
		 * Returns a link for editing order
		 *
		 * @param Array $params
		 * @return string
		 */
		function OrderLink($params)
		{
			$object =& $this->getObject($params);

			$url_params = Array (
				'm_opener' => 'd',
				$this->Prefix.'_mode' => 't',
				$this->Prefix.'_event' => 'OnEdit',
				$this->Prefix.'_id' => $object->GetID(),
				'pass' => 'all,'.$this->Prefix,
				'no_pass_through' => 1,
			);

			return $this->Application->HREF($params['edit_template'], '', $url_params);
		}

		function HasOriginalAmount($params)
		{
			$object =& $this->getObject($params);
			$original_amount = $object->GetDBField('OriginalAmount');
			return $original_amount && ($original_amount != $object->GetDBField('TotalAmount') );
		}

		/**
		 * Returns true, when order has tangible items
		 *
		 * @param Array $params
		 * @return bool
		 *
		 * @todo This is copy from OrdersItem::HasTangibleItems. Copy to helper (and create it) and use here.
		 */
		function OrderHasTangibleItems($params)
		{
			$object =& $this->getObject($params);
			if ($object->GetID() == FAKE_ORDER_ID) {
				return false;
			}

			$sql = 'SELECT COUNT(*)
					FROM '.TABLE_PREFIX.'OrderItems orditems
					LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = orditems.ProductId
					WHERE (orditems.OrderId = '.$object->GetID().') AND (p.Type = '.PRODUCT_TYPE_TANGIBLE.')';
			return $this->Conn->GetOne($sql) ? true : false;
		}

		function ShipmentsExists($params)
		{
			return $this->Application->GetVar('ShipmentsExists') ? 1 : 0;
		}

		function Field($params)
		{
			$value = parent::Field($params);
			$field = $this->SelectParam($params,'name,field');
			if( ($field == 'PaymentAccount') && getArrayValue($params,'masked') )
			{
				$value = str_repeat('X',12).substr($value,-4);
			}
			return $value;
		}

		function CartHasError($params)
		{
			return $this->Application->GetVar('checkout_error') > 0;
		}

		function CheckoutError($params)
		{
			$error_codes = Array (
				1 => 'state_changed',
				2 => 'qty_unavailable',
				3 => 'outofstock',
				4 => 'invalid_code',
				5 => 'code_expired',
				6 => 'min_qty',
				7 => 'code_removed',
				8 => 'code_removed_automatically',
				9 => 'changed_after_login',
				10 => 'coupon_applied',
				104 => 'invalid_gc_code',
				105 => 'gc_code_expired',
				107 => 'gc_code_removed',
				108 => 'gc_code_removed_automatically',
				110 => 'gift_certificate_applied',
			);

			$error_param = $error_codes[ $this->Application->GetVar('checkout_error') ];
			return $this->Application->Phrase($params[$error_param]);
		}

		function GetFormAction($params)
		{
			$object =& $this->getObject($params);
			/* @var $object OrdersItem */

			$gw_data = $object->getGatewayData($params['payment_type_id']);

			$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
			$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
			/* @var $gateway_object kGWBase */

			return $gateway_object->getFormAction($gw_data['gw_params']);
		}

		function GetFormHiddenFields($params)
		{
			$object =& $this->getObject($params);
			/* @var $object OrdersItem */

			$gw_data = $object->getGatewayData(array_key_exists('payment_type_id', $params) ? $params['payment_type_id'] : null);

			$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
			$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );

			$tpl = '<input type="hidden" name="%s" value="%s" />'."\n";
			$hidden_fields = $gateway_object->getHiddenFields($object->FieldValues, $params, $gw_data['gw_params']);

			$ret = '';
			if (!is_array($hidden_fields)) {
				return $hidden_fields;
			}
			foreach($hidden_fields as $hidden_name => $hidden_value)
			{
				$ret .= sprintf($tpl, $hidden_name, $hidden_value);
			}
			return $ret;
		}

		function NeedsPlaceButton($params)
		{
			$object =& $this->getObject($params);
			$gw_data = $object->getGatewayData();

			$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
			$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );

			return $gateway_object->NeedPlaceButton($object->FieldValues, $params, $gw_data['gw_params']);
		}

		function HasGatewayError($params)
		{
			return $this->Application->RecallVar('gw_error');
		}

		function ShowGatewayError($params)
		{
			$ret = $this->Application->RecallVar('gw_error');
			$this->Application->RemoveVar('gw_error');
			return $ret;
		}

		function ShippingType($params)
		{
			$object =& $this->getObject($params);
			$shipping_info = unserialize( $object->GetDBField('ShippingInfo') );
			if (count($shipping_info) > 1) {
				return $this->Application->Phrase('lu_MultipleShippingTypes');
			}
			$shipping_info = array_shift($shipping_info);
			return $shipping_info['ShippingName'];
		}

		function DiscountHelpLink($params)
		{
			$params['pass'] = 'all,orditems';
			$params['m_cat_id'] = 0;
			$m_tag_processor =& $this->Application->recallObject('m_TagProcessor');
			return $m_tag_processor->Link($params);
		}

		function DiscountField($params)
		{
			$orditems =& $this->Application->recallObject( 'orditems' );
			$item_data = $orditems->GetDBField('ItemData');
			if(!$item_data) return '';
			$item_data = unserialize($item_data);
			$discount_prefix = ($item_data['DiscountType'] == 'coupon') ? 'coup' : 'd';

			$discount =& $this->Application->recallObject($discount_prefix, null, Array('skip_autoload' => true));
			if(!$discount->isLoaded())
			{
				$discount->Load($item_data['DiscountId']);
			}
			return $discount->GetField( $this->SelectParam($params, 'field,name') );
		}

		function HasDiscount($params)
		{
			$object =& $this->getObject($params);
			return (float)$object->GetDBField('DiscountTotal') ? 1 : 0;
		}

		/**
		 * Allows to check if required product types are present in order
		 *
		 * @param Array $params
		 */
		function HasProductType($params)
		{
			$product_types = Array('tangible' => 1, 'subscription' => 2, 'service' => 3, 'downloadable' => 4, 'package' => 5, 'gift' => 6);
			$object =& $this->getObject($params);

			$sql = 'SELECT COUNT(*)
					FROM '.TABLE_PREFIX.'OrderItems oi
					LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
					WHERE (oi.OrderId = '.$object->GetID().') AND (p.Type = '.$product_types[ $params['type'] ].')';
			return $this->Conn->GetOne($sql);
		}

		function PrintSerializedFields($params)
		{
			$object =& $this->getObject($params);
			$field = $this->SelectParam($params, 'field');
			if (!$field) $field = $this->Application->GetVar('field');
			$data = unserialize($object->GetDBField($field));

			$o = '';
			$block_params['name'] = $params['render_as'];
			foreach ($data as $field => $value) {
				$block_params['field'] = $field;
				$block_params['value'] = $value;
				$o .= $this->Application->ParseBlock($block_params);
			}
			return $o;
		}

		function OrderProductEmail($params)
		{
			$order =& $this->Application->recallObject('ord');
			$orditems =& $this->Application->recallObject('orditems');

			$sql = 'SELECT ResourceId
					FROM '.TABLE_PREFIX.'Products
					WHERE ProductId = '.$orditems->GetDBField('ProductId');
			$resource_id = $this->Conn->GetOne($sql);

			$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
			$custom_fields = $this->Application->getUnitOption('p', 'CustomFields');
			$custom_name = $ml_formatter->LangFieldName('cust_'.array_search($params['msg_custom_field'], $custom_fields));

			$sql = 'SELECT '.$custom_name.'
					FROM '.$this->Application->getUnitOption('p-cdata', 'TableName').'
					WHERE ResourceId = '.$resource_id;
			$message_template = $this->Conn->GetOne($sql);

			if (!$message_template || trim($message_template) == '') {
				// message template missing
				return ;
			}

			$from_name = strip_tags($this->Application->ConfigValue('Site_Name'));
			$from_email = $this->Application->ConfigValue('Smtp_AdminMailFrom');

			$to_name = $order->GetDBField('BillingTo');
			$to_email = $order->GetDBField('BillingEmail');
			if (!$to_email) {
				// billing email is empty, then use user's email
				$sql = 'SELECT Email
						FROM '.$this->Application->getUnitOption('u', 'TableName').'
						WHERE PortalUserId = '.$order->GetDBField('PortalUserId');
				$to_email = $this->Conn->GetOne($sql);
			}

			$esender =& $application->recallObject('EmailSender.-product');
			/* @var $esender kEmailSendingHelper */

			$esender->SetFrom($from_email, $from_name);
			$esender->AddTo($to_email, $to_name);

			$email_events_eh =& $this->Application->recallObject('emailevents_EventHandler');
			/* @var $email_events_eh EmailEventsEventsHandler */

			list ($message_headers, $message_body) = $email_events_eh->ParseMessageBody($message_template, Array());
			if (!trim($message_body)) {
				// message body missing
				return false;
			}

			foreach ($message_headers as $header_name => $header_value) {
				$esender->SetEncodedHeader($header_name, $header_value);
			}

			$esender->SetHTML($message_body);
			$esender->Deliver();
		}

		function PrintTotals($params)
		{
			$order =& $this->getObject($params);

			$totals = array();
			if (ABS($order->GetDBField('SubTotal') - $order->GetDBField('AmountWithoutVAT')) > 0.01) {
				$totals[] = 'products';
			}

			$has_tangible = $this->OrderHasTangibleItems($params);

			if ($has_tangible && $order->GetDBField('ShippingTaxable')) {
				$totals[] = 'shipping';
			}

			if ($order->GetDBField('ProcessingFee') > 0 && $order->GetDBField('ProcessingTaxable')) {
				$totals[] = 'processing';
			}

			if ($order->GetDBField('ReturnTotal') > 0 && $order->GetDBField('ReturnTotal')) {
				$totals[] = 'return';
			}

			$totals[] = 'sub_total';

			if ($order->GetDBField('VAT') > 0) {
				$totals[] = 'vat';
			}

			if ($has_tangible && !$order->GetDBField('ShippingTaxable')) {
				$totals[] = 'shipping';
			}

			if ($order->GetDBField('ProcessingFee') > 0 && !$order->GetDBField('ProcessingTaxable')) {
				$totals[] = 'processing';
			}


			$o = '';
			foreach ($totals as $type)
			{
				if ($element = getArrayValue($params, $type.'_render_as')) {
					$o .= $this->Application->ParseBlock( array('name' => $element), 1 );
				}
			}
			return $o;
		}

		function ShowDefaultAddress($params)
		{
			$address_type = ucfirst($params['type']);
			if ($this->Application->GetVar('check_'.strtolower($address_type).'_address')) {
				// form type doesn't match check type, e.g. shipping check on billing form
				return '';
			}

			// for required field highlighting on form when no submit made
			$this->Application->SetVar('check_'.strtolower($address_type).'_address', 'true');
			/*if ((strtolower($address_type) == 'billing') && $this->UsingCreditCard($params)) {
				$this->Application->SetVar('check_credit_card', 'true');
			}*/

			$this->Application->HandleEvent(new kEvent('ord:SetStepRequiredFields'));

			$user_id = $this->Application->RecallVar('user_id');
			$sql = 'SELECT AddressId
					FROM '.TABLE_PREFIX.'Addresses
					WHERE PortalUserId = '.$user_id.' AND LastUsedAs'.$address_type.' = 1';
			$address_id = $this->Conn->GetOne($sql);
			if (!$address_id) {
				return '';
			}

			$addr_list =& $this->Application->recallObject('addr', 'addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
			$addr_list->Query();

			$object =& $this->getObject();
			if (!$addr_list->CheckAddress($object->FieldValues, $address_type)) {
				$addr_list->CopyAddress($address_id, $address_type);
			}
		}

		function IsProfileAddress($params)
		{
			$object =& $this->getObject($params);
			$address_type = ucfirst($params['type']);
			return $object->IsProfileAddress($address_type);
		}

		function HasPayPalSubscription($params)
		{
			$object =& $this->getObject($params);

			$sql = 'SELECT COUNT(*)
					FROM '.TABLE_PREFIX.'OrderItems oi
					LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
					WHERE (oi.OrderId = '.$object->GetID().') AND (p.PayPalRecurring = 1)';
			return $this->Conn->GetOne($sql);

		}

		function GetPayPalSubscriptionForm($params)
		{
			$object =& $this->getObject($params);
			$gw_data = $object->getGatewayData($params['payment_type_id']);

			$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
			$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );


			$sql = 'SELECT oi.*
					FROM '.TABLE_PREFIX.'OrderItems oi
					LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
					WHERE (oi.OrderId = '.$object->GetID().') AND (p.PayPalRecurring = 1)';
			$order_item = $this->Conn->GetRow($sql);
			$order_item_data =  unserialize($order_item['ItemData']);

			$cycle = ceil($order_item_data['Duration'] / 86400);
			$cycle_units = 'D';

			$item_data = $object->FieldValues;
			$item_data['item_name'] = $order_item['ProductName'];
			$item_data['item_number'] = $order_item['OrderItemId'];
			$item_data['custom'] = $order_item['OrderId'];
			$item_data['a1'] = '';
			$item_data['p1'] = '';
			$item_data['t1'] = '';
			$item_data['a2'] = '';
			$item_data['p2'] = '';
			$item_data['t2'] = '';
			$item_data['a3'] = $order_item['Price']; //rate
			$item_data['p3'] = $cycle; //cycle
			$item_data['t3'] = $cycle_units; //cycle units D (days), W (weeks), M (months), Y (years)
			$item_data['src'] = '1'; // Recurring payments. If set to 1, the payment will recur unless your customer cancels the subscription before the end of the billing cycle.
			$item_data['sra'] = '1'; // Reattempt on failure. If set to 1, and the payment fails, the payment will be reattempted two more times. After the third failure, the subscription will be cancelled.
			$item_data['srt'] = ''; // Recurring Times. This is the number of payments which will occur at the regular rate.

			$hidden_fields = $gateway_object->getSubscriptionFields($item_data, $params, $gw_data['gw_params']);

			$ret = '';
			if (!is_array($hidden_fields)) {
				return $hidden_fields;
			}
			$tpl = '<input type="hidden" name="%s" value="%s" />'."\n";
			foreach($hidden_fields as $hidden_name => $hidden_value)
			{
				$ret .= sprintf($tpl, $hidden_name, $hidden_value);
			}
			return $ret;
		}

		function UserHasPendingOrders($params)
		{
			$sql = 'SELECT OrderId FROM '.$this->Application->getUnitOption($this->Prefix, 'TableName').'
							WHERE PortalUserId = '.$this->Application->RecallVar('user_id').'
							AND Status = '.ORDER_STATUS_PENDING;
			return $this->Conn->GetOne($sql) ? 1 : 0;
		}

		function AllowAddAddress($params)
		{
			$user =& $this->Application->recallObject('u.current');

			if ($user->GetDBField('cust_shipping_addr_block')) return false;

			$address_list =& $this->Application->recallObject('addr','addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
			$address_list->Query();

			$max = $this->Application->ConfigValue('MaxAddresses');

			return $max <= 0 ? true : $address_list->RecordsCount < $max;
		}

		function FreePromoShippingAvailable($params)
		{
			$object =& $this->Application->recallObject('orditems');
			$free_ship = $object->GetDBField('MinQtyFreePromoShipping');
			$tangible = ($object->GetDBField('Type') == 1)? 1 : 0;
			return ($tangible && ($free_ship > 0 && $free_ship <= $object->GetDBField('Quantity')))? 1 : 0;
		}

		/**
		 * Creates link for removing coupon or gift certificate
		 *
		 * @param Array $params
		 * @return string
		 */
		function RemoveCouponLink($params)
		{
			$type = strtolower($params['type']);
			$url_params = Array (
				'pass' => 'm,ord',
				'ord_event' => ($type == 'coupon') ? 'OnRemoveCoupon' : 'OnRemoveGiftCertificate',
				'm_cat_id' => 0,
			);

			return $this->Application->HREF('', '', $url_params);
		}

		/**
		 * Calculates total weight of items in shopping cart
		 *
		 * @param Array $params
		 * @return float
		 */
		function TotalOrderWeight($params)
		{
			$object =& $this->getObject();
			/* @var $object kDBItem */

			$sql = 'SELECT SUM( IF(oi.Weight IS NULL, 0, oi.Weight * oi.Quantity) )
					FROM '.TABLE_PREFIX.'OrderItems oi
					WHERE oi.OrderId = '.$object->GetID();
			$total_weight = $this->Conn->GetOne($sql);

			if ($total_weight == '') {
				// zero weight -> return text about it
				return $this->Application->Phrase('lu_NotAvailable');
			}

			$regional =& $this->Application->recallObject('lang.current');

			switch ($regional->GetDBField('UnitSystem')) {
				case 1:
					// metric system -> add kg sign
					$total_weight .= ' '.$this->Application->Phrase('lu_kg');
					break;

				case 2:
					// uk system -> convert to pounds
					list($pounds, $ounces) = Kg2Pounds($total_weight);
					$total_weight = $pounds.' '.$this->Application->Phrase('lu_pounds').' '.$ounces.' '.$this->Application->Phrase('lu_ounces');
					break;
			}

			return $total_weight;
		}

		function InitCatalogTab($params)
		{
			$tab_params['mode'] = $this->Application->GetVar('tm'); // single/multi selection possible
			$tab_params['special'] = $this->Application->GetVar('ts'); // use special for this tab
			$tab_params['dependant'] = $this->Application->GetVar('td'); // is grid dependant on categories grid

			// set default params (same as in catalog)
			if ($tab_params['mode'] === false) $tab_params['mode'] = 'multi';
			if ($tab_params['special'] === false) $tab_params['special'] = '';
			if ($tab_params['dependant'] === false) $tab_params['dependant'] = 'yes';

			// pass params to block with tab content
			$params['name'] = $params['render_as'];
			$params['prefix'] = trim($this->Prefix.'.'.($tab_params['special'] ? $tab_params['special'] : $this->Special), '.');
			$params['cat_prefix'] = trim('c.'.($tab_params['special'] ? $tab_params['special'] : $this->Special), '.');
			$params['tab_mode'] = $tab_params['mode'];
			$params['grid_name'] = ($tab_params['mode'] == 'multi') ? $params['default_grid'] : $params['radio_grid'];
			$params['tab_dependant'] = $tab_params['dependant'];
			$params['show_category'] = $tab_params['special'] == 'showall' ? 1 : 0; // this is advanced view -> show category name

			// use $pass_params to be able to pass 'tab_init' parameter from m_ModuleInclude tag
			return $this->Application->ParseBlock($params, 1);
		}

		/**
		 * Checks if required payment method is available
		 *
		 * @param Array $params
		 * @return bool
		 */
		function HasPaymentGateway($params)
		{
			static $payment_types = Array ();

			$gw_name = $params['name'];
			if (!array_key_exists($gw_name, $payment_types)) {
				$sql = 'SELECT pt.PaymentTypeId, pt.PortalGroups
						FROM '.TABLE_PREFIX.'PaymentTypes pt
						LEFT JOIN '.TABLE_PREFIX.'Gateways g ON pt.GatewayId = g.GatewayId
						WHERE (g.Name = '.$this->Conn->qstr($params['name']).') AND (pt.Status = '.STATUS_ACTIVE.')';
				$payment_types[$gw_name] = $this->Conn->GetRow($sql);
			}

			if (!$payment_types[$gw_name]) {
				return false;
			}

			$pt_groups = explode(',', substr($payment_types[$gw_name]['PortalGroups'], 1, -1));
			$user_groups = explode(',', $this->Application->RecallVar('UserGroups'));

			return array_intersect($user_groups, $pt_groups) ? $payment_types[$gw_name]['PaymentTypeId'] : false;
		}

		function DisplayPaymentGateway($params)
		{
			$payment_type_id = $this->HasPaymentGateway($params);
			if (!$payment_type_id) {
				return '';
			}

			$object =& $this->getObject($params);
			/* @var $object OrdersItem */

			$gw_data = $object->getGatewayData($payment_type_id);

			$block_params = $gw_data['gw_params'];
			$block_params['name'] = $params['render_as'];
			$block_params['payment_type_id'] = $payment_type_id;

			return $this->Application->ParseBlock($block_params);
		}

		/**
		 * Checks, that USPS returned valid label
		 *
		 * @param Array $params
		 * @return bool
		 */
		function USPSLabelFound($params)
		{
			$object =& $this->getObject($params);
			/* @var $object kDBItem */

			$full_path = USPS_LABEL_FOLDER . $object->GetDBField( $params['field'] ) . '.pdf';

			return file_exists($full_path) && is_file($full_path);
		}

		/**
		 * Prints USPS errors from session
		 *
		 * @param Array $params
		 * @return string
		 */
		function PrintUSPSErrors($params)
		{
			$o = '';
			$ses_usps_erros = Array();
			$ses_usps_erros = unserialize($this->Application->RecallVar('usps_errors'));
			if ( count($ses_usps_erros) > 0 && is_array($ses_usps_erros)) {
				foreach ( $ses_usps_erros as $order_number => $error_description ) {
					$block_params = Array();
					$block_params['name'] = $params['render_as'];
					$block_params['order_number'] = $order_number;
					$block_params['error_description'] = $error_description;
					$o.=$this->Application->ParseBlock($block_params, 1);
				}
				$this->Application->RemoveVar('usps_errors');
			}

			return $o;
		}

	}