snapshot: modularize workflow model layer by responsibility
This commit is contained in:
172
backend/workflows/model_requests.py
Normal file
172
backend/workflows/model_requests.py
Normal file
@@ -0,0 +1,172 @@
|
||||
from django.db import models
|
||||
from django.utils.translation import get_language, gettext_lazy as _
|
||||
|
||||
from .model_account import EmployeeProfile
|
||||
from .model_shared import REQUEST_STATUS_CHOICES, normalized_language_code
|
||||
|
||||
|
||||
class OnboardingRequest(models.Model):
|
||||
STATUS_CHOICES = REQUEST_STATUS_CHOICES
|
||||
|
||||
full_name = models.CharField(max_length=255, verbose_name='Vorname und Nachname')
|
||||
gender = models.CharField(max_length=20, blank=True, choices=[('herr', _('Herr')), ('frau', _('Frau')), ('divers', _('Divers'))], verbose_name='Anrede')
|
||||
job_title = models.CharField(max_length=255, blank=True, verbose_name='Berufsbezeichnung')
|
||||
department = models.CharField(max_length=255, blank=True, verbose_name='Abteilung')
|
||||
work_email = models.EmailField(verbose_name='Gewünschte dienstliche E-Mail-Adresse')
|
||||
contract_start = models.DateField(verbose_name='Vertragsbeginn')
|
||||
employment_type = models.CharField(max_length=20, blank=True, choices=[('befristet', _('befristet')), ('unbefristet', _('unbefristet'))], verbose_name='Beschäftigungsverhältnis')
|
||||
employment_end_date = models.DateField(null=True, blank=True, verbose_name='Enddatum (nur bei befristet)')
|
||||
handover_date = models.DateField(null=True, blank=True, verbose_name='Gewünschtes Übergabedatum der Geräte')
|
||||
order_business_cards = models.BooleanField(default=False, verbose_name='Bestellung Visitenkarten')
|
||||
business_card_name = models.CharField(max_length=255, blank=True, verbose_name='Name (Visitenkarte)')
|
||||
business_card_title = models.CharField(max_length=255, blank=True, verbose_name='Titel (Visitenkarte)')
|
||||
business_card_email = models.EmailField(blank=True, verbose_name='E-Mailadresse (Visitenkarte)')
|
||||
business_card_phone = models.CharField(max_length=100, blank=True, verbose_name='Telefonnummer (Visitenkarte)')
|
||||
group_mailboxes_required = models.BooleanField(default=False, verbose_name='Gruppenpostfächer erforderlich?')
|
||||
group_mailboxes = models.TextField(blank=True, verbose_name='Gruppenpostfächer')
|
||||
needed_devices = models.TextField(blank=True, verbose_name='Benötigte Geräte und Gegenstände')
|
||||
needed_software = models.TextField(blank=True, verbose_name='Benötigte Software')
|
||||
needed_accesses = models.TextField(blank=True, verbose_name='Benötigte Zugänge')
|
||||
needed_workspace_groups = models.TextField(blank=True, verbose_name='Benötigte Gruppen im Workspace')
|
||||
additional_software_needed = models.BooleanField(default=False, verbose_name='Wird zusätzliche Software benötigt?')
|
||||
additional_software = models.TextField(blank=True, verbose_name='Zusätzlich gewünschte Software (ohne Garantie)')
|
||||
additional_hardware_needed = models.BooleanField(default=False, verbose_name='Wird zusätzliche Hardware benötigt?')
|
||||
additional_hardware = models.TextField(blank=True, verbose_name='Zusätzliche Hardware')
|
||||
additional_hardware_other = models.TextField(blank=True, verbose_name='Weitere Hardware (Freitext)')
|
||||
additional_access_needed = models.BooleanField(default=False, verbose_name='Werden weitere Zugänge benötigt?')
|
||||
additional_access_text = models.TextField(blank=True, verbose_name='Weitere Zugänge (Freitext)')
|
||||
needed_resources = models.TextField(blank=True, verbose_name='Benötigte Ressourcen')
|
||||
phone_number = models.CharField(max_length=100, blank=True, verbose_name='Telefon-Direktwahl')
|
||||
successor_required = models.BooleanField(default=False, verbose_name='Neue Mitarbeitende ist Nachfolge von?')
|
||||
successor_name = models.CharField(max_length=255, blank=True, verbose_name='Name der Vorgängerperson')
|
||||
inherit_phone_number = models.BooleanField(default=False, verbose_name='Telefonnummer von Vorgängerperson übernehmen')
|
||||
additional_notes = models.TextField(blank=True, verbose_name='Raum für zusätzliche Anmerkungen und Wünsche')
|
||||
onboarded_by_email = models.EmailField(blank=True, verbose_name='E-Mail der anfordernden Person')
|
||||
onboarded_by_name = models.CharField(max_length=255, blank=True, verbose_name='Name der anfordernden Person')
|
||||
agreement = models.TextField(blank=True, verbose_name='Vereinbarung')
|
||||
signature_url = models.URLField(blank=True, verbose_name='Unterschrift')
|
||||
signature_image = models.ImageField(upload_to='signatures/', blank=True, null=True, verbose_name='Unterschrift (Bilddatei)')
|
||||
personalized_text = models.TextField(blank=True, verbose_name='Personalisierter Text für PDF', help_text='Optionaler individueller Textblock im Onboarding PDF.')
|
||||
custom_field_values = models.JSONField(default=dict, blank=True)
|
||||
generated_pdf_path = models.CharField(max_length=500, blank=True)
|
||||
intro_pdf_path = models.CharField(max_length=500, blank=True)
|
||||
processing_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='submitted')
|
||||
last_error = models.TextField(blank=True)
|
||||
preferred_language = models.CharField(max_length=10, blank=True, default='de', db_default='de')
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'Onboarding #{self.id} - {self.full_name}'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.preferred_language = normalized_language_code(self.preferred_language)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class ScheduledWelcomeEmail(models.Model):
|
||||
STATUS_CHOICES = [
|
||||
('scheduled', _('Geplant')),
|
||||
('paused', _('Pausiert')),
|
||||
('cancelled', _('Abgebrochen')),
|
||||
('sent', _('Gesendet')),
|
||||
('failed', _('Fehlgeschlagen')),
|
||||
]
|
||||
|
||||
onboarding_request = models.OneToOneField(OnboardingRequest, on_delete=models.CASCADE)
|
||||
recipient_email = models.EmailField()
|
||||
send_at = models.DateTimeField()
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='scheduled')
|
||||
celery_task_id = models.CharField(max_length=100, blank=True)
|
||||
sent_at = models.DateTimeField(null=True, blank=True)
|
||||
last_error = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-send_at', '-id']
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'Welcome #{self.id} | {self.recipient_email} | {self.status}'
|
||||
|
||||
|
||||
class IntroChecklistItem(models.Model):
|
||||
SECTION_CHOICES = [
|
||||
('workplace', _('Geräte und Arbeitsplatz')),
|
||||
('accounts', _('Konten und Berechtigungen')),
|
||||
('software', _('Software und Tools')),
|
||||
('process', _('Prozesse und Hinweise')),
|
||||
]
|
||||
OPERATOR_CHOICES = [
|
||||
('always', _('Immer anzeigen')),
|
||||
('contains', _('Enthält')),
|
||||
('equals', _('Ist gleich')),
|
||||
('is_true', _('Ist Ja / aktiv')),
|
||||
('is_false', _('Ist Nein / inaktiv')),
|
||||
]
|
||||
|
||||
section = models.CharField(max_length=30, choices=SECTION_CHOICES)
|
||||
label = models.CharField(max_length=255)
|
||||
label_en = models.CharField(max_length=255, blank=True)
|
||||
sort_order = models.PositiveIntegerField(default=0)
|
||||
is_active = models.BooleanField(default=True)
|
||||
condition_field = models.CharField(max_length=80, blank=True)
|
||||
condition_operator = models.CharField(max_length=20, choices=OPERATOR_CHOICES, default='always')
|
||||
condition_value = models.CharField(max_length=255, blank=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['section', 'sort_order', 'label']
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'{self.get_section_display()}: {self.label}'
|
||||
|
||||
def translated_label(self, language_code: str | None = None) -> str:
|
||||
lang = (language_code or get_language() or 'de').split('-')[0]
|
||||
if lang == 'en' and self.label_en.strip():
|
||||
return self.label_en.strip()
|
||||
return self.label.strip()
|
||||
|
||||
|
||||
class OnboardingIntroductionSession(models.Model):
|
||||
STATUS_CHOICES = [('draft', _('Entwurf')), ('completed', _('Abgeschlossen'))]
|
||||
|
||||
onboarding_request = models.OneToOneField(OnboardingRequest, on_delete=models.CASCADE)
|
||||
checklist_state = models.JSONField(default=dict, blank=True)
|
||||
notes = models.TextField(blank=True)
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft')
|
||||
completed_at = models.DateTimeField(null=True, blank=True)
|
||||
completed_by_name = models.CharField(max_length=255, blank=True)
|
||||
exported_pdf_path = models.CharField(max_length=500, blank=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'Einweisung #{self.id} | {self.onboarding_request.full_name} | {self.status}'
|
||||
|
||||
|
||||
class OffboardingRequest(models.Model):
|
||||
STATUS_CHOICES = REQUEST_STATUS_CHOICES
|
||||
|
||||
employee_profile = models.ForeignKey(EmployeeProfile, null=True, blank=True, on_delete=models.SET_NULL)
|
||||
full_name = models.CharField(max_length=255, verbose_name='Vorname und Nachname')
|
||||
work_email = models.EmailField(verbose_name='Dienstliche E-Mail-Adresse')
|
||||
department = models.CharField(max_length=255, blank=True, verbose_name='Abteilung')
|
||||
job_title = models.CharField(max_length=255, blank=True, verbose_name='Berufsbezeichnung')
|
||||
last_working_day = models.DateField(verbose_name='Letzter Arbeitstag')
|
||||
offboarding_reason = models.TextField(blank=True, verbose_name='Grund')
|
||||
notes = models.TextField(blank=True, verbose_name='Notizen')
|
||||
signature = models.CharField(max_length=255, blank=True, verbose_name='Unterschrift (Name)')
|
||||
requested_by_email = models.EmailField(verbose_name='E-Mail der anfordernden Person')
|
||||
requested_by_name = models.CharField(max_length=255, blank=True, verbose_name='Name der anfordernden Person')
|
||||
preferred_language = models.CharField(max_length=10, blank=True, default='de', db_default='de')
|
||||
generated_pdf_path = models.CharField(max_length=500, blank=True)
|
||||
custom_field_values = models.JSONField(default=dict, blank=True)
|
||||
processing_status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='submitted')
|
||||
last_error = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f'Offboarding #{self.id} - {self.full_name}'
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.preferred_language = normalized_language_code(self.preferred_language)
|
||||
super().save(*args, **kwargs)
|
||||
Reference in New Issue
Block a user