Чому Magento зберігає дельту округлення при обчисленні податків


14

У моделі tax/Sales_Total_Quote_Taxє метод, _deltaRound()який округляє ціну. Він додає невелику дельту, щоб зупинити недетерміновану поведінку під час округлення 0,5.

/**
 * Round price based on previous rounding operation delta
 *
 * @param float $price
 * @param string $rate
 * @param bool $direction price including or excluding tax
 * @param string $type
 * @return float
 */
protected function _deltaRound($price, $rate, $direction, $type = 'regular')
{
    if ($price) {
        $rate = (string)$rate;
        $type = $type . $direction;
        // initialize the delta to a small number to avoid non-deterministic behavior with rounding of 0.5
        $delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] : 0.000001;
        $price += $delta;
        $this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);
        $price = $this->_calculator->round($price);
    }
    return $price;
}

Але він зберігає дельту. Якщо він не може знайти таку збережену дельту, вона складає її. Чому? Як я можу сказати, це призводить до різних результатів при однакових операціях.

Скажімо, у нас є $price3,559, а у нас немає кешу $delta. Під час проходження методу ми отримаємо $ delta = 0,000001. Тоді ми отримуємо $price= 3,5595001, що округляє до 3,60, тому у нас є новий $delta-0,004999. І повертаємо 3.60.

За винятком тепер у нас є дельта, тож давайте зробимо це ще раз, з $price= 3,559. $price= 3,5595 - 0,004999 = 3,590001

Що, якщо ми округлимо, отримаємо 3,59. Різні відповіді.

Мені здається, що будь-який використаний алгоритм округлення повинен хоча б давати однакову відповідь кожного разу, коли він запускається з тими ж аргументами, але не цього разу.


BTW, зіткнувся з такою ж помилкою в Magento 2.2.2
TheKitMurkit

Відповіді:


9

У мене на сервері є Magento 1.8, і я перевірив _deltaRound()метод. Так виглядає зараз.

/**
 * Round price based on previous rounding operation delta
 *
 * @param float $price
 * @param string $rate
 * @param bool $direction price including or excluding tax
 * @param string $type
 * @return float
 */
protected function _deltaRound($price, $rate, $direction, $type = 'regular')
{
    if ($price) {
        $rate  = (string) $rate;
        $type  = $type . $direction;
        $delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] : 0;
        $price += $delta;
        $this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);
        $price = $this->_calculator->round($price);
    }
    return $price;
}

Як бачите, якщо значення _roundingDeltas()не встановлено, воно приймається zeroза значенням за замовчуванням. Це лише для того, щоб вас помітили. Команда Magento може вислухати ваші сумніви. Вони вирішили вашу проблему мовчки. :)

EDIT

Проаналізуємо використання цієї функції, застосувавши її в прикладі реального часу. Припустимо, у мене в кошику є товар, який оподатковується. Кількість, яку я збираюся придбати, буде 5. Після застосування податку, prdouct має ціну 10,5356 доларів. Так це моя ситуація

CART
-------
   Product A
       - Price (including tax) - 10.5356
       - Quantity              - 5
       - Tax Rule  - Apply tax for each product. Then calculate the total price according to the quantity purchased.

Тож давайте тепер порахуємо реальну ціну, яка збирається виробити в цій ситуації. Це буде

  Total =  10.5356 x 5 = 52.678

Тепер припустимо, що Магенто не використовує _deltaRound()метод. Він просто округляє ціну товару до двох знаків після коми, а потім обчислює загальну ціну. У цьому випадку ціна товару буде округлена 10.54і, отже, загальна ціна

  Total = 10.54 x 5 = 52.7

Тепер припустимо, що магент використовує _deltaRound()метод, і ця функція фактично округляє ціну продукту до двох десятків. Поряд з цим він буде зберігати дельта-величину, яка насправді є різницею між фактичною ціною і округлою ціною, використовуючи для розрахунку округлої ціни пізніше. Осьвведіть тут опис зображення

  Total =  10.54+10.53+10.54+10.53+10.54 = 52.68

Це означає, що _deltaRound()фактично округлення податкової ціни є більш точним до фактичного податкового ціноутворення. Як ви заявляли, цей метод повертає різні значення круглої форми залежно від значення delta. Це значення дельти фактично робить податкове округлення більш точним.

Відповідно до цього, ми можемо зробити висновок, що із збільшенням кількості, якщо ми не застосовуємо цей метод, він призведе до великої різниці між округленою величиною та фактичною величиною. Але якщо ми використовуємо цей метод, він зробить наше округлене значення максимально наближеним до фактичного значення.

Magento за замовчуванням округляє два знаки після коми. Це метод, який відповідає за округлення двох знаків після коми

Location :app/code/core/Mage/Core/Model/Store.php
public function roundPrice($price)
{
    return round($price, 2);
}

Якщо встановити його на 4 чи щось, ми можемо ще більше підвищити точність округлення.

Примітка. Це моє відкриття та огляд. Це може бути, а може і не бути правдою. Однак це здається точним і логічним для мене.

Спасибі.


Я справді ненавиджу важко закодований 2 дюймroundPrice
Девід Маннерс

@DavidManners: так, це правильно. Але Магенто використовує _deltaRound()для подолання труднощів на деякий час. Як би не було важко закодовано. Це, безумовно, спричинить певні труднощі в деяких випадках
Радєєв К Томі,

1
Дивлячись на github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/…, значення за замовчуванням все ще дорівнює 0,0001 в magento 1.9, що для нас призвело до помилки маршрутизації із розрахунком податку на доставку
ProxiBlue

2

інформація

Ціна круглої форми в Магенто на основі попередньої дельти операції округлення.

app / code / core / Mage / Tax / Model / Sales / Total / Quote / Tax.php: 1392 app / code / core / Mage / Tax / Model / Sales / Total / Quote / Subtotal.php: 719

protected function _deltaRound($price, $rate, $direction, $type = 'regular')
{
    if ($price) {
        $rate = (string)$rate;
        $type = $type . $direction;
        // initialize the delta to a small number to avoid non-deterministic behavior with rounding of 0.5
        $delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] : 0.000001;
        $price += $delta;
        $this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);
        $price = $this->_calculator->round($price);
    }
    return $price;
}

Іноді це може спричинити помилку через велику помилку обчислення дельти ( $this->_calculator->round($price)). Наприклад, з цієї причини деякі ціни можуть коливатися в межах ± 1 цент .

Рішення

Щоб цього уникнути, потрібно підвищити точність розрахунку дельти.

Зміна

$this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);

до

$this->_roundingDeltas[$type][$rate] = $price - round($price, 4);

Зміни потрібно внести в обидва файли:

app / code / core / Mage / Tax / Model / Sales / Total / Quote / Tax.php: 1392 app / code / core / Mage / Tax / Model / Sales / Total / Quote / Subtotal.php: 719

Не змінюйте та не руйнуйте основні файли! Зробіть перезапис!

Рішення було протестовано на різних версіях Magento 1.9.x, але, можливо, це буде працювати і в більш ранніх версіях.

PS

roundPriceФункція зміни , як показано нижче, може вирішити проблему помилки округлення, але це може спричинити інші (наприклад, деякі платформи потребують округлення до 2 знаків після коми).

app / code / core / Mage / Core / Model / Store.php: 995

public function roundPrice($price)
{
    return round($price, 4);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.