main funtions are ready

issue: ServiceRequest creates when QR code scanned
This commit is contained in:
2024-10-14 05:24:48 +09:00
parent ee254ef17f
commit f17c6b3a1a
65 changed files with 1609 additions and 327 deletions

View File

@@ -52,6 +52,7 @@ services:
build: . build: .
container_name: django_app container_name: django_app
env_file: .env env_file: .env
restart: always
volumes: volumes:
- .:/app - .:/app
- ./wait-for-it.sh:/wait-for-it.sh - ./wait-for-it.sh:/wait-for-it.sh
@@ -62,6 +63,21 @@ services:
networks: networks:
- web_db_network - 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: volumes:
pgdata: pgdata:
pgadmin: pgadmin:

View File

@@ -1,8 +1,19 @@
anyio==4.6.0
asgiref==3.8.1 asgiref==3.8.1
certifi==2024.8.30
Django==5.1.1 Django==5.1.1
django-jazzmin==3.0.0 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 pillow==10.4.0
psycopg2-binary==2.9.9
python-decouple==3.8 python-decouple==3.8
python-telegram-bot==21.6
qrcode==8.0
sniffio==1.3.1
sqlparse==0.5.1 sqlparse==0.5.1
typing_extensions==4.12.2 typing_extensions==4.12.2
psycopg2-binary pyTelegramBotAPI

View File

View File

@@ -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')

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ComunicationConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'comunication'

View File

View File

@@ -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()

View File

@@ -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)),
],
),
]

View File

@@ -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)),
],
),
]

View File

@@ -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',
),
]

View File

@@ -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)),
],
),
]

View File

@@ -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='Телефон'),
),
]

View File

@@ -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='Клиент'),
),
]

View File

@@ -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})"

View File

@@ -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="Ваши данные для входа на сайт.")

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

View File

@@ -44,6 +44,7 @@ INSTALLED_APPS = [
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'web', 'web',
'comunication'
] ]
MIDDLEWARE = [ MIDDLEWARE = [

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,5 +1,5 @@
from django.contrib import admin 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 from .forms import ProjectForm
@admin.register(Service) @admin.register(Service)
@@ -41,3 +41,8 @@ class CategoryAdmin(admin.ModelAdmin):
list_display = ('name','description') list_display = ('name','description')
search_fields = ('name',) 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')

View File

@@ -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 django.db.models.deletion
import uuid
from django.db import migrations, models from django.db import migrations, models
@@ -27,6 +28,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)), ('name', models.CharField(max_length=100)),
('description', models.TextField(default='Описание категории')),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@@ -35,21 +37,33 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('first_name', models.CharField(max_length=100)), ('first_name', models.CharField(max_length=100)),
('last_name', models.CharField(max_length=100)), ('last_name', models.CharField(max_length=100)),
('email', models.EmailField(max_length=254)), ('email', models.EmailField(max_length=254, unique=True)),
('phone_number', models.CharField(max_length=15)), ('phone_number', models.CharField(max_length=15, unique=True)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/customer/')), ('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( migrations.CreateModel(
name='Project', name='Project',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)), ('name', models.CharField(max_length=200)),
('description', models.TextField()), ('description', models.TextField(default='Описание проекта')),
('completion_date', models.DateField()), ('completion_date', models.DateField(blank=True, null=True)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/project/')), ('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')), ('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( migrations.CreateModel(
@@ -57,10 +71,10 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)), ('name', models.CharField(max_length=200)),
('description', models.TextField()), ('description', models.TextField(default='Описание услуги')),
('price', models.DecimalField(decimal_places=2, max_digits=10)), ('price', models.DecimalField(decimal_places=2, max_digits=10)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/services/')), ('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( migrations.CreateModel(
@@ -72,17 +86,30 @@ class Migration(migrations.Migration):
('review_date', models.DateTimeField(auto_now_add=True)), ('review_date', models.DateTimeField(auto_now_add=True)),
('image', models.ImageField(blank=True, null=True, upload_to='static/img/review/')), ('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')), ('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')), ('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( migrations.CreateModel(
name='Order', name='ServiceRequest',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('order_date', models.DateTimeField(auto_now_add=True)), ('client_name', models.CharField(max_length=100)),
('status', models.CharField(choices=[('pending', 'Pending'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], max_length=50)), ('client_email', models.EmailField(max_length=254)),
('client', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.client')), ('client_phone', models.CharField(max_length=20)),
('service', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='web.service')), ('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')),
], ],
), ),
] ]

View File

@@ -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,
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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'),
),
]

View File

@@ -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'),
),
]

View File

@@ -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'),
),
]

