diff --git a/docker-compose.yml b/docker-compose.yml index fde8038..c5885be 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,6 +52,7 @@ services: build: . container_name: django_app env_file: .env + restart: always volumes: - .:/app - ./wait-for-it.sh:/wait-for-it.sh @@ -62,6 +63,21 @@ services: networks: - web_db_network + bot: + build: . + container_name: telegram_bot + command: python3 smartsoltech/manage.py start_telegram_bot + restart: always + volumes: + - .:/app + env_file: + - .env + depends_on: + - web + networks: + - web_db_network + + volumes: pgdata: pgadmin: diff --git a/requirements.txt b/requirements.txt index bcc711a..f582aae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,19 @@ +anyio==4.6.0 asgiref==3.8.1 +certifi==2024.8.30 Django==5.1.1 django-jazzmin==3.0.0 +exceptiongroup==1.2.2 +h11==0.14.0 +httpcore==1.0.6 +httpx==0.27.2 +idna==3.10 pillow==10.4.0 +psycopg2-binary==2.9.9 python-decouple==3.8 +python-telegram-bot==21.6 +qrcode==8.0 +sniffio==1.3.1 sqlparse==0.5.1 typing_extensions==4.12.2 -psycopg2-binary \ No newline at end of file +pyTelegramBotAPI \ No newline at end of file diff --git a/smartsoltech/comunication/__init__.py b/smartsoltech/comunication/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/smartsoltech/comunication/admin.py b/smartsoltech/comunication/admin.py new file mode 100644 index 0000000..76de8b2 --- /dev/null +++ b/smartsoltech/comunication/admin.py @@ -0,0 +1,16 @@ +# communication/admin.py +from django.contrib import admin +from .models import EmailSettings, TelegramSettings, UserCommunication + +@admin.register(EmailSettings) +class EmailSettingsAdmin(admin.ModelAdmin): + list_display = ('smtp_server', 'sender_email', 'use_tls', 'use_ssl') + +@admin.register(TelegramSettings) +class TelegramSettingsAdmin(admin.ModelAdmin): + list_display = ('bot_name', 'bot_token', 'use_polling') + +@admin.register(UserCommunication) +class UserCommunicationAdmin(admin.ModelAdmin): + list_display = ('client', 'email', 'phone', 'chat_id') + \ No newline at end of file diff --git a/smartsoltech/comunication/apps.py b/smartsoltech/comunication/apps.py new file mode 100644 index 0000000..1b75129 --- /dev/null +++ b/smartsoltech/comunication/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ComunicationConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'comunication' diff --git a/smartsoltech/comunication/management/.gitignore b/smartsoltech/comunication/management/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/smartsoltech/comunication/management/commands/start_telegram_bot.py b/smartsoltech/comunication/management/commands/start_telegram_bot.py new file mode 100644 index 0000000..5dfb2fe --- /dev/null +++ b/smartsoltech/comunication/management/commands/start_telegram_bot.py @@ -0,0 +1,11 @@ +# comunication/management/commands/start_telegram_bot.py +from django.core.management.base import BaseCommand +from comunication.telegram_bot import TelegramBot + +class Command(BaseCommand): + help = 'Starts the Telegram bot' + + def handle(self, *args, **kwargs): + bot = TelegramBot() + self.stdout.write('Starting Telegram bot polling...') + bot.start_bot_polling() \ No newline at end of file diff --git a/smartsoltech/comunication/migrations/0001_initial.py b/smartsoltech/comunication/migrations/0001_initial.py new file mode 100644 index 0000000..65fb392 --- /dev/null +++ b/smartsoltech/comunication/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.1 on 2024-10-08 12:20 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='CommunicationMethod', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(choices=[('email', 'Email'), ('telegram', 'Telegram')], max_length=50)), + ('settings', models.JSONField()), + ], + ), + migrations.CreateModel( + name='UserCommunication', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('chat_id', models.CharField(blank=True, max_length=100, null=True)), + ('email', models.EmailField(blank=True, max_length=254, null=True)), + ('phone', models.CharField(blank=True, max_length=20, null=True)), + ('preferred_method', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='comunication.communicationmethod')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/smartsoltech/comunication/migrations/0002_emailsettings_telegramsettings.py b/smartsoltech/comunication/migrations/0002_emailsettings_telegramsettings.py new file mode 100644 index 0000000..2630905 --- /dev/null +++ b/smartsoltech/comunication/migrations/0002_emailsettings_telegramsettings.py @@ -0,0 +1,36 @@ +# Generated by Django 5.1.1 on 2024-10-08 12:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('comunication', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='EmailSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('smtp_server', models.CharField(max_length=255)), + ('smtp_port', models.PositiveIntegerField()), + ('sender_email', models.EmailField(max_length=254)), + ('password', models.CharField(max_length=255)), + ('use_tls', models.BooleanField(default=True)), + ('use_ssl', models.BooleanField(default=False)), + ('display_name', models.CharField(blank=True, max_length=255, null=True)), + ], + ), + migrations.CreateModel( + name='TelegramSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('bot_name', models.CharField(max_length=100)), + ('bot_token', models.CharField(max_length=255)), + ('webhook_url', models.URLField(blank=True, null=True)), + ('use_polling', models.BooleanField(default=True)), + ], + ), + ] diff --git a/smartsoltech/comunication/migrations/0003_remove_usercommunication_preferred_method_and_more.py b/smartsoltech/comunication/migrations/0003_remove_usercommunication_preferred_method_and_more.py new file mode 100644 index 0000000..0fe608e --- /dev/null +++ b/smartsoltech/comunication/migrations/0003_remove_usercommunication_preferred_method_and_more.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.1 on 2024-10-08 12:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('comunication', '0002_emailsettings_telegramsettings'), + ] + + operations = [ + migrations.RemoveField( + model_name='usercommunication', + name='preferred_method', + ), + migrations.RemoveField( + model_name='usercommunication', + name='user', + ), + migrations.DeleteModel( + name='CommunicationMethod', + ), + migrations.DeleteModel( + name='UserCommunication', + ), + ] diff --git a/smartsoltech/comunication/migrations/0004_usercommunication.py b/smartsoltech/comunication/migrations/0004_usercommunication.py new file mode 100644 index 0000000..85ee8c1 --- /dev/null +++ b/smartsoltech/comunication/migrations/0004_usercommunication.py @@ -0,0 +1,22 @@ +# Generated by Django 5.1.1 on 2024-10-13 00:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('comunication', '0003_remove_usercommunication_preferred_method_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='UserCommunication', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('email', models.EmailField(max_length=254)), + ('phone', models.CharField(blank=True, max_length=15)), + ('chat_id', models.CharField(blank=True, max_length=50)), + ], + ), + ] diff --git a/smartsoltech/comunication/migrations/0005_alter_emailsettings_options_and_more.py b/smartsoltech/comunication/migrations/0005_alter_emailsettings_options_and_more.py new file mode 100644 index 0000000..496ecaf --- /dev/null +++ b/smartsoltech/comunication/migrations/0005_alter_emailsettings_options_and_more.py @@ -0,0 +1,47 @@ +# Generated by Django 5.1.1 on 2024-10-13 04:18 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('comunication', '0004_usercommunication'), + ('web', '0005_alter_blogpost_options_alter_category_options_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='emailsettings', + options={'ordering': ['-display_name'], 'verbose_name': 'Параметры E-mail', 'verbose_name_plural': 'Параметры E-mail'}, + ), + migrations.AlterModelOptions( + name='telegramsettings', + options={'ordering': ['-bot_name'], 'verbose_name': 'Параметры Telegram бота', 'verbose_name_plural': 'Параметры Telegram ботов'}, + ), + migrations.AlterModelOptions( + name='usercommunication', + options={'ordering': ['-id'], 'verbose_name': 'Связь с клиентом', 'verbose_name_plural': 'Связи с клиентами'}, + ), + migrations.AddField( + model_name='usercommunication', + name='client', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='communications', to='web.client', verbose_name='Клиент'), + ), + migrations.AlterField( + model_name='usercommunication', + name='chat_id', + field=models.CharField(blank=True, max_length=50, verbose_name='Telegram Chat ID'), + ), + migrations.AlterField( + model_name='usercommunication', + name='email', + field=models.EmailField(max_length=254, verbose_name='Электронная почта'), + ), + migrations.AlterField( + model_name='usercommunication', + name='phone', + field=models.CharField(blank=True, max_length=15, verbose_name='Телефон'), + ), + ] diff --git a/smartsoltech/comunication/migrations/0006_alter_usercommunication_client.py b/smartsoltech/comunication/migrations/0006_alter_usercommunication_client.py new file mode 100644 index 0000000..434ab09 --- /dev/null +++ b/smartsoltech/comunication/migrations/0006_alter_usercommunication_client.py @@ -0,0 +1,20 @@ +# Generated by Django 5.1.1 on 2024-10-13 04:20 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('comunication', '0005_alter_emailsettings_options_and_more'), + ('web', '0005_alter_blogpost_options_alter_category_options_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='usercommunication', + name='client', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='communications', to='web.client', verbose_name='Клиент'), + ), + ] diff --git a/smartsoltech/comunication/migrations/__init__.py b/smartsoltech/comunication/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/smartsoltech/comunication/models.py b/smartsoltech/comunication/models.py new file mode 100644 index 0000000..44d886e --- /dev/null +++ b/smartsoltech/comunication/models.py @@ -0,0 +1,50 @@ +# communication/models.py +from django.db import models +from django.contrib.auth.models import User +from web.models import Client + +class EmailSettings(models.Model): + smtp_server = models.CharField(max_length=255) + smtp_port = models.PositiveIntegerField() + sender_email = models.EmailField() + password = models.CharField(max_length=255) + use_tls = models.BooleanField(default=True) + use_ssl = models.BooleanField(default=False) + display_name = models.CharField(max_length=255, null=True, blank=True) + + def __str__(self): + return f"SMTP: {self.smtp_server}, Email: {self.sender_email}" + class Meta: + verbose_name = 'Параметры E-mail' + verbose_name_plural = 'Параметры E-mail' + ordering = ['-display_name'] + +class TelegramSettings(models.Model): + bot_name = models.CharField(max_length=100) + bot_token = models.CharField(max_length=255) + webhook_url = models.URLField(null=True, blank=True) + use_polling = models.BooleanField(default=True) + + def __str__(self): + return f"Telegram Bot: {self.bot_name}" + class Meta: + verbose_name = 'Параметры Telegram бота' + verbose_name_plural = 'Параметры Telegram ботов' + ordering = ['-bot_name'] +class UserCommunication(models.Model): + client = models.ForeignKey( + 'web.Client', on_delete=models.CASCADE, related_name='communications', verbose_name='Клиент', null=True, blank=True + ) + email = models.EmailField(verbose_name='Электронная почта') + phone = models.CharField(max_length=15, blank=True, verbose_name='Телефон') + chat_id = models.CharField(max_length=50, blank=True, verbose_name='Telegram Chat ID') + + class Meta: + verbose_name = 'Связь с клиентом' + verbose_name_plural = 'Связи с клиентами' + ordering = ['-id'] + + def __str__(self): + if self.client: + return f"Связь с клиентом: {self.client.first_name} {self.client.last_name} ({self.email})" + return f"Связь без клиента ({self.email})" diff --git a/smartsoltech/comunication/telegram_bot.py b/smartsoltech/comunication/telegram_bot.py new file mode 100644 index 0000000..c28742a --- /dev/null +++ b/smartsoltech/comunication/telegram_bot.py @@ -0,0 +1,256 @@ +# import telebot +# from decouple import config +# from django.shortcuts import get_object_or_404 +# from web.models import Client, ServiceRequest, Order +# from comunication.models import TelegramSettings +# import re +# import base64 +# import logging + +# class TelegramBot: +# def __init__(self): +# # Get bot settings from the database +# bot_settings = TelegramSettings.objects.first() +# if bot_settings: +# TELEGRAM_BOT_TOKEN = bot_settings.bot_token +# self.bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN) +# else: +# raise Exception("Telegram bot settings not found") + +# def start_bot_polling(self): +# @self.bot.message_handler(commands=['start']) +# def send_welcome(message): +# # Проверяем, содержатся ли параметры в команде /start +# match = re.match(r'/start request_(\d+)_token_(.*)', message.text) +# if match: +# self.handle_confirm_command(message, match) +# elif message.text.strip() == '/start': +# self.bot.reply_to(message, "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации.") +# else: +# self.bot.reply_to(message, "Здравствуйте! Чем я могу помочь? Вы можете задать вопросы о статусе заявки или заказе.") + +# @self.bot.message_handler(func=lambda message: 'статус заявки' in message.text.lower()) +# def handle_service_request_status(message): +# chat_id = message.chat.id +# client = Client.objects.filter(chat_id=chat_id).first() +# if client: +# service_requests = ServiceRequest.objects.filter(client_email=client.email) +# if service_requests.exists(): +# response = "Ваши заявки:\n" +# for req in service_requests: +# response += f"Номер заявки: {req.id}, Услуга: {req.service.name}, Дата создания: {req.created_at.strftime('%d-%m-%Y')}\n" +# else: +# response = "У вас нет активных заявок." +# else: +# response = "Клиент не найден. Пожалуйста, зарегистрируйтесь." +# self.bot.reply_to(message, response) + +# @self.bot.message_handler(func=lambda message: 'статус заказа' in message.text.lower()) +# def handle_order_status(message): +# chat_id = message.chat.id +# client = Client.objects.filter(chat_id=chat_id).first() +# if client: +# orders = Order.objects.filter(client=client) +# if orders.exists(): +# response = "Ваши заказы:\n" +# for order in orders: +# response += f"Номер заказа: {order.id}, Услуга: {order.service.name}, Статус: {order.get_status_display()}\n" +# else: +# response = "У вас нет активных заказов." +# else: +# response = "Клиент не найден. Пожалуйста, зарегистрируйтесь." +# self.bot.reply_to(message, response) + +# self.bot.polling(non_stop=True) + +# def handle_confirm_command(self, message, match=None): +# chat_id = message.chat.id +# if not match: +# match = re.match(r'/start request_(\d+)_token_(.*)', message.text) +# if match: +# request_id = match.group(1) +# encoded_token = match.group(2) + +# # Декодируем токен из base64 +# try: +# token = base64.urlsafe_b64decode(encoded_token + '==').decode('utf-8') +# logging.info(f"Декодированный токен: {token}") +# except Exception as e: +# logging.error(f"Ошибка при декодировании токена: {e}") +# self.bot.send_message(chat_id, "Ошибка: Некорректный токен. Пожалуйста, повторите попытку позже.") +# return + +# # Получаем заявку по ID и токену +# service_request = ServiceRequest.objects.filter(id=request_id, token=token).first() +# if service_request: +# # Обновляем chat_id клиента +# service_request.chat_id = chat_id +# service_request.client_name = message.from_user.first_name +# service_request.save() + +# response_message = ( +# f"Здравствуйте, {message.from_user.first_name}!\n" +# f"Ваша заявка на услугу успешно зарегистрирована. " +# f"Пожалуйста, вернитесь на сайт для продолжения оформления." +# ) +# else: +# response_message = "Ошибка: Неверная заявка или токен. Пожалуйста, проверьте ссылку." + +# self.bot.send_message(chat_id, response_message) +# else: +# response_message = "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации." +# self.bot.send_message(chat_id, response_message) + +# def send_telegram_message(self, client_id, service_request_id, custom_message, order_id=None): +# # Get the client and service request from the database +# client = get_object_or_404(Client, pk=client_id) +# service_request = get_object_or_404(ServiceRequest, pk=service_request_id) +# chat_id = client.chat_id + +# # Build the message content +# message = f"Здравствуйте, {client.first_name} {client.last_name}!\n" +# message += custom_message + +# if order_id: +# order = get_object_or_404(Order, pk=order_id) +# message += f"\n\nДетали заказа:\nУслуга: {order.service.name}\nСтатус: {order.get_status_display()}\n" + +# # Add service request details +# message += f"\nНомер заявки: {service_request.id}\nДата создания заявки: {service_request.created_at.strftime('%d-%m-%Y')}\n" + +# # Send the message using the bot +# try: +# self.bot.send_message(chat_id, message) +# except Exception as e: +# logging.error(f"Ошибка при отправке сообщения в Telegram: {e}") + + +import telebot +from decouple import config +from django.shortcuts import get_object_or_404 +from web.models import Client, ServiceRequest, Order +from comunication.models import TelegramSettings +import re +import base64 +import logging + +class TelegramBot: + def __init__(self): + # Get bot settings from the database + bot_settings = TelegramSettings.objects.first() + if bot_settings: + TELEGRAM_BOT_TOKEN = bot_settings.bot_token + self.bot = telebot.TeleBot(TELEGRAM_BOT_TOKEN) + else: + raise Exception("Telegram bot settings not found") + + def start_bot_polling(self): + @self.bot.message_handler(commands=['start']) + def send_welcome(message): + # Проверяем, содержатся ли параметры в команде /start + match = re.match(r'/start request_(\d+)_token_(.*)', message.text) + if match: + self.handle_confirm_command(message, match) + elif message.text.strip() == '/start': + self.bot.reply_to(message, "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации.") + else: + self.bot.reply_to(message, "Здравствуйте! Пожалуйста, используйте команду /start с корректными параметрами для подтверждения регистрации.") + + @self.bot.message_handler(func=lambda message: 'статус заявки' in message.text.lower()) + def handle_service_request_status(message): + chat_id = message.chat.id + client = Client.objects.filter(chat_id=chat_id).first() + if client: + service_requests = ServiceRequest.objects.filter(client_email=client.email) + if service_requests.exists(): + response = "Ваши заявки:\n" + for req in service_requests: + response += f"Номер заявки: {req.id}, Услуга: {req.service.name}, Дата создания: {req.created_at.strftime('%d-%m-%Y')}\n" + else: + response = "У вас нет активных заявок." + else: + response = "Клиент не найден. Пожалуйста, зарегистрируйтесь." + self.bot.reply_to(message, response) + + @self.bot.message_handler(func=lambda message: 'статус заказа' in message.text.lower()) + def handle_order_status(message): + chat_id = message.chat.id + client = Client.objects.filter(chat_id=chat_id).first() + if client: + orders = Order.objects.filter(client=client) + if orders.exists(): + response = "Ваши заказы:\n" + for order in orders: + response += f"Номер заказа: {order.id}, Услуга: {order.service.name}, Статус: {order.get_status_display()}\n" + else: + response = "У вас нет активных заказов." + else: + response = "Клиент не найден. Пожалуйста, зарегистрируйтесь." + self.bot.reply_to(message, response) + + self.bot.polling(non_stop=True) + + def handle_confirm_command(self, message, match=None): + chat_id = message.chat.id + if not match: + match = re.match(r'/start request_(\d+)_token_(.*)', message.text) + if match: + request_id = match.group(1) + encoded_token = match.group(2) + + # Декодируем токен из base64 + try: + token = base64.urlsafe_b64decode(encoded_token + '==').decode('utf-8') + logging.info(f"Декодированный токен: {token}") + except Exception as e: + logging.error(f"Ошибка при декодировании токена: {e}") + self.bot.send_message(chat_id, "Ошибка: Некорректный токен. Пожалуйста, повторите попытку позже.") + return + + # Получаем заявку по ID и токену + service_request = ServiceRequest.objects.filter(id=request_id, token=token).first() + if service_request: + # Обновляем chat_id клиента + service_request.chat_id = chat_id + service_request.client_name = message.from_user.first_name + service_request.save() + + response_message = ( + f"Здравствуйте, {message.from_user.first_name}!\n" + f"Ваш Telegram аккаунт успешно подтвержден. Пожалуйста, вернитесь на сайт для заполнения остальных данных." + ) + else: + response_message = "Ошибка: Неверная заявка или токен. Пожалуйста, проверьте ссылку." + + self.bot.send_message(chat_id, response_message) + else: + response_message = "Ошибка: Некорректная команда. Пожалуйста, используйте ссылку, предоставленную на сайте для регистрации." + self.bot.send_message(chat_id, response_message) + + def send_telegram_message(self, client_id, service_request_id, custom_message, order_id=None): + # Get the client and service request from the database + client = get_object_or_404(Client, pk=client_id) + service_request = get_object_or_404(ServiceRequest, pk=service_request_id) + chat_id = client.chat_id + + # Build the message content + message = f"Здравствуйте, {client.first_name} {client.last_name}!\n" + message += custom_message + + if order_id: + order = get_object_or_404(Order, pk=order_id) + message += f"\n\nДетали заказа:\nУслуга: {order.service.name}\nСтатус: {order.get_status_display()}\n" + + # Add service request details + message += f"\nНомер заявки: {service_request.id}\nДата создания заявки: {service_request.created_at.strftime('%d-%m-%Y')}\n" + + # Send the message using the bot + try: + self.bot.send_message(chat_id, message) + except Exception as e: + logging.error(f"Ошибка при отправке сообщения в Telegram: {e}") + +# Example usage: +# bot = TelegramBot() +# bot.start_bot_polling() +# bot.send_telegram_message(client_id=1, service_request_id=1, custom_message="Ваши данные для входа на сайт.") \ No newline at end of file diff --git a/smartsoltech/comunication/tests.py b/smartsoltech/comunication/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/smartsoltech/comunication/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/smartsoltech/comunication/views.py b/smartsoltech/comunication/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/smartsoltech/comunication/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/smartsoltech/media/static/img/services/1_ym12Lh9.png b/smartsoltech/media/static/img/services/1_ym12Lh9.png new file mode 100644 index 0000000..32740b6 Binary files /dev/null and b/smartsoltech/media/static/img/services/1_ym12Lh9.png differ diff --git a/smartsoltech/media/static/img/services/2_VlpsUXb.png b/smartsoltech/media/static/img/services/2_VlpsUXb.png new file mode 100644 index 0000000..0a9886b Binary files /dev/null and b/smartsoltech/media/static/img/services/2_VlpsUXb.png differ diff --git a/smartsoltech/media/static/img/services/4_MIdyabb.png b/smartsoltech/media/static/img/services/4_MIdyabb.png new file mode 100644 index 0000000..1b25a9a Binary files /dev/null and b/smartsoltech/media/static/img/services/4_MIdyabb.png differ diff --git a/smartsoltech/smartsoltech/settings.py b/smartsoltech/smartsoltech/settings.py index 92c6a94..1a35377 100644 --- a/smartsoltech/smartsoltech/settings.py +++ b/smartsoltech/smartsoltech/settings.py @@ -44,6 +44,7 @@ INSTALLED_APPS = [ 'django.contrib.messages', 'django.contrib.staticfiles', 'web', + 'comunication' ] MIDDLEWARE = [ diff --git a/smartsoltech/static/qr_codes/request_277.png b/smartsoltech/static/qr_codes/request_277.png new file mode 100644 index 0000000..2992841 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_277.png differ diff --git a/smartsoltech/static/qr_codes/request_278.png b/smartsoltech/static/qr_codes/request_278.png new file mode 100644 index 0000000..ca0f05c Binary files /dev/null and b/smartsoltech/static/qr_codes/request_278.png differ diff --git a/smartsoltech/static/qr_codes/request_279.png b/smartsoltech/static/qr_codes/request_279.png new file mode 100644 index 0000000..482f216 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_279.png differ diff --git a/smartsoltech/static/qr_codes/request_280.png b/smartsoltech/static/qr_codes/request_280.png new file mode 100644 index 0000000..3490f31 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_280.png differ diff --git a/smartsoltech/static/qr_codes/request_281.png b/smartsoltech/static/qr_codes/request_281.png new file mode 100644 index 0000000..592425f Binary files /dev/null and b/smartsoltech/static/qr_codes/request_281.png differ diff --git a/smartsoltech/static/qr_codes/request_282.png b/smartsoltech/static/qr_codes/request_282.png new file mode 100644 index 0000000..8a4c799 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_282.png differ diff --git a/smartsoltech/static/qr_codes/request_283.png b/smartsoltech/static/qr_codes/request_283.png new file mode 100644 index 0000000..f653db9 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_283.png differ diff --git a/smartsoltech/static/qr_codes/request_284.png b/smartsoltech/static/qr_codes/request_284.png new file mode 100644 index 0000000..1d72cfc Binary files /dev/null and b/smartsoltech/static/qr_codes/request_284.png differ diff --git a/smartsoltech/static/qr_codes/request_285.png b/smartsoltech/static/qr_codes/request_285.png new file mode 100644 index 0000000..627aa2b Binary files /dev/null and b/smartsoltech/static/qr_codes/request_285.png differ diff --git a/smartsoltech/static/qr_codes/request_286.png b/smartsoltech/static/qr_codes/request_286.png new file mode 100644 index 0000000..820480e Binary files /dev/null and b/smartsoltech/static/qr_codes/request_286.png differ diff --git a/smartsoltech/static/qr_codes/request_287.png b/smartsoltech/static/qr_codes/request_287.png new file mode 100644 index 0000000..9bbd672 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_287.png differ diff --git a/smartsoltech/static/qr_codes/request_288.png b/smartsoltech/static/qr_codes/request_288.png new file mode 100644 index 0000000..49e1334 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_288.png differ diff --git a/smartsoltech/static/qr_codes/request_289.png b/smartsoltech/static/qr_codes/request_289.png new file mode 100644 index 0000000..b8bf68a Binary files /dev/null and b/smartsoltech/static/qr_codes/request_289.png differ diff --git a/smartsoltech/static/qr_codes/request_290.png b/smartsoltech/static/qr_codes/request_290.png new file mode 100644 index 0000000..f7e5dc3 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_290.png differ diff --git a/smartsoltech/static/qr_codes/request_291.png b/smartsoltech/static/qr_codes/request_291.png new file mode 100644 index 0000000..b73a652 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_291.png differ diff --git a/smartsoltech/static/qr_codes/request_292.png b/smartsoltech/static/qr_codes/request_292.png new file mode 100644 index 0000000..7607264 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_292.png differ diff --git a/smartsoltech/static/qr_codes/request_293.png b/smartsoltech/static/qr_codes/request_293.png new file mode 100644 index 0000000..7b523be Binary files /dev/null and b/smartsoltech/static/qr_codes/request_293.png differ diff --git a/smartsoltech/static/qr_codes/request_294.png b/smartsoltech/static/qr_codes/request_294.png new file mode 100644 index 0000000..3f39fb1 Binary files /dev/null and b/smartsoltech/static/qr_codes/request_294.png differ diff --git a/smartsoltech/web/admin.py b/smartsoltech/web/admin.py index acb2ddf..406d641 100644 --- a/smartsoltech/web/admin.py +++ b/smartsoltech/web/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Service, Project, Client, Order, Review, BlogPost, Category +from .models import Service, Project, Client, Order, Review, BlogPost, Category, ServiceRequest from .forms import ProjectForm @admin.register(Service) @@ -41,3 +41,8 @@ class CategoryAdmin(admin.ModelAdmin): list_display = ('name','description') search_fields = ('name',) +@admin.register(ServiceRequest) +class ServiceRequestAdmin(admin.ModelAdmin): + list_display = ('service','token', 'chat_id','client_name', 'client_email', 'client_phone', 'created_at') + search_fields = ('service','token','client_name', 'client_email', 'client_phone') + list_filter = ('service','token','client_name', 'client_phone') \ No newline at end of file diff --git a/smartsoltech/web/migrations/0001_initial.py b/smartsoltech/web/migrations/0001_initial.py index 40894b3..07198b2 100644 --- a/smartsoltech/web/migrations/0001_initial.py +++ b/smartsoltech/web/migrations/0001_initial.py @@ -1,6 +1,7 @@ -# Generated by Django 5.1.1 on 2024-10-06 07:28 +# Generated by Django 5.1.1 on 2024-10-08 13:56 import django.db.models.deletion +import uuid from django.db import migrations, models @@ -27,6 +28,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=100)), + ('description', models.TextField(default='Описание категории')), ], ), migrations.CreateModel( @@ -35,21 +37,33 @@ class Migration(migrations.Migration): ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('first_name', models.CharField(max_length=100)), ('last_name', models.CharField(max_length=100)), - ('email', models.EmailField(max_length=254)), - ('phone_number', models.CharField(max_length=15)), + ('email', models.EmailField(max_length=254, unique=True)), + ('phone_number', models.CharField(max_length=15, unique=True)), ('image', models.ImageField(blank=True, null=True, upload_to='static/img/customer/')), ], ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('message', models.TextField(blank=True, null=True)), + ('order_date', models.DateTimeField(auto_now_add=True)), + ('status', models.CharField(choices=[('pending', 'Pending'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=50)), + ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.client')), + ], + ), migrations.CreateModel( name='Project', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=200)), - ('description', models.TextField()), - ('completion_date', models.DateField()), + ('description', models.TextField(default='Описание проекта')), + ('completion_date', models.DateField(blank=True, null=True)), ('image', models.ImageField(blank=True, null=True, upload_to='static/img/project/')), - ('category', models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='web.category')), + ('status', models.CharField(choices=[('in_progress', 'In Progress'), ('completed', 'Completed')], default='in_progress', max_length=50)), + ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='web.category')), ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='web.client')), + ('order', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project', to='web.order')), ], ), migrations.CreateModel( @@ -57,10 +71,10 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=200)), - ('description', models.TextField()), + ('description', models.TextField(default='Описание услуги')), ('price', models.DecimalField(decimal_places=2, max_digits=10)), ('image', models.ImageField(blank=True, null=True, upload_to='static/img/services/')), - ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='services', to='web.category')), + ('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='services', to='web.category')), ], ), migrations.CreateModel( @@ -72,17 +86,30 @@ class Migration(migrations.Migration): ('review_date', models.DateTimeField(auto_now_add=True)), ('image', models.ImageField(blank=True, null=True, upload_to='static/img/review/')), ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.client')), + ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.project')), ('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.service')), ], ), + migrations.AddField( + model_name='project', + name='service', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='web.service'), + ), + migrations.AddField( + model_name='order', + name='service', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.service'), + ), migrations.CreateModel( - name='Order', + name='ServiceRequest', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order_date', models.DateTimeField(auto_now_add=True)), - ('status', models.CharField(choices=[('pending', 'Pending'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], max_length=50)), - ('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.client')), - ('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.service')), + ('client_name', models.CharField(max_length=100)), + ('client_email', models.EmailField(max_length=254)), + ('client_phone', models.CharField(max_length=20)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('token', models.UUIDField(default=uuid.uuid4, unique=True)), + ('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.service')), ], ), ] diff --git a/smartsoltech/web/migrations/0002_category_description.py b/smartsoltech/web/migrations/0002_category_description.py deleted file mode 100644 index 375e64c..0000000 --- a/smartsoltech/web/migrations/0002_category_description.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-06 07:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('web', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='category', - name='description', - field=models.TextField(default=1), - preserve_default=False, - ), - ] diff --git a/smartsoltech/web/migrations/0002_client_chat_id_servicerequest_chat_id.py b/smartsoltech/web/migrations/0002_client_chat_id_servicerequest_chat_id.py new file mode 100644 index 0000000..310b7a5 --- /dev/null +++ b/smartsoltech/web/migrations/0002_client_chat_id_servicerequest_chat_id.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.1 on 2024-10-13 03:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='client', + name='chat_id', + field=models.CharField(blank=True, max_length=100, null=True), + ), + migrations.AddField( + model_name='servicerequest', + name='chat_id', + field=models.CharField(blank=True, max_length=100, null=True), + ), + ] diff --git a/smartsoltech/web/migrations/0003_client_user.py b/smartsoltech/web/migrations/0003_client_user.py new file mode 100644 index 0000000..e24ddb5 --- /dev/null +++ b/smartsoltech/web/migrations/0003_client_user.py @@ -0,0 +1,21 @@ +# Generated by Django 5.1.1 on 2024-10-13 03:54 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0002_client_chat_id_servicerequest_chat_id'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='client', + name='user', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='client_profile', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/smartsoltech/web/migrations/0003_review_project_alter_project_category_and_more.py b/smartsoltech/web/migrations/0003_review_project_alter_project_category_and_more.py deleted file mode 100644 index 6a6c022..0000000 --- a/smartsoltech/web/migrations/0003_review_project_alter_project_category_and_more.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-06 09:01 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('web', '0002_category_description'), - ] - - operations = [ - migrations.AddField( - model_name='review', - name='project', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.project'), - ), - migrations.AlterField( - model_name='project', - name='category', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='web.category'), - ), - migrations.AlterField( - model_name='review', - name='service', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.service'), - ), - ] diff --git a/smartsoltech/web/migrations/0004_alter_order_options_alter_order_client_and_more.py b/smartsoltech/web/migrations/0004_alter_order_options_alter_order_client_and_more.py new file mode 100644 index 0000000..e9e291e --- /dev/null +++ b/smartsoltech/web/migrations/0004_alter_order_options_alter_order_client_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.1 on 2024-10-13 03:56 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0003_client_user'), + ] + + operations = [ + migrations.AlterModelOptions( + name='order', + options={'ordering': ['-order_date'], 'verbose_name': 'Заказ', 'verbose_name_plural': 'Заказы'}, + ), + migrations.AlterField( + model_name='order', + name='client', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_orders', to='web.client'), + ), + migrations.AlterField( + model_name='order', + name='service', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_orders', to='web.service'), + ), + ] diff --git a/smartsoltech/web/migrations/0004_alter_project_category_alter_service_category.py b/smartsoltech/web/migrations/0004_alter_project_category_alter_service_category.py deleted file mode 100644 index acef0f8..0000000 --- a/smartsoltech/web/migrations/0004_alter_project_category_alter_service_category.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-06 09:08 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('web', '0003_review_project_alter_project_category_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='project', - name='category', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='projects', to='web.category'), - ), - migrations.AlterField( - model_name='service', - name='category', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='services', to='web.category'), - ), - ] diff --git a/smartsoltech/web/migrations/0005_alter_blogpost_options_alter_category_options_and_more.py b/smartsoltech/web/migrations/0005_alter_blogpost_options_alter_category_options_and_more.py new file mode 100644 index 0000000..c29fb62 --- /dev/null +++ b/smartsoltech/web/migrations/0005_alter_blogpost_options_alter_category_options_and_more.py @@ -0,0 +1,51 @@ +# Generated by Django 5.1.1 on 2024-10-13 04:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0004_alter_order_options_alter_order_client_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='blogpost', + options={'ordering': ['-published_date'], 'verbose_name': 'Блог', 'verbose_name_plural': 'Блоги'}, + ), + migrations.AlterModelOptions( + name='category', + options={'ordering': ['name'], 'verbose_name': 'Категория', 'verbose_name_plural': 'Категории'}, + ), + migrations.AlterModelOptions( + name='client', + options={'ordering': ['last_name', 'first_name'], 'verbose_name': 'Клиент', 'verbose_name_plural': 'Клиенты'}, + ), + migrations.AlterModelOptions( + name='project', + options={'ordering': ['-completion_date'], 'verbose_name': 'Проект', 'verbose_name_plural': 'Проекты'}, + ), + migrations.AlterModelOptions( + name='review', + options={'ordering': ['-review_date'], 'verbose_name': 'Отзыв', 'verbose_name_plural': 'Отзывы'}, + ), + migrations.AlterModelOptions( + name='service', + options={'ordering': ['name'], 'verbose_name': 'Услуга', 'verbose_name_plural': 'Услуги'}, + ), + migrations.AlterModelOptions( + name='servicerequest', + options={'ordering': ['-created_at'], 'verbose_name': 'Заявка на услугу', 'verbose_name_plural': 'Заявки на услуги'}, + ), + migrations.AlterField( + model_name='order', + name='status', + field=models.CharField(choices=[('pending', 'Ожидание'), ('in_progress', 'В процессе'), ('completed', 'Завершен'), ('cancelled', 'Отменён')], default='pending', max_length=50), + ), + migrations.AlterField( + model_name='project', + name='status', + field=models.CharField(choices=[('in_progress', 'В процессе'), ('completed', 'Завершен')], default='in_progress', max_length=50), + ), + ] diff --git a/smartsoltech/web/migrations/0005_order_client_email_order_client_phone_order_message_and_more.py b/smartsoltech/web/migrations/0005_order_client_email_order_client_phone_order_message_and_more.py deleted file mode 100644 index 3ae4e4b..0000000 --- a/smartsoltech/web/migrations/0005_order_client_email_order_client_phone_order_message_and_more.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-06 10:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('web', '0004_alter_project_category_alter_service_category'), - ] - - operations = [ - migrations.AddField( - model_name='order', - name='client_email', - field=models.EmailField(default='notprovided@example.com', max_length=254), - ), - migrations.AddField( - model_name='order', - name='client_phone', - field=models.CharField(default='unknown', max_length=15), - ), - migrations.AddField( - model_name='order', - name='message', - field=models.TextField(blank=True, null=True), - ), - migrations.AlterField( - model_name='order', - name='status', - field=models.CharField(choices=[('pending', 'Pending'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=50), - ), - ] diff --git a/smartsoltech/web/migrations/0006_remove_order_client_email_remove_order_client_phone_and_more.py b/smartsoltech/web/migrations/0006_remove_order_client_email_remove_order_client_phone_and_more.py deleted file mode 100644 index b6b98ae..0000000 --- a/smartsoltech/web/migrations/0006_remove_order_client_email_remove_order_client_phone_and_more.py +++ /dev/null @@ -1,60 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-07 11:23 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('web', '0005_order_client_email_order_client_phone_order_message_and_more'), - ] - - operations = [ - migrations.RemoveField( - model_name='order', - name='client_email', - ), - migrations.RemoveField( - model_name='order', - name='client_phone', - ), - migrations.AddField( - model_name='project', - name='order', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='project', to='web.order'), - ), - migrations.AddField( - model_name='project', - name='service', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='web.service'), - preserve_default=False, - ), - migrations.AddField( - model_name='project', - name='status', - field=models.CharField(choices=[('in_progress', 'In Progress'), ('completed', 'Completed')], default='in_progress', max_length=50), - ), - migrations.AlterField( - model_name='order', - name='status', - field=models.CharField(choices=[('pending', 'Pending'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=50), - ), - migrations.AlterField( - model_name='project', - name='category', - field=models.CharField(default=1, max_length=100), - preserve_default=False, - ), - migrations.AlterField( - model_name='project', - name='completion_date', - field=models.DateField(blank=True, null=True), - ), - migrations.AlterField( - model_name='review', - name='service', - field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='web.service'), - preserve_default=False, - ), - ] diff --git a/smartsoltech/web/migrations/0007_remove_project_category.py b/smartsoltech/web/migrations/0007_remove_project_category.py deleted file mode 100644 index e3ff4d7..0000000 --- a/smartsoltech/web/migrations/0007_remove_project_category.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-07 11:31 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('web', '0006_remove_order_client_email_remove_order_client_phone_and_more'), - ] - - operations = [ - migrations.RemoveField( - model_name='project', - name='category', - ), - ] diff --git a/smartsoltech/web/migrations/0008_project_category.py b/smartsoltech/web/migrations/0008_project_category.py deleted file mode 100644 index 60c06af..0000000 --- a/smartsoltech/web/migrations/0008_project_category.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.1 on 2024-10-07 11:43 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('web', '0007_remove_project_category'), - ] - - operations = [ - migrations.AddField( - model_name='project', - name='category', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='web.category'), - ), - ] diff --git a/smartsoltech/web/models.py b/smartsoltech/web/models.py index 1ba6acb..7d03fef 100644 --- a/smartsoltech/web/models.py +++ b/smartsoltech/web/models.py @@ -1,21 +1,32 @@ -# web/models.py from django.db import models -from django.contrib.auth.models import AbstractUser - +from django.contrib.auth.models import AbstractUser, User +import uuid +from django.urls import reverse class Category(models.Model): name = models.CharField(max_length=100) - description = models.TextField() + description = models.TextField(default='Описание категории') + + class Meta: + verbose_name = 'Категория' + verbose_name_plural = 'Категории' + ordering = ['name'] + def __str__(self): return self.name class Service(models.Model): name = models.CharField(max_length=200) - description = models.TextField() + description = models.TextField(default='Описание услуги') price = models.DecimalField(max_digits=10, decimal_places=2) category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='services') image = models.ImageField(upload_to='static/img/services/', blank=True, null=True) + class Meta: + verbose_name = 'Услуга' + verbose_name_plural = 'Услуги' + ordering = ['name'] + def __str__(self): return self.name @@ -29,14 +40,21 @@ class Service(models.Model): return self.reviews.count() class Client(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='client_profile', null=True, blank=True) first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) - email = models.EmailField() - phone_number = models.CharField(max_length=15) + email = models.EmailField(unique=True) + phone_number = models.CharField(max_length=15, unique=True) image = models.ImageField(upload_to='static/img/customer/', blank=True, null=True) + chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID + + class Meta: + verbose_name = 'Клиент' + verbose_name_plural = 'Клиенты' + ordering = ['last_name', 'first_name'] def __str__(self): - return f"{self.first_name} {self.last_name}" + return f"{self.first_name} {self.last_name} {self.chat_id}" class BlogPost(models.Model): title = models.CharField(max_length=200) @@ -44,29 +62,59 @@ class BlogPost(models.Model): published_date = models.DateTimeField(auto_now_add=True) image = models.ImageField(upload_to='static/img/blog/', blank=True, null=True) + class Meta: + verbose_name = 'Блог' + verbose_name_plural = 'Блоги' + ordering = ['-published_date'] + def __str__(self): return self.title class Order(models.Model): - service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='orders') - client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='orders') + service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='related_orders') + client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='related_orders') message = models.TextField(blank=True, null=True) order_date = models.DateTimeField(auto_now_add=True) - status = models.CharField(max_length=50, choices=[('pending', 'Pending'), ('in_progress', 'In Progress'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending') + status = models.CharField( + max_length=50, + choices=[ + ('pending', 'Ожидание'), + ('in_progress', 'В процессе'), + ('completed', 'Завершен'), + ('cancelled', 'Отменён') + ], + default='pending' + ) + + class Meta: + ordering = ['-order_date'] + verbose_name = 'Заказ' + verbose_name_plural = 'Заказы' def __str__(self): return f"Order #{self.id} by {self.client.first_name}" + def is_completed(self): + return self.status == 'completed' + + def get_absolute_url(self): + return reverse('order_detail', kwargs={'pk': self.pk}) + class Project(models.Model): name = models.CharField(max_length=200) - description = models.TextField() + description = models.TextField(default='Описание проекта') completion_date = models.DateField(blank=True, null=True) client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='projects') service = models.ForeignKey(Service, on_delete=models.CASCADE, related_name='projects') order = models.OneToOneField(Order, on_delete=models.CASCADE, related_name='project', null=True, blank=True) - category = models.ForeignKey('Category', on_delete=models.SET_NULL, null=True, blank=True) + category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True) image = models.ImageField(upload_to='static/img/project/', blank=True, null=True) - status = models.CharField(max_length=50, choices=[('in_progress', 'In Progress'), ('completed', 'Completed')], default='in_progress') + status = models.CharField(max_length=50, choices=[('in_progress', 'В процессе'), ('completed', 'Завершен')], default='in_progress') + + class Meta: + verbose_name = 'Проект' + verbose_name_plural = 'Проекты' + ordering = ['-completion_date'] def __str__(self): return self.name @@ -80,19 +128,27 @@ class Review(models.Model): review_date = models.DateTimeField(auto_now_add=True) image = models.ImageField(upload_to='static/img/review/', blank=True, null=True) + class Meta: + verbose_name = 'Отзыв' + verbose_name_plural = 'Отзывы' + ordering = ['-review_date'] + def __str__(self): return f"Review by {self.client.first_name} {self.client.last_name} for {self.service.name}" - - COMMUNICATION_METHODS = [ - ('email', 'Email'), - ('telegram', 'Telegram'), - ('sms', 'SMS'), -] -class User(AbstractUser): - telegram_id = models.CharField(max_length=50, blank=True, null=True, unique=True) - preferred_communication = models.CharField( - max_length=20, - choices=COMMUNICATION_METHODS, - default='email', - ) \ No newline at end of file +class ServiceRequest(models.Model): + service = models.ForeignKey(Service, on_delete=models.CASCADE) + client_name = models.CharField(max_length=100) + client_email = models.EmailField() + client_phone = models.CharField(max_length=20) + created_at = models.DateTimeField(auto_now_add=True) + token = models.UUIDField(default=uuid.uuid4, unique=True) # Генерация уникального токена + chat_id = models.CharField(max_length=100, blank=True, null=True) # Telegram chat ID + + class Meta: + verbose_name = 'Заявка на услугу' + verbose_name_plural = 'Заявки на услуги' + ordering = ['-created_at'] + + def __str__(self): + return f"Request for {self.service.name} by {self.client_name}" \ No newline at end of file diff --git a/smartsoltech/web/templates/web/client_orders.html b/smartsoltech/web/templates/web/client_orders.html new file mode 100644 index 0000000..811aaff --- /dev/null +++ b/smartsoltech/web/templates/web/client_orders.html @@ -0,0 +1,13 @@ +{% extends 'web/base.html' %} + +{% block content %} +
Спасибо, ваша заявка на услугу "{{ service_request.service.name }}" успешно создана.
+Вы также можете перейти к своему заказу ниже:
+ Перейти к заказу +