396 lines
14 KiB
PHP
396 lines
14 KiB
PHP
<?php
|
|
|
|
namespace KupShop\OrderDiscountBundle\Tests;
|
|
|
|
use KupShop\DevelopmentBundle\Util\Tests\CartTestTrait;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Util\Price\Price;
|
|
use KupShop\OrderDiscountBundle\Entity\OrderDiscount;
|
|
use KupShop\OrderDiscountBundle\OrderDiscountLocator;
|
|
use KupShop\OrderDiscountBundle\Util\DiscountManager;
|
|
use KupShop\OrderingBundle\Entity\Purchase\DiscountPurchaseItem;
|
|
use KupShop\OrderingBundle\Entity\Purchase\ProductPurchaseItem;
|
|
use KupShop\OrderingBundle\Entity\Purchase\PurchaseState;
|
|
use KupShop\OrderingBundle\Util\Purchase\PurchaseUtil;
|
|
|
|
class OrderDiscountsTest extends \DatabaseTestCase
|
|
{
|
|
use CartTestTrait;
|
|
use OrderTestDiscountTrait;
|
|
|
|
public function testTriggersLocator()
|
|
{
|
|
$discountLocator = $this->get(OrderDiscountLocator::class);
|
|
|
|
$triggers = $discountLocator->getTriggers();
|
|
|
|
$this->assertNotNull($triggers);
|
|
}
|
|
|
|
public function testFilterDiscounts()
|
|
{
|
|
$this->activateDiscount([1]);
|
|
$discountManager = $this->get(DiscountManager::class);
|
|
$purchaseStateUtil = $this->get(PurchaseUtil::class);
|
|
|
|
$this->prepareCart();
|
|
$this->insertProduct(9);
|
|
$this->cart->addCoupon('TEST321');
|
|
$this->cart->createFromDB();
|
|
|
|
$pState = $purchaseStateUtil->createStateFromCart($this->cart);
|
|
$discountManager->setPurchaseState($pState);
|
|
|
|
$discounts = $discountManager->getDiscountForCheck();
|
|
|
|
$this->assertEquals([
|
|
1 => [
|
|
'id' => '1',
|
|
'date_created' => '2019-09-13 12:00:00',
|
|
'name' => 'Test discount',
|
|
'display_name' => 'Discount 10%',
|
|
'uses_count' => '0',
|
|
'active' => 'Y',
|
|
'data' => null,
|
|
'position' => 0,
|
|
'type' => null,
|
|
],
|
|
], $discounts);
|
|
}
|
|
|
|
public function testApplyAction()
|
|
{
|
|
$this->activateDiscount([1]);
|
|
$discountManager = $this->get(DiscountManager::class);
|
|
$purchaseStateUtil = $this->get(PurchaseUtil::class);
|
|
|
|
$this->prepareCart();
|
|
$this->insertProduct(9);
|
|
$this->cart->addCoupon('TEST321');
|
|
$this->cart->createFromDB();
|
|
|
|
$pState = $purchaseStateUtil->createStateFromCart($this->cart);
|
|
$discountManager->setPurchaseState($pState);
|
|
|
|
$discountManager->recalculate();
|
|
|
|
$this->assertEquals('Discount 10% (TEST321) (10%)', $pState->getDiscounts()[0]->name);
|
|
}
|
|
|
|
public function testGetOrderDiscount()
|
|
{
|
|
/** @var DiscountManager $discountManager */
|
|
$discountManager = $this->get(DiscountManager::class);
|
|
|
|
$orderDiscount = $this->selectSQL('order_discounts', ['id' => 1])->fetch();
|
|
$orderDiscount = $discountManager->getOrderDiscount($orderDiscount);
|
|
|
|
$this->assertInstanceOf(OrderDiscount::class, $orderDiscount);
|
|
$this->assertEquals(1, $orderDiscount->getId());
|
|
}
|
|
|
|
public function testProductDiscountInCart(): void
|
|
{
|
|
$this->activateDiscount([14]);
|
|
|
|
// Create cart
|
|
$this->prepareCart();
|
|
$this->insertProduct(9);
|
|
|
|
$e = null;
|
|
try {
|
|
$this->cart->createFromDB();
|
|
} catch (\Exception $e) {
|
|
}
|
|
|
|
// Odhalí například exceptionu "Serialization of 'Closure' is not allowed" při serializaci PurchaseStatu
|
|
$this->assertNull($e, 'Exception should not be thrown when creating cart');
|
|
|
|
$purchaseState = $this->cart->getPurchaseState();
|
|
|
|
$this->assertCount(1, $purchaseState->getDiscounts());
|
|
$this->assertInstanceOf(DiscountPurchaseItem::class, $purchaseState->getDiscounts()[0]);
|
|
}
|
|
|
|
public function testDiscountsInCart()
|
|
{
|
|
$this->activateDiscount([1, 2]);
|
|
|
|
// Create cart
|
|
$this->prepareCart();
|
|
$this->mockDiscountManager();
|
|
$this->insertProduct(9);
|
|
|
|
$this->cart->createFromDB();
|
|
|
|
$purchaseState = $this->cart->getPurchaseState();
|
|
|
|
$this->assertCount(2, $purchaseState->getDiscounts());
|
|
$this->assertInstanceOf(DiscountPurchaseItem::class, $purchaseState->getDiscounts()[0]);
|
|
$this->assertInstanceOf(ProductPurchaseItem::class, $purchaseState->getDiscounts()[1]);
|
|
$this->assertEquals(5899, $this->cart->totalPricePay->asFloat(), '5999 (cena produktu) - 100 (sleva) = 5899');
|
|
}
|
|
|
|
public function testDiscountsInOrder()
|
|
{
|
|
$this->activateDiscount([1, 2]);
|
|
|
|
// Create cart
|
|
$this->prepareCart();
|
|
$this->mockDiscountManager();
|
|
$this->insertProduct(9);
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$order = $this->order;
|
|
$items = $order->fetchItems();
|
|
|
|
$this->assertCount(3, $items);
|
|
|
|
$discounts = sqlQuery('SELECT * FROM order_discounts')->fetchAll();
|
|
|
|
$this->assertEquals(1, $discounts[0]['uses_count'], 'Otestuje, ze se navysi pocet pouziti');
|
|
$this->assertEquals(1, $discounts[1]['uses_count'], 'Otestuje, ze se navysi pocet pouziti');
|
|
|
|
$orderDiscountsOrders = sqlQuery('SELECT * FROM order_discounts_orders')->fetchAll();
|
|
|
|
$this->assertCount(2, $orderDiscountsOrders, 'Otestuje, ze se vytvori relace mezi objednavkou a slevou');
|
|
}
|
|
|
|
public function testMultipleCouponsUsed()
|
|
{
|
|
$this->activateDiscount([1]);
|
|
|
|
$this->prepareCart();
|
|
$this->insertProduct(9);
|
|
$this->cart->addCoupon('TEST321');
|
|
$this->cart->addCoupon('TEST3213');
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$order = $this->order;
|
|
$items = $order->fetchItems();
|
|
|
|
$this->assertCount(3, $items);
|
|
}
|
|
|
|
public function testUseCouponActionUnusedDiscount()
|
|
{
|
|
$this->activateDiscount([361]);
|
|
|
|
$this->prepareCart();
|
|
$this->setDeliveryType(6);
|
|
|
|
$this->insertProduct(1, 16);
|
|
$this->cart->addCoupon('OVEOMN46X5');
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$order = $this->order;
|
|
$items = $order->fetchItems();
|
|
|
|
// Sleva se nevztahuje na dopravu a kupon není vyčerpaný celý
|
|
$this->assertCount(4, $items, 'Produkt + kupon + doprava/platby + zbytek kuponu = 4');
|
|
$key = array_keys($items)[1];
|
|
$this->assertEqualsWithDelta(-10000.0, $items[$key]['total_price']['value_with_vat']->asFloat(), 0.01, 'Nesedi uplatnena castka s castkou kuponu - kupon musi byt uplatnen cely 10000 Kc');
|
|
$this->assertEqualsWithDelta(0, $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01, 'Objednavka ma byt zadarmo');
|
|
}
|
|
|
|
public function testUseCouponActionFreeDelivery()
|
|
{
|
|
$this->activateDiscount([361]);
|
|
|
|
$this->prepareCart();
|
|
$this->setDeliveryType(4); // nezapočítat od 2000
|
|
|
|
$this->insertProduct(1, 16, 13); // 12 * 800 = 10400 => doprava zdarma
|
|
$this->cart->addCoupon('OVEOMN46X5'); // poukaz 10000
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$this->assertEquals(10400, $this->cart->totalPriceWithVat->asFloat()); // produkty
|
|
$this->assertEquals(10000, $this->cart->totalDiscountPrice->asFloat()); // slevy
|
|
$this->assertEquals(400, $this->cart->totalPricePay->asFloat()); // k uhrade
|
|
$priceForDelivery = $this->cart->getTotalPriceForDelivery(); // cena pro vypocet ceny dopravy
|
|
$this->assertEquals(10400, $priceForDelivery->getPriceWithVat()->asFloat(), 'Aplikovaný poukaz nesnižuje hodnotu objednávky pro dopravu zdarma.');
|
|
|
|
$order = $this->order;
|
|
$items = $order->fetchItems();
|
|
|
|
// doprava zdarma (polozka doprava/platba neni) a kupon je vyčerpaný celý (polozka zbytek kuponu neni)
|
|
$this->assertCount(2, $items, 'Produkt + kupon = 2');
|
|
$key = array_keys($items)[1]; // uplatneni kuponu = 10000
|
|
$this->assertEqualsWithDelta(-10000.0, $items[$key]['total_price']['value_with_vat']->asFloat(), 0.01, 'Nesedi uplatnena castka s castkou kuponu - kupon musi byt uplatnen cely 10000 Kc');
|
|
$this->assertEqualsWithDelta(400, $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01);
|
|
}
|
|
|
|
public function testUseCouponAction()
|
|
{
|
|
$this->activateDiscount([361]);
|
|
|
|
$this->prepareCart();
|
|
|
|
$this->insertProduct(9, null, 2);
|
|
$this->cart->addCoupon('OVEOMN46X5');
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$order = $this->order;
|
|
$items = $order->fetchItems();
|
|
|
|
// Kupon je vyčerpaný celý a zbývá něco doplatit
|
|
$this->assertCount(2, $items, 'Produkt + uplatneny cely kupon = 2');
|
|
$this->assertEqualsWithDelta(1998.0, $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01, 'Za objednavku zbyva doplatit 11998 - 10000 = 1998');
|
|
}
|
|
|
|
public function testUseCouponActionEurCzk()
|
|
{
|
|
$this->activateDiscount([503]);
|
|
|
|
$this->prepareCart();
|
|
$this->insertProduct(9);
|
|
$this->cart->addCoupon('TEST_ZBYTEK');
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$order = $this->order;
|
|
$items = $order->fetchItems();
|
|
|
|
// Přebytek v jiné měně
|
|
$this->assertCount(3, $items, 'Produkt + kupon + zbytek = 3ks');
|
|
|
|
$key = array_keys($items)[1];
|
|
$this->assertEqualsWithDelta(-52000.0, $items[$key]['total_price']['value_with_vat']->asFloat(), 0.1, 'Kupon 2000 EUR je uplatnen jako kupon 52 000Kc');
|
|
$key = array_keys($items)[2];
|
|
$this->assertEqualsWithDelta(46001.0, $items[$key]['total_price']['value_with_vat']->asFloat(), 0.1, 'Zbytek kuponu 52 000 - 5 999 = 46001 Kc');
|
|
$this->assertEqualsWithDelta(0, $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01, 'Objednavka ma byt zadarmo');
|
|
$this->assertEqualsWithDelta(-1041.15, $order->getTotalPrice()->getPriceWithoutVat()->asFloat(), 0.01, 'Kdyz mam jiny DPH nez produkty, tam cena s DPH muze byt zaporna 4957.85 - 52 000 + 46 001 = -1041,15');
|
|
}
|
|
|
|
/**
|
|
* BUG: Hodnota kuponu je nastavena na `500.500089`. Do objednavky je vlozena spravne hodnota jako 500.50, ale nasledne je
|
|
* tady `KupShop\OrderDiscountBundle\EventListener\OrderItemSubscriber::addRemainingValue` neuplatnena castka `0.0001`
|
|
* protoze tam vznikne mikrorozdil kvuli pouziti `->value(2)` pri ukladani slevy do obj, ale pri porovnavani ty remaining hodnoty
|
|
* tam je sleva bez tohohle zaokrouhleni na dve mista.
|
|
*
|
|
* To stejny vznikalo napr. pri prepoctu CZK do EUR, kdy ten prepocet vysel napr. 19.960078 a do objednavky pak byla vlozena nesmyslne
|
|
* neuplatnena cast i kdyz se to uplatnilo cely.
|
|
*/
|
|
public function testUseCouponActionUnusedDiscountBug(): void
|
|
{
|
|
$this->activateDiscount([785]);
|
|
|
|
$this->prepareCart();
|
|
$this->insertProduct(9);
|
|
$this->cart->addCoupon('KUP500');
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$items = $this->order->fetchItems();
|
|
|
|
$this->assertCount(2, $items, 'Produkt + uplatneny cely kupon = 2');
|
|
$this->assertEquals(-500.50, array_values($items)[1]['total_price']['value_with_vat']->asFloat(), 'Sleva 500.50 Kč');
|
|
}
|
|
|
|
public function testDiscountOnDelivery()
|
|
{
|
|
$this->addModule(\Modules::DELIVERY_TYPES, \Modules::SUB_APPLY_DISCOUNT_ON_DELIVERY);
|
|
$this->activateDiscount([361]);
|
|
|
|
$this->prepareCart();
|
|
$this->setDeliveryType(6);
|
|
|
|
$this->insertProduct(1, 16);
|
|
$this->cart->addCoupon('OVEOMN46X5');
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$order = $this->order;
|
|
$items = $order->fetchItems();
|
|
|
|
// Sleva se vztahuje na dopravu a kupon není vyčerpaný celý
|
|
$this->assertCount(4, $items, 'Produkt + kupon + doprava + zbytek kuponu = 4');
|
|
$this->assertEqualsWithDelta(0.0, $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01, 'Objednavka ma byt zadarmo');
|
|
|
|
$key = array_keys($items)[3];
|
|
$this->assertEqualsWithDelta(9152.0, $items[$key]['total_price']['value_with_vat']->asFloat(), 0.1, 'Zbytek je 10000 - 800 - 48 = 9182 Kc');
|
|
}
|
|
|
|
/** @dataProvider data_purchaseStateProvider */
|
|
public function testGift()
|
|
{
|
|
$this->activateDiscount([15]);
|
|
|
|
$this->prepareCart();
|
|
|
|
$this->insertProduct(1, 16);
|
|
|
|
$this->cart->setActionHandlersData([
|
|
15 => [
|
|
'gift' => true,
|
|
'id_product' => 1,
|
|
],
|
|
]);
|
|
|
|
$this->checkOrderPriceIsSameAsCart();
|
|
|
|
$order = $this->order;
|
|
$items = array_values($order->fetchItems());
|
|
|
|
// Sleva přidává 2ks dárkového produktu za celkem 100 Kc
|
|
$this->assertCount(2, $items, 'Produkt + Dárek');
|
|
$this->assertEqualsWithDelta(900, $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01, '800 za produkt + 100 za 2ks dárků');
|
|
$this->assertEqualsWithDelta(100, $items[1]['total_price']['value_with_vat']->asFloat(), 0.1, 'Dárek celkem za 100 Kc');
|
|
$this->assertEqualsWithDelta(50, $items[1]['piece_price']['value_with_vat']->asFloat(), 0.1, 'Dárek za 50 Kc kus');
|
|
}
|
|
|
|
private function mockDiscountManager()
|
|
{
|
|
$this->set(
|
|
DiscountManager::class,
|
|
new MockDiscountManager(
|
|
$this->get(OrderDiscountLocator::class),
|
|
$this->get(PurchaseUtil::class),
|
|
$this->get(CurrencyContext::class)
|
|
)
|
|
);
|
|
}
|
|
|
|
public function getDataSet()
|
|
{
|
|
return $this->getJsonDataSetFromFile('data.json');
|
|
}
|
|
}
|
|
|
|
class MockDiscountManager extends DiscountManager
|
|
{
|
|
private $purchaseState;
|
|
private $purchaseUtil;
|
|
|
|
private $currencyContext;
|
|
|
|
public function __construct(OrderDiscountLocator $orderDiscountLocator, PurchaseUtil $purchaseUtil, CurrencyContext $currencyContext)
|
|
{
|
|
parent::__construct($orderDiscountLocator, $purchaseUtil);
|
|
|
|
$this->purchaseUtil = $purchaseUtil;
|
|
$this->currencyContext = $currencyContext;
|
|
}
|
|
|
|
public function recalculate($discountsToCheck = null): PurchaseState
|
|
{
|
|
$this->purchaseState->addDiscount(
|
|
new DiscountPurchaseItem('Sleva 100 Kč na nákup', new Price(toDecimal(-100)->removeVat(21), $this->currencyContext->getActive(), 21), 1)
|
|
);
|
|
$this->purchaseState->addDiscount(
|
|
new ProductPurchaseItem(3, null, 1, new Price(toDecimal(0), $this->currencyContext->getActive(), 21), null, 2)
|
|
);
|
|
|
|
return $this->purchaseUtil->recalculateTotalPrices($this->purchaseState);
|
|
}
|
|
|
|
public function setPurchaseState(PurchaseState $purchaseState): void
|
|
{
|
|
$this->purchaseState = $purchaseState;
|
|
}
|
|
}
|