mirror of
https://github.com/Sylius/Sylius.git
synced 2026-07-14 09:02:12 +00:00
Merge branch '1.12' into 1.13
* 1.12: [Maintenance][CI] Prevent setup errors when using symfony commands with no db connection [Order] Prevent paying with a disabled method
This commit is contained in:
commit
07d8cd9cc8
14 changed files with 319 additions and 11 deletions
11
.github/workflows/ci_static-checks.yaml
vendored
11
.github/workflows/ci_static-checks.yaml
vendored
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
with:
|
||||
path: '.github/workflows/matrix.json'
|
||||
prop_path: '${{ inputs.type }}.static-checks'
|
||||
|
||||
|
||||
static-checks:
|
||||
needs: get-matrix
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -46,6 +46,9 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.get-matrix.outputs.matrix) }}
|
||||
env:
|
||||
APP_ENV: test_cached
|
||||
DATABASE_URL: "mysql://root:root@127.0.0.1/sylius?charset=utf8mb4&serverVersion=8.0"
|
||||
steps:
|
||||
- name: "Checkout (With Branch)"
|
||||
if: "${{ inputs.branch != '' }}"
|
||||
|
|
@ -72,11 +75,11 @@ jobs:
|
|||
composer global require --no-progress --no-scripts --no-plugins "symfony/flex:1.18.5"
|
||||
composer config extra.symfony.require "${{ matrix.symfony }}"
|
||||
composer require "api-platform/core:${{ matrix.api-platform }}" --no-update --no-scripts --no-interaction
|
||||
|
||||
|
||||
- name: Restrict API Platform version for ApiBundle
|
||||
run: composer require "api-platform/core:${{ matrix.api-platform }}" --no-update --no-scripts --no-interaction
|
||||
working-directory: "src/Sylius/Bundle/ApiBundle"
|
||||
|
||||
|
||||
- name: Get Composer cache directory
|
||||
id: composer-cache
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
|
@ -121,7 +124,7 @@ jobs:
|
|||
|
||||
- name: Run PHPStan
|
||||
run: vendor/bin/phpstan analyse
|
||||
|
||||
|
||||
- name: Run PHPSpec
|
||||
run: vendor/bin/phpspec run --ansi --no-interaction -f dot
|
||||
|
||||
|
|
|
|||
|
|
@ -21,3 +21,10 @@ Feature: Changing a payment method of a placed order
|
|||
When I browse my orders
|
||||
And I change my payment method to "Bank Transfer"
|
||||
Then I should have "Bank Transfer" payment method on my order
|
||||
|
||||
@ui @api
|
||||
Scenario: Changing a payment method of an order with a disabled payment method
|
||||
Given the payment method "Cash on Delivery" is disabled
|
||||
When I browse my orders
|
||||
And I change my payment method to "Bank Transfer"
|
||||
Then I should have "Bank Transfer" payment method on my order
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ Feature: Changing the offline payment method after order confirmation
|
|||
And I retry the payment with "Offline" payment method
|
||||
Then I should have chosen "Offline" payment method
|
||||
|
||||
@ui
|
||||
@ui @no-api
|
||||
Scenario: Retrying the payment with different Offline payment works correctly together with inventory
|
||||
Given there is 1 unit of product "PHP T-Shirt" available in the inventory
|
||||
And this product is tracked by the inventory
|
||||
|
|
@ -33,12 +33,69 @@ Feature: Changing the offline payment method after order confirmation
|
|||
And I retry the payment with "Offline" payment method
|
||||
Then I should have chosen "Offline" payment method
|
||||
|
||||
@api
|
||||
@ui @no-api
|
||||
Scenario: Being unable to pay for my order if my chosen payment method gets disabled
|
||||
Given I added product "PHP T-Shirt" to the cart
|
||||
When I complete addressing step with email "john@example.com" and "United States" based billing address
|
||||
And I have proceeded selecting "Free" shipping method
|
||||
And I have proceeded selecting "Cash on delivery" payment method
|
||||
And I have confirmed order
|
||||
And the payment method "Cash on delivery" gets disabled
|
||||
And I try to pay for my order
|
||||
Then I should be notified to choose a payment method
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Retrying the payment with different payment method after order confirmation when the original is disabled
|
||||
Given I added product "PHP T-Shirt" to the cart
|
||||
When I complete addressing step with email "john@example.com" and "United States" based billing address
|
||||
And I have proceeded selecting "Free" shipping method
|
||||
And I have proceeded selecting "Cash on delivery" payment method
|
||||
And I have confirmed order
|
||||
And the payment method "Cash on delivery" gets disabled
|
||||
And I retry the payment with "Offline" payment method
|
||||
Then I should have chosen "Offline" payment method
|
||||
|
||||
@ui @no-api
|
||||
Scenario: Being unable to pay for my order if no methods are available
|
||||
Given I added product "PHP T-Shirt" to the cart
|
||||
When I complete addressing step with email "john@example.com" and "United States" based billing address
|
||||
And I have proceeded selecting "Free" shipping method
|
||||
And I have proceeded selecting "Cash on delivery" payment method
|
||||
And I have confirmed order
|
||||
And the payment method "Cash on delivery" gets disabled
|
||||
And the payment method "Offline" gets disabled
|
||||
And I want to pay for my order
|
||||
Then I should not be able to pay
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Changing chosen Offline payment method to another Offline payment method after checkout
|
||||
Given I added product "PHP T-Shirt" to the cart
|
||||
When I complete addressing step with email "john@example.com" and "United States" based billing address
|
||||
And I proceed selecting "Free" shipping method
|
||||
And I proceed with "Free" shipping method
|
||||
And I proceed selecting "Cash on delivery" payment method
|
||||
And I confirm my order
|
||||
And I change payment method to "Offline" after checkout
|
||||
Then I should have chosen "Offline" payment method
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Changing the payment method to a different Offline payment method works correctly together with inventory
|
||||
Given there is 1 unit of product "PHP T-Shirt" available in the inventory
|
||||
And this product is tracked by the inventory
|
||||
When I added product "PHP T-Shirt" to the cart
|
||||
And I complete addressing step with email "john@example.com" and "United States" based billing address
|
||||
And I proceed with "Free" shipping method
|
||||
And I proceed selecting "Cash on delivery" payment method
|
||||
And I confirm my order
|
||||
And I change payment method to "Offline" after checkout
|
||||
Then I should have chosen "Offline" payment method
|
||||
|
||||
@api @no-ui
|
||||
Scenario: Changing chosen Offline payment method to another Offline payment method after checkout when the original is disabled
|
||||
Given I added product "PHP T-Shirt" to the cart
|
||||
When I complete addressing step with email "john@example.com" and "United States" based billing address
|
||||
And I proceed with "Free" shipping method
|
||||
And I proceed selecting "Cash on delivery" payment method
|
||||
And I confirm my order
|
||||
And the payment method "Cash on delivery" gets disabled
|
||||
And I change payment method to "Offline" after checkout
|
||||
Then I should have chosen "Offline" payment method
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Feature: Preventing not available payment method selection
|
|||
And I should be able to select "Paypal Express Checkout" payment method
|
||||
|
||||
@api
|
||||
Scenario: Preventing customer from selecting inexistent payment method
|
||||
Scenario: Preventing customer from selecting nonexistent payment method
|
||||
Given I have product "PHP T-Shirt" in the cart
|
||||
And I am at the checkout addressing step
|
||||
When I specify the billing address as "Ankh Morpork", "Frost Alley", "90210", "United States" for "Jon Snow"
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ final class PaymentContext implements Context
|
|||
/**
|
||||
* @Given the payment method :paymentMethod is disabled
|
||||
* @Given /^(this payment method) has been disabled$/
|
||||
* @When the payment method :paymentMethod gets disabled
|
||||
*/
|
||||
public function theStoreHasAPaymentMethodDisabled(PaymentMethodInterface $paymentMethod)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -54,6 +54,23 @@ final class CheckoutOrderDetailsContext implements Context
|
|||
$this->orderDetails->pay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I want to pay for my order
|
||||
*/
|
||||
public function iWantToPayForMyOrder(): void
|
||||
{
|
||||
$this->thankYouPage->goToTheChangePaymentMethodPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* @When I try to pay for my order
|
||||
*/
|
||||
public function iTryToPayForMyOrder(): void
|
||||
{
|
||||
$this->thankYouPage->goToTheChangePaymentMethodPage();
|
||||
$this->orderDetails->pay();
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be able to pay (again)
|
||||
*/
|
||||
|
|
@ -67,7 +84,7 @@ final class CheckoutOrderDetailsContext implements Context
|
|||
*/
|
||||
public function iShouldNotBeAbleToPay(): void
|
||||
{
|
||||
Assert::false($this->orderDetails->hasPayAction());
|
||||
Assert::false($this->orderDetails->canBePaid());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,4 +103,12 @@ final class CheckoutOrderDetailsContext implements Context
|
|||
$this->thankYouPage->goToTheChangePaymentMethodPage();
|
||||
Assert::same($this->orderDetails->getChosenPaymentMethod(), $paymentMethodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Then I should be notified to choose a payment method
|
||||
*/
|
||||
public function iShouldBeNotifiedToChooseAPaymentMethod(): void
|
||||
{
|
||||
Assert::contains($this->orderDetails->getPaymentValidationMessage(), 'Please select a payment method.');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ class ShowPage extends SymfonyPage implements ShowPageInterface
|
|||
return $this->hasElement('pay_link');
|
||||
}
|
||||
|
||||
public function canBePaid(): bool
|
||||
{
|
||||
return $this->hasPayAction() && !$this->getElement('pay_link')->hasAttribute('disabled');
|
||||
}
|
||||
|
||||
public function pay(): void
|
||||
{
|
||||
$this->getElement('pay_link')->click();
|
||||
|
|
@ -72,6 +77,17 @@ class ShowPage extends SymfonyPage implements ShowPageInterface
|
|||
return '';
|
||||
}
|
||||
|
||||
public function getPaymentValidationMessage(): string
|
||||
{
|
||||
$message = '';
|
||||
$validationElements = $this->getDocument()->findAll('css', 'form .items .sylius-validation-error');
|
||||
foreach ($validationElements as $validationElement) {
|
||||
$message .= $validationElement->getText();
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
protected function getDefinedElements(): array
|
||||
{
|
||||
return array_merge(parent::getDefinedElements(), [
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ interface ShowPageInterface extends SymfonyPageInterface
|
|||
{
|
||||
public function hasPayAction(): bool;
|
||||
|
||||
public function canBePaid(): bool;
|
||||
|
||||
public function pay(): void;
|
||||
|
||||
public function choosePaymentMethod(string $paymentMethodName): void;
|
||||
|
|
@ -28,4 +30,6 @@ interface ShowPageInterface extends SymfonyPageInterface
|
|||
public function getAmountOfItems(): int;
|
||||
|
||||
public function getChosenPaymentMethod(): string;
|
||||
|
||||
public function getPaymentValidationMessage(): string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
|
||||
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping http://symfony.com/schema/dic/services/constraint-mapping-1.0.xsd">
|
||||
<class name="Sylius\Component\Core\Model\Payment">
|
||||
<property name="method">
|
||||
<constraint name="NotBlank">
|
||||
<option name="message">sylius.payment.method.not_blank</option>
|
||||
<option name="groups">sylius_order_pay</option>
|
||||
</constraint>
|
||||
</property>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
@ -37,7 +37,7 @@ sylius_shop_order_show:
|
|||
form:
|
||||
type: Sylius\Bundle\CoreBundle\Form\Type\Checkout\SelectPaymentType
|
||||
options:
|
||||
validation_groups: []
|
||||
validation_groups: [sylius_order_pay]
|
||||
redirect:
|
||||
route: sylius_shop_order_pay
|
||||
parameters:
|
||||
|
|
|
|||
|
|
@ -28,5 +28,10 @@
|
|||
<service id="sylius.twig.extension.original_price_to_display" class="Sylius\Bundle\ShopBundle\Twig\OrderItemOriginalPriceToDisplayExtension" public="false">
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
|
||||
<service id="Sylius\Bundle\ShopBundle\Twig\OrderPaymentsExtension" public="false">
|
||||
<argument type="service" id="sylius.payment_methods_resolver" />
|
||||
<tag name="twig.extension" />
|
||||
</service>
|
||||
</services>
|
||||
</container>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@
|
|||
|
||||
{% include '@SyliusShop/Checkout/SelectPayment/_form.html.twig' %}
|
||||
<div class="ui hidden divider"></div>
|
||||
<button type="submit" class="ui large blue icon labeled button" id="sylius-pay-link" {{ sylius_test_html_attribute('pay-link') }}>
|
||||
{% set order_cannot_be_paid = not sylius_order_can_be_paid(order) %}
|
||||
<button type="submit" class="ui large blue icon labeled button {% if order_cannot_be_paid %}disabled{% endif %}" id="sylius-pay-link" {{ sylius_test_html_attribute('pay-link') }} {% if order_cannot_be_paid %}disabled{% endif %}>
|
||||
<i class="check icon"></i> {{ 'sylius.ui.pay'|trans }}
|
||||
</button>
|
||||
|
||||
|
|
|
|||
53
src/Sylius/Bundle/ShopBundle/Twig/OrderPaymentsExtension.php
Normal file
53
src/Sylius/Bundle/ShopBundle/Twig/OrderPaymentsExtension.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?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 Sylius\Bundle\ShopBundle\Twig;
|
||||
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Model\PaymentInterface;
|
||||
use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
final class OrderPaymentsExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(private PaymentMethodsResolverInterface $paymentMethodsResolver)
|
||||
{
|
||||
}
|
||||
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('sylius_order_can_be_paid', [$this, 'allNewPaymentsCanBePaid']),
|
||||
];
|
||||
}
|
||||
|
||||
public function allNewPaymentsCanBePaid(OrderInterface $order): bool
|
||||
{
|
||||
$newPayments = $order->getPayments()->filter(function (PaymentInterface $payment) {
|
||||
return $payment->getState() === PaymentInterface::STATE_NEW;
|
||||
});
|
||||
|
||||
if ($newPayments->isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($newPayments as $payment) {
|
||||
if (0 === count($this->paymentMethodsResolver->getSupportedMethods($payment))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
<?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 spec\Sylius\Bundle\ShopBundle\Twig;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use PhpSpec\ObjectBehavior;
|
||||
use Sylius\Component\Core\Model\OrderInterface;
|
||||
use Sylius\Component\Core\Model\PaymentInterface;
|
||||
use Sylius\Component\Payment\Resolver\PaymentMethodsResolverInterface;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
||||
final class OrderPaymentsExtensionSpec extends ObjectBehavior
|
||||
{
|
||||
function let(PaymentMethodsResolverInterface $paymentMethodsResolver): void
|
||||
{
|
||||
$this->beConstructedWith($paymentMethodsResolver);
|
||||
}
|
||||
|
||||
function it_is_a_twig_extension(): void
|
||||
{
|
||||
$this->shouldImplement(AbstractExtension::class);
|
||||
}
|
||||
|
||||
function it_returns_false_if_order_has_no_new_payments(OrderInterface $order): void
|
||||
{
|
||||
$order->getPayments()->willReturn(new ArrayCollection());
|
||||
|
||||
$this->allNewPaymentsCanBePaid($order)->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_returns_false_when_all_new_payments_have_no_supported_methods(
|
||||
PaymentMethodsResolverInterface $paymentMethodsResolver,
|
||||
PaymentInterface $firstPayment,
|
||||
PaymentInterface $secondPayment,
|
||||
PaymentInterface $thirdPayment,
|
||||
OrderInterface $order,
|
||||
): void {
|
||||
$firstPayment->getState()->willReturn(PaymentInterface::STATE_NEW);
|
||||
$secondPayment->getState()->willReturn(PaymentInterface::STATE_CANCELLED);
|
||||
$thirdPayment->getState()->willReturn(PaymentInterface::STATE_NEW);
|
||||
|
||||
$order->getPayments()->willReturn(new ArrayCollection([
|
||||
$firstPayment->getWrappedObject(),
|
||||
$secondPayment->getWrappedObject(),
|
||||
$thirdPayment->getWrappedObject(),
|
||||
]));
|
||||
|
||||
$paymentMethodsResolver->getSupportedMethods($firstPayment)->willReturn([]);
|
||||
$paymentMethodsResolver->getSupportedMethods($secondPayment)->shouldNotBeCalled();
|
||||
$paymentMethodsResolver->getSupportedMethods($thirdPayment)->willReturn([]);
|
||||
|
||||
$this->allNewPaymentsCanBePaid($order)->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_returns_false_when_at_least_one_new_payment_has_no_supported_methods(
|
||||
PaymentMethodsResolverInterface $paymentMethodsResolver,
|
||||
PaymentInterface $firstPayment,
|
||||
PaymentInterface $secondPayment,
|
||||
PaymentInterface $thirdPayment,
|
||||
OrderInterface $order,
|
||||
): void {
|
||||
$firstPayment->getState()->willReturn(PaymentInterface::STATE_NEW);
|
||||
$secondPayment->getState()->willReturn(PaymentInterface::STATE_CANCELLED);
|
||||
$thirdPayment->getState()->willReturn(PaymentInterface::STATE_NEW);
|
||||
|
||||
$order->getPayments()->willReturn(new ArrayCollection([
|
||||
$firstPayment->getWrappedObject(),
|
||||
$secondPayment->getWrappedObject(),
|
||||
$thirdPayment->getWrappedObject(),
|
||||
]));
|
||||
|
||||
$paymentMethodsResolver->getSupportedMethods($firstPayment)->willReturn(['method']);
|
||||
$paymentMethodsResolver->getSupportedMethods($secondPayment)->shouldNotBeCalled();
|
||||
$paymentMethodsResolver->getSupportedMethods($thirdPayment)->willReturn([]);
|
||||
|
||||
$this->allNewPaymentsCanBePaid($order)->shouldReturn(false);
|
||||
}
|
||||
|
||||
function it_returns_true_when_all_new_payments_have_at_least_one_supported_method(
|
||||
PaymentMethodsResolverInterface $paymentMethodsResolver,
|
||||
PaymentInterface $firstPayment,
|
||||
PaymentInterface $secondPayment,
|
||||
PaymentInterface $thirdPayment,
|
||||
OrderInterface $order,
|
||||
): void {
|
||||
$firstPayment->getState()->willReturn(PaymentInterface::STATE_NEW);
|
||||
$secondPayment->getState()->willReturn(PaymentInterface::STATE_CANCELLED);
|
||||
$thirdPayment->getState()->willReturn(PaymentInterface::STATE_NEW);
|
||||
|
||||
$order->getPayments()->willReturn(new ArrayCollection([
|
||||
$firstPayment->getWrappedObject(),
|
||||
$secondPayment->getWrappedObject(),
|
||||
$thirdPayment->getWrappedObject(),
|
||||
]));
|
||||
|
||||
$paymentMethodsResolver->getSupportedMethods($firstPayment)->willReturn(['method', 'another_method']);
|
||||
$paymentMethodsResolver->getSupportedMethods($secondPayment)->shouldNotBeCalled();
|
||||
$paymentMethodsResolver->getSupportedMethods($thirdPayment)->willReturn(['method']);
|
||||
|
||||
$this->allNewPaymentsCanBePaid($order)->shouldReturn(true);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue