ทำไมวีโอไอพีจึงจัดเก็บเดลต้าการปัดเศษเมื่อคำนวณภาษี


14

ในรูปแบบtax/Sales_Total_Quote_Taxมีวิธีการ_deltaRound()ที่รอบราคา มันเพิ่มเดลต้าขนาดเล็กเพื่อหยุดพฤติกรรม nondeterministic เมื่อปัดเศษ 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;
}

แต่มันเก็บเดลต้า หากไม่สามารถหาเดลต้าที่เก็บไว้ได้ ทำไม? ในฐานะ tar ที่ฉันสามารถบอกได้สิ่งนี้นำไปสู่ผลลัพธ์ที่แตกต่างด้วยการทำงานที่เหมือนกัน

สมมติว่าเรามี$priceของ 3.595 $deltaและเราไม่ได้มีแคช เมื่อเราทำตามวิธีนี้เราจะได้รับ $ delta = 0.000001 จากนั้นเราจะได้รับ$price= 3.595001 ซึ่งปัดเป็น 3.60 ดังนั้นเราจึงมี$delta-0.004999 ใหม่ และเรากลับ 3.60

ยกเว้นตอนนี้เรามีเดลต้าลองทำอีกครั้งกับ$price= 3.595 $price= 3.595 - 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 อาจได้ยินข้อสงสัยของคุณ พวกเขาแก้ไขปัญหาของคุณอย่างเงียบ ๆ :)

แก้ไข

ให้เราวิเคราะห์การใช้งานฟังก์ชั่นนี้โดยนำไปใช้เป็นตัวอย่างตามเวลาจริง สมมติว่าฉันมีสินค้าในตะกร้าซึ่งต้องเสียภาษี ปริมาณที่ฉันจะซื้อคือ 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()วิธีการทำให้การปัดเศษราคาภาษีมีความแม่นยำมากขึ้นสำหรับการกำหนดราคาภาษีจริง ตามที่คุณระบุวิธีการนี้จะส่งกลับค่ารอบที่แตกต่างกันขึ้นอยู่กับค่าของเดลต้า ค่าของเดลต้านี้ทำให้การปัดเศษภาษีถูกต้องมากขึ้น

จากนี้เราสามารถสรุปได้ว่าเมื่อปริมาณเพิ่มขึ้นหากเราไม่ได้ใช้วิธีนี้มันจะสร้างความแตกต่างอย่างมากระหว่างค่าที่ปัดเศษและค่าจริง แต่ถ้าเราใช้วิธีนี้มันจะทำให้ค่ากลมของเราใกล้เคียงกับค่าจริง

วีโอไอพีเป็นค่าเริ่มต้นปัดเศษเป็นทศนิยมสองตำแหน่ง นี่เป็นวิธีการที่รับผิดชอบในการปัดเศษทศนิยมสองตำแหน่ง

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

หากเราตั้งค่าเป็น 4 หรือบางอย่างเราสามารถเพิ่มความแม่นยำของการปัดเศษเพิ่มเติม

หมายเหตุ: นี่คือ openion และภาพรวมของฉัน อาจเป็นจริงหรือไม่ก็ได้ อย่างไรก็ตามดูเหมือนว่าถูกต้องและสมเหตุสมผลสำหรับฉัน

ขอบคุณ


ฉันเกลียดการเข้ารหัสที่ยากลำบาก 2 ในroundPrice
David Manners

@DavidManners: ใช่ถูกต้อง แต่วีโอไอพีใช้_deltaRound()ในการเอาชนะความยากลำบากโดยบางอย่างขยาย ใด ๆ ที่มันยากรหัส มันจะทำให้เกิดปัญหาแน่นอนในบางกรณี
Rajeev K Tomy

1
การดูที่github.com/OpenMage/magento-mirror/blob/magento-1.9/app/code/…ค่าเริ่มต้นยังคงเป็น 0.0001 ใน magento 1.9 ซึ่งทำให้เราเกิดข้อผิดพลาดในการจัดส่งภาษี
ProxiBlue

2

ข้อมูล

ราคาทรงกลมในวีโอไอพีขึ้นอยู่กับพื้นที่สามเหลี่ยมปากแม่น้ำก่อนหน้านี้

แอพ / รหัส / หลัก / ผู้วิเศษ / ภาษี / รุ่น / ยอดขาย / รวม / อ้าง / Tax.php: 1392 app / รหัส / หลัก / ผู้วิเศษ / ภาษี / รุ่น / ยอดขาย / รวม / อ้าง / 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);

จำเป็นต้องทำการเปลี่ยนแปลงในไฟล์ทั้งสอง:

แอพ / รหัส / หลัก / ผู้วิเศษ / ภาษี / รุ่น / ยอดขาย / รวม / อ้าง / Tax.php: 1392 app / รหัส / หลัก / ผู้วิเศษ / ภาษี / รุ่น / ยอดขาย / รวม / อ้าง / Subtotal.php: 719

อย่าดัดแปลงหรือแฮ็คไฟล์หลัก! เขียนใหม่!

โซลูชันได้รับการทดสอบกับ Magento 1.9.x รุ่นต่าง ๆ แต่อาจใช้งานได้กับเวอร์ชั่นก่อนหน้า

PS

roundPriceฟังก์ชั่นเปลี่ยนดังที่แสดงด้านล่างสามารถแก้ปัญหาข้อผิดพลาดในการปัดเศษ แต่อาจทำให้เกิดปัญหาอื่น ๆ ได้ (ตัวอย่างเช่นบางแพลตฟอร์มต้องการปัดเศษทศนิยมสูงสุด 2 ตำแหน่ง)

app / รหัส / core / Mage / core / รุ่น / Store.php: 995

public function roundPrice($price)
{
    return round($price, 4);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.