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

Migrate "Orders > Shopping Carts" page #35007

Merged

Conversation

boherm
Copy link
Member

@boherm boherm commented Jan 9, 2024

Questions Answers
Branch? develop
Description? "Orders > Carts" page migration
Type? improvement
Category? BO
BC breaks? no
Deprecations? no
How to test? Before, please build admin new-theme with make admin-new-theme !
First, active Carts index feature flag, and then navigate into Orders -> Carts page.
You can filter data, delete not already ordered carts one by one or by bulk actions.
Multistore mode is supported, and in that case we add Shop column into grid (only for all / group context).
Fixed ticket? Fixes #10584
Related PRs #31934
Sponsor company

Warning:
For now new Kpi use legacy to retrieve data in all already migrated pages!

@boherm boherm requested a review from a team as a code owner January 9, 2024 13:19
@prestonBot
Copy link
Collaborator

Hello @boherm!

This is your first pull request on the PrestaShop project. Thank you, and welcome to this Open Source community!

@boherm boherm added Improvement Type: Improvement develop Branch labels Jan 9, 2024
@matks matks added the migration symfony migration project label Jan 15, 2024
Comment on lines 46 to 63
/**
* @var CartRepository
*/
private $cartRepository;

/**
* @var OrderRepository
*/
private $orderRepository;

