snapshot: preserve company config foundation and staff dashboard access
This commit is contained in:
@@ -3,7 +3,7 @@ from django.conf import settings
|
||||
from django import forms
|
||||
|
||||
from .emailing import send_system_email
|
||||
from .models import AdminAuditLog, EmployeeProfile, FormFieldConfig, FormOption, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalAppConfig, PortalBranding, ScheduledWelcomeEmail, SystemEmailConfig, WorkflowConfig
|
||||
from .models import AdminAuditLog, EmployeeProfile, FormFieldConfig, FormOption, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalAppConfig, PortalBranding, PortalCompanyConfig, ScheduledWelcomeEmail, SystemEmailConfig, WorkflowConfig
|
||||
|
||||
|
||||
@admin.register(EmployeeProfile)
|
||||
@@ -25,6 +25,11 @@ class PortalBrandingAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'portal_title', 'company_name', 'company_domain', 'support_email', 'default_language', 'updated_at')
|
||||
|
||||
|
||||
@admin.register(PortalCompanyConfig)
|
||||
class PortalCompanyConfigAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'legal_company_name', 'website_url', 'hr_contact_email', 'it_contact_email', 'updated_at')
|
||||
|
||||
|
||||
@admin.register(PortalAppConfig)
|
||||
class PortalAppConfigAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
|
||||
@@ -58,6 +58,15 @@ APP_DEFINITIONS: tuple[AppDefinition, ...] = (
|
||||
accent='APP',
|
||||
tags=(_('Suche'), _('Status'), _('PDF Zugriff')),
|
||||
),
|
||||
AppDefinition(
|
||||
key='company_config',
|
||||
section=PortalAppConfig.SECTION_PLATFORM,
|
||||
route_name='portal_company_config_page',
|
||||
title=_('Company Config'),
|
||||
description=_('Rechtliche Firmendaten, Kontaktpunkte und öffentliche Unternehmenslinks pflegen.'),
|
||||
action_label=_('Öffnen'),
|
||||
capability='manage_company_config',
|
||||
),
|
||||
AppDefinition(
|
||||
key='branding',
|
||||
section=PortalAppConfig.SECTION_PLATFORM,
|
||||
@@ -185,6 +194,12 @@ DEFAULT_ROLE_VISIBILITY = {
|
||||
ROLE_IT_STAFF: False,
|
||||
ROLE_STAFF: False,
|
||||
},
|
||||
'company_config': {
|
||||
ROLE_SUPER_ADMIN: False,
|
||||
ROLE_ADMIN: False,
|
||||
ROLE_IT_STAFF: False,
|
||||
ROLE_STAFF: False,
|
||||
},
|
||||
'app_registry': {
|
||||
ROLE_SUPER_ADMIN: False,
|
||||
ROLE_ADMIN: False,
|
||||
|
||||
@@ -7,7 +7,7 @@ from django.conf import settings
|
||||
from django.templatetags.static import static
|
||||
from django.utils.translation import get_language
|
||||
|
||||
from .models import PortalBranding
|
||||
from .models import PortalBranding, PortalCompanyConfig
|
||||
|
||||
|
||||
def get_portal_branding() -> PortalBranding:
|
||||
@@ -32,6 +32,26 @@ def get_portal_branding() -> PortalBranding:
|
||||
return branding
|
||||
|
||||
|
||||
def get_portal_company_config() -> PortalCompanyConfig:
|
||||
company_config, _ = PortalCompanyConfig.objects.get_or_create(
|
||||
name='Default',
|
||||
defaults={
|
||||
'legal_company_name': 'TUBCO GmbH',
|
||||
'country': 'Deutschland',
|
||||
'website_url': '',
|
||||
'imprint_url': '',
|
||||
'privacy_url': '',
|
||||
'hr_contact_email': '',
|
||||
'it_contact_email': '',
|
||||
'operations_contact_email': '',
|
||||
'phone_number': '',
|
||||
'vat_id': '',
|
||||
'registration_number': '',
|
||||
},
|
||||
)
|
||||
return company_config
|
||||
|
||||
|
||||
def get_company_email_domain() -> str:
|
||||
branding = get_portal_branding()
|
||||
domain = (branding.company_domain or '').strip().lower().lstrip('@')
|
||||
@@ -72,6 +92,7 @@ def get_portal_letterhead_path() -> Path:
|
||||
|
||||
def get_branding_context() -> dict[str, object]:
|
||||
branding = get_portal_branding()
|
||||
company_config = get_portal_company_config()
|
||||
lang = (get_language() or branding.default_language or 'de').split('-')[0]
|
||||
footer_text = (branding.footer_text_en or '').strip() if lang == 'en' else ''
|
||||
legal_notice = (branding.legal_notice_en or '').strip() if lang == 'en' else ''
|
||||
@@ -97,6 +118,21 @@ def get_branding_context() -> dict[str, object]:
|
||||
'portal_has_custom_logo': bool(branding.logo_image),
|
||||
'portal_has_custom_letterhead': bool(branding.pdf_letterhead),
|
||||
'portal_has_custom_favicon': bool(branding.favicon_image),
|
||||
'portal_company_config': company_config,
|
||||
'portal_company_legal_name': company_config.legal_company_name or branding.company_name,
|
||||
'portal_company_street': company_config.street_address,
|
||||
'portal_company_postal_code': company_config.postal_code,
|
||||
'portal_company_city': company_config.city,
|
||||
'portal_company_country': company_config.country,
|
||||
'portal_company_website_url': company_config.website_url,
|
||||
'portal_company_imprint_url': company_config.imprint_url,
|
||||
'portal_company_privacy_url': company_config.privacy_url,
|
||||
'portal_company_hr_contact_email': company_config.hr_contact_email,
|
||||
'portal_company_it_contact_email': company_config.it_contact_email,
|
||||
'portal_company_operations_contact_email': company_config.operations_contact_email,
|
||||
'portal_company_phone_number': company_config.phone_number,
|
||||
'portal_company_vat_id': company_config.vat_id,
|
||||
'portal_company_registration_number': company_config.registration_number,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from django.utils.translation import get_language, gettext as _, gettext_lazy
|
||||
|
||||
from .branding import get_company_email_domain
|
||||
from .form_builder import apply_form_field_config
|
||||
from .models import EmployeeProfile, FormOption, OffboardingRequest, OnboardingRequest, PortalBranding, WorkflowConfig
|
||||
from .models import EmployeeProfile, FormOption, OffboardingRequest, OnboardingRequest, PortalBranding, PortalCompanyConfig, WorkflowConfig
|
||||
from .roles import ROLE_ADMIN, ROLE_GROUP_NAMES, ROLE_IT_STAFF, ROLE_LABELS, ROLE_PLATFORM_OWNER, ROLE_STAFF, ROLE_SUPER_ADMIN, assign_user_role
|
||||
|
||||
|
||||
@@ -244,6 +244,43 @@ class PortalBrandingForm(forms.ModelForm):
|
||||
return favicon
|
||||
|
||||
|
||||
class PortalCompanyConfigForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = PortalCompanyConfig
|
||||
fields = [
|
||||
'legal_company_name',
|
||||
'street_address',
|
||||
'postal_code',
|
||||
'city',
|
||||
'country',
|
||||
'website_url',
|
||||
'imprint_url',
|
||||
'privacy_url',
|
||||
'hr_contact_email',
|
||||
'it_contact_email',
|
||||
'operations_contact_email',
|
||||
'phone_number',
|
||||
'vat_id',
|
||||
'registration_number',
|
||||
]
|
||||
labels = {
|
||||
'legal_company_name': gettext_lazy('Rechtlicher Firmenname'),
|
||||
'street_address': gettext_lazy('Straße und Hausnummer'),
|
||||
'postal_code': gettext_lazy('Postleitzahl'),
|
||||
'city': gettext_lazy('Stadt'),
|
||||
'country': gettext_lazy('Land'),
|
||||
'website_url': gettext_lazy('Website'),
|
||||
'imprint_url': gettext_lazy('Impressum-URL'),
|
||||
'privacy_url': gettext_lazy('Datenschutz-URL'),
|
||||
'hr_contact_email': gettext_lazy('HR-Kontakt'),
|
||||
'it_contact_email': gettext_lazy('IT-Kontakt'),
|
||||
'operations_contact_email': gettext_lazy('Operations-Kontakt'),
|
||||
'phone_number': gettext_lazy('Zentrale Telefonnummer'),
|
||||
'vat_id': gettext_lazy('USt-IdNr.'),
|
||||
'registration_number': gettext_lazy('Register- oder Handelsnummer'),
|
||||
}
|
||||
|
||||
|
||||
class OnboardingRequestForm(forms.ModelForm):
|
||||
first_name = forms.CharField(label='Vorname', required=False)
|
||||
last_name = forms.CharField(label='Nachname', required=False)
|
||||
|
||||
39
backend/workflows/migrations/0042_portalcompanyconfig.py
Normal file
39
backend/workflows/migrations/0042_portalcompanyconfig.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# Generated by Django 5.1.5 on 2026-03-26 12:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('workflows', '0041_portalappconfig_visible_to_admin_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PortalCompanyConfig',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(default='Default', max_length=80, unique=True)),
|
||||
('legal_company_name', models.CharField(blank=True, default='', max_length=255)),
|
||||
('street_address', models.CharField(blank=True, default='', max_length=255)),
|
||||
('postal_code', models.CharField(blank=True, default='', max_length=50)),
|
||||
('city', models.CharField(blank=True, default='', max_length=120)),
|
||||
('country', models.CharField(blank=True, default='Deutschland', max_length=120)),
|
||||
('website_url', models.URLField(blank=True, default='')),
|
||||
('imprint_url', models.URLField(blank=True, default='')),
|
||||
('privacy_url', models.URLField(blank=True, default='')),
|
||||
('hr_contact_email', models.EmailField(blank=True, default='', max_length=254)),
|
||||
('it_contact_email', models.EmailField(blank=True, default='', max_length=254)),
|
||||
('operations_contact_email', models.EmailField(blank=True, default='', max_length=254)),
|
||||
('phone_number', models.CharField(blank=True, default='', max_length=80)),
|
||||
('vat_id', models.CharField(blank=True, default='', max_length=80)),
|
||||
('registration_number', models.CharField(blank=True, default='', max_length=120)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Portal Company Config',
|
||||
'verbose_name_plural': 'Portal Company Config',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -72,6 +72,32 @@ class PortalBranding(models.Model):
|
||||
return self.portal_title or self.company_name or self.name
|
||||
|
||||
|
||||
class PortalCompanyConfig(models.Model):
|
||||
name = models.CharField(max_length=80, default='Default', unique=True)
|
||||
legal_company_name = models.CharField(max_length=255, blank=True, default='')
|
||||
street_address = models.CharField(max_length=255, blank=True, default='')
|
||||
postal_code = models.CharField(max_length=50, blank=True, default='')
|
||||
city = models.CharField(max_length=120, blank=True, default='')
|
||||
country = models.CharField(max_length=120, blank=True, default='Deutschland')
|
||||
website_url = models.URLField(blank=True, default='')
|
||||
imprint_url = models.URLField(blank=True, default='')
|
||||
privacy_url = models.URLField(blank=True, default='')
|
||||
hr_contact_email = models.EmailField(blank=True, default='')
|
||||
it_contact_email = models.EmailField(blank=True, default='')
|
||||
operations_contact_email = models.EmailField(blank=True, default='')
|
||||
phone_number = models.CharField(max_length=80, blank=True, default='')
|
||||
vat_id = models.CharField(max_length=80, blank=True, default='')
|
||||
registration_number = models.CharField(max_length=120, blank=True, default='')
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'Portal Company Config'
|
||||
verbose_name_plural = 'Portal Company Config'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.legal_company_name or self.name
|
||||
|
||||
|
||||
class PortalAppConfig(models.Model):
|
||||
SECTION_APP = 'app'
|
||||
SECTION_PLATFORM = 'platform'
|
||||
|
||||
@@ -29,8 +29,10 @@ ROLE_LABELS = {
|
||||
CAPABILITIES = {
|
||||
'manage_users': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN},
|
||||
'manage_product_branding': {ROLE_PLATFORM_OWNER},
|
||||
'manage_company_config': {ROLE_PLATFORM_OWNER},
|
||||
'manage_app_registry': {ROLE_PLATFORM_OWNER},
|
||||
'access_requests_dashboard': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF},
|
||||
'access_requests_dashboard': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF, ROLE_STAFF},
|
||||
'view_request_timeline': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF},
|
||||
'run_intro_session': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF},
|
||||
'generate_intro_pdfs': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF},
|
||||
'retry_requests': {ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, ROLE_ADMIN, ROLE_IT_STAFF},
|
||||
@@ -124,9 +126,11 @@ def template_role_context(user) -> dict[str, object]:
|
||||
'role_key': role_key,
|
||||
'role_label': str(ROLE_LABELS[role_key]),
|
||||
'can_manage_product_branding': user_has_capability(user, 'manage_product_branding'),
|
||||
'can_manage_company_config': user_has_capability(user, 'manage_company_config'),
|
||||
'can_manage_app_registry': user_has_capability(user, 'manage_app_registry'),
|
||||
'can_manage_users': user_has_capability(user, 'manage_users'),
|
||||
'can_access_requests_dashboard': user_has_capability(user, 'access_requests_dashboard'),
|
||||
'can_view_request_timeline': user_has_capability(user, 'view_request_timeline'),
|
||||
'can_run_intro_session': user_has_capability(user, 'run_intro_session'),
|
||||
'can_generate_intro_pdfs': user_has_capability(user, 'generate_intro_pdfs'),
|
||||
'can_retry_requests': user_has_capability(user, 'retry_requests'),
|
||||
|
||||
@@ -141,6 +141,29 @@
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.app-site-footer-links {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.app-site-footer-links a {
|
||||
color: #1f3a5f;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
color var(--motion-fast) var(--motion-ease),
|
||||
transform var(--motion-fast) var(--motion-ease);
|
||||
}
|
||||
|
||||
.app-site-footer-links a:hover {
|
||||
color: var(--app-brand-blue);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.app-header,
|
||||
.app-header-in-shell {
|
||||
|
||||
@@ -197,7 +197,7 @@
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% block extra_scripts %}
|
||||
<script>
|
||||
(function () {
|
||||
const searchInput = document.getElementById('app-registry-search');
|
||||
|
||||
@@ -22,6 +22,13 @@
|
||||
<div class="app-site-footer">
|
||||
{% if portal_footer_text %}<div class="app-site-footer-main">{{ portal_footer_text }}</div>{% endif %}
|
||||
{% if portal_legal_notice %}<div class="app-site-footer-legal">{{ portal_legal_notice }}</div>{% endif %}
|
||||
{% if portal_company_imprint_url or portal_company_privacy_url or portal_company_website_url %}
|
||||
<div class="app-site-footer-links">
|
||||
{% if portal_company_website_url %}<a href="{{ portal_company_website_url }}" target="_blank" rel="noopener">{% trans "Website" %}</a>{% endif %}
|
||||
{% if portal_company_imprint_url %}<a href="{{ portal_company_imprint_url }}" target="_blank" rel="noopener">{% trans "Impressum" %}</a>{% endif %}
|
||||
{% if portal_company_privacy_url %}<a href="{{ portal_company_privacy_url }}" target="_blank" rel="noopener">{% trans "Datenschutz" %}</a>{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="confirm-modal" id="app-confirm-modal" hidden aria-hidden="true">
|
||||
|
||||
120
backend/workflows/templates/workflows/company_config.html
Normal file
120
backend/workflows/templates/workflows/company_config.html
Normal file
@@ -0,0 +1,120 @@
|
||||
{% extends 'workflows/base_shell.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Company Config" %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link rel="stylesheet" href="{% static 'workflows/css/admin_tools.css' %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block shell_body %}
|
||||
{% include 'workflows/includes/app_header.html' with header_show_home=1 header_show_lang=1 header_inside_shell=1 %}
|
||||
<h1>{% trans "Company Config" %}</h1>
|
||||
<p class="sub">{% trans "Strukturierte Firmendaten, Kontaktpunkte und öffentliche Unternehmenslinks zentral pflegen." %}</p>
|
||||
|
||||
{% include 'workflows/includes/messages.html' %}
|
||||
|
||||
<section class="branding-sections">
|
||||
<form method="post" action="{% url 'save_portal_company_config' %}" class="stack-form">
|
||||
{% csrf_token %}
|
||||
|
||||
<section class="branding-block">
|
||||
<div class="branding-block-head">
|
||||
<h2>{% trans "Firmenprofil" %}</h2>
|
||||
<p>{% trans "Rechtlicher Name und zentrale Stammdaten der Firma." %}</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="field">
|
||||
<label for="{{ form.legal_company_name.id_for_label }}">{{ form.legal_company_name.label }}</label>
|
||||
{{ form.legal_company_name }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.phone_number.id_for_label }}">{{ form.phone_number.label }}</label>
|
||||
{{ form.phone_number }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.website_url.id_for_label }}">{{ form.website_url.label }}</label>
|
||||
{{ form.website_url }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.country.id_for_label }}">{{ form.country.label }}</label>
|
||||
{{ form.country }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="branding-block">
|
||||
<div class="branding-block-head">
|
||||
<h2>{% trans "Adresse & Register" %}</h2>
|
||||
<p>{% trans "Anschrift sowie optionale Register- und Steuerangaben." %}</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="field field-full">
|
||||
<label for="{{ form.street_address.id_for_label }}">{{ form.street_address.label }}</label>
|
||||
{{ form.street_address }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.postal_code.id_for_label }}">{{ form.postal_code.label }}</label>
|
||||
{{ form.postal_code }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.city.id_for_label }}">{{ form.city.label }}</label>
|
||||
{{ form.city }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.registration_number.id_for_label }}">{{ form.registration_number.label }}</label>
|
||||
{{ form.registration_number }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.vat_id.id_for_label }}">{{ form.vat_id.label }}</label>
|
||||
{{ form.vat_id }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="branding-block">
|
||||
<div class="branding-block-head">
|
||||
<h2>{% trans "Kontaktpunkte" %}</h2>
|
||||
<p>{% trans "Zentrale Ansprechpartner für HR, IT und Operations." %}</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="field">
|
||||
<label for="{{ form.hr_contact_email.id_for_label }}">{{ form.hr_contact_email.label }}</label>
|
||||
{{ form.hr_contact_email }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.it_contact_email.id_for_label }}">{{ form.it_contact_email.label }}</label>
|
||||
{{ form.it_contact_email }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.operations_contact_email.id_for_label }}">{{ form.operations_contact_email.label }}</label>
|
||||
{{ form.operations_contact_email }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="branding-block">
|
||||
<div class="branding-block-head">
|
||||
<h2>{% trans "Recht & Öffentlichkeit" %}</h2>
|
||||
<p>{% trans "Öffentliche Links für Website, Impressum und Datenschutz." %}</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="field">
|
||||
<label for="{{ form.imprint_url.id_for_label }}">{{ form.imprint_url.label }}</label>
|
||||
{{ form.imprint_url }}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="{{ form.privacy_url.id_for_label }}">{{ form.privacy_url.label }}</label>
|
||||
{{ form.privacy_url }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="hint">{% trans "Diese Links können später im Portal-Footer oder in öffentlichen Seiten verwendet werden." %}</div>
|
||||
</section>
|
||||
|
||||
<div class="toolbar" style="margin-top:1rem;">
|
||||
<div class="hint">{% trans "Diese Ebene ist bewusst von Branding getrennt: Hier geht es um strukturierte Firmendaten, nicht um visuelle Gestaltung." %}</div>
|
||||
<button class="btn btn-primary" type="submit">{% trans "Firmenkonfiguration speichern" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{% endblock %}
|
||||
@@ -190,7 +190,7 @@
|
||||
<th>{% trans "E-Mail" %}</th>
|
||||
<th>{% trans "Dokument" %}</th>
|
||||
{% if can_run_intro_session or can_generate_intro_pdfs %}<th>{% trans "Einweisung" %}</th>{% endif %}
|
||||
{% if can_retry_requests or can_delete_requests or can_access_requests_dashboard %}<th>{% trans "Aktion" %}</th>{% endif %}
|
||||
{% if can_view_request_timeline or can_retry_requests or can_delete_requests %}<th>{% trans "Aktion" %}</th>{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -271,9 +271,11 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% if can_retry_requests or can_delete_requests or can_access_requests_dashboard %}
|
||||
{% if can_view_request_timeline or can_retry_requests or can_delete_requests %}
|
||||
<td class="actions-cell">
|
||||
{% if can_view_request_timeline %}
|
||||
<a class="btn btn-secondary" href="/requests/timeline/{{ row.kind_slug }}/{{ row.id }}/">{% trans "Timeline" %}</a>
|
||||
{% endif %}
|
||||
{% if can_retry_requests and row.status_key == 'failed' %}
|
||||
<form method="post" action="/requests/retry/{{ row.kind_slug }}/{{ row.id }}/" class="inline-delete" data-confirm="{% trans 'Eintrag erneut verarbeiten?' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
@@ -32,6 +32,8 @@ urlpatterns = [
|
||||
path('admin-tools/handbook/', views.handbook_page, name='handbook_page'),
|
||||
path('admin-tools/branding/', views.portal_branding_page, name='portal_branding_page'),
|
||||
path('admin-tools/branding/save/', views.save_portal_branding, name='save_portal_branding'),
|
||||
path('admin-tools/company/', views.portal_company_config_page, name='portal_company_config_page'),
|
||||
path('admin-tools/company/save/', views.save_portal_company_config, name='save_portal_company_config'),
|
||||
path('admin-tools/apps/', views.portal_app_registry_page, name='portal_app_registry_page'),
|
||||
path('admin-tools/apps/save/', views.save_portal_app_registry, name='save_portal_app_registry'),
|
||||
path('admin-tools/users/', views.user_management_page, name='user_management_page'),
|
||||
|
||||
@@ -27,7 +27,7 @@ from django.urls import reverse
|
||||
from .app_registry import build_portal_app_sections, get_portal_app_registry_rows
|
||||
from .backup_ops import create_backup_bundle, delete_backup_bundle, list_backup_bundles, verify_backup_bundle
|
||||
from .branding import get_branding_email_copy, get_company_email_domain, get_default_notification_templates
|
||||
from .forms import OffboardingRequestForm, OnboardingRequestForm, PortalBrandingForm, UserManagementCreateForm
|
||||
from .forms import OffboardingRequestForm, OnboardingRequestForm, PortalBrandingForm, PortalCompanyConfigForm, UserManagementCreateForm
|
||||
from .form_builder import (
|
||||
DEFAULT_FIELD_ORDER,
|
||||
LOCKED_FIELD_RULES,
|
||||
@@ -36,7 +36,7 @@ from .form_builder import (
|
||||
ONBOARDING_PAGE_ORDER,
|
||||
ensure_form_field_configs,
|
||||
)
|
||||
from .models import AdminAuditLog, EmployeeProfile, FormFieldConfig, FormOption, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalAppConfig, PortalBranding, ScheduledWelcomeEmail, SystemEmailConfig, WorkflowConfig
|
||||
from .models import AdminAuditLog, EmployeeProfile, FormFieldConfig, FormOption, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalAppConfig, PortalBranding, PortalCompanyConfig, ScheduledWelcomeEmail, SystemEmailConfig, WorkflowConfig
|
||||
from .emailing import send_system_email
|
||||
from .roles import ROLE_GROUP_NAMES, ROLE_LABELS, ROLE_PLATFORM_OWNER, ROLE_SUPER_ADMIN, assign_user_role, get_user_role_key, get_user_role_label, user_has_capability
|
||||
from .services import get_email_test_redirect, is_email_test_mode, is_nextcloud_enabled, upload_to_nextcloud
|
||||
@@ -587,6 +587,64 @@ def save_portal_branding(request):
|
||||
)
|
||||
|
||||
|
||||
@_require_capability('manage_company_config')
|
||||
def portal_company_config_page(request):
|
||||
company_config, created = PortalCompanyConfig.objects.get_or_create(name='Default')
|
||||
form = PortalCompanyConfigForm(instance=company_config)
|
||||
return render(
|
||||
request,
|
||||
'workflows/company_config.html',
|
||||
{
|
||||
'form': form,
|
||||
'company_config': company_config,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@_require_capability('manage_company_config')
|
||||
@require_POST
|
||||
def save_portal_company_config(request):
|
||||
company_config, created = PortalCompanyConfig.objects.get_or_create(name='Default')
|
||||
form = PortalCompanyConfigForm(request.POST, instance=company_config)
|
||||
if not form.is_valid():
|
||||
messages.error(request, _('Firmenkonfiguration konnte nicht gespeichert werden. Bitte prüfen Sie die Eingaben.'))
|
||||
return render(
|
||||
request,
|
||||
'workflows/company_config.html',
|
||||
{
|
||||
'form': form,
|
||||
'company_config': company_config,
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
||||
company_config = form.save()
|
||||
_audit(
|
||||
request,
|
||||
'portal_company_config_saved',
|
||||
target_type='portal_company_config',
|
||||
target_id=company_config.id,
|
||||
target_label=company_config.legal_company_name or 'Default',
|
||||
details={
|
||||
'website_url': company_config.website_url,
|
||||
'imprint_url': company_config.imprint_url,
|
||||
'privacy_url': company_config.privacy_url,
|
||||
'hr_contact_email': company_config.hr_contact_email,
|
||||
'it_contact_email': company_config.it_contact_email,
|
||||
'operations_contact_email': company_config.operations_contact_email,
|
||||
},
|
||||
)
|
||||
messages.success(request, _('Firmenkonfiguration wurde gespeichert.'))
|
||||
return render(
|
||||
request,
|
||||
'workflows/company_config.html',
|
||||
{
|
||||
'form': PortalCompanyConfigForm(instance=company_config),
|
||||
'company_config': company_config,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@_require_capability('manage_users')
|
||||
@require_POST
|
||||
def create_user_from_admin(request):
|
||||
@@ -838,7 +896,7 @@ def delete_backup_from_admin(request, backup_name: str):
|
||||
return redirect('backup_recovery_page')
|
||||
|
||||
|
||||
@_require_capability('access_requests_dashboard')
|
||||
@_require_capability('view_request_timeline')
|
||||
def request_timeline_page(request, kind: str, request_id: int):
|
||||
if kind == 'onboarding':
|
||||
obj = get_object_or_404(OnboardingRequest, id=request_id)
|
||||
|
||||
Reference in New Issue
Block a user