Features: 1) Add support for processing chosen_products in order creation and update mutations; 2) Introduce new field chosen_products to serializers and GraphQL inputs; 3) Enhance order model to allow bulk addition of products with quantity updates;

Fixes: 1) Correct avatar resolution in `resolve_avatar` method for cases where `url` or `name` attributes are missing; 2) Refresh user object from the database after avatar update to avoid stale data;

Extra: 1) Rename `BulkActionOrderProductInput` to `BulkProductInput` for consistency; 2) Minor code refactorings and adjustments for clarity.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-07-08 21:02:54 +03:00
parent 171f2cb20f
commit 89301d6432
7 changed files with 21 additions and 10 deletions

View file

@ -13,7 +13,7 @@ from core.elasticsearch import process_query
from core.graphene import BaseMutation
from core.graphene.object_types import (
AddressType,
BulkActionOrderProductInput,
BulkProductInput,
OrderType,
ProductType,
SearchResultsType,
@ -182,6 +182,7 @@ class BuyOrder(BaseMutation):
promocode_uuid = String(required=False)
shipping_address = String(required=False)
billing_address = String(required=False)
chosen_products = List(BulkProductInput, required=False)
order = Field(OrderType, required=False)
transaction = Field(TransactionType, required=False)
@ -197,6 +198,7 @@ class BuyOrder(BaseMutation):
promocode_uuid=None,
shipping_address=None,
billing_address=None,
chosen_products=None,
):
if not any([order_uuid, order_hr_id]) or all([order_uuid, order_hr_id]):
raise BadRequest(_("please provide either order_uuid or order_hr_id - mutually exclusive"))
@ -215,6 +217,7 @@ class BuyOrder(BaseMutation):
promocode_uuid=promocode_uuid,
shipping_address=shipping_address,
billing_address=billing_address,
chosen_products=chosen_products,
)
match str(type(instance)):
@ -237,7 +240,7 @@ class BulkOrderAction(BaseMutation):
order_uuid = UUID(required=False)
order_hr_id = String(required=False)
action = String(required=True, description=_("remove/add"))
products = List(BulkActionOrderProductInput, required=True)
products = List(BulkProductInput, required=True)
order = Field(OrderType, required=False)
@ -283,7 +286,7 @@ class BulkWishlistAction(BaseMutation):
class Arguments:
wishlist_uuid = UUID(required=False)
action = String(required=True, description="remove/add")
products = List(BulkActionOrderProductInput, required=True)
products = List(BulkProductInput, required=True)
wishlist = Field(WishlistType, required=False)

View file

@ -566,6 +566,6 @@ class SearchResultsType(ObjectType):
posts = List(description=_("posts search results"), of_type=SearchPostsResultsType)
class BulkActionOrderProductInput(InputObjectType):
class BulkProductInput(InputObjectType):
uuid = UUID(required=True)
attributes = GenericScalar(required=False)

View file

@ -1579,7 +1579,7 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
if chosen_products:
order = Order.objects.create(status="MOMENTAL", user=self.user)
order.bulk_add_products(chosen_products)
order.bulk_add_products(chosen_products, update_quantity=True)
if config.DISABLED_COMMERCE:
raise DisabledCommerceError(_("you can not buy at this moment, please try again in a few minutes"))
@ -1713,19 +1713,19 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
def update_order_products_statuses(self, status: str = "PENDING"):
self.order_products.update(status=status)
def bulk_add_products(self, products: list[dict[str, Any]]):
def bulk_add_products(self, products: list[dict[str, Any]], update_quantity: bool = False):
for product in products:
self.add_product(
product.get("uuid"),
product.get("uuid") or product.get("product_uuid"),
attributes=product.get("attributes"),
update_quantity=False,
update_quantity=update_quantity,
)
return self
def bulk_remove_products(self, products: list):
for product in products:
self.remove_product(
product.get("uuid"),
product.get("uuid") or product.get("product_uuid"),
attributes=product.get("attributes"),
zero_quantity=True,
)

View file

@ -160,6 +160,7 @@ class BuyOrderSerializer(Serializer):
promocode_uuid = CharField(required=False)
shipping_address_uuid = CharField(required=False)
billing_address_uuid = CharField(required=False)
chosen_products = ListField(child=AddOrderProductSerializer(), required=False)
class BuyUnregisteredOrderSerializer(Serializer):

View file

@ -585,6 +585,7 @@ class OrderViewSet(EvibesViewSet):
promocode_uuid=serializer.validated_data.get("promocode_uuid"),
shipping_address=serializer.validated_data.get("shipping_address_uuid"),
billing_address=serializer.validated_data.get("billing_address_uuid"),
chosen_products=serializer.validated_data.get("chosen_products"),
)
match str(type(instance)):
case "<class 'payments.models.Transaction'>":

View file

@ -338,6 +338,7 @@ class UploadAvatar(BaseMutation):
try:
info.context.user.avatar = avatar
info.context.user.save()
info.context.user.refresh_from_db()
except Exception as e:
raise BadRequest(str(e)) from e

View file

@ -97,7 +97,12 @@ class UserType(DjangoObjectType):
def resolve_avatar(self: User, info) -> str:
if self.avatar:
return info.context.build_absolute_uri(self.avatar.url)
if hasattr(self.avatar, "url"):
return info.context.build_absolute_uri(self.avatar.url)
elif hasattr(self.avatar, "name"):
return info.context.build_absolute_uri(f"/media/{self.avatar.name}")
else:
return info.context.build_absolute_uri(f"/media/{self.avatar}")
else:
return ""