Your IP : 18.222.77.149


Current Path : /home/bitrix/ext_www/home-comfort.in.ua/bitrix/modules/catalog/lib/product/
Upload File :
Current File : /home/bitrix/ext_www/home-comfort.in.ua/bitrix/modules/catalog/lib/product/price.php

<?php
namespace Bitrix\Catalog\Product;

use Bitrix\Main,
	Bitrix\Catalog;
/**
 * Class Price
 * Provides various useful methods for prices.
 *
 * @package Bitrix\Catalog\Product
 */
class Price
{
	/* cache id template */
	const CACHE_ID = 'catalog_price_round_';
	/* cache time */
	const CACHE_TIME = 360000;
	/* maximal precision */
	const VALUE_EPS = 1E-5;

	/* static cache */
	protected static $roundRules = array();

	/**
	 * Handler onAfterUpdateCurrencyBaseRate for update field PRICE_SCALE after change currency base rate.
	 *
	 * @param Main\Event $event			Event data (old and new currency rate).
	 * @return void
	 */
	public static function handlerAfterUpdateCurrencyBaseRate(Main\Event $event)
	{
		$params = $event->getParameters();
		if (empty($params))
			return;

		$oldBaseRate = (float)$params['OLD_BASE_RATE'];
		if ($oldBaseRate < 1E-4)
			return;
		$currentBaseRate = (float)$params['CURRENT_BASE_RATE'];
		if (abs($currentBaseRate - $oldBaseRate)/$oldBaseRate < 1E-4)
			return;
		$currency = $params['CURRENCY'];

		$conn = Main\Application::getConnection();
		$helper = $conn->getSqlHelper();

		$query = 'update '.$helper->quote(Catalog\PriceTable::getTableName()).
			' set '.$helper->quote('PRICE_SCALE').' = '.$helper->quote('PRICE').' * '.$currentBaseRate.
			' where '.$helper->quote('CURRENCY').' = \''.$helper->forSql($currency).'\'';
		$conn->queryExecute($query);

		if (defined('BX_COMP_MANAGED_CACHE'))
		{
			$taggedCache = Main\Application::getInstance()->getTaggedCache();
			$taggedCache->clearByTag('currency_id_'.$currency);
		}
	}

	/**
	 * Load round rules list for price types.
	 *
	 * @param array $priceTypes		Price type ids.
	 * @return void
	 */
	public static function loadRoundRules(array $priceTypes)
	{
		if (empty($priceTypes))
			return;
		Main\Type\Collection::normalizeArrayValuesByInt($priceTypes);
		if (empty($priceTypes))
			return;

		$skipCache = (defined('CATALOG_SKIP_CACHE') && CATALOG_SKIP_CACHE);
		$cacheTime = (int)self::CACHE_TIME;
		/** @var \Bitrix\Main\Data\ManagedCache $managedCache */
		$managedCache = Main\Application::getInstance()->getManagedCache();

		$needLoad = array();
		foreach ($priceTypes as $priceTypeId)
		{
			if (!isset(self::$roundRules[$priceTypeId]))
			{
				$rulesFound = false;
				$cacheId = static::getRulesCacheId($priceTypeId);
				if (!$skipCache)
				{
					$cacheExist = $managedCache->read($cacheTime, $cacheId, Catalog\RoundingTable::getTableName());
					if ($cacheExist)
					{
						$rulesFound = true;
						self::$roundRules[$priceTypeId] = $managedCache->get($cacheId);
					}
				}
				if ($skipCache || !$rulesFound)
					$needLoad[] = $priceTypeId;
				unset($cacheId, $rulesFound);
			}
		}
		unset($priceTypeId);

		if (!empty($needLoad))
		{
			foreach ($needLoad as $priceTypeId)
				self::$roundRules[$priceTypeId] = array();
			unset($priceTypeId);
			$iterator = Catalog\RoundingTable::getList(array(
				'select' => array('PRICE', 'ROUND_TYPE', 'ROUND_PRECISION', 'CATALOG_GROUP_ID'),
				'filter' => array('@CATALOG_GROUP_ID' => $needLoad),
				'order' => array('CATALOG_GROUP_ID' => 'ASC', 'PRICE' => 'DESC')
			));
			while ($row = $iterator->fetch())
			{
				$priceTypeId = (int)$row['CATALOG_GROUP_ID'];
				self::$roundRules[$priceTypeId][] = $row;
				unset($priceTypeId);
			}
			unset($row, $iterator);

			if (!$skipCache)
			{
				foreach ($needLoad as $priceTypeId)
					$managedCache->set(static::getRulesCacheId($priceTypeId), self::$roundRules[$priceTypeId]);
				unset($priceType);
			}
		}
		unset($needLoad);
		unset($managedCache, $cacheTime, $skipCache);
	}

