HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers27/dndin.nl/app/KommaApp/Shop/Tests/Unit/DiscountTest.php
<?php

namespace App\KommaApp\Shop\Tests\Unit;

use App\KommaApp\Shop\Cart\ShoppingCartItem;
use App\KommaApp\Shop\Cart\ShoppingCartItemInterface;
use App\KommaApp\Shop\Cart\ShoppingCartService;
use App\KommaApp\Shop\Cart\ShoppingCartServiceInterface;
use App\KommaApp\Shop\Discounts\Actions\ModifyPriceAction;
use App\KommaApp\Shop\Discounts\Conditions\CouponDiscountCondition;
use App\KommaApp\Shop\Discounts\Conditions\DiscountableCondition;
use App\KommaApp\Shop\Discounts\Conditions\QuantityDiscountCondition;
use App\KommaApp\Shop\Discounts\DiscountableTypes;
use App\KommaApp\Shop\Discounts\Discount;
use App\KommaApp\Shop\Discounts\DiscountService;
use App\KommaApp\Shop\Discounts\DiscountTypes;
use App\KommaApp\Shop\Products\Product\Product;
use App\KommaApp\Shop\Tests\TestCase;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;

class DiscountTest extends TestCase
{
    use DatabaseTransactions; //Automatically rolls back database actions after tests

    /**
     * Runs before each test
     */
    public function setup()
    {
        parent::setup();
    }

    /**
     * Runs after each test
     */
    public function teardown()
    {
        parent::tearDown();
    }

    /**
     * @group Discount
     * @test
     */
    public function DiscountableDiscountCreationTest()
    {
        $discount = new Discount();
        $condition = new DiscountableCondition(ShoppingCartServiceInterface::class);
        $action = new ModifyPriceAction(1000,ModifyPriceAction::MethodAbsolute); //Modify the price by lowering it with 10 euro's, dollars, whatever
        $discount->setDiscountCondition($condition)->setDiscountAction($action);

        $this->assertInstanceOf(DiscountableCondition::class, $discount->getDiscountCondition());
        $this->assertInstanceOf(ModifyPriceAction::class, $discount->getDiscountAction());

        $this->assertEquals($discount->action_settings, $discount->getDiscountAction()->getValue());
        $this->assertEquals($discount->condition_settings, $discount->getDiscountCondition()->getValue());

        //Get the settings from the condition and action. These values are stored in the database so we must check them
        $conditionSettings = $discount->getDiscountCondition()->getValue();
        $actionSettings = $discount->getDiscountAction()->getValue();

        $this->assertEquals(DiscountableTypes::ShoppingCart, $conditionSettings);
        $this->assertEquals('1000,'.ModifyPriceAction::MethodAbsolute, $actionSettings);

        $discount->save();

        $dbModel = Discount::find($discount->id);

        $this->assertInstanceOf(Discount::class, $dbModel);

        $this->assertEquals($dbModel->action_settings, $discount->getDiscountAction()->getValue());
        $this->assertEquals($dbModel->condition_settings, $discount->getDiscountCondition()->getValue());
    }

    /**
     * @group Discount
     * @test
     */
    public function QuantityDiscountCreationTest()
    {
        $discount = new Discount();
        $condition = new QuantityDiscountCondition(ShoppingCartItemInterface::class, 3, [1]);
        $action = new ModifyPriceAction(10,ModifyPriceAction::MethodPercentageWise); //Modify the price by lowering it with 10%
        $discount->setDiscountCondition($condition)->setDiscountAction($action);

        $this->assertInstanceOf(QuantityDiscountCondition::class, $discount->getDiscountCondition());
        $this->assertInstanceOf(ModifyPriceAction::class, $discount->getDiscountAction());

        $this->assertEquals($discount->action_settings, $discount->getDiscountAction()->getValue());
        $this->assertEquals($discount->condition_settings, $discount->getDiscountCondition()->getValue());

        //Get the settings from the condition and action. These values are stored in the database so we must check them
        $conditionSettings = $discount->getDiscountCondition()->getValue();
        $actionSettings = $discount->getDiscountAction()->getValue();

        $this->assertEquals(DiscountableTypes::ShoppingCartItem.',3,1', $conditionSettings);
        $this->assertEquals('10,'.ModifyPriceAction::MethodPercentageWise, $actionSettings);

        $discount->save();

        $dbModel = Discount::find($discount->id);

        $this->assertInstanceOf(Discount::class, $dbModel);

        $this->assertEquals($dbModel->action_settings, $discount->getDiscountAction()->getValue());
        $this->assertEquals($dbModel->condition_settings, $discount->getDiscountCondition()->getValue());
    }

