Skip to content

Commit

Permalink
[project-base] repeat order improvements (#2876)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebaholesz committed Nov 23, 2023
2 parents 0b22f3c + 3e549c2 commit 635e01b
Show file tree
Hide file tree
Showing 93 changed files with 1,662 additions and 644 deletions.
44 changes: 41 additions & 3 deletions UPGRADE-14.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,39 @@ Follow the instructions in relevant sections, e.g. `shopsys/coding-standards` or
- add consumers and RabbitMQ to deployed application ([#2904](https://github.com/shopsys/shopsys/pull/2904))
- set new environment variables `RABBITMQ_DEFAULT_USER`, `RABBITMQ_DEFAULT_PASS`, `RABBITMQ_IP_WHITELIST` in your deployment tool (with use of the default config it will be Gitlab CI)
- see #project-base-diff to update your project
- re-enable phing target cron ([#2875](https://github.com/shopsys/shopsys/pull/2875))
- see #project-base-diff to update your project
- re-enable phing target cron ([#2875](https://github.com/shopsys/shopsys/pull/2875)) - see #project-base-diff to update your project
- prepare core for dispatch/consume system ([#2907](https://github.com/shopsys/shopsys/pull/2907))
- your custom classes that utilize internal array caching or need to be reset between message consumption should now implement the `\Symfony\Contracts\Service\ResetInterface` interface
- method `Shopsys\FrameworkBundle\Model\Product\Pricing\ProductPriceRecalculationScheduler::cleanScheduleForImmediateRecalculation()` has been renamed to `reset()`
- see #project-base-diff to update your project
- replace custom application bootstrapping with symfony/runtime ([#2914](https://github.com/shopsys/shopsys/pull/2914))
- # replace custom application bootstrapping with symfony/runtime ([#2914](https://github.com/shopsys/shopsys/pull/2914))
- improve repeat order related mutations ([#2876](https://github.com/shopsys/shopsys/pull/2876))
- constant `Shopsys\FrameworkBundle\Component\Setting\Setting::ORDER_SENT_PAGE_CONTENT` was removed, use `Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageSettingFacade::ORDER_SENT_PAGE_CONTENT` instead
- `Shopsys\FrameworkBundle\Controller\Admin\CustomerCommunicationController`
- method `__construct()` changed its interface:
```diff
public function __construct(
- protected readonly Setting $setting,
+ protected readonly OrderContentPageSettingFacade $orderContentPageSettingFacade,
protected readonly AdminDomainTabsFacade $adminDomainTabsFacade,
)
```
- method `orderSubmittedAction()` now returns `Symfony\Component\HttpFoundation\Response`
- `Shopsys\FrameworkBundle\Model\Order\OrderFacade`
- constant `VARIABLE_NUMBER` was removed, use `Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageFacade::VARIABLE_NUMBER` instead
- constant `VARIABLE_ORDER_DETAIL_URL` was removed, use `Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageFacade::VARIABLE_ORDER_DETAIL_URL` instead
- constant `VARIABLE_PAYMENT_INSTRUCTIONS` was removed, use `Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageFacade::VARIABLE_PAYMENT_INSTRUCTIONS` instead
- constant `VARIABLE_TRANSPORT_INSTRUCTIONS` was removed, use `Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageFacade::VARIABLE_TRANSPORT_INSTRUCTIONS` instead
- method `getOrderSentPageContent()` was removed, use `Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageFacade::getOrderSentPageContent()` instead
- class `Shopsys\FrontendApiBundle\Model\Order\OrderFacade`
- was renamed to `Shopsys\FrontendApiBundle\Model\Order\OrderApiFacade`, change your usage accordingly
- method `__construct()` changed its interface:
```diff
public function __construct(
protected readonly OrderRepository $orderRepository,
+ protected readonly OrderFacade $orderFacade,
)
```
- see #project-base-diff to update your project
- prevent duplicate color parameters in data fixtures ([#2911](https://github.com/shopsys/shopsys/pull/2911))
- see #project-base-diff to update your project
Expand Down Expand Up @@ -273,3 +299,15 @@ Follow the instructions in relevant sections, e.g. `shopsys/coding-standards` or
- remove EmptyCartWrapper
- this component was making whole order process very difficult to predict behavior, now we have the logic from this component splitted into each page of the order process which makes it much more predictable
- also this fixes bug with infinite cart page loading

- improve repeat order related mutations ([#2876](https://github.com/shopsys/shopsys/pull/2876))
- the maximum number of payment transaction is set to 2 (using `MAX_ALLOWED_PAYMENT_TRANSACTIONS`), this means that the user can repeat payment only once, so if this does not match your requirements, you need to change it
- GoPay SWIFT selection was moved directly inside payment selection, which might not be visible right away if you use the same approach as we do (hide unselected transports and payments), however if you do not do that, this new layout might be suitable for you
- RegistrationAfterOrder now takes care of its own conditional rendering, so if you want to add a condition, move it inside the component
- PaymentConfirmationElements and PaymentConfirmationContent were removed and instead a new ConfirmationPageContent is used, which is common for all confirmation pages. If this does not suit your needs (e.g. the page needs to look completely different in each case), you probably want to either modify the new component or use multiple different ones.
- logic for PaymentFail and PaymentSuccess was reduced and is now either inside the usePaymentConfirmationContent hook, which is responsible for fetching the content from the API, or inside ConfirmationPageContent, which is responsible for structuring the content. If you do not want to load the page content from API, these changes might not be necessary for you, and you can simplify the logic by having static components, instead of dynamic ones.
- two new queries for payment confirmation were added on SF. If you have previously implemented the logic for order confirmation page dynamic content, you can follow that example and implement it similarly.
- query for order confirmation was renamed (the word 'Query' was added)
- expiration of confirmation content is now taken into account on SF by ignoring the API error and not rendering the content. This was a bug (missing feature) in previous versions. If you have not fixed it yourself before, you definitely want to implement these changes, because otherwise, the user can see errors when displaying the order confirmation page after a certain timeout.
- query parameters parsed using getStringFromUrlQuery are now also trimmed, which makes sure the API can understand the string. For example, the API does not understand " 440ecde4-b992-4290-8636-4f454c9cf475 " as a valid UUID.
- for not-discussed changes, see #project-base-diff to update your project
1 change: 0 additions & 1 deletion packages/framework/src/Component/Setting/Setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

class Setting
{
public const ORDER_SENT_PAGE_CONTENT = 'orderSubmittedText';
public const PERSONAL_DATA_DISPLAY_SITE_CONTENT = 'personalDataDisplaySiteContent';
public const PERSONAL_DATA_EXPORT_SITE_CONTENT = 'personalDataExportSiteContent';
public const DEFAULT_PRICING_GROUP = 'defaultPricingGroupId';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,62 @@
namespace Shopsys\FrameworkBundle\Controller\Admin;

use Shopsys\FrameworkBundle\Component\Domain\AdminDomainTabsFacade;
use Shopsys\FrameworkBundle\Component\Setting\Setting;
use Shopsys\FrameworkBundle\Form\Admin\CustomerCommunication\CustomerUserCommunicationFormType;
use Shopsys\FrameworkBundle\Model\Order\OrderFacade;
use Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageFacade;
use Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageSettingFacade;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class CustomerCommunicationController extends AdminBaseController
{
/**
* @param \Shopsys\FrameworkBundle\Component\Setting\Setting $setting
* @param \Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageSettingFacade $orderContentPageSettingFacade
* @param \Shopsys\FrameworkBundle\Component\Domain\AdminDomainTabsFacade $adminDomainTabsFacade
*/
public function __construct(
protected readonly Setting $setting,
protected readonly OrderContentPageSettingFacade $orderContentPageSettingFacade,
protected readonly AdminDomainTabsFacade $adminDomainTabsFacade,
) {
}

/**
* @Route("/customer-communication/order-submitted/")
* @param \Symfony\Component\HttpFoundation\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function orderSubmittedAction(Request $request)
public function orderSubmittedAction(Request $request): Response
{
$domainId = $this->adminDomainTabsFacade->getSelectedDomainId();
$orderSentPageContent = $this->setting->getForDomain(Setting::ORDER_SENT_PAGE_CONTENT, $domainId);

$form = $this->createForm(CustomerUserCommunicationFormType::class, ['content' => $orderSentPageContent]);
$form = $this->createForm(
CustomerUserCommunicationFormType::class,
[
CustomerUserCommunicationFormType::ORDER_SENT_CONTENT_FIELD_NAME => $this->orderContentPageSettingFacade->getOrderSentPageContent($domainId),
CustomerUserCommunicationFormType::PAYMENT_SUCCESSFUL_CONTENT_FIELD_NAME => $this->orderContentPageSettingFacade->getPaymentSuccessfulPageContent($domainId),
CustomerUserCommunicationFormType::PAYMENT_FAILED_CONTENT_FIELD_NAME => $this->orderContentPageSettingFacade->getPaymentFailedPageContent($domainId),
],
);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$formData = $form->getData();
$this->setting->setForDomain(Setting::ORDER_SENT_PAGE_CONTENT, $formData['content'], $domainId);

$this->orderContentPageSettingFacade->setOrderSentPageContent($formData[CustomerUserCommunicationFormType::ORDER_SENT_CONTENT_FIELD_NAME], $domainId);
$this->orderContentPageSettingFacade->setPaymentSuccessfulPageContent($formData[CustomerUserCommunicationFormType::PAYMENT_SUCCESSFUL_CONTENT_FIELD_NAME], $domainId);
$this->orderContentPageSettingFacade->setPaymentFailedPageContent($formData[CustomerUserCommunicationFormType::PAYMENT_FAILED_CONTENT_FIELD_NAME], $domainId);

$this->addSuccessFlash(t('Order confirmation page content modified'));

return $this->redirectToRoute('admin_customercommunication_ordersubmitted');
return $this->redirectToRoute($request->attributes->get('_route'));
}

return $this->render('@ShopsysFramework/Admin/Content/CustomerCommunication/orderSubmitted.html.twig', [
'form' => $form->createView(),
'VARIABLE_TRANSPORT_INSTRUCTIONS' => OrderFacade::VARIABLE_TRANSPORT_INSTRUCTIONS,
'VARIABLE_PAYMENT_INSTRUCTIONS' => OrderFacade::VARIABLE_PAYMENT_INSTRUCTIONS,
'VARIABLE_ORDER_DETAIL_URL' => OrderFacade::VARIABLE_ORDER_DETAIL_URL,
'VARIABLE_NUMBER' => OrderFacade::VARIABLE_NUMBER,
'VARIABLE_TRANSPORT_INSTRUCTIONS' => OrderContentPageFacade::VARIABLE_TRANSPORT_INSTRUCTIONS,
'VARIABLE_PAYMENT_INSTRUCTIONS' => OrderContentPageFacade::VARIABLE_PAYMENT_INSTRUCTIONS,
'VARIABLE_ORDER_DETAIL_URL' => OrderContentPageFacade::VARIABLE_ORDER_DETAIL_URL,
'VARIABLE_NUMBER' => OrderContentPageFacade::VARIABLE_NUMBER,
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,31 @@

class CustomerUserCommunicationFormType extends AbstractType
{
public const ORDER_SENT_CONTENT_FIELD_NAME = 'order-sent-content';
public const PAYMENT_FAILED_CONTENT_FIELD_NAME = 'payment-failed-content';
public const PAYMENT_SUCCESSFUL_CONTENT_FIELD_NAME = 'payment-successful-content';

/**
* @param \Symfony\Component\Form\FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builderSettingsGroup = $builder->create('settings', GroupType::class, [
'label' => t('Settings'),
]);

$builderSettingsGroup
->add('content', CKEditorType::class, [
'label' => t('Content'),
->add(self::ORDER_SENT_CONTENT_FIELD_NAME, CKEditorType::class, [
'label' => t('Order sent page content'),
'required' => false,
])
->add(self::PAYMENT_SUCCESSFUL_CONTENT_FIELD_NAME, CKEditorType::class, [
'label' => t('Payment successful page content'),
'required' => false,
])
->add(self::PAYMENT_FAILED_CONTENT_FIELD_NAME, CKEditorType::class, [
'label' => t('Payment failed page content'),
'required' => false,
]);

Expand Down
80 changes: 80 additions & 0 deletions packages/framework/src/Migrations/Version20231101182132.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Shopsys\FrameworkBundle\Component\Translation\Translator;
use Shopsys\MigrationBundle\Component\Doctrine\Migrations\AbstractMigration;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;

class Version20231101182132 extends AbstractMigration implements ContainerAwareInterface
{
use MultidomainMigrationTrait;

/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function up(Schema $schema): void
{
foreach ($this->getAllDomainIds() as $domainId) {
$this->setPageContent(
'paymentSuccessfulText',
t('
<p>
Payment for order number {number} has been successful. <br /><br />
<a href="{order_detail_url}">Track</a> the status of your order. <br />
{transport_instructions}
</p>', [], Translator::DATA_FIXTURES_TRANSLATION_DOMAIN, $this->getDomainLocale($domainId)),
$domainId,
);

$this->setPageContent(
'paymentFailedText',
t('
<p>
Payment for order number {number} has failed. <br /><br />
Please contact us to resolve the issue.
</p>', [], Translator::DATA_FIXTURES_TRANSLATION_DOMAIN, $this->getDomainLocale($domainId)),
$domainId,
);
}
}

/**
* @param string $settingName
* @param string $pageContent
* @param int $domainId
*/
private function setPageContent(string $settingName, string $pageContent, int $domainId): void
{
$paymentSuccessfulPageContent = $this->sql(
'SELECT COUNT(*) FROM setting_values WHERE name = :settingName AND domain_id = :domainId;',
[
'settingName' => $settingName,
'domainId' => $domainId,
],
)->fetchOne();

if ($paymentSuccessfulPageContent > 0) {
return;
}

$this->sql(
'INSERT INTO setting_values (name, domain_id, value, type) VALUES (:settingName, :domainId, :settingValue, \'string\')',
[
'settingName' => $settingName,
'settingValue' => $pageContent,
'domainId' => $domainId,
],
);
}

/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function down(Schema $schema): void
{
}
}
32 changes: 32 additions & 0 deletions packages/framework/src/Migrations/Version20231102205308.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Shopsys\MigrationBundle\Component\Doctrine\Migrations\AbstractMigration;

class Version20231102205308 extends AbstractMigration
{
/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function up(Schema $schema): void
{
$this->sql('
ALTER TABLE
orders
ADD
order_payment_status_page_valid_from TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL');
$this->sql('ALTER TABLE orders ADD order_payment_status_page_validity_hash UUID DEFAULT NULL');
$this->sql('ALTER TABLE orders ALTER order_payment_status_page_validity_hash DROP DEFAULT');
}

/**
* @param \Doctrine\DBAL\Schema\Schema $schema
*/
public function down(Schema $schema): void
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Model\Order\ContentPage;

use Shopsys\FrameworkBundle\Model\Order\Order;
use Shopsys\FrameworkBundle\Model\Order\OrderFacade;
use Shopsys\FrameworkBundle\Model\Order\OrderUrlGenerator;

class OrderContentPageFacade
{
public const VARIABLE_PAYMENT_INSTRUCTIONS = '{payment_instructions}';
public const VARIABLE_TRANSPORT_INSTRUCTIONS = '{transport_instructions}';
public const VARIABLE_ORDER_DETAIL_URL = '{order_detail_url}';
public const VARIABLE_NUMBER = '{number}';

/**
* @param \Shopsys\FrameworkBundle\Model\Order\OrderFacade $orderFacade
* @param \Shopsys\FrameworkBundle\Model\Order\OrderUrlGenerator $orderUrlGenerator
* @param \Shopsys\FrameworkBundle\Model\Order\ContentPage\OrderContentPageSettingFacade $orderContentPageSettingFacade
*/
public function __construct(
protected readonly OrderFacade $orderFacade,
protected readonly OrderUrlGenerator $orderUrlGenerator,
protected readonly OrderContentPageSettingFacade $orderContentPageSettingFacade,
) {
}

/**
* @param \Shopsys\FrameworkBundle\Model\Order\Order $order
* @return string
*/
public function getOrderSentPageContent(Order $order): string
{
$orderSentPageContent = $this->orderContentPageSettingFacade->getOrderSentPageContent($order->getDomainId());

return $this->replaceVariables($order, $orderSentPageContent);
}

/**
* @param \Shopsys\FrameworkBundle\Model\Order\Order $order
* @return string
*/
public function getPaymentSuccessfulPageContent(Order $order): string
{
$orderSentPageContent = $this->orderContentPageSettingFacade->getPaymentSuccessfulPageContent($order->getDomainId());

return $this->replaceVariables($order, $orderSentPageContent);
}

/**
* @param \Shopsys\FrameworkBundle\Model\Order\Order $order
* @return string
*/
public function getPaymentFailedPageContent(Order $order): string
{
$orderSentPageContent = $this->orderContentPageSettingFacade->getPaymentFailedPageContent($order->getDomainId());

return $this->replaceVariables($order, $orderSentPageContent);
}

/**
* @param \Shopsys\FrameworkBundle\Model\Order\Order $order
* @param string $orderSentPageContent
* @return string
*/
protected function replaceVariables(Order $order, string $orderSentPageContent): string
{
$orderDetailUrl = $this->orderUrlGenerator->getOrderDetailUrl($order);

$variables = [
self::VARIABLE_TRANSPORT_INSTRUCTIONS => $order->getTransport()->getInstructions(),
self::VARIABLE_PAYMENT_INSTRUCTIONS => $order->getPayment()->getInstructions(),
self::VARIABLE_ORDER_DETAIL_URL => $orderDetailUrl,
self::VARIABLE_NUMBER => $order->getNumber(),
];

return strtr($orderSentPageContent, $variables);
}
}

0 comments on commit 635e01b

Please sign in to comment.