diff --git a/backend/core/urls.py b/backend/core/urls.py index b5e2f05..56a7db0 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -25,6 +25,7 @@ urlpatterns = [ path('chat/', include('chat.urls')), path('tickets/', include('ticket.urls')), path('blogs/', include('blog.urls')), + path('order/', include('order.urls')), path('', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), ] diff --git a/backend/order/execptions.py b/backend/order/execptions.py new file mode 100644 index 0000000..c92b966 --- /dev/null +++ b/backend/order/execptions.py @@ -0,0 +1,2 @@ +class DiscountNotAvailableError(Exception): + pass \ No newline at end of file diff --git a/backend/order/migrations/0004_alter_orderitemmodel_product.py b/backend/order/migrations/0004_alter_orderitemmodel_product.py new file mode 100644 index 0000000..7008b7e --- /dev/null +++ b/backend/order/migrations/0004_alter_orderitemmodel_product.py @@ -0,0 +1,20 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:07 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('order', '0003_alter_orderitemmodel_product'), + ('product', '0023_alter_productimagemodel_options_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='orderitemmodel', + name='product', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='product.productvariant', verbose_name='محصول'), + ), + ] diff --git a/backend/order/models.py b/backend/order/models.py index f7efb32..94b5396 100644 --- a/backend/order/models.py +++ b/backend/order/models.py @@ -1,8 +1,8 @@ from django.db import models from account.models import User, UserAddressModel -from product.models import ProductModel +from product.models import ProductModel, ProductVariant from django.utils import timezone - +from .execptions import DiscountNotAvailableError class DiscountCode(models.Model): name = models.CharField(max_length=50, verbose_name='کد تخفیف') percent = models.DecimalField(max_digits=4, decimal_places=2, verbose_name='درصد') @@ -43,13 +43,15 @@ class OrderModel(models.Model): verbose_name = 'سفارش' verbose_name_plural = 'سفارشات' - def total_without_tax(self): - return sum(item.total() for item in self.items.all()) + # def total_without_tax(self): + # return sum(item.total() for item in self.items.all()) def total_with_discount(self): total_with_item_discount = sum(item.total_with_discount() for item in self.items.all()) - if self.discount_code and self.discount_code.is_valid(): + if self.discount_code: + if not self.discount_code.is_valid(): + raise DiscountNotAvailableError('این کد تخفیف دیگر معتبر نیست') discount_percent = self.discount_code.percent return total_with_item_discount * ((100 - discount_percent) / 100) return total_with_item_discount @@ -59,7 +61,7 @@ class OrderModel(models.Model): return self.total_without_tax() * 0.2 def total(self): - return self.total_without_tax + self.tax() + return self.total_with_discount() + self.tax() def remove_order_item(self, item_pk, quantity): pass @@ -75,7 +77,7 @@ class OrderModel(models.Model): class OrderItemModel(models.Model): order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='items', verbose_name='سفارش') quantity = models.SmallIntegerField(verbose_name="تعداد") - product = models.ForeignKey(ProductModel, on_delete=models.PROTECT, verbose_name="محصول") + product = models.ForeignKey(ProductVariant, on_delete=models.PROTECT, verbose_name="محصول") class Meta: verbose_name = 'محصول خریداری شده' verbose_name_plural = 'محصولات خریداری شده' @@ -85,5 +87,6 @@ class OrderItemModel(models.Model): def total_with_discount(self): return self.quantity * self.product.get_toman_price_after_discount() - + def __str__(self): + return f'ایتم سبد خرید محصول: {self.product} کاربر {self.order.user}' diff --git a/backend/order/serializers.py b/backend/order/serializers.py new file mode 100644 index 0000000..2a7d8e8 --- /dev/null +++ b/backend/order/serializers.py @@ -0,0 +1,9 @@ +from rest_framework import serializers +from .models import OrderItemModel + + +class OrderItemSerailzier(serializers.ModelSerializer): + class Meta: + model = OrderItemModel + fields = "__all__" + read_only_fields = ('order', 'product') \ No newline at end of file diff --git a/backend/order/urls.py b/backend/order/urls.py new file mode 100644 index 0000000..c506e46 --- /dev/null +++ b/backend/order/urls.py @@ -0,0 +1,8 @@ +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import path, include +from .views import CartItemViews + +urlpatterns = [ + path('cart/item/', CartItemViews.as_view(), name='add cart'), +] diff --git a/backend/order/views.py b/backend/order/views.py index 91ea44a..1f3451e 100644 --- a/backend/order/views.py +++ b/backend/order/views.py @@ -1,3 +1,68 @@ from django.shortcuts import render +from .execptions import DiscountNotAvailableError +from rest_framework.views import APIView, Response +from django.shortcuts import get_object_or_404 +from product.models import ProductVariant +from rest_framework.permissions import IsAuthenticated +from .serializers import OrderItemSerailzier +# from cart.models import +from rest_framework import status +from .models import OrderItemModel, OrderModel +try: + pass +except DiscountNotAvailableError: + pass -# Create your views here. +""" + +add post +remove delete +show get + +pay +""" + + + +class CartItemViews(APIView): + permission_classes = [IsAuthenticated] + serializer_class = OrderItemSerailzier + def post(self, request, pk): + product_variant = get_object_or_404(ProductVariant, pk=pk) + + cart_order, created = OrderModel.objects.get_or_create( + user=request.user, + status='CART' + ) + order_item, created = OrderItemModel.objects.get_or_create( + order=cart_order, + product=product_variant, + defaults={'quantity': request.data.get('quantity', 1)} + ) + + if not created: + order_item.quantity = request.data.get('quantity', 1) + order_item.save() + + return Response({'detail': 'it did something'}, status=status.HTTP_201_CREATED) + def delete(self, request, pk): + product_variant = get_object_or_404(ProductVariant, pk=pk) + + cart_order, created = OrderModel.objects.get_or_create( + user=request.user, + status='CART' + ) + # order_item, created = OrderItemModel.objects.get_or_create( + # order=cart_order, + # product=product_variant, + # defaults={'quantity': request.data.get('quantity', 1)} + # ) + order_item = get_object_or_404(OrderItemModel, order=cart_order, product=product_variant) + order_item.delete() + return Response({'detail': 'it did something related to delete'}, status=status.HTTP_204_NO_CONTENT) + + + +class CartViews(APIView): + def get(self, request): + pass diff --git a/backend/product/admin.py b/backend/product/admin.py index 43b905d..5ea98c7 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -15,7 +15,7 @@ from unfold.decorators import action, display class ProductVariantAdmin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm export_form_class = ExportForm - autocomplete_fields = ['attributes'] + autocomplete_fields = ['product_attributes', 'images', 'in_pack_items'] warn_unsaved_form = True @@ -105,7 +105,7 @@ class ProductVariantInLine(StackedInline): show_change_link = True tab = True min_num = 1 - autocomplete_fields = ['attributes', 'in_pack_items', 'images'] + autocomplete_fields = ['product_attributes', 'in_pack_items', 'images'] # search_fields = [''] diff --git a/backend/product/migrations/0024_alter_attributevalue_unique_together.py b/backend/product/migrations/0024_alter_attributevalue_unique_together.py new file mode 100644 index 0000000..28379cb --- /dev/null +++ b/backend/product/migrations/0024_alter_attributevalue_unique_together.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:35 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0023_alter_productimagemodel_options_and_more'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='attributevalue', + unique_together=set(), + ), + ] diff --git a/backend/product/migrations/0025_alter_productvariant_attributes_and_more.py b/backend/product/migrations/0025_alter_productvariant_attributes_and_more.py new file mode 100644 index 0000000..ef29eb5 --- /dev/null +++ b/backend/product/migrations/0025_alter_productvariant_attributes_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:39 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0024_alter_attributevalue_unique_together'), + ] + + operations = [ + migrations.AlterField( + model_name='productvariant', + name='attributes', + field=models.ManyToManyField(blank=True, related_name='variant', to='product.attributevalue', verbose_name='ویژگی\u200cها'), + ), + migrations.AlterUniqueTogether( + name='attributevalue', + unique_together={('attribute_type', 'value')}, + ), + ] diff --git a/backend/product/migrations/0026_alter_productvariant_attributes.py b/backend/product/migrations/0026_alter_productvariant_attributes.py new file mode 100644 index 0000000..d1bf5e7 --- /dev/null +++ b/backend/product/migrations/0026_alter_productvariant_attributes.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0025_alter_productvariant_attributes_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='productvariant', + name='attributes', + field=models.ManyToManyField(related_name='variant', to='product.attributevalue', verbose_name='ویژگی\u200cها'), + ), + ] diff --git a/backend/product/migrations/0027_alter_productvariant_attributes.py b/backend/product/migrations/0027_alter_productvariant_attributes.py new file mode 100644 index 0000000..f799514 --- /dev/null +++ b/backend/product/migrations/0027_alter_productvariant_attributes.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0026_alter_productvariant_attributes'), + ] + + operations = [ + migrations.AlterField( + model_name='productvariant', + name='attributes', + field=models.ManyToManyField(blank=True, related_name='variant', to='product.attributevalue', verbose_name='ویژگی\u200cها'), + ), + ] diff --git a/backend/product/migrations/0028_alter_productvariant_attributes.py b/backend/product/migrations/0028_alter_productvariant_attributes.py new file mode 100644 index 0000000..dcf388a --- /dev/null +++ b/backend/product/migrations/0028_alter_productvariant_attributes.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0027_alter_productvariant_attributes'), + ] + + operations = [ + migrations.AlterField( + model_name='productvariant', + name='attributes', + field=models.ManyToManyField(related_name='variant', to='product.attributevalue', verbose_name='ویژگی\u200cها'), + ), + ] diff --git a/backend/product/migrations/0029_alter_attributevalue_unique_together_and_more.py b/backend/product/migrations/0029_alter_attributevalue_unique_together_and_more.py new file mode 100644 index 0000000..438e9bc --- /dev/null +++ b/backend/product/migrations/0029_alter_attributevalue_unique_together_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0028_alter_productvariant_attributes'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='attributevalue', + unique_together=set(), + ), + migrations.RemoveField( + model_name='attributevalue', + name='attribute_type', + ), + ] diff --git a/backend/product/migrations/0030_rename_attributes_productvariant_product_attributes_and_more.py b/backend/product/migrations/0030_rename_attributes_productvariant_product_attributes_and_more.py new file mode 100644 index 0000000..decf2e1 --- /dev/null +++ b/backend/product/migrations/0030_rename_attributes_productvariant_product_attributes_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.2 on 2025-02-13 20:49 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0029_alter_attributevalue_unique_together_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='productvariant', + old_name='attributes', + new_name='product_attributes', + ), + migrations.AddField( + model_name='attributevalue', + name='attribute_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='product.attributetype'), + ), + migrations.AlterUniqueTogether( + name='attributevalue', + unique_together={('attribute_type', 'value')}, + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 1c65209..1cf00fa 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -201,7 +201,7 @@ class AttributeValue(models.Model): unique_together = ('attribute_type', 'value') def __str__(self): - return f"{self.attribute_type.name}: {self.value}" + return f"{self.attribute_type}: {self.value}" class ProductImageModel(models.Model): @@ -220,7 +220,7 @@ class ProductImageModel(models.Model): class ProductVariant(models.Model): product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='variants', verbose_name='محصول') - attributes = models.ManyToManyField(AttributeValue, verbose_name='ویژگی‌ها', related_name='variant') + product_attributes = models.ManyToManyField(AttributeValue, verbose_name='ویژگی‌ها', related_name='variant') in_stock = models.PositiveIntegerField(default=0, verbose_name='تعداد موجود') price = models.PositiveIntegerField(default=0, verbose_name='قیمت') min_price = models.PositiveIntegerField(verbose_name='قیمت کف', help_text='این قیمت برای کف قیمتی محصول در نظر گرفته میشود') @@ -242,7 +242,7 @@ class ProductVariant(models.Model): verbose_name_plural = 'تنوع‌های محصول' def __str__(self): - return f"{self.product.name} - {', '.join(str(attr) for attr in self.attributes.all())}" + return f"{self.product.name} - {', '.join(str(attr) for attr in self.product_attributes.all())}" def get_toman_price(self, dollor_price=None): if not dollor_price: