ob-dj-store 0.0.19__py3-none-any.whl → 0.0.23.2__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.
- ob_dj_store/apis/stores/filters.py +42 -19
- ob_dj_store/apis/stores/rest/serializers/serializers.py +256 -63
- ob_dj_store/apis/stores/urls.py +6 -0
- ob_dj_store/apis/stores/views.py +140 -227
- ob_dj_store/apis/stripe/__init__.py +0 -0
- ob_dj_store/apis/stripe/serializers.py +185 -0
- ob_dj_store/apis/stripe/urls.py +25 -0
- ob_dj_store/apis/stripe/views.py +191 -0
- ob_dj_store/apis/tap/views.py +2 -6
- ob_dj_store/core/stores/admin.py +41 -38
- ob_dj_store/core/stores/admin_inlines.py +8 -13
- ob_dj_store/core/stores/gateway/stripe/__init__.py +2 -0
- ob_dj_store/core/stores/gateway/stripe/admin.py +77 -0
- ob_dj_store/core/stores/gateway/stripe/apps.py +9 -0
- ob_dj_store/core/stores/gateway/stripe/managers.py +35 -0
- ob_dj_store/core/stores/gateway/stripe/migrations/0001_initial.py +168 -0
- ob_dj_store/core/stores/gateway/stripe/migrations/__init__.py +1 -0
- ob_dj_store/core/stores/gateway/stripe/models.py +174 -0
- ob_dj_store/core/stores/gateway/stripe/utils.py +170 -0
- ob_dj_store/core/stores/gateway/tap/admin.py +1 -3
- ob_dj_store/core/stores/gateway/tap/managers.py +1 -6
- ob_dj_store/core/stores/gateway/tap/migrations/0001_initial.py +1 -3
- ob_dj_store/core/stores/gateway/tap/migrations/0008_alter_tappayment_user.py +25 -0
- ob_dj_store/core/stores/gateway/tap/models.py +4 -13
- ob_dj_store/core/stores/gateway/tap/utils.py +2 -7
- ob_dj_store/core/stores/managers.py +12 -4
- ob_dj_store/core/stores/migrations/0001_initial.py +1 -4
- ob_dj_store/core/stores/migrations/0005_auto_20220425_2119.py +2 -5
- ob_dj_store/core/stores/migrations/0005_auto_20220427_1729.py +1 -2
- ob_dj_store/core/stores/migrations/0006_auto_20220428_0100.py +2 -8
- ob_dj_store/core/stores/migrations/0007_cart_cartitem_order_orderitem.py +2 -8
- ob_dj_store/core/stores/migrations/0010_auto_20220509_1633.py +1 -4
- ob_dj_store/core/stores/migrations/0012_auto_20220514_0633.py +1 -4
- ob_dj_store/core/stores/migrations/0013_auto_20220518_1539.py +1 -4
- ob_dj_store/core/stores/migrations/0014_auto_20220519_0018.py +3 -12
- ob_dj_store/core/stores/migrations/0017_auto_20220524_0912.py +3 -10
- ob_dj_store/core/stores/migrations/0018_auto_20220524_1613.py +1 -3
- ob_dj_store/core/stores/migrations/0021_auto_20220531_1849.py +1 -4
- ob_dj_store/core/stores/migrations/0026_auto_20220630_1913.py +8 -32
- ob_dj_store/core/stores/migrations/0031_auto_20220811_1733.py +1 -4
- ob_dj_store/core/stores/migrations/0033_auto_20220815_0133.py +2 -8
- ob_dj_store/core/stores/migrations/0039_auto_20220831_1521.py +1 -4
- ob_dj_store/core/stores/migrations/0044_remove_productvariant_has_inventory.py +1 -4
- ob_dj_store/core/stores/migrations/0049_auto_20221029_1524.py +2 -8
- ob_dj_store/core/stores/migrations/0050_favoriteextra.py +1 -3
- ob_dj_store/core/stores/migrations/0052_auto_20221129_1732.py +2 -8
- ob_dj_store/core/stores/migrations/0059_auto_20230217_2006.py +2 -8
- ob_dj_store/core/stores/migrations/0062_auto_20230226_2005.py +2 -6
- ob_dj_store/core/stores/migrations/0064_auto_20230228_1814.py +1 -2
- ob_dj_store/core/stores/migrations/0066_auto_20230304_1532.py +2 -8
- ob_dj_store/core/stores/migrations/0070_auto_20230323_1628.py +1 -4
- ob_dj_store/core/stores/migrations/0071_auto_20230328_1825.py +2 -5
- ob_dj_store/core/stores/migrations/0082_auto_20230613_1424.py +1 -4
- ob_dj_store/core/stores/migrations/0084_payment_result.py +1 -3
- ob_dj_store/core/stores/migrations/0087_auto_20230828_2138.py +1 -4
- ob_dj_store/core/stores/migrations/0097_auto_20231108_1939.py +1 -4
- ob_dj_store/core/stores/migrations/0100_remove_shippingmethod_type_arabic.py +1 -4
- ob_dj_store/core/stores/migrations/0106_alter_paymentmethod_payment_provider.py +35 -0
- ob_dj_store/core/stores/migrations/0107_auto_20250425_2059.py +29 -0
- ob_dj_store/core/stores/migrations/0108_alter_paymentmethod_payment_provider.py +35 -0
- ob_dj_store/core/stores/migrations/0109_wallettransaction_cashback_type.py +27 -0
- ob_dj_store/core/stores/migrations/0110_auto_20250923_1714.py +26 -0
- ob_dj_store/core/stores/migrations/0111_auto_20251023_1700.py +35 -0
- ob_dj_store/core/stores/migrations/0112_auto_20251027_1739.py +98 -0
- ob_dj_store/core/stores/migrations/0113_order_tax_value.py +20 -0
- ob_dj_store/core/stores/migrations/0114_store_mask_customer_info.py +18 -0
- ob_dj_store/core/stores/models/__init__.py +9 -1
- ob_dj_store/core/stores/models/_address.py +1 -3
- ob_dj_store/core/stores/models/_cart.py +11 -5
- ob_dj_store/core/stores/models/_feedback.py +1 -3
- ob_dj_store/core/stores/models/_inventory.py +3 -2
- ob_dj_store/core/stores/models/_order.py +69 -20
- ob_dj_store/core/stores/models/_payment.py +28 -24
- ob_dj_store/core/stores/models/_product.py +31 -17
- ob_dj_store/core/stores/models/_store.py +9 -13
- ob_dj_store/core/stores/models/_wallet.py +34 -26
- ob_dj_store/core/stores/receivers.py +43 -27
- ob_dj_store/core/stores/utils.py +1 -2
- {ob_dj_store-0.0.19.dist-info → ob_dj_store-0.0.23.2.dist-info}/METADATA +3 -2
- {ob_dj_store-0.0.19.dist-info → ob_dj_store-0.0.23.2.dist-info}/RECORD +82 -60
- {ob_dj_store-0.0.19.dist-info → ob_dj_store-0.0.23.2.dist-info}/WHEEL +1 -1
- {ob_dj_store-0.0.19.dist-info → ob_dj_store-0.0.23.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Generated by Django 3.2.8 on 2024-12-22 19:07
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("stores", "0105_store_is_open_after_midnight"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name="paymentmethod",
|
|
15
|
+
name="payment_provider",
|
|
16
|
+
field=models.CharField(
|
|
17
|
+
choices=[
|
|
18
|
+
("cod", "cash on delivery"),
|
|
19
|
+
("src_all", "TAP all payment methods"),
|
|
20
|
+
("src_card", "Tap Credit Card"),
|
|
21
|
+
("src_kw.knet", "Tap knet"),
|
|
22
|
+
("paypal", "Paypal"),
|
|
23
|
+
("stripe", "Stripe"),
|
|
24
|
+
("wallet", "Wallet"),
|
|
25
|
+
("gift", "Gift"),
|
|
26
|
+
("src_apple_pay", "Apple Pay"),
|
|
27
|
+
("google_pay", "Google Pay"),
|
|
28
|
+
("src_sa.mada", "Mada"),
|
|
29
|
+
("src_bh.benefit", "Benefit"),
|
|
30
|
+
],
|
|
31
|
+
default="cod",
|
|
32
|
+
max_length=20,
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Generated by Django 3.2.8 on 2025-04-25 17:59
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("stores", "0106_alter_paymentmethod_payment_provider"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RemoveField(model_name="store", name="is_open_after_midnight",),
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name="openinghours",
|
|
16
|
+
name="is_open_after_midnight",
|
|
17
|
+
field=models.BooleanField(default=False),
|
|
18
|
+
),
|
|
19
|
+
migrations.AddField(
|
|
20
|
+
model_name="order",
|
|
21
|
+
name="offer_id",
|
|
22
|
+
field=models.IntegerField(blank=True, default=None, null=True),
|
|
23
|
+
),
|
|
24
|
+
migrations.AddField(
|
|
25
|
+
model_name="order",
|
|
26
|
+
name="offer_redeemed",
|
|
27
|
+
field=models.BooleanField(default=False),
|
|
28
|
+
),
|
|
29
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Generated by Django 3.2.8 on 2025-04-29 20:27
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("stores", "0107_auto_20250425_2059"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterField(
|
|
14
|
+
model_name="paymentmethod",
|
|
15
|
+
name="payment_provider",
|
|
16
|
+
field=models.CharField(
|
|
17
|
+
choices=[
|
|
18
|
+
("cod", "cash on delivery"),
|
|
19
|
+
("src_all", "TAP all payment methods"),
|
|
20
|
+
("src_card", "Tap Credit Card"),
|
|
21
|
+
("src_kw.knet", "Tap knet"),
|
|
22
|
+
("paypal", "Paypal"),
|
|
23
|
+
("stripe", "Stripe"),
|
|
24
|
+
("wallet", "Wallet"),
|
|
25
|
+
("gift", "Gift"),
|
|
26
|
+
("apple_pay", "Apple Pay"),
|
|
27
|
+
("google_pay", "Google Pay"),
|
|
28
|
+
("src_sa.mada", "Mada"),
|
|
29
|
+
("src_bh.benefit", "Benefit"),
|
|
30
|
+
],
|
|
31
|
+
default="cod",
|
|
32
|
+
max_length=20,
|
|
33
|
+
),
|
|
34
|
+
),
|
|
35
|
+
]
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Generated by Django 3.2.8 on 2025-07-31 16:12
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("stores", "0108_alter_paymentmethod_payment_provider"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name="wallettransaction",
|
|
15
|
+
name="cashback_type",
|
|
16
|
+
field=models.CharField(
|
|
17
|
+
blank=True,
|
|
18
|
+
choices=[
|
|
19
|
+
("BY_ORDER", "By Order"),
|
|
20
|
+
("BY_OFFER", "By Offer"),
|
|
21
|
+
("BY_STREAK", "By Streak"),
|
|
22
|
+
],
|
|
23
|
+
max_length=100,
|
|
24
|
+
null=True,
|
|
25
|
+
),
|
|
26
|
+
),
|
|
27
|
+
]
|
|
@@ -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
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Generated by Django 3.2.8 on 2025-10-27 14:39
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
import django_countries.fields
|
|
5
|
+
from django.db import migrations, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
("stores", "0111_auto_20251023_1700"),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.CreateModel(
|
|
16
|
+
name="Tip",
|
|
17
|
+
fields=[
|
|
18
|
+
(
|
|
19
|
+
"id",
|
|
20
|
+
models.AutoField(
|
|
21
|
+
auto_created=True,
|
|
22
|
+
primary_key=True,
|
|
23
|
+
serialize=False,
|
|
24
|
+
verbose_name="ID",
|
|
25
|
+
),
|
|
26
|
+
),
|
|
27
|
+
("name", models.CharField(max_length=150)),
|
|
28
|
+
("description", models.TextField(blank=True, null=True)),
|
|
29
|
+
("is_applied", models.BooleanField(default=True)),
|
|
30
|
+
(
|
|
31
|
+
"country",
|
|
32
|
+
django_countries.fields.CountryField(
|
|
33
|
+
blank=True, default="KW", max_length=2, null=True
|
|
34
|
+
),
|
|
35
|
+
),
|
|
36
|
+
("is_active", models.BooleanField(default=False)),
|
|
37
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
38
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
39
|
+
],
|
|
40
|
+
options={"verbose_name": "Tip", "verbose_name_plural": "Tips",},
|
|
41
|
+
),
|
|
42
|
+
migrations.AddField(
|
|
43
|
+
model_name="order",
|
|
44
|
+
name="tip_percentage",
|
|
45
|
+
field=models.DecimalField(
|
|
46
|
+
blank=True,
|
|
47
|
+
decimal_places=2,
|
|
48
|
+
help_text="Percentage value of the tip (e.g., 10 for 10%)",
|
|
49
|
+
max_digits=5,
|
|
50
|
+
null=True,
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
migrations.AddField(
|
|
54
|
+
model_name="order",
|
|
55
|
+
name="tip_value",
|
|
56
|
+
field=models.DecimalField(
|
|
57
|
+
blank=True, decimal_places=2, max_digits=5, null=True
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
migrations.CreateModel(
|
|
61
|
+
name="TipAmount",
|
|
62
|
+
fields=[
|
|
63
|
+
(
|
|
64
|
+
"id",
|
|
65
|
+
models.AutoField(
|
|
66
|
+
auto_created=True,
|
|
67
|
+
primary_key=True,
|
|
68
|
+
serialize=False,
|
|
69
|
+
verbose_name="ID",
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
(
|
|
73
|
+
"percentage",
|
|
74
|
+
models.DecimalField(
|
|
75
|
+
blank=True,
|
|
76
|
+
decimal_places=2,
|
|
77
|
+
help_text="Percentage value of the tip (e.g., 10 for 10%)",
|
|
78
|
+
max_digits=5,
|
|
79
|
+
null=True,
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
(
|
|
83
|
+
"tip",
|
|
84
|
+
models.ForeignKey(
|
|
85
|
+
blank=True,
|
|
86
|
+
null=True,
|
|
87
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
88
|
+
related_name="tip_amounts",
|
|
89
|
+
to="stores.tip",
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
],
|
|
93
|
+
options={
|
|
94
|
+
"verbose_name": "Tip Amount",
|
|
95
|
+
"verbose_name_plural": "Tip Amounts",
|
|
96
|
+
},
|
|
97
|
+
),
|
|
98
|
+
]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Generated by Django 3.2.8 on 2025-11-25 14:24
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("stores", "0112_auto_20251027_1739"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name="order",
|
|
15
|
+
name="tax_value",
|
|
16
|
+
field=models.DecimalField(
|
|
17
|
+
blank=True, decimal_places=2, max_digits=5, null=True
|
|
18
|
+
),
|
|
19
|
+
),
|
|
20
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 3.2.8 on 2025-12-09 20:08
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("stores", "0113_order_tax_value"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AddField(
|
|
14
|
+
model_name="store",
|
|
15
|
+
name="mask_customer_info",
|
|
16
|
+
field=models.BooleanField(default=False),
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -7,7 +7,13 @@ from ob_dj_store.core.stores.models._feedback import (
|
|
|
7
7
|
FeedbackConfig,
|
|
8
8
|
)
|
|
9
9
|
from ob_dj_store.core.stores.models._inventory import Inventory, InventoryOperations
|
|
10
|
-
from ob_dj_store.core.stores.models._order import
|
|
10
|
+
from ob_dj_store.core.stores.models._order import (
|
|
11
|
+
Order,
|
|
12
|
+
OrderHistory,
|
|
13
|
+
OrderItem,
|
|
14
|
+
Tip,
|
|
15
|
+
TipAmount,
|
|
16
|
+
)
|
|
11
17
|
from ob_dj_store.core.stores.models._partner import (
|
|
12
18
|
Discount,
|
|
13
19
|
Partner,
|
|
@@ -85,4 +91,6 @@ __all__ = [
|
|
|
85
91
|
"PartnerOTPAuth",
|
|
86
92
|
"Discount",
|
|
87
93
|
"CountryPaymentMethod",
|
|
94
|
+
"Tip",
|
|
95
|
+
"TipAmount",
|
|
88
96
|
]
|
|
@@ -10,9 +10,7 @@ class BaseAddress(models.Model):
|
|
|
10
10
|
Base class for all address models.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
address_line = models.CharField(
|
|
14
|
-
max_length=250,
|
|
15
|
-
)
|
|
13
|
+
address_line = models.CharField(max_length=250,)
|
|
16
14
|
address_line_arabic = models.CharField(max_length=250, blank=True, null=True)
|
|
17
15
|
postal_code = models.CharField(
|
|
18
16
|
max_length=64, help_text=_("The address postal/zip code.")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from decimal import Decimal
|
|
2
3
|
|
|
3
4
|
from django.contrib.auth import get_user_model
|
|
@@ -10,6 +11,8 @@ from ob_dj_store.core.stores.managers import CartItemManager, CartManager
|
|
|
10
11
|
from ob_dj_store.core.stores.models._partner import PartnerAuthInfo
|
|
11
12
|
from ob_dj_store.core.stores.utils import round_up_tie
|
|
12
13
|
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
13
16
|
|
|
14
17
|
class Cart(models.Model):
|
|
15
18
|
customer = models.OneToOneField(
|
|
@@ -48,7 +51,11 @@ class Cart(models.Model):
|
|
|
48
51
|
def total_price(self) -> Decimal:
|
|
49
52
|
total_price = Decimal(0)
|
|
50
53
|
for item in self.items.all():
|
|
51
|
-
|
|
54
|
+
try:
|
|
55
|
+
if item.inventory.quantity and item.inventory.quantity > 0:
|
|
56
|
+
total_price += item.total_price
|
|
57
|
+
except AttributeError:
|
|
58
|
+
logger.error(f"Item {item} has no inventory")
|
|
52
59
|
return total_price
|
|
53
60
|
|
|
54
61
|
@property
|
|
@@ -56,9 +63,7 @@ class Cart(models.Model):
|
|
|
56
63
|
return self.total_price + self.get_applied_tax_amount()
|
|
57
64
|
|
|
58
65
|
@property
|
|
59
|
-
def full_price(
|
|
60
|
-
self,
|
|
61
|
-
) -> Decimal:
|
|
66
|
+
def full_price(self,) -> Decimal:
|
|
62
67
|
return self.total_price_with_tax - self.discount_offer_amount
|
|
63
68
|
|
|
64
69
|
@property
|
|
@@ -90,7 +95,8 @@ class Cart(models.Model):
|
|
|
90
95
|
def fill(self, order):
|
|
91
96
|
from ob_dj_store.core.stores.models._cart import CartItem
|
|
92
97
|
|
|
93
|
-
|
|
98
|
+
order_items = order.items.all()
|
|
99
|
+
for item in order_items:
|
|
94
100
|
cart_item = CartItem.objects.create(
|
|
95
101
|
cart=self,
|
|
96
102
|
product_variant=item.product_variant,
|
|
@@ -22,9 +22,7 @@ class Feedback(models.Model):
|
|
|
22
22
|
get_user_model(), related_name="feedbacks", on_delete=models.CASCADE
|
|
23
23
|
)
|
|
24
24
|
order = models.ForeignKey(
|
|
25
|
-
"stores.Order",
|
|
26
|
-
related_name="feedbacks",
|
|
27
|
-
on_delete=models.CASCADE,
|
|
25
|
+
"stores.Order", related_name="feedbacks", on_delete=models.CASCADE,
|
|
28
26
|
)
|
|
29
27
|
review = models.CharField(
|
|
30
28
|
max_length=100, choices=Reviews.choices, default="NOT_AVAILABLE"
|
|
@@ -37,8 +37,7 @@ class Inventory(DjangoModelCleanMixin, models.Model):
|
|
|
37
37
|
# Add is_primary for variant
|
|
38
38
|
is_primary = models.BooleanField(default=True)
|
|
39
39
|
preparation_time = models.DurationField(
|
|
40
|
-
default=timedelta(minutes=0),
|
|
41
|
-
help_text=_("Preparation time in minutes"),
|
|
40
|
+
default=timedelta(minutes=0), help_text=_("Preparation time in minutes"),
|
|
42
41
|
)
|
|
43
42
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
44
43
|
updated_at = models.DateTimeField(auto_now=True)
|
|
@@ -61,6 +60,8 @@ class Inventory(DjangoModelCleanMixin, models.Model):
|
|
|
61
60
|
|
|
62
61
|
@property
|
|
63
62
|
def is_snoozed(self):
|
|
63
|
+
if not self.is_active:
|
|
64
|
+
return True
|
|
64
65
|
return self.snooze_start_date <= now() <= self.snooze_end_date
|
|
65
66
|
|
|
66
67
|
@property
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
from decimal import Decimal
|
|
1
|
+
from decimal import ROUND_HALF_UP, Decimal
|
|
2
2
|
|
|
3
3
|
from django.contrib.auth import get_user_model
|
|
4
4
|
from django.core.exceptions import ObjectDoesNotExist, ValidationError
|
|
5
5
|
from django.core.validators import MinValueValidator
|
|
6
6
|
from django.db import models
|
|
7
7
|
from django.utils.translation import gettext_lazy as _
|
|
8
|
+
from django_countries.fields import CountryField
|
|
8
9
|
|
|
9
|
-
from ob_dj_store.core.stores.managers import OrderItemManager, OrderManager
|
|
10
|
+
from ob_dj_store.core.stores.managers import OrderItemManager, OrderManager, TipManager
|
|
10
11
|
from ob_dj_store.utils.model import DjangoModelCleanMixin
|
|
11
12
|
|
|
12
13
|
|
|
@@ -41,10 +42,7 @@ class Order(DjangoModelCleanMixin, models.Model):
|
|
|
41
42
|
"stores.Discount", on_delete=models.PROTECT, null=True, blank=True
|
|
42
43
|
)
|
|
43
44
|
customer = models.ForeignKey(
|
|
44
|
-
get_user_model(),
|
|
45
|
-
related_name="orders",
|
|
46
|
-
on_delete=models.SET_NULL,
|
|
47
|
-
null=True,
|
|
45
|
+
get_user_model(), related_name="orders", on_delete=models.SET_NULL, null=True,
|
|
48
46
|
)
|
|
49
47
|
store = models.ForeignKey(
|
|
50
48
|
"stores.Store",
|
|
@@ -82,9 +80,7 @@ class Order(DjangoModelCleanMixin, models.Model):
|
|
|
82
80
|
blank=True,
|
|
83
81
|
)
|
|
84
82
|
status = models.CharField(
|
|
85
|
-
max_length=32,
|
|
86
|
-
default=OrderStatus.PENDING,
|
|
87
|
-
choices=OrderStatus.choices,
|
|
83
|
+
max_length=32, default=OrderStatus.PENDING, choices=OrderStatus.choices,
|
|
88
84
|
)
|
|
89
85
|
# Add pickup time for an order, Pick up can be now or a later hour during the day
|
|
90
86
|
pickup_time = models.DateTimeField(
|
|
@@ -93,12 +89,23 @@ class Order(DjangoModelCleanMixin, models.Model):
|
|
|
93
89
|
# Order id of the pickup_car
|
|
94
90
|
car_id = models.PositiveIntegerField(null=True, blank=True)
|
|
95
91
|
# Pick up can be now or a later hour during the day. If pickup_time is not set,
|
|
96
|
-
extra_infos = models.JSONField(
|
|
92
|
+
extra_infos = models.JSONField(null=True, blank=True,)
|
|
93
|
+
init_data = models.JSONField(null=True, blank=True)
|
|
94
|
+
offer_redeemed = models.BooleanField(default=False)
|
|
95
|
+
offer_id = models.IntegerField(null=True, blank=True, default=None)
|
|
96
|
+
tip_percentage = models.DecimalField(
|
|
97
|
+
max_digits=5,
|
|
98
|
+
decimal_places=2,
|
|
97
99
|
null=True,
|
|
98
100
|
blank=True,
|
|
101
|
+
help_text=_("Percentage value of the tip (e.g., 10 for 10%)"),
|
|
102
|
+
)
|
|
103
|
+
tip_value = models.DecimalField(
|
|
104
|
+
max_digits=5, decimal_places=2, null=True, blank=True,
|
|
105
|
+
)
|
|
106
|
+
tax_value = models.DecimalField(
|
|
107
|
+
max_digits=5, decimal_places=2, null=True, blank=True,
|
|
99
108
|
)
|
|
100
|
-
init_data = models.JSONField(null=True, blank=True)
|
|
101
|
-
|
|
102
109
|
# TODO: add pick_up_time maybe ?
|
|
103
110
|
# audit fields
|
|
104
111
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
@@ -163,6 +170,17 @@ class Order(DjangoModelCleanMixin, models.Model):
|
|
|
163
170
|
return self.OrderType.WALLET.value
|
|
164
171
|
return self.OrderType.PHYSICAL.value
|
|
165
172
|
|
|
173
|
+
def calculate_tip(self):
|
|
174
|
+
"""
|
|
175
|
+
Returns the tip amount based on a total order amount.
|
|
176
|
+
"""
|
|
177
|
+
if not self.tip_percentage:
|
|
178
|
+
return Decimal("0.00")
|
|
179
|
+
tip_value = (self.total_amount * self.tip_percentage / Decimal("100")).quantize(
|
|
180
|
+
Decimal("0.01"), rounding=ROUND_HALF_UP
|
|
181
|
+
)
|
|
182
|
+
return tip_value
|
|
183
|
+
|
|
166
184
|
def save(self, **kwargs):
|
|
167
185
|
if not self.pk and self.shipping_address:
|
|
168
186
|
self.immutable_shipping_address = self.shipping_address.to_immutable()
|
|
@@ -195,10 +213,7 @@ class OrderItem(DjangoModelCleanMixin, models.Model):
|
|
|
195
213
|
total_price = models.DecimalField(max_digits=10, decimal_places=3, default=0)
|
|
196
214
|
quantity = models.PositiveIntegerField(
|
|
197
215
|
validators=[
|
|
198
|
-
MinValueValidator(
|
|
199
|
-
1,
|
|
200
|
-
message="Can you please provide a valid quantity !",
|
|
201
|
-
)
|
|
216
|
+
MinValueValidator(1, message="Can you please provide a valid quantity !",)
|
|
202
217
|
],
|
|
203
218
|
help_text=_("quantity of the variant"),
|
|
204
219
|
)
|
|
@@ -277,10 +292,7 @@ class OrderHistory(DjangoModelCleanMixin, models.Model):
|
|
|
277
292
|
"""
|
|
278
293
|
|
|
279
294
|
order = models.ForeignKey(Order, related_name="history", on_delete=models.CASCADE)
|
|
280
|
-
status = models.CharField(
|
|
281
|
-
max_length=32,
|
|
282
|
-
choices=Order.OrderStatus.choices,
|
|
283
|
-
)
|
|
295
|
+
status = models.CharField(max_length=32, choices=Order.OrderStatus.choices,)
|
|
284
296
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
285
297
|
|
|
286
298
|
class Meta:
|
|
@@ -290,3 +302,40 @@ class OrderHistory(DjangoModelCleanMixin, models.Model):
|
|
|
290
302
|
|
|
291
303
|
def __str__(self):
|
|
292
304
|
return f"OrderHistory - {self.status}"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class TipAmount(models.Model):
|
|
308
|
+
percentage = models.DecimalField(
|
|
309
|
+
max_digits=5,
|
|
310
|
+
decimal_places=2,
|
|
311
|
+
null=True,
|
|
312
|
+
blank=True,
|
|
313
|
+
help_text=_("Percentage value of the tip (e.g., 10 for 10%)"),
|
|
314
|
+
)
|
|
315
|
+
tip = models.ForeignKey(
|
|
316
|
+
"Tip",
|
|
317
|
+
related_name="tip_amounts",
|
|
318
|
+
on_delete=models.CASCADE,
|
|
319
|
+
null=True,
|
|
320
|
+
blank=True,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
class Meta:
|
|
324
|
+
verbose_name = _("Tip Amount")
|
|
325
|
+
verbose_name_plural = _("Tip Amounts")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
class Tip(models.Model):
|
|
329
|
+
name = models.CharField(max_length=150)
|
|
330
|
+
description = models.TextField(null=True, blank=True)
|
|
331
|
+
is_applied = models.BooleanField(default=True)
|
|
332
|
+
country = CountryField(blank=True, null=True, default="KW")
|
|
333
|
+
is_active = models.BooleanField(default=False)
|
|
334
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
335
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
336
|
+
|
|
337
|
+
objects = TipManager()
|
|
338
|
+
|
|
339
|
+
class Meta:
|
|
340
|
+
verbose_name = _("Tip")
|
|
341
|
+
verbose_name_plural = _("Tips")
|
|
@@ -10,6 +10,7 @@ from django_countries.fields import CountryField
|
|
|
10
10
|
from config import settings as store_settings
|
|
11
11
|
from ob_dj_store.core.stores.managers import PaymentManager
|
|
12
12
|
from ob_dj_store.core.stores.models import Order
|
|
13
|
+
from ob_dj_store.core.stores.utils import round_up_tie
|
|
13
14
|
|
|
14
15
|
logger = logging.getLogger(__name__)
|
|
15
16
|
|
|
@@ -35,8 +36,8 @@ class Tax(models.Model):
|
|
|
35
36
|
country = CountryField(help_text=_("The address country."), default="KW")
|
|
36
37
|
value = models.DecimalField(
|
|
37
38
|
blank=True,
|
|
38
|
-
max_digits=
|
|
39
|
-
decimal_places=
|
|
39
|
+
max_digits=7,
|
|
40
|
+
decimal_places=5,
|
|
40
41
|
null=True,
|
|
41
42
|
help_text="Value for the given Payment -> 0.0625",
|
|
42
43
|
)
|
|
@@ -75,21 +76,12 @@ class Payment(models.Model):
|
|
|
75
76
|
related_name="user_payments",
|
|
76
77
|
)
|
|
77
78
|
status = models.CharField(
|
|
78
|
-
max_length=100,
|
|
79
|
-
default=PaymentStatus.INIT,
|
|
80
|
-
choices=PaymentStatus.choices,
|
|
79
|
+
max_length=100, default=PaymentStatus.INIT, choices=PaymentStatus.choices,
|
|
81
80
|
)
|
|
82
81
|
method = models.ForeignKey(
|
|
83
|
-
"stores.PaymentMethod",
|
|
84
|
-
on_delete=models.CASCADE,
|
|
85
|
-
null=True,
|
|
86
|
-
blank=True,
|
|
87
|
-
)
|
|
88
|
-
payment_tax = models.ForeignKey(
|
|
89
|
-
Tax,
|
|
90
|
-
on_delete=models.SET_NULL,
|
|
91
|
-
null=True,
|
|
82
|
+
"stores.PaymentMethod", on_delete=models.CASCADE, null=True, blank=True,
|
|
92
83
|
)
|
|
84
|
+
payment_tax = models.ForeignKey(Tax, on_delete=models.SET_NULL, null=True,)
|
|
93
85
|
orders = models.ManyToManyField("stores.Order", related_name="payments")
|
|
94
86
|
amount = models.DecimalField(
|
|
95
87
|
max_digits=settings.DEFAULT_MAX_DIGITS,
|
|
@@ -155,18 +147,26 @@ class Payment(models.Model):
|
|
|
155
147
|
@property
|
|
156
148
|
def total_payment(self):
|
|
157
149
|
orders = self.orders.all()
|
|
158
|
-
sum_orders =
|
|
159
|
-
|
|
150
|
+
sum_orders = sum(
|
|
151
|
+
(Decimal(order.total_amount) if order.total_amount else Decimal("0"))
|
|
152
|
+
for order in orders
|
|
160
153
|
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
154
|
+
sum_orders = Decimal(sum_orders)
|
|
155
|
+
|
|
156
|
+
# Apply tax if applicable
|
|
157
|
+
tax = self.payment_tax
|
|
158
|
+
if tax and tax.is_applied:
|
|
159
|
+
if tax.rate == Tax.Rates.PERCENTAGE:
|
|
160
|
+
sum_orders += round_up_tie(sum_orders * Decimal(tax.value) / 100, 3)
|
|
161
|
+
else:
|
|
162
|
+
sum_orders += round_up_tie(self.payment_tax.value, 3)
|
|
163
|
+
|
|
164
|
+
# Add tip if present
|
|
165
|
+
first_order = orders.first()
|
|
166
|
+
if first_order and first_order.tip_value:
|
|
167
|
+
sum_orders += Decimal(first_order.tip_value)
|
|
168
168
|
|
|
169
|
-
return
|
|
169
|
+
return round_up_tie(sum_orders, 3)
|
|
170
170
|
|
|
171
171
|
@property
|
|
172
172
|
def type_of_order(self):
|
|
@@ -189,4 +189,8 @@ class Payment(models.Model):
|
|
|
189
189
|
settings.BENEFIT,
|
|
190
190
|
]:
|
|
191
191
|
payment_url = self.tap_payment.payment_url
|
|
192
|
+
elif gateway == settings.STRIPE:
|
|
193
|
+
# For Stripe, return the client_secret which is used by Stripe.js
|
|
194
|
+
if hasattr(self, "stripe_payment"):
|
|
195
|
+
payment_url = self.stripe_payment.payment_url
|
|
192
196
|
return payment_url
|