View File

@@ -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),
),
]

View File

@@ -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),
),
]

View File

@@ -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,
),
]

View File

@@ -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',
),
]

View File

@@ -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'),
),
]

View File

@@ -1,21 +1,32 @@
# web/models.py
from django.db import models 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): class Category(models.Model):
name = models.CharField(max_length=100) 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): def __str__(self):
return self.name return self.name
class Service(models.Model): class Service(models.Model):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
description = models.TextField() description = models.TextField(default='Описание услуги')
price = models.DecimalField(max_digits=10, decimal_places=2) price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, related_name='services') 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) image = models.ImageField(upload_to='static/img/services/', blank=True, null=True)
class Meta:
verbose_name = 'Услуга'
verbose_name_plural = 'Услуги'
ordering = ['name']
def __str__(self): def __str__(self):
return self.name return self.name
@@ -29,14 +40,21 @@ class Service(models.Model):
return self.reviews.count() return self.reviews.count()
class Client(models.Model): 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) first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100)
email = models.EmailField() email = models.EmailField(unique=True)
phone_number = models.CharField(max_length=15) phone_number = models.CharField(max_length=15, unique=True)
image = models.ImageField(upload_to='static/img/customer/', blank=True, null=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): 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): class BlogPost(models.Model):
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
@@ -44,29 +62,59 @@ class BlogPost(models.Model):
published_date = models.DateTimeField(auto_now_add=True) published_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='static/img/blog/', blank=True, null=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): def __str__(self):
return self.title return self.title
class Order(models.Model): class Order(models.Model):
service = models.ForeignKey(Service, 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='orders') client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='related_orders')
message = models.TextField(blank=True, null=True) message = models.TextField(blank=True, null=True)
order_date = models.DateTimeField(auto_now_add=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): def __str__(self):
return f"Order #{self.id} by {self.client.first_name}" 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): class Project(models.Model):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
description = models.TextField() description = models.TextField(default='Описание проекта')
completion_date = models.DateField(blank=True, null=True) completion_date = models.DateField(blank=True, null=True)
client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='projects') client = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='projects')
service = models.ForeignKey(Service, 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) 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) 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): def __str__(self):
return self.name return self.name
@@ -80,19 +128,27 @@ class Review(models.Model):
review_date = models.DateTimeField(auto_now_add=True) review_date = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to='static/img/review/', blank=True, null=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): def __str__(self):
return f"Review by {self.client.first_name} {self.client.last_name} for {self.service.name}" return f"Review by {self.client.first_name} {self.client.last_name} for {self.service.name}"
COMMUNICATION_METHODS = [ class ServiceRequest(models.Model):
('email', 'Email'), service = models.ForeignKey(Service, on_delete=models.CASCADE)
('telegram', 'Telegram'), client_name = models.CharField(max_length=100)
('sms', 'SMS'), 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 User(AbstractUser): class Meta:
telegram_id = models.CharField(max_length=50, blank=True, null=True, unique=True) verbose_name = 'Заявка на услугу'
preferred_communication = models.CharField( verbose_name_plural = 'Заявки на услуги'
max_length=20, ordering = ['-created_at']
choices=COMMUNICATION_METHODS,
default='email', def __str__(self):
) return f"Request for {self.service.name} by {self.client_name}"

View File

@@ -0,0 +1,13 @@
{% extends 'web/base.html' %}
{% block content %}
<h2>Мои заказы</h2>
<ul>
{% for order in orders %}
<li>
<strong>Заказ №{{ order.id }}:</strong> {{ order.service.name }} - Статус: {{ order.get_status_display }}
<a href="{% url 'order_detail' order.pk %}">Подробнее</a>
</li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -0,0 +1,29 @@
{% extends 'web/base.html' %}
<!-- web/templates/web/complete_registration_basic.html -->
{% load static %}
{% include 'web/header.html' %}
{% block content %}
<div class="container-fluid">
<h1>Регистрация</h1>
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="client_name">Имя</label>
<input type="text" id="client_name" name="client_name" required>
</div>
<div class="form-group">
<label for="client_email">Email</label>
<input type="email" id="client_email" name="client_email" required>
</div>
<div class="form-group">
<label for="client_phone">Телефон</label>
<input type="tel" id="client_phone" name="client_phone" required>
</div>
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</form>
</div>
<!-- Подключение JavaScript файлов из static -->
<script src="{% static 'assets/js/script.min.js' %}"></script>
{% endblock%}

View File

@@ -0,0 +1,29 @@
{% extends 'web/base.html' %}
<!-- web/templates/web/complete_registration_basic.html -->
{% load static %}
{% include 'web/header.html' %}
{% block content %}
<div class="container-fluid">
<h1>Регистрация</h1>
<form method="post">
{% csrf_token %}
<div class="form-group">
<label for="client_name">Имя</label>
<input type="text" id="client_name" name="client_name" required>
</div>
<div class="form-group">
<label for="client_email">Email</label>
<input type="email" id="client_email" name="client_email" required>
</div>
<div class="form-group">
<label for="client_phone">Телефон</label>
<input type="tel" id="client_phone" name="client_phone" required>
</div>
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</form>
</div>
<!-- Подключение JavaScript файлов из static -->
<script src="{% static 'assets/js/script.min.js' %}"></script>
{% endblock%}

View File

@@ -0,0 +1,64 @@
<!-- web/templates/web/modal_order_form.html -->
<div id="orderModal" class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Оформление заявки на услугу</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="orderForm" method="post" action="{% url 'create_service_request' service_id=service.pk %}">
{% csrf_token %}
<div class="form-group mb-3">
<label class="form-label">Имя</label>
<input id="client_name" class="form-control" type="text" name="client_name" required minlength="2" maxlength="50" />
</div>
<div class="form-group mb-3">
<label class="form-label">Телефон</label>
<input id="client_phone" class="form-control" type="tel" name="client_phone" required pattern="^\+?[0-9\s\-]{7,15}$" />
</div>
<div class="form-group mb-3">
<label class="form-label">Адрес электронной почты</label>
<input id="client_email" class="form-control" type="email" name="client_email" required />
</div>
<div class="form-group mb-3 text-center">
<label class="form-label">Сканируйте QR код для регистрации в Telegram боте</label>
<div id="qrCodeContainer">
<img src="{{ qr_code }}" alt="QR код для Telegram бота" class="img-fluid" />
</div>
<div class="form-group mt-3">
<a href="{{ registration_link }}" target="_blank">Перейдите по этой ссылке для регистрации в Telegram боте</a>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" id="submitButton" disabled>Отправить заявку</button>
</div>
</form>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const checkVerificationStatus = () => {
fetch(`/service/request_status/{{ service_request_id }}/`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
if (data.is_verified) {
document.getElementById('submitButton').disabled = false;
}
})
.catch(error => console.error('Ошибка при проверке статуса:', error));
};
setInterval(checkVerificationStatus, 5000);
});
</script>

View File

@@ -1,36 +1,214 @@
<!-- web/templates/web/modal_order_form.html --> <!DOCTYPE html>
<div id="orderModal" class="modal fade" tabindex="-1"> <html lang="ru">
<div class="modal-dialog"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<title>Модальное окно для заявки на услугу</title>
<style>
/* Стили для модального окна */
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px;
}
.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
#qrCodeImg {
display: block;
margin: 20px auto;
width: 200px;
height: 200px;
}
</style>
</head>
<body>
<!-- Модальное окно -->
<div id="serviceModal" class="modal">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <span class="close">&times;</span>
<h5 class="modal-title">Оформление заявки на услугу</h5> <h4>Заполните заявку на услугу</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <p>QR-код для завершения регистрации:</p>
<img id="qrCodeImg" src="" alt="QR Code">
<form id="serviceRequestForm">
<div class="form-group">
<label for="clientName">Ваше имя:</label>
<input type="text" class="form-control" id="clientName" name="client_name" placeholder="Введите ваше имя" required minlength="2" maxlength="50" readonly>
</div> </div>
<div class="modal-body"> <div class="form-group">
<form id="orderForm" method="post" action="{% url 'create_order' pk=service.pk %}"> <label for="clientChatId">Ваш chat ID:</label>
{% csrf_token %} <input type="text" class="form-control" id="clientChatId" name="client_chat_id" placeholder="Ваш chat ID" readonly>
<div class="form-group mb-3">
<label class="form-label">Имя</label>
<input id="client_name" class="form-control" type="text" name="client_name" required minlength="2" maxlength="50" />
</div> </div>
<div class="form-group mb-3"> <div class="form-group">
<label class="form-label">Телефон</label> <label for="clientEmail">Ваш email:</label>
<input id="client_phone" class="form-control" type="tel" name="client_phone" required pattern="^\+?[0-9\s\-]{7,15}$" /> <input type="email" class="form-control" id="clientEmail" name="client_email" placeholder="Введите ваш email" required>
</div> </div>
<div class="form-group mb-3"> <div class="form-group">
<label class="form-label">Адрес электронной почты</label> <label for="clientPhone">Ваш телефон:</label>
<input id="client_email" class="form-control" type="email" name="client_email" required /> <input type="text" class="form-control" id="clientPhone" name="client_phone" placeholder="Введите ваш телефон" required pattern="^\+?[0-9\s\-]{7,15}$">
</div> </div>
<div class="form-group mb-3"> <div class="form-group">
<label class="form-label">Описание услуги</label> <label for="description">Описание заявки:</label>
<textarea id="message" class="form-control" name="message" rows="4" required minlength="10" maxlength="1000"></textarea> <textarea class="form-control" id="description" name="description" placeholder="Опишите вашу заявку" required></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Отправить заявку</button>
</div> </div>
<button type="submit" class="btn btn-success" id="submitButton" disabled>Отправить заявку</button>
</form> </form>
</div> </div>
</div> </div>
</div>
</div> <script>
// Обработчик открытия модального окна
document.getElementById('openModalBtn').addEventListener('click', function () {
const serviceId = this.getAttribute('data-service-id');
// Открываем модальное окно
document.getElementById('serviceModal').style.display = 'block';
// Выполняем запрос на генерацию QR-кода
fetch(`/service/generate_qr_code/${serviceId}/`)
.then(response => response.json())
.then(data => {
// Обновляем src изображения QR-кода
document.getElementById('qrCodeImg').src = data.qr_code_url;
// Запуск проверки статуса каждые 5 секунд
const interval = setInterval(() => {
checkVerificationStatus(data.service_request_id, interval);
}, 5000);
})
.catch(error => console.error('Ошибка при генерации QR-кода:', error));
});
// Обработчик закрытия модального окна
document.querySelector('.close').addEventListener('click', function () {
document.getElementById('serviceModal').style.display = 'none';
});
// Обработчик отправки формы
document.getElementById('serviceRequestForm').addEventListener('submit', function (event) {
event.preventDefault();
const formData = new FormData(this);
const serviceId = document.getElementById('openModalBtn').getAttribute('data-service-id');
// Отправка данных формы на сервер
fetch('/service/request/' + serviceId + '/', {
method: 'POST',
body: formData
}).then(response => {
if (response.ok) {
alert('Заявка успешно отправлена!');
document.getElementById('serviceModal').style.display = 'none';
// Отправка сообщения в Telegram
sendTelegramNotification(formData);
} else {
alert('Ошибка при отправке заявки. Пожалуйста, попробуйте снова.');
}
}).catch(error => console.error('Ошибка при отправке данных формы:', error));
});
// Проверка статуса заявки на наличие подтверждения Telegram
const checkVerificationStatus = (serviceRequestId, interval) => {
fetch(`/service/request_status/${serviceRequestId}/`)
.then(response => response.json())
.then(data => {
if (data.is_verified) {
// Заполнение полей формы данными пользователя
document.getElementById('clientName').value = data.client_name;
document.getElementById('clientChatId').value = data.client_chat_id;
// Активируем кнопку отправки, если все поля заполнены
updateButtonState();
// Останавливаем интервал проверки статуса
clearInterval(interval);
}
})
.catch(error => console.error('Ошибка при проверке статуса заявки:', error));
};
// Код для активации кнопки "Отправить" при заполнении всех полей
const clientEmail = document.getElementById('clientEmail');
const clientPhone = document.getElementById('clientPhone');
const clientName = document.getElementById('clientName');
const description = document.getElementById('description');
const clientChatId = document.getElementById('clientChatId');
const submitButton = document.getElementById('submitButton');
const updateButtonState = () => {
if (clientEmail.value && clientPhone.value && clientName.value && description.value && clientChatId.value) {
submitButton.disabled = false;
} else {
submitButton.disabled = true;
}
};
// Привязка событий к полям для обновления состояния кнопки отправки
clientEmail.addEventListener('input', updateButtonState);
clientPhone.addEventListener('input', updateButtonState);
description.addEventListener('input', updateButtonState);
// Удаление placeholder при установке фокуса на поле
document.querySelectorAll('input, textarea').forEach(field => {
field.addEventListener('focus', function () {
this.dataset.placeholder = this.placeholder;
this.placeholder = '';
});
field.addEventListener('blur', function () {
this.placeholder = this.dataset.placeholder;
});
});
// Функция для отправки уведомления в Telegram
const sendTelegramNotification = (formData) => {
const clientName = formData.get('client_name');
const serviceDescription = formData.get('description');
const chatId = formData.get('client_chat_id');
const message = `Здравствуйте, ${clientName}! Ваша заявка успешно зарегистрирована. Детали: ${serviceDescription}`;
fetch('/service/send_telegram_notification/', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ chat_id: chatId, message: message })
}).then(response => {
if (response.ok) {
console.log('Уведомление успешно отправлено в Telegram');
} else {
console.error('Ошибка при отправке уведомления в Telegram');
}
}).catch(error => console.error('Ошибка при отправке уведомления в Telegram:', error));
};
</script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,161 @@
<!-- web/templates/web/modal_order_form.html -->
<div id="orderModal" class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Оформление заявки на услугу</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="orderForm" method="post" action="{% url 'create_service_request' service_id=service.pk %}">
{% csrf_token %}
<div class="form-group mb-3">
<label class="form-label">Имя</label>
<input id="client_name" class="form-control" type="text" name="client_name" required minlength="2" maxlength="50" />
</div>
<div class="form-group mb-3">
<label class="form-label">Телефон</label>
<input id="client_phone" class="form-control" type="tel" name="client_phone" required pattern="^\+?[0-9\s\-]{7,15}$" />
</div>
<div class="form-group mb-3">
<label class="form-label">Адрес электронной почты</label>
<input id="client_email" class="form-control" type="email" name="client_email" required />
</div>
<div class="form-group mb-3 text-center">
<label class="form-label">Сканируйте QR код для регистрации в Telegram боте</label>
<div id="qrCodeContainer">
<img id="qrCodeImage" src="{{ qr_code_url }}" alt="QR код для Telegram бота" class="img-fluid" style="max-width: 150px;" />
</div>
<div class="form-group mt-3">
<a id="registrationLink" href="#" target="_blank">Перейдите по этой ссылке для регистрации в Telegram боте</a>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary" id="submitButton" disabled>Отправить заявку</button>
</div>
</form>
{% comment %} <div id="diagnostic_info" class="mt-3">
<form id="registrationForm">
<h3>Диагностическая информация</h3>
<div class="form-group mb-3">
<label for="registrationLinkField" class="form-label">Ссылка для регистрации в Telegram боте:</label>
<input type="text" id="registrationLinkField" class="form-control" readonly />
</div>
<div class="form-group mb-3">
<label for="requestIdField" class="form-label">Номер заявки:</label>
<input type="text" id="requestIdField" class="form-control" readonly />
</div>
</form>
</div> {% endcomment %}
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('registrationForm');
const submitButton = document.getElementById('submitButton');
const qrCodeImage = document.getElementById('qrCodeImage');
const registrationLink = document.getElementById('registrationLink');
let serviceId = "{{ service.pk }}"; // Получаем значение serviceId из шаблона
// Генерация QR-кода при открытии формы
fetch(`/service/generate_qr_code/${serviceId}/`)
.then(response => response.json())
.then(data => {
qrCodeImage.src = data.qr_code_url;
registrationLink.href = data.registration_link;
// Извлекаем номер заявки из ссылки на регистрацию
const requestMatch = data.registration_link.match(/request_(\d+)_token/);
if (requestMatch) {
serviceId = requestMatch[1];
}
// Отображение диагностической информации на форме
//document.getElementById('registrationLinkField').value = data.registration_link;
//document.getElementById('requestIdField').value = serviceId;
// Запуск проверки статуса каждые 5 секунд
const interval = setInterval(() => {
checkVerificationStatus(serviceId, interval);
}, 5000);
})
.catch(error => console.error('Ошибка при генерации QR-кода:', error));
const checkVerificationStatus = (serviceId, interval) => {
fetch(`/service/request_status/${serviceId}/`)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
if (data.is_verified) {
// Заполнение полей формы данными пользователя
document.getElementById('client_name').value = data.client_name;
document.getElementById('client_email').value = data.client_email;
document.getElementById('client_phone').value = data.client_phone;
// Активируем кнопку отправки, если поля заполнены
updateButtonState();
// Останавливаем интервал проверки статуса
clearInterval(interval);
}
})
.catch(error => console.error('Ошибка при проверке статуса:', error));
};
// Обработчик кнопки отправки формы
submitButton.addEventListener('click', function () {
if (submitButton.disabled) {
return;
}
// Проверка наличия данных в таблице ServiceRequest перед отправкой формы
fetch(`/service/check_service_request_data/?request_id=${serviceId}`)
.then(response => response.json())
.then(data => {
if (data.exists) {
// Обратная связь пользователю
alert('Заявка уже существует. Данные будут обновлены.');
// Заполнение формы данными из заявки
document.getElementById('client_name').value = data.client_name;
document.getElementById('client_email').value = data.client_email;
document.getElementById('client_phone').value = data.client_phone;
// Отправка формы
form.submit();
} else {
// Если данных нет, отправляем форму как новую заявку
form.submit();
}
})
.catch(error => {
console.error('Ошибка при проверке данных заявки:', error);
alert('Произошла ошибка при проверке данных. Пожалуйста, попробуйте еще раз.');
});
});
// Код для активации кнопки "Отправить" при заполнении полей телефона и email
const clientEmail = document.getElementById('client_email');
const clientPhone = document.getElementById('client_phone');
const updateButtonState = () => {
if (clientEmail.value && clientPhone.value) {
submitButton.disabled = false;
} else {
submitButton.disabled = true;
}
};
// Привязка событий к полям email и телефона
clientEmail.addEventListener('input', updateButtonState);
clientPhone.addEventListener('input', updateButtonState);
});
</script>

View File

@@ -0,0 +1,14 @@
{% extends 'web/base.html' %}
{% block content %}
<h2>Детали заказа</h2>
<p><strong>Номер заказа:</strong> {{ order.id }}</p>
<p><strong>Услуга:</strong> {{ order.service.name }}</p>
<p><strong>Клиент:</strong> {{ order.client.first_name }} {{ order.client.last_name }}</p>
<p><strong>Сообщение:</strong> {{ order.message }}</p>
<p><strong>Статус:</strong> {{ order.get_status_display }}</p>
<p><strong>Дата создания:</strong> {{ order.order_date }}</p>
<a href="{% url 'home' %}">Вернуться на главную</a>
{% endblock %}

View File

@@ -14,7 +14,11 @@
<div style="max-width: 350px;"> <div style="max-width: 350px;">
<h2 class="text-uppercase fw-bold">{{ service.name }}<br /></h2> <h2 class="text-uppercase fw-bold">{{ service.name }}<br /></h2>
<p class="my-3">{{ service.description }}</p> <p class="my-3">{{ service.description }}</p>
<a class="btn btn-primary btn-lg me-2" role="button" href="#" data-bs-toggle="modal" data-bs-target="#orderModal">Order Service</a> <button id="orderButton" data-service-id="{{ service.pk }}" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#orderModal">Заказать услугу</button>
<!-- Кнопка открытия модального окна -->
<button id="openModalBtn" class="btn btn-primary" data-service-id="{{ service.id }}">Открыть заявку на услугу</button>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,10 @@
{% extends 'web/base.html' %}
{% block content %}
<div class="container mt-4">
<h2>Заявка успешно создана</h2>
<p>Спасибо, ваша заявка на услугу "{{ service_request.service.name }}" успешно создана.</p>
<p>Вы также можете перейти к своему заказу ниже:</p>
<a href="{% url 'order_detail' order.pk %}" class="btn btn-primary">Перейти к заказу</a>
</div>
{% endblock %}

