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