    /**
     * @group Discount
     * @test
     */
    public function AbsoluteDiscountOnWholeShoppingCartTest()
    {
        //Create the discount for an absolute discount on the whole shopping cart
        $discount = new Discount();
        $discount->type = DiscountTypes::Discountable;
        $discount->active = 1;
        $discount->valid_from = Carbon::now();
        $discount->valid_trough = Carbon::now()->addDays(1);
        $condition = new DiscountableCondition(ShoppingCartServiceInterface::class);
        $action = new ModifyPriceAction(1000,ModifyPriceAction::MethodAbsolute); //Modify the price by lowering it with 10 euro's / dollars / whatever
        $discount->setDiscountCondition($condition)->setDiscountAction($action);
        $discount->save(); //Save it to the database so that the discountService knows about it.

        //Create a product that we are going to put in the cart
        /** @var Product $product */
        $product = factory(Product::class)->create();

        //And a cart (that holds the DiscountService)
        $cart = new ShoppingCartService();

        $cart->addProductable($product, 1);

        $totalWithoutDiscount = $cart->getTotal(false);
        $totalWithDiscount = $cart->getTotal(true);

        $this->assertNotEquals($totalWithDiscount, $totalWithoutDiscount);
        $this->assertEquals($totalWithoutDiscount - 1000, $totalWithDiscount);
    }

    /**
     * @group Discount
     * @test
     */
    public function PercentageDiscountOnWholeShoppingCartTest()
    {
        $discount = new Discount();
        $discount->type = DiscountTypes::Discountable;
        $discount->active = 1;
        $discount->valid_from = Carbon::now();
        $discount->valid_trough = Carbon::now()->addDays(1);
        $condition = new DiscountableCondition(ShoppingCartServiceInterface::class);
        $action = new ModifyPriceAction(20,ModifyPriceAction::MethodPercentageWise); //Modify the price by lowering it with 10 percent
        $discount->setDiscountCondition($condition)->setDiscountAction($action);
        $discount->save(); //Save it to the database so that the discountService knows about it.

        //Create a product that we are going to put in the cart
        /** @var Product $product */
        $product = factory(Product::class)->create();
        /** @var Product $product2 */
        $product2 = factory(Product::class)->create();

        //And a cart (that holds the DiscountService)
        $cart = new ShoppingCartService();

        $cart->addProductable($product, 1);
        $cart->addProductable($product2, 1);

        $totalWithoutDiscount = $cart->getTotal(false);
        $totalWithDiscount = $cart->getTotal(true);

        $manuallyCalculatedTotal = $product->getTotal() + $product2->getTotal();

        $manuallyCalculatedPercentageValue = ($manuallyCalculatedTotal / 100) * 20;
        $manuallyCalculatedExpectedPriceWithDiscount = $manuallyCalculatedTotal - $manuallyCalculatedPercentageValue;

//        echo 'Product 1 price: '.$product->getPrice().PHP_EOL;
//        echo 'Product 2 price: '.$product2->getPrice().PHP_EOL;
//        echo 'Total manually calculated: '.$manuallyCalculatedTotal.PHP_EOL;
//        echo 'Percentage value to subtract: '.$manuallyCalculatedPercentageValue.PHP_EOL;

        $this->assertEquals($totalWithoutDiscount, $manuallyCalculatedTotal);
        $this->assertNotEquals($totalWithDiscount, $totalWithoutDiscount);
        $this->assertEquals($manuallyCalculatedExpectedPriceWithDiscount, $totalWithDiscount);
    }

