How To: Округление цен в магазине, форматирование цен

Централизованно округлить цены можно установкой количества десятичных знаков для Shop_Controller. В файл bootstrap.php, в корне, добавьте для округления до десятых (по умолчанию значение 2):

Shop_Controller::instance()->decimalDigits(1);

До целых:

Shop_Controller::instance()->decimalDigits(0);

Опция decimalDigits() добавлена в версии 6.6.3.

Формат вывода

Для изменения формата вывода цены используйте опцию floatFormat(). В файл bootstrap.php, в корне, добавьте для вывода только десятых:

Shop_Controller::instance()->floatFormat("%.1f");

или целых:

Shop_Controller::instance()->floatFormat("%.0f");

Различное округление для сайтов

Добавьте в файл bootstrap.php обработчик события Core_Command_Controller_Default.onBeforeShowAction с проверкой на текущий сайт, например:

class Core_Command_Controller_Observer
{
    static public function onBeforeShowAction($object)
    {
        switch (CURRENT_SITE)
        {
            // Округление для сайта 1
            case 1:
                //  до десятых
                Shop_Controller::instance()->decimalDigits(1);
                Shop_Controller::instance()->floatFormat("%.1f");
            break;
            // Округление для сайта 3
            case 3:
                //  до целых
                Shop_Controller::instance()->decimalDigits(0);
                Shop_Controller::instance()->floatFormat("%.0f");
            break;
            // По умолчанию
            default:
                // до сотых
                Shop_Controller::instance()->floatFormat("%.2f");
                // Банковское округление как в 1С
                Shop_Controller::instance()->bankersRoundHalfToEven(FALSE);
        }
    }
}

// Add observer
Core_Event::attach('Core_Command_Controller_Default.onBeforeShowAction', array('Core_Command_Controller_Observer', 'onBeforeShowAction'));

Международное банковское округление

Округление к ближайшему четному или нечетному значительно сокращает вероятность накопления ошибки за счет того, что количество четных и нечетных сумм статистически примерно одинаково.

По умолчанию используется округление к ближайшему четному. Например, налог 63.545 будет преобразован в 63.54, а 63.555 в 63.56.
Допускается включения округления к ближайшему нечетному (1С), тогда налог 63.545 будет преобразован в 63.55, а 63.555 тоже в 63.55.

Для переключения банковского округления к ближайшему нечетному (как в 1С) в bootstrap.php внесите

Shop_Controller::instance()->bankersRoundHalfToEven(FALSE);

Для отключения банковского округления в bootstrap.php внесите

Shop_Controller::instance()->bankersRounding(FALSE);

Округление до тысяч, сотен, десятков

Чтобы цена товара в интернет-магазине не была отпугивающей, иногда нужно округлить её до целых десятков. Округление осуществляется с помощью хука на событие Shop_Item_Controller.onAfterCalculatePrice, вносимого в bootstrap.php или с помощью опции decimalDigits(), описанной в начале статьи.

class Shop_Price_Observer
{
    static public function round($price)
    {
        /*
        -3 — до тысяч
        -2 — до сотен
        -1 — до десятков
        */
        return round($price, -3);
    }
    
    static public function onAfterCalculatePrice($object, $args)
    {
        $aPrice = $object->getAPrice();
        $aPrice['price_discount'] = self::round($aPrice['price_discount']);
        $object->setAPrice($aPrice);
    }
}

Core_Event::attach('Shop_Item_Controller.onAfterCalculatePrice', array('Shop_Price_Observer', 'onAfterCalculatePrice'));

Форматирование цен в XSL

Создайте собственный формат, дайте ему наименование, например, my. Опишите формат, указав разделитель дробной части и групп. При выводе чисел используйте свой формат:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:hostcms="https://www.hostcms.ru/"
    exclude-result-prefixes="hostcms">
    <xsl:output xmlns="http://www.w3.org/TR/xhtml1/strict" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" encoding="utf-8" indent="yes" method="html" omit-xml-declaration="no" version="1.0" media-type="text/xml"/>

    <!-- Определяем собственный формат "my" -->
    <xsl:decimal-format name="my" decimal-separator="," grouping-separator=" "/>
    
    <xsl:template match="/shop">
        ...
    </xsl:template>
    
    <xsl:template match="shop_item">
        ...
        Вариант 1:
        <xsl:value-of select="format-number(price, '# ###', 'my')" />
        
        Вариант 2:
        <xsl:value-of select="format-number(price, '### ##0,00', 'my')" />
        ...
    </xsl:template>
</xsl:stylesheet>
Разделители, указанные в <xsl:decimal-format>, не должны отличаться от разделителей в шаблоне формата format-number()

В шаблоне формата поддерживаются следующие символы:

  1. «.» и «,» — используются для описания расположения и типа пунктуации;
  2. «#» — используется для описания целых чисел;
  3. «0» — используется для расположения 0 до и после символа в случае, если позиция не покажет цифру. При усечении чисел справа от десятичного разделителя значение округляется (значение слева от десятичного разделителя не округляется);
  4. «%» и «‰».

Хранение цен в базе данных

Цены для товаров хранятся в поле price таблицы shop_items, данное поле имеет формат DECIMAL(12,2), где первое число (точность) представляет собой общее количество значащих десятичных знаков, с которыми будет храниться данная величина, а вторая цифра (масштаб) задает количество десятичных знаков после запятой.

Пример SQL-запроса для изменения формата поля:

ALTER TABLE `shop_items` CHANGE `price` `price` DECIMAL( 14, 4 ) NOT NULL DEFAULT '0.00';

Не нашли ответ на свой вопрос в документации? Направьте обращение в службу поддержки или онлайн чат.

Комментарии

  • Без темы

    везде - на всех сайтах на одной установке?
    как сделать, чтобы округление было только для конкретного сайта?

    18.03.2016 11:54:26
    EZ-Web
  • Без темы

    А если мне нужно до десятков, то соответственно:
    Shop_Controller::instance()->floatFormat("%.-1f");
    или как?

    16.04.2015 01:50:38
    Юлия

    Без темы

    Все варианты описаны на странице http://php.net/manual/ru/function.sprintf.php

    16.04.2015 09:30:33
    hostcms
  • Без темы

    А это округление если сделать то везде будет округлять или только на некоторых шагах? Ибо сейчас у нас и так округляет но в админке в истории заказов все ровно пишет с копейками и в робокассы тоже все ровно выдает с копейками

    22.03.2015 11:32:45
    Юлия

    Без темы

    Это округление должно везде округлять.

    23.03.2015 09:24:31
    hostcms