<?php
class aOrdersClass extends ArrayIterator {
	private $current = null;
	private $key = null;
	private $iterator = null;
	private $aSavedOrders = Array();
	function __construct(){
		$this->rewind();
	}
	function rewind(){
		$oDB = DataBase::getInstance();
		$this->current = $oDB->executeSQL( 'SELECT * FROM `' . $oDB->throwTableName( DB_ORDERS ) . '` ORDER BY `iOrder` DESC LIMIT 1', true, DataBase::SINGLE_RECORD );
		$this->iterator = 0;
		$this->key = $this->current['iOrder'];
		if( !empty( $this->aSavedOrders[$this->key] ) ){
			$this->current = $this->aSavedOrders[$this->key];
		} elseif( !empty( $this->current ) ){
			$this->current['sDate'] = date( 'Y-m-d H:i', $this->current['iTime'] );
			$this->current['sStatus'] = $GLOBALS['oOrder']->throwStatus( $this->current['iStatus'] );
		}
		return $this->current;
	}
	function current(){
		return $this->current;
	}
	function key(){
		return $this->key;
	}
	function next(){
		$oDB = DataBase::getInstance();
		$this->iterator++;
		$this->current = $oDB->executeSQL( 'SELECT * FROM `' . $oDB->throwTableName( DB_ORDERS ) . '` ORDER BY `iOrder` DESC LIMIT ' . $this->iterator . ',1', true, DataBase::SINGLE_RECORD );
		$this->key = $this->current['iOrder'];
		if( !empty( $this->aSavedOrders[$this->key] ) ){
			$this->current = $this->aSavedOrders[$this->key];
		} elseif( !empty( $this->current ) ){
			$this->current['sDate'] = date( 'Y-m-d H:i', $this->current['iTime'] );
			$this->current['sStatus'] = $GLOBALS['oOrder']->throwStatus( $this->current['iStatus'] );
		}
		return $this->current();
	}
	function valid(){
		return !empty( $this->key );
	}

/* ******************************* */	

	public function offsetGet( $name ){
		$oDB = DataBase::getInstance();
		$aOrder = $oDB->executeSQL( 'SELECT * FROM `' . $oDB->throwTableName( DB_ORDERS ) . '` WHERE `iOrder` = ' . intval( $name ), true, DataBase::SINGLE_RECORD );
		if( !empty( $this->aSavedOrders[$aOrder['iOrder']] ) ){
			$aOrder = $this->aSavedOrders[$aOrder['iOrder']];
		} elseif( !empty( $aOrder ) ){
			$aOrder['sDate'] = date( 'Y-m-d H:i', $aOrder['iTime'] );
			$aOrder['sStatus'] = $GLOBALS['oOrder']->throwStatus( $aOrder['iStatus'] );
		}
		return $aOrder;
	}
	public function offsetSet( $name, $value ){
		$this->aSavedOrders[$name] = $value;
		return $value;
	}
	public function offsetExists( $name ){
		$oDB = DataBase::getInstance();
		$iOrder = $oDB->executeSQL( 'SELECT `iOrder` FROM `' . $oDB->throwTableName( DB_ORDERS ) . '` WHERE `iOrder` = ' . intval( $name ), true, DataBase::SINGLE_VALUE );
		return !empty( $iOrder );
	}
}

class Orders{
	var $aOrders = null;
	var $aProducts = null;
	var $iExplodeRange = null;
	var $bCountOnly = false;
	/**
	 * Constructor which removes expired baskets (temp_orders)
	 */
	function __construct( ){
		$oDB = & DataBase::getInstance( );
		$sql = 'START TRANSACTION';
		$oDB->executeSQL( $sql );
		$iTime = time( ) - 2592000;
		/*		$sql = 'UPDATE
							`'.$oDB->throwTableName( DB_PRODUCTS_VARIANTS ).'` AS `pv` 
								INNER JOIN (
									SELECT SUM( `iQuantity` ) AS `iSum`, `iProductVariant`
										FROM `'.$oDB->throwTableName( DB_ORDERS_TEMP ).'` AS `to`
											LEFT JOIN `'.$oDB->throwTableName( DB_ORDERS_TEMP_INFO ).'` AS `toi` USING( `iCustomer` )
										WHERE ( UNIX_TIMESTAMP( `iLastUpdate` ) < '.$iTime.' )
										GROUP BY `iProductVariant`
								) AS `sq` USING( `iProductVariant` )
							
							SET `pv`.`iStock` = (`pv`.`iStock` + `sq`.`iSum` )';
		$oDB->executeSQL( $sql );*/
		$sql = 'DELETE FROM `oti`, `ot` USING `' . $oDB->throwTableName( DB_ORDERS_TEMP_INFO ) . '` AS `oti` LEFT JOIN `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` AS `ot` USING( `iCustomer` ) WHERE UNIX_TIMESTAMP( `oti`.`iLastUpdate` ) < ' . $iTime;
		$oDB->executeSQL( $sql );
		$sql = 'COMMIT';
		$oDB->executeSQL( $sql );
	}
	function generateCache( ){
		$oDB = & DataBase::getInstance( );
		$this->aOrders = new aOrdersClass();
	}
	function throwProducts( $iId = null, $getAll = true ){
		$oDB = & DataBase::getInstance( );
		$oFile = Files::getInstance( );
		$this->aProducts = null;
		if( isset( $iId ) ){
			$this->generateProducts( $iId );
		} else{
			$this->generateBasket( );
		}
		$fSummary = 0;
		$fSummaryForDiscount = 0;
		$aProducts = $this->aProducts;
		if( isset( $aProducts ) ){
			if( !empty( $iId ) ) $aData = $this->throwOrder( $iId, true );
			$aStores = $oDB->throwFileArraySmall( DB_STORES, 'iStore', 'sName' );
			foreach( $aProducts as &$aProduct ){
				if( $getAll ){
					$aProductFull = $GLOBALS['oProduct']->throwProduct( $aProduct['iProduct'] );
					if( is_array( $aProductFull ) ){
						$aProduct = array_merge( $aProductFull, $aProduct );
					}
				} else{
					if( !empty( $aProduct['iProduct'] ) )
						$aProductFull = $oDB->executeSQL( 'SELECT `sName`, `sAdminInfo` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` WHERE `iProduct` = ' . $aProduct['iProduct'], true, DataBase::SINGLE_RECORD );
					else
						$aProductFull = null;
					if( is_array( $aProductFull ) ){
						$aProduct = array_merge( $aProductFull, $aProduct );
						$aProduct['sLinkName'] = throwPageUrl( $aProduct['iProduct'] . ',' . change2Url( $aProductFull['sName'] ) );
					}
				}
				if( count( $aStores ) > 1 && !empty( $aProduct['sStoresInfo'] ) ){
					$aProduct['aStores'] = explode( ';', $aProduct['sStoresInfo'] );
					foreach( $aProduct['aStores'] as &$mStore ){
						$mStore = array_combine( Array( 'iStore', 'iQuantity' ), explode( ':', $mStore ) );
						$mStore['sName'] = $aStores[$mStore['iStore']];
					}
				}
				$aProduct['sSummary'] = displayPrice( normalizePrice( $aProduct['fSummary'] ) );
				$aProduct['sPrice'] = displayPrice( normalizePrice( $aProduct['fPrice'] ) );
				$aProduct['sLinkDelete'] = defined( 'CUSTOMER_PAGE' ) ? $GLOBALS['aData']['sLinkName'] . '?' . 'iProductDelete=' . $aProduct['iProductVariant'] : null;
				$fSummary+= $aProduct['fSummary'];
				if( isset( $aProduct['iFile'] ) ){
					$aProduct['aDataImage'] = $oFile->throwFile( 2, $aProduct['iFile'] );
				}
				elseif( isset( $oFile->aImagesDefault[2][$aProduct['iProduct']] ) ){
					$aProduct['aDataImage'] = $oFile->aImagesDefault[2][$aProduct['iProduct']];
				}
				if( !empty( $aData['iDiscountCoupon'] ) && is_numeric( $aData['iDiscountCoupon'] ) ){
					if( $aProduct['iDiscount'] ){
						$aProduct['bDiscount'] = true;
						$fSummaryForDiscount+= $aProduct['fSummary'];
					} else{
						$aProduct['bDiscount'] = false;
					}
				}
			}
			$aData['fProductsSummary'] = normalizePrice( $fSummary );
			$aData['sProductsSummary'] = displayPrice( $aData['fProductsSummary'] );
			if( !empty( $aData['iDiscountCoupon'] ) && is_numeric( $aData['iDiscountCoupon'] ) ) list( $aData['sDiscountCouponValue'], $aData['iDiscountCouponProducer'], $aData['sDiscountCode'] ) = $oDB->executeSQL( 'SELECT `sDiscountValue`, `iProducer`, `sCode` FROM `' . $oDB->throwTableName( DB_DISCOUNT_COUPONS ) . '` WHERE `iDiscountCoupon` = ' . intval( $aData['iDiscountCoupon'] ), true, DataBase::SINGLE_RECORD | MYSQLI_NUM );
			else $aData['sDiscountCouponValue'] = 0;
			if( $aData['sDiscountCouponValue'] > 0 ){
				//Aktywne kody jednorazowe
				if( empty( $aData['sDiscountCouponValue'] ) ){
					$aData['sDiscountCouponValue'] = '0.00';
					$aData['sProductsSummaryDiscount'] = $aData['sProductsSummary'];
				} else{
					$aData['bDiscountPanelActive'] = true;
					if( substr( $aData['sDiscountCouponValue'], -1 ) == '%' ){
						$aData['sProductsSummaryDiscount'] = normalizePrice( $aData['sProductsSummary'] - ( $fSummaryForDiscount * substr( $aData['sDiscountCouponValue'], 0, -1 ) / 100 ) );
						if( !empty( $aData['iDiscountCouponProducer'] ) ){
							$aData['aProducer'] = throwProducer( $aData['iDiscountCouponProducer'] );
							$aData['sDiscountCouponValue'] = '-' . $aData['sDiscountCouponValue'] . ' *' . $aData['aProducer']['sName'];
						} else{
							$aData['sDiscountCouponValue'] = '-' . $aData['sDiscountCouponValue'];
						}
					} else{
						$aData['sProductsSummaryDiscount'] = normalizePrice( $aData['sProductsSummary'] - $aData['sDiscountCouponValue'] );
						$aData['sDiscountCouponValue'] = '-' . normalizePrice( $aData['sDiscountCouponValue'] );
					}
				}
			} else{
				$aData['sProductsSummaryDiscount'] = normalizePrice( $aData['sProductsSummary'] );
			}
			if( isset( $iId ) ){
				$aOrder = $this->aOrders[$iId];
				if( isset( $aOrder ) ){
					$aOrder['fProductsSummary'] = $aData['fProductsSummary'];
					if( empty( $aOrder['fPaymentCarrierPrice'] ) ){
						$aOrder['fPaymentCarrierPrice'] = generatePrice( $aOrder['fCarrierPrice'], $aOrder['sPaymentPrice'], $aOrder['fProductsSummary'] );
					}
					if( !empty( $aOrder['fPaymentCarrierPrice'] ) ){
						$aOrder['fOrderSummary'] = $aData['fOrderSummary'] = normalizePrice( $aData['sProductsSummaryDiscount'] + $aOrder['fPaymentCarrierPrice'] );
						$aOrder['sOrderSummary'] = $aData['sOrderSummary'] = displayPrice( $aData['fOrderSummary'] );
					}
					$this->aOrders[$iId] = $aOrder;
				}
			}
			if( $aData['iGift'] ){
				$aData['fOrderSummary']+= $GLOBALS['config']['gift_cost'];
				$aData['sGiftCost'] = displayPrice( normalizePrice( $GLOBALS['config']['gift_cost'] ) );
			}
			$aData['fOrderSummary'] = normalizePrice( $aData['fOrderSummary'] );
			$aData['sOrderSummary'] = displayPrice( $aData['fOrderSummary'] );
			$aData['aProducts'] = $aProducts;
			return $aData;
		}
	}
	function generateBasket( ){
		$oDB = & DataBase::getInstance( );
		if( is_numeric( $_SESSION['iUserClient'] ) && ( $_SESSION['iUserClient'] > 0 ) ){
			$sUserId = intval( $_SESSION['iUserClient'] );
		} else{
			$sUserId = 'NULL';
		}
		$iStatus = throwStatus( );
		$sql = 'SELECT `products_variants`.`TiAvailable` AS `iAvailable`, `iProductVariant`
							FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `products_variants`
--								INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `products` USING( `iProduct` )
								INNER JOIN `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` AS `orders_temp` USING( `iProductVariant` )
							WHERE `orders_temp`.`iCustomer` = \'' . $oDB->escape( $_SESSION['iCustomer' . LANGUAGE] ) . '\'
								AND `products_variants`.`iStatus` >= ' . intval( $iStatus );
		$sql = 'SELECT `ot`.`iProductVariant`, `ot`.`iQuantity`, ' . $oDB->throwRoutineName( '%%_calculateProductVariantPriceForUser' ) . '( ' . $sUserId . ', `ot`.`iProductVariant`, ' . JOIN_PROMOTIONS . ' ) AS `fPrice`, `ot`.`iFotoliaPhoto`, `p`.`iProduct`, IFNULL( `pv`.`fPriceTimeValue`, `p`.`fPriceTimeValue` ) AS `fPriceTimeValue`, `p`.`iPriceTimeFrom`, `p`.`iPriceTimeTo`, `p`.`iPriceTimeMaxCount`, `p`.`iPriceTimeCount`, IF( `vpvn`.`sName` IS NULL, `p`.`sName`, CONCAT( `p`.`sName`, \', \', `vpvn`.`sName` ) ) AS `sName`, IFNULL( `pv`.`sWeight`, `p`.`sWeight` ) AS `sWeight`, `p`.`iFotolia`, `v`.`fVatValue` AS `fVat`, `pa`.`sText` AS `sAvailability`, `iFile`, `p`.`iProducer`, IFNULL( `pv`.`fBuyPrice`, `p`.`fBuyPrice` ) AS `fBuyPrice`
							FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` AS `ot`
								LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pv` USING( `iProductVariant` )
								LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `p` USING( `iProduct` )
								LEFT JOIN `' . $oDB->throwTableName( DB_VAT ) . '` AS `v` ON( IFNULL( `pv`.`iVat`, `p`.`iVat` ) = `v`.`iVat` )
								
								LEFT JOIN (' . $sql . ') AS `pag` USING( `iProductVariant` )
								LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_AVAILABLE ) . '` AS `pa` USING( `iAvailable` )
								
