diff --git a/backend/workflows/migrations/0060_request_release_compat.py b/backend/workflows/migrations/0060_request_release_compat.py new file mode 100644 index 0000000..0e9ad70 --- /dev/null +++ b/backend/workflows/migrations/0060_request_release_compat.py @@ -0,0 +1,155 @@ +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workflows', '0059_userprofile_temporary_role_fields'), + ] + + operations = [ + migrations.SeparateDatabaseAndState( + database_operations=[ + migrations.RunSQL( + sql=( + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS street_address varchar(255) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS postal_code varchar(50) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS city varchar(120) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS salary_gross_monthly numeric(10,2) NULL; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS weekly_hours numeric(5,2) NULL; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS probation_months smallint NULL; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS notice_period varchar(255) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS vacation_days_total smallint NULL; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS place_of_work varchar(255) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS approval_released_at timestamp with time zone NULL; " + "ALTER TABLE workflows_onboardingrequest " + "ADD COLUMN IF NOT EXISTS approval_released_by_name varchar(255) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_offboardingrequest " + "ADD COLUMN IF NOT EXISTS approval_released_at timestamp with time zone NULL; " + "ALTER TABLE workflows_offboardingrequest " + "ADD COLUMN IF NOT EXISTS approval_released_by_name varchar(255) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_portalcompanyconfig " + "ADD COLUMN IF NOT EXISTS contract_signatory_name varchar(255) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_portalcompanyconfig " + "ADD COLUMN IF NOT EXISTS contract_signatory_line varchar(255) NOT NULL DEFAULT ''; " + "ALTER TABLE workflows_portalcompanyconfig " + "ADD COLUMN IF NOT EXISTS contract_signature_image varchar(100) NULL;" + ), + reverse_sql=( + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS street_address; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS postal_code; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS city; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS salary_gross_monthly; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS weekly_hours; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS probation_months; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS notice_period; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS vacation_days_total; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS place_of_work; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS approval_released_at; " + "ALTER TABLE workflows_onboardingrequest DROP COLUMN IF EXISTS approval_released_by_name; " + "ALTER TABLE workflows_offboardingrequest DROP COLUMN IF EXISTS approval_released_at; " + "ALTER TABLE workflows_offboardingrequest DROP COLUMN IF EXISTS approval_released_by_name; " + "ALTER TABLE workflows_portalcompanyconfig DROP COLUMN IF EXISTS contract_signatory_name; " + "ALTER TABLE workflows_portalcompanyconfig DROP COLUMN IF EXISTS contract_signatory_line; " + "ALTER TABLE workflows_portalcompanyconfig DROP COLUMN IF EXISTS contract_signature_image;" + ), + ), + ], + state_operations=[ + migrations.AddField( + model_name='onboardingrequest', + name='street_address', + field=models.CharField(blank=True, default='', max_length=255, verbose_name='Straße und Hausnummer'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='postal_code', + field=models.CharField(blank=True, default='', max_length=50, verbose_name='Postleitzahl'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='city', + field=models.CharField(blank=True, default='', max_length=120, verbose_name='Stadt'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='salary_gross_monthly', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Monatsgehalt brutto'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='weekly_hours', + field=models.DecimalField(blank=True, decimal_places=2, max_digits=5, null=True, verbose_name='Wochenstunden'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='probation_months', + field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Probezeit in Monaten'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='notice_period', + field=models.CharField(blank=True, default='', max_length=255, verbose_name='Kündigungsfrist'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='vacation_days_total', + field=models.PositiveSmallIntegerField(blank=True, null=True, verbose_name='Urlaubstage pro Jahr'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='place_of_work', + field=models.CharField(blank=True, default='', max_length=255, verbose_name='Arbeitsort'), + ), + migrations.AddField( + model_name='onboardingrequest', + name='approval_released_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='onboardingrequest', + name='approval_released_by_name', + field=models.CharField(blank=True, default='', max_length=255), + ), + migrations.AddField( + model_name='offboardingrequest', + name='approval_released_at', + field=models.DateTimeField(blank=True, null=True), + ), + migrations.AddField( + model_name='offboardingrequest', + name='approval_released_by_name', + field=models.CharField(blank=True, default='', max_length=255), + ), + migrations.AddField( + model_name='portalcompanyconfig', + name='contract_signatory_name', + field=models.CharField(blank=True, default='', max_length=255), + ), + migrations.AddField( + model_name='portalcompanyconfig', + name='contract_signatory_line', + field=models.CharField(blank=True, default='', max_length=255), + ), + migrations.AddField( + model_name='portalcompanyconfig', + name='contract_signature_image', + field=models.FileField( + blank=True, + null=True, + upload_to='branding/signatures/', + ), + ), + ], + ), + ] diff --git a/backend/workflows/model_portal.py b/backend/workflows/model_portal.py index 18db518..bc2e2ee 100644 --- a/backend/workflows/model_portal.py +++ b/backend/workflows/model_portal.py @@ -41,6 +41,14 @@ class PortalCompanyConfig(models.Model): website_url = models.URLField(blank=True, default='') imprint_url = models.URLField(blank=True, default='') privacy_url = models.URLField(blank=True, default='') + contract_signatory_name = models.CharField(max_length=255, blank=True, default='') + contract_signatory_line = models.CharField(max_length=255, blank=True, default='') + contract_signature_image = models.FileField( + upload_to='branding/signatures/', + blank=True, + null=True, + validators=[FileExtensionValidator(allowed_extensions=['png', 'jpg', 'jpeg', 'webp'])], + ) hr_contact_email = models.EmailField(blank=True, default='') it_contact_email = models.EmailField(blank=True, default='') operations_contact_email = models.EmailField(blank=True, default='') diff --git a/backend/workflows/model_requests.py b/backend/workflows/model_requests.py index 283a4a2..15d2038 100644 --- a/backend/workflows/model_requests.py +++ b/backend/workflows/model_requests.py @@ -12,10 +12,19 @@ class OnboardingRequest(models.Model): 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') + street_address = models.CharField(max_length=255, blank=True, default='', verbose_name='Straße und Hausnummer') + postal_code = models.CharField(max_length=50, blank=True, default='', verbose_name='Postleitzahl') + city = models.CharField(max_length=120, blank=True, default='', verbose_name='Stadt') 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)') + salary_gross_monthly = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='Monatsgehalt brutto') + weekly_hours = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True, verbose_name='Wochenstunden') + probation_months = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name='Probezeit in Monaten') + notice_period = models.CharField(max_length=255, blank=True, default='', verbose_name='Kündigungsfrist') + vacation_days_total = models.PositiveSmallIntegerField(null=True, blank=True, verbose_name='Urlaubstage pro Jahr') + place_of_work = models.CharField(max_length=255, blank=True, default='', verbose_name='Arbeitsort') 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)') @@ -52,6 +61,8 @@ class OnboardingRequest(models.Model): 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) + approval_released_at = models.DateTimeField(null=True, blank=True) + approval_released_by_name = models.CharField(max_length=255, blank=True, default='') preferred_language = models.CharField(max_length=10, blank=True, default='de', db_default='de') created_at = models.DateTimeField(auto_now_add=True) @@ -162,6 +173,8 @@ class OffboardingRequest(models.Model): 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) + approval_released_at = models.DateTimeField(null=True, blank=True) + approval_released_by_name = models.CharField(max_length=255, blank=True, default='') created_at = models.DateTimeField(auto_now_add=True) def __str__(self) -> str: diff --git a/backend/workflows/tests/test_onboarding_flow.py b/backend/workflows/tests/test_onboarding_flow.py index 84d0efc..272913e 100644 --- a/backend/workflows/tests/test_onboarding_flow.py +++ b/backend/workflows/tests/test_onboarding_flow.py @@ -49,6 +49,8 @@ class OnboardingFlowTests(TestCase): self.assertEqual(obj.full_name, 'Max Mustermann') self.assertEqual(obj.onboarded_by_email, f'requester@{self.company_domain}') self.assertEqual(obj.onboarded_by_name, 'Mia Beispiel') + self.assertEqual(obj.approval_released_by_name, '') + self.assertIsNone(obj.approval_released_at) mock_delay.assert_called_once_with(obj.id) @patch('workflows.views.process_onboarding_request.delay')