ob-dj-store 0.0.22.2__py3-none-any.whl → 0.0.22.4__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.
@@ -401,8 +401,7 @@ class OrderSerializer(serializers.ModelSerializer):
401
401
 
402
402
  def _validate_pickup_time(self, store, pickup_time):
403
403
  # validate that the pickup_time is always in the future
404
-
405
- if pickup_time < localtime(now()):
404
+ if pickup_time < localtime(now(), self._get_store().timezone):
406
405
  raise serializers.ValidationError(_("Pickup time must be in the future"))
407
406
  pickup_time_time = pickup_time.time()
408
407
  # validate that the pickup_time is between store's opening hours and closing hours
@@ -1059,11 +1058,13 @@ class SubCategorySerializer(ArabicFieldsMixin, serializers.ModelSerializer):
1059
1058
  def get_is_available(self, obj) -> bool:
1060
1059
  store_id = self.context["request"].query_params.get("store", None)
1061
1060
  if store_id:
1061
+ local_tz = Store.objects.get(id=store_id).timezone
1062
+ current_time = localtime(now(), local_tz) if local_tz else localtime(now())
1062
1063
  for availability_hours in obj.parent.availability_hours.all():
1063
1064
  if availability_hours.category == obj.parent:
1064
1065
  return (
1065
1066
  availability_hours.from_hour
1066
- <= localtime(now()).time()
1067
+ <= current_time.time()
1067
1068
  <= availability_hours.to_hour
1068
1069
  )
1069
1070
  return False
@@ -1095,11 +1096,13 @@ class CategorySerializer(ArabicFieldsMixin, serializers.ModelSerializer):
1095
1096
  def get_is_available(self, obj) -> bool:
1096
1097
  store_id = self.context["request"].query_params.get("store", None)
1097
1098
  if store_id:
1099
+ local_tz = Store.objects.get(id=store_id).timezone
1100
+ current_time = localtime(now(), local_tz) if local_tz else localtime(now())
1098
1101
  for availability_hours in obj.availability_hours.all():
1099
1102
  if availability_hours.category == obj:
1100
1103
  return (
1101
1104
  availability_hours.from_hour
1102
- <= localtime(now()).time()
1105
+ <= current_time.time()
1103
1106
  <= availability_hours.to_hour
1104
1107
  )
1105
1108
  return False
