287 lines
11 KiB
PHP
287 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KupShop\BonusProgramBundle\Tests;
|
|
|
|
use Doctrine\DBAL\Exception;
|
|
use KupShop\BonusProgramBundle\Utils\BonusProvider;
|
|
use KupShop\DevelopmentBundle\Util\Tests\CartTestTrait;
|
|
use KupShop\KupShopBundle\Context\UserContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\OrderDiscountBundle\Util\DiscountManager;
|
|
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
|
use KupShop\OrderingBundle\Util\Purchase\PurchaseUtil;
|
|
use Query\Operator;
|
|
|
|
class BonusProgramDiscountsTest extends \DatabaseTestCase
|
|
{
|
|
use CartTestTrait;
|
|
|
|
private const USER_WITH_POINTS = 1;
|
|
private const DISCOUNT_ACTION_ID = 11;
|
|
private const POINTS_USED = 200;
|
|
|
|
private PurchaseUtil $purchaseUtil;
|
|
private DiscountManager $discountManager;
|
|
private OrderInfo $orderInfo;
|
|
private BonusProvider $bonusProvider;
|
|
|
|
public function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->purchaseUtil = $this->get(PurchaseUtil::class);
|
|
$this->discountManager = $this->get(DiscountManager::class);
|
|
$this->orderInfo = $this->get(OrderInfo::class);
|
|
$this->bonusProvider = $this->get(BonusProvider::class);
|
|
}
|
|
|
|
/**
|
|
* Test pro bonus program akci s produktovým filtrem.
|
|
*
|
|
* Košík s 1 produktem, uživatel chce použít body pro získání slevy, kterou na základě nastaveného produkt filtru
|
|
* buď získá a nebo nezíská.
|
|
*
|
|
* @dataProvider data_testBonusProgramActionWithProductsFilter
|
|
*/
|
|
public function testBonusProgramActionWithProductsFilter(callable $setModule, ?string $filter, ?array $expectedDiscount): void
|
|
{
|
|
$setModule();
|
|
|
|
$this->setBonusProgramActionProductsFilter(
|
|
filter: $filter ? json_decode($filter, true) : null,
|
|
);
|
|
|
|
$this->loginUser(self::USER_WITH_POINTS);
|
|
$pointsBefore = $this->getActivePoints();
|
|
|
|
$this->prepareCart();
|
|
$this->insertProduct(3);
|
|
|
|
$this->applyBonusPoints(self::POINTS_USED);
|
|
|
|
$order = $this->submitOrder();
|
|
|
|
$itemsDiscounts = $this->orderInfo->getDiscounts($order);
|
|
|
|
if (!$expectedDiscount) {
|
|
// discount is not expected
|
|
$this->assertEmpty($itemsDiscounts, 'Žádna sleva by se neměla aplikovat');
|
|
} else {
|
|
// discount is expected
|
|
$discount = reset($itemsDiscounts);
|
|
$this->assertEquals($expectedDiscount['name'], $discount['name']);
|
|
$this->assertEqualsWithDelta($expectedDiscount['price'], $discount['totalPrice']->asFloat(), 0.01);
|
|
}
|
|
|
|
$pointsAfter = $this->getActivePoints();
|
|
|
|
$this->assertEquals($expectedDiscount ? ($pointsBefore - self::POINTS_USED) : $pointsBefore, $pointsAfter, 'Špatně se odečetly body z bonus programu po použití!');
|
|
}
|
|
|
|
public function data_testBonusProgramActionWithProductsFilter(): iterable
|
|
{
|
|
foreach ($this->data_purchaseStateProvider() as $key => [$setModule]) {
|
|
yield "{$key}: Bonus program action is not used when product filter enabled and does not match any product" => [
|
|
$setModule, '{"enabled":"Y","products":["3"],"products_invert":"invert"}', null,
|
|
];
|
|
|
|
yield "{$key}: Bonus program action is used on products applicable by filter" => [
|
|
$setModule, '{"enabled":"Y","products":["3"]}', ['name' => 'Uplatnění bodů (200)', 'price' => 200],
|
|
];
|
|
|
|
yield "{$key}: Filter is disabled so discount is applicable" => [
|
|
$setModule, '{"enabled":"N"}', ['name' => 'Uplatnění bodů (200)', 'price' => 200],
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test uplatnění bodů z bon. prog. vytvořením ne-produktové položky objednávky.
|
|
* - Pre-cond: košík s 2 produkty (1 + 2 ks), dostatek bodů na účtu uživatele, vytovřená sleva v DB (1 bod = 1 Kč slevy).
|
|
* - Steps:
|
|
* - využiju 200 bodů,
|
|
* - vytvořím objednávku.
|
|
* - Expected:
|
|
* - 1) přídá se mi 3. položka objednávky se zápornou cenou 200 Kč,
|
|
* - 2) cena produktů zůstane stejná, celková cena objednávky se sníží o 200 Kč,
|
|
* - 3) uživateli se odečtou body.
|
|
*
|
|
* @dataProvider data_purchaseStateProvider
|
|
*/
|
|
public function testBonusProgramSeparateItem(callable $setModule): void
|
|
{
|
|
$setModule();
|
|
$this->setDivideDiscountOnBonusAction(false);
|
|
|
|
$this->loginUser(self::USER_WITH_POINTS);
|
|
$pointsBefore = $this->getActivePoints();
|
|
|
|
$this->prepareCart();
|
|
|
|
$this->insertProduct(3);
|
|
$this->insertProduct(11, 22, 2);
|
|
$this->applyBonusPoints(self::POINTS_USED);
|
|
|
|
$order = $this->submitOrder();
|
|
|
|
$items = array_values($order->fetchItems());
|
|
$this->assertCount(3, $items); // 1)
|
|
|
|
$this->assertEqualsWithDelta(732., $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01); // 2)
|
|
|
|
$this->assertEquals(800., $items[0]['piece_price']['value_with_vat']->asFloat()); // 2)
|
|
$this->assertEquals(800., $items[0]['total_price']['value_with_vat']->asFloat());
|
|
$this->assertEquals(1, $items[0]['pieces']);
|
|
|
|
$this->assertEquals(66., $items[1]['piece_price']['value_with_vat']->asFloat()); // 2)
|
|
$this->assertEquals(132., $items[1]['total_price']['value_with_vat']->asFloat());
|
|
$this->assertEquals(2, $items[1]['pieces']);
|
|
|
|
$this->assertEquals(-200., $items[2]['piece_price']['value_with_vat']->asFloat()); // 1)
|
|
$this->assertEquals(-200., $items[2]['total_price']['value_with_vat']->asFloat());
|
|
$this->assertEquals(1, $items[2]['pieces']);
|
|
|
|
$orderDiscounts = $this->orderInfo->getUsedDiscounts($order->id);
|
|
$this->assertEquals([11 => 'Uplatnění bodů'], $orderDiscounts);
|
|
|
|
$itemsDiscounts = $this->orderInfo->getDiscounts($order);
|
|
$this->assertCount(1, $itemsDiscounts);
|
|
$this->assertArrayHasKey('11-Uplatnění bodů (200)', $itemsDiscounts);
|
|
|
|
$discount = array_values($itemsDiscounts)[0];
|
|
$this->assertEqualsWithDelta(200., $discount['totalPrice']->asFloat(), 0.01);
|
|
$this->assertEquals('Uplatnění bodů (200)', $discount['name']);
|
|
|
|
$pointsAfter = $this->getActivePoints();
|
|
$this->assertEquals($pointsBefore - self::POINTS_USED, $pointsAfter, 'Špatně se odečetly body z bonus programu po použití!'); // 3)
|
|
}
|
|
|
|
/**
|
|
* Test uplatnění bodů z bon. prog. rozpočítáním slevy na produkty objednávky.
|
|
* - Pre-cond: košík s 2 produkty (1 + _1_ ks), dostatek bodů na účtu uživatele, vytovřená sleva v DB (1 bod = 1 Kč slevy).
|
|
* - Steps:
|
|
* - využiju 200 bodů,
|
|
* - vytvořím objednávku.
|
|
* - Expected:
|
|
* - 1) počet produktů v objednávce se nezmění,
|
|
* - 2) cena produktů se sníží o 200 Kč (rozpočítáno), celková cena objednávky se sníží o 200 Kč,
|
|
* - 3) uživateli se odečtou body.
|
|
*
|
|
* @dataProvider data_purchaseStateProvider
|
|
*/
|
|
public function testBonusProgramDiscountDivide(callable $setModule): void
|
|
{
|
|
$setModule();
|
|
$this->setDivideDiscountOnBonusAction(true);
|
|
|
|
$this->loginUser(self::USER_WITH_POINTS);
|
|
$pointsBefore = $this->getActivePoints();
|
|
|
|
$this->prepareCart();
|
|
|
|
$this->insertProduct(3);
|
|
$this->insertProduct(11, 22);
|
|
$this->applyBonusPoints(self::POINTS_USED);
|
|
|
|
$order = $this->submitOrder();
|
|
|
|
$items = array_values($order->fetchItems());
|
|
$this->assertCount(2, $items, 'Měly by tu být 2 položky, ale jsou: '.implode(','.PHP_EOL, array_column($items, 'descr')));
|
|
|
|
$this->assertEqualsWithDelta(666., $order->getTotalPrice()->getPriceWithVat()->asFloat(), 0.01);
|
|
|
|
$this->assertEquals(615.24, $items[0]['piece_price']['value_with_vat']->asFloat());
|
|
$this->assertEquals(615.24, $items[0]['total_price']['value_with_vat']->asFloat());
|
|
$this->assertEquals(1, $items[0]['pieces']);
|
|
|
|
$this->assertEquals(50.76, $items[1]['piece_price']['value_with_vat']->asFloat());
|
|
$this->assertEquals(50.76, $items[1]['total_price']['value_with_vat']->asFloat());
|
|
$this->assertEquals(1, $items[1]['pieces']);
|
|
|
|
$this->assertArrayNotHasKey(2, $items);
|
|
|
|
$orderDiscounts = $this->orderInfo->getUsedDiscounts($order->id);
|
|
$this->assertEquals([11 => 'Uplatnění bodů'], $orderDiscounts);
|
|
|
|
$itemsDiscounts = $this->orderInfo->getDiscounts($order);
|
|
$this->assertCount(1, $itemsDiscounts);
|
|
$this->assertArrayHasKey('11-Uplatnění bodů (200)', $itemsDiscounts);
|
|
|
|
$discount = array_values($itemsDiscounts)[0];
|
|
$this->assertEqualsWithDelta(200., $discount['totalPrice']->asFloat(), 0.01);
|
|
$this->assertEquals('Uplatnění bodů (200)', $discount['name']);
|
|
$partials = array_map(fn ($partial) => $partial->asFloat(), $discount['itemsPartials']);
|
|
|
|
$this->assertEqualsWithDelta([184.76, 15.24], array_values($partials), 0.01);
|
|
|
|
$pointsAfter = $this->getActivePoints();
|
|
$this->assertEquals($pointsBefore - self::POINTS_USED, $pointsAfter, 'Špatně se odečetly body z bonus programu po použití!');
|
|
}
|
|
|
|
private function applyBonusPoints(int $numberOfPoints): void
|
|
{
|
|
$this->discountManager->setPurchaseState($this->purchaseUtil->getPurchaseState());
|
|
|
|
foreach ($this->discountManager->loadDiscountsEntities($this->discountManager->getDiscountForCheck()) as $discount) {
|
|
foreach ($discount->getActions() as $action) {
|
|
if (($action['type'] ?? null) !== 'bonus_program') {
|
|
continue;
|
|
}
|
|
|
|
$this->cart->setActionHandlersData([
|
|
$action['id'] => ['bonus_points' => (string) $numberOfPoints],
|
|
]);
|
|
}
|
|
}
|
|
|
|
$this->cart->invalidatePurchaseState();
|
|
}
|
|
|
|
/**
|
|
* @throws Exception
|
|
*/
|
|
private function setDivideDiscountOnBonusAction(bool $divideDiscount): void
|
|
{
|
|
$value = $divideDiscount ? 'Y' : 'N';
|
|
|
|
$query = sqlQueryBuilder()
|
|
->update('order_discounts_actions')
|
|
->andWhere(Operator::equals(['id' => self::DISCOUNT_ACTION_ID]))
|
|
->set('data', "JSON_SET(data, '$.divideDiscountPrice', '{$value}')");
|
|
|
|
$query->execute();
|
|
}
|
|
|
|
private function getActivePoints(): int
|
|
{
|
|
$user = Contexts::get(UserContext::class)->getActive();
|
|
|
|
return $this->bonusProvider->getActivePointsAmount($user);
|
|
}
|
|
|
|
private function setBonusProgramActionProductsFilter(?array $filter): void
|
|
{
|
|
$data = sqlQueryBuilder()
|
|
->select('data')
|
|
->from('order_discounts_actions')
|
|
->where(Operator::equals(['id' => self::DISCOUNT_ACTION_ID]))
|
|
->execute()->fetchOne();
|
|
|
|
$data = json_decode($data ?: '', true) ?: [];
|
|
$data['filter'] = $filter;
|
|
|
|
sqlQueryBuilder()
|
|
->update('order_discounts_actions')
|
|
->directValues(['data' => json_encode($data)])
|
|
->where(Operator::equals(['id' => self::DISCOUNT_ACTION_ID]))
|
|
->execute();
|
|
}
|
|
|
|
public function getDataSet(): object
|
|
{
|
|
return $this->getJsonDataSetFromFile();
|
|
}
|
|
}
|