/**
* @param CartRepository $cartRepository
*/
public function __construct(CartRepository $cartRepository, OrderRepository $orderRepository)
{
$this->cartRepository = $cartRepository;
$this->orderRepository = $orderRepository;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We could type, or even use the construct to define class variables

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep you can use property promotion now, which probably wasn't possible when you started the PR, but I'd say it can be optional if you have time to do it Maybe not blocking

Comment on lines 48 to 65
/**
* @var CartRepository
*/
private $cartRepository;

/**
* @var OrderRepository
*/
private $orderRepository;

/**
* @param CartRepository $cartRepository
*/
public function __construct(CartRepository $cartRepository, OrderRepository $orderRepository)
{
$this->cartRepository = $cartRepository;
$this->orderRepository = $orderRepository;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We could type, or even use the construct to define class variables

Comment on lines 75 to 82
public function handle(DeleteCartCommand $command): void
{
$order = $this->orderRepository->findByCartId($command->getCartId());
if ($order) {
throw new CannotDeleteOrderedCartException(sprintf('Cart "%s" with order cannot be deleted.', $command->getCartId()->getValue()));
}
$this->cartRepository->delete($command->getCartId());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This part is practically the same as the bulk delete, without the loop. Maybe this can be merged

Copy link
Contributor

Choose a reason for hiding this comment

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

I would advise to keep them separated ;)

If you forget they look similar, do you think it makes sense the code for bulk delete and code for delete is the same? Aren't they two different things?

I think the implementations coincide but it's just random

https://github.com/PrestaShop/PrestaShop/blob/develop/src/Adapter/Profile/CommandHandler/BulkDeleteProfileHandler.php
https://github.com/PrestaShop/PrestaShop/blob/develop/src/Adapter/Profile/CommandHandler/DeleteProfileHandler.php

Copy link
Contributor

Choose a reason for hiding this comment

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

Both handlers do the same thing so it's normal they look alike, however each command implementation should remain independent to avoid too much coupling In this case three lines of code that are duplicates are acceptable I think Maintaining a common trait or abstract class would probably cost us more in the long run

Comment on lines 44 to 88
/**
* @var TranslatorInterface
*/
private $translator;

/**
* @var ConfigurationInterface
*/
private $configuration;

/**
* @var LegacyContext
*/
private $contextAdapter;

/**
* @var UrlGeneratorInterface
*/
private $router;

/**
* @var FeatureFlagRepository
*/
private $featureFlag;

/**
* @param TranslatorInterface $translator
* @param ConfigurationInterface $configuration
* @param LegacyContext $contextAdapter
* @param UrlGeneratorInterface $router
* @param FeatureFlagRepository $featureFlag
*/
public function __construct(
TranslatorInterface $translator,
ConfigurationInterface $configuration,
LegacyContext $contextAdapter,
UrlGeneratorInterface $router,
FeatureFlagRepository $featureFlag
) {
$this->translator = $translator;
$this->configuration = $configuration;
$this->contextAdapter = $contextAdapter;
$this->router = $router;
$this->featureFlag = $featureFlag;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We could type, or even use the construct to define class variables

Comment on lines 106 to 114
$helper->href = $this->contextAdapter->getAdminLink('AdminCarts', true, [
'action' => 'filterOnlyAbandonedCarts',
]);

if ($this->featureFlag->isEnabled(FeatureFlagSettings::FEATURE_FLAG_CARTS)) {
$helper->href = $this->router->generate('admin_carts_index', [
'cart[filters][status]' => CartStatus::ABANDONED_CART,
]);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
$helper->href = $this->contextAdapter->getAdminLink('AdminCarts', true, [
'action' => 'filterOnlyAbandonedCarts',
]);
if ($this->featureFlag->isEnabled(FeatureFlagSettings::FEATURE_FLAG_CARTS)) {
$helper->href = $this->router->generate('admin_carts_index', [
'cart[filters][status]' => CartStatus::ABANDONED_CART,
]);
}
if ($this->featureFlag->isEnabled(FeatureFlagSettings::FEATURE_FLAG_CARTS)) {
$helper->href = $this->router->generate('admin_carts_index', [
'cart[filters][status]' => CartStatus::ABANDONED_CART,
]);
} else {
$helper->href = $this->contextAdapter->getAdminLink('AdminCarts', true, [
'action' => 'filterOnlyAbandonedCarts',
]);
}

@@ -536,3 +536,11 @@ services:
$languageId: '@=service("prestashop.adapter.legacy.context").getContext().language.id'
$connection: '@doctrine.dbal.default_connection'
$dbPrefix: '%database_prefix%'

prestashop.core.grid.query.builder.cart:
Copy link
Contributor

Choose a reason for hiding this comment

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

Use FQCN instead

- '@prestashop.core.grid.query.doctrine_query_parser'
- 'cart'

prestashop.core.grid.data.factory.cart_decorator:
Copy link
Contributor

Choose a reason for hiding this comment

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

Use FQCN instead

@@ -428,3 +428,14 @@ services:
calls:
- [ setTranslator, [ '@translator' ] ]
deprecated: ~

prestashop.core.grid.definition.factory.cart:
Copy link
Contributor

Choose a reason for hiding this comment

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

Use FQCN instead

Comment on lines 437 to 441
- '@=service("prestashop.adapter.legacy.context").getContext().language.date_format_full'
- '@prestashop.adapter.shop.context'
- '@=service("prestashop.adapter.multistore_feature").isActive()'
Copy link
Contributor

Choose a reason for hiding this comment

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

it is better to do these operations in the class

Copy link
Contributor

@matks matks left a comment

Choose a reason for hiding this comment

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

Great work 😉

I provided little things to improve/fix but it's very minor

@@ -0,0 +1,51 @@
/**
* vCopyright since 2007 PrestaShop SA and Contributors
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* vCopyright since 2007 PrestaShop SA and Contributors
* Copyright since 2007 PrestaShop SA and Contributors

Little typo, I guess a copy-paste missclick 😄

Comment on lines 75 to 82
public function handle(DeleteCartCommand $command): void
{
$order = $this->orderRepository->findByCartId($command->getCartId());
if ($order) {
throw new CannotDeleteOrderedCartException(sprintf('Cart "%s" with order cannot be deleted.', $command->getCartId()->getValue()));
}
$this->cartRepository->delete($command->getCartId());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I would advise to keep them separated ;)

If you forget they look similar, do you think it makes sense the code for bulk delete and code for delete is the same? Aren't they two different things?

I think the implementations coincide but it's just random

https://github.com/PrestaShop/PrestaShop/blob/develop/src/Adapter/Profile/CommandHandler/BulkDeleteProfileHandler.php
https://github.com/PrestaShop/PrestaShop/blob/develop/src/Adapter/Profile/CommandHandler/DeleteProfileHandler.php

/**
* {@inheritdoc}
*/
public function render()
{
$translator = Context::getContext()->getTranslator();
$dateFormat = $this->contextAdapter->getLanguage()->date_format_lite;
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we have the modern contexts you should prefer using them so you can inject LanguageContext instead here, date_format_lite is synonym to LanguageContext->getDateFormat()

Copy link
Contributor

@jolelievre jolelievre left a comment

Choose a reason for hiding this comment

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

Thanks @boherm

A few comments here and there but it's looking almost finished, the suggestions from Morgan about property promotion would be a nice bonus but I won't block only for that personally

Comment on lines 46 to 63
/**
* @var CartRepository
*/
private $cartRepository;

/**
* @var OrderRepository
*/
private $orderRepository;

/**
* @param CartRepository $cartRepository
*/
public function __construct(CartRepository $cartRepository, OrderRepository $orderRepository)
{
$this->cartRepository = $cartRepository;
$this->orderRepository = $orderRepository;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Yep you can use property promotion now, which probably wasn't possible when you started the PR, but I'd say it can be optional if you have time to do it Maybe not blocking

* Deletes cart in bulk action using legacy object model
*/
#[AsCommandHandler]
final class BulkDeleteCartHandler implements BulkDeleteCartHandlerInterface
Copy link
Contributor

Choose a reason for hiding this comment

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

You couldn't know about it but a class as introduced for bulk handlers AbstractBulkCommandHandler there is an example of usage here:

class BulkDeleteAliasHandler extends AbstractBulkCommandHandler implements BulkDeleteAliasHandlerInterface

It has two advantages:

  • it mutualizes the code to handle bulk commands which is almost always the same except for the specific part that performs the single action
  • it forces using an exception that implements BulkCommandExceptionInterface this interface allows handling an exception for each entity that failed thus allowing more accurate error message in the UX

Comment on lines 75 to 82
public function handle(DeleteCartCommand $command): void
{
$order = $this->orderRepository->findByCartId($command->getCartId());
if ($order) {
throw new CannotDeleteOrderedCartException(sprintf('Cart "%s" with order cannot be deleted.', $command->getCartId()->getValue()));
}
$this->cartRepository->delete($command->getCartId());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Both handlers do the same thing so it's normal they look alike, however each command implementation should remain independent to avoid too much coupling In this case three lines of code that are duplicates are acceptable I think Maintaining a common trait or abstract class would probably cost us more in the long run

*
* @throws CoreException
*/
public function findByCartId(CartId $cartId): ?Order
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public function findByCartId(CartId $cartId): ?Order
public function getByCartId(CartId $cartId): Order

To be more consistent with the usual get method in the object model repositories Also, instead of returning null maybe you should throw an OrderNotFoundException, also to be more consistent with the get behaviour

Copy link
Contributor

Choose a reason for hiding this comment

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

Either that or you could implement a public function getOrderIdByCartId(CartId $cartId): ?OrderId method (this one could have a nullable return), which would match more with the usage you have (check if an order is associated to the cart) and it prevents fetching the whole Order object which you don't need in your delete handlers

/**
* {@inheritdoc}
*/
public function render()
{
$translator = Context::getContext()->getTranslator();
$dateFormat = $this->contextAdapter->getLanguage()->date_format_lite;
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we have the modern contexts you should prefer using them so you can inject LanguageContext instead here, date_format_lite is synonym to LanguageContext->getDateFormat()

_controller: PrestaShopBundle\Controller\Admin\Sell\Order\CartController::indexAction
_legacy_controller: AdminCarts
_legacy_link: AdminCarts
_legacy_feature_flag: carts
Copy link
Contributor

Choose a reason for hiding this comment

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

It's good to add the feature flag on the new routes, but you probably should also add it on the existing ones in this file

Comment on lines 99 to 102
prestashop.adapter.cart.command_handler.delete_cart_handler:
class: 'PrestaShop\PrestaShop\Adapter\Cart\CommandHandler\DeleteCartHandler'
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
prestashop.adapter.cart.command_handler.delete_cart_handler:
class: 'PrestaShop\PrestaShop\Adapter\Cart\CommandHandler\DeleteCartHandler'
PrestaShop\PrestaShop\Adapter\Cart\CommandHandler\DeleteCartHandler:

We favor the FQCN notation now, even for private services and handlers

Comment on lines 106 to 109
prestashop.adapter.cart.command_handler.bulk_delete_cart_handler:
class: 'PrestaShop\PrestaShop\Adapter\Cart\CommandHandler\BulkDeleteCartHandler'
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
prestashop.adapter.cart.command_handler.bulk_delete_cart_handler:
class: 'PrestaShop\PrestaShop\Adapter\Cart\CommandHandler\BulkDeleteCartHandler'
PrestaShop\PrestaShop\Adapter\Cart\CommandHandler\BulkDeleteCartHandler:

*/
public function __construct(
HookDispatcherInterface $hookDispatcher,
string $contextDateFormat,
Copy link
Contributor

Choose a reason for hiding this comment

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

You should inject LanguageContext instead

Comment on lines 54 to 56
Then cart "dummy_cart1" should exist because cart is already ordered
And cart "dummy_cart2" should exist
And cart "dummy_cart3" should exist
Copy link
Contributor

Choose a reason for hiding this comment

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

This actually shows an issue here, the bulk action shouldn't stop because one of the actions failed It's supposed to keep doing the delete on the whole list and store if any of them failed

But this comment is actually in line with my previous suggestions on the bulk handler, the suggested abstract class is actually here to help implementing this kind of behavior and we'd like to make it generic on all bulk actions for consistency

- Add alignement system in column
- Add `badge_type_field` to use column in grid to select badge type directly and no only color (like `color_field` for instance)
- Use DI instead of `Context`
- Add purple color
- Create kpi_row factory for Carts page

kpi
- Add index action page
- Add solo/bulk delete actions
- Add export into csv
Add cart JS

grid

query

grid
- Add new column for display & search status
- Fix AbandonedCartKpi to go directly into abandoned cart

fix grid improvement
@boherm boherm force-pushed the #10584-migrate-bo-orders-shopping-carts-page branch from 6e3e69b to c4f68ae Compare February 7, 2024 17:16
@boherm boherm force-pushed the #10584-migrate-bo-orders-shopping-carts-page branch from 0965d59 to 236e4c7 Compare February 7, 2024 19:05
@boherm
Copy link
Member Author

boherm commented Feb 9, 2024

Some tests before merging: https://github.com/boherm/ga.tests.ui.pr/actions/runs/7841541053

@boherm boherm added the Waiting for author Status: action required, waiting for author feedback label Feb 9, 2024
@WahbiPS WahbiPS removed the Waiting for author Status: action required, waiting for author feedback label Feb 12, 2024
@boherm boherm merged commit ff4c55d into PrestaShop:develop Feb 12, 2024
23 checks passed
@boherm boherm deleted the #10584-migrate-bo-orders-shopping-carts-page branch February 12, 2024 08:38
@boherm boherm added this to the 9.0.0 milestone Feb 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
develop Branch Improvement Type: Improvement migration symfony migration project
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Migrate "Orders > Shopping Carts" page
6 participants