Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[API][Checkout]checkout without logged in account #11717

Merged
merged 4 commits into from
Aug 7, 2020
Merged
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
Expand Up @@ -11,7 +11,7 @@ Feature: Checking out as guest with a registered email
And the store allows paying offline
And there is a customer account "john@example.com"

@ui
@ui @api
Scenario: Successfully placing an order
Given I have product "PHP T-Shirt" in the cart
When I complete addressing step with email "john@example.com" and "United States" based billing address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Feature: Addressing an order
And I complete the addressing step
Then I should be on the checkout shipping step

@ui
@ui @api
Scenario: Address an order using existing email
Given the store has customer "eddard.stark@example.com"
And I have product "PHP T-Shirt" in the cart
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderCheckoutTransitions;
use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Webmozart\Assert\Assert;
Expand All @@ -28,6 +29,9 @@ final class AddressOrderHandler
/** @var OrderRepositoryInterface */
private $orderRepository;

/** @var CustomerRepositoryInterface */
private $customerRepository;

/** @var FactoryInterface */
private $customerFactory;

Expand All @@ -39,11 +43,13 @@ final class AddressOrderHandler

public function __construct(
OrderRepositoryInterface $orderRepository,
CustomerRepositoryInterface $customerRepository,
FactoryInterface $customerFactory,
ObjectManager $manager,
StateMachineFactoryInterface $stateMachineFactory
) {
$this->orderRepository = $orderRepository;
$this->customerRepository = $customerRepository;
$this->customerFactory = $customerFactory;
$this->manager = $manager;
$this->stateMachineFactory = $stateMachineFactory;
Expand All @@ -64,25 +70,32 @@ public function __invoke(AddressOrder $addressOrder): OrderInterface
sprintf('Order with %s token cannot be addressed.', $tokenValue)
);

/** @var CustomerInterface|null $customer */
$customer = $order->getCustomer();
if ($customer === null) {
Assert::notNull($addressOrder->email, sprintf('Visitor should provide an email.'));

/** @var CustomerInterface $customer */
$customer = $this->customerFactory->createNew();
$customer->setEmail($addressOrder->email);

$this->manager->persist($customer);

$order->setCustomer($customer);
if (null === $order->getCustomer()) {
$order->setCustomer($this->provideCustomerByEmail($addressOrder->email));
}

$order->setBillingAddress($addressOrder->billingAddress);
$order->setShippingAddress($addressOrder->shippingAddress ?? clone $addressOrder->billingAddress);

$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS);

$this->manager->persist($order);

return $order;
}

