From 755d74a9ff51396e88085401996e9e0fccde908e Mon Sep 17 00:00:00 2001 From: Ali Asadi Date: Sat, 22 Aug 2026 17:04:21 +0330 Subject: [PATCH] Return phone_number and distance_km on store list phone_number was only exposed on the store detail serializer; add it to the list too. Also surface the distance annotation (already computed when lat/lng are passed) as distance_km on both, instead of using it only for ordering. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 1 + apps/stores/serializers.py | 13 ++++++++++--- apps/stores/tests/test_store_list.py | 27 +++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d94057f..0f8a94e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Added - `GET /api/v1/cities/?search=` — search cities by name (`apps/locations/views.py`); previously no text search existed. - `GET /api/v1/neighborhoods/?search=` — search neighborhoods by neighborhood name or city name, alongside the existing `city` UUID filter (`apps/locations/views.py`). +- `GET /api/v1/stores/` now returns `phone_number` in the list (previously only on the single-store detail view) and a `distance_km` field, populated whenever `lat`/`lng` are passed (`apps/stores/serializers.py`). - Test coverage for `StoreViewSet` list filtering (`apps/stores/tests/test_store_list.py`) — previously untested. - Test coverage for `ProductViewSet` list filtering (`apps/catalog/tests/test_product_list.py`) — previously untested. - Test coverage for `CityViewSet`/`NeighborhoodViewSet` list filtering (`apps/locations/tests/test_location_list.py`) — previously untested. diff --git a/apps/stores/serializers.py b/apps/stores/serializers.py index 262fde7..26b2af7 100644 --- a/apps/stores/serializers.py +++ b/apps/stores/serializers.py @@ -31,13 +31,14 @@ class StoreListSerializer(serializers.ModelSerializer): category = StoreCategorySerializer(read_only=True) logo_url = serializers.SerializerMethodField() cover_image_url = serializers.SerializerMethodField() + distance_km = serializers.SerializerMethodField() class Meta: model = Store fields = ( 'uuid', 'name', 'category', 'logo', 'logo_url', 'cover_image', 'cover_image_url', - 'rating_avg', 'rating_count', 'min_order_amount', 'delivery_fee', - 'free_delivery_threshold', 'is_open', + 'phone_number', 'rating_avg', 'rating_count', 'min_order_amount', 'delivery_fee', + 'free_delivery_threshold', 'is_open', 'distance_km', ) @extend_schema_field(serializers.URLField(allow_null=True)) @@ -48,13 +49,19 @@ class StoreListSerializer(serializers.ModelSerializer): def get_cover_image_url(self, obj): return presigned_media_url(obj.cover_image) + @extend_schema_field(serializers.FloatField(allow_null=True)) + def get_distance_km(self, obj): + """Only present when the request passed `lat`/`lng` (see StoreViewSet.get_queryset).""" + distance = getattr(obj, 'distance', None) + return round(distance.km, 2) if distance is not None else None + class StoreDetailSerializer(StoreListSerializer): working_hours = StoreWorkingHoursSerializer(many=True, read_only=True) class Meta(StoreListSerializer.Meta): fields = StoreListSerializer.Meta.fields + ( - 'description', 'address', 'phone_number', + 'description', 'address', 'accepts_wallet', 'accepts_online', 'accepts_cash_on_delivery', 'working_hours', ) diff --git a/apps/stores/tests/test_store_list.py b/apps/stores/tests/test_store_list.py index 9049da1..11b61cf 100644 --- a/apps/stores/tests/test_store_list.py +++ b/apps/stores/tests/test_store_list.py @@ -1,3 +1,5 @@ +from django.contrib.gis.geos import Point + from apps.stores.models import StoreCategory from apps.orders.tests.base import OrdersTestCase @@ -21,3 +23,28 @@ class StoreListFilterTests(OrdersTestCase): self.assertEqual(response.status_code, 200) uuids = {store['uuid'] for store in response.data['results']} self.assertEqual(uuids, {str(self.store1.uuid), str(self.store2.uuid)}) + + def test_phone_number_included_in_list(self): + self.store1.phone_number = '02112345678' + self.store1.save(update_fields=['phone_number']) + + response = self.client.get('/api/v1/stores/') + + store = next(s for s in response.data['results'] if s['uuid'] == str(self.store1.uuid)) + self.assertEqual(store['phone_number'], '02112345678') + + def test_distance_km_present_when_lat_lng_given(self): + self.store1.location = Point(51.4, 35.75, srid=4326) + self.store1.save(update_fields=['location']) + + response = self.client.get('/api/v1/stores/', {'lat': '35.75', 'lng': '51.4'}) + + store = next(s for s in response.data['results'] if s['uuid'] == str(self.store1.uuid)) + self.assertIsNotNone(store['distance_km']) + self.assertAlmostEqual(store['distance_km'], 0, delta=0.1) + + def test_distance_km_absent_without_lat_lng(self): + response = self.client.get('/api/v1/stores/') + + store = next(s for s in response.data['results'] if s['uuid'] == str(self.store1.uuid)) + self.assertIsNone(store['distance_km'])