-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
[Inventory] Holding inventory units during checkout #6033
Conversation
Arminek
commented
Sep 8, 2016
Q | A |
---|---|
Bug fix? | no |
New feature? | yes |
BC breaks? | no |
Related tickets | |
License | MIT |
19f1c6d
to
8b8fe77
Compare
are you sure you want to do this as a standard behavior of Sylius? because reserving the stock of a product for the user only for adding it to cart is a pretty big commitment, from business point of view. also, it's not clear for me from these scenarios how the units get released? only by acquisition? how can I, as an admin, cancel these reservations? |
{ | ||
foreach ($order->getItems() as $orderItem) { | ||
/** @var OrderItemInterface $orderItem */ | ||
/** @var ProductVariantInterface $variant */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is redundant, as getVariant
is typehinted.
d6b9f09
to
4a4231c
Compare
@gabiudrescu we decided to have the following default behavior:
Later we will think about mechanisms like releasing reserved units when order is not paid after X minutes, but right now it complicates a lot. E.g. you can have offline payment method (e.g. cash on delivery) and you don't want to release the reserved units after X minutes. What is more we need to figure out what will happen when the reserved units are released and after that someone will pay for the order that have products which are out of stock... Let's discuss that later :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should figure out better naming for these services.
Scenario: Holding inventory units | ||
Given this user has added 3 products "Iron Maiden T-Shirt" to the cart | ||
And this user bought those products | ||
When I view all variants of the product "Iron Maiden T-Shirt" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that When I view variants of product "Iron Maiden T-Shirt"
will be more appropriate step.
Given this user has added 3 products "Iron Maiden T-Shirt" to the cart | ||
And this user bought those products | ||
When I view all variants of the product "Iron Maiden T-Shirt" | ||
Then I should know that 3 units of this product is hold |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then 3 units of this product should be on hold
And 5 units of this product should be on hand
Scenario: Release hold units after order has been paid | ||
Given this user has added 3 products "Iron Maiden T-Shirt" to the cart | ||
And this user bought this product | ||
When I view the summary of this order made by "sylius@example.com" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I view the summary of this order
When I view the summary of this order made by "sylius@example.com" | ||
And I mark the order of "sylius@example.com" as a paid | ||
And I view all variants of the product "Iron Maiden T-Shirt" | ||
Then I should not know about on hold quantity of this product |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then 2 units of this product should be on hand
And there should be no units of this product on hold
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalmarcinkowski And there should be no units of this product on hold?
Given this user has added 3 products "Iron Maiden T-Shirt" to the cart | ||
And this user bought this product | ||
When I view the summary of this order made by "sylius@example.com" | ||
And I mark the order of "sylius@example.com" as a paid |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
... as a paid
|
||
return $quantity === $onHandQuantity; | ||
} catch (ElementNotFoundException $exception) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we return false
when the element is not on page?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO in those kind of methods yes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should return false
only if that element is not rendered if on hand quantity is equal to 0.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at src/Sylius/Bundle/AdminBundle/Resources/views/ProductVariant/Field/inventory.html.twig
we can see, that those elements are always rendered, so we should not catch that exception.
/** | ||
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com> | ||
*/ | ||
final class PaidOrderInventoryHandler implements PaidOrderInventoryHandlerInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the name of the class is coupled with order state (PaidOrder
)?
|
||
public function __construct() | ||
{ | ||
$this->decreasingQuantityUpdaters = new PriorityQueue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another composite? Looks like overhead for this use case...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed.
When I view the summary of this order made by "sylius@example.com" | ||
And I mark the order of "sylius@example.com" as a paid | ||
And I view all variants of the product "Iron Maiden T-Shirt" | ||
Then I should not know about on hold quantity of this product |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalmarcinkowski And there should be no units of this product on hold?
@@ -170,7 +170,7 @@ public function iAmAtTheCheckoutAddressingStep() | |||
} | |||
|
|||
/** | |||
* @Given /^(this user) bought this product$/ | |||
* @Given /^(this user) bought (?:this|those) product(?:|s)$/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(?:this product|those products)
?
@@ -276,6 +276,16 @@ public function thisUserHasProductInTheCart(ShopUserInterface $user, ProductInte | |||
} | |||
|
|||
/** | |||
* @Given /^(this user) has(?:| added) (\d+) (products "[^"]+") (?:to|in) the cart$/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about expanding this regexp?
/^(this user) has (\d+) (products "[^"]+") in the cart$/
/^(this user) added (\d+) (products "[^"]+") to the cart$/
|
||
return $quantity === $onHandQuantity; | ||
} catch (ElementNotFoundException $exception) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should return false
only if that element is not rendered if on hand quantity is equal to 0.
|
||
return $quantity === $onHoldQuantity; | ||
} catch (ElementNotFoundException $exception) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should throw exception when element not found.
|
||
public function __construct() | ||
{ | ||
$this->decreasingQuantityUpdaters = new PriorityQueue(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed.
foreach ($this->decreasingQuantityUpdaters as $decreasingQuantityUpdater) { | ||
try { | ||
$decreasingQuantityUpdater->decrease($order); | ||
} catch (\InvalidArgumentException $exception) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't catch general exceptions.
public function handle(OrderInterface $order) | ||
{ | ||
if ($this->decreasingQuantityUpdaters->isEmpty()) { | ||
throw new HandleException(self::class, 'There is no decreasing updaters to handle this request.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HandleException
says nothing, as we can get a stacktrace from any exception, but it does not say what came wrong.
/** | ||
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com> | ||
*/ | ||
interface PaidOrderInventoryHandlerInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PaidOrder -> Order
/** | ||
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com> | ||
*/ | ||
interface PaidOrderInventoryHandlerInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, Handler
seems to be a too general name. What should it do?
@@ -16,7 +16,7 @@ | |||
/** | |||
* @author Anna Walasek <anna.walasek@lakion.com> | |||
*/ | |||
interface OnHandQuantityUpdaterInterface | |||
interface DecreasingQuantityUpdaterInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it Decreasing
? What should it do?
/** | ||
* @author Arkadiusz Krakowiak <arkadiusz.krakowiak@lakion.com> | ||
*/ | ||
interface IncreasingQuantityUpdaterInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems redundant, has one method with one argument, same as DecreasingQuantity...
, why won't you merge them?
$orderItem->getQuantity()->willReturn(10); | ||
$orderItems = new ArrayCollection(); | ||
$orderItems->add($orderItem->getWrappedObject()); | ||
$order->getItems()->willReturn($orderItems); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$order->getItems()->willReturn([$orderItem]);
instead of these three lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for every ArrayCollection in specs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GitHub does not allow to add request changes review without any text here...
f613632
to
049455b
Compare
public function thereShouldBeNoUnitsOfThisProductOnHold(ProductInterface $product) | ||
{ | ||
Assert::false( | ||
$this->indexPage->hasOnHoldQuantity($product->getFirstVariant(), 1), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why checking for 1
? If 0
is not displayed we ca simply check if there is onHold
label or not.
|
||
return $quantity === $onHoldQuantity; | ||
} catch (ElementNotFoundException $exception) { | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should throw exception when element not found.
$variant = $orderItem->getVariant(); | ||
|
||
if ($variant->isTracked()) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant
) { | ||
$productVariant->getOnHold()->willReturn(10); | ||
$productVariant->isTracked()->willReturn(true); | ||
$productVariant->getName()->willReturn('t-shirt'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need it?
) { | ||
$productVariant->getOnHold()->willReturn(5); | ||
$productVariant->isTracked()->willReturn(true); | ||
$productVariant->getName()->willReturn('t-shirt'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep we are using Assert library so i need this to pass information about the name of variant before the exception will be thrown.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FYI for name
we should use something like "Start wars T-Shirt" to make it closer to real data :)
$productVariant->setOnHold(5)->shouldBeCalled(); | ||
|
||
$this->decrease($order); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing spec for decreasing onHold of two different items.
049455b
to
74fe73e
Compare
* | ||
* @return bool | ||
*/ | ||
public function hasOnHoldLabelFor(ProductVariantInterface $productVariant); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method should be removed.
What do you think about refactoring hasOn*QuantityFor(ProductVariantInterface $productVariant, $quantity) : bool
to getOn*QuantityFor(ProductVariantInterface $productVariant) : int
and comparing it in the context?
This way we can also show nicer exceptions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this separates templating logic (do not show onHold if onHold = 0) as the other shops may implement it differently.
@@ -1,10 +1,10 @@ | |||
{% if data.isTracked %} | |||
<div class="ui {{ data.onHand > 0 ? 'green' : 'red' }} icon label"> | |||
<i class="cube icon"></i> | |||
{{ data.onHand }} {{ 'sylius.ui.available_on_hand'|trans }} | |||
<span id="sylius-{{ data.id }}-on-hand-quantity">{{ data.onHand }}</span> {{ 'sylius.ui.available_on_hand'|trans }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
id="sylius-{{ data.id }}-on-hand-quantity"
-> class="sylius-on-hand-quantity" data-product-variant-id="{{ data.id }}"
? These non-semantic IDs made only for Behat looks quite bad to me :/
ba12d2c
to
95a777d
Compare
95a777d
to
08fcd5e
Compare
Good job Arek! 👍 |