private function provideCustomerByEmail(?string $email): CustomerInterface
{
Assert::notNull($email, sprintf('Visitor should provide an email.'));

$customer = $this->customerRepository->findOneBy(['email' => $email]);
if (null === $customer) {
/** @var CustomerInterface $customer */
$customer = $this->customerFactory->createNew();
$customer->setEmail($email);
$this->manager->persist($customer);
}

return $customer;
arti0090 marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@

<service id="Sylius\Bundle\ApiBundle\CommandHandler\Checkout\AddressOrderHandler">
<argument type="service" id="sylius.repository.order"/>
<argument type="service" id="sylius.repository.customer"/>
<argument type="service" id="sylius.factory.customer"/>
<argument type="service" id="sylius.manager.customer"/>
<argument type="service" id="sm.factory" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,20 @@
use Sylius\Component\Core\Model\CustomerInterface;
use Sylius\Component\Core\Model\OrderInterface;
use Sylius\Component\Core\OrderCheckoutTransitions;
use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
use Sylius\Component\Core\Repository\OrderRepositoryInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;

final class AddressOrderHandlerSpec extends ObjectBehavior
{
function let(
OrderRepositoryInterface $orderRepository,
CustomerRepositoryInterface $customerRepository,
FactoryInterface $customerFactory,
ObjectManager $manager,
StateMachineFactoryInterface $stateMachineFactory
): void {
$this->beConstructedWith($orderRepository, $customerFactory, $manager, $stateMachineFactory);
$this->beConstructedWith($orderRepository, $customerRepository, $customerFactory, $manager, $stateMachineFactory);
}

function it_handles_addressing_an_order_without_provided_shipping_address(
Expand All @@ -50,14 +52,15 @@ function it_handles_addressing_an_order_without_provided_shipping_address(

$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$order->setCustomer($customer);
$order->getCustomer()->willReturn($customer);

$order->setBillingAddress($billingAddress)->shouldBeCalled();
$order->setShippingAddress(Argument::type(AddressInterface::class))->shouldBeCalled();

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('address')->willReturn(true);
$stateMachine->apply('address')->shouldBeCalled();
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(true);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled();

$this($addressOrder);
}
Expand Down Expand Up @@ -93,8 +96,10 @@ function it_handles_addressing_an_order_for_visitor(
$order->setShippingAddress($shippingAddress)->shouldBeCalled();

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('address')->willReturn(true);
$stateMachine->apply('address')->shouldBeCalled();
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(true);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled();

$manager->persist($order)->shouldBeCalled();

$this($addressOrder);
}
Expand Down Expand Up @@ -130,8 +135,49 @@ function it_handles_addressing_an_order_for_logged_in_shop_user(
$order->setShippingAddress($shippingAddress)->shouldBeCalled();

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('address')->willReturn(true);
$stateMachine->apply('address')->shouldBeCalled();
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(true);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled();

$manager->persist($order)->shouldBeCalled();

$this($addressOrder);
}

function it_handles_addressing_an_order_for_not_logged_in_shop_user(
OrderRepositoryInterface $orderRepository,
CustomerRepositoryInterface $customerRepository,
FactoryInterface $customerFactory,
ObjectManager $manager,
StateMachineFactoryInterface $stateMachineFactory,
CustomerInterface $customer,
AddressInterface $billingAddress,
AddressInterface $shippingAddress,
OrderInterface $order,
StateMachineInterface $stateMachine
): void {
$addressOrder = new AddressOrder(
'r2d2@droid.com',
$billingAddress->getWrappedObject(),
$shippingAddress->getWrappedObject()
);
$addressOrder->setOrderTokenValue('ORDERTOKEN');

$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$order->getCustomer()->willReturn(null);

$customerRepository->findOneBy(['email' => 'r2d2@droid.com'])->willReturn($customer);

$order->getCustomer()->shouldBeCalled();
$order->setCustomer($customer)->shouldBeCalled();
$order->setBillingAddress($billingAddress)->shouldBeCalled();
$order->setShippingAddress($shippingAddress)->shouldBeCalled();

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(true);
$stateMachine->apply(OrderCheckoutTransitions::TRANSITION_ADDRESS)->shouldBeCalled();

$manager->persist($order)->shouldBeCalled();

$this($addressOrder);
}
Expand All @@ -156,7 +202,7 @@ function it_throws_an_exception_if_visitor_does_not_provide_an_email(
$order->getCustomer()->willReturn(null);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('address')->willReturn(true);
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(true);

$this->shouldThrow(\LogicException::class)->during('__invoke', [$addressOrder]);
}
Expand Down Expand Up @@ -196,7 +242,7 @@ function it_throws_an_exception_if_order_cannot_be_addressed(
$orderRepository->findOneBy(['tokenValue' => 'ORDERTOKEN'])->willReturn($order);

$stateMachineFactory->get($order, OrderCheckoutTransitions::GRAPH)->willReturn($stateMachine);
$stateMachine->can('address')->willReturn(false);
$stateMachine->can(OrderCheckoutTransitions::TRANSITION_ADDRESS)->willReturn(false);

$this->shouldThrow(\LogicException::class)->during('__invoke', [$addressOrder]);
}
Expand Down