Добавил оповещение о новой заявке на почту

This commit is contained in:
NikDizell 2025-11-14 13:20:44 +03:00
parent c3228a8baa
commit fae45abcee
15 changed files with 310 additions and 0 deletions

View File

@ -137,3 +137,25 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
# Настройки email
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# EMAIL_HOST = 'smtp.yandex.ru' # или smtp.gmail.com, smtp.mail.ru
# EMAIL_PORT = 587
# EMAIL_USE_TLS = True
# EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', 'it@yandex.ru')
# EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', 'tifdctkrcjcqwxyc')
# DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
# SERVER_EMAIL = EMAIL_HOST_USER
EMAIL_HOST = 'smtp.gmail.com' # или smtp.gmail.com, smtp.mail.ru
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', 'nikdizell@gmail.com')
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', 'qvmw yccb msqv mmpj')
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
SERVER_EMAIL = EMAIL_HOST_USER
# Email для уведомлений (можно указать несколько через запятую)
# ADMIN_EMAILS = os.getenv('ADMIN_EMAILS', 'nikdizell@gmail.com').split(',')
ADMIN_EMAILS = os.getenv('ADMIN_EMAILS', 'it@nserdyuk.ru').split(',')

View File

@ -49,6 +49,18 @@ class CallbackAdmin(admin.ModelAdmin):
search_fields = ('name', 'phone', 'email')
readonly_fields = ('time_create',)
actions = ['mark_as_read', 'mark_as_unread', 'mark_as_processed']
actions = ['mark_as_read', 'mark_as_unread', 'mark_as_processed', 'resend_notification']
def resend_notification(self, request, queryset):
from .utils.email_notifications import send_callback_notification
count = 0
for callback in queryset:
success = send_callback_notification(callback)
if success:
count += 1
self.message_user(request, f'Уведомления отправлены для {count} заявок')
resend_notification.short_description = "Переотправить email уведомления"
def new_badge(self, obj):
if not obj.is_read:

View File

@ -0,0 +1,33 @@
# programmer/management/commands/send_daily_summary.py
from django.core.management.base import BaseCommand
from django.utils import timezone
from programmer.utils.email_notifications import send_daily_summary
class Command(BaseCommand):
help = 'Отправляет ежедневную сводку по заявкам на email'
def add_arguments(self, parser):
parser.add_argument(
'--test',
action='store_true',
help='Тестовая отправка (не учитывает реальные данные)',
)
def handle(self, *args, **options):
if options['test']:
self.stdout.write(self.style.WARNING('Тестовая отправка ежедневной сводки...'))
# Здесь можно добавить тестовые данные
else:
self.stdout.write('Отправка ежедневной сводки...')
success = send_daily_summary()
if success:
self.stdout.write(
self.style.SUCCESS(f'Ежедневная сводка отправлена успешно! Время: {timezone.now()}')
)
else:
self.stdout.write(
self.style.WARNING('Ежедневная сводка не отправлена (нет данных или ошибка)')
)

View File

@ -0,0 +1,29 @@
# programmer/management/commands/test_email.py
from django.core.management.base import BaseCommand
from django.conf import settings
from programmer.utils.email_notifications import send_test_email
class Command(BaseCommand):
help = 'Test email configuration'
def handle(self, *args, **options):
self.stdout.write("Testing email configuration...")
# Проверяем настройки
self.stdout.write(f"EMAIL_HOST: {settings.EMAIL_HOST}")
self.stdout.write(f"EMAIL_PORT: {settings.EMAIL_PORT}")
self.stdout.write(f"EMAIL_HOST_USER: {settings.EMAIL_HOST_USER}")
self.stdout.write(f"ADMIN_EMAILS: {settings.ADMIN_EMAILS}")
# Тестируем отправку
success = send_test_email()
if success:
self.stdout.write(
self.style.SUCCESS('✅ Test email sent successfully!')
)
else:
self.stdout.write(
self.style.ERROR('❌ Failed to send test email. Check your email settings.')
)

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.7 on 2025-11-14 10:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('programmer', '0012_callbackrequest_is_read'),
]
operations = [
migrations.AddField(
model_name='callbackrequest',
name='notification_sent',
field=models.BooleanField(default=False, verbose_name='Уведомление отправлено'),
),
]

View File

@ -4,6 +4,7 @@ from django.utils import timezone
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.core.cache import cache
from .utils.email_notifications import send_callback_notification
class Recall(models.Model):
@ -95,6 +96,7 @@ class CallbackRequest(models.Model):
time_create = models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')
is_processed = models.BooleanField(default=False, verbose_name='Обработано')
is_read = models.BooleanField(default=False, verbose_name='Прочитано')
notification_sent = models.BooleanField(default=False, verbose_name='Уведомление отправлено')
def __str__(self):
return f"{self.name} - {self.phone}"
@ -105,6 +107,18 @@ class CallbackRequest(models.Model):
ordering = ['-time_create']
# Сигнал для отправки уведомления при создании заявки
@receiver(post_save, sender=CallbackRequest)
def send_callback_email_notification(sender, instance, created, **kwargs):
if created and not instance.notification_sent:
# Отправляем email уведомление
success = send_callback_notification(instance)
if success:
instance.notification_sent = True
# Сохраняем без повторного вызова сигнала
sender.objects.filter(pk=instance.pk).update(notification_sent=True)
class PageView(models.Model):
url = models.CharField(max_length=500)
timestamp = models.DateTimeField(default=timezone.now)

View File

@ -0,0 +1,57 @@
<!-- templates/emails/callback_notification.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #FF6B00 0%, #0055A5 100%); color: white; padding: 20px; text-align: center; border-radius: 10px 10px 0 0; }
.content { background: #f9f9f9; padding: 20px; border-radius: 0 0 10px 10px; }
.alert { background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 15px 0; }
.info-box { background: white; padding: 15px; border-radius: 5px; margin: 10px 0; border-left: 4px solid #007bff; }
.btn { display: inline-block; background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; margin: 10px 0; }
.footer { text-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚨 Новая заявка на сайте</h1>
<p>Требуется ваше внимание!</p>
</div>
<div class="content">
<div class="alert">
<strong>⚠️ Срочно!</strong> Пользователь оставил заявку на обратный звонок.
</div>
<div class="info-box">
<h3>📋 Информация о заявке:</h3>
<p><strong>👤 Имя:</strong> {{ callback.name }}</p>
<p><strong>📞 Телефон:</strong> {{ callback.phone }}</p>
{% if callback.email %}
<p><strong>📧 Email:</strong> {{ callback.email }}</p>
{% endif %}
{% if callback.question %}
<p><strong>❓ Вопрос:</strong><br>{{ callback.question }}</p>
{% endif %}
<p><strong>🕒 Время отправки:</strong> {{ callback.time_create|date:"d.m.Y H:i" }}</p>
</div>
<div style="text-align: center; margin: 20px 0;">
<a href="http://{{ site_url }}/admin/programmer/callbackrequest/" class="btn">
📋 Перейти к заявкам в админке
</a>
</div>
<p><em>Не забудьте отметить заявку как обработанную после связи с клиентом!</em></p>
</div>
<div class="footer">
<p>Это автоматическое уведомление от системы сайта.</p>
<p>Если вы получили это письмо по ошибке, пожалуйста, свяжитесь с администратором.</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,60 @@
<!-- templates/emails/daily_summary.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #28a745 0%, #20c997 100%); color: white; padding: 20px; text-align: center; border-radius: 10px 10px 0 0; }
.content { background: #f9f9f9; padding: 20px; border-radius: 0 0 10px 10px; }
.stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin: 20px 0; }
.stat-card { background: white; padding: 15px; border-radius: 8px; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.stat-number { font-size: 2rem; font-weight: bold; margin: 0; }
.alert { background: #fff3cd; border: 1px solid #ffeaa7; padding: 15px; border-radius: 5px; margin: 15px 0; }
.btn { display: inline-block; background: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 5px; margin: 10px 0; }
.footer { text-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; font-size: 12px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📊 Ежедневная сводка</h1>
<p>Статистика заявок за {{ yesterday|date:"d.m.Y" }}</p>
</div>
<div class="content">
<div class="stats-grid">
<div class="stat-card">
<h3>📅 Вчерашние заявки</h3>
<p class="stat-number" style="color: #28a745;">{{ yesterday_callbacks }}</p>
</div>
<div class="stat-card">
<h3>⏳ Ожидают обработки</h3>
<p class="stat-number" style="color: #ffc107;">{{ unprocessed_callbacks }}</p>
</div>
</div>
{% if unprocessed_callbacks > 0 %}
<div class="alert">
<strong>⚠️ Внимание!</strong> У вас есть {{ unprocessed_callbacks }} необработанных заявок.
</div>
{% endif %}
<div style="text-align: center; margin: 20px 0;">
<a href="http://{{ site_url }}/admin/programmer/callbackrequest/" class="btn">
📋 Управление заявками
</a>
</div>
<p><em>Не забудьте обработать все pending заявки!</em></p>
</div>
<div class="footer">
<p>Ежедневная автоматическая сводка от системы сайта.</p>
<p>Дата формирования: {{ today|date:"d.m.Y H:i" }}</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,65 @@
# programmer/utils/email_notifications.py
from django.core.mail import send_mail, EmailMultiAlternatives
from django.template.loader import render_to_string
from django.conf import settings
from django.utils.html import strip_tags
import logging
logger = logging.getLogger(__name__)
def send_callback_notification(callback_request):
"""
Отправляет уведомление о новой заявке на обратный звонок
"""
try:
subject = f'🚨 Новая заявка на обратный звонок от {callback_request.name}'
# HTML версия письма
html_message = render_to_string('emails/callback_notification.html', {
'callback': callback_request,
'site_url': settings.ALLOWED_HOSTS[0] if settings.ALLOWED_HOSTS else 'localhost',
})
# Текстовая версия письма
plain_message = strip_tags(html_message)
# Проверяем настройки email
if not all([settings.EMAIL_HOST_USER, settings.EMAIL_HOST_PASSWORD]):
logger.error("Email settings are not configured properly")
return False
# Отправляем email
send_mail(
subject=subject,
message=plain_message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=settings.ADMIN_EMAILS,
html_message=html_message,
fail_silently=False,
)
logger.info(f"Email notification sent successfully for callback #{callback_request.id}")
return True
except Exception as e:
logger.error(f"Error sending email notification: {e}")
return False
def send_test_email():
"""
Функция для тестирования отправки email
"""
try:
send_mail(
subject='📧 Test Email from Django',
message='This is a test email from your Django application.',
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=settings.ADMIN_EMAILS,
fail_silently=False,
)
return True
except Exception as e:
logger.error(f"Test email failed: {e}")
return False