snapshot: preserve configurable onboarding conditional logic
This commit is contained in:
@@ -37,6 +37,7 @@ from .branding import get_branding_email_copy, get_company_email_domain, get_def
|
||||
from .forms import AccountAvatarForm, AccountDetailsForm, AccountNotificationPreferencesForm, AccountTOTPDisableForm, AccountTOTPEnableForm, AccountTOTPRegenerateRecoveryCodesForm, AppLoginForm, AppTOTPChallengeForm, OffboardingRequestForm, OnboardingRequestForm, PortalBrandingForm, PortalCompanyConfigForm, PortalTrialConfigForm, UserManagementCreateForm
|
||||
from .form_builder import (
|
||||
DEFAULT_FIELD_ORDER,
|
||||
DEFAULT_CONDITIONAL_RULES,
|
||||
FORM_PRESETS,
|
||||
LOCKED_FIELD_RULES,
|
||||
LOCKED_SECTION_RULES,
|
||||
@@ -46,13 +47,14 @@ from .form_builder import (
|
||||
ONBOARDING_PAGE_LABELS,
|
||||
ONBOARDING_PAGE_ORDER,
|
||||
ensure_form_field_configs,
|
||||
ensure_form_conditional_rule_configs,
|
||||
ensure_form_section_configs,
|
||||
get_default_page_map,
|
||||
get_section_labels,
|
||||
get_section_order,
|
||||
apply_form_preset,
|
||||
)
|
||||
from .models import AdminAuditLog, AsyncTaskLog, EmployeeProfile, FormFieldConfig, FormOption, FormSectionConfig, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalAppConfig, PortalBranding, PortalCompanyConfig, PortalTrialConfig, ScheduledWelcomeEmail, SystemEmailConfig, UserNotification, UserProfile, WorkflowConfig
|
||||
from .models import AdminAuditLog, AsyncTaskLog, EmployeeProfile, FormConditionalRuleConfig, FormFieldConfig, FormOption, FormSectionConfig, IntroChecklistItem, NotificationRule, NotificationTemplate, OffboardingRequest, OnboardingIntroductionSession, OnboardingRequest, PortalAppConfig, PortalBranding, PortalCompanyConfig, PortalTrialConfig, ScheduledWelcomeEmail, SystemEmailConfig, UserNotification, UserProfile, WorkflowConfig
|
||||
from .emailing import send_system_email
|
||||
from .notifications import notify_user
|
||||
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
|
||||
@@ -103,15 +105,7 @@ ONBOARDING_GROUPS = {
|
||||
'phone-box': ['phone_number_choice'],
|
||||
}
|
||||
|
||||
ONBOARDING_HIDDEN_BY_DEFAULT = {
|
||||
'business-card-box',
|
||||
'employment-end-box',
|
||||
'group-mailboxes-box',
|
||||
'extra-hardware-box',
|
||||
'extra-software-box',
|
||||
'extra-access-box',
|
||||
'successor-box',
|
||||
}
|
||||
ONBOARDING_HIDDEN_BY_DEFAULT = set(DEFAULT_CONDITIONAL_RULES.get('onboarding', {}).keys())
|
||||
|
||||
ONBOARDING_INLINE_CHECKS = {'order_business_cards', 'agreement_confirm'}
|
||||
ONBOARDING_CHECKBOX_LISTS = {
|
||||
@@ -131,6 +125,24 @@ ONBOARDING_SECTION_META = {
|
||||
'abschluss': {'title': gettext_lazy('Abschluss'), 'subtitle': gettext_lazy('Notizen und Freigabe')},
|
||||
}
|
||||
|
||||
CONDITIONAL_RULE_OPERATOR_CHOICES = [
|
||||
('checked', _('ist aktiviert')),
|
||||
('equals', _('ist gleich')),
|
||||
('not_equals', _('ist nicht gleich')),
|
||||
]
|
||||
|
||||
|
||||
def _normalized_conditional_rule_payload(form_type: str) -> dict[str, dict]:
|
||||
configs = ensure_form_conditional_rule_configs(form_type)
|
||||
payload = {}
|
||||
for target_key, cfg in configs.items():
|
||||
if not cfg.is_active:
|
||||
continue
|
||||
clauses = [clause for clause in (cfg.clauses or []) if clause.get('field') and clause.get('operator')]
|
||||
if clauses:
|
||||
payload[target_key] = {'all': clauses}
|
||||
return payload
|
||||
|
||||
|
||||
def healthz(request):
|
||||
db_ok = True
|
||||
@@ -1825,6 +1837,7 @@ def onboarding_create(request):
|
||||
if key in LOCKED_SECTION_RULES.get('onboarding', set()) or section_configs.get(key, None) is None or section_configs[key].is_visible
|
||||
}
|
||||
onboarding_sections = _build_onboarding_sections(onboarding_blocks, field_pages, visible_section_keys=visible_section_keys)
|
||||
onboarding_conditional_rules = _normalized_conditional_rule_payload('onboarding')
|
||||
|
||||
return render(
|
||||
request,
|
||||
@@ -1835,6 +1848,7 @@ def onboarding_create(request):
|
||||
'onboarding_sections': onboarding_sections,
|
||||
'onboarding_inline_checks': ONBOARDING_INLINE_CHECKS,
|
||||
'onboarding_checkbox_lists': ONBOARDING_CHECKBOX_LISTS,
|
||||
'onboarding_conditional_rules': onboarding_conditional_rules,
|
||||
'legal_text': legal_text,
|
||||
'saved': request.GET.get('saved') == '1',
|
||||
'saved_request_id': request.GET.get('id', ''),
|
||||
@@ -2179,6 +2193,27 @@ def form_builder_page(request):
|
||||
_audit(request, 'form_section_rules_saved', target_type='form_config', target_label=form_type, details={'count': updated})
|
||||
messages.success(request, 'Abschnittsregeln wurden gespeichert.')
|
||||
|
||||
elif action == 'save_conditional_rules' and form_type == 'onboarding':
|
||||
rule_configs = ensure_form_conditional_rule_configs(form_type)
|
||||
updated = 0
|
||||
for target_key, cfg in rule_configs.items():
|
||||
cfg.is_active = request.POST.get(f'conditional_active_{target_key}') == 'on'
|
||||
clauses = []
|
||||
clause_total = 2
|
||||
for index in range(clause_total):
|
||||
field_name = (request.POST.get(f'conditional_field_{target_key}_{index}') or '').strip()
|
||||
operator = (request.POST.get(f'conditional_operator_{target_key}_{index}') or '').strip()
|
||||
value = (request.POST.get(f'conditional_value_{target_key}_{index}') or '').strip()
|
||||
if not field_name or not operator:
|
||||
continue
|
||||
parsed_value = True if operator == 'checked' else value
|
||||
clauses.append({'field': field_name, 'operator': operator, 'value': parsed_value})
|
||||
cfg.clauses = clauses
|
||||
cfg.save(update_fields=['is_active', 'clauses'])
|
||||
updated += 1
|
||||
_audit(request, 'form_conditional_rules_saved', target_type='form_config', target_label=form_type, details={'count': updated})
|
||||
messages.success(request, 'Bedingte Logik wurde gespeichert.')
|
||||
|
||||
elif action == 'apply_preset':
|
||||
preset_key = (request.POST.get('preset_key') or '').strip()
|
||||
if apply_form_preset(form_type, preset_key):
|
||||
@@ -2194,7 +2229,7 @@ def form_builder_page(request):
|
||||
active_subpanel = 'options'
|
||||
elif action == 'save_field_texts':
|
||||
active_subpanel = 'field-texts'
|
||||
elif action in {'save_field_rules', 'save_section_rules'}:
|
||||
elif action in {'save_field_rules', 'save_section_rules', 'save_conditional_rules'}:
|
||||
active_panel = 'builder-rules'
|
||||
redirect_target = f"/admin-tools/form-builder/?form_type={form_type}&option_category={option_category}"
|
||||
if active_panel:
|
||||
@@ -2217,6 +2252,7 @@ def form_builder_page(request):
|
||||
|
||||
ensure_form_field_configs(form_type, default_names)
|
||||
section_configs = ensure_form_section_configs(form_type)
|
||||
conditional_rule_configs = ensure_form_conditional_rule_configs(form_type) if form_type == 'onboarding' else {}
|
||||
section_order = get_section_order(form_type)
|
||||
section_labels = get_section_labels(form_type)
|
||||
default_page_map = get_default_page_map(form_type)
|
||||
@@ -2343,6 +2379,57 @@ def form_builder_page(request):
|
||||
}
|
||||
)
|
||||
|
||||
conditional_rule_items = []
|
||||
if form_type == 'onboarding':
|
||||
conditional_field_choices = []
|
||||
for field_name in [
|
||||
'order_business_cards',
|
||||
'employment_type',
|
||||
'group_mailboxes_required_choice',
|
||||
'additional_hardware_needed_choice',
|
||||
'additional_software_needed_choice',
|
||||
'additional_access_needed_choice',
|
||||
'successor_required_choice',
|
||||
'inherit_phone_number_choice',
|
||||
]:
|
||||
conditional_field_choices.append((field_name, labels.get(field_name, field_name)))
|
||||
conditional_target_titles = {
|
||||
'business-card-box': _('Visitenkarten-Details'),
|
||||
'employment-end-box': _('Vertragsende'),
|
||||
'group-mailboxes-box': _('Gruppenpostfächer'),
|
||||
'extra-hardware-box': _('Zusätzliche Hardware'),
|
||||
'extra-software-box': _('Zusätzliche Software'),
|
||||
'extra-access-box': _('Zusätzliche Zugänge'),
|
||||
'successor-box': _('Nachfolge'),
|
||||
'phone-box': _('Direktwahl'),
|
||||
}
|
||||
conditional_target_descriptions = {
|
||||
'business-card-box': _('Steuert die Detailfelder für Visitenkarten.'),
|
||||
'employment-end-box': _('Steuert das Enddatum bei befristeter Beschäftigung.'),
|
||||
'group-mailboxes-box': _('Steuert das Freitextfeld für Gruppenpostfächer.'),
|
||||
'extra-hardware-box': _('Steuert zusätzliche Hardware-Felder.'),
|
||||
'extra-software-box': _('Steuert zusätzliche Software-Felder.'),
|
||||
'extra-access-box': _('Steuert zusätzliche Zugangsangaben.'),
|
||||
'successor-box': _('Steuert Nachfolge- und Übernahmefelder.'),
|
||||
'phone-box': _('Steuert die manuelle Direktwahl.'),
|
||||
}
|
||||
for target_key, cfg in conditional_rule_configs.items():
|
||||
clauses = list(cfg.clauses or [])
|
||||
while len(clauses) < 2:
|
||||
clauses.append({'field': '', 'operator': 'equals', 'value': ''})
|
||||
conditional_rule_items.append(
|
||||
{
|
||||
'target_key': target_key,
|
||||
'title': conditional_target_titles.get(target_key, target_key),
|
||||
'description': conditional_target_descriptions.get(target_key, ''),
|
||||
'is_active': cfg.is_active,
|
||||
'clauses': clauses[:2],
|
||||
'field_choices': conditional_field_choices,
|
||||
'operator_choices': CONDITIONAL_RULE_OPERATOR_CHOICES,
|
||||
'target_fields': [labels.get(name, name) for name in ONBOARDING_GROUPS.get(target_key, [])],
|
||||
}
|
||||
)
|
||||
|
||||
preview_sections = []
|
||||
if section_order:
|
||||
field_rule_group_map = {group['key']: group['items'] for group in field_rule_groups}
|
||||
@@ -2391,6 +2478,7 @@ def form_builder_page(request):
|
||||
'preview_sections': preview_sections,
|
||||
'section_rule_items': section_rule_items,
|
||||
'builder_summary': builder_summary,
|
||||
'conditional_rule_items': conditional_rule_items,
|
||||
'active_panel': active_panel,
|
||||
'active_subpanel': active_subpanel,
|
||||
'available_presets': FORM_PRESETS.get(form_type, {}),
|
||||
|
||||
Reference in New Issue
Block a user