View File

@@ -11,8 +11,17 @@ urlpatterns = [
path('client/<int:pk>/', views.client_detail, name='client_detail'), path('client/<int:pk>/', views.client_detail, name='client_detail'),
path('blog/<int:pk>/', views.blog_post_detail, name='blog_post_detail'), path('blog/<int:pk>/', views.blog_post_detail, name='blog_post_detail'),
path('services/', views.services_view, name='services'), path('services/', views.services_view, name='services'),
path('create_order/<int:pk>/', views.create_order, name='create_order'), # path('create_order/<int:pk>/', views.create_order, name='create_order'),
path('about/', views.about_view, name="about_view"), path('about/', views.about_view, name="about_view"),
path('service/generate_qr_code/<int:service_id>/', views.generate_qr_code, name='generate_qr_code'),
path('service/request_status/<int:service_id>/', views.request_status, name='request_status'),
path('service/request/<int:service_id>/', views.create_service_request, name='create_service_request'),
path('complete_registration/<int:request_id>/', views.complete_registration, name='complete_registration'),
path('complete_registration/', views.complete_registration_basic, name='complete_registration_basic'),
path('service/check_service_request_data/', views.check_service_request_data, name='check_service_request_data'),
path('client/orders/', views.client_orders, name='client_orders'),
path('order/<int:pk>/', views.order_detail, name='order_detail'),
path('service/send_telegram_notification/', views.send_telegram_notification, name='send_telegram_notification'),
] ]
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View File