								LEFT JOIN `' . $oDB->throwTableName( DB_V_PRODUCTS_VARIANTS_NAMES ) . '` AS `vpvn` USING( `iProductVariant` )
							WHERE `iCustomer` = \'' . $oDB->escape( $_SESSION['iCustomer' . LANGUAGE] ) . '\'
								AND `pv`.`iStatus` >= ' . intval( $iStatus ) . ' AND `p`.`iStatus` >= ' . intval( $iStatus );
		$aProducts = $oDB->executeSQL( $sql, true );
		$this->aProducts = null;
		$_SESSION['iOrderQuantity' . LANGUAGE] = 0;
		$_SESSION['fOrderSummary' . LANGUAGE] = null;
		foreach( $aProducts as $row ){
			$row['sName'] = trim( preg_replace( '/\\#[0-9a-fA-F]{6}/', '', $row['sName'] ) );
			if( !empty( $row['iPriceTimeFrom'] ) && ( $row['iPriceTimeFrom'] <= time( ) ) && !empty( $row['iPriceTimeTo'] ) && ( $row['iPriceTimeTo'] > time( ) ) && ( $row['fPriceTimeValue'] > 0 ) && ( $row['fPriceTimeValue'] < $row['fPrice'] ) ){
				if( defined( 'CUSTOMER_PAGE' ) ) $row['fPrice'] = normalizePrice( $row['fPriceTimeValue'] );
			} else{
				$row['fPrice'] = normalizePrice( $row['fPrice'] );
			}
			$this->aProducts[$row['iProductVariant']] = $row;
			$this->aProducts[$row['iProductVariant']]['fSummary'] = normalizePrice( $row['fPrice'] * $row['iQuantity'] );
			$this->aProducts[$row['iProductVariant']]['sLinkName'] = throwPageUrl( $row['iProduct'] . ',' . change2Url( $row['sName'] ) );
			$sql = 'SELECT `iCarrier`, `fPrice` FROM `' . $oDB->throwTableName( DB_PRODUCTS_CARRIERS_PRICES ) . '` WHERE `iProduct` = ' . intval( $row['iProduct'] );
			$this->aProducts[$row['iProductVariant']]['aCarriersPrices'] = $oDB->executeSQL( $sql, true, DataBase::ASSOCIATIVE );
			$sql = 'SELECT `iPayment`, `iPayment` FROM `' . $oDB->throwTableName( DB_PRODUCTS_PAYMENTS_BLOCK ) . '` WHERE `iProduct` = ' . intval( $row['iProduct'] );
			$this->aProducts[$row['iProductVariant']]['aBlockedPayments'] = $oDB->executeSQL( $sql, true, DataBase::ASSOCIATIVE );
			$_SESSION['iOrderQuantity' . LANGUAGE]+= $row['iQuantity'];
			$_SESSION['fOrderSummary' . LANGUAGE]+= $row['fPrice'] * $row['iQuantity'];
		}
		if( isset( $_SESSION['fOrderSummary' . LANGUAGE] ) ) $_SESSION['fOrderSummary' . LANGUAGE] = normalizePrice( $_SESSION['fOrderSummary' . LANGUAGE] );
		if( isset( $GLOBALS['config']['basket_summary'] ) ) $_SESSION['aProductsBasket'] = $this->aProducts;
	} // end function generateBasket
	function generateProducts( $iOrder ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sql = 'SELECT `op`.*, `pv`.`iProduct`, `pv`.`sEan`, `pv`.`sOuterId`, `si`.`sStoresInfo`, `pv`.`iFile`, `p`.`iProducer`
							FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` AS `op`
								LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pv` USING( `iProductVariant` )
								LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `p` USING( `iProduct` )
								LEFT JOIN ( SELECT `opq`.`iOrderProduct`, GROUP_CONCAT( CONCAT( `opsq`.`iStore`, \':\', `opsq`.`iQuantity` ) ORDER BY `opsq`.`iStore` SEPARATOR \';\' ) AS `sStoresInfo` FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS_STORES ) . '` AS `opsq` INNER JOIN `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` AS `opq` USING( `iOrderProduct` ) WHERE `iOrder` = ' . intval( $iOrder ) . ' GROUP BY `iOrderProduct` ) AS `si` USING( `iOrderProduct` )
							WHERE `iOrder` = ' . intval( $iOrder );/* . '
								AND `pv`.`iStatus` >= ' . intval( $iStatus );*/ //Pokaż również, jeśli wariant jest nieaktywny - nie zmienia to faktu, że taki produkt został zamówiony
		$aProducts = $oDB->executeSQL( $sql, true );
		if( is_array( $aProducts ) ) foreach( $aProducts as $aProduct ){
			$aProduct['fSummary'] = normalizePrice( $aProduct['fPrice'] * $aProduct['iQuantity'] );
			$this->aProducts[$aProduct['iOrderProduct']] = $aProduct;
		}
	}
	function checkEmptyBasket( ){
		$this->generateBasket( );
		return( isset( $this->aProducts ) ) ? false : true;
	}
	function saveBasket( $aForm ){
		$aErrors = null;
		$this->generateBasket( );
		$oDB = & DataBase::getInstance( );
		$iCustomer = $_SESSION['iCustomer' . LANGUAGE];
		$iStatus = throwStatus( );
		if( isset( $aForm['aProducts'] ) && is_array( $aForm['aProducts'] ) ){
			if( is_array( $aForm['aProductsFotolia'] ) ) foreach( $aForm['aProductsFotolia'] as $iProductVariant => $iFotoliaPhoto ){
				$oDB->update( DB_ORDERS_TEMP, Array( 'iFotoliaPhoto' => $iFotoliaPhoto ), Array( 'iProductVariant' => $iProductVariant, 'iCustomer' => $iCustomer ) );
			}
			foreach( $aForm['aProducts'] as $iProductVariant => $iQuantity ){
				if( $iQuantity != $this->aProducts[$iProductVariant]['iQuantity'] ){
					if( $iQuantity <= 0 ) $this->deleteFromBasket( $iProductVariant );
					elseif( $iQuantity < $this->aProducts[$iProductVariant]['iQuantity'] ){
						$oDB->executeSQL( 'UPDATE `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` SET `iQuantity` = `iQuantity` - ' . ( $this->aProducts[$iProductVariant]['iQuantity'] - $iQuantity ) . ' WHERE `iProductVariant` = ' . $iProductVariant . ' AND `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'' );
					} else{
						$sql = 'SELECT IF( `pv`.`TiStock` >= ' . ( $iQuantity ) . ', 1, `pv`.`TiCanBuyOutOfStock` )
											FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pv` 
												LEFT JOIN `' . $oDB->throwTableName( DB_PRODUCTS ) . '` AS `p` USING( `iProduct` )
											WHERE `pv`.`iStatus` >= ' . intval( $iStatus ) . ' AND `p`.`iStatus` >= ' . intval( $iStatus ) . ' AND `pv`.`iProductVariant` = ' . intval( $iProductVariant );
						if( $oDB->executeSQL( $sql, true, DataBase::SINGLE_VALUE ) ){
							$oDB->executeSQL( 'UPDATE `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` SET `iQuantity` = `iQuantity` + ' . ( $iQuantity - $this->aProducts[$iProductVariant]['iQuantity'] ) . ' WHERE `iProductVariant` = ' . $iProductVariant . ' AND `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'' );
						} else{
							$aErrors[] = str_replace( Array( '%sProductName%', '%iQuantityWanted%', '%iQuantitySet%' ), Array( $this->aProducts[$iProductVariant]['sName'], $iQuantity, $this->aProducts[$iProductVariant]['iQuantity'] ), $GLOBALS['lang']['Sorry_product_quantity_too_small'] );
						}
					}
				}
			}
			$this->generateBasket( );
			$sql = 'SELECT `iProductVariant`, `iQuantity` FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` WHERE `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'';
			$aStockInfo = $oDB->executeSQL( $sql, true );
			$iDays = null;
			if( is_array( $aStockInfo ) && !empty( $aStockInfo ) ){
				$aAvailabilities = $oDB->throwFileArraySmall( DB_PRODUCTS_AVAILABLE, 'iAvailable', 'iDays', 'iAvailable', Array( 'iCanBuy' => 1 ) );
				foreach( $aStockInfo as &$aPV ){
					$from_where = 'FROM `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` WHERE `iProductVariant` = ' . $aPV['iProductVariant'];
					$sql = 'SELECT `iStock`, `iAvailable` FROM ( SELECT `iStock`, `TiAvailableOnStock` AS `iAvailable` ' . $from_where . ' AND `iStock` > 0 UNION ALL SELECT NULL, `TiAvailableOutOfStock` AS `iAvailable` ' . $from_where . ' ) AS `pva` ORDER BY `iAvailable` ASC';

					foreach( $oDB->executeSQL( $sql, true ) as $aAvail ){
						if( !isset( $aAvail['iStock'] ) || $aAvail['iStock'] >= $aPV['iQuantity'] ){
							if( !isset( $iDays ) || $iDays < $aAvailabilities[$aAvail['iAvailable']] ) $iDays = $aAvailabilities[$aAvail['iAvailable']];
							break;
						} else{
							$aPV['iQuantity'] -= $aAvail['iStock'];
						}
					}
				}
			}
			if( isset( $iDays ) ){
				$this->saveTempOrderInfo( Array( 'iCustomer' => $iCustomer, 'iDays' => $iDays ) );
			} else{			
				$this->saveTempOrderInfo( Array( 'iCustomer' => $iCustomer ) );
			}
		}
		return $aErrors;
	}
	function deleteFromBasket( $iProduct ){
		$iProduct = intval( $iProduct );
		$iCustomer = $_SESSION['iCustomer' . LANGUAGE];
		$oDB = & DataBase::getInstance( );
		//		$oDB->executeSQL( 'START TRANSACTION WITH CONSISTENT SNAPSHOT' );
		/*		$oDB->executeSQL( 'UPDATE `'.$oDB->throwTableName( DB_PRODUCTS_VARIANTS ).'` SET `iStock` = `iStock` + ( SELECT `iQuantity` FROM `'.$oDB->throwTableName( DB_ORDERS_TEMP ).'` WHERE `iProductVariant` = '.$iProduct.' AND `iCustomer` = \''.$oDB->escape( $iCustomer ).'\' ) WHERE `iProductVariant` = '.$iProduct );*/
		$oDB->executeSQL( 'DELETE FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` WHERE `iProductVariant` = ' . $iProduct . ' AND `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'' );
		//		$oDB->executeSQL( 'COMMIT' );
		$this->generateBasket( );
	}
	function addToBasket( $iProduct, $iQuantity, $aAttributes = null ){
		$iStatus = throwStatus( );
		$iQuantity = intval( $iQuantity );
		if( $iQuantity > 0 ){
			$iCustomer = $_SESSION['iCustomer' . LANGUAGE];
			$oDB = DataBase::getInstance( );
			$sql = 'SELECT `pv`.`iProductVariant` FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pv`
								WHERE `pv`.`iProduct` = \'' . intval( $iProduct ) . '\'
									AND `pv`.`iStatus` >= ' . intval( $iStatus );
			if( is_array( $aAttributes ) && !empty( $aAttributes ) ){
				foreach( $aAttributes as $iAttribute => $iAttributeValue ){
					if( $iAttributeValue ){
						$sql.= ' AND EXISTS( SELECT 1
																		FROM `' . $oDB->throwTableName( DB_PRODUCTS_ATTRIBUTES ) . '` AS `pa`
																		WHERE `pa`.`iProductVariant` = `pv`.`iProductVariant`
																			AND `pa`.`iAttributeValue` = ' . intval( $iAttributeValue ) . ' )';
					}
				}
			}
			$r = $oDB->executeSQL( $sql, true );
			$num_rows = $oDB->numRows( );
			switch( $num_rows ){
				case 0: // 0 - wariant nie istnieje
					return $GLOBALS['lang']['ErrorMessageNoVariant'];
				break;
				case 1: // 1 - prawidłowo określony wariant
					$iProductVariant = $r[0]['iProductVariant'];
					if( $this->addVariantToBasket( $iProductVariant, $iQuantity ) !== false ){
						return 0;
					}
					$this->saveTempOrderInfo( Array( 'iCustomer' => $iCustomer ) );
					return $GLOBALS['lang']['ErrorMessageNotAvailable'];
				break;
				default: // > 1 - niedostatecznie określony wariant
					return $GLOBALS['lang']['ErrorMessageNotSpecified'];
				break;
			}
		}
	}
	function addVariantToBasket( $iProductVariant, $iQuantity ){
		$iCustomer = $_SESSION['iCustomer' . LANGUAGE];
		$oDB = DataBase::getInstance( );
		$iCurrentQuantity = 0;
		$this->generateBasket( );
		if( isset( $this->aProducts ) && isset( $this->aProducts[$iProductVariant] ) ){
			$iCurrentQuantity = $this->aProducts[$iProductVariant]['iQuantity'];
		}

		$sql = 'SELECT IF( `TiStock` >= \'' . ( $iQuantity + $iCurrentQuantity ) . '\', 1, `TiCanBuyOutOfStock` ) AS `iCanBuy`
							FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '`
							WHERE `iProductVariant` = \'' . $iProductVariant . '\'';
		$iCanBuy = $oDB->executeSQL( $sql, true, DataBase::SINGLE_VALUE );
		if( $iCanBuy ){
			$oDB->executeSQL( 'SELECT 1
														FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '`
														WHERE `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'
															AND `iProductVariant` = ' . $iProductVariant );
			$iCount = $oDB->numRows( );
			if( $iCount > 0 ){
				$oDB->executeSQL( 'UPDATE `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '`
															SET `iQuantity` = `iQuantity` + \'' . $iQuantity . '\'
															WHERE `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'
																AND `iProductVariant` = ' . $iProductVariant );
			} else{
				$oDB->executeSQL( 'INSERT INTO `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` ( `iCustomer`, `iProductVariant`, `iQuantity` )
															VALUES( \'' . $oDB->escape( $iCustomer ) . '\', ' . $iProductVariant . ', \'' . $iQuantity . '\' )' );
			}
			$sql = 'SELECT `iProductVariant`, `iQuantity` FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` WHERE `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'';
			$aStockInfo = $oDB->executeSQL( $sql, true );
			$iDays = null;
			if( is_array( $aStockInfo ) && !empty( $aStockInfo ) ){
				$aAvailabilities = $oDB->throwFileArraySmall( DB_PRODUCTS_AVAILABLE, 'iAvailable', 'iDays', 'iAvailable', Array( 'iCanBuy' => 1 ) );
				foreach( $aStockInfo as &$aPV ){
					$from_where = 'FROM `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` WHERE `iProductVariant` = ' . $aPV['iProductVariant'];
					$sql = 'SELECT `iStock`, `iAvailable` FROM ( SELECT `iStock`, `TiAvailableOnStock` AS `iAvailable` ' . $from_where . ' AND `iStock` > 0 UNION ALL SELECT NULL, `TiAvailableOutOfStock` AS `iAvailable` ' . $from_where . ' ) AS `pva` ORDER BY `iAvailable` ASC';

					foreach( $oDB->executeSQL( $sql, true ) as $aAvail ){
						if( !isset( $aAvail['iStock'] ) || $aAvail['iStock'] >= $aPV['iQuantity'] ){
							if( !isset( $iDays ) || $iDays < $aAvailabilities[$aAvail['iAvailable']] ) $iDays = $aAvailabilities[$aAvail['iAvailable']];
							break;
						} else{
							$aPV['iQuantity'] -= $aAvail['iStock'];
						}
					}
				}
			}
			if( isset( $iDays ) ){
				$this->saveTempOrderInfo( Array( 'iCustomer' => $iCustomer, 'iDays' => $iDays ) );
			}
			return 0;
		}
		return false;
	}
	function getTempOrderInfo( ){
		$iCustomer = $_SESSION['iCustomer' . LANGUAGE];
		$oDB = DataBase::getInstance( );
		$sql = 'SELECT *
							FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP_INFO ) . '`
							WHERE `iCustomer` = \'' . $oDB->escape( $iCustomer ) . '\'';
		$r = $oDB->executeSQL( $sql, true, DataBase::SINGLE_RECORD );
		if( isset( $r ) ){
			if( !empty( $r['iDiscountCoupon'] ) ){
				$r = array_merge( $r, $oDB->executeSQL( 'SELECT `sCode` AS `discount_coupon_code`, `sDiscountValue` AS `sDiscountCouponValue`, `iProducer` AS `iDiscountCouponProducer` FROM `' . $oDB->throwTableName( DB_DISCOUNT_COUPONS ) . '` WHERE `iDiscountCoupon` = ' . intval( $r['iDiscountCoupon'] ) . ' AND `iUsed` < 1 AND `iTimeTo` > ' . time( ), true, DataBase::SINGLE_RECORD ) );
			}
			return $r;
		} else{
			return Array( );
		}
	}
	function saveTempOrderInfo( $aForm ){
		$oDB = DataBase::getInstance( );
		$aForm['iLastUpdate'] = date( 'Y-m-d H:i:s' );
		$aForm['iCustomer'] = $_SESSION['iCustomer' . LANGUAGE];
		
		if( isset( $aForm['sCarrierPayment'] ) ){
			$aCarrierPaymentData = explode( ';', $aForm['sCarrierPayment'] );
			$aForm['iCarrier'] = $aCarrierPaymentData[0];
			$aForm['iPayment'] = $aCarrierPaymentData[1];
			$aCarrier = $this->throwCarrier( $aForm['iCarrier'] );
			if( isset( $aCarrier['iOuterCarrier'] ) ){
				$aForm['iCarrierSystem'] = $aCarrier['iOuterCarrier'];
			} else{
				$aForm['iCarrierSystem'] = false;
			}
		}
		if( isset( $aForm['discount_coupon_code'] ) ){
			$aForm['iDiscountCoupon'] = $oDB->executeSQL( 'SELECT `iDiscountCoupon` FROM `' . $oDB->throwTableName( DB_DISCOUNT_COUPONS ) . '` WHERE `sCode` = \'' . $oDB->escape( $aForm['discount_coupon_code'] ) . '\' AND `iUsed` < 1 AND `iTimeTo` > ' . time( ), true, DataBase::SINGLE_VALUE );
		}
		$oDB->save( DB_ORDERS_TEMP_INFO, $aForm, 'iCustomer' );
	}
	function throwCountriesArray( $sIndex = 'iCountry', $sValue = 'sCountry', $bAllCountries = false ){
		$oDB = DataBase::getInstance( );
		if( $bAllCountries ){
			return $oDB->throwFileArraySmall( DB_COUNTRIES, $sIndex, $sValue, $sValue );
		} else{
			$iStatus = throwStatus( );
			$sql = 'SELECT DISTINCTROW `countries`.`' . $sIndex . '`, `countries`.`' . $sValue . '`
								FROM `' . $oDB->throwTableName( DB_COUNTRIES ) . '` AS `countries`
									LEFT JOIN `' . $oDB->throwTableName( DB_CARRIERS_COUNTRIES ) . '` AS `carriers_countries` ON( ( `carriers_countries`.`iCountry` = 0 ) OR ( `carriers_countries`.`iCountry` = `countries`.`iCountry` ) )
									LEFT JOIN `' . $oDB->throwTableName( DB_CARRIERS ) . '` AS `carriers` USING( `iCarrier` )
								WHERE `carriers`.`iStatus` >= \'' . $iStatus . '\'
								ORDER BY `' . $sValue . '` ASC';
			$aCountries = $oDB->executeSQL( $sql, true );
			$aResult = Array( );
			foreach( $aCountries as $aData ){
				$aResult[$aData[$sIndex]] = $aData[$sValue];
			}
			return $aResult;
		}
	}
	function throwCountry( $iCountry, $sIndex = null ){
		$oDB = DataBase::getInstance( );
		if( !isset( $sIndex ) ) return $oDB->throwData( DB_COUNTRIES, $iCountry, 'iCountry' );
		else{
			$aCountry = $oDB->throwData( DB_COUNTRIES, $iCountry, 'iCountry' );
			return $aCountry[$sIndex];
		}
	}
	function throwCarrierPaymentPrice( $iPayment, $iCarrier, $fOrderSummary ){
		$oDB = & DataBase::getInstance( );
		$aCarrier = $this->throwCarrier( $iCarrier );
		$fFreeDelivery = $oDB->executeSQL( 'SELECT `fFreeDelivery` FROM `' . $oDB->throwTableName( DB_CARRIERS_PAYMENTS ) . '` WHERE `iCarrier` = ' . intval( $iCarrier ) . ' AND `iPayment` = ' . intval( $iPayment ) . ' LIMIT 1', true, DataBase::SINGLE_VALUE );
		if( !empty( $fFreeDelivery ) && ( $fOrderSummary >= $fFreeDelivery ) ){
			$this->bFreeDelivery = true;
		}
		$fCarrierPrice = $this->countCarrierPrice( $iCarrier );
		$sPaymentPrice = $this->countPaymentPrice( $this->throwPaymentCarrierPrice( $iCarrier, $iPayment ) );
		return generatePrice( $fCarrierPrice, $sPaymentPrice, $fOrderSummary );
	}
	function addOrder( ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$oDB->executeSQL( 'START TRANSACTION WITH CONSISTENT SNAPSHOT' );
		$aOrder = $this->getTempOrderInfo( );
		$oDB->executeSQL( 'UPDATE `' . $oDB->throwTableName( DB_USER_ACTIONS ) . '` SET `sEmail` = \'' . $oDB->escape( $aOrder['sEmail'] ) . '\' WHERE `sEmail` IS NULL AND `iCustomer` = ' . $_SESSION['iCustomer' . LANGUAGE] );
		$this->generateBasket( );
		if( $_SESSION['fOrderSummary' . LANGUAGE] < $GLOBALS['config']['min_products_summary'] ){
			header( 'Location: ' . $GLOBALS['oPage']->getLinkName( $GLOBALS['config']['basket_page'] ) . '?mes=too_small_order' );
			exit;
		}
		if( $_SESSION['iOrderQuantity' . LANGUAGE] < $GLOBALS['config']['min_products_count'] ){
			header( 'Location: ' . $GLOBALS['oPage']->getLinkName( $GLOBALS['config']['basket_page'] ) . '?mes=too_small_order_quantity' );
			exit;
		}
		$aOrder = changeMassTxt( $aOrder, 'H', Array( 'sComment', 'LenHNds' ) );
		$aOrder['iTime'] = time( );
		$aOrder['sIp'] = $_SERVER['REMOTE_ADDR'];
		$aOrder['iStatus'] = 1;
		$aOrder['sLanguage'] = LANGUAGE;
		
		if( isset( $aOrder['sPesel'] ) && $aOrder['sPesel'] != '' ){
			$aOrder['sPesel'] = preg_replace( '/[^0-9]+/', '', $aOrder['sPesel'] );
			if( !preg_match( '/^[0-9]{11}$/', $aOrder['sPesel'] ) ){
				header( 'Location: ' . $GLOBALS['oPage']->getLinkName( $GLOBALS['config']['basket_page'] ) . '?mes=pesel_error' );
				exit;
			}
		}
		
		if( isset( $aOrder['sPhone'] ) && $aOrder['sPhone'] != '' ){
			$aOrder['sPhone'] = preg_replace( '/[^+A-Za-z0-9]+/', '', $aOrder['sPhone'] );
			if( !preg_match( '/^\\+?[a-zA-Z0-9\\s]+$/', $aOrder['sPhone'] ) ){
				header( 'Location: ' . $GLOBALS['oPage']->getLinkName( $GLOBALS['config']['basket_page'] ) . '?mes=phone_error' );
				exit;
			}
		}
		
		if( isset( $aOrder['sNip'] ) && $aOrder['sNip'] != '' ){
			$aOrder['sNip'] = preg_replace( '/[^A-Za-z0-9]+/', '', $aOrder['sNip'] );
			if( !preg_match( '/^[a-zA-Z]{0,3}\\s*[0-9]{10}$/', $aOrder['sNip'] ) ){
				header( 'Location: ' . $GLOBALS['oPage']->getLinkName( $GLOBALS['config']['basket_page'] ) . '?mes=nip_error' );
				exit;
			}
		}
		
		$aOrder['sStreet'] = str_replace( ',', ' ', $aOrder['sStreet'] );
		
		$aCarrier = $this->throwCarrier( $aOrder['iCarrier'] );
		$aPayment = $this->throwPayment( $aOrder['iPayment'] );
		$aOrder['sCarrierName'] = $aCarrier['sName'];
		$fFreeDelivery = $oDB->executeSQL( 'SELECT `fFreeDelivery` FROM `' . $oDB->throwTableName( DB_CARRIERS_PAYMENTS ) . '` WHERE `iCarrier` = ' . intval( $aOrder['iCarrier'] ) . ' AND `iPayment` = ' . intval( $aOrder['iPayment'] ) . ' LIMIT 1', true, DataBase::SINGLE_VALUE );
		if( !empty( $fFreeDelivery ) && ( $_SESSION['fOrderSummary' . LANGUAGE] >= $fFreeDelivery ) ){
			$this->bFreeDelivery = true;
		}
		$aOrder['fCarrierPrice'] = $this->countCarrierPrice( $aCarrier['iCarrier'] );
		$aOrder['iCarrier'] = $aCarrier['iCarrier'];
		$aOrder['sPaymentName'] = $aPayment['sName'];
		$aOrder['iPayment'] = $aPayment['iPayment'];
		if( isset( $_SESSION['iUserClient'] ) ){
			$aOrder['iUser'] = $_SESSION['iUserClient'];
		} else{
			$bAnonymousUser = true;
			$aOrder['sLogin'] = 'anonymous';
			$aOrder['iUser'] = saveUser( $aOrder, null, true );
		}
		if( !empty( $fFreeDelivery ) && ( $_SESSION['fOrderSummary' . LANGUAGE] >= $fFreeDelivery ) ){
			$aOrder['sPaymentPrice'] = 0;
		} else{
			$aOrder['sPaymentPrice'] = $this->countPaymentPrice( $this->throwPaymentCarrierPrice( $aOrder['iCarrier'], $aOrder['iPayment'] ) );
		}
		if( isset( $aOrder['iUser'] ) ){
			$aFields = Array( 'sFirstName', 'sLastName', 'sCompanyName', 'sStreet', 'sZipCode', 'sCity', 'iCountry', 'sPhone', 'sNip' );
			foreach( $aFields as $sField ){
				$sql = 'UPDATE `' . $oDB->throwTableName( DB_USERS ) . '` SET `' . $sField . '` = \'' . $oDB->escape( $aOrder[$sField] ) . '\' WHERE ( `' . $sField . '` IS NULL OR `' . $sField . '` = \'\' ) AND `iUser` = \'' . $aOrder['iUser'] . '\' LIMIT 1';
				$oDB->executeSQL( $sql );
			}
		}
		if( isset( $aCarrier['iOuterCarrier'] ) ){
			$aOrder['iCarrierSystem'] = $aCarrier['iOuterCarrier'];
		} else{
			$aOrder['iCarrierSystem'] = false;
		}
		if( isset( $aPayment['iOuterSystem'] ) ){
			$aOrder['iPaymentSystem'] = $aPayment['iOuterSystem'];
		} else{
			$aOrder['iPaymentSystem'] = null;
		}
		$aOrder['mPaymentChannel'] = null;
		if( ( $aOrder['iInvoice'] > 0 ) && ( $GLOBALS['config']['invoices_automatic_invoice_numbers'] ) ){
			require_once( DIR_ENGINE . 'common-admin.php' );
			$aOrder['sInvoiceId'] = $GLOBALS['config']['invoices_next_number'] . '/' . date( 'm/Y' );
			saveVariables( Array( 'invoices_next_number' => $GLOBALS['config']['invoices_next_number'] + 1 ), DB_CONFIG_LANG );
		}
		// SPRAWDZANIE POPRAWNOŚCI KOSZYKA W MOMENCIE ZAKUPU
		$sql = 'SELECT `pv`.`iProductVariant`, `iProduct`, `iQuantity` FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` `ot` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` `pv` USING( `iProductVariant` ) WHERE `iCustomer` = \'' . $oDB->escape( $_SESSION['iCustomer' . LANGUAGE] ) . '\'';
		$aStockInfo = $oDB->executeSQL( $sql, true );
		$iDays = $aOrder['iDays'];
		$aErrors = null;
		if( is_array( $aStockInfo ) && !empty( $aStockInfo ) ){
			$aAvailabilities = $oDB->throwFileArraySmall( DB_PRODUCTS_AVAILABLE, 'iAvailable', 'iDays', 'iAvailable', Array( 'iCanBuy' => 1 ) );
			foreach( $aStockInfo as &$aPV ){
				$from_where = 'FROM `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` WHERE `iProductVariant` = ' . $aPV['iProductVariant'];
				$sql = 'SELECT `iStock`, `iAvailable` FROM ( SELECT `iStock`, `TiAvailableOnStock` AS `iAvailable` ' . $from_where . ' AND `iStock` > 0 UNION ALL SELECT NULL, `TiAvailableOutOfStock` AS `iAvailable` ' . $from_where . ' ) AS `pva` ORDER BY `iAvailable` ASC';

				foreach( $oDB->executeSQL( $sql, true ) as $aAvail ){
					if( !isset( $aAvail['iStock'] ) || $aAvail['iStock'] >= $aPV['iQuantity'] ){
						if( !isset( $aAvailabilities[$aAvail['iAvailable']] ) ) $aErrors[0][] = Array( 'iProductVariant' => $aPV['iProductVariant'], 'iProduct' => $aPV['iProduct'] );
						if( !isset( $iDays ) || $iDays < $aAvailabilities[$aAvail['iAvailable']] ) $aErrors[1][] = Array( 'iProductVariant' => $aPV['iProductVariant'], 'iProduct' => $aPV['iProduct'], 'iDays' => $aAvailabilities[$aAvail['iAvailable']] );
						break;
					} else{
						$aPV['iQuantity'] -= $aAvail['iStock'];
					}
				}
			}
		}
		if( !empty( $aErrors ) ){
			return $aErrors;
		}
//		if( !empty( $GLOBALS['config']['younity_id'] ) && !empty( $_COOKIE['cn'] ) ){
//			$aOrder['sYounityId'] = $_COOKIE['cn'];
//		}
		// *****************************************************
		
		if( isset( $_COOKIE['refererinfo'] ) ){
			$refererinfo = json_decode( base64_decode( $_COOKIE['refererinfo'] ), true );
			$aOrder['sOuterReferer'] = $refererinfo['r'];
			$aOrder['sOuterRefererDomain'] = preg_replace( '|/.*$|', '', $refererinfo['r'] );
			$aOrder['iRefererTime'] = $refererinfo['t'];
			$aOrder['sRefererUri'] = $refererinfo['u'];
		}
		$aOrder['iOrder'] = @chack_ordar( ) + $oDB->save( DB_ORDERS, $aOrder );
		if( $bAnonymousUser ){
			$aOrder['sLogin'] = 'anonymous' . $aOrder['iOrder'];
			saveUser( $aOrder, null, true ); //Dopisanie id zamówienia do loginu
		}
		$oDB->save( DB_ORDERS_COMMENTS, $aOrder );
		foreach( $this->aProducts as $aProduct ){
			$aProduct['iOrder'] = $aOrder['iOrder'];
			if( substr( $aOrder['sDiscountCouponValue'], -1 ) == '%' ){
				if( $aProduct['iPriceTimeFrom'] <= time() && $aProduct['iPriceTimeTo'] >= time() ){
					//Dla promocji czasowych nie działają kody
					$aProduct['iDiscount'] = 0;
				} elseif( !empty( $aOrder['iDiscountCouponProducer'] ) && $aProduct['iProducer'] != $aOrder['iDiscountCouponProducer'] ){
					//Dla innych producentów też
					$aProduct['iDiscount'] = 0;
				} else{
					$aProduct['iDiscount'] = 1;
				}
			}
			
			$iOrderProduct = $oDB->save( DB_ORDERS_PRODUCTS, $aProduct );
			$oDB->executeSQL( 'UPDATE `' . $oDB->throwTableName( DB_PRODUCTS ) . '` SET `iPriceTimeCount` = `iPriceTimeCount` + ' . $aProduct['iQuantity'] . ' WHERE `iProduct` = ' . $aProduct['iProduct'] );

			$from_where = 'FROM `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` WHERE `iProductVariant` = ' . $aProduct['iProductVariant'];
			$sql = 'SELECT `iStock`, `iStore` FROM ( SELECT `iStore`, `iStock`, `TiAvailableOnStock` AS `iAvailable`, 1 AS `onstock` ' . $from_where . ' AND `iStock` > 0 UNION ALL SELECT `iStore`, NULL, `TiAvailableOutOfStock` AS `iAvailable`, 0 AS `onstock` ' . $from_where . ' ) AS `pva` INNER JOIN `' . $oDB->throwTableName( DB_STORES ) . '` USING( `iStore` ) ORDER BY `iAvailable` ASC, `iPriority` ASC, `onstock` DESC'; //Jeśli dostępność na stanie i poza stanem w magazynach o tym samym priorytecie jest taka sama - najpierw bierzemy ze stanu magazynowego, dopiero w dalszej kolejności spoza

			foreach( $oDB->executeSQL( $sql, true ) as $aAvail ){
				if( !isset( $aAvail['iStock'] ) ){
					$iStoreQuantity = $aProduct['iQuantity'];
				} else{
					$iStoreQuantity = min( $aProduct['iQuantity'], $aAvail['iStock'] );
				}
				if( $iStoreQuantity > 0 ){
					$oDB->executeSQL( 'UPDATE `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` SET `iStock` = `iStock` - ' . $iStoreQuantity . ' WHERE `iProductVariant` = ' . $aProduct['iProductVariant'] . ' AND `iStore` = ' . $aAvail['iStore'] );
					if( $aInfo = $oDB->throwData( DB_ORDERS_PRODUCTS_STORES, Array( 'iOrderProduct' => $iOrderProduct, 'iStore' => $aAvail['iStore'] ), Array( 'iOrderProduct', 'iStore' ) ) ){
						$oDB->executeSQL( 'UPDATE  `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS_STORES ) . '` SET `iQuantity` = `iQuantity` + ' . intval( $iStoreQuantity ) . ' WHERE `iElement` = ' . intval( $aInfo['iElement'] ) );
					} else{
						$oDB->save( DB_ORDERS_PRODUCTS_STORES, Array( 'iOrderProduct' => $iOrderProduct, 'iStore' => $aAvail['iStore'], 'iQuantity' => $iStoreQuantity ) );
					}
				}
				if( !isset( $aAvail['iStock'] ) || $aAvail['iStock'] >= $aProduct['iQuantity'] ){
					$aProduct['iQuantity'] = 0;
					break;
				} else{
					$aProduct['iQuantity'] -= $aAvail['iStock'];
				}
			}
		}
		if( !empty( $aOrder['iDiscountCoupon'] ) ){
			$oDB->update( DB_DISCOUNT_COUPONS, Array( 'iUsed' => 1 ), Array( 'iDiscountCoupon' => $aOrder['iDiscountCoupon'], 'iUsed' => 0 ) );
		}
		$oDB->deleteInFile( DB_ORDERS_TEMP, $_SESSION['iCustomer' . LANGUAGE], 'iCustomer' );
		$oDB->deleteInFile( DB_ORDERS_TEMP_INFO, $_SESSION['iCustomer' . LANGUAGE], 'iCustomer' );
		$_SESSION['aProductsBasket'] = null;
		$_SESSION['iOrderQuantity' . LANGUAGE] = 0;
		$_SESSION['fOrderSummary' . LANGUAGE] = null;

		/*******************************************************************************************************************/
		/*                                                     YOUNITY                                                     */
		/*******************************************************************************************************************/
		if( !empty( $GLOBALS['config']['younity_id'] ) && !empty( $_COOKIE['cn'] ) ){
			$amount = sprintf( '%01.2f', $aOrder['fOrderSummary'] ) * 100;
			$currency = $GLOBALS['config']['currency_symbol_ISO'];
			$shopId = $GLOBALS['config']['younity_id'];
			$cardId = $_COOKIE['cn'];
			$transactionId = $aOrder['iOrder'];
			$key = $GLOBALS['config']['younity_token'];
			$hash = strtoupper( hash( 'sha256', "$shopId|$amount|$currency|$cardId|$transactionId|$key" ) );
			$url = "http://younityterminalapi.azurewebsites.net/" . ( $GLOBALS['config']['younity_sandbox'] ? 'sandbox/' : null ) . "transaction/register/$amount/$currency/$shopId/$cardId/$transactionId/$hash";
			file_get_contents( $url );
		}
		/*******************************************************************************************************************/

		$oDB->executeSQL( 'COMMIT' );
		return $aOrder['iOrder'];
	}
	/**
	 * !!!!! UWAGA !!!!! Statusy powyżej 50 są statusami anulowanymi, co pociąga za sobą konsekwencje!
	 * Przy ustawianiu statusu anulowanego dla zamówienia, które miało status nieanulowany stany magazynowe zamówionych produktów są zwiększane
	 * o ilość tych produktów w zamówieniu!
	 * Przy ustawianiu statusu nieanulowanego dla zamówienia, które miało status anulowany stany magazynowe zamówionych produktów są zmniejszane
	 * o ilość tych produktów w zamówieniu!
	 */
	function throwStatus( $iStatus = null ){
		global $lang;
		$aStatus = Array( 1 => $lang['Order_status_New'], 17 => $lang['Order_status_Awaiting_credit'], 3 => $lang['Order_status_Awaiting_payment'], 4 => $lang['Order_status_Paid'], 5 => $lang['Order_status_Paid_on_delivery'], 6 => $lang['Order_status_Completing'], 21 => $lang['Order_status_Individual_time'], 22 => $lang['Order_status_Outer_store'], 23 => $lang['Order_status_advance_awaiting'], 24 => $lang['Order_status_advance'], 14 => $lang['Order_status_Ready_to_post'], 2 => $lang['Order_status_Post'], 7 => $lang['Order_status_Collection'], 8 => $lang['Order_status_Finished'], 51 => $lang['Order_status_Canceled_by_customer'], 52 => $lang['Order_status_Canceled_by_shop'], 53 => $lang['Order_status_Canceled_by_payment_system'], 9 => $lang['Order_status_Exchange'], 10 => $lang['Order_status_Back'], 11 => $lang['Order_status_Complaint'], 12 => $lang['Order_status_Missed_delivery'], 13 => $lang['Order_status_Legal_action'], 15 => $lang['Order_status_Contact'], 16 => $lang['Order_status_Remind'] );
		return isset( $iStatus ) ? $aStatus[$iStatus] : $aStatus;
	}
	function throwInvoice( $iInvoice = null ){
		global $lang;
		global $config;
		$aInvoice = Array();
		if( $config['invoice_type_1'] ){ $aInvoice[0] = $lang['Invoice_Bill']; }
		if( $config['invoice_type_2'] ){ $aInvoice[1] = $lang['Invoice_VAT']; }
		if( $config['invoice_type_3'] ){ $aInvoice[2] = $lang['Invoice_Name']; }
		return isset( $iInvoice ) ? $aInvoice[$iInvoice] : $aInvoice;
	}
	function throwOrder( $iOrder, $bSkipProducts = false ){
		$oDB = & DataBase::getInstance( );
		$aData = $oDB->throwDataFromFiles( Array( DB_ORDERS, DB_ORDERS_COMMENTS ), $iOrder, 'iOrder' );
		if( isset( $aData ) ){
			$aData['fOrderSummary'] = $oDB->executeSQL( 'SELECT SUM( `fPrice` * `iQuantity` ) FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` WHERE `iOrder` = ' . intval( $iOrder ), true, DataBase::SINGLE_VALUE );
			$aData['fProductsSummary'] = $aData['fOrderSummary'];
			$aData['fGrossSummary'] = $aData['fOrderSummary'];
			$aData['sInvoice'] = $this->throwInvoice( $aData['iInvoice'] );
			$aData['fPaymentCarrierPrice'] = generatePrice( $aData['fCarrierPrice'], $aData['sPaymentPrice'], $aData['fGrossSummary'] );
			if( isset( $aData['iGift'] ) && ( $aData['iGift'] == 1 ) ){
				$aData['sGift'] = '+ ' . $GLOBALS['lang']['Packing_as_gift'];
			}
			$aData['sPaymentCarrierPrice'] = displayPrice( $aData['fPaymentCarrierPrice'] );
			$aData['sDate'] = date( 'Y-m-d H:i', $aData['iTime'] );
			if( !empty( $aData['iPaymentSystem'] ) && isset( $GLOBALS['aOuterPaymentOption'][$aData['iPaymentSystem']] ) ){
				$aData['sPaymentChannel'] = $GLOBALS['aOuterPaymentOption'][$aData['iPaymentSystem']];
			} else $aData['sPaymentChannel'] = '-';
			$this->aOrders[$iOrder] = $aData;
			return $aData;
		}
	}
	function throwSavedOrderId( $sOrder ){
		$oDB = & DataBase::getInstance( );
		$sql = 'SELECT `iCustomer` FROM `' . $oDB->throwTableName( DB_ORDERS_TEMP ) . '` WHERE MD5( `iCustomer` ) = \'' . $oDB->escape( $sOrder ) . '\'';
		$sResult = $oDB->executeSQL( $sql, true, DataBase::SINGLE_VALUE );
		if( $sResult ) return $sResult;
		else return null;
	}
	private function throwPaymentCarrierPrice( $iCarrier, $iPayment ){
		$oDB = & DataBase::getInstance( );
		$aData = $oDB->throwData( DB_CARRIERS_PAYMENTS, Array( 'iCarrier' => $iCarrier, 'iPayment' => $iPayment ), Array( 'iCarrier', 'iPayment' ) );
		return $aData['sPrice'];
	}
	function throwCarriersJSON( ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$aCarriers = $oDB->throwFileArray( DB_CARRIERS );
		$aReturn = Array( );
		foreach( $aCarriers as $aCarrier ){
			if( $aCarrier['iStatus'] >= $iStatus ) $aReturn[] = Array( 'id' => $aCarrier['iCarrier'], 'name' => $aCarrier['sName'] );
		}
		return json_encode( $aReturn );
	}
	function throwPaymentsJSON( ){
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$aPayments = $oDB->throwFileArray( DB_PAYMENTS );
		$aReturn = Array( );
		foreach( $aPayments as $aPayment ){
			if( $aPayment['iStatus'] >= $iStatus ) $aReturn[] = Array( 'id' => $aPayment['iPayment'], 'name' => $aPayment['sName'] );
		}
		return json_encode( $aReturn );
	}
	function throwCarriersPayments( $iCountry = null, $iRegion = null, &$aPaczkomaty, $bBlockCOD = false ){
		$this->generateBasket( );
		$oDB = & DataBase::getInstance( );
		$iStatus = throwStatus( );
		$sql = 'SELECT `iCarrier`, `sName`, `iOuterCarrier`
							FROM `' . $oDB->throwTableName( DB_CARRIERS ) . '` as `carriers`';
		if( ( isset( $iCountry ) && is_numeric( $iCountry ) && !empty( $iCountry ) ) || ( isset( $iRegion ) && is_numeric( $iRegion ) && !empty( $iRegion ) ) ){
			$sql.= ' LEFT JOIN `' . $oDB->throwTableName( DB_CARRIERS_COUNTRIES ) . '` USING( `iCarrier` )';
		}
		$sql.= ' WHERE `iStatus` >= \'' . $iStatus . '\'';
		if( isset( $iCountry ) && is_numeric( $iCountry ) && !empty( $iCountry ) ){
			$sql.= ' AND ( `iCountry` = \'' . $iCountry . '\' OR `iCountry` IS NULL )';
		}
		if( isset( $iRegion ) && is_numeric( $iRegion ) && !empty( $iRegion ) ){
			$sql.= ' AND ( `iRegion` = \'' . $iRegion . '\' OR `iRegion` IS NULL )';
		}
		$aCarriers = $oDB->executeSQL( $sql, true );
		$bFreeDeliveryOld = $this->bFreeDelivery;
		foreach( $aCarriers as $iIndex => &$aCarrier ){
			# Paczkomaty
			if( $aCarrier['iOuterCarrier'] == CARRIER_PACZKOMATY ){
				$aPaczkomaty[] = $aCarrier['iCarrier'];
			}
			$sql = 'SELECT `iPayment`, `sName`, `iOuterSystem`, `sPrice`, `fFreeDelivery`
								FROM `' . $oDB->throwTableName( DB_PAYMENTS ) . '` AS `payments`
									LEFT JOIN `' . $oDB->throwTableName( DB_CARRIERS_PAYMENTS ) . '` AS `c_p` USING( `iPayment` )
								WHERE `iStatus` >= \'' . $iStatus . '\' AND `iCarrier` = \'' . $aCarrier['iCarrier'] . '\'';
			$aCarrier['aPayments'] = $oDB->executeSQL( $sql, true );
			$this->bFreeDelivery = $bFreeDeliveryOld;
			$aCarrier['fPrice'] = $this->countCarrierPrice( $aCarrier['iCarrier'] );
			if( $aCarrier['fPrice'] !== null ){
				foreach( $aCarrier['aPayments'] as $iIndex2 => &$aPayment ){
					foreach( $this->aProducts as $aProductVariant ){
						if( !empty( $aProductVariant['aBlockedPayments'][$aPayment['iPayment']] ) ){
							unset( $aCarrier['aPayments'][$iIndex2] );
							continue 2;
						}
					}
					if( ( $this->fOrderSummary < LR_MIN_PRICE && $aPayment['iOuterSystem'] == 8 ) || ( $bBlockCOD && $aPayment['iOuterSystem'] == PAID_ON_DELIVERY ) ){
						unset( $aCarrier['aPayments'][$iIndex2] );
						continue;
					}
					if( !empty( $aPayment['fFreeDelivery'] ) && ( $this->fOrderSummary >= $aPayment['fFreeDelivery'] ) ){
						$this->bFreeDelivery = true;
					} else{
						$this->bFreeDelivery = $bFreeDeliveryOld;
					}
					$aPayment['fPrice'] = normalizePrice( generatePrice( ( $this->bFreeDelivery == true ? 0 : $aCarrier['fPrice'] ), $this->countPaymentPrice( $aPayment['sPrice'] ), $this->fOrderSummary ) );
					$aPayment['sPrice'] = displayPrice( $aPayment['fPrice'] );
				}
				if( empty( $aCarrier['aPayments'] ) ){
					unset( $aCarriers[$iIndex] );
				}
			} else{
				unset( $aCarriers[$iIndex] );
			}
		}
		return $aCarriers;
	}
	function throwCarrier( $iCarrier ){
		$oDB = & DataBase::getInstance( );
		return $oDB->throwData( DB_CARRIERS, $iCarrier, 'iCarrier' );
	}
	function throwPayment( $iPayment ){
		$oDB = & DataBase::getInstance( );
		$aData = $oDB->throwData( DB_PAYMENTS, $iPayment, 'iPayment' );
		if( isset( $aData ) && is_array( $aData ) ){
			$aData['sDescription'] = changeTxt( $aData['sDescription'], 'Ndsnl' );
			$aData['sEmailDescription'] = changeTxt( $aData['sEmailDescription'], 'Ndsnl' );
			return $aData;
		} else return null;
	}
	function sendEmailWithOrderDetails( $iOrder ){
		require_once( $GLOBALS['config']['dir_libraries'] . 'emogrifier.php' );
		$oTpl = & TplParser::getInstance( );
		$content = null;
		$aData = $this->throwOrder( $iOrder );
		if( empty( $aData ) ){
			return false;
		}
		$aData['sCountry'] = $this->throwCountry( $aData['iCountry'], 'sCountry' );
		if( isset( $aData['iPostalCountry'] ) ) $aData['sPostalCountry'] = $this->throwCountry( $aData['iPostalCountry'], 'sCountry' );
		$aUrl = parse_url( 'http' . ( !empty( $_SERVER['HTTPS'] ) && $_SERVER['HTTPS'] !== 'off' ? 's' : null) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
		$aUrl['path'] = preg_replace( '|/[^/]*$|', '/', $aUrl['path'] );
		$oTpl->assign( 'aUrl', $aUrl );
		$aData = array_merge( $aData, $this->throwProducts( $iOrder ) );
		$aData['sComment'] = str_replace( '|n|', "\r\n", $aData['sComment'] );
		$aData['sOrderKey'] = md5( PK . $iOrder );
		$aData['sOrderSummaryPageLink'] = $GLOBALS['oPage']->getLinkName( $GLOBALS['config']['order_summary_page'] ) . '?';
		$aData['aPayment'] = $this->throwPayment( $aData['iPayment'] );
		$aData['aCarrier'] = $this->throwCarrier( $aData['iCarrier'] );
		$aDescriptionWords = Array( '%sSummary%' => $aData['sOrderSummary'], '%sCarrierName%' => $aData['aCarrier']['sName'], '%iOrder%' => $aData['iOrder'], '%sName%' => $aData['sFirstName'] . ' ' . $aData['sLastName'], '%sEmail%' => $aData['sEmail'], '%sInvoice%' => $aData['sInvoice'], '%sBankName%' => $GLOBALS['config']['invoices_Bank'], '%sBankAccount%' => $GLOBALS['config']['invoices_Account'] );
		$aData['aPayment']['sEmailDescription'] = str_replace( array_keys( $aDescriptionWords ), array_values( $aDescriptionWords ), parseSpecials( $aData['aPayment']['sEmailDescription'] ) );
		if( !empty( $aData['aPayment']['sEmailDescription'] ) ) $aData['sPaymentDescription'] = strip_tags( preg_replace( '/<br\\s*?\\/?>/', "\n", $aData['aPayment']['sEmailDescription'] ) );
		$oTpl->assign( 'aOrder', $aData );
		$aSend['sMailContent'] = $oTpl->parse( 'orders_email.tpl' );
		if( !empty( $aData['aPayment']['sEmailDescription'] ) ) $aData['sPaymentDescription'] = $aData['aPayment']['sEmailDescription'];
		$oTpl->assign( 'aOrder', $aData );
		$sHtml = $oTpl->parse( 'orders_email_html.tpl' );
		$sCss = file_get_contents( $GLOBALS['config']['dir_templates'] . 'style_orders_email.css' );
		$oEm = new Emogrifier( $sHtml, $sCss );
		$aSend['sHtmlContent'] = $oEm->emogrify( );
		$aSend['sTopic'] = $GLOBALS['config']['website_name'] . ' - ' . $GLOBALS['lang']['Order_info_title'] . ' (ID ' . $aData['iOrder'] . ')';
		$aAttachments = null;
		if( !empty( $GLOBALS['config']['rules_page'] ) ){
			$oDB =& DataBase::getInstance();
			$aAttachments = $oDB->executeSQL( 'SELECT CONCAT( \'' . $oDB->escape( DIR_FILES ) . '\', `sFileName` ) FROM `' . $oDB->throwTableName( DB_PAGES_FILES ) . '` WHERE `iPage` = ' . intval( $GLOBALS['config']['rules_page'] ), true, DataBase::SINGLE_COLUMN );
		}
		$aSend['sSender'] = $GLOBALS['config']['orders_email'];
		sendEmail( $aSend, $aData['sEmail'], $aAttachments );
		$aSend['sSender'] = $aData['sEmail'];
		sendEmail( $aSend, $GLOBALS['config']['orders_email'], $aAttachments );
	}
	function throwProductsBasket( $fPaymentCarrierPrice = 0, &$fSummary = 0 ){
		$oDB = & DataBase::getInstance( );
		$oFile = Files::getInstance( );
		if( !isset( $this->aProducts ) ){
			$this->generateBasket( );
		}
		$fSummary = 0;
		$fSummaryForDiscount = 0;
		$aStockProducts = Array( );
		$aProducts = $this->aProducts;
		if( is_array( $aProducts ) ){
			$aData = $this->getTempOrderInfo( );
			foreach( $aProducts as $mKey => &$aProduct ){
				$aProduct = array_merge( $GLOBALS['oProduct']->throwProduct( $aProduct['iProduct'] ), $aProduct );
				if( isset( $aProduct ) ){
					$aStockProducts[] = $aProduct['iProductVariant'];
					$aProduct['sSummary'] = displayPrice( normalizePrice( $aProduct['fSummary'] ) );
					$aProduct['sPrice'] = displayPrice( normalizePrice( $aProduct['fPrice'] ) );
					$aProduct['sLinkDelete'] = defined( 'CUSTOMER_PAGE' ) ? $GLOBALS['aData']['sLinkName'] . '?iProductDelete=' . $aProduct['iProductVariant'] : null;
					foreach( $aProduct['aVariants'] as $aVariant ) if( $aVariant['iProductVariant'] == $aProduct['iProductVariant'] ) $aProduct['aVariant'] = $aVariant;
					if( isset( $aProduct['aVariant']['iFile'] ) ){
						$aProduct['aDataImage'] = $oFile->throwFile( 2, $aProduct['aVariant']['iFile'] );
					}
					elseif( isset( $oFile->aImagesDefault[2][$aProduct['iProduct']] ) ){
						$aProduct['aDataImage'] = $oFile->aImagesDefault[2][$aProduct['iProduct']];
					}
					$fSummary+= $aProduct['fSummary'];
					if( substr( $aData['sDiscountCouponValue'], -1 ) == '%' ){
						if( $aProduct['iPriceTimeFrom'] <= time() && $aProduct['iPriceTimeTo'] >= time() ){
							//Dla promocji czasowych nie działają kody
							$aProduct['bDiscount'] = false;
							$aProduct['sDiscountInfo'] = $GLOBALS['lang']['Discount_not_affected_promotion'];
						} elseif( !empty( $aData['iDiscountCouponProducer'] ) && $aProduct['iProducer'] != $aData['iDiscountCouponProducer'] ){
							//Dla innych producentów też
							$aProduct['bDiscount'] = false;
							$aData['aDiscountProducer'] = throwProducer( $aData['iDiscountCouponProducer'] );
							$aProduct['sDiscountInfo'] = str_replace( '%Producer%', $aData['aDiscountProducer']['sName'], $GLOBALS['lang']['Discount_not_affected_producer'] );
						} else{
							$fSummaryForDiscount+= $aProduct['fSummary'];
							$aProduct['bDiscount'] = true;
						}
					}
				} else{
					unset( $aProducts[$mKey] );
				}
			}
		} else{
			return false;
		}
		
		if( is_array( $aStockProducts ) && !empty( $aStockProducts ) ){
			$aAvailabilities = $oDB->throwFileArraySmall( DB_PRODUCTS_AVAILABLE, 'iAvailable', 'sText', 'iAvailable', Array( 'iCanBuy' => 1 ) );
			$aAvailabilitiesDays = $oDB->throwFileArraySmall( DB_PRODUCTS_AVAILABLE, 'iAvailable', 'iDays', 'iAvailable', Array( 'iCanBuy' => 1 ) );
			$sql = 'SELECT `pv`.`iProductVariant`, `pv`.`TiStock` AS `iStock`, `pv`.`TiCanBuyOutOfStock` AS `iInfStock`
								FROM `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` AS `pv`
								WHERE `iProductVariant` IN ( ' . implode( ', ', $aStockProducts ) . ' )';
			$aStockInfo = $oDB->executeSQL( $sql, true );
			foreach( $aStockInfo as &$aPV ){
				$from_where = 'FROM `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` WHERE `iProductVariant` = ' . $aPV['iProductVariant'];
				$sql = 'SELECT `iStock`, `iAvailable` FROM ( SELECT `iStock`, `TiAvailableOnStock` AS `iAvailable` ' . $from_where . ' AND `iStock` > 0 UNION ALL SELECT NULL, `TiAvailableOutOfStock` AS `iAvailable` ' . $from_where . ' ) AS `pva` ORDER BY `iAvailable` ASC';
				$aPV['aAvailabilities'] = Array();
				$iStock = 0;
				foreach( $oDB->executeSQL( $sql, true ) as $aAvail ){
					if( !isset( $aAvail['iStock'] ) ){
						$aPV['aAvailabilities'][] = Array( 'iStock' => 'inf', 'sAvailable' => $aAvailabilities[$aAvail['iAvailable']], 'iDays' => $aAvailabilitiesDays[$aAvail['iAvailable']] );
						break;
					} else{
						$iStock += $aAvail['iStock'];
						$aPV['aAvailabilities'][] = Array( 'iStock' => $iStock, 'sAvailable' => $aAvailabilities[$aAvail['iAvailable']], 'iDays' => $aAvailabilitiesDays[$aAvail['iAvailable']] );
					}
				}
			}
			$aData['sStockInfoJSON'] = json_encode( $aStockInfo );
		}
		$aData['fProductsSummary'] = normalizePrice( $fSummary );
		$aData['sProductsSummary'] = displayPrice( $aData['fProductsSummary'] );
		if( $aData['iGift'] ) $fSummary+= $GLOBALS['config']['gift_cost'];
		if( $oDB->executeSQL( 'SELECT COUNT( `iDiscountCoupon` ) FROM `' . $oDB->throwTableName( DB_DISCOUNT_COUPONS ) . '` WHERE `iUsed` < 1 AND `iTimeTo` > UNIX_TIMESTAMP()', true, DataBase::SINGLE_VALUE ) > 0 ){
			//Aktywne kody jednorazowe
			$aData['sDiscountCouponValueJS'] = $aData['sDiscountCouponValue'];
			if( !empty( $aData['iDiscountCouponProducer'] ) ){
				$aProducer = throwProducer( $aData['iDiscountCouponProducer'] );
				$aData['sDiscountCouponProducer'] = $aProducer['sName'];
			}
			if( empty( $aData['sDiscountCouponValue'] ) ){
				$aData['sDiscountCouponValue'] = '0.00';
				$aData['sProductsSummaryDiscount'] = $aData['sProductsSummary'];
			} else{
				if( substr( $aData['sDiscountCouponValue'], -1 ) == '%' ){
					$aData['sProductsSummaryDiscount'] = normalizePrice( $aData['sProductsSummary'] - ( $fSummaryForDiscount * substr( $aData['sDiscountCouponValue'], 0, -1 ) / 100 ) );
					if( !empty( $aData['iDiscountCouponProducer'] ) ){
						$aData['aProducer'] = throwProducer( $aData['iDiscountCouponProducer'] );
						$aData['sDiscountCouponValue'] = '-' . $aData['sDiscountCouponValue'] . ' *' . $aData['aProducer']['sName'];
					} else{
						$aData['sDiscountCouponValue'] = '-' . $aData['sDiscountCouponValue'];
					}
				} else{
					$aData['sProductsSummaryDiscount'] = normalizePrice( $aData['sProductsSummary'] - $aData['sDiscountCouponValue'] );
					$aData['sDiscountCouponValue'] = '-' . normalizePrice( $aData['sDiscountCouponValue'] );
				}
			}
			$aData['bDiscountPanelActive'] = true;
		} else{
			$aData['sProductsSummaryDiscount'] = normalizePrice( $aData['sProductsSummary'] );
			$aData['bDiscountPanelActive'] = false;
		}{
			$aData['fPaymentCarrierPrice'] = $fPaymentCarrierPrice;
			$aData['sPaymentCarrierPrice'] = displayPrice( $aData['fPaymentCarrierPrice'] );
			$aData['fOrderSummary'] = normalizePrice( $aData['sProductsSummaryDiscount'] + $fPaymentCarrierPrice );
			$aData['sOrderSummary'] = displayPrice( $aData['fOrderSummary'] );
		}
		if( $aData['iGift'] ){
			$aData['fOrderSummary']+= $GLOBALS['config']['gift_cost'];
			$aData['sOrderSummary'] = displayPrice( normalizePrice( $aData['fOrderSummary'] ) );
			$aData['sGiftCost'] = displayPrice( normalizePrice( $GLOBALS['config']['gift_cost'] ) );
		}
		$aData['aProducts'] = $aProducts;
		return $aData;
	}
	function listProductsBasketInfo( ){
		if( isset( $_SESSION['aProductsBasket'] ) ){
			$oFile = & Files::getInstance( );
			$aProducts = $_SESSION['aProductsBasket'];
			$fSummary = 0;
			foreach( $aProducts as &$aData ){
				$aData['sSummary'] = normalizePrice( $aData['fSummary'] );
				$aData['sPrice'] = displayPrice( normalizePrice( $aData['fPrice'] ) );
				$sAttributes = !empty( $aData['sAttributes'] ) ? '&amp;sAttributes=' . $aData['sAttributes'] : null;
				if( isset( $aData['iFile'] ) ){
					$aData['aImage'] = $oFile->throwFile( 2, $aData['iFile'] );
				}
				elseif( isset( $oFile->aImagesDefault[2][$aData['iProduct']] ) ){
					$aData['aImage'] = $oFile->aImagesDefault[2][$aData['iProduct']];
				}
				$fSummary+= $aData['fSummary'];
			}
			$fProductsSummary = normalizePrice( $fSummary );
			$sProductsSummary = displayPrice( $fProductsSummary );
			return Array( 'aProducts' => $aProducts, 'sProductsSummary' => $sProductsSummary );
		} else{
			return null;
		}
	}
	function countCarrierPrice( $iCarrier = null ){
		$oDB = DataBase::getInstance();
		$fAdditionalCostPerProducts = 0;
		$bCountByWeight = false;
		$this->sWeightSummary = 0;
		$this->fOrderSummary = 0;
		$this->iExplodeRange = null;
		if( is_array( $this->aProducts ) ) foreach( $this->aProducts as $sProductId => $aData ){
			if( isset( $aData['aCarriersPrices'][$iCarrier] ) ){
				if( $aData['aCarriersPrices'][$iCarrier] === '-' ){
					return null;
				}
				$fAdditionalCostPerProducts+= $aData['aCarriersPrices'][$iCarrier] * $aData['iQuantity'];
			} else{
				if( !empty( $aData['sWeight'] ) ){
					$this->sWeightSummary+= ( $aData['iQuantity'] * $aData['sWeight'] );
				}
				$bCountByWeight = true;
			}
			$this->fOrderSummary+= $aData['fSummary'];
		}
		if( $this->bFreeDelivery || ( isset( $GLOBALS['config']['delivery_free'] ) && is_numeric( $GLOBALS['config']['delivery_free'] ) && $this->fOrderSummary >= $GLOBALS['config']['delivery_free'] ) ){
			$this->bFreeDelivery = true;
			return normalizePrice( $fAdditionalCostPerProducts );
		}
		if( !$bCountByWeight ){
			return normalizePrice( $fAdditionalCostPerProducts );
		} else{
			$sql = 'SELECT `fPrice` FROM `' . $oDB->throwTableName( DB_CARRIERS_PRICES ) . '` WHERE `iCarrier` = ' . intval( $iCarrier ) . ' AND `fMinWeight` <= ' . floatval( $this->sWeightSummary ) . ' AND `fMinSummary` <= ' . floatval( $this->fOrderSummary ) . ' ORDER BY `fMinWeight` DESC, `fMinSummary` DESC LIMIT 1';
			$mPrice = $oDB->executeSQL( $sql, true, DataBase::SINGLE_VALUE );
			return( $mPrice == '-' ) ? null : normalizePrice( $mPrice + $fAdditionalCostPerProducts );
		}
	}
	private function countPaymentPrice( $mPaymentPrice ){
		if( isset( $this->bFreeDelivery ) ) return normalizePrice( 0 );
		else return $mPaymentPrice;
	}
	/**
	 * Changes status and sends e-mail about it to client if neccessary
	 * Moved from orders-admin.php due to autoset status by online payments
	 */
	function changeStatus( $iOrder, $iNewStatus, $iStatusOld = null ){
		require_once( $GLOBALS['config']['dir_libraries'] . 'emogrifier.php' );
		$oDB = DataBase::getInstance( );
		$oDB->executeSQL( 'START TRANSACTION' );
		$aUrl = parse_url( 'http' . ( $_SERVER['HTTPS'] ? 's' : '' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
		if( !empty( $aUrl['path'] ) ){
			$aUrl['path'] = dirname( $aUrl['path'] );
			if( $aUrl['path'] !== "/" ){
				$aUrl['path'] = $aUrl['path'] . '/';
			}
		}
		$this->generateCache( );
		$aOrder = $this->aOrders[$iOrder];
		$sEmail = $aOrder['sEmail'];
		$sPhone = $aOrder['sPhone'];
		if( !isset( $iStatusOld ) ) $iStatusOld = $aOrder['iStatus'];
		$oDB->save( DB_ORDERS_STATUS, Array( 'iOrder' => $iOrder, 'iStatus' => $iNewStatus, 'iTime' => time( ) ) );
		$oDB->update( DB_ORDERS, Array( 'iStatus' => $iNewStatus ), Array( 'iOrder' => $iOrder ) );
		$this->generateCache( );
		$aOrder = $this->aOrders[$iOrder];
		if( $iNewStatus > 50 && $iStatusOld <= 50 ){
			$sql = 'UPDATE `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` AS `sp`
								SET `sp`.`iStock` = IFNULL(
									`sp`.`iStock` + (
										SELECT SUM( `ops`.`iQuantity` )
											FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` AS `op`
												INNER JOIN `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS_STORES ) . '` AS `ops` USING( `iOrderProduct` )
											WHERE `op`.`iOrder` = ' . intval( $iOrder ) . '
												AND `op`.`iProductVariant` = `sp`.`iProductVariant`
												AND `ops`.`iStore` = `sp`.`iStore`
										),
										`sp`.`iStock`
									)';
			$oDB->executeSQL( $sql );

			/*******************************************************************************************************************/
			/*                                                     YOUNITY                                                     */
			/*******************************************************************************************************************/
			if( !empty( $GLOBALS['config']['younity_id'] ) && !empty( $aOrder['sYounityId'] ) ){
				$amount = sprintf( '%01.2f', $aOrder['fOrderSummary'] ) * 100;
				$currency = $GLOBALS['config']['currency_symbol_ISO'];
				$shopId = $GLOBALS['config']['younity_id'];
				$transactionId = $aOrder['iOrder'];
				$key = $GLOBALS['config']['younity_token'];
				$hash = strtoupper( hash( 'sha256', "$shopId|$amount|$currency|$transactionId|$key" ) );
				$url = "http://younityterminalapi.azurewebsites.net/" . ( $GLOBALS['config']['younity_sandbox'] ? 'sandbox/' : null ) . "transaction/decline/$amount/$currency/$shopId/$transactionId/$hash";
				file_get_contents( $url );
			}
			/*******************************************************************************************************************/
			
		} elseif( $iNewStatus <= 50 && $iStatusOld > 50 ){
			$sql = 'UPDATE `' . $oDB->throwTableName( DB_STORES_PRODUCTS ) . '` AS `sp`
								SET `sp`.`iStock` = IFNULL(
									`sp`.`iStock` - (
										SELECT SUM( `ops`.`iQuantity` )
											FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` AS `op`
												INNER JOIN `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS_STORES ) . '` AS `ops` USING( `iOrderProduct` )
											WHERE `op`.`iOrder` = ' . intval( $iOrder ) . '
												AND `op`.`iProductVariant` = `sp`.`iProductVariant`
												AND `ops`.`iStore` = `sp`.`iStore`
										),
										`sp`.`iStock`
									)';
			$oDB->executeSQL( $sql );

			/*******************************************************************************************************************/
			/*                                                     YOUNITY                                                     */
			/*******************************************************************************************************************/
			if( !empty( $GLOBALS['config']['younity_id'] ) && !empty( $aOrder['sYounityId'] ) ){
				$amount = sprintf( '%01.2f', $aOrder['fOrderSummary'] ) * 100;
				$currency = $GLOBALS['config']['currency_symbol_ISO'];
				$shopId = $GLOBALS['config']['younity_id'];
				$cardId = $_COOKIE['cn'];
				$transactionId = $aOrder['iOrder'];
				$key = $GLOBALS['config']['younity_token'];
				$hash = strtoupper( hash( 'sha256', "$shopId|$amount|$currency|$cardId|$transactionId|$key" ) );
				$url = "http://younityterminalapi.azurewebsites.net/" . ( $GLOBALS['config']['younity_sandbox'] ? 'sandbox/' : null ) . "transaction/register/$amount/$currency/$shopId/$cardId/$transactionId/$hash";
				file_get_contents( $url );
			}
			/*******************************************************************************************************************/
			
		}
		$aLangOld = $GLOBALS['lang'];
		include( DIR_LANG . $aOrder['sLanguage'] . '.php' );
		$GLOBALS['lang'] = $lang;
		if( $GLOBALS['config']['sms_send_status_change_to_client'] == 1 && !empty( $sPhone ) ){
			$sText = str_replace( Array( '%iOrder%', '%sShopName%', '%sNewStatus%', '|n|' ), Array( $iOrder, $GLOBALS['config']['website_name'], $this->throwStatus( $iNewStatus ), "\n" ), $lang['sms_send_order_status_to_client'] );
			$oSMS = new SMS( $GLOBALS['config']['serwersms_login'], $GLOBALS['config']['serwersms_password'] );
			$oSMS->sendSMS_delay( $sPhone, $sText );
		}
		if( $GLOBALS['config']['send_order_status_to_client'] == 1 && !empty( $GLOBALS['config']['email'] ) && !empty( $sEmail ) ){
			$oTpl = & TplParser::getInstance( );
			$sOldTplDir = $oTpl->getDir( );
			$oTpl->setDir( DIR_TEMPLATES . 'admin/' );
			$oTpl->assign( 'lang', $lang );
			$aSS['sSelectedStatus'] = $this->throwStatus( $iNewStatus );
			if( !empty( $aSS['sSelectedStatus'] ) ){
				$this->throwOrder( $iOrder );
				$aSS['iOrder'] = $iOrder;
				if( !empty( $aOrder['sPackageNumber'] ) && $iNewStatus == 2 ){
					$aPackageNumber = explode( ':', $aOrder['sPackageNumber'] );
					$aSS['sPackageCarrier'] = $GLOBALS['aCarriersNames'][$aPackageNumber[0]];
					$aSS['sCarrierText'] = $GLOBALS['aCarriersTexts'][$aPackageNumber[0]];
					$aSS['sPackageNumber2'] = $aPackageNumber[1];
					if( !empty( $aSS['sCarrierText'] ) ){
						$aSS['sCarrierText'] = str_replace( '%sPackageNumber2%', $aSS['sPackageNumber2'], $aSS['sCarrierText'] );
						$aSS['sCarrierText'] = str_replace( '&', '&amp;', $aSS['sCarrierText'] );
					} else{
						$aSS['sHtmlPackageInfo'] = '';
						$aSS['sPackageInfo'] = '';
					}
				} else{
					$aSS['sHtmlPackageInfo'] = '';
					$aSS['sPackageInfo'] = '';
				}
				$aSS['sStatusInfo'] = $GLOBALS['config']['order_status_desc_'.$iNewStatus];
				if( $iNewStatus == 8 ){
					$sql = 'SELECT DISTINCT `p`.`iProduct`, `p`.`sName` FROM `' . $oDB->throwTableName( DB_PRODUCTS ) . '` `p` INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_EXT ) . '` `pe` USING( `iProduct` )
										INNER JOIN `' . $oDB->throwTableName( DB_PRODUCTS_VARIANTS ) . '` `pv` USING( `iProduct` ) INNER JOIN `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` `op` USING( `iProductVariant` )
										WHERE `pe`.`iComments` = 1 AND `op`.`iOrder` = ' . $iOrder;
					$aSS['aProductsForComments'] = $oDB->executeSQL( $sql, true );
					foreach( $aSS['aProductsForComments'] as &$aProduct ){
						$aProduct['sLinkName'] = throwPageUrl( $aProduct['iProduct'] . ',' . change2Url( $aProduct['sName'] ) );
					}
				}
				$oTpl->assign( 'aSS', $aSS );
				$oTpl->assign( 'aUrl', $aUrl );
				$aSSSend['sTopic'] = $oTpl->parse( 'sendOrderStatusToClient_title.tpl' );
				$sHtml = $oTpl->parse( 'sendOrderStatusToClient_html.tpl' );
				$aSSSend['sMailContent'] = strip_tags( $sHtml );
				$sCss = file_get_contents( DIR_TEMPLATES . 'admin/style_sendOrderStatusToClient.css' );
				$oEm = new Emogrifier( $sHtml, $sCss );
				$aSSSend['sHtmlContent'] = $oEm->emogrify( );
				$aSSSend['sSender'] = $GLOBALS['config']['email'];
				sendEmail( $aSSSend, $sEmail );
			}
			$oTpl->assign( 'lang', $aLangOld );
			$oTpl->setDir( $sOldTplDir );
		}
		$GLOBALS['lang'] = $aLangOld;
		if( $iNewStatus == 8 ){ //Sprawdzenie autogrup od sumy zamówień
			$this->autoGroupClient( $aOrder['sLanguage'], $aOrder['iUser'] );
		}
		$oDB->executeSQL( 'COMMIT' );
	}
	function sendSMSWithOrderInfo( $iOrder ){
		$aLangOld = $GLOBALS['lang'];
		include( DIR_LANG . 'admin.php' );
		$GLOBALS['lang'] = $lang;
		if( $GLOBALS['config']['sms_send_new_order_to_admin'] == 1 && !empty( $GLOBALS['config']['sms_admin_phone'] ) ){
			$sText = str_replace( Array( '%iOrder%', '%sShopName%', '|n|' ), Array( $iOrder, $GLOBALS['config']['website_name'], "\n" ), $lang['sms_send_new_order_to_admin'] );
			$oSMS = new SMS( $GLOBALS['config']['serwersms_login'], $GLOBALS['config']['serwersms_password'] );
			$oSMS->sendSMS_delay( $GLOBALS['config']['sms_admin_phone'], $sText );
		}
		$GLOBALS['lang'] = $aLangOld;
	}
	function autoGroupClient( $sLang, $iUser ){
		$oDB = DataBase::getInstance( );
		if( $oDB->executeSQL( 'SELECT 1 FROM `' . $oDB->throwTableName( DB_USERS ) . '` WHERE `iUser` = ' . intval( $iUser ) . ' AND `sPass` IS NOT NULL', true, DataBase::SINGLE_VALUE ) ){ //Użytkownik nie jest anonimowy
			$aRelatedOld = $oDB->executeSQL( 'SELECT `iGroup` FROM `' . $oDB->throwTableName( DB_USERS_TO_GROUPS ) . '` WHERE `iUser` = ' . intval( $iUser ), true, DataBase::SINGLE_COLUMN );
			$oDB->executeSQL( 'DELETE FROM `' . $oDB->throwTableName( DB_USERS_TO_GROUPS ) . '` WHERE `iUser` = ' . intval( $iUser ) . ' AND `iGroup` IN( SELECT `iGroup` FROM `' . $oDB->throwTableName( DB_USERS_GROUPS ) . '` WHERE `fAutoSummary` IS NOT NULL AND `fAutoSummary` > 0 )' );
			$fSummary = $oDB->executeSQL( 'SELECT SUM( `fPrice` * `iQuantity` ) FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` INNER JOIN `' . $oDB->throwTableName( DB_ORDERS ) . '` USING( `iOrder` ) WHERE `sLanguage` = \'' . $sLang . '\' AND `iUser` = ' . intval( $iUser ) . ' AND `iStatus` = 8', true, DataBase::SINGLE_VALUE );
			if( $fSummary > 0 ){
				$oDB->executeSQL( 'INSERT INTO `' . $oDB->throwTableName( DB_USERS_TO_GROUPS ) . '` ( `iUser`, `iGroup` ) SELECT ' . intval( $iUser ) . ', `ug`.`iGroup` FROM `' . $oDB->throwTableName( DB_USERS_GROUPS ) . '` `ug` LEFT JOIN `' . $oDB->throwTableName( DB_USERS_TO_GROUPS ) . '` `utg` ON( `ug`.`iGroup` = `utg`.`iGroup` AND `utg`.`iUser` = ' . intval( $iUser ) . ' ) WHERE `fAutoSummary` > 0 AND `fAutoSummary` <= ' . $fSummary . ' AND `iUser` IS NULL AND `iAutoDays` IS NULL AND `sLang` = \'' . $sLang . '\'' );
				$oDB->executeSQL( 'INSERT INTO `' . $oDB->throwTableName( DB_USERS_TO_GROUPS ) . '` ( `iUser`, `iGroup` ) SELECT ' . intval( $iUser ) . ', `ug`.`iGroup` FROM `' . $oDB->throwTableName( DB_USERS_GROUPS ) . '` `ug` LEFT JOIN `' . $oDB->throwTableName( DB_USERS_TO_GROUPS ) . '` `utg` ON( `ug`.`iGroup` = `utg`.`iGroup` AND `utg`.`iUser` = ' . intval( $iUser ) . ' ) WHERE `fAutoSummary` > 0 AND `fAutoSummary` <= ( SELECT SUM( `fPrice` * `iQuantity` ) FROM `' . $oDB->throwTableName( DB_ORDERS_PRODUCTS ) . '` INNER JOIN `' . $oDB->throwTableName( DB_ORDERS ) . '` USING( `iOrder` ) WHERE `sLanguage` = \'' . $sLang . '\' AND `iUser` = ' . intval( $iUser ) . ' AND `iStatus` = 8 AND `iTime` > ( UNIX_TIMESTAMP() - ( `iAutoDays` * 24 * 3600 ) ) ) AND `iUser` IS NULL AND `sLang` = \'' . $sLang . '\'' );
			}
			$aRelated = $oDB->executeSQL( 'SELECT `iGroup` FROM `' . $oDB->throwTableName( DB_USERS_TO_GROUPS ) . '` WHERE `iUser` = ' . intval( $iUser ), true, DataBase::SINGLE_COLUMN ); //Po przypisaniu grup
			$aRelatedAdded = array_diff( $aRelated, $aRelatedOld );
			if( !empty( $aRelatedAdded ) ){
				$aRelatedAdded = $oDB->executeSQL( 'SELECT `fDiscount`, COUNT( `iPage` ) AS `count` FROM `' . $oDB->throwTableName( DB_USERS_GROUPS ) . '` LEFT JOIN `' . $oDB->throwTableName( DB_USERS_PAGES ) . '` USING( `iGroup` ) WHERE `iInformEmail` = 1 AND `iGroup` IN ( ' . implode( ', ', $aRelatedAdded ) . ' )', true, DataBase::SINGLE_RECORD );
				if( !empty( $aRelatedAdded ) ){
					informUserOnGroup( $iUser, $aRelatedAdded['fDiscount'] > 0, $aRelatedAdded['count'] > 0 );
				}
			}
		}
	}
};
?>