Perché Magento memorizza un delta di arrotondamento quando si calcolano le tasse


14

Nel modello tax/Sales_Total_Quote_Taxesiste un metodo _deltaRound()che arrotonda un prezzo. Aggiunge un piccolo delta, per fermare il comportamento non deterministico quando si arrotondano 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;
}

Ma memorizza un delta. Se non riesce a trovare un delta così memorizzato, ne crea uno. Perché? Per quanto posso dire, questo porta a risultati diversi con operazioni identiche.

Diciamo che abbiamo un $price3.595 e non abbiamo un cache $delta. Seguendo il metodo, otterremo $ delta = 0,000001. Otteniamo quindi $price= 3.595001, che arrotondano a 3.60, quindi abbiamo un nuovo $deltadi -0.004999. E torniamo 3.60.

Tranne ora che abbiamo un delta, quindi facciamolo di nuovo, con $price= 3.595. $price= 3.595 - 0.004999 = 3.590001

Che se arrotondiamo otteniamo 3,59. Risposte diverse.

Mi sembra che qualsiasi algoritmo di arrotondamento utilizzato dovrebbe almeno dare la stessa risposta ogni volta che viene eseguito con gli stessi argomenti, ma non questa volta.


A proposito, si è verificato lo stesso errore in Magento 2.2.2
TheKitMurkit

Risposte:


9

Ho Magento 1.8 sul mio server e ho verificato il _deltaRound()metodo. Sembra così adesso.

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

Come puoi vedere, se _roundingDeltas()non è impostato, assume zerocome valore predefinito. È solo per averti notato. Magento Team potrebbe sentire i tuoi dubbi. Hanno risolto il tuo problema in silenzio. :)

MODIFICARE

Analizziamo l'uso di questa funzione applicandola in un esempio in tempo reale. Supponiamo di avere un prodotto nel carrello che è tassabile. La quantità che sto per acquistare è, 5. Dopo aver applicato l'imposta, il prezzo ha un prezzo di $ 10.5356. Quindi questa è la mia situazione

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.

Quindi ora calcoliamo il prezzo reale che produrrà in questa situazione. Sarà

  Total =  10.5356 x 5 = 52.678

Ora supponiamo che magento non usi il _deltaRound()metodo. Arrotonda solo il prezzo del prodotto fino a due decimali e quindi calcola il prezzo totale. In questo caso, il prezzo del prodotto verrà arrotondato 10.54e quindi il prezzo totale sarebbe

  Total = 10.54 x 5 = 52.7

Ora supponiamo che magento stia usando il _deltaRound()metodo e questa funzione arrotonda effettivamente il prezzo del prodotto a due decimali. Oltre a ciò manterrà un valore delta, che in realtà è la differenza tra il prezzo effettivo e il prezzo arrotondato, verrà utilizzato per calcolare il prezzo arrotondato in un secondo momento. Quiinserisci qui la descrizione dell'immagine

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

Ciò significa che il _deltaRound()metodo effettivamente rende l'arrotondamento dei prezzi delle imposte più accurato rispetto ai prezzi delle tasse effettivi. Come hai affermato, questo metodo restituisce valori arrotondati diversi a seconda del valore delta. Questo valore delta in realtà rende più accurato l'arrotondamento fiscale.

In base a ciò, possiamo concludere che, all'aumentare della quantità, se non adottiamo questo metodo, produrrà una grande differenza tra valore arrotondato e valore reale. Ma se usiamo questo metodo, renderà il nostro valore arrotondato il più vicino possibile al valore reale.

Per impostazione predefinita Magento arrotonda a due cifre decimali. Questo è il metodo responsabile dell'arrotondamento di due decimali

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

Se lo impostiamo su 4 o qualcosa del genere, possiamo aumentare ulteriormente la precisione dell'arrotondamento.

Nota: questa è la mia apertura e panoramica. Potrebbe essere o non essere vero. Comunque mi sembra preciso e logico.

Grazie.


Odio davvero il 2 in hard codedroundPrice
David Manners,

@DavidManners: sì, è corretto. Ma il magento usa _deltaRound()per superare in qualche modo la difficoltà. Qualsiasi sia il suo hard coded. In alcuni casi produrrà sicuramente alcune difficoltà
Rajeev K Tomy,

1
Guardando github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/… il valore predefinito è ancora 0,0001 in magento 1.9, che per noi ha provocato un errore rouning con calcoli fiscali sulla spedizione
ProxiBlue

2

Informazioni

Prezzo circolare in Magento basato sul delta operazione di arrotondamento precedente.

app / codice / 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;
}

A volte ciò può causare un errore a causa dell'errore di calcolo delta elevato ( $this->_calculator->round($price)). Ad esempio, per questo motivo, alcuni prezzi possono variare nell'intervallo di ± 1 centesimo .

Soluzione

Per evitare ciò, è necessario migliorare l'accuratezza del calcolo delta.

Modificare

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

per

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

È necessario apportare modifiche in entrambi i file:

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

Non modificare o hackerare i file core! Fai una riscrittura!

La soluzione è stata testata su diverse versioni di Magento 1.9.x, ma forse funzionerà nelle versioni precedenti.

PS

La roundPricefunzione di modifica , come mostrato di seguito, può risolvere il problema dell'errore di arrotondamento, ma può causare altri (ad esempio, alcune piattaforme richiedono arrotondamenti fino a 2 decimali).

app / code / core / Mage / core / Modello / Store.php: 995

public function roundPrice($price)
{
    return round($price, 4);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.