Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@viewing_products
Feature: Viewing a product discounted price
In order to see products discounted price
As a Visitor
I want to be able to view a single product discounted price

Background:
Given the store operates on a single channel in "United States"

@ui
Scenario: Viewing a detailed page with product's original price
Given the store has a product "T-shirt banana" priced at "$39.00"
Given the product "T-shirt banana" has original price at "$50.00"
When I check this product's details
Then I should see the product price "$39.00"
Then I should see the product original price "$50.00"
2 changes: 1 addition & 1 deletion src/Sylius/Behat/Context/Setup/ProductContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,7 @@ public function shortDescriptionOfProductIs(ProductInterface $product, string $s
}

/**
* @Given the product :product has original price :originalPrice
* @Given the product :product has original price at :originalPrice
*/
public function theProductHasOriginalPrice(ProductInterface $product, string $originalPrice): void
{
Expand Down
9 changes: 9 additions & 0 deletions src/Sylius/Behat/Context/Ui/Shop/ProductContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,15 @@ public function iShouldSeeTheProductPrice($price)
Assert::same($this->showPage->getPrice(), $price);
}

/**
* @Then the product original price should be :price
* @Then I should see the product original price :price
*/
public function iShouldSeeTheProductOriginalPrice($price)
{
Assert::same($this->showPage->getOriginalPrice(), $price);
}

/**
* @When I set its :optionName to :optionValue
*/
Expand Down
6 changes: 6 additions & 0 deletions src/Sylius/Behat/Page/Shop/Product/ShowPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ public function getPrice(): string
return $this->getElement('product_price')->getText();
}

public function getOriginalPrice(): string
{
return $this->getElement('product_original_price')->getText();
}