	/**
	 * Return round rules list for price type.
	 *
	 * @param int $priceType		Price type id.
	 * @return array
	 */
	public static function getRoundRules($priceType)
	{
		$priceType = (int)$priceType;
		if ($priceType <= 0)
			return array();
		if (!isset(self::$roundRules[$priceType]))
			static::loadRoundRules(array($priceType));

		return self::$roundRules[$priceType];
	}

	/**
	 * Clear cache rules for price type.
	 *
	 * @param int $priceType		Price type id.
	 * @return void
	 */
	public static function clearRoundRulesCache($priceType)
	{
		$priceType = (int)$priceType;
		if ($priceType <= 0)
			return;
		if (isset(self::$roundRules[$priceType]))
			unset(self::$roundRules[$priceType]);
		Main\Application::getInstance()->getManagedCache()->clean(
			static::getRulesCacheId($priceType),
			Catalog\RoundingTable::getTableName()
		);
	}

	/**
	 * Get rule for price value.
	 *
	 * @param int $priceType		Price type id.
	 * @param float|int $price		Price value.
	 * @param string $currency		Price currency.
	 * @return array
	 */
	public static function searchRoundRule(
		$priceType,
		$price,
		/** @noinspection PhpUnusedParameterInspection */ $currency = ''
	)
	{
		$rules = static::getRoundRules($priceType);
		if (empty($rules))
			return array();
		foreach ($rules as $row)
		{
			if ($row['PRICE'] < $price)
				return $row;
		}
		return array();
	}

	/**
	 * Round price.
	 *
	 * @param int $priceType		Price type id.
	 * @param float|int $price		Price value.
	 * @param string $currency		Currency.
	 * @return float|int
	 */
	public static function roundPrice($priceType, $price, $currency)
	{
		$price = (float)$price;
		if ($price <= 0)
			return $price;
		$priceType = (int)$priceType;
		if ($priceType <= 0)
			return $price;
		$rule = static::searchRoundRule($priceType, $price, $currency);
		if (empty($rule))
			return $price;
		return static::roundValue($price, $rule['ROUND_PRECISION'], $rule['ROUND_TYPE']);
	}

	/**
	 * Round value with arbitrary precision.
	 *
	 * @param float|int $value			Round value.
	 * @param float|int $precision		Precision.
	 * @param int $type					Round type.
	 * @return float|int
	 */
	public static function roundValue($value, $precision, $type)
	{
		$type = (int)$type;
		if (!in_array($type, Catalog\RoundingTable::getRoundTypes(false)))
			return $value;

		$precision = (float)$precision;
		if ($precision <= 0)
			return 0;
		if ($precision >= 1)
			$precision = (int)$precision;

		$value = (float)$value;
		if (abs($value) <= self::VALUE_EPS)
			return 0;

		return ($precision < 1
			? static::roundFraction($value, $precision, $type)
			: static::roundWhole($value, $precision, $type)
		);
	}

	/**
	 * Return cache id for price type.
	 *
	 * @param string|int $priceType		Price type id.
	 * @return string
	 */
	protected static function getRulesCacheId($priceType)
	{
		return self::CACHE_ID.$priceType;
	}

	/**
	 * Round whole part.
	 *
	 * @param float|int $value			Round value.
	 * @param float|int $precision		Precision.
	 * @param int $type					Round type.
	 * @return float|int
	 */
	protected static function roundWhole($value, $precision, $type)
	{
		$quotient = $value/$precision;
		$quotientFloor = floor($quotient);
		switch ($type)
		{
			case Catalog\RoundingTable::ROUND_UP:
				if (($quotient - $quotientFloor) > self::VALUE_EPS)
					$quotientFloor += 1;
				break;
			case Catalog\RoundingTable::ROUND_DOWN:
				break;
			case Catalog\RoundingTable::ROUND_MATH:
			default:
				if (($quotient - $quotientFloor) >= .5)
					$quotientFloor += 1;
				break;
		}

		return $quotientFloor*$precision;
	}

	/**
	 * Round fraction part.
	 *
	 * @param float|int $value			Round value.
	 * @param float|int $precision		Precision.
	 * @param int $type					Round type.
	 * @return float
	 */
	protected static function roundFraction($value, $precision, $type)
	{
		$valueFloor = floor($value);
		$fraction = $value - $valueFloor;
		if ($fraction <= self::VALUE_EPS)
			return $value;

		return $valueFloor + static::roundWhole($fraction, $precision, $type);
	}
}