Warum speichert Magento bei der Berechnung von Steuern ein Rundungsdelta?

14

Im Modell tax/Sales_Total_Quote_Taxgibt es eine Methode _deltaRound(), die einen Preis rundet. Es wird ein kleines Delta hinzugefügt, um nichtdeterministisches Verhalten beim Runden von 0,5 zu stoppen.

/**
 * 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;
}

Aber es speichert ein Delta. Wenn es ein solches gespeichertes Delta nicht finden kann, bildet es eines. Warum? Soweit ich das beurteilen kann, führt dies bei identischen Operationen zu unterschiedlichen Ergebnissen.

Nehmen wir an, wir haben einen $priceWert von 3,595 und keinen zwischengespeicherten Wert $delta. Während wir die Methode durchlaufen, erhalten wir $ delta = 0.000001. Wir bekommen dann $price= 3.595001, was auf 3.60 rundet, also haben wir einen neuen $deltavon -0.004999. Und wir geben 3,60 zurück.

Außer jetzt haben wir ein Delta, also lass es uns noch einmal machen, mit $price= 3.595. $price= 3,595 - 0,004999 = 3,590001

Wenn wir runden, erhalten wir 3,59. Unterschiedliche Antworten.

Es scheint mir, dass jeder verwendete Rundungsalgorithmus jedes Mal, wenn er mit denselben Argumenten ausgeführt wird, mindestens dieselbe Antwort geben sollte, diesmal jedoch nicht.

Max Bucknell
quelle
Übrigens ist derselbe Fehler in Magento 2.2.2
TheKitMurkit

Antworten:

9

Ich habe Magento 1.8 auf meinem Server und die _deltaRound()Methode überprüft . Es sieht jetzt so aus.

/**
 * 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;
}

Wie Sie sehen, wird der Wert als Standardwert verwendet , wenn der _roundingDeltas()nicht festgelegt ist zero. Es ist nur, um dich zu bemerken. Das Magento-Team kann Ihre Zweifel überhören. Sie haben Ihr Problem im Stillen gelöst. :)

BEARBEITEN

Analysieren wir die Verwendung dieser Funktion, indem wir sie in einem Echtzeitbeispiel anwenden. Angenommen, ich habe ein steuerpflichtiges Produkt im Warenkorb. Die Menge, die ich kaufen werde, ist 5. Nach dem Anwenden von Steuern hat das Produkt einen Preis von 10.5356 USD. Das ist also meine Situation

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.

Lassen Sie uns nun den tatsächlichen Preis berechnen, der in dieser Situation entstehen wird. Es wird sein

  Total =  10.5356 x 5 = 52.678

Nehmen wir nun an, dass Magento keine _deltaRound()Methode verwendet. Es wird lediglich der Produktpreis auf zwei Dezimalstellen gerundet und anschließend der Gesamtpreis berechnet. In diesem Fall wird der Produktpreis auf gerundet 10.54und somit der Gesamtpreis

  Total = 10.54 x 5 = 52.7

Nehmen wir nun an, dass Magento die _deltaRound()Methode verwendet und diese Funktion den Produktpreis auf zwei Dezimalstellen rundet. Gleichzeitig wird ein Deltawert beibehalten, der die Differenz zwischen dem tatsächlichen Preis und dem gerundeten Preis darstellt und später zur Berechnung des gerundeten Preises verwendet wird. HierBildbeschreibung hier eingeben

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

Dies bedeutet, dass die _deltaRound()Methode die Steuerpreisrundung tatsächlich genauer auf den tatsächlichen Steuerpreis abrundet. Wie Sie angegeben haben, gibt diese Methode unterschiedliche Rundungswerte zurück, die vom Delta-Wert abhängen. Dieser Delta-Wert erhöht die Genauigkeit der Steuerrundung.

Demnach können wir schließen, dass sich bei zunehmender Menge, wenn wir diese Methode nicht anwenden, eine große Differenz zwischen gerundetem Wert und tatsächlichem Wert ergibt. Wenn wir diese Methode verwenden, wird unser gerundeter Wert dem tatsächlichen Wert so nahe wie möglich kommen.

Magento rundet standardmäßig auf zwei Dezimalstellen. Dies ist die Methode, die für das Runden mit zwei Dezimalstellen verantwortlich ist

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

Wenn wir den Wert auf 4 oder so setzen, können wir die Rundungsgenauigkeit weiter erhöhen.

Hinweis: Dies ist meine Meinung und Übersicht. Es kann wahr sein oder nicht. Es erscheint mir jedoch richtig und logisch.

Vielen Dank.

Rajeev K Tomy
quelle
Ich hasse wirklich die hart codierten 2 inroundPrice
David Manners
@ DavidManners: ja es ist richtig. Aber mit Magento _deltaRound()kann man die Schwierigkeit ein Stück weit überwinden. Irgendwie ist es hart codiert. Es wird definitiv einige Schwierigkeiten in einigen Fällen produzieren
Rajeev K Tomy
1
Wenn Sie sich github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/… ansehen, ist der Standardwert in Magento 1.9 immer noch 0.0001, was für uns zu einem Rundum-Fehler bei den Steuerberechnungen für den Versand geführt hat
ProxiBlue
2

Die Info

Rundungspreis in Magento basierend auf dem vorherigen Rundungsoperations-Delta.

App / Code / Kern / Magier / Steuer / Modell / Verkauf / Insgesamt / Quote / Tax.php: 1392 App / Code / Kern / Magier / Steuer / Modell / Verkauf / Insgesamt / 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;
}

Manchmal kann dies aufgrund des Fehlers bei der Berechnung des hohen Deltas ( $this->_calculator->round($price)) zu einem Fehler führen . Beispielsweise können aus diesem Grund einige Preise im Bereich von variieren ± 1 Cent .

Lösung

Um dies zu vermeiden, müssen Sie die Genauigkeit der Delta-Berechnung verbessern.

Veränderung

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

zu

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

Änderungen müssen in beiden Dateien vorgenommen werden:

App / Code / Kern / Magier / Steuer / Modell / Verkauf / Insgesamt / Quote / Tax.php: 1392 App / Code / Kern / Magier / Steuer / Modell / Verkauf / Insgesamt / Quote / Subtotal.php: 719

Ändern oder hacken Sie keine Core-Dateien! Mach ein Rewrite!

Die Lösung wurde auf verschiedenen Versionen von Magento 1.9.x getestet, aber möglicherweise funktioniert dies in früheren Versionen.

PS

Die Änderungsfunktion roundPrice, wie unten gezeigt, kann das Rundungsfehlerproblem lösen, aber andere verursachen (zum Beispiel erfordern einige Plattformen das Runden auf 2 Dezimalstellen).

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

public function roundPrice($price)
{
    return round($price, 4);
}
Victor S.
quelle