public function hasAddToCartButton(): bool
{
if (!$this->hasElement('add_to_cart_button')) {
Expand Down Expand Up @@ -261,6 +266,7 @@ protected function getDefinedElements(): array
'option_select' => '#sylius_add_to_cart_cartItem_variant_%optionCode%',
'out_of_stock' => '[data-test-product-out-of-stock]',
'product_price' => '[data-test-product-price]',
'product_original_price' => '[data-test-product-original-price]',
'product_name' => '[data-test-product-name]',
'reviews' => '[data-test-product-reviews]',
'reviews_comment' => '[data-test-comment="%title%"]',
Expand Down
2 changes: 2 additions & 0 deletions src/Sylius/Behat/Page/Shop/Product/ShowPageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public function getName(): string;

public function getPrice(): string;

public function getOriginalPrice(): string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a BC Break.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Behats are not covered with our BC promise ;)

public function hasAddToCartButton(): bool;

public function hasAssociation(string $productAssociationName): bool;
Expand Down
26 changes: 26 additions & 0 deletions src/Sylius/Bundle/CoreBundle/Templating/Helper/PriceHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,32 @@ public function getPrice(ProductVariantInterface $productVariant, array $context
;
}

/**
* {@inheritdoc}
*
* @throws \InvalidArgumentException
*/
public function getOriginalPrice(ProductVariantInterface $productVariant, array $context): int
{
Assert::keyExists($context, 'channel');

return $this
->productVariantPriceCalculator
->calculateOriginal($productVariant, $context);
}

/**
* {@inheritdoc}
*
* @throws \InvalidArgumentException
*/
public function hasDiscount(ProductVariantInterface $productVariant, array $context): bool
{
Assert::keyExists($context, 'channel');

return $this->getOriginalPrice($productVariant, $context) > $this->getPrice($productVariant, $context);
}

/**
* {@inheritdoc}
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Sylius/Bundle/CoreBundle/Twig/PriceExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ public function getFilters(): array
{
return [
new TwigFilter('sylius_calculate_price', [$this->helper, 'getPrice']),
new TwigFilter('sylius_calculate_original_price', [$this->helper, 'getOriginalPrice']),
new TwigFilter('sylius_has_discount', [$this->helper, 'hasDiscount']),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,67 @@ function it_throws_invalid_argument_exception_when_channel_key_is_not_present_in
$productVariantPriceCalculator->calculate($productVariant, $context)->shouldNotBeCalled();
}

function it_returns_variant_original_price_for_channel_given_in_context(
ChannelInterface $channel,
ProductVariantInterface $productVariant,
ProductVariantPriceCalculatorInterface $productVariantPriceCalculator
): void {
$context = ['channel' => $channel];

$productVariantPriceCalculator->calculateOriginal($productVariant, $context)->willReturn(1000);

$this->getOriginalPrice($productVariant, $context)->shouldReturn(1000);
}

function it_throws_invalid_argument_exception_when_channel_key_is_not_present_in_context_when_getting_original_price(
ProductVariantInterface $productVariant,
ProductVariantPriceCalculatorInterface $productVariantPriceCalculator
): void {
$context = ['lennahc' => ''];

$this->shouldThrow(\InvalidArgumentException::class)->during('getOriginalPrice', [$productVariant, $context]);

$productVariantPriceCalculator->calculateOriginal($productVariant, $context)->shouldNotBeCalled();
}

function it_returns_true_if_variant_is_discounted_for_channel_given_in_context(
ChannelInterface $channel,
ProductVariantInterface $productVariant,
ProductVariantPriceCalculatorInterface $productVariantPriceCalculator
): void {
$context = ['channel' => $channel];

$productVariantPriceCalculator->calculate($productVariant, $context)->willReturn(950);
$productVariantPriceCalculator->calculateOriginal($productVariant, $context)->willReturn(1000);

$this->hasDiscount($productVariant, $context)->shouldReturn(true);
}

function it_returns_false_if_variant_is_not_discounted_for_channel_given_in_context(
ChannelInterface $channel,
ProductVariantInterface $productVariant,
ProductVariantPriceCalculatorInterface $productVariantPriceCalculator
): void {
$context = ['channel' => $channel];

$productVariantPriceCalculator->calculate($productVariant, $context)->willReturn(1000);
$productVariantPriceCalculator->calculateOriginal($productVariant, $context)->willReturn(1000);

$this->hasDiscount($productVariant, $context)->shouldReturn(false);
}

function it_throws_invalid_argument_exception_when_channel_key_is_not_present_in_context_when_checking_if_variant_is_discounted(
ProductVariantInterface $productVariant,
ProductVariantPriceCalculatorInterface $productVariantPriceCalculator
): void {
$context = ['lennahc' => ''];

$this->shouldThrow(\InvalidArgumentException::class)->during('hasDiscount', [$productVariant, $context]);

$productVariantPriceCalculator->calculate($productVariant, $context)->shouldNotBeCalled();
$productVariantPriceCalculator->calculateOriginal($productVariant, $context)->shouldNotBeCalled();
}

function it_has_name(): void
{
$this->getName()->shouldReturn('sylius_calculate_price');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@

{{- convertAndFormat(variant|sylius_calculate_price({'channel': sylius.channel})) }}
{%- endmacro -%}

{%- macro calculateOriginalPrice(variant) -%}
{% from _self import convertAndFormat %}

{{- convertAndFormat(variant|sylius_calculate_original_price({'channel': sylius.channel})) }}
{%- endmacro -%}
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
{% import "@SyliusShop/Common/Macro/money.html.twig" as money %}

<span class="ui huge header" id="product-price" {{ sylius_test_html_attribute('product-price', money.calculatePrice(product|sylius_resolve_variant)) }}>
{{ money.calculatePrice(product|sylius_resolve_variant) }}
{% set variant = product|sylius_resolve_variant %}
{% set hasDiscount = variant|sylius_has_discount({'channel': sylius.channel}) %}

{% if hasDiscount %}
<span class="ui small header" id="product-original-price" {{ sylius_test_html_attribute('product-original-price', money.calculateOriginalPrice(variant)) }}>
<del>{{ money.calculateOriginalPrice(variant) }}</del>
</span>
{% endif %}

<span class="ui huge header" id="product-price" {{ sylius_test_html_attribute('product-price', money.calculatePrice(variant)) }}>
{{ money.calculatePrice(variant) }}
</span>
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,30 @@ public function calculate(ProductVariantInterface $productVariant, array $contex

return $channelPricing->getPrice();
}

/**
* {@inheritdoc}
*
* @throws \InvalidArgumentException|MissingChannelConfigurationException
*/
public function calculateOriginal(ProductVariantInterface $productVariant, array $context): int
{
Assert::keyExists($context, 'channel');

$channelPricing = $productVariant->getChannelPricingForChannel($context['channel']);

if (null === $channelPricing) {
throw new MissingChannelConfigurationException(sprintf(
'Channel %s has no price defined for product variant %s',
$context['channel']->getName(),
$productVariant->getName()
));
}

if (null === $channelPricing->getOriginalPrice()) {
return $channelPricing->getPrice();
}

return $channelPricing->getOriginalPrice();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ interface ProductVariantPriceCalculatorInterface
* @throws MissingChannelConfigurationException when price for given channel does not exist
*/
public function calculate(ProductVariantInterface $productVariant, array $context): int;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a BC Break.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, we cannot merge this PR due to this change. Maybe just a separate service could do the job? Or a new one ProductVariantPricesCalculatorInterface (with s) and this one should be deprecated but used internally to ensure proper calculation logic

/**
* @throws MissingChannelConfigurationException when price for given channel does not exist
*/
public function calculateOriginal(ProductVariantInterface $productVariant, array $context): int;
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,51 @@ function it_throws_exception_if_no_channel_is_defined_in_configuration(ProductVa
->during('calculate', [$productVariant, []])
;
}

function it_gets_original_price_for_product_variant_in_given_channel(
ChannelInterface $channel,
ChannelPricingInterface $channelPricing,
ProductVariantInterface $productVariant
): void {
$productVariant->getChannelPricingForChannel($channel)->willReturn($channelPricing);
$channelPricing->getOriginalPrice()->willReturn(1000);

$this->calculateOriginal($productVariant, ['channel' => $channel])->shouldReturn(1000);
}

function it_gets_price_for_product_variant_if_it_has_no_original_price_in_given_channel(
ChannelInterface $channel,
ChannelPricingInterface $channelPricing,
ProductVariantInterface $productVariant
): void {
$productVariant->getChannelPricingForChannel($channel)->willReturn($channelPricing);
$channelPricing->getPrice()->willReturn(1000);
$channelPricing->getOriginalPrice()->willReturn(null);

$this->calculateOriginal($productVariant, ['channel' => $channel])->shouldReturn(1000);
}

function it_throws_a_channel_not_defined_exception_if_there_is_no_variant_price_for_given_channel_when_calculating_original_price(
ChannelInterface $channel,
ProductVariantInterface $productVariant
): void {
$channel->getName()->willReturn('WEB');

$productVariant->getChannelPricingForChannel($channel)->willReturn(null);
$productVariant->getName()->willReturn('Red variant');
$productVariant->getCode()->willReturn('RED_VARIANT');

$this
->shouldThrow(MissingChannelConfigurationException::class)
->during('calculateOriginal', [$productVariant, ['channel' => $channel]])
;
}

function it_throws_exception_if_no_channel_is_defined_in_configuration_when_calculating_original_price(ProductVariantInterface $productVariant): void
{
$this
->shouldThrow(\InvalidArgumentException::class)
->during('calculateOriginal', [$productVariant, []])
;
}
}