Skip to content

Commit

Permalink
minor #180 [Refunds] Disable refund when order is free (AdamKasp)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.0-dev branch.

Discussion
----------

![Screenshot 2020-01-30 at 13 08 10](https://user-images.githubusercontent.com/29897151/73448660-a5286c80-4361-11ea-8446-9207bc6fc06c.png)
![Screenshot 2020-01-30 at 13 08 24](https://user-images.githubusercontent.com/29897151/73448661-a5286c80-4361-11ea-9e39-f3a9d4b3db86.png)


Commits
-------

d20f724 Behats - refund free orders
1ea802a Prevent from refund free order
  • Loading branch information
GSadee authored Jan 31, 2020
2 parents 92c102d + 1ea802a commit a0e4bf2
Show file tree
Hide file tree
Showing 13 changed files with 213 additions and 44 deletions.
28 changes: 28 additions & 0 deletions features/being_unable_to_refund_free_order.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@refunds
Feature: Being unable to refund a free order
In order to give back money to the customer only if it is possible
As an Administrator
I want not to be able to generate a refund for a free order

Background:
Given the store operates on a single green channel in "United States"
And the store has a free product "Witcher Sword"
And the store ships everywhere for free for all channels
And the store allows paying with "Space money"
And there is a customer "Lucifer Morningstar" that placed an order "#0000001"
And the customer bought 3 "Witcher Sword" products
And the customer "Lucifer Morningstar" addressed it to "Seaside Fwy", "90802" "Los Angeles" in the "United States" with identical billing address
And the customer chose "Free" shipping method
And this order is already paid
And I am logged in as an administrator

@ui
Scenario: Being unable to refund a free order
When I view the summary of the order "#0000001"
Then I should not see refunds button

@ui
Scenario: Being unable to open refund page when order is free
When I try to refund some units of order "#0000001"
Then I should be redirected to the order "#0000001" show page
And I should be notified that I cannot refund a free order
2 changes: 1 addition & 1 deletion features/refunding_single_order_unit.feature
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Feature: Refunding a single order unit
@ui
Scenario: Not being able to see refunds button
When I view the summary of the order "#00000022"
Then I should not be able to see refunds button
Then I should not see refunds button

@ui
Scenario: Being able to choose only offline payment methods
Expand Down
42 changes: 39 additions & 3 deletions spec/Checker/OrderRefundingAvailabilityCheckerSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,68 @@ function it_implements_order_refunding_availability_checker_interface(): void
$this->shouldImplement(OrderRefundingAvailabilityCheckerInterface::class);
}

function it_returns_true_if_order_is_paid(
function it_returns_true_if_order_is_paid_and_not_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
$order->getTotal()->willReturn(100);

$this('00000007')->shouldReturn(true);
}

function it_returns_true_if_order_is_partialy_refunded(
function it_returns_true_if_order_is_partially_refunded_and_not_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PARTIALLY_REFUNDED);
$order->getTotal()->willReturn(100);

$this('00000007')->shouldReturn(true);
}

function it_returns_false_if_order_is_in_other_state(
function it_returns_false_if_order_is_in_other_state_and_not_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_AWAITING_PAYMENT);
$order->getTotal()->willReturn(100);

$this('00000007')->shouldReturn(false);
}

function it_returns_false_if_order_is_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
$order->getTotal()->willReturn(0);

$this('00000007')->shouldReturn(false);
}

function it_returns_false_if_order_is_partially_refunded_and_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PARTIALLY_REFUNDED);
$order->getTotal()->willReturn(0);

$this('00000007')->shouldReturn(false);
}

function it_returns_false_if_order_is_in_other_state_and_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_AWAITING_PAYMENT);
$order->getTotal()->willReturn(0);

$this('00000007')->shouldReturn(false);
}
Expand Down
45 changes: 41 additions & 4 deletions spec/Checker/OrderRefundsListAvailabilityCheckerSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,42 +22,79 @@ function it_implements_order_refunding_availability_checker_interface(): void
$this->shouldImplement(OrderRefundingAvailabilityCheckerInterface::class);
}

function it_returns_true_if_order_is_paid(
function it_returns_true_if_order_is_paid_and_not_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
$order->getTotal()->willReturn(100);

$this('00000007')->shouldReturn(true);
}

function it_returns_true_if_order_is_refunded(
function it_returns_true_if_order_is_refunded_and_not_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_REFUNDED);
$order->getTotal()->willReturn(100);

$this('00000007')->shouldReturn(true);
}

