Repository: saleor/saleor
Pull request: Add separate input for ProductVariantCreate and ProductVariantUpdate
I want to merge this change because...

closes #2674 

### Screenshots

<!-- If your changes affect the UI, providing "before" and "after" screenshots will
greatly reduce the amount of work needed to review your work. -->

### Pull Request Checklist

<!-- Please keep this section. It will make maintainer's life easier. -->

1. [x] Privileged views and APIs are guarded by proper permission checks.
1. [x] All visible strings are translated with proper context.
1. [x] All data-formatting is locale-aware (dates, numbers, and so on).
1. [x] Database queries are optimized and the number of queries is constant.
1. [x] The changes are tested.
1. [ ] The code is documented (docstrings, project documentation).


Problem statement:
It's possible to reparent a product variant using the GraphQL mutation
Currently `productVariantCreate` and `productVariantUpdate` share the same input type.

This causes `productVariantCreate` to list `product` (an ID field) as optional (which is invalid).

It also causes `productVariantUpdate` to accept a new value for `product` which should be illegal as it would move a variant between two products.

Hints from issue discussion:

Commit message: Add separate input for ProductVariantCreate and ProductVariantUpdate

Patch to review:
diff --git a/saleor/graphql/product/mutations/products.py b/saleor/graphql/product/mutations/products.py
index 7dad6e2fd00..9d9930c12fd 100644
--- a/saleor/graphql/product/mutations/products.py
+++ b/saleor/graphql/product/mutations/products.py
@@ -294,16 +294,13 @@ def user_is_allowed(cls, user, input):
         return user.has_perm('product.manage_products')
 
 
-class ProductVariantInput(graphene.InputObjectType):
+class ProductVariantPropertiesInput(graphene.InputObjectType):
     attributes = graphene.List(
         AttributeValueInput,
         description='List of attributes specific to this variant.')
     cost_price = Decimal(description='Cost price of the variant.')
     price_override = Decimal(
         description='Special price of the particular variant.')
-    product = graphene.ID(
-        description='Product ID of which type is the variant.',
-        name='product')
     sku = graphene.String(description='Stock keeping unit.')
     quantity = graphene.Int(
         description='The total quantity of this variant available for sale.')
@@ -315,9 +312,15 @@ class ProductVariantInput(graphene.InputObjectType):
         description='Weight of the Product Variant.', required=False)
 
 
+class ProductVariantCreateInput(ProductVariantPropertiesInput):
+    product = graphene.ID(
+        description='Product ID of which type is the variant.',
+        name='product', required=True)
+
+
 class ProductVariantCreate(ModelMutation):
     class Arguments:
-        input = ProductVariantInput(
+        input = ProductVariantCreateInput(
             required=True,
             description='Fields required to create a product variant.')
 
@@ -364,7 +367,7 @@ class ProductVariantUpdate(ProductVariantCreate):
     class Arguments:
         id = graphene.ID(
             required=True, description='ID of a product variant to update.')
-        input = ProductVariantInput(
+        input = ProductVariantPropertiesInput(
             required=True,
             description='Fields required to update a product variant.')
 


Code review comments:
diff_hunk: @@ -294,16 +294,13 @@ def user_is_allowed(cls, user, input):
         return user.has_perm('product.manage_products')
 
 
-class ProductVariantInput(graphene.InputObjectType):
+class ProductVariantPropertiesInput(graphene.InputObjectType):
path: saleor/graphql/product/mutations/products.py
text: @user1:
I think the word "properties" may be misinterpreted as relating to product attributes. I would keep the name ProductVariantInput" here. The other name, "ProductVariantCreateInput" is fine.