diff --git a/backend/.env.local b/backend/.env.local index 4cb8221..2678574 100644 --- a/backend/.env.local +++ b/backend/.env.local @@ -28,4 +28,16 @@ SMS_API_KEY = '' VAPID_PRIVATE_KEY = 'NajogmGTsGsZ_dfURrjUpgsm5fui-s5AzruBQgMh_I4' -OPENAI_API_KEY = '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' \ No newline at end of file +OPENAI_API_KEY = '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' + + +# DATABASES = { +# 'default': { +# 'ENGINE': 'django.db.backends.postgresql', +# 'NAME': 'hshop', +# 'USER': 'byeto', +# 'PASSWORD': 'vuhbyq-cypMu0-sirbon', +# 'HOST': '185.110.189.208', +# 'PORT': '5434', +# } +# } \ No newline at end of file diff --git a/backend/core/settings/unfold_conf.py b/backend/core/settings/unfold_conf.py index 23bf662..a143954 100644 --- a/backend/core/settings/unfold_conf.py +++ b/backend/core/settings/unfold_conf.py @@ -239,6 +239,12 @@ UNFOLD = { "badge": "utils.admin.new_ticket_count", }, + { + "title": _("ارتباط با ما"), + "icon": "perm_phone_msg", + "link": reverse_lazy("admin:ticket_contactusmodel_changelist"), + "badge": "utils.admin.new_contact_us_count", + }, ], }, { @@ -262,6 +268,11 @@ UNFOLD = { "icon": "photo_prints", "link": reverse_lazy("admin:product_productvariant_changelist"), }, + { + "title": _("بخش جزيیات محصول"), + "icon": "subject", + "link": reverse_lazy("admin:product_productdetailmodel_changelist"), + }, ], }, diff --git a/backend/core/views.py b/backend/core/views.py index be623df..9894f4e 100644 --- a/backend/core/views.py +++ b/backend/core/views.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _ from django.views.generic import RedirectView, TemplateView from unfold.views import UnfoldModelAdminViewMixin from order.models import OrderModel -from ticket.models import Ticket +from ticket.models import Ticket, ContactUsModel from account.models import SecurityBreachAttemptModel import json @@ -19,9 +19,11 @@ def dashboard_callback(request, context): pending_count = OrderModel.objects.filter(status='ADMIN_PENDING').count() open_tickets_count = Ticket.objects.filter(status__in=['open', 'in_progress']).count() + open_contact_us_count = ContactUsModel.objects.filter(is_reviewed=False).count() context.update(random_data()) context.update({'pending_count': pending_count}) context.update({'open_tickets_count': open_tickets_count}) + context.update({'open_contact_us_count': open_contact_us_count}) return context diff --git a/backend/product/admin.py b/backend/product/admin.py index a7d6e19..1f1b709 100644 --- a/backend/product/admin.py +++ b/backend/product/admin.py @@ -1,4 +1,5 @@ from django.contrib import admin, messages +# from product.tasks import update_prices from .models import * from unfold.admin import TabularInline, StackedInline from home.models import LearnVideoModel @@ -118,14 +119,23 @@ class DetailModelAdmin(ModelAdmin, ImportExportModelAdmin): "widget": ArrayWidget, } } + +class DetailInLine(StackedInline): + model = DetailModel + extra = 0 + show_change_link = True + min_num = 1 + max_num = 4 + @admin.register(ProductDetailModel) class ProductDetailModel1Admin(ModelAdmin, ImportExportModelAdmin): import_form_class = ImportForm export_form_class = ExportForm - search_fields = ['detail_category__title'] + search_fields = ['detail_category__title', 'name'] compressed_fields = True warn_unsaved_form = True - + autocomplete_fields = ['detail_category',] + inlines = [DetailInLine] formfield_overrides = { ArrayField: { "widget": ArrayWidget, @@ -216,8 +226,7 @@ class ProductModelAdmin(ModelAdmin, ImportExportModelAdmin): @action(description=f"اپدیت قیمت ها") def update_products_price(self, request): - print('from the button') - ProductVariant.update_all_prices() + # update_prices() messages.success(request, f"قیمت {ProductVariant.objects.all().count()} تنوع محصول اپدیت شد") return redirect("admin:product_productmodel_changelist") diff --git a/backend/product/migrations/0036_alter_productdetailmodel_detail_category.py b/backend/product/migrations/0036_alter_productdetailmodel_detail_category.py new file mode 100644 index 0000000..1ae6eb5 --- /dev/null +++ b/backend/product/migrations/0036_alter_productdetailmodel_detail_category.py @@ -0,0 +1,20 @@ +# Generated by Django 5.1.2 on 2025-04-21 23:30 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0035_alter_attributetype_options_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='productdetailmodel', + name='detail_category', + field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='product.productdetailcategory', verbose_name='دسته بندی جزيات'), + preserve_default=False, + ), + ] diff --git a/backend/product/migrations/0037_productdetailmodel_name.py b/backend/product/migrations/0037_productdetailmodel_name.py new file mode 100644 index 0000000..c93a035 --- /dev/null +++ b/backend/product/migrations/0037_productdetailmodel_name.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.2 on 2025-04-21 23:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0036_alter_productdetailmodel_detail_category'), + ] + + operations = [ + migrations.AddField( + model_name='productdetailmodel', + name='name', + field=models.CharField(default=1, help_text='این متن فقط برای راحتی در استفاده از پنل ادمین میباشد', max_length=50, verbose_name='نام جزيیات'), + preserve_default=False, + ), + ] diff --git a/backend/product/migrations/0038_detailmodel_name.py b/backend/product/migrations/0038_detailmodel_name.py new file mode 100644 index 0000000..33082ac --- /dev/null +++ b/backend/product/migrations/0038_detailmodel_name.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.2 on 2025-04-22 16:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0037_productdetailmodel_name'), + ] + + operations = [ + migrations.AddField( + model_name='detailmodel', + name='name', + field=models.CharField(default='', help_text='این فیلد فقط برای راحتی در استفاده و جست و جو در پنل ادمین میباشد و در وبسایت نمایش نخواهد داده شد', max_length=50, verbose_name='نام چزيیات'), + preserve_default=False, + ), + ] diff --git a/backend/product/migrations/0039_remove_productdetailmodel_detail_and_more.py b/backend/product/migrations/0039_remove_productdetailmodel_detail_and_more.py new file mode 100644 index 0000000..ce6f375 --- /dev/null +++ b/backend/product/migrations/0039_remove_productdetailmodel_detail_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 5.1.2 on 2025-04-22 17:02 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0038_detailmodel_name'), + ] + + operations = [ + migrations.RemoveField( + model_name='productdetailmodel', + name='detail', + ), + migrations.AddField( + model_name='detailmodel', + name='detail_model', + field=models.ForeignKey(default=2, on_delete=django.db.models.deletion.CASCADE, to='product.productdetailmodel', verbose_name='دسته بندی جزيات'), + preserve_default=False, + ), + ] diff --git a/backend/product/migrations/0040_remove_detailmodel_name.py b/backend/product/migrations/0040_remove_detailmodel_name.py new file mode 100644 index 0000000..d12ad16 --- /dev/null +++ b/backend/product/migrations/0040_remove_detailmodel_name.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.2 on 2025-04-22 17:04 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('product', '0039_remove_productdetailmodel_detail_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='detailmodel', + name='name', + ), + ] diff --git a/backend/product/models.py b/backend/product/models.py index 1a1ca6b..7d8c160 100644 --- a/backend/product/models.py +++ b/backend/product/models.py @@ -4,6 +4,7 @@ from account.models import User from django.urls import reverse import requests from django.utils.translation import gettext_lazy as _ +from django.core.exceptions import ValidationError class MainCategoryModel(models.Model): name = models.CharField(max_length=50, verbose_name='نام دسته بندی') @@ -128,18 +129,6 @@ class ProductModel(models.Model): -class DetailModel(models.Model): - title = models.CharField(max_length=50, verbose_name='عنوان') - detail_text1 = models.CharField(max_length=150 , verbose_name='متن جزیات ۱') - detail_text2 = models.CharField(max_length=150 , verbose_name='متن جزیات ۲', blank=True, null=True) - detail_text3 = models.CharField(max_length=150 , verbose_name='متن جزیات ۳', blank=True, null=True) - detail_text4 = models.CharField(max_length=150 , verbose_name='متن جزیات ۴', blank=True, null=True) - def __str__(self): - return self.title - - class Meta: - verbose_name = 'مدل جزیات' - verbose_name_plural = 'مدل های جزیات' class ProductDetailCategory(models.Model): @@ -175,8 +164,6 @@ class CommentModel(models.Model): def __str__(self): return f"{self.user}-{self.content[:30]}" - - class AttributeType(models.Model): name = models.CharField(verbose_name='نام نوع متغییر', max_length=100) @@ -198,7 +185,6 @@ class AttributeValue(models.Model): def __str__(self): return f"{self.attribute_type}: {self.value}" - class ProductImageModel(models.Model): name = models.CharField(max_length=30, verbose_name='نام عکس') image = models.ImageField(upload_to='product_images/') @@ -212,14 +198,30 @@ class ProductImageModel(models.Model): class ProductDetailModel(models.Model): - detail_category = models.ForeignKey(ProductDetailCategory, on_delete=models.CASCADE, verbose_name='دسته بندی جزيات', blank=True, null=True) - detail = models.ManyToManyField(DetailModel, verbose_name='جزيات ها') + name = models.CharField(max_length=50, verbose_name='نام جزيیات', help_text='این متن فقط برای راحتی در استفاده از پنل ادمین میباشد') + detail_category = models.ForeignKey(ProductDetailCategory, on_delete=models.CASCADE, verbose_name='دسته بندی جزيات') class Meta: verbose_name = 'جزیات محصول' verbose_name_plural = 'جزیات محصول ها' - # def __str__(self): - # return f'جزيیات محصول {self.product}' + def __str__(self): + return f'جزيیات محصول {self.detail_category.title} - {self.name}' + + +class DetailModel(models.Model): + title = models.CharField(max_length=50, verbose_name='عنوان') + detail_text1 = models.CharField(max_length=150 , verbose_name='متن جزیات ۱') + detail_text2 = models.CharField(max_length=150 , verbose_name='متن جزیات ۲', blank=True, null=True) + detail_text3 = models.CharField(max_length=150 , verbose_name='متن جزیات ۳', blank=True, null=True) + detail_text4 = models.CharField(max_length=150 , verbose_name='متن جزیات ۴', blank=True, null=True) + detail_model = models.ForeignKey(ProductDetailModel, on_delete=models.CASCADE, verbose_name='دسته بندی جزيات', related_name='details') + def __str__(self): + return f'{self.title}' + + class Meta: + verbose_name = 'مدل جزیات' + verbose_name_plural = 'مدل های جزیات' + class ProductVariant(models.Model): product = models.ForeignKey(ProductModel, on_delete=models.CASCADE, related_name='variants', verbose_name='محصول') @@ -272,21 +274,3 @@ class ProductVariant(models.Model): def save(self, *args, **kwargs): self.set_or_update_price() super().save(*args, **kwargs) - - def get_toman_price_after_discount(self): - return self.price * ((100 - self.discount) / 100) - - @classmethod - def update_all_prices(cls): - print('calling the update all prices ') - dollor_object, _ = DollorModel.objects.get_or_create(unique_filed='unique') - print(dollor_object.price) - dollor_object.update_price() - dollor_object.save() - dollor_price = dollor_object.price - print(dollor_object.price) - print('classmethod dollor price update ') - products = cls.objects.all() - for product in products: - product.set_or_update_price(dollor_price=dollor_price) - product.save() \ No newline at end of file diff --git a/backend/product/serializers.py b/backend/product/serializers.py index 984b8f0..af7cf93 100644 --- a/backend/product/serializers.py +++ b/backend/product/serializers.py @@ -10,14 +10,14 @@ from django.contrib.auth.models import AnonymousUser class DetailSerializer(serializers.ModelSerializer): class Meta: model = DetailModel - fields = '__all__' + exclude = ['detail_model'] class ProductDetailSerializer(serializers.ModelSerializer): - detail = DetailSerializer(many=True, read_only=True) + details = DetailSerializer(many=True, read_only=True) detail_category = serializers.StringRelatedField() class Meta: model = ProductDetailModel - fields = "__all__" + exclude = ['name'] class AttributeTypeSerialzier(serializers.ModelSerializer): diff --git a/backend/templates/formula/service.html b/backend/templates/formula/service.html index 57856a4..ca6cc92 100644 --- a/backend/templates/formula/service.html +++ b/backend/templates/formula/service.html @@ -39,4 +39,25 @@ {% endcomponent %} -{% endif %} \ No newline at end of file +{% endif %} +{% if open_contact_us_count%} + + +