function it_returns_true_if_order_is_partialy_refunded(
function it_returns_true_if_order_is_partially_refunded_and_not_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PARTIALLY_REFUNDED);
$order->getTotal()->willReturn(100);

$this('00000007')->shouldReturn(true);
}

function it_returns_false_if_order_is_in_other_state(
function it_returns_false_if_order_is_in_other_state_and_not_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_AWAITING_PAYMENT);
$order->getTotal()->willReturn(100);

$this('00000007')->shouldReturn(false);
}

function it_returns_false_if_order_is_paid_and_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_PAID);
$order->getTotal()->willReturn(0);

$this('00000007')->shouldReturn(false);
}

function it_returns_false_if_order_is_refunded_and_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_REFUNDED);
$order->getTotal()->willReturn(0);

$this('00000007')->shouldReturn(false);
}

function it_returns_false_if_order_is_in_other_state_and_free(
OrderRepositoryInterface $orderRepository,
OrderInterface $order
): void {
$orderRepository->findOneByNumber('00000007')->willReturn($order);
$order->getPaymentState()->willReturn(OrderPaymentStates::STATE_AWAITING_PAYMENT);
$order->getTotal()->willReturn(0);

$this('00000007')->shouldReturn(false);
}
Expand Down
10 changes: 7 additions & 3 deletions src/Action/Admin/OrderRefundsListAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ public function __invoke(Request $request): Response
$order = $this->orderRepository->findOneByNumber($request->attributes->get('orderNumber'));

if (!$this->orderRefundsListAvailabilityChecker->__invoke($request->attributes->get('orderNumber'))) {
return $this->redirectToReferer($order);
if ($order->getTotal() === 0) {
return $this->redirectToReferer($order, 'sylius_refund.free_order_should_not_be_refund');
}

return $this->redirectToReferer($order, 'sylius_refund.order_should_be_paid');
}

return new Response(
Expand All @@ -68,9 +72,9 @@ public function __invoke(Request $request): Response
);
}

private function redirectToReferer(OrderInterface $order): Response
private function redirectToReferer(OrderInterface $order, string $message): Response
{
$this->session->getFlashBag()->add('error', 'sylius_refund.order_should_be_paid');
$this->session->getFlashBag()->add('error', $message);

return new RedirectResponse($this->router->generate('sylius_admin_order_show', ['id' => $order->getId()]));
}
Expand Down
8 changes: 4 additions & 4 deletions src/Checker/OrderRefundingAvailabilityChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public function __invoke(string $orderNumber): bool
$order = $this->orderRepository->findOneByNumber($orderNumber);
Assert::notNull($order);

return in_array(
$order->getPaymentState(),
[OrderPaymentStates::STATE_PAID, OrderPaymentStates::STATE_PARTIALLY_REFUNDED]
);
return in_array($order->getPaymentState(), [
OrderPaymentStates::STATE_PAID,
OrderPaymentStates::STATE_PARTIALLY_REFUNDED,
]) && $order->getTotal() !== 0;
}
}
9 changes: 5 additions & 4 deletions src/Checker/OrderRefundsListAvailabilityChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ public function __invoke(string $orderNumber): bool
$order = $this->orderRepository->findOneByNumber($orderNumber);
Assert::notNull($order);

return in_array(
$order->getPaymentState(),
[OrderPaymentStates::STATE_PAID, OrderPaymentStates::STATE_PARTIALLY_REFUNDED, OrderPaymentStates::STATE_REFUNDED]
);
return in_array($order->getPaymentState(), [
OrderPaymentStates::STATE_PAID,
OrderPaymentStates::STATE_PARTIALLY_REFUNDED,
OrderPaymentStates::STATE_REFUNDED,
]) && $order->getTotal() !== 0;
}
}
3 changes: 2 additions & 1 deletion src/Resources/translations/flashes.en.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
sylius_refund:
at_least_one_unit_should_be_selected_to_refund: 'At least one unit should be selected to refund'
error_occurred: 'Unexpected error occurred'
free_order_should_not_be_refund: 'You cannot refund a free order'
order_should_be_paid: 'Order should be paid for the units to could be refunded'
refund_amount_must_be_greater: 'Refund amount must be greater than 0'
refund_amount_must_be_less: 'You cannot refund more money than the refunded unit total'
Expand All @@ -8,4 +10,3 @@ sylius_refund:
resend_credit_memo_success: 'Selected credit memo has been successfully resent'
unit_refund_exceeded: 'You cannot refund more money than the order unit total'
units_successfully_refunded: 'Selected order units have been successfully refunded'
error_occurred: 'Unexpected error occurred'
44 changes: 23 additions & 21 deletions src/Resources/views/_paymentMethod.html.twig
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
{% set original_payment_method = order.payments.first().method %}
{% if order.payments.first() %}
{% set original_payment_method = order.payments.first().method %}

