snapshot: modularize integrations and builder order flows
This commit is contained in:
279
backend/workflows/welcome_email_views.py
Normal file
279
backend/workflows/welcome_email_views.py
Normal file
@@ -0,0 +1,279 @@
|
||||
from celery import current_app
|
||||
from django.contrib import messages
|
||||
from django.shortcuts import redirect, render
|
||||
from django.utils import timezone
|
||||
|
||||
from .branding import get_default_notification_templates
|
||||
from .models import NotificationTemplate, ScheduledWelcomeEmail, WorkflowConfig
|
||||
from .tasks import send_scheduled_welcome_email
|
||||
|
||||
|
||||
def welcome_emails_page_impl(request):
|
||||
rows = ScheduledWelcomeEmail.objects.select_related('onboarding_request').order_by('-send_at', '-id')[:200]
|
||||
config, _ = WorkflowConfig.objects.get_or_create(name='Default')
|
||||
welcome_template = NotificationTemplate.objects.filter(key='onboarding_welcome').first()
|
||||
default_welcome = get_default_notification_templates().get('onboarding_welcome', {})
|
||||
default_subject = (default_welcome.get('subject') or '').strip()
|
||||
default_body = (default_welcome.get('body') or '').strip()
|
||||
default_subject_en = (default_welcome.get('subject_en') or '').strip()
|
||||
default_body_en = (default_welcome.get('body_en') or '').strip()
|
||||
subject_value = (welcome_template.subject_template if welcome_template else '').strip() or default_subject
|
||||
body_value = (welcome_template.body_template if welcome_template else '').strip() or default_body
|
||||
subject_value_en = (welcome_template.subject_template_en if welcome_template else '').strip() or default_subject_en
|
||||
body_value_en = (welcome_template.body_template_en if welcome_template else '').strip() or default_body_en
|
||||
return render(
|
||||
request,
|
||||
'workflows/welcome_emails.html',
|
||||
{
|
||||
'rows': rows,
|
||||
'workflow_config': config,
|
||||
'welcome_template': welcome_template,
|
||||
'welcome_subject_value': subject_value,
|
||||
'welcome_body_value': body_value,
|
||||
'welcome_subject_value_en': subject_value_en,
|
||||
'welcome_body_value_en': body_value_en,
|
||||
'welcome_keywords': ['{{ FULL_NAME }}', '{{ VORNAME }}', '{{ NACHNAME }}', '{{ DEPARTMENT }}', '{{ CONTRACT_START }}', '{{ EMAIL }}', '{{ REQUESTED_BY }}'],
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def trigger_welcome_email_now_impl(request, schedule_id: int, *, audit_fn, send_task_fn=send_scheduled_welcome_email):
|
||||
scheduled = ScheduledWelcomeEmail.objects.filter(id=schedule_id).first()
|
||||
if not scheduled:
|
||||
messages.error(request, f'Geplanter Welcome-Eintrag #{schedule_id} nicht gefunden.')
|
||||
return redirect('welcome_emails_page')
|
||||
if scheduled.status == 'cancelled':
|
||||
messages.error(request, f'Welcome E-Mail #{schedule_id} ist abgebrochen und kann nicht gesendet werden.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
async_result = send_task_fn.delay(scheduled.id, True)
|
||||
scheduled.celery_task_id = async_result.id or scheduled.celery_task_id
|
||||
scheduled.status = 'scheduled'
|
||||
scheduled.last_error = ''
|
||||
scheduled.save(update_fields=['celery_task_id', 'status', 'last_error', 'updated_at'])
|
||||
audit_fn(request, 'welcome_email_triggered_now', target_type='welcome_email', target_id=scheduled.id, target_label=scheduled.recipient_email)
|
||||
messages.success(request, f'Welcome E-Mail #{schedule_id} wurde sofort angestoßen.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
|
||||
def save_welcome_email_settings_impl(request, *, audit_fn):
|
||||
config, _ = WorkflowConfig.objects.get_or_create(name='Default')
|
||||
try:
|
||||
delay_days = int(request.POST.get('welcome_email_delay_days', config.welcome_email_delay_days or 5))
|
||||
except ValueError:
|
||||
messages.error(request, 'Ungültige Zahl bei der Welcome-Verzögerung.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
config.welcome_email_delay_days = max(0, delay_days)
|
||||
config.welcome_sender_email = request.POST.get('welcome_sender_email', '').strip()
|
||||
config.welcome_include_pdf = request.POST.get('welcome_include_pdf') == 'on'
|
||||
config.save(update_fields=['welcome_email_delay_days', 'welcome_sender_email', 'welcome_include_pdf'])
|
||||
|
||||
subject = request.POST.get('welcome_subject')
|
||||
body = request.POST.get('welcome_body')
|
||||
subject_en = request.POST.get('welcome_subject_en')
|
||||
body_en = request.POST.get('welcome_body_en')
|
||||
if subject is not None or body is not None or subject_en is not None or body_en is not None:
|
||||
default_welcome = get_default_notification_templates().get('onboarding_welcome', {})
|
||||
default_subject = (default_welcome.get('subject') or '').strip()
|
||||
default_body = (default_welcome.get('body') or '').strip()
|
||||
default_subject_en = (default_welcome.get('subject_en') or '').strip()
|
||||
default_body_en = (default_welcome.get('body_en') or '').strip()
|
||||
subject_clean = (subject or '').strip() or default_subject
|
||||
body_clean = (body or '').strip() or default_body
|
||||
subject_clean_en = (subject_en or '').strip() or default_subject_en
|
||||
body_clean_en = (body_en or '').strip() or default_body_en
|
||||
template, _ = NotificationTemplate.objects.get_or_create(
|
||||
key='onboarding_welcome',
|
||||
defaults={
|
||||
'subject_template': subject_clean,
|
||||
'body_template': body_clean,
|
||||
'subject_template_en': subject_clean_en,
|
||||
'body_template_en': body_clean_en,
|
||||
'is_active': True,
|
||||
},
|
||||
)
|
||||
changes = []
|
||||
if template.subject_template != subject_clean:
|
||||
template.subject_template = subject_clean
|
||||
changes.append('subject_template')
|
||||
if template.body_template != body_clean:
|
||||
template.body_template = body_clean
|
||||
changes.append('body_template')
|
||||
if template.subject_template_en != subject_clean_en:
|
||||
template.subject_template_en = subject_clean_en
|
||||
changes.append('subject_template_en')
|
||||
if template.body_template_en != body_clean_en:
|
||||
template.body_template_en = body_clean_en
|
||||
changes.append('body_template_en')
|
||||
if not template.is_active:
|
||||
template.is_active = True
|
||||
changes.append('is_active')
|
||||
if changes:
|
||||
template.save(update_fields=changes)
|
||||
|
||||
audit_fn(
|
||||
request,
|
||||
'welcome_email_settings_saved',
|
||||
target_type='welcome_email_settings',
|
||||
target_label='onboarding_welcome',
|
||||
details={
|
||||
'delay_days': config.welcome_email_delay_days,
|
||||
'sender_email': config.welcome_sender_email,
|
||||
'include_pdf': config.welcome_include_pdf,
|
||||
},
|
||||
)
|
||||
messages.success(request, 'Welcome-E-Mail Einstellungen wurden gespeichert.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
|
||||
def _revoke_celery_task(task_id: str) -> None:
|
||||
if not task_id:
|
||||
return
|
||||
try:
|
||||
current_app.control.revoke(task_id, terminate=False)
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
def _parse_selected_schedule_ids(raw: str) -> list[int]:
|
||||
if not raw:
|
||||
return []
|
||||
parsed: list[int] = []
|
||||
seen: set[int] = set()
|
||||
for token in raw.split(','):
|
||||
token = token.strip()
|
||||
if not token:
|
||||
continue
|
||||
try:
|
||||
schedule_id = int(token)
|
||||
except ValueError:
|
||||
continue
|
||||
if schedule_id in seen:
|
||||
continue
|
||||
seen.add(schedule_id)
|
||||
parsed.append(schedule_id)
|
||||
return parsed
|
||||
|
||||
|
||||
def bulk_welcome_email_action_impl(request, *, audit_fn, send_task_fn=send_scheduled_welcome_email):
|
||||
action = (request.POST.get('bulk_action') or '').strip().lower()
|
||||
selected_ids = _parse_selected_schedule_ids(request.POST.get('selected_ids', ''))
|
||||
|
||||
if action not in {'pause', 'send_now', 'delete'}:
|
||||
messages.error(request, 'Ungültige Bulk-Aktion.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
if not selected_ids:
|
||||
messages.warning(request, 'Keine Welcome-Einträge ausgewählt.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
rows = list(ScheduledWelcomeEmail.objects.filter(id__in=selected_ids).order_by('id'))
|
||||
if not rows:
|
||||
messages.warning(request, 'Keine passenden Welcome-Einträge gefunden.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
success_count = 0
|
||||
skipped_count = 0
|
||||
|
||||
for scheduled in rows:
|
||||
if action == 'pause':
|
||||
if scheduled.status in {'sent', 'cancelled'}:
|
||||
skipped_count += 1
|
||||
continue
|
||||
_revoke_celery_task(scheduled.celery_task_id)
|
||||
scheduled.status = 'paused'
|
||||
scheduled.save(update_fields=['status', 'updated_at'])
|
||||
success_count += 1
|
||||
continue
|
||||
|
||||
if action == 'send_now':
|
||||
if scheduled.status == 'cancelled':
|
||||
skipped_count += 1
|
||||
continue
|
||||
async_result = send_task_fn.delay(scheduled.id, True)
|
||||
scheduled.celery_task_id = async_result.id or scheduled.celery_task_id
|
||||
scheduled.status = 'scheduled'
|
||||
scheduled.last_error = ''
|
||||
scheduled.save(update_fields=['celery_task_id', 'status', 'last_error', 'updated_at'])
|
||||
success_count += 1
|
||||
continue
|
||||
|
||||
if action == 'delete':
|
||||
if scheduled.status == 'scheduled':
|
||||
_revoke_celery_task(scheduled.celery_task_id)
|
||||
scheduled.delete()
|
||||
success_count += 1
|
||||
|
||||
action_label = {
|
||||
'pause': 'pausiert',
|
||||
'send_now': 'sofort angestoßen',
|
||||
'delete': 'gelöscht',
|
||||
}[action]
|
||||
if success_count:
|
||||
audit_fn(
|
||||
request,
|
||||
'welcome_email_bulk_action',
|
||||
target_type='welcome_email',
|
||||
target_label=action,
|
||||
details={'selected_ids': selected_ids, 'success_count': success_count, 'skipped_count': skipped_count},
|
||||
)
|
||||
messages.success(request, f'{success_count} Welcome-Eintrag/Einträge {action_label}.')
|
||||
if skipped_count:
|
||||
messages.warning(request, f'{skipped_count} Eintrag/Einträge wurden übersprungen (Status nicht geeignet).')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
|
||||
def pause_welcome_email_impl(request, schedule_id: int, *, audit_fn):
|
||||
scheduled = ScheduledWelcomeEmail.objects.filter(id=schedule_id).first()
|
||||
if not scheduled:
|
||||
messages.error(request, f'Geplanter Welcome-Eintrag #{schedule_id} nicht gefunden.')
|
||||
return redirect('welcome_emails_page')
|
||||
if scheduled.status in {'sent', 'cancelled'}:
|
||||
messages.error(request, f'Welcome E-Mail #{schedule_id} kann nicht pausiert werden.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
_revoke_celery_task(scheduled.celery_task_id)
|
||||
scheduled.status = 'paused'
|
||||
scheduled.save(update_fields=['status', 'updated_at'])
|
||||
audit_fn(request, 'welcome_email_paused', target_type='welcome_email', target_id=scheduled.id, target_label=scheduled.recipient_email)
|
||||
messages.success(request, f'Welcome E-Mail #{schedule_id} wurde pausiert.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
|
||||
def resume_welcome_email_impl(request, schedule_id: int, *, audit_fn, send_task_fn=send_scheduled_welcome_email):
|
||||
scheduled = ScheduledWelcomeEmail.objects.filter(id=schedule_id).first()
|
||||
if not scheduled:
|
||||
messages.error(request, f'Geplanter Welcome-Eintrag #{schedule_id} nicht gefunden.')
|
||||
return redirect('welcome_emails_page')
|
||||
if scheduled.status != 'paused':
|
||||
messages.error(request, f'Welcome E-Mail #{schedule_id} ist nicht pausiert.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
eta = scheduled.send_at if timezone.now() < scheduled.send_at else None
|
||||
async_result = send_task_fn.apply_async(args=[scheduled.id], eta=eta)
|
||||
scheduled.celery_task_id = async_result.id or scheduled.celery_task_id
|
||||
scheduled.status = 'scheduled'
|
||||
scheduled.last_error = ''
|
||||
scheduled.save(update_fields=['celery_task_id', 'status', 'last_error', 'updated_at'])
|
||||
audit_fn(request, 'welcome_email_resumed', target_type='welcome_email', target_id=scheduled.id, target_label=scheduled.recipient_email)
|
||||
messages.success(request, f'Welcome E-Mail #{schedule_id} wurde fortgesetzt.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
|
||||
def cancel_welcome_email_impl(request, schedule_id: int, *, audit_fn):
|
||||
scheduled = ScheduledWelcomeEmail.objects.filter(id=schedule_id).first()
|
||||
if not scheduled:
|
||||
messages.error(request, f'Geplanter Welcome-Eintrag #{schedule_id} nicht gefunden.')
|
||||
return redirect('welcome_emails_page')
|
||||
if scheduled.status == 'sent':
|
||||
messages.error(request, f'Welcome E-Mail #{schedule_id} wurde bereits gesendet.')
|
||||
return redirect('welcome_emails_page')
|
||||
|
||||
_revoke_celery_task(scheduled.celery_task_id)
|
||||
scheduled.status = 'cancelled'
|
||||
scheduled.last_error = ''
|
||||
scheduled.save(update_fields=['status', 'last_error', 'updated_at'])
|
||||
audit_fn(request, 'welcome_email_cancelled', target_type='welcome_email', target_id=scheduled.id, target_label=scheduled.recipient_email)
|
||||
messages.success(request, f'Welcome E-Mail #{schedule_id} wurde abgebrochen.')
|
||||
return redirect('welcome_emails_page')
|
||||
Reference in New Issue
Block a user