@@ -1210,6 +1213,7 @@ class StoreSerializer(ArabicFieldsMixin, FavoriteMixin, serializers.ModelSeriali
1210
1213
  shipping_methods = ShippingMethodSerializer(many=True, read_only=True)
1211
1214
  distance = serializers.SerializerMethodField()
1212
1215
  current_day_opening_hours = serializers.SerializerMethodField()
1216
+ timezone = serializers.SerializerMethodField()
1213
1217
 
1214
1218
  class Meta:
1215
1219
  model = Store
@@ -1238,6 +1242,7 @@ class StoreSerializer(ArabicFieldsMixin, FavoriteMixin, serializers.ModelSeriali
1238
1242
  "image",
1239
1243
  "busy_mode",
1240
1244
  "name_arabic",
1245
+ "timezone",
1241
1246
  )
1242
1247
  extra_kwargs = {
1243
1248
  "image": {"read_only": True, "required": False},
@@ -1246,12 +1251,12 @@ class StoreSerializer(ArabicFieldsMixin, FavoriteMixin, serializers.ModelSeriali
1246
1251
  def get_is_closed(self, obj):
1247
1252
  if obj.busy_mode:
1248
1253
  return True
1249
- current_time = localtime(now())
1254
+ current_time = localtime(now(), obj.timezone)
1250
1255
  current_op_hour = obj.current_opening_hours
1251
1256
  if current_op_hour:
1252
1257
  from_hour = current_op_hour.from_hour
1253
1258
  to_hour = current_op_hour.to_hour
1254
- if current_time.tzinfo.zone != "Asia/Dubai":
1259
+ if current_time.tzinfo != "Asia/Dubai":
1255
1260
  if obj.currency == "AED":
1256
1261
  try:
1257
1262
  current_time += timedelta(hours=1)
@@ -1365,6 +1370,9 @@ class StoreSerializer(ArabicFieldsMixin, FavoriteMixin, serializers.ModelSeriali
1365
1370
  op_hour = f"{settings.DEFAULT_OPENING_HOURS[0]['from_hour']} - {settings.DEFAULT_OPENING_HOURS[0]['to_hour']}"
1366
1371
  return op_hour
1367
1372
 
1373
+ def get_timezone(self, obj):
1374
+ return str(obj.timezone)
1375
+
1368
1376
  def to_representation(self, instance):
1369
1377
  return super().to_representation(instance)
1370
1378
 
@@ -1403,10 +1411,15 @@ class TaxSerializer(serializers.ModelSerializer):
1403
1411
 
1404
1412
 
1405
1413
  class StoreListSerializer(ArabicFieldsMixin, serializers.ModelSerializer):
1414
+ timezone = serializers.SerializerMethodField()
1415
+
1406
1416
  class Meta:
1407
1417
  model = Store
1408
1418
  fields = "__all__"
1409
1419
 
1420
+ def get_timezone(self, obj):
1421
+ return str(obj.timezone)
1422
+
1410
1423
 
1411
1424
  class GenericSerializer(serializers.Serializer):
1412
1425
  """
@@ -1268,7 +1268,7 @@ class PartnerAuthInfoViewSet(
1268
1268
  class PartnerViewSet(
1269
1269
  mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet,
1270
1270
  ):
1271
- queryset = Partner.objects.all()
1271
+ queryset = Partner.objects.all().order_by("name")
1272
1272
  permission_classes = [
1273
1273
  permissions.IsAuthenticated,
1274
1274
  ]
@@ -112,6 +112,7 @@ class StoreAdmin(LeafletGeoAdmin):
112
112
  "pickup_addresses",
113
113
  "image",
114
114
  "currency",
115
+ "timezone",
115
116
  )
116
117
  },
117
118
  ),
@@ -0,0 +1,26 @@
1
+ # Generated by Django 3.2.8 on 2025-09-23 14:14
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("stores", "0109_wallettransaction_cashback_type"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterModelOptions(
14
+ name="productvariant",
15
+ options={
16
+ "ordering": ("order_value",),
17
+ "verbose_name": "Product Variation",
18
+ "verbose_name_plural": "Product Variations",
19
+ },
20
+ ),
21
+ migrations.AddField(
22
+ model_name="productvariant",
23
+ name="order_value",
24
+ field=models.PositiveSmallIntegerField(default=1, verbose_name="ordering"),
25
+ ),
26
+ ]
@@ -0,0 +1,35 @@
1
+ # Generated by Django 3.2.8 on 2025-10-23 14:00
2
+
3
+ import timezone_field.fields
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("stores", "0110_auto_20250923_1714"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="store",
16
+ name="has_daylight_saving_time",
17
+ field=models.BooleanField(default=False),
18
+ ),
19
+ migrations.AddField(
20
+ model_name="store",
21
+ name="timezone",
22
+ field=timezone_field.fields.TimeZoneField(default="Asia/Kuwait"),
23
+ ),
24
+ migrations.AlterField(
25
+ model_name="tax",
26
+ name="value",
27
+ field=models.DecimalField(
28
+ blank=True,
29
+ decimal_places=5,
30
+ help_text="Value for the given Payment -> 0.0625",
31
+ max_digits=7,
32
+ null=True,
33
+ ),
34
+ ),
35
+ ]
@@ -35,8 +35,8 @@ class Tax(models.Model):
35
35
  country = CountryField(help_text=_("The address country."), default="KW")
36
36
  value = models.DecimalField(
37
37
  blank=True,
38
- max_digits=5,
39
- decimal_places=3,
38
+ max_digits=7,
39
+ decimal_places=5,
40
40
  null=True,
41
41
  help_text="Value for the given Payment -> 0.0625",
42
42
  )
@@ -447,6 +447,9 @@ class ProductVariant(DjangoModelCleanMixin, models.Model):
447
447
  image_thumbnail_medium = models.ImageField(
448
448
  upload_to="product_variant_media/", null=True, blank=True
449
449
  )
450
+ order_value = models.PositiveSmallIntegerField(
451
+ verbose_name=_("ordering"), default=1
452
+ )
450
453
  # Audit fields
451
454
  created_at = models.DateTimeField(auto_now_add=True)
452
455
  updated_at = models.DateTimeField(auto_now=True)
@@ -456,6 +459,7 @@ class ProductVariant(DjangoModelCleanMixin, models.Model):
456
459
  class Meta:
457
460
  verbose_name = _("Product Variation")
458
461
  verbose_name_plural = _("Product Variations")
462
+ ordering = ("order_value",)
459
463
 
460
464
  def __str__(self):
461
465
  return f"Variation {self.product.name} {self.name} (PK={self.pk})"
@@ -8,6 +8,7 @@ from django.utils.timezone import now
8
8
  from django.utils.translation import gettext_lazy as _
9
9
  from django_countries.fields import CountryField
10
10
  from phonenumber_field.modelfields import PhoneNumberField
11
+ from timezone_field import TimeZoneField
11
12
 
12
13
  from ob_dj_store.core.stores.managers import (
13
14
  CountryPaymentMethodManager,
@@ -179,6 +180,8 @@ class Store(DjangoModelCleanMixin, models.Model):
179
180
  )
180
181
  busy_mode = models.BooleanField(default=False)
181
182
  is_digital = models.BooleanField(default=False)
183
+ timezone = TimeZoneField(default="Asia/Kuwait")
184
+ has_daylight_saving_time = models.BooleanField(default=False)
182
185
  created_at = models.DateTimeField(auto_now_add=True)
183
186
  updated_at = models.DateTimeField(auto_now=True)
184
187
 
@@ -122,15 +122,22 @@ class Wallet(models.Model):
122
122
  )
123
123
 
124
124
  provider = payment_method.payment_provider
125
- if provider == settings.TAP_ALL:
126
- return (payment.tap_payment.payment_url, payment.tap_payment.charge_id)
127
- elif provider == settings.STRIPE:
128
- return (
129
- payment.stripe_payment.payment_url,
130
- payment.stripe_payment.payment_intent_id,
131
- )
132
- else:
133
- logger.warning(f"Unsupported payment provider: {provider}")
125
+ logger.info(f"Payment ID: {payment.id}")
126
+ logger.info(f"Payment provider: {provider}")
127
+ try:
128
+ if provider == settings.STRIPE:
129
+ logger.info(
130
+ f"Stripe payment info: {payment.stripe_payment.payment_intent_id}"
131
+ )
132
+ return (
133
+ payment.stripe_payment.payment_url,
134
+ payment.stripe_payment.payment_intent_id,
135
+ )
136
+ else:
137
+ logger.info(f"TAP payment info: {payment.tap_payment.charge_id}")
138
+ return (payment.tap_payment.payment_url, payment.tap_payment.charge_id)
139
+ except Exception as e:
140
+ logger.warning(f"Error returning payment details")
134
141
  return (None, None)
135
142
 
136
143
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ob-dj-store
3
- Version: 0.0.22.2
3
+ Version: 0.0.22.4
4
4
  Summary: OBytes django application for managing ecommerce stores.
5
5
  Home-page: https://www.obytes.com/
6
6
  Author: OBytes
@@ -2,8 +2,8 @@ ob_dj_store/apis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
2
2
  ob_dj_store/apis/stores/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  ob_dj_store/apis/stores/filters.py,sha256=JeUtcicJ3cFnZc71aUMEwn2jaVj1oNQsZOCZfdxGDu8,10338
4
4
  ob_dj_store/apis/stores/urls.py,sha256=W9sNJ7ST9pGnfVCf7GmduDO-_zK9MGTI6ybV8n-lzSc,2111
5
- ob_dj_store/apis/stores/views.py,sha256=cwDFNY2rtTfjl_F9PpR5zOJy2DPmGidOjIlJ9fsgSYI,42744
6
- ob_dj_store/apis/stores/rest/serializers/serializers.py,sha256=rJ3oWg0GqbZ8OoPWRTxcuPkIRYPQPfK4TH4UH4vsjns,69192
5
+ ob_dj_store/apis/stores/views.py,sha256=E9msGnkPEFjMTqU9jn-6rnCof3pivoHLesZda6D1BBs,42761
6
+ ob_dj_store/apis/stores/rest/serializers/serializers.py,sha256=Rm-ay0ZwfkcChoy7lXZDbfyn5h2qPUVaEFATkVMeHvc,69783
7
7
  ob_dj_store/apis/stripe/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  ob_dj_store/apis/stripe/serializers.py,sha256=SX3yKVNbDvE5LL35DF9XoC8sjwGkCSdKz_JUVWsJDLg,5949
9
9
  ob_dj_store/apis/stripe/urls.py,sha256=IHH9sX3d74KDUlXkYnIAwYRC6zEvUaOTSFsJGJfiAjA,723
@@ -14,7 +14,7 @@ ob_dj_store/apis/tap/urls.py,sha256=bnOTv6an11kxpo_FdqlhsizlGPLVpNxBjCyKcf3_C9M,
14
14
  ob_dj_store/apis/tap/views.py,sha256=sWqNT3nnEFXwRlVokoZREpCOwMQtyd_ZzrvUtQ6c5L0,2620
15
15
  ob_dj_store/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
16
  ob_dj_store/core/stores/__init__.py,sha256=-izNGrxNn_nn3IQXd5pkuES9lSF-AHYb14yhNPozYCI,65
17
- ob_dj_store/core/stores/admin.py,sha256=aI1t3nIeOw_Vcy2E2LNaAxMRX2GjvVrVeBaH-n4IHco,14362
17
+ ob_dj_store/core/stores/admin.py,sha256=j7c_kDWY4BJrM0xWK1P8WRQSnu7kX6UBXFxfZ5oewj4,14394
18
18
  ob_dj_store/core/stores/admin_inlines.py,sha256=9_35VwwRh5w8YU2CRTJ7sENUjF-D-J-o9jXhLEPpS3w,2900
19
19
  ob_dj_store/core/stores/apps.py,sha256=ZadmEER_dNcQTH617b3fAsYZJSyRw0g46Kjp4eOAsOU,498
20
20
  ob_dj_store/core/stores/managers.py,sha256=Q0PUB_LSz0bufJ0OfgHuvSC-5uAGSsDDlhitisFJqw4,10141
@@ -155,6 +155,8 @@ ob_dj_store/core/stores/migrations/0106_alter_paymentmethod_payment_provider.py,
155
155
  ob_dj_store/core/stores/migrations/0107_auto_20250425_2059.py,sha256=XCj2Inlw8e-_6W9wvYeVVV-bGk6lX8_n2FNI5W80XVw,848
156
156
  ob_dj_store/core/stores/migrations/0108_alter_paymentmethod_payment_provider.py,sha256=abuqKAhuha8tEdxJy-Tj1RNFOMV63_z5v_qK5LLSNVI,1078
157
157
  ob_dj_store/core/stores/migrations/0109_wallettransaction_cashback_type.py,sha256=XA_RtxLjnpsz-i0uKlCyYK-UWL4RR10Q_XeqVd3KX6A,691
158
+ ob_dj_store/core/stores/migrations/0110_auto_20250923_1714.py,sha256=o8a6zt9iyZp7_Km4Wos8OSZ-vn-mVyAIdXvWdCytQaQ,727
159
+ ob_dj_store/core/stores/migrations/0111_auto_20251023_1700.py,sha256=CZcu32ZUREnZcpu0jmEYWKyus8G_TT_ULXm3o_jf8T8,949
158
160
  ob_dj_store/core/stores/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
159
161
  ob_dj_store/core/stores/models/__init__.py,sha256=VeWrDiIbw94ZDSFD-62rz9iTTW87iPdwDW5jcjxm7bs,2045
160
162
  ob_dj_store/core/stores/models/_address.py,sha256=uf9W4dnlXkEFhhsK75ZsDwWq5R2JEngf7VhBiLEnIVs,2193
@@ -164,15 +166,15 @@ ob_dj_store/core/stores/models/_feedback.py,sha256=5kQ_AD9nkY57UOKo_qHg4_nEQAdFj
164
166
  ob_dj_store/core/stores/models/_inventory.py,sha256=_rGlVL5HjOlHQVB8CI0776CPcE5r6szv1w3PjtkYnWM,4306
165
167
  ob_dj_store/core/stores/models/_order.py,sha256=_08lqX5p4brCdUilqfbT--6Ao2TMsqnzXaWoV_7IYL8,9758
166
168
  ob_dj_store/core/stores/models/_partner.py,sha256=OuYvevUWn1sYHs9PcFf51EUUC1uqytQss8Bx91aMOH8,4732
167
- ob_dj_store/core/stores/models/_payment.py,sha256=SfAMVrdP2Hfz604AyO8EOjeiOgGMeJ8My0s5U4GZ7Yc,6650
168
- ob_dj_store/core/stores/models/_product.py,sha256=KUi2hkA4VNCrWwWp3AM_tkubFptpOdiFt0Twzi2wW14,18535
169
- ob_dj_store/core/stores/models/_store.py,sha256=0K-CNJWuXNqeyULL1J0M9hiNcVla0UNNjdCdN_nzNEE,9833
170
- ob_dj_store/core/stores/models/_wallet.py,sha256=QktPaDH2gOGDyOqBqY0lXU91gL2_MhHhy2d7GcCO1ZA,6019
169
+ ob_dj_store/core/stores/models/_payment.py,sha256=1Ssz2RbI_kI-n-bejApbH3EMm96Bg19dgiMUjQHj9R4,6650
170
+ ob_dj_store/core/stores/models/_product.py,sha256=rt30n_X9RRGcyqwB-hj1PMEZRQNvzJhc0M-mLkBoPWI,18675
171
+ ob_dj_store/core/stores/models/_store.py,sha256=vz-rVEwWCmz62pRtj3qAR7D3doBH1p2FkOUjKtI4-lo,9992
172
+ ob_dj_store/core/stores/models/_wallet.py,sha256=SjGizV1P3fhCYyxD-X0ts-PjhzdpGVHd5vIz54Rbj8A,6357
171
173
  ob_dj_store/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
174
  ob_dj_store/utils/helpers.py,sha256=o7wgypM7mI2vZqZKkhxnTcnHJC8GMQDOuYMnRwXr6tY,2058
173
175
  ob_dj_store/utils/model.py,sha256=DV7hOhTaZL3gh9sptts2jTUFlTArKG3i7oPioq9HLFE,303
174
176
  ob_dj_store/utils/utils.py,sha256=8UVAFB56qUSjJJ5f9vnermtw638gdFy4CFRCuMbns_M,1342
175
- ob_dj_store-0.0.22.2.dist-info/METADATA,sha256=QGoLBwonfeehmzz5Mx-VnKThLe0F7W6mHcd-6AV_ARM,2850
176
- ob_dj_store-0.0.22.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
177
- ob_dj_store-0.0.22.2.dist-info/top_level.txt,sha256=CZG3G0ptTkzGnc0dFYN-ZD7YKdJBmm47bsmGwofD_lk,12
178
- ob_dj_store-0.0.22.2.dist-info/RECORD,,
177
+ ob_dj_store-0.0.22.4.dist-info/METADATA,sha256=JEtu0JWXnO20S15Hxt2YF4Rt5NbMz_bGkTWzaigtPh4,2850
178
+ ob_dj_store-0.0.22.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
179
+ ob_dj_store-0.0.22.4.dist-info/top_level.txt,sha256=CZG3G0ptTkzGnc0dFYN-ZD7YKdJBmm47bsmGwofD_lk,12
180
+ ob_dj_store-0.0.22.4.dist-info/RECORD,,