@@ -1,7 +1,31 @@
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from .models import Service, Project, Client, BlogPost, Review, Order from .models import Service, Project, Client, BlogPost, Review, Order, ServiceRequest
from django.db.models import Avg from django.db.models import Avg
from comunication.models import TelegramSettings
import qrcode
import os
from django.conf import settings
import uuid
from django.utils.http import urlsafe_base64_encode
from django.utils.encoding import force_bytes
from django.http import JsonResponse
from django.utils.crypto import get_random_string # Импорт get_random_string
from django.contrib.auth.models import User
from decouple import config
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.core.mail import send_mail
from comunication.telegram_bot import TelegramBot
import hmac
import hashlib
import json
from django.views.decorators.csrf import csrf_exempt
# sens
try:
bot = TelegramBot()
except Exception as e:
print (e)
def home(request): def home(request):
services = Service.objects.all() services = Service.objects.all()
@@ -37,60 +61,236 @@ def services_view(request):
services = Service.objects.all() services = Service.objects.all()
return render(request, 'web/services.html', {'services': services}) return render(request, 'web/services.html', {'services': services})
# def create_order(request, pk): def about_view(request):
# if request.method == 'POST': return render(request, 'web/about.html')
# service = get_object_or_404(Service, pk=pk)
# client_name = request.POST.get('client_name')
# client_email = request.POST.get('client_email')
# client_phone = request.POST.get('client_phone')
# message = request.POST.get('message')
# # Создаем клиента, если он не существует def create_service_request(request, service_id):
# client, created = Client.objects.get_or_create( service = get_object_or_404(Service, pk=service_id)
# email=client_email,
# defaults={'first_name': client_name, 'phone_number': client_phone}
# )
# # Создаем новый заказ
# order = Order(
# service=service,
# client=client,
# client_email=client.email,
# client_phone=client.phone_number,
# message=message,
# )
# order.save()
# # Редирект на страницу подтверждения или обратно к услуге
# return redirect('service_detail', pk=pk)
def create_order(request, pk):
if request.method == 'POST': if request.method == 'POST':
service = get_object_or_404(Service, pk=pk)
client_name = request.POST.get('client_name') client_name = request.POST.get('client_name')
client_email = request.POST.get('client_email') client_email = request.POST.get('client_email')
client_phone = request.POST.get('client_phone') client_phone = request.POST.get('client_phone')
message = request.POST.get('message') description = request.POST.get('description') # New description field
chat_id = request.POST.get('chat_id')
token = uuid.uuid4().hex
# Создаем клиента, если он не существует # Check for existing service request
service_request = ServiceRequest.objects.filter(client_email=client_email, client_phone=client_phone).first()
if not service_request:
# Create a new service request
service_request = ServiceRequest.objects.create(
service=service,
client_name=client_name,
client_email=client_email,
client_phone=client_phone,
chat_id=chat_id,
token=token
)
# Generate user credentials
username = f"{client_email.split('@')[0]}_{get_random_string(5)}"
password = get_random_string(8)
# Create a new user
user = User.objects.create_user(username=username, password=password)
user.first_name = client_name.split()[0] if client_name else ""
user.last_name = client_name.split()[-1] if len(client_name.split()) > 1 else ""
user.email = client_email
user.save()
# Create or get client
client, created = Client.objects.get_or_create( client, created = Client.objects.get_or_create(
email=client_email, email=client_email,
defaults={'first_name': client_name, 'phone_number': client_phone} defaults={
'first_name': user.first_name,
'last_name': user.last_name,
'phone_number': client_phone,
}
) )
# Создаем новый заказ # Create an order linked to the service request and client
order = Order( order = Order.objects.create(
service=service, service=service,
client=client, client=client,
message=message, message=description,
status="pending"
) )
order.save()
# Редирект на страницу подтверждения или обратно к услуге # Send credentials via Telegram
return redirect('service_detail', pk=pk) if chat_id:
bot.send_telegram_message(client.id, service_request.id, "Ваши данные для входа на сайт.", order.id)
else:
# If service request exists, update the chat_id
service_request.chat_id = chat_id
service_request.save()
return redirect(reverse('order_detail', args=[order.pk]))
def generate_qr_code(request, service_id):
service = get_object_or_404(Service, pk=service_id)
telegram_settings = get_object_or_404(TelegramSettings, pk=1)
token = uuid.uuid4().hex
# Создание новой заявки на услугу
service_request = ServiceRequest.objects.create(
service=service,
client_name='',
client_email='',
client_phone='',
token=token
)
# Генерация ссылки для регистрации в Telegram
registration_link = (f'https://t.me/{telegram_settings.bot_name}?start=request_{service_request.id}_token_{urlsafe_base64_encode(force_bytes(token))}'
)
# Генерация QR-кода
qr = qrcode.make(registration_link)
qr_code_dir = os.path.join(settings.STATICFILES_DIRS[0], 'qr_codes')
qr_code_path = os.path.join(qr_code_dir, f"request_{service_request.id}.png")
external_qr_link = f'static/qr_codes/request_{service_request.id}.png'
if not os.path.exists(qr_code_dir):
os.makedirs(qr_code_dir)
qr.save(qr_code_path)
return JsonResponse({
'registration_link': registration_link,
'qr_code_url': f"/{external_qr_link}",
'service_request_id': service_request.id
})
def complete_registration(request, request_id):
# Завершение регистрации по идентификатору заявки
service_request = get_object_or_404(ServiceRequest, pk=request_id)
if request.method == 'POST':
client_name = request.POST.get('client_name', service_request.client_name)
client_email = request.POST.get('client_email', service_request.client_email)
client_phone = request.POST.get('client_phone', service_request.client_phone)
chat_id = request.POST.get('chat_id', service_request.chat_id)
# Обновляем данные заявки
service_request.client_name = client_name
service_request.client_email = client_email
service_request.client_phone = client_phone
service_request.chat_id = chat_id
service_request.save()
return redirect('home')
return render(request, 'web/complete_registration.html', {'service_request': service_request})
def request_status(request, service_id):
# Проверяем статус заявки на услугу
service_request = get_object_or_404(ServiceRequest, pk=service_id)
is_verified = service_request.client_name != '' and service_request.chat_id != ''
return JsonResponse({
'is_verified': is_verified,
'client_name': service_request.client_name,
'client_chat_id' : service_request.chat_id,
})
def complete_registration_basic(request):
# Базовая регистрация без идентификатора заявки
if request.method == 'POST':
client_name = request.POST.get('client_name')
client_email = request.POST.get('client_email')
client_phone = request.POST.get('client_phone')
# # Создаем новую запись заявки
# service_request = ServiceRequest.objects.create(
# client_name=client_name,
# client_email=client_email,
# client_phone=client_phone
# )
return redirect('home')
return render(request, 'web/complete_registration_basic.html')
def check_service_request_data(request, token=None, request_id=None):
# Проверка наличия данных в таблице ServiceRequest по токену или номеру заявки
service_request = None
if token:
try:
service_request = ServiceRequest.objects.get(token=token)
except ServiceRequest.DoesNotExist:
service_request = None
elif request_id:
try:
service_request = ServiceRequest.objects.get(id=request_id)
except ServiceRequest.DoesNotExist:
service_request = None
if service_request:
return JsonResponse({
'exists': True,
'client_name': service_request.client_name,
'client_email': service_request.client_email,
'client_phone': service_request.client_phone,
'chat_id': service_request.chat_id
})
else:
return JsonResponse({'exists': False})
def order_detail(request, pk):
order = get_object_or_404(Order, pk=pk)
return render(request, 'web/order_detail.html', {'order': order})
@login_required
def client_orders(request):
client = request.user.client_profile
orders = client.related_orders.all()
return render(request, 'web/client_orders.html', {'orders': orders})
def about_view(request): def generate_secure_token(service_request_id, secret_key):
return render(request, 'web/about.html') """Генерация безопасного токена для подтверждения подлинности запроса."""
data = f'{service_request_id}:{secret_key}'
return hmac.new(secret_key.encode(), data.encode(), hashlib.sha256).hexdigest()
@csrf_exempt
def send_telegram_notification(request):
if request.method == 'POST':
try:
data = json.loads(request.body)
service_request_id = data.get('service_request_id')
provided_token = data.get('token')
# Проверка корректности переданных данных
if not service_request_id or not provided_token:
return JsonResponse({'error': 'Недостаточно данных для подтверждения'}, status=400)
# Получение заявки
service_request = ServiceRequest.objects.filter(id=service_request_id).first()
if not service_request:
return JsonResponse({'error': 'Заявка не найдена'}, status=404)
# Генерация токена и сравнение
secret_key = settings.SECRET_KEY # Используем секретный ключ из настроек
expected_token = generate_secure_token(service_request_id, secret_key)
if not hmac.compare_digest(provided_token, expected_token):
return JsonResponse({'error': 'Неверный токен. Доступ запрещен.'}, status=403)
# Отправка сообщения в Telegram
chat_id = service_request.chat_id
if not chat_id:
return JsonResponse({'error': 'Нет chat_id для отправки сообщения'}, status=400)
message = (
f"Здравствуйте, {service_request.client_name}!\n"
f"Ваша заявка на услугу '{service_request.service.name}' успешно зарегистрирована."
)
bot.send_telegram_message(chat_id=chat_id, message=message)
return JsonResponse({'status': 'Уведомление успешно отправлено в Telegram'})
except Exception as e:
return JsonResponse({'error': str(e)}, status=500)
return JsonResponse({'error': 'Метод запроса должен быть POST'}, status=405)