Mengapa Magento menyimpan delta pembulatan saat menghitung pajak

14

Dalam model tax/Sales_Total_Quote_Tax, ada metode _deltaRound()yang membulatkan harga. Ini menambah delta kecil, untuk menghentikan perilaku nondeterministik ketika membulatkan 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;
}

Tapi itu menyimpan delta. Jika tidak dapat menemukan delta yang disimpan, itu membuatnya. Mengapa? Seperti yang saya tahu, ini mengarah ke hasil yang berbeda dengan operasi yang identik.

Katakanlah kita memiliki $price3.595, dan kita tidak memiliki cache $delta. Saat kita melalui metode ini, kita akan mendapatkan $ delta = 0,000001. Kami kemudian mendapatkan $price= 3,595001, yang membulatkan ke 3,60, jadi kami memiliki yang baru $deltasebesar -0,004999. Dan kami mengembalikan 3,60.

Kecuali sekarang kita memiliki delta jadi mari kita lakukan lagi, dengan $price= 3,595. $price= 3,595 - 0,004999 = 3,590001

Yang jika kita bulat, kita mendapat 3,59. Jawaban berbeda.

Menurut saya, algoritma pembulatan apa pun yang digunakan setidaknya harus memberikan jawaban yang sama setiap kali dijalankan dengan argumen yang sama, tetapi tidak kali ini.

Max Bucknell
sumber
BTW, mengalami kesalahan yang sama di Magento 2.2.2
TheKitMurkit

Jawaban:

9

Saya memiliki Magento 1.8 di server saya dan saya telah memeriksa _deltaRound()metodenya. Terlihat seperti ini sekarang.

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

Seperti yang Anda lihat, jika _roundingDeltas()tidak diatur, dibutuhkan zerosebagai nilai default. Hanya untuk memperhatikanmu. Tim Magento mungkin mendengar keraguan Anda. Mereka memecahkan masalah Anda secara diam-diam. :)

EDIT

Mari kita menganalisis penggunaan fungsi ini dengan menerapkannya ke dalam contoh waktu nyata. Misalkan saya memiliki produk dalam keranjang yang kena pajak. Jumlah yang akan saya beli adalah, 5. Setelah menerapkan pajak, prdouct memiliki harga $ 10.5356. Jadi ini situasiku

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.

Jadi sekarang mari kita menghitung harga riil yang akan dihasilkan dalam situasi ini. Boleh jadi

  Total =  10.5356 x 5 = 52.678

Sekarang mari kita asumsikan, Magento tidak menggunakan _deltaRound()metode. Itu hanya membulatkan harga produk hingga dua tempat desimal dan kemudian menghitung harga total. Dalam hal ini, harga produk akan dibulatkan ke 10.54dan karenanya harga total akan menjadi

  Total = 10.54 x 5 = 52.7

Sekarang mari kita asumsikan Magento menggunakan _deltaRound()metode dan fungsi ini sebenarnya membulatkan harga produk menjadi dua desimal. Bersamaan dengan itu akan mempertahankan nilai delta, yang sebenarnya perbedaan antara harga aktual dan harga bulat, akan digunakan untuk menghitung harga bulat nanti. Sinimasukkan deskripsi gambar di sini

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

Itu berarti _deltaRound()metode sebenarnya membuat pembulatan harga pajak lebih akurat ke harga pajak yang sebenarnya. Seperti yang Anda nyatakan metode ini mengembalikan nilai putaran berbeda tergantung pada nilai delta. Nilai delta ini sebenarnya membuat pembulatan pajak lebih akurat.

Menurut ini, kita dapat menyimpulkan bahwa, ketika kuantitas meningkat, jika kita tidak mengadopsi metode ini, itu akan menghasilkan perbedaan besar antara nilai bulat dan nilai aktual. Tetapi jika kita menggunakan metode ini, itu akan membuat nilai bulat kita sedekat mungkin dengan nilai aktual.

Magento secara default putaran ke dua tempat desimal. Ini adalah metode yang bertanggung jawab untuk pembulatan dua tempat desimal

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

Jika kita mengaturnya menjadi 4 atau sesuatu, kita dapat lebih meningkatkan akurasi pembulatan.

Catatan: Ini adalah keterbukaan dan tinjauan umum saya. Itu mungkin atau mungkin tidak benar. Namun sepertinya akurat dan logis untuk saya.

Terima kasih.

Rajeev K Tomy
sumber
Saya benar-benar membenci kode 2 dalamroundPrice
David Manners
@ Davidvider: ya itu benar. Tetapi magento digunakan _deltaRound()untuk mengatasi kesulitan dengan beberapa memperpanjang. Bagaimanapun caranya itu sulit dikodekan. Ini pasti akan menimbulkan beberapa kesulitan dalam beberapa kasus
Rajeev K Tomy
1
Melihat di github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/... nilai defaultnya masih 0,0001 di magento 1.9, yang bagi kami mengakibatkan kesalahan rouning dengan perhitungan pajak pengiriman
ProxiBlue
2

Info

Harga putaran di Magento berdasarkan delta operasi pembulatan sebelumnya.

app / kode / inti / Penyihir / Pajak / Model / Penjualan / Total / Penawaran / Tax.php: 1392 app / kode / inti / Penyihir / Pajak / Model / Penjualan / Total / Penawaran / 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;
}

Terkadang ini dapat menyebabkan kesalahan karena kesalahan perhitungan delta tinggi ( $this->_calculator->round($price)). Misalnya, untuk alasan ini, beberapa harga dapat bervariasi dalam kisaran ± 1 sen .

Larutan

Untuk menghindari ini, Anda perlu meningkatkan akurasi perhitungan delta.

Perubahan

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

untuk

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

Perubahan perlu dilakukan di kedua file:

app / kode / inti / Penyihir / Pajak / Model / Penjualan / Total / Penawaran / Tax.php: 1392 app / kode / inti / Penyihir / Pajak / Model / Penjualan / Total / Penawaran / Subtotal.php: 719

Jangan memodifikasi atau meretas file inti! Lakukan penulisan ulang!

Solusi ini diuji pada versi Magento 1.9.x yang berbeda, tetapi mungkin ini akan bekerja di versi yang lebih lama.

PS

Ubah roundPricefungsi, seperti yang ditunjukkan di bawah ini, dapat memecahkan masalah kesalahan pembulatan, tetapi dapat menyebabkan yang lain (misalnya, beberapa platform memerlukan pembulatan hingga 2 tempat desimal).

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

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