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(); } }