<div class="ui hidden divider"></div>
<div class="ui hidden divider"></div>

<div class="ui stackable grid">
<div class="two column row">
<div class="ui form column">
<div class="field">
<label for="payment-methods">{{ 'sylius.ui.payment_method'|trans }}</label>
<select id="payment-methods" name="sylius_refund_payment_method" class="ui fluid selection dropdown">
{% for payment_method in payment_methods %}
<option value="{{ payment_method.id }}" {{ (payment_method.code == original_payment_method.code) ? 'selected="selected"' : '' }}>
{{ payment_method.name }}
</option>
{% endfor %}
</select>
<small>{{ 'sylius.ui.original_payment_method'|trans }}: <strong>{{ original_payment_method }}</strong></small>
<div class="ui stackable grid">
<div class="two column row">
<div class="ui form column">
<div class="field">
<label for="payment-methods">{{ 'sylius.ui.payment_method'|trans }}</label>
<select id="payment-methods" name="sylius_refund_payment_method" class="ui fluid selection dropdown">
{% for payment_method in payment_methods %}
<option value="{{ payment_method.id }}" {{ (payment_method.code == original_payment_method.code) ? 'selected="selected"' : '' }}>
{{ payment_method.name }}
</option>
{% endfor %}
</select>
<small>{{ 'sylius.ui.original_payment_method'|trans }}: <strong>{{ original_payment_method }}</strong></small>
</div>
</div>
</div>
<div class="ui form column">
<div class="field">
<label for="sylius-refund-comment">{{ 'sylius.ui.comment'|trans }}</label>
<textarea rows="3" name="sylius_refund_comment" id="sylius-refund-comment"></textarea>
<div class="ui form column">
<div class="field">
<label for="sylius-refund-comment">{{ 'sylius.ui.comment'|trans }}</label>
<textarea rows="3" name="sylius_refund_comment" id="sylius-refund-comment"></textarea>
</div>
</div>
</div>
</div>
</div>
{% endif %}
36 changes: 36 additions & 0 deletions tests/Behat/Context/Setup/ProductContext.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Paweł Jędrzejewski
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Tests\Sylius\RefundPlugin\Behat\Context\Setup;

use Behat\Behat\Context\Context;
use Sylius\Behat\Context\Setup\ProductContext as BaseProductContext;

final class ProductContext implements Context
{
/** @var BaseProductContext */
private $baseProductContext;

public function __construct(BaseProductContext $baseProductContext)
{
$this->baseProductContext = $baseProductContext;
}

/**
* @Given the store has a free product :productName
*/
public function theStoreHasAFreeProduct(string $productName): void
{
$this->baseProductContext->storeHasAProductPricedAt($productName, 0, null);
}
}
23 changes: 21 additions & 2 deletions tests/Behat/Context/Ui/ManagingOrdersContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ public function shouldBeNotifiedThatTheOrderShouldBePaid(): void
}

/**
* @Then I should not be able to see refunds button
* @Then I should not see refunds button
*/
public function shouldNotBeAbleToSeeRefundsButton(): void
public function iShouldNotSeeRefundsButton(): void
{
Assert::false($this->showPage->hasRefundsButton());
}
Expand Down Expand Up @@ -106,4 +106,23 @@ public function thisOrderSPaymentStateShouldBe(OrderInterface $order, string $or
'paymentState' => $orderPaymentState,
]));
}

/**
* @Then I should be redirected to the order :order show page
*/
public function iShouldBeRedirectedToTheOrderShowPage(OrderInterface $order): void
{
Assert::true($this->showPage->isOpen(['id' => $order->getId()]));
}

/**
* @Then I should be notified that I cannot refund a free order
*/
public function iShouldBeNotifiedThatICannotRefundAFreeOrder(): void
{
$this->notificationChecker->checkNotification(
'You cannot refund a free order',
NotificationType::failure()
);
}
}
4 changes: 4 additions & 0 deletions tests/Behat/Resources/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,9 @@
<service id="Tests\Sylius\RefundPlugin\Behat\Services\Provider\MessagesProvider">
<argument>%kernel.cache_dir%/spool</argument>
</service>

<service id="Tests\Sylius\RefundPlugin\Behat\Context\Setup\ProductContext">
<argument type="service" id="sylius.behat.context.setup.product" />
</service>
</services>
</container>
Loading

0 comments on commit a0e4bf2

Please sign in to comment.