ob-dj-store 0.0.11.3__py3-none-any.whl → 0.0.11.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. ob_dj_store/apis/stores/filters.py +33 -18
  2. ob_dj_store/apis/stores/rest/serializers/serializers.py +129 -53
  3. ob_dj_store/apis/stores/urls.py +3 -3
  4. ob_dj_store/apis/stores/views.py +182 -42
  5. ob_dj_store/core/stores/admin.py +8 -3
  6. ob_dj_store/core/stores/admin_inlines.py +4 -4
  7. ob_dj_store/core/stores/gateway/tap/models.py +1 -1
  8. ob_dj_store/core/stores/gateway/tap/utils.py +4 -4
  9. ob_dj_store/core/stores/managers.py +8 -6
  10. ob_dj_store/core/stores/migrations/0056_auto_20230213_2224.py +33 -0
  11. ob_dj_store/core/stores/migrations/0057_auto_20230214_1724.py +37 -0
  12. ob_dj_store/core/stores/migrations/0058_attributechoice_is_default.py +18 -0
  13. ob_dj_store/core/stores/migrations/0059_auto_20230217_2006.py +41 -0
  14. ob_dj_store/core/stores/migrations/0060_alter_orderitem_product_variant.py +24 -0
  15. ob_dj_store/core/stores/migrations/0061_auto_20230223_1435.py +28 -0
  16. ob_dj_store/core/stores/migrations/0062_auto_20230226_2005.py +43 -0
  17. ob_dj_store/core/stores/migrations/0063_alter_store_payment_methods.py +23 -0
  18. ob_dj_store/core/stores/migrations/0064_auto_20230228_1814.py +24 -0
  19. ob_dj_store/core/stores/migrations/0065_auto_20230228_1932.py +34 -0
  20. ob_dj_store/core/stores/models/_favorite.py +4 -1
  21. ob_dj_store/core/stores/models/_order.py +4 -2
  22. ob_dj_store/core/stores/models/_payment.py +31 -11
  23. ob_dj_store/core/stores/models/_product.py +22 -13
  24. ob_dj_store/core/stores/models/_store.py +15 -6
  25. ob_dj_store/core/stores/models/_wallet.py +41 -8
  26. ob_dj_store/core/stores/receivers.py +79 -4
  27. ob_dj_store/core/stores/utils.py +12 -6
  28. ob_dj_store/utils/helpers.py +8 -2
  29. ob_dj_store/utils/utils.py +43 -3
  30. {ob_dj_store-0.0.11.3.dist-info → ob_dj_store-0.0.11.10.dist-info}/METADATA +1 -1
  31. {ob_dj_store-0.0.11.3.dist-info → ob_dj_store-0.0.11.10.dist-info}/RECORD +33 -23
  32. {ob_dj_store-0.0.11.3.dist-info → ob_dj_store-0.0.11.10.dist-info}/WHEEL +0 -0
  33. {ob_dj_store-0.0.11.3.dist-info → ob_dj_store-0.0.11.10.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,8 @@ import typing
4
4
  from django.contrib.contenttypes.models import ContentType
5
5
  from django.core.exceptions import ObjectDoesNotExist
6
6
  from django.db.models import Prefetch
7
+ from django.http import Http404
8
+ from django.shortcuts import get_object_or_404
7
9
  from django.utils.decorators import method_decorator
8
10
  from django.utils.translation import ugettext_lazy as _
9
11
  from django_auto_prefetching import AutoPrefetchViewSetMixin, prefetch
@@ -24,6 +26,7 @@ from ob_dj_store.apis.stores.filters import (
24
26
  FavoriteFilter,
25
27
  InventoryFilter,
26
28
  OrderFilter,
29
+ PaymentMethodFilter,
27
30
  ProductFilter,
28
31
  StoreFilter,
29
32
  VariantFilter,
@@ -45,6 +48,8 @@ from ob_dj_store.apis.stores.rest.serializers.serializers import (
45
48
  ShippingMethodSerializer,
46
49
  StoreSerializer,
47
50
  TaxSerializer,
51
+ WalletSerializer,
52
+ WalletTopUpSerializer,
48
53
  )
49
54
  from ob_dj_store.core.stores.models import (
50
55
  Cart,
@@ -60,6 +65,7 @@ from ob_dj_store.core.stores.models import (
60
65
  ShippingMethod,
61
66
  Store,
62
67
  Tax,
68
+ Wallet,
63
69
  )
64
70
  from ob_dj_store.core.stores.models._inventory import Inventory
65
71
 
@@ -80,9 +86,10 @@ class StoreView(
80
86
  queryset = Store.objects.active()
81
87
  distance_ordering_filter_field = "location"
82
88
  filter_backends = [DistanceToPointOrderingFilter, DjangoFilterBackend]
89
+ lookup_value_regex = "[0-9]+"
83
90
 
84
91
  def get_permissions(self):
85
- if self.action in ["favorites", "favorite", "recently_ordered_from"]:
92
+ if self.action in ["favorites", "favorite", "recently_ordered_from", "count"]:
86
93
  return [
87
94
  permissions.IsAuthenticated(),
88
95
  ]
@@ -90,7 +97,7 @@ class StoreView(
90
97
 
91
98
  def get_queryset(self):
92
99
  queryset = super().get_queryset()
93
- queryset.select_related("opening_hours")
100
+ queryset = queryset.prefetch_related("opening_hours")
94
101
  if self.action == "favorites":
95
102
  favorite_store_ids = Favorite.objects.favorites_for_model(
96
103
  Store, self.request.user
@@ -185,29 +192,6 @@ class StoreView(
185
192
  serializer = self.get_serializer(page, many=True)
186
193
  return self.get_paginated_response(serializer.data)
187
194
 
188
- @swagger_auto_schema(
189
- operation_summary="Add or Remove Store from Favorites",
190
- operation_description="""
191
- Add or Remove Store from Favorites
192
- """,
193
- tags=[
194
- "Store",
195
- ],
196
- )
197
- @action(
198
- detail=True,
199
- methods=["GET"],
200
- url_path="favorite",
201
- )
202
- def favorite(self, request, *args, **kwargs):
203
- instance = self.get_object()
204
- try:
205
- Favorite.objects.favorite_for_user(instance, request.user).delete()
206
- except Favorite.DoesNotExist:
207
- Favorite.add_favorite(instance, request.user)
208
- serializer = StoreSerializer(instance=instance, context={"request": request})
209
- return Response(serializer.data)
210
-
211
195
  @swagger_auto_schema(
212
196
  operation_summary="Retrieve count of store's products",
213
197
  operation_description="""
@@ -228,10 +212,13 @@ class StoreView(
228
212
  product_variants__inventories__store=store
229
213
  ).values_list("id", flat=True)
230
214
  content_type = ContentType.objects.get_for_model(Product)
231
- favorites_count = Favorite.objects.filter(
232
- content_type=content_type,
233
- object_id__in=products_ids,
234
- ).count()
215
+ favorites_count = (
216
+ Favorite.objects.filter(
217
+ content_type=content_type, object_id__in=products_ids, user=request.user
218
+ ).count()
219
+ if request.user.id
220
+ else 0
221
+ )
235
222
  menu = Product.objects.filter(
236
223
  product_variants__inventories__store=store,
237
224
  category__isnull=False,
@@ -244,6 +231,45 @@ class StoreView(
244
231
  }
245
232
  return Response(data, status=status.HTTP_200_OK)
246
233
 
234
+ @swagger_auto_schema(
235
+ operation_summary="Retrieve count of stores",
236
+ operation_description="""
237
+ Retrieve count of stores (Nearby,previous,favorites)
238
+ """,
239
+ tags=[
240
+ "Store",
241
+ ],
242
+ )
243
+ @action(
244
+ detail=False,
245
+ methods=["GET"],
246
+ url_path="count",
247
+ )
248
+ def count(self, request, *args, **kwargs):
249
+ previous_count = (
250
+ super()
251
+ .get_queryset()
252
+ .filter(
253
+ orders__customer=self.request.user,
254
+ orders__status__in=[
255
+ "PAID",
256
+ "DELIVERED",
257
+ ],
258
+ )
259
+ .count()
260
+ )
261
+ nearby_count = super().get_queryset().count()
262
+ favorite_store_ids = Favorite.objects.favorites_for_model(
263
+ Store, self.request.user
264
+ ).values_list("object_id", flat=True)
265
+ favorites_count = self.queryset.filter(pk__in=favorite_store_ids).count()
266
+ data = {
267
+ "previous_count": previous_count,
268
+ "nearby_count": nearby_count,
269
+ "favorites_count": favorites_count,
270
+ }
271
+ return Response(data, status=status.HTTP_200_OK)
272
+
247
273
 
248
274
  class CartView(
249
275
  mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet
@@ -293,6 +319,7 @@ class CartItemView(
293
319
  permissions.IsAuthenticated,
294
320
  ]
295
321
  queryset = CartItem.objects.all()
322
+ lookup_value_regex = "[0-9]+"
296
323
 
297
324
  @swagger_auto_schema(
298
325
  operation_summary="Retrieve Cart Item",
@@ -522,10 +549,12 @@ class ProductView(
522
549
  queryset = self.queryset.filter(pk__in=favorite_product_ids)
523
550
  return queryset
524
551
 
525
- def get_serializer_class(self):
526
- # # TODO: Replace If logic with dict lookup
527
- # Listing Serializer
528
- return ProductListSerializer if self.action == "list" else ProductSerializer
552
+ def get_object(self):
553
+
554
+ instance = self.get_queryset().filter(pk=self.kwargs["pk"]).distinct().first()
555
+ if not instance:
556
+ raise Http404("No Product matches the given query.")
557
+ return instance
529
558
 
530
559
  @swagger_auto_schema(
531
560
  operation_summary="Retrieve A Product",
@@ -624,6 +653,41 @@ class CategoryViewSet(
624
653
  permission_classes = (permissions.AllowAny,)
625
654
  queryset = Category.objects.active()
626
655
  filterset_class = CategoryFilter
656
+ lookup_value_regex = "[0-9]+"
657
+
658
+ def get_queryset(self):
659
+ return super().get_queryset().prefetch_related("products__images")
660
+
661
+ def get_object(self):
662
+ store_id = self.request.query_params.get("store", None)
663
+ instance_pk = self.kwargs["pk"]
664
+ if not store_id:
665
+ return get_object_or_404(Category, pk=instance_pk, is_active=True)
666
+ try:
667
+ product_queryset = Product.objects.filter(
668
+ product_variants__inventories__store=store_id,
669
+ is_active=True,
670
+ )
671
+ instance = self.queryset.prefetch_related(
672
+ Prefetch(
673
+ "subcategories",
674
+ queryset=Category.objects.filter(
675
+ products__product_variants__inventories__store=store_id
676
+ ).distinct(),
677
+ ),
678
+ Prefetch(
679
+ "subcategories__products",
680
+ queryset=product_queryset.distinct(),
681
+ ),
682
+ Prefetch(
683
+ "products",
684
+ queryset=product_queryset.distinct(),
685
+ ),
686
+ "subcategories__products__images",
687
+ ).get(pk=instance_pk)
688
+ except Category.DoesNotExist:
689
+ raise Http404("No Category matches the given query.")
690
+ return instance
627
691
 
628
692
  @method_decorator(
629
693
  name="retrieve",
@@ -698,7 +762,7 @@ class TransactionsViewSet(
698
762
  def get_queryset(self):
699
763
  return Payment.objects.filter(
700
764
  user=self.request.user, status=Payment.PaymentStatus.SUCCESS.value
701
- ).order_by("payment_post_at")
765
+ ).order_by("-payment_post_at")
702
766
 
703
767
  @swagger_auto_schema(
704
768
  operation_summary="List Users's Captured Transactions",
@@ -721,14 +785,9 @@ class PaymentMethodViewSet(
721
785
  permission_classes = [
722
786
  permissions.IsAuthenticated,
723
787
  ]
724
- queryset = PaymentMethod.objects.all()
725
-
726
- def get_queryset(self):
727
- try:
728
- store = Store.objects.get(pk=self.kwargs["store_pk"])
729
- except ObjectDoesNotExist:
730
- raise ValidationError(_(f"Store does not Exist"))
731
- return store.payment_methods.filter(is_active=True)
788
+ queryset = PaymentMethod.objects.filter(is_active=True)
789
+ filterset_class = PaymentMethodFilter
790
+ lookup_value_regex = "[0-9]+"
732
791
 
733
792
  @swagger_auto_schema(
734
793
  operation_summary="List Payment Methods",
@@ -870,3 +929,84 @@ class FavoriteViewSet(
870
929
  )
871
930
  def destroy(self, request, *args, **kwargs):
872
931
  return super().destroy(request, *args, **kwargs)
932
+
933
+
934
+ class WalletViewSet(
935
+ mixins.ListModelMixin,
936
+ mixins.RetrieveModelMixin,
937
+ mixins.UpdateModelMixin,
938
+ viewsets.GenericViewSet,
939
+ ):
940
+ queryset = Wallet.objects.all()
941
+ serializer_class = WalletSerializer
942
+ permission_classes = [
943
+ permissions.IsAuthenticated,
944
+ ]
945
+
946
+ def get_queryset(self):
947
+ return Wallet.objects.filter(user=self.request.user)
948
+
949
+ @swagger_auto_schema(
950
+ operation_summary="Get User Wallet",
951
+ operation_description="""
952
+ Get User Wallet
953
+ """,
954
+ tags=[
955
+ "Wallet",
956
+ ],
957
+ )
958
+ def retrieve(
959
+ self, request: Request, *args: typing.Any, **kwargs: typing.Any
960
+ ) -> Response:
961
+ return super().retrieve(request=request, *args, **kwargs)
962
+
963
+ @swagger_auto_schema(
964
+ operation_summary="Update user wallet",
965
+ operation_description="""
966
+ Update a wallet
967
+ """,
968
+ tags=[
969
+ "Wallet",
970
+ ],
971
+ )
972
+ def partial_update(self, request: Request, *args: typing.Any, **kwargs: typing.Any):
973
+ return super().partial_update(request, *args, **kwargs)
974
+
975
+ @swagger_auto_schema(
976
+ operation_summary="List User Wallets",
977
+ operation_description="""
978
+ List User Wallets
979
+ """,
980
+ tags=[
981
+ "Wallet",
982
+ ],
983
+ )
984
+ def list(
985
+ self, request: Request, *args: typing.Any, **kwargs: typing.Any
986
+ ) -> Response:
987
+ return super().list(request=request, *args, **kwargs)
988
+
989
+ @swagger_auto_schema(
990
+ operation_summary="top up a wallet",
991
+ operation_description="""
992
+ top up a user wallet with tap payment
993
+ """,
994
+ tags=[
995
+ "Wallet",
996
+ ],
997
+ )
998
+ @action(
999
+ methods=["POST"],
1000
+ detail=True,
1001
+ url_path="top-up",
1002
+ url_name="top-up",
1003
+ serializer_class=WalletTopUpSerializer,
1004
+ )
1005
+ def top_up_wallet(
1006
+ self, request: Request, *args: typing.Any, **kwargs: typing.Any
1007
+ ) -> Response:
1008
+ serializer = self.get_serializer(data=request.data)
1009
+ serializer.is_valid(raise_exception=True)
1010
+ instance = self.get_object()
1011
+ payment_url = serializer.top_up_wallet(instance)
1012
+ return Response({"payment_url": payment_url}, status=status.HTTP_200_OK)
@@ -99,7 +99,6 @@ class CategoryAdmin(admin.ModelAdmin):
99
99
  list_display = ["name", "is_active", "parent", "image"]
100
100
  search_fields = [
101
101
  "name",
102
- "parent__name",
103
102
  ]
104
103
  list_filter = [
105
104
  "is_active",
@@ -117,6 +116,9 @@ class ProductVariantAdmin(admin.ModelAdmin):
117
116
  ]
118
117
  search_fields = ["name", "product__name", "sku"]
119
118
 
119
+ def get_queryset(self, request):
120
+ return super().get_queryset(request).prefetch_related("inventories")
121
+
120
122
 
121
123
  class ProductAdmin(admin.ModelAdmin):
122
124
  list_display = ["id", "name", "category", "type", "is_active"]
@@ -221,7 +223,7 @@ class PaymentAdmin(admin.ModelAdmin):
221
223
  "method__payment_provider",
222
224
  "status",
223
225
  ]
224
- search_fields = ["orders", "user__email"]
226
+ search_fields = ["orders__store__name", "user__email"]
225
227
 
226
228
 
227
229
  class InventoryAdmin(admin.ModelAdmin):
@@ -244,6 +246,9 @@ class InventoryAdmin(admin.ModelAdmin):
244
246
  "is_uncountable",
245
247
  ]
246
248
 
249
+ def get_queryset(self, request):
250
+ return super().get_queryset(request).prefetch_related("store")
251
+
247
252
 
248
253
  class TaxAdmin(admin.ModelAdmin):
249
254
  list_display = [
@@ -272,7 +277,7 @@ class WalletTransactionAdmin(admin.ModelAdmin):
272
277
  "type",
273
278
  ]
274
279
  search_fields = [
275
- "wallet__user_email",
280
+ "wallet__user__email",
276
281
  ]
277
282
 
278
283
 
@@ -22,10 +22,10 @@ class InventoryInlineAdmin(admin.TabularInline):
22
22
  extra = 1
23
23
 
24
24
  def get_queryset(self, request):
25
- qs = super().get_queryset(request)
26
- return qs.select_related(
27
- "store",
28
- "variant",
25
+ return (
26
+ super()
27
+ .get_queryset(request)
28
+ .select_related("variant", "store", "variant__product")
29
29
  )
30
30
 
31
31
 
@@ -93,7 +93,7 @@ class TapPayment(models.Model):
93
93
 
94
94
  @property
95
95
  def amount(self):
96
- return self.payment.amount
96
+ return self.payment.total_payment
97
97
 
98
98
  def callback_update(self, tap_payload):
99
99
  if self.status == self.Status.INITIATED:
@@ -32,7 +32,7 @@ def initiate_payment(
32
32
  callback_url = f"{settings.WEBSITE_URI}{callback_path}"
33
33
 
34
34
  payload = {
35
- "amount": "%.3f" % payment.amount,
35
+ "amount": "%.3f" % payment.total_payment,
36
36
  "currency": currency_code,
37
37
  "source": {"id": source},
38
38
  "customer": {
@@ -59,15 +59,15 @@ def initiate_payment(
59
59
  url=f"{settings.TAP_API_URL}{url}", method=method, data=payload, headers=headers
60
60
  )
61
61
  tap_response = response.json()
62
- payment_url = tap_response.get("transaction").get("url")
62
+ payment_transaction = tap_response.get("transaction", None)
63
63
  charge_id = tap_response.get("id")
64
- if not payment_url or not charge_id:
64
+ if not payment_transaction or not charge_id:
65
65
  # TODO: How does this issue occur and is this the best way to handle it?
66
66
  logger.error(
67
67
  f"Failed to create charge request no payment_url or charge_id returned."
68
68
  )
69
69
  raise TapException(payload)
70
-
70
+ payment_url = payment_transaction.get("url")
71
71
  status = tap_response.get("status")
72
72
  source = tap_response.get("source").get("id") if source else ""
73
73
 
@@ -6,7 +6,6 @@ from django.db import models
6
6
  from django.utils.translation import gettext_lazy as _
7
7
 
8
8
  from config import settings
9
- from ob_dj_store.core.stores.utils import get_country_by_currency
10
9
 
11
10
 
12
11
  class ActiveMixin:
@@ -48,11 +47,15 @@ class PaymentMethodManager(ActiveMixin, models.Manager):
48
47
 
49
48
 
50
49
  class PaymentManager(models.Manager):
51
- def create(self, *args: typing.Any, **kwargs: typing.Any):
50
+ def create(self, currency: str, *args: typing.Any, **kwargs: typing.Any):
52
51
  from ob_dj_store.core.stores.gateway.tap.models import TapPayment
53
52
  from ob_dj_store.core.stores.models import Tax, WalletTransaction
54
53
 
55
- orders = kwargs.pop("orders", [])
54
+ orders = kwargs.pop("orders", None)
55
+ if not orders:
56
+ raise ValidationError(
57
+ {"order", _("You cannot perform payment without items")}
58
+ )
56
59
  try:
57
60
  kwargs["payment_tax"] = Tax.objects.get(is_active=True)
58
61
  except ObjectDoesNotExist:
@@ -61,7 +64,7 @@ class PaymentManager(models.Manager):
61
64
  method = kwargs.get("method", None)
62
65
  if method:
63
66
  gateway = method.payment_provider
64
- instance: "models.Payment" = super().create(*args, **kwargs)
67
+ instance: "models.Payment" = super().create(currency=currency, *args, **kwargs)
65
68
  instance.orders.set(orders)
66
69
  if gateway in [settings.TAP_CREDIT_CARD, settings.TAP_KNET, settings.TAP_ALL]:
67
70
  source = gateway
@@ -73,8 +76,7 @@ class PaymentManager(models.Manager):
73
76
  return instance
74
77
  elif gateway == settings.WALLET:
75
78
  try:
76
- country = get_country_by_currency(kwargs["currency"])
77
- wallet = kwargs["user"].wallets.get(country=country)
79
+ wallet = kwargs["user"].wallets.get(currency=currency)
78
80
  except ObjectDoesNotExist:
79
81
  raise ValidationError({"wallet": _("Wallet Not Found")})
80
82
  WalletTransaction.objects.create(
@@ -0,0 +1,33 @@
1
+ # Generated by Django 3.1.14 on 2023-02-13 19:24
2
+
3
+ from django.db import migrations, models
4
+
5
+ import ob_dj_store.utils.helpers
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+
10
+ dependencies = [
11
+ ("stores", "0055_store_image"),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AddField(
16
+ model_name="category",
17
+ name="image_thumbnail",
18
+ field=models.ImageField(
19
+ blank=True,
20
+ null=True,
21
+ upload_to=ob_dj_store.utils.helpers.category_media_upload_to,
22
+ ),
23
+ ),
24
+ migrations.AddField(
25
+ model_name="productmedia",
26
+ name="image_thumbnail",
27
+ field=models.ImageField(
28
+ blank=True,
29
+ null=True,
30
+ upload_to=ob_dj_store.utils.helpers.product_media_upload_to,
31
+ ),
32
+ ),
33
+ ]
@@ -0,0 +1,37 @@
1
+ # Generated by Django 3.1.14 on 2023-02-14 14:24
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("stores", "0056_auto_20230213_2224"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="productattribute",
15
+ name="max",
16
+ field=models.PositiveSmallIntegerField(default=1),
17
+ ),
18
+ migrations.AddField(
19
+ model_name="productattribute",
20
+ name="min",
21
+ field=models.PositiveSmallIntegerField(default=1),
22
+ ),
23
+ migrations.AlterField(
24
+ model_name="productattribute",
25
+ name="type",
26
+ field=models.CharField(
27
+ choices=[
28
+ ("ONE_CHOICE", "one choice"),
29
+ ("MULTIPLE_CHOICES", "multiple choices"),
30
+ ("LIST_CHOICES", "list choices"),
31
+ ("INCREMENT_CHOICE", "increment choice"),
32
+ ],
33
+ default="ONE_CHOICE",
34
+ max_length=32,
35
+ ),
36
+ ),
37
+ ]
@@ -0,0 +1,18 @@
1
+ # Generated by Django 3.2.8 on 2023-02-17 12:43
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("stores", "0057_auto_20230214_1724"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="attributechoice",
15
+ name="is_default",
16
+ field=models.BooleanField(default=False),
17
+ ),
18
+ ]
@@ -0,0 +1,41 @@
1
+ # Generated by Django 3.2.8 on 2023-02-17 17:06
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("stores", "0058_attributechoice_is_default"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name="category",
15
+ name="image_thumbnail",
16
+ ),
17
+ migrations.RemoveField(
18
+ model_name="productmedia",
19
+ name="image_thumbnail",
20
+ ),
21
+ migrations.AddField(
22
+ model_name="category",
23
+ name="image_thumbnail_medium",
24
+ field=models.ImageField(blank=True, null=True, upload_to="category_media/"),
25
+ ),
26
+ migrations.AddField(
27
+ model_name="category",
28
+ name="image_thumbnail_small",
29
+ field=models.ImageField(blank=True, null=True, upload_to="category_media/"),
30
+ ),
31
+ migrations.AddField(
32
+ model_name="productmedia",
33
+ name="image_thumbnail_medium",
34
+ field=models.ImageField(blank=True, null=True, upload_to="product_media/"),
35
+ ),
36
+ migrations.AddField(
37
+ model_name="productmedia",
38
+ name="image_thumbnail_small",
39
+ field=models.ImageField(blank=True, null=True, upload_to="product_media/"),
40
+ ),
41
+ ]
@@ -0,0 +1,24 @@
1
+ # Generated by Django 3.2.8 on 2023-02-22 16:56
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("stores", "0059_auto_20230217_2006"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AlterField(
15
+ model_name="orderitem",
16
+ name="product_variant",
17
+ field=models.ForeignKey(
18
+ null=True,
19
+ on_delete=django.db.models.deletion.PROTECT,
20
+ related_name="order_items",
21
+ to="stores.productvariant",
22
+ ),
23
+ ),
24
+ ]
@@ -0,0 +1,28 @@
1
+ # Generated by Django 3.2.8 on 2023-02-23 11:35
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("stores", "0060_alter_orderitem_product_variant"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AddField(
14
+ model_name="attributechoice",
15
+ name="label",
16
+ field=models.CharField(blank=True, max_length=200, null=True),
17
+ ),
18
+ migrations.AddField(
19
+ model_name="productattribute",
20
+ name="label",
21
+ field=models.CharField(blank=True, max_length=200, null=True),
22
+ ),
23
+ migrations.AlterField(
24
+ model_name="product",
25
+ name="name",
26
+ field=models.CharField(help_text="Name", max_length=200, unique=True),
27
+ ),
28
+ ]