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

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

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

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.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',
)
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}"

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 -->
<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>
<!DOCTYPE html>
<html lang="ru">
<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">
<span class="close">&times;</span>
<h4>Заполните заявку на услугу</h4>
<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 class="modal-body">
<form id="orderForm" method="post" action="{% url 'create_order' pk=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">
<label class="form-label">Описание услуги</label>
<textarea id="message" class="form-control" name="message" rows="4" required minlength="10" maxlength="1000"></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>
</form>
<div class="form-group">
<label for="clientChatId">Ваш chat ID:</label>
<input type="text" class="form-control" id="clientChatId" name="client_chat_id" placeholder="Ваш chat ID" readonly>
</div>
</div>
<div class="form-group">
<label for="clientEmail">Ваш email:</label>
<input type="email" class="form-control" id="clientEmail" name="client_email" placeholder="Введите ваш email" required>
</div>
<div class="form-group">
<label for="clientPhone">Ваш телефон:</label>
<input type="text" class="form-control" id="clientPhone" name="client_phone" placeholder="Введите ваш телефон" required pattern="^\+?[0-9\s\-]{7,15}$">
</div>
<div class="form-group">
<label for="description">Описание заявки:</label>
<textarea class="form-control" id="description" name="description" placeholder="Опишите вашу заявку" required></textarea>
</div>
<button type="submit" class="btn btn-success" id="submitButton" disabled>Отправить заявку</button>
</form>
</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;">
<h2 class="text-uppercase fw-bold">{{ service.name }}<br /></h2>
<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>

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('blog/<int:pk>/', views.blog_post_detail, name='blog_post_detail'),
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('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:
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 .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 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):
services = Service.objects.all()
@@ -37,60 +61,236 @@ def services_view(request):
services = Service.objects.all()
return render(request, 'web/services.html', {'services': services})
# def create_order(request, pk):
# if request.method == 'POST':
# 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 about_view(request):
return render(request, 'web/about.html')
# # Создаем клиента, если он не существует
# client, created = Client.objects.get_or_create(
# 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):
def create_service_request(request, service_id):
service = get_object_or_404(Service, pk=service_id)
if request.method == 'POST':
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')
description = request.POST.get('description') # New description field
chat_id = request.POST.get('chat_id')
token = uuid.uuid4().hex
# Создаем клиента, если он не существует
client, created = Client.objects.get_or_create(
email=client_email,
defaults={'first_name': client_name, 'phone_number': client_phone}
)
# Check for existing service request
service_request = ServiceRequest.objects.filter(client_email=client_email, client_phone=client_phone).first()
# Создаем новый заказ
order = Order(
service=service,
client=client,
message=message,
)
order.save()
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
)
# Редирект на страницу подтверждения или обратно к услуге
return redirect('service_detail', pk=pk)
def about_view(request):
return render(request, 'web/about.html')
# 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(
email=client_email,
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.objects.create(
service=service,
client=client,
message=description,
status="pending"
)
# Send credentials via Telegram
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 generate_secure_token(service_request_id, secret_key):
"""Генерация безопасного токена для подтверждения подлинности запроса."""
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)