    /**
     * @group Discount
     * @test
     */
    public function AbsoluteDiscountOnShoppingCartItemTest()
    {
        $discount = new Discount();
        $discount->type = DiscountTypes::Quantity;
        $discount->active = 1;
        $discount->valid_from = Carbon::now();
        $discount->valid_trough = Carbon::now()->addDays(1);
        $condition = new QuantityDiscountCondition(ShoppingCartItemInterface::class, 3);
        $action = new ModifyPriceAction(20,ModifyPriceAction::MethodAbsolute); //Modify the price by lowering it with 20 euro
        $discount->setDiscountCondition($condition)->setDiscountAction($action);
        $discount->save(); //Save it to the database so that the discountService knows about it when it does it's job

        //Test that the cart does not have any discounts
        $cart = new ShoppingCartService();

        $priceOfAllProductsWithDiscounts = 0;
        $products = [];
        for($index = 0; $index < 3; $index++) {
            /** @var Product $product */
            $product = factory(Product::class)->create();
            $products[] = $product;
            $cart->addProductable($product);
            $priceOfAllProductsWithDiscounts += $product->getTotal();
        }

        //No discounts should be applied right now. Because the discount says it will be applied to a specific product when that specific products quantity is more then specified. Now we have more then 3 products, but of a different type.
        $this->assertEquals($cart->getTotal(true), $priceOfAllProductsWithDiscounts);

        //Now test on shoppingCartItem level
        $discountService = new DiscountService();

        $shoppingCartItem = (new ShoppingCartItem())->setProductable($products[0])->setAmount(2);
        $discountService->updateDiscounts($shoppingCartItem);
        //We still should not have any discounts applied
        $this->assertEquals(0, count($shoppingCartItem->getDiscounts()));

        //Setting the amount on 3 must result in an discount to be applied since that is what it is used for
        $shoppingCartItem = (new ShoppingCartItem())->setProductable($products[0])->setAmount(3);
        $discountService->updateDiscounts($shoppingCartItem);
        $priceWithoutDiscounts = $shoppingCartItem->getTotal(false);
        $priceWithDiscounts = $shoppingCartItem->getTotal(true);

        $this->assertNotEquals($priceWithDiscounts, $priceWithoutDiscounts);

        $manuallyDiscountedPrice = $priceWithoutDiscounts - 20;
        $this->assertEquals($manuallyDiscountedPrice, $priceWithDiscounts);
    }

    /**
     * @group Discount
     * @test
     */
    public function PercentageDiscountOnShoppingCartItemTest()
    {
        $discount = new Discount();
        $discount->type = DiscountTypes::Quantity;
        $discount->active = 1;
        $discount->valid_from = Carbon::now();
        $discount->valid_trough = Carbon::now()->addDays(1);
        $condition = new QuantityDiscountCondition(ShoppingCartItemInterface::class, 3);
        $action = new ModifyPriceAction(50,ModifyPriceAction::MethodPercentageWise); //Modify the price by lowering it with 20 percent
        $discount->setDiscountCondition($condition)->setDiscountAction($action);
        $discount->save(); //Save it to the database so that the discountService knows about it when it does it's job

        //Now test on shoppingCartItem level
        $discountService = new DiscountService();

        $product = factory(Product::class)->create();

        //Setting the amount on 3 must result in an discount to be applied since that is what it is used for
        $shoppingCartItem = (new ShoppingCartItem())->setProductable($product)->setAmount(6);
        $discountService->updateDiscounts($shoppingCartItem);
        $priceWithoutDiscounts = $shoppingCartItem->getTotal(false);
        $priceWithDiscounts = $shoppingCartItem->getTotal(true);

        $this->assertNotEquals($priceWithDiscounts, $priceWithoutDiscounts);

        $manuallyDiscountedPrice = $priceWithoutDiscounts - ($priceWithoutDiscounts * .5);
        $this->assertEquals($manuallyDiscountedPrice, $priceWithDiscounts);
    }

    /**
     * @group Discount
     * @test
     */
    public function couponCodeTest()
    {
        $discount = new Discount();
        $discount->type = DiscountTypes::Coupon;
        $discount->active = 1;
        $discount->valid_from = Carbon::now();
        $discount->valid_trough = Carbon::now()->addDays(1);
        $condition = new CouponDiscountCondition('KommAwesome');
        $action = new ModifyPriceAction(5,ModifyPriceAction::MethodPercentageWise); //Modify the price by lowering it with 5 percent
        $discount->setDiscountCondition($condition)->setDiscountAction($action);
        $discount->save(); //Save it to the database so that the discountService knows about it when it does it's job

        $cart = new ShoppingCartService();

        $product = factory(Product::class)->create();

        $cart->addProductable($product, 1);

        $totalWithoutDiscounts = $cart->getTotal(false);
        $totalWithDiscounts = $cart->getTotal(true);
        $this->assertEquals($totalWithDiscounts, $totalWithoutDiscounts);

        $cart->addCouponCode('KommAwesome');

        $totalWithoutDiscounts = $cart->getTotal(false);

        $totalWithDiscounts = $cart->getTotal(true);

        $this->assertNotEquals($totalWithDiscounts, $totalWithoutDiscounts);
        $this->assertEquals($totalWithoutDiscounts - ($totalWithoutDiscounts * .05), $totalWithDiscounts);
    }

    /**
     * @group Discount
     * @test
     */
    public function ValidityAndActiveTest()
    {
        //Create the discount for an absolute discount on the whole shopping cart. Make it not yet valid
        $discount = new Discount();
        $discount->type = DiscountTypes::Discountable;
        $discount->active = 1;
        $discount->valid_from = Carbon::now()->addDays(2);
        $discount->valid_trough = Carbon::now()->addDays(2);
        $condition = new DiscountableCondition(ShoppingCartServiceInterface::class);
        $action = new ModifyPriceAction(1000,ModifyPriceAction::MethodAbsolute); //Modify the price by lowering it with 10 euro's / dollars / whatever
        $discount->setDiscountCondition($condition)->setDiscountAction($action);
        $discount->save(); //Save it to the database so that the discountService knows about it.

        //Create a product that we are going to put in the cart
        /** @var Product $product */
        $product = factory(Product::class)->create();

        //And a cart (that holds the DiscountService)
        $cart = new ShoppingCartService();

        $cart->addProductable($product, 1);

        $totalWithoutDiscount = $cart->getTotal(false);
        $totalWithDiscount = $cart->getTotal(true);

        //No discount should be applied yet since it is not valid yet.
        $this->assertEquals($totalWithDiscount, $totalWithoutDiscount);
        $this->assertNotEquals($totalWithoutDiscount - 1000, $totalWithDiscount);

        //Change the discount so that it is valid. Clear the cart and re-add the product
        $discount->valid_from = Carbon::now()->subDay(1);
        $discount->valid_trough = Carbon::now()->addDay(2);
        $discount->save();

        $cart->clear();
        $cart->addProductable($product, 1);

        //The discount should be applied since it now is valid
        $totalWithoutDiscount = $cart->getTotal(false);
        $totalWithDiscount = $cart->getTotal(true);
        $this->assertNotEquals($totalWithDiscount, $totalWithoutDiscount);
        $this->assertEquals($totalWithoutDiscount - 1000, $totalWithDiscount);


        //Change the discount so that it was valid in the past. Clear the cart and re-add the product
        $discount->valid_from = Carbon::now()->subDay(5);
        $discount->valid_trough = Carbon::now()->subDay(1);
        $discount->save();

        $cart->clear();
        $cart->addProductable($product, 1);

        //The discount should not be applied since it was valid in the past but not now anymore
        $totalWithoutDiscount = $cart->getTotal(false);
        $totalWithDiscount = $cart->getTotal(true);
        $this->assertEquals($totalWithDiscount, $totalWithoutDiscount);
        $this->assertNotEquals($totalWithoutDiscount - 1000, $totalWithDiscount);

        //Change the discount so that it has a valid date but isn't active. Clear the cart and re-add the product
        $discount->valid_from = Carbon::now();
        $discount->valid_trough = Carbon::now()->addDay(1);
        $discount->active = 0;
        $discount->save();

        $cart->clear();
        $cart->addProductable($product, 1);

        //The discount should not be applied since its date is valid but it isn't active
        $totalWithoutDiscount = $cart->getTotal(false);
        $totalWithDiscount = $cart->getTotal(true);
        $this->assertEquals($totalWithDiscount, $totalWithoutDiscount);
        $this->assertNotEquals($totalWithoutDiscount - 1000, $totalWithDiscount);
    }
}