fix discount calulation for percentage coupon, various bug fixes in tax rounding, add test
This commit is contained in:
parent
7f3e564c14
commit
de1f75df12
|
|
@ -324,16 +324,14 @@ class CartRule
|
|||
break;
|
||||
}
|
||||
|
||||
$item->discount_amount = round(
|
||||
min(
|
||||
$item->discount_amount + $discountAmount,
|
||||
$item->price * $quantity + $item->tax_amount
|
||||
),2);
|
||||
$item->base_discount_amount = round(
|
||||
min(
|
||||
$item->base_discount_amount + $baseDiscountAmount,
|
||||
$item->base_price * $quantity + $item->base_tax_amount
|
||||
), 2);
|
||||
$item->discount_amount = min(
|
||||
$item->discount_amount + $discountAmount,
|
||||
$item->price * $quantity + $item->tax_amount
|
||||
);
|
||||
$item->base_discount_amount = min(
|
||||
$item->base_discount_amount + $baseDiscountAmount,
|
||||
$item->base_price * $quantity + $item->base_tax_amount
|
||||
);
|
||||
|
||||
$appliedRuleIds[$rule->id] = $rule->id;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Webkul\Checkout;
|
||||
|
||||
use Webkul\Checkout\Models\Cart as CartModel;
|
||||
use Webkul\Checkout\Models\CartAddress;
|
||||
use Webkul\Checkout\Repositories\CartRepository;
|
||||
use Webkul\Checkout\Repositories\CartItemRepository;
|
||||
|
|
@ -600,6 +601,8 @@ class Cart
|
|||
$cart->base_discount_amount += $shipping->base_discount_amount;
|
||||
}
|
||||
|
||||
$cart = $this->finalizeCartTotals($cart);
|
||||
|
||||
$quantities = 0;
|
||||
|
||||
foreach ($cart->items as $item) {
|
||||
|
|
@ -1052,6 +1055,25 @@ class Cart
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Round cart totals
|
||||
*
|
||||
* @param \Webkul\Checkout\Models\Cart $cart
|
||||
*
|
||||
* @return \Webkul\Checkout\Models\Cart
|
||||
*/
|
||||
private function finalizeCartTotals(CartModel $cart): CartModel
|
||||
{
|
||||
$cart->discount_amount = round($cart->discount_amount, 2);
|
||||
$cart->base_discount_amount = round($cart->base_discount_amount, 2);
|
||||
|
||||
$cart->grand_total = round($cart->grand_total, 2);
|
||||
$cart->grand_total = round($cart->grand_total, 2);
|
||||
$cart->base_grand_total = round($cart->base_grand_total, 2);
|
||||
|
||||
return $cart;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $user
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,16 +13,16 @@ class Tax
|
|||
/**
|
||||
* Returns an array with tax rates and tax amount
|
||||
*
|
||||
* @param \Webkul\Checkout\Contracts\Cart $cart
|
||||
* @param bool $asBase
|
||||
* @param object $that
|
||||
* @param bool $asBase
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getTaxRatesWithAmount(\Webkul\Checkout\Contracts\Cart $cart, bool $asBase = false): array
|
||||
public static function getTaxRatesWithAmount(object $that, bool $asBase = false): array
|
||||
{
|
||||
$taxes = [];
|
||||
|
||||
foreach ($cart->items as $item) {
|
||||
foreach ($that->items as $item) {
|
||||
$taxRate = (string) round((float) $item->tax_percent, self::TAX_RATE_PRECISION);
|
||||
|
||||
if (! array_key_exists($taxRate, $taxes)) {
|
||||
|
|
@ -43,14 +43,14 @@ class Tax
|
|||
/**
|
||||
* Returns the total tax amount
|
||||
*
|
||||
* @param \Webkul\Checkout\Contracts\Cart $cart
|
||||
* @param bool $asBase
|
||||
* @param object $that
|
||||
* @param bool $asBase
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function getTaxTotal(\Webkul\Checkout\Contracts\Cart $cart, bool $asBase = false): float
|
||||
public static function getTaxTotal(object $that, bool $asBase = false): float
|
||||
{
|
||||
$taxes = self::getTaxRatesWithAmount($cart, $asBase);
|
||||
$taxes = self::getTaxRatesWithAmount($that, $asBase);
|
||||
|
||||
$result = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class cartRuleWithCoupon
|
|||
|
||||
class expectedCartItem
|
||||
{
|
||||
public const ITEM_DISCOUNT_AMOUNT_PRECISION = 2;
|
||||
public const ITEM_DISCOUNT_AMOUNT_PRECISION = 4;
|
||||
public const ITEM_TAX_AMOUNT_PRECISION = 4;
|
||||
|
||||
public $cart_id;
|
||||
|
|
@ -106,6 +106,7 @@ class expectedCartItem
|
|||
class expectedCart
|
||||
{
|
||||
public const CART_TOTAL_PRECISION = 2;
|
||||
public const CART_GRAND_TOTAL_PRECISION = 4;
|
||||
|
||||
public $customer_id;
|
||||
public $id;
|
||||
|
|
@ -138,11 +139,15 @@ class expectedCart
|
|||
{
|
||||
$this->sub_total = round($this->sub_total, self::CART_TOTAL_PRECISION);
|
||||
$this->tax_total = round($this->tax_total, self::CART_TOTAL_PRECISION);
|
||||
$this->grand_total = round($this->sub_total + $this->tax_total - $this->discount_amount, self::CART_TOTAL_PRECISION);
|
||||
$this->discount_amount = round($this->discount_amount, self::CART_TOTAL_PRECISION);
|
||||
$this->grand_total = round($this->sub_total + $this->tax_total - $this->discount_amount,
|
||||
self::CART_GRAND_TOTAL_PRECISION);
|
||||
|
||||
$this->base_sub_total = round($this->base_sub_total, self::CART_TOTAL_PRECISION);
|
||||
$this->base_tax_total = round($this->base_tax_total, self::CART_TOTAL_PRECISION);
|
||||
$this->base_grand_total = round($this->base_sub_total + $this->base_tax_total - $this->base_discount_amount, self::CART_TOTAL_PRECISION);
|
||||
$this->base_discount_amount = round($this->base_discount_amount, self::CART_TOTAL_PRECISION);
|
||||
$this->base_grand_total = round($this->base_sub_total + $this->base_tax_total - $this->base_discount_amount,
|
||||
self::CART_GRAND_TOTAL_PRECISION);
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
|
|
@ -579,6 +584,102 @@ class CartRuleCest
|
|||
}
|
||||
}
|
||||
|
||||
public function checkExampleCase(UnitTester $I)
|
||||
{
|
||||
config(['app.default_country' => 'DE']);
|
||||
|
||||
$faker = Factory::create();
|
||||
|
||||
$customer = $I->have(Customer::class);
|
||||
|
||||
auth()->guard('customer')->loginUsingId($customer->id);
|
||||
Event::dispatch('customer.after.login', $customer['email']);
|
||||
|
||||
$this->sessionToken = $faker->uuid;
|
||||
session(['_token' => $this->sessionToken]);
|
||||
|
||||
$tax = $I->have(TaxRate::class, [
|
||||
'country' => 'DE',
|
||||
'tax_rate' => 19.0,
|
||||
]);
|
||||
|
||||
$taxCategorie = $I->have(TaxCategory::class);
|
||||
|
||||
$I->have(TaxMap::class, [
|
||||
'tax_rate_id' => $tax->id,
|
||||
'tax_category_id' => $taxCategorie->id,
|
||||
]);
|
||||
|
||||
$productConfig = [
|
||||
'attributeValues' => [
|
||||
'price' => 23.92,
|
||||
'tax_category_id' => $taxCategorie->id,
|
||||
],
|
||||
];
|
||||
$product = $I->haveProduct(Laravel5Helper::SIMPLE_PRODUCT, $productConfig);
|
||||
|
||||
$ruleConfig = [
|
||||
'action_type' => self::ACTION_TYPE_PERCENTAGE,
|
||||
'discount_amount' => 100,
|
||||
'conditions' => [
|
||||
[
|
||||
'attribute' => 'product|sku',
|
||||
'value' => $product->sku,
|
||||
'operator' => '==',
|
||||
'attribute_type' => 'text',
|
||||
],
|
||||
],
|
||||
];
|
||||
$cartRule = $I->have(CartRule::class, $ruleConfig);
|
||||
|
||||
DB::table('cart_rule_channels')->insert([
|
||||
'cart_rule_id' => $cartRule->id,
|
||||
'channel_id' => core()->getCurrentChannel()->id,
|
||||
]);
|
||||
|
||||
$guestCustomerGroup = $I->grabRecord('customer_groups', ['code' => 'guest']);
|
||||
DB::table('cart_rule_customer_groups')->insert([
|
||||
'cart_rule_id' => $cartRule->id,
|
||||
'customer_group_id' => $guestCustomerGroup['id'],
|
||||
]);
|
||||
|
||||
$generalCustomerGroup = $I->grabRecord('customer_groups', ['code' => 'general']);
|
||||
DB::table('cart_rule_customer_groups')->insert([
|
||||
'cart_rule_id' => $cartRule->id,
|
||||
'customer_group_id' => $generalCustomerGroup['id'],
|
||||
]);
|
||||
|
||||
$coupon = $I->have(CartRuleCoupon::class, [
|
||||
'code' => 'AWESOME',
|
||||
'cart_rule_id' => $cartRule->id,
|
||||
]);
|
||||
|
||||
|
||||
$data = [
|
||||
'_token' => session('_token'),
|
||||
'product_id' => $product->id,
|
||||
'quantity' => 1,
|
||||
];
|
||||
cart()->addProduct($product->id, $data);
|
||||
cart()->setCouponCode('AWESOME')->collectTotals();
|
||||
|
||||
$cart = cart()->getCart();
|
||||
$cartItem = $cart->items()->first();
|
||||
|
||||
$I->assertEquals('AWESOME', $cartItem['coupon_code']);
|
||||
$I->assertEquals(23.92, $cartItem['price']);
|
||||
$I->assertEquals(19.0, $cartItem['tax_percent']);
|
||||
$I->assertEquals(4.5448, $cartItem['tax_amount']);
|
||||
$I->assertEquals(28.4648, $cartItem['discount_amount']);
|
||||
|
||||
$I->assertEquals('AWESOME', $cart->coupon_code);
|
||||
$I->assertEquals(23.92, $cart->sub_total);
|
||||
$I->assertEquals(4.54, $cart->tax_total);
|
||||
$I->assertEquals(28.46, $cart->discount_amount);
|
||||
// 23.92 + 4.54 - 28.46 = 0.00
|
||||
$I->assertEquals(0.00, $cart->grand_total);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Codeception\Example $scenario
|
||||
* @param \Tests\Unit\Category\cartRuleWithCoupon $cartRuleWithCoupon
|
||||
|
|
@ -586,19 +687,15 @@ class CartRuleCest
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getExpectedCartItems(
|
||||
Example $scenario,
|
||||
?cartRuleWithCoupon $cartRuleWithCoupon,
|
||||
int $cartID
|
||||
): array {
|
||||
private function getExpectedCartItems(Example $scenario, ?cartRuleWithCoupon $cartRuleWithCoupon, int $cartID): array
|
||||
{
|
||||
$cartItems = [];
|
||||
|
||||
foreach ($scenario['productSequence'] as $key => $item) {
|
||||
$pos = $this->array_find(
|
||||
'product_id',
|
||||
$this->products[$scenario['productSequence'][$key]]->id,
|
||||
$cartItems,
|
||||
true
|
||||
$cartItems
|
||||
);
|
||||
|
||||
if ($pos === null) {
|
||||
|
|
@ -793,11 +890,8 @@ class CartRuleCest
|
|||
*
|
||||
* @return \Tests\Unit\Category\expectedCart
|
||||
*/
|
||||
private function getExpectedCart(
|
||||
int $cartId,
|
||||
array $expectedCartItems,
|
||||
?cartRuleWithCoupon $cartRuleWithCoupon
|
||||
): expectedCart {
|
||||
private function getExpectedCart(int $cartId, array $expectedCartItems, ?cartRuleWithCoupon $cartRuleWithCoupon): expectedCart
|
||||
{
|
||||
$cart = new expectedCart(
|
||||
$cartId,
|
||||
auth()->guard('customer')->user()->id
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ class TaxCest
|
|||
private const PRODUCT2_QTY = 7;
|
||||
|
||||
private const CART_TOTAL_PRECISION = 2;
|
||||
private const TAX_AMOUNT_PRECISION = 2;
|
||||
|
||||
public function _before(UnitTester $I)
|
||||
{
|
||||
|
|
@ -73,16 +72,17 @@ class TaxCest
|
|||
'quantity' => self::PRODUCT2_QTY,
|
||||
]);
|
||||
|
||||
// rounded by precision of 2 because this are sums of corresponding tax categories
|
||||
$expectedTaxAmount1 = round(
|
||||
round(self::PRODUCT1_QTY * $product1->price, self::CART_TOTAL_PRECISION)
|
||||
* $tax1->tax_rate / 100,
|
||||
self::TAX_AMOUNT_PRECISION
|
||||
self::CART_TOTAL_PRECISION
|
||||
);
|
||||
|
||||
$expectedTaxAmount2 = round(
|
||||
round(self::PRODUCT2_QTY * $product2->price, self::CART_TOTAL_PRECISION)
|
||||
* $tax2->tax_rate / 100,
|
||||
self::TAX_AMOUNT_PRECISION
|
||||
self::CART_TOTAL_PRECISION
|
||||
);
|
||||
|
||||
$this->scenario = [
|
||||
|
|
@ -92,7 +92,7 @@ class TaxCest
|
|||
(string)round((float)$tax2->tax_rate, 4) => $expectedTaxAmount2,
|
||||
],
|
||||
'expectedTaxTotal' =>
|
||||
round($expectedTaxAmount1 + $expectedTaxAmount2, self::TAX_AMOUNT_PRECISION),
|
||||
round($expectedTaxAmount1 + $expectedTaxAmount2, self::CART_TOTAL_PRECISION),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue