Skip to content

Commit

Permalink
Added the CartPercentageDiscount promotion action class
Browse files Browse the repository at this point in the history
  • Loading branch information
fulopattila122 committed Aug 25, 2024
1 parent 555b52b commit a81be8d
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 4 deletions.
78 changes: 78 additions & 0 deletions src/Promotion/Actions/CartPercentageDiscount.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

declare(strict_types=1);

/**
* Contains the CartPercentageDiscount class.
*
* @copyright Copyright (c) 2024 Attila Fulop
* @author Attila Fulop
* @license MIT
* @since 2024-08-25
*
*/

namespace Vanilo\Promotion\Actions;

use Nette\Schema\Expect;
use Nette\Schema\Processor;
use Nette\Schema\Schema;
use Vanilo\Adjustments\Adjusters\PercentDiscount;
use Vanilo\Adjustments\Contracts\Adjustable;
use Vanilo\Adjustments\Contracts\Adjuster;
use Vanilo\Promotion\Contracts\PromotionActionType;

class CartPercentageDiscount implements PromotionActionType
{
public const DEFAULT_ID = 'cart_percentage_discount';

public static function getName(): string
{
return __('Cart Percentage Discount');
}

public function getTitle(array $configuration): string
{
if (null === $percent = $configuration['percent'] ?? null) {
return __('X% discount on the entire cart. Invalid Configuration: the `:parameter` parameter is missing', ['parameter' => 'percent']);
}

return __(':percent% discount on the entire cart', ['percent' => $percent]);
}

public function getAdjuster(array $configuration): Adjuster
{
$configuration = (new Processor())->process($this->getSchema(), $configuration);

return new PercentDiscount($configuration['percent']);
}

public function apply(object $subject, array $configuration): array
{
$result = [];
if ($subject instanceof Adjustable) {
$result[] = $subject->adjustments()->create($this->getAdjuster($configuration));
}
//@todo also set the origin

return $result;
}

public function getSchema(): Schema
{
return Expect::structure([
'percent' => Expect::anyOf(
Expect::float(0)->min(0)->max(100),
Expect::int(0)->min(0)->max(100)
)->required()
])
->castTo('array');
}

public function getSchemaSample(array $mergeWith = null): array
{
$sample = ['percent' => 10];

return is_null($mergeWith) ? $sample : array_merge($sample, $mergeWith);
}
}
3 changes: 3 additions & 0 deletions src/Promotion/Contracts/PromotionActionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Vanilo\Promotion\Contracts;

use Konekt\Extend\Contracts\Registerable;
use Vanilo\Adjustments\Contracts\Adjuster;
use Vanilo\Adjustments\Contracts\Adjustment;
use Vanilo\Contracts\Schematized;

Expand All @@ -14,6 +15,8 @@ public static function getName(): string;

public function getTitle(array $configuration): string;

public function getAdjuster(array $configuration): Adjuster;

/** @return Adjustment[] Returns the list of adjustments created */
public function apply(object $subject, array $configuration): array;
}
2 changes: 2 additions & 0 deletions src/Promotion/Providers/ModuleServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Konekt\Concord\BaseModuleServiceProvider;
use Vanilo\Promotion\Actions\CartFixedDiscount;
use Vanilo\Promotion\Actions\CartPercentageDiscount;
use Vanilo\Promotion\Models\Coupon;
use Vanilo\Promotion\Models\Promotion;
use Vanilo\Promotion\Models\PromotionAction;
Expand All @@ -29,5 +30,6 @@ public function boot()

PromotionRuleTypes::register(CartQuantity::ID, CartQuantity::class);
PromotionActionTypes::register(CartFixedDiscount::DEFAULT_ID, CartFixedDiscount::class);
PromotionActionTypes::register(CartPercentageDiscount::DEFAULT_ID, CartPercentageDiscount::class);
}
}
2 changes: 1 addition & 1 deletion src/Promotion/Tests/Actions/CartFixedDiscountTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function it_can_be_created_from_the_registry()
}

/** @test */
public function throws_exception_if_configuration_is_wrong()
public function it_throws_an_exception_if_the_configuration_is_wrong()
{
$this->expectException(ValidationException::class);

Expand Down
91 changes: 91 additions & 0 deletions src/Promotion/Tests/Actions/CartPercentageDiscountTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

declare(strict_types=1);

namespace Vanilo\Promotion\Tests\Actions;

use Nette\Schema\ValidationException;
use Vanilo\Adjustments\Adjusters\PercentDiscount;
use Vanilo\Promotion\Actions\CartPercentageDiscount;
use Vanilo\Promotion\PromotionActionTypes;
use Vanilo\Promotion\Tests\Examples\SampleAdjustable;
use Vanilo\Promotion\Tests\TestCase;

class CartPercentageDiscountTest extends TestCase
{
/** @test */
public function it_has_a_name()
{
$this->assertNotEmpty(CartPercentageDiscount::getName());
}

/** @test */
public function it_can_be_created_from_the_registry()
{
$fixedDiscount = PromotionActionTypes::make(CartPercentageDiscount::DEFAULT_ID);

$this->assertInstanceOf(CartPercentageDiscount::class, $fixedDiscount);
}

/** @test */
public function it_throws_a_validation_exception_if_the_percent_is_missing_from_the_configuration()
{
$this->expectException(ValidationException::class);

$discount = new CartPercentageDiscount();

$discount->getAdjuster([]);
}

/** @test */
public function it_throws_a_validation_exception_if_the_configured_percent_is_higher_than_100()
{
$this->expectException(ValidationException::class);

$discount = new CartPercentageDiscount();

$discount->getAdjuster(['percent' => 101]);
}

/** @test */
public function it_throws_a_validation_exception_if_the_configured_percent_is_negative()
{
$this->expectException(ValidationException::class);

$discount = new CartPercentageDiscount();

$discount->getAdjuster(['percent' => -3]);
}

/** @test */
public function the_title_contains_the_configured_percent()
{
$this->assertStringContainsString('7%', (new CartPercentageDiscount())->getTitle(['percent' => 7]));
}

/** @test */
public function the_title_warns_if_the_percent_configuration_is_missing()
{
$this->assertStringContainsString('Invalid', (new CartPercentageDiscount())->getTitle([]));
}

/** @test */
public function it_returns_a_percent_discount_adjuster_if_the_configuration_is_correct()
{
$this->assertInstanceOf(PercentDiscount::class, (new CartPercentageDiscount())->getAdjuster(['percent' => 20]));
}

/** @test */
public function it_adds_a_promotion_adjustment_to_the_subject_if_applied()
{
$discount = new CartPercentageDiscount();
$subject = new SampleAdjustable(179);

$discount->apply($subject, ['percent' => 10]);

$this->assertCount(1, $subject->adjustments());
$adjustment = $subject->adjustments()->first();
$this->assertInstanceOf(PercentDiscount::class, $adjustment->getAdjuster());
$this->assertEquals(-17.9, $adjustment->getAmount());
}
}
7 changes: 4 additions & 3 deletions src/Promotion/Tests/Examples/SampleAdjustable.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ class SampleAdjustable implements Adjustable

private ArrayAdjustmentCollection $adjustments;

public function __construct()
{
public function __construct(
private readonly float $preAdjustmentsTotal = 0,
) {
$this->adjustments = new ArrayAdjustmentCollection($this);
}

public function preAdjustmentTotal(): float
{
return 0;
return $this->preAdjustmentsTotal;
}

public function invalidateAdjustments(): void
Expand Down

0 comments on commit a81be8d

Please sign in to comment.