Pourquoi Magento enregistre-t-il un delta d'arrondi lors du calcul des taxes

14

Dans le modèle tax/Sales_Total_Quote_Tax, il existe une méthode _deltaRound()qui arrondit un prix. Il ajoute un petit delta pour arrêter le comportement non déterministe lors de l'arrondi à 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;
}

Mais il stocke un delta. S'il ne trouve pas un tel delta stocké, il en crée un. Pourquoi? Comme tar que je peux dire, cela conduit à des résultats différents avec des opérations identiques.

Disons que nous avons un $price3.595, et nous n'avons pas de cache $delta. En parcourant la méthode, nous obtiendrons $ delta = 0,000001. Nous obtenons alors $price= 3,595001, qui arrondit à 3,60, nous avons donc un nouveau $deltade -0,004999. Et nous retournons 3,60.

Sauf que maintenant nous avons un delta, alors recommençons, avec $price= 3,595. $price= 3,595 - 0,004999 = 3,590001

Si nous arrondissons, nous obtenons 3,59. Des réponses différentes.

Il me semble que tout algorithme d'arrondi utilisé devrait au moins donner la même réponse à chaque fois qu'il est exécuté avec les mêmes arguments, mais pas cette fois.

Max Bucknell
la source
BTW, a rencontré la même erreur dans Magento 2.2.2
TheKitMurkit

Réponses:

9

J'ai Magento 1.8 sur mon serveur et j'ai vérifié la _deltaRound()méthode. Ça ressemble à ça maintenant.

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

Comme vous pouvez le voir, si le _roundingDeltas()n'est pas défini, il prend zerocomme valeur par défaut. C'est juste pour vous avoir remarqué. L'équipe Magento peut entendre votre doute. Ils ont résolu votre problème en silence. :)

ÉDITER

Analysons l'utilisation de cette fonction en l'appliquant à un exemple en temps réel. Supposons que j'ai un produit dans le panier qui est taxable. La quantité que je vais acheter sera de 5. Après avoir appliqué la taxe, le produit a un prix de 10,5356 $. Voilà ma 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.

Alors maintenant, calculons le prix réel qui va produire dans cette situation. Ce sera

  Total =  10.5356 x 5 = 52.678

Supposons maintenant que magento n'utilise pas _deltaRound() méthode. Il arrondit simplement le prix du produit jusqu'à deux décimales, puis calcule le prix total. Dans ce cas, le prix du produit sera arrondi à 10.54et donc le prix total serait

  Total = 10.54 x 5 = 52.7

Supposons maintenant que magento utilise _deltaRound() méthode et que cette fonction arrondit le prix du produit à deux décimales. Parallèlement à cela, il conservera une valeur delta, qui est en fait la différence entre le prix réel et le prix arrondi, qui sera utilisée pour calculer le prix arrondi ultérieurement. Icientrez la description de l'image ici

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

Cela signifie que la _deltaRound()méthode rend en fait l'arrondi du prix de la taxe plus précis que le prix réel de la taxe. Comme vous l'avez indiqué, cette méthode renvoie une valeur arrondie différente dépend de la valeur delta. Cette valeur delta rend en fait l'arrondissement fiscal plus précis.

Selon cela, nous pouvons conclure que, à mesure que la quantité augmente, si nous n'adoptons pas cette méthode, elle produira une grande différence entre la valeur arrondie et la valeur réelle. Mais si nous utilisons cette méthode, elle rendra notre valeur arrondie aussi proche que possible de la valeur réelle.

Magento arrondit par défaut à deux décimales. Il s'agit de la méthode responsable de l'arrondi à deux décimales

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

Si nous le définissons sur 4 ou quelque chose, nous pouvons encore augmenter la précision de l'arrondi.

Remarque: Ceci est mon ouverture et mon aperçu. Cela peut être vrai ou non. Cependant, cela me semble précis et logique.

Merci.

Rajeev K Tomy
la source
Je déteste vraiment les 2 pouces codés en durroundPrice
David Manners
@DavidManners: oui c'est correct. Mais magento utilise _deltaRound()pour surmonter la difficulté dans une certaine mesure. N'importe comment son codé en dur. Cela entraînera certainement des difficultés dans certains cas
Rajeev K Tomy
1
En regardant github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/… la valeur par défaut est toujours 0,0001 dans magento 1.9, ce qui a pour nous entraîné une erreur de calcul avec les calculs de taxes sur l'expédition
ProxiBlue
2

Info

Prix ​​du tour à Magento basé sur le delta d'opération d'arrondi précédent.

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

Parfois, cela peut provoquer une erreur en raison de l'erreur de calcul de delta élevé ( $this->_calculator->round($price)). Par exemple, pour cette raison, certains prix peuvent varier dans une fourchette de ± 1 cent .

Solution

Pour éviter cela, vous devez améliorer la précision du calcul du delta.

Changement

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

à

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

Des modifications doivent être apportées dans les deux fichiers:

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

Ne modifiez pas ou ne piratez pas les fichiers de base! Faites une réécriture!

La solution a été testée sur différentes versions de Magento 1.9.x, mais cela fonctionnera peut-être dans les versions antérieures.

PS

La roundPricefonction de modification , comme indiqué ci-dessous, peut résoudre le problème d'erreur d'arrondi, mais elle peut en provoquer d'autres (par exemple, certaines plates-formes nécessitent un arrondi à la deuxième décimale).

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

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