-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Fix product import for Product page v2. #28923
Fix product import for Product page v2. #28923
Conversation
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.
Import calls legacy controller that creates some weird data with 0 ids (associative tables). Inducing wrong behaviors when trying to get consistent data with product page v2.
PrestaShop 8.0.0 allows users to use product page v1 or v2. So what will happen for users of product page v1?
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.
Thanks @MeKeyCool
One test should be added, and I'm not convinced about the modification in import, seems like it induces a regression
@@ -301,6 +301,11 @@ public function getProductType(ProductId $productId): ProductType | |||
)); | |||
} | |||
|
|||
if ($result['product_type'] === ProductType::TYPE_UNDEFINED) { |
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.
That's a good initiative to make this method more resilient to absent data in the DB! I was afraid that maybe we were actually relying on the fact that this exception needs to be thrown in some cases but it doesn't seem to be the case.
To validate this new behavior and make sure we don't include some regression later we should enforce this with a test. This could be tested in behat test by creating a Product with an undefined type maybe in an integration test
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.
After some investigations, I found those tests : https://github.com/PrestaShop/PrestaShop/blob/develop/tests/Integration/Behaviour/Features/Scenario/Product/legacy_product_type.feature
Isn't it enough ? What test is lacking for you ?
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 test was already present before your fix, and yet a bug was happening when we requested the product's suppliers using the GetProductSupplierOptions
query Which means this use case was not tested (trying to ftehc the suppliers while the type is undefined)
So our test coverage didn't handle this buggy use case, meaning the use case wasn't present and should be added, and you're right it should probably be tested in the feature you mentioned
Basically, you can add a test that tries and get the suppliers, if you remove your fix it should fail but with the fix it should be fine now
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.
Just to be precise, this fix is not fixing initial problem, when we investigated initial issue, we found two different problems.
I shared a behat test but I am not well experienced with it so I expect you correct me if I did anything wrong ^^'
@@ -2104,17 +2104,14 @@ protected function productImportOne($info, $default_language_id, $id_lang, $forc | |||
$id_product_supplier = (int) ProductSupplier::getIdByProductAndSupplier((int) $product->id, 0, (int) $product->id_supplier); | |||
if ($id_product_supplier) { | |||
$product_supplier = new ProductSupplier($id_product_supplier); | |||
} else { | |||
$product_supplier = new ProductSupplier(); | |||
$product_supplier->id_product = (int) $product->id; |
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'm not sure about this modification, it means that importing a new supplier that doesn't exist will never work anymore so in most cases import of product supplier will remain empty..
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.
@jolelievre that's actually what happens when you create a product with product page v1 when you don't specify a supplier, that's why we made that choice.
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.
Hmm but I'm not sure it is the same use case 🤔 In product page we are editing a product which can have an existing ProductSupplier, in this case we are importing a product, so most of the time a new product so the chances are high that the ProductSupplier will never exist for this couple of id_product/id_supplier
meaning if it's not present the association is never created
I have close to zero knowledge about the import feature, but it really feels like in this case the ProductSupplier can only be updated when it exists and can never created be created by import
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.
@jolelievre I'm not sure to understand why it's different?
In product page you can also create a product without defining a product supplier so to me it's like importing without specifying a product supplier?
Also, if I remember well, you can have a product supplier during import, you just have to specify which one
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 the main thing to check here is if !empty($product->id_supplier)
to make sure we don't create an association to a non existing supplier
But if the association doesn't exist (and that's the purpose of ProductSupplier::getIdByProductAndSupplier
) we still need to create it (and not update it)
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.
@jolelievre you're right, I didn't get it right 😅
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 am a bit lost with this issue so I may be out of the subject.
But @jolelievre , isn't the test you are asking for done at line 2103 ?
if (!$validateOnly && !empty($product->id) && property_exists($product, 'supplier_reference')) {
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.
Hmm, I'm not talking about the reference value but about the actual $product->id_supplier
which stores the relation between the product and its default supplier
What I mean in this comment is:
- with your modification, you only create the
ProductSupplier
when$id_product_supplier
is not empty $id_product_supplier
is initialized withProductSupplier::getIdByProductAndSupplier
which searches for a relationship between the product and$product->id_supplier
, meaning it will only return a value if the supplier is already associated to the product (ProductSupplier represents the association table between product and supplier)- so if the relationship isn't found (because it's not in DB) you don't create the ProductSupplier, meaning you don't create the relationship
- so an inexistent relationship will never be created
- so with you modify you can only modify/update an existing relationship
- TLDR: if the relationship doesn't exist yet it's never created so the import is now unable to create a new relationship (it was able to do so with the previous code)
So that's why I'm blocking this modification because it introduces a regression
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.
Now besides this modification which is not appropriate I discussed with @atomiix who worked with you on this bug fix and it seems like there was a bug because of this because a ProductSupplier instance was created but the id_supplier was 0 which is not accepted in the product page v2 because it tries to get a non-existent supplier (indeed no supplier can have an ID 0) But the problem comes from the fact that we created a ProductSuplier relationship with id_supplier being 0
This should be prevented, and to do so we need to check that $product->id_supplier
is different from 0 before we try and create the ProductSupplier (and we don't even need to look for a relationship with ProductSupplier::getIdByProductAndSupplier
since it doesn't make sense)
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.
TLDR: you should add && !empty($product->id_supplier)
on Line 2103, currently the test checks the supplier_reference
value not the ID itself
@matks , do you mean that you suspect I didn't test enough ? |
2b986a3
to
b91f71f
Compare
tests/Integration/Behaviour/Features/Context/LegacyProductFeatureContext.php
Outdated
Show resolved
Hide resolved
b91f71f
to
cbc3407
Compare
cbc3407
to
0b651bb
Compare
0652e8d
to
35e7a8e
Compare
return new ProductType($result['product_type']); | ||
// Older products that were created before product page v2, might have no type, so we determine it dynamically | ||
// According Phpstan, "Strict comparison using === between mixed and '' will always evaluate to false." so we use == instead of === | ||
if (empty($result['product_type']) || ($result['product_type'] == ProductType::TYPE_UNDEFINED)) { |
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 you should annotate the $result
variable instead of using ==
:
/** @var array<string, string> $result */
$result = $this->connection->createQueryBuilder()
...
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.
Lol, in fact I was completely out of subject, the point was that ProductType::TYPE_UNDEFINED = ''
(empty string) so empty($result['product_type'])
already test this case 😅
b4000d6
to
1e24d7a
Compare
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's at least the method productAsToBeEditable
that needs to be updated and I think productShouldBeEditable
is a better name to be consistent with the other *should be*
methods
* @Then product :productName has to be editable | ||
*/ | ||
public function productAsToBeEditable($productName) |
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 product :productName has to be editable | |
*/ | |
public function productAsToBeEditable($productName) | |
* @Then product :productName should be editable | |
*/ | |
public function productShouldBeEditable($productName) |
Then there is a product "undefined_product" with name "Undefined Product" | ||
And product "undefined_product" type should be standard | ||
And product "undefined_product" persisted type should be undefined | ||
And product "undefined_product" dynamic type should be standard | ||
And product "Undefined Product" has to be editable |
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 know it's not what has been done with the tests just above but it make more sense because there is a product "undefined_product" with name "Undefined Product"
doesn't check something, it sets something.
Then there is a product "undefined_product" with name "Undefined Product" | |
And product "undefined_product" type should be standard | |
And product "undefined_product" persisted type should be undefined | |
And product "undefined_product" dynamic type should be standard | |
And product "Undefined Product" has to be editable | |
And there is a product "undefined_product" with name "Undefined Product" | |
Then product "undefined_product" type should be standard | |
And product "undefined_product" persisted type should be undefined | |
And product "undefined_product" dynamic type should be standard | |
And product "Undefined Product" should be editable |
@@ -309,7 +309,7 @@ public function getProductType(ProductId $productId): ProductType | |||
$productId->getValue() | |||
)); | |||
} | |||
|
|||
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.
- Remove default empty supplier creation during import. - Fix getProductType for `ProductType::TYPE_UNDEFINED`
1e24d7a
to
5690e58
Compare
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.
Thanks @MeKeyCool
I will check this pr |
Hello @MeKeyCool
PR-28923.mp4Thank you ! |
good job @MeKeyCool and @sLorenzini ! |
In order to fix product import for Product page v2, we
ProductType::TYPE_UNDEFINED