diff --git a/admin-compliance/app/sdk/vvt/page.tsx b/admin-compliance/app/sdk/vvt/page.tsx index c79cb8b..5c04a2b 100644 --- a/admin-compliance/app/sdk/vvt/page.tsx +++ b/admin-compliance/app/sdk/vvt/page.tsx @@ -3,11 +3,10 @@ /** * VVT — Verarbeitungsverzeichnis (Art. 30 DSGVO) * - * 4 Tabs: - * 1. Verzeichnis (Uebersicht aller Verarbeitungstaetigkeiten) + * 3 Tabs: + * 1. Verzeichnis (Uebersicht + "Aus Scope generieren") * 2. Verarbeitung bearbeiten (Detail-Editor) - * 3. Generator (Profiling-Fragebogen) - * 4. Export & Compliance + * 3. Export & Compliance */ import { useState, useEffect, useCallback } from 'react' @@ -32,23 +31,15 @@ import { } from '@/lib/sdk/vvt-types' import type { VVTActivity, VVTOrganizationHeader, BusinessFunction } from '@/lib/sdk/vvt-types' import { - PROFILING_STEPS, - PROFILING_QUESTIONS, - DEPARTMENT_DATA_CATEGORIES, - getQuestionsForStep, - getStepProgress, - getTotalProgress, generateActivities, + prefillFromScopeAnswers, } from '@/lib/sdk/vvt-profiling' -import type { ProfilingAnswers } from '@/lib/sdk/vvt-profiling' // ============================================================================= // CONSTANTS // ============================================================================= -type Tab = 'verzeichnis' | 'editor' | 'generator' | 'export' - -const PROFILING_STORAGE_KEY = 'bp_vvt_profiling' +type Tab = 'verzeichnis' | 'editor' | 'export' // ============================================================================= // API CLIENT @@ -202,24 +193,13 @@ export default function VVTPage() { const [tab, setTab] = useState('verzeichnis') const [activities, setActivities] = useState([]) const [orgHeader, setOrgHeader] = useState(createDefaultOrgHeader()) - const [profilingAnswers, setProfilingAnswers] = useState({}) const [editingId, setEditingId] = useState(null) const [filter, setFilter] = useState('all') const [searchQuery, setSearchQuery] = useState('') const [sortBy, setSortBy] = useState<'name' | 'date' | 'status'>('name') - const [generatorStep, setGeneratorStep] = useState(1) - const [generatorPreview, setGeneratorPreview] = useState(null) const [isLoading, setIsLoading] = useState(true) const [apiError, setApiError] = useState(null) - // Load profiling answers from localStorage (UI state only) - useEffect(() => { - try { - const stored = localStorage.getItem(PROFILING_STORAGE_KEY) - if (stored) setProfilingAnswers(JSON.parse(stored)) - } catch { /* ignore */ } - }, []) - // Load activities + org header from API useEffect(() => { async function loadFromApi() { @@ -242,13 +222,6 @@ export default function VVTPage() { loadFromApi() }, []) - const updateProfilingAnswers = useCallback((prof: ProfilingAnswers) => { - setProfilingAnswers(prof) - try { - localStorage.setItem(PROFILING_STORAGE_KEY, JSON.stringify(prof)) - } catch { /* ignore */ } - }, []) - // Computed stats const activeCount = activities.filter(a => a.status === 'APPROVED').length const draftCount = activities.filter(a => a.status === 'DRAFT').length @@ -279,7 +252,6 @@ export default function VVTPage() { const tabs: { id: Tab; label: string; count?: number }[] = [ { id: 'verzeichnis', label: 'Verzeichnis', count: activities.length }, { id: 'editor', label: 'Verarbeitung bearbeiten' }, - { id: 'generator', label: 'Generator' }, { id: 'export', label: 'Export & Compliance' }, ] @@ -342,6 +314,7 @@ export default function VVTPage() { setSearchQuery={setSearchQuery} sortBy={sortBy} setSortBy={setSortBy} + scopeAnswers={state.complianceScope?.answers} onEdit={(id) => { setEditingId(id); setTab('editor') }} onNew={async () => { const vvtId = generateVVTId(activities.map(a => a.vvtId)) @@ -365,6 +338,18 @@ export default function VVTPage() { console.error(err) } }} + onAdoptGenerated={async (newActivities) => { + const created: VVTActivity[] = [] + for (const act of newActivities) { + try { + const saved = await apiCreateActivity(act) + created.push(saved) + } catch (err) { + console.error('Failed to create activity from scope:', err) + } + } + if (created.length > 0) setActivities(prev => [...prev, ...created]) + }} /> )} @@ -386,32 +371,6 @@ export default function VVTPage() { /> )} - {tab === 'generator' && ( - { - const created: VVTActivity[] = [] - for (const act of newActivities) { - try { - const saved = await apiCreateActivity(act) - created.push(saved) - } catch (err) { - console.error('Failed to create activity from generator:', err) - } - } - if (created.length > 0) setActivities(prev => [...prev, ...created]) - setGeneratorPreview(null) - setGeneratorStep(1) - setTab('verzeichnis') - }} - /> - )} - {tab === 'export' && ( void sortBy: string setSortBy: (s: 'name' | 'date' | 'status') => void + scopeAnswers?: import('@/lib/sdk/compliance-scope-types').ScopeProfilingAnswer[] onEdit: (id: string) => void onNew: () => void onDelete: (id: string) => void + onAdoptGenerated: (activities: VVTActivity[]) => void }) { + const [scopePreview, setScopePreview] = useState(null) + const [isGenerating, setIsGenerating] = useState(false) + + const handleGenerateFromScope = useCallback(() => { + if (!scopeAnswers) return + setIsGenerating(true) + try { + const profilingAnswers = prefillFromScopeAnswers(scopeAnswers) + const result = generateActivities(profilingAnswers) + setScopePreview(result.generatedActivities) + } finally { + setIsGenerating(false) + } + }, [scopeAnswers]) + + const handleAdoptPreview = useCallback(() => { + if (!scopePreview) return + onAdoptGenerated(scopePreview) + setScopePreview(null) + }, [scopePreview, onAdoptGenerated]) + + // Preview mode for generated activities + if (scopePreview) { + return ( +
+
+

Generierte Verarbeitungen

+

+ Basierend auf Ihrer Scope-Analyse wurden {scopePreview.length} Verarbeitungstaetigkeiten generiert. + Sie koennen einzelne Eintraege abwaehlen, bevor Sie diese uebernehmen. +

+
+ {scopePreview.map((a, i) => ( +
+ { + if (!e.target.checked) { + setScopePreview(scopePreview.filter((_, j) => j !== i)) + } + }} /> +
+
+ {a.vvtId} + {a.name} + {BUSINESS_FUNCTION_LABELS[a.businessFunction]} +
+

{a.description}

+
+
+ ))} +
+
+
+ + +
+
+ ) + } + return (
+ {/* Scope Generate Button */} + {scopeAnswers && scopeAnswers.length > 0 && ( +
+
+

Aus Scope-Analyse generieren

+

+ Erstellen Sie automatisch Verarbeitungstaetigkeiten basierend auf Ihren Scope-Profiling-Antworten. +

+
+ +
+ )} + {/* Stats */}
@@ -537,7 +584,7 @@ function TabVerzeichnis({

Keine Verarbeitungen gefunden

- Erstellen Sie eine neue Verarbeitung manuell oder nutzen Sie den Generator, um automatisch Eintraege aus einem Fragebogen zu erzeugen. + Erstellen Sie eine neue Verarbeitung manuell oder generieren Sie Eintraege automatisch aus Ihrer Scope-Analyse.

)} @@ -1004,314 +1051,7 @@ function TabEditor({ } // ============================================================================= -// TAB 3: GENERATOR -// ============================================================================= - -function TabGenerator({ - step, setStep, answers, setAnswers, preview, setPreview, onAdoptAll, -}: { - step: number - setStep: (s: number) => void - answers: ProfilingAnswers - setAnswers: (a: ProfilingAnswers) => void - preview: VVTActivity[] | null - setPreview: (p: VVTActivity[] | null) => void - onAdoptAll: (activities: VVTActivity[]) => void -}) { - const questions = getQuestionsForStep(step) - const totalSteps = PROFILING_STEPS.length - const currentStepInfo = PROFILING_STEPS.find(s => s.step === step) - const totalProgress = getTotalProgress(answers) - - const handleGenerate = () => { - const result = generateActivities(answers) - setPreview(result.generatedActivities) - } - - // Preview mode - if (preview) { - return ( -
-
-

Generierte Verarbeitungen

-

- Basierend auf Ihren Antworten wurden {preview.length} Verarbeitungstaetigkeiten generiert. - Sie koennen einzelne Eintraege abwaehlen, bevor Sie diese uebernehmen. -

-
- {preview.map((a, i) => ( -
- { - if (!e.target.checked) { - setPreview(preview.filter((_, j) => j !== i)) - } - }} /> -
-
- {a.vvtId} - {a.name} - {BUSINESS_FUNCTION_LABELS[a.businessFunction]} -
-

{a.description}

-
-
- ))} -
-
-
- - -
-
- ) - } - - return ( -
- {/* Progress Bar */} -
-
- Fortschritt: {totalProgress}% - Schritt {step} von {totalSteps} -
-
-
-
-
- {PROFILING_STEPS.map(s => ( -
-
- - {/* Step Info */} -
-

{currentStepInfo?.title}

-

{currentStepInfo?.description}

- -
- {questions.map(q => { - const deptConfig = DEPARTMENT_DATA_CATEGORIES[q.id] - const isDeptQuestion = q.type === 'boolean' && q.step === 2 && deptConfig - const isActive = answers[q.id] === true - const categoriesKey = `${q.id}_categories` - const selectedCategories = (answers[categoriesKey] as string[] | undefined) || [] - - // Initialize typical categories when dept is activated - const handleDeptToggle = (value: boolean) => { - const updated = { ...answers, [q.id]: value } - if (value && deptConfig && !answers[categoriesKey]) { - // Prefill typical categories - updated[categoriesKey] = deptConfig.categories - .filter(c => c.isTypical) - .map(c => c.id) - } - setAnswers(updated) - } - - const handleCategoryToggle = (catId: string) => { - const current = (answers[categoriesKey] as string[] | undefined) || [] - const updated = current.includes(catId) - ? current.filter(id => id !== catId) - : [...current, catId] - setAnswers({ ...answers, [categoriesKey]: updated }) - } - - // Expandable department tile (Step 2) - if (isDeptQuestion) { - const hasArt9Selected = deptConfig.categories - .filter(c => c.isArt9) - .some(c => selectedCategories.includes(c.id)) - - return ( -
- {/* Header row with Ja/Nein */} -
-
- {deptConfig.icon} -
- {deptConfig.label} - {q.helpText &&

{q.helpText}

} -
-
-
- - -
-
- - {/* Expandable categories panel */} - {isActive && ( -
-

- Typische Datenkategorien -

-
- {deptConfig.categories.map(cat => { - const isChecked = selectedCategories.includes(cat.id) - return ( - - ) - })} -
- - {/* Art. 9 warning */} - {hasArt9Selected && ( -
-

- Art. 9 DSGVO: Sie verarbeiten besondere Kategorien - personenbezogener Daten. Eine zusaetzliche Rechtsgrundlage nach Art. 9 Abs. 2 DSGVO ist - erforderlich (z.B. § 26 Abs. 3 BDSG fuer Beschaeftigtendaten). -

-
- )} -
- )} -
- ) - } - - // Standard rendering for non-department questions - return ( -
- - {q.helpText &&

{q.helpText}

} - - {q.type === 'boolean' && ( -
- - -
- )} - - {q.type === 'single_choice' && q.options && ( -
- {q.options.map(opt => ( - - ))} -
- )} - - {q.type === 'number' && ( - setAnswers({ ...answers, [q.id]: parseInt(e.target.value) || 0 })} - className="w-40 px-3 py-2 border border-gray-300 rounded-lg" - placeholder="Anzahl" /> - )} - - {q.type === 'text' && ( - setAnswers({ ...answers, [q.id]: e.target.value })} - className="w-full px-3 py-2 border border-gray-300 rounded-lg" /> - )} -
- ) - })} -
-
- - {/* Navigation */} -
- -
- {step < totalSteps ? ( - - ) : ( - - )} -
-
-
- ) -} - -// ============================================================================= -// TAB 4: EXPORT & COMPLIANCE +// TAB 3: EXPORT & COMPLIANCE // ============================================================================= function TabExport({ diff --git a/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx b/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx index c684b76..75c0606 100644 --- a/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx +++ b/admin-compliance/components/sdk/compliance-scope/ScopeWizardTab.tsx @@ -2,6 +2,7 @@ import React, { useState, useCallback, useEffect, useMemo } from 'react' import type { ScopeProfilingAnswer, ScopeProfilingQuestion } from '@/lib/sdk/compliance-scope-types' import { SCOPE_QUESTION_BLOCKS, getBlockProgress, getTotalProgress, getAnswerValue, prefillFromCompanyProfile, getProfileInfoForBlock, getAutoFilledScoringAnswers, getUnansweredRequiredQuestions } from '@/lib/sdk/compliance-scope-profiling' +import { DEPARTMENT_DATA_CATEGORIES } from '@/lib/sdk/vvt-profiling' import type { ScopeQuestionBlockId } from '@/lib/sdk/compliance-scope-types' import { useSDK } from '@/lib/sdk' @@ -509,19 +510,26 @@ export function ScopeWizardTab({ {/* Questions */}
- {currentBlock.questions.map((question) => { - const isAnswered = answers.some(a => a.questionId === question.id) - const borderClass = question.required - ? isAnswered - ? 'border-l-4 border-l-green-400 pl-4' - : 'border-l-4 border-l-orange-400 pl-4' - : '' - return ( -
- {renderQuestion(question)} -
- ) - })} + {currentBlock.id === 'datenkategorien_detail' ? ( + + ) : ( + currentBlock.questions.map((question) => { + const isAnswered = answers.some(a => a.questionId === question.id) + const borderClass = question.required + ? isAnswered + ? 'border-l-4 border-l-green-400 pl-4' + : 'border-l-4 border-l-orange-400 pl-4' + : '' + return ( +
+ {renderQuestion(question)} +
+ ) + }) + )}
@@ -561,3 +569,209 @@ export function ScopeWizardTab({ ) } + +// ============================================================================= +// BLOCK 9: Datenkategorien pro Abteilung (aufklappbare Kacheln) +// ============================================================================= + +/** Mapping Block 8 vvt_departments values → DEPARTMENT_DATA_CATEGORIES keys */ +const DEPT_VALUE_TO_KEY: Record = { + personal: ['dept_hr', 'dept_recruiting'], + finanzen: ['dept_finance'], + vertrieb: ['dept_sales'], + marketing: ['dept_marketing'], + kundenservice: ['dept_support'], +} + +/** Mapping department key → scope question ID for Block 9 */ +const DEPT_KEY_TO_QUESTION: Record = { + dept_hr: 'dk_dept_hr', + dept_recruiting: 'dk_dept_recruiting', + dept_finance: 'dk_dept_finance', + dept_sales: 'dk_dept_sales', + dept_marketing: 'dk_dept_marketing', + dept_support: 'dk_dept_support', +} + +function DatenkategorienBlock9({ + answers, + onAnswerChange, +}: { + answers: ScopeProfilingAnswer[] + onAnswerChange: (questionId: string, value: string | string[] | boolean | number) => void +}) { + const [expandedDepts, setExpandedDepts] = useState>(new Set()) + const [initializedDepts, setInitializedDepts] = useState>(new Set()) + + // Get selected departments from Block 8 + const deptAnswer = answers.find(a => a.questionId === 'vvt_departments') + const selectedDepts = Array.isArray(deptAnswer?.value) ? (deptAnswer.value as string[]) : [] + + // Resolve which department keys are active + const activeDeptKeys: string[] = [] + for (const deptValue of selectedDepts) { + const keys = DEPT_VALUE_TO_KEY[deptValue] + if (keys) { + for (const k of keys) { + if (!activeDeptKeys.includes(k)) activeDeptKeys.push(k) + } + } + } + + const toggleDept = (deptKey: string) => { + setExpandedDepts(prev => { + const next = new Set(prev) + if (next.has(deptKey)) { + next.delete(deptKey) + } else { + next.add(deptKey) + // Prefill typical categories on first expand + if (!initializedDepts.has(deptKey)) { + const config = DEPARTMENT_DATA_CATEGORIES[deptKey] + const questionId = DEPT_KEY_TO_QUESTION[deptKey] + if (config && questionId) { + const existing = answers.find(a => a.questionId === questionId) + if (!existing) { + const typicalIds = config.categories.filter(c => c.isTypical).map(c => c.id) + onAnswerChange(questionId, typicalIds) + } + } + setInitializedDepts(p => new Set(p).add(deptKey)) + } + } + return next + }) + } + + const handleCategoryToggle = (deptKey: string, catId: string) => { + const questionId = DEPT_KEY_TO_QUESTION[deptKey] + if (!questionId) return + const existing = answers.find(a => a.questionId === questionId) + const current = Array.isArray(existing?.value) ? (existing.value as string[]) : [] + const updated = current.includes(catId) + ? current.filter(id => id !== catId) + : [...current, catId] + onAnswerChange(questionId, updated) + } + + if (activeDeptKeys.length === 0) { + return ( +
+

+ Bitte waehlen Sie zuerst in Block 8 (Verarbeitungstaetigkeiten) die + Abteilungen aus, in denen personenbezogene Daten verarbeitet werden. +

+
+ ) + } + + return ( +
+ {activeDeptKeys.map(deptKey => { + const config = DEPARTMENT_DATA_CATEGORIES[deptKey] + if (!config) return null + const questionId = DEPT_KEY_TO_QUESTION[deptKey] + const isExpanded = expandedDepts.has(deptKey) + const existing = answers.find(a => a.questionId === questionId) + const selectedCategories = Array.isArray(existing?.value) ? (existing.value as string[]) : [] + const hasArt9Selected = config.categories + .filter(c => c.isArt9) + .some(c => selectedCategories.includes(c.id)) + + return ( +
+ {/* Header */} + + + {/* Expandable categories panel */} + {isExpanded && ( +
+

+ Datenkategorien +

+
+ {config.categories.map(cat => { + const isChecked = selectedCategories.includes(cat.id) + return ( + + ) + })} +
+ + {/* Art. 9 warning */} + {hasArt9Selected && ( +
+

+ Art. 9 DSGVO: Sie verarbeiten besondere Kategorien + personenbezogener Daten. Eine zusaetzliche Rechtsgrundlage nach Art. 9 Abs. 2 DSGVO ist + erforderlich (z.B. § 26 Abs. 3 BDSG fuer Beschaeftigtendaten). +

+
+ )} +
+ )} +
+ ) + })} +
+ ) +} diff --git a/admin-compliance/lib/sdk/compliance-scope-profiling.ts b/admin-compliance/lib/sdk/compliance-scope-profiling.ts index 053e123..a9ce4e0 100644 --- a/admin-compliance/lib/sdk/compliance-scope-profiling.ts +++ b/admin-compliance/lib/sdk/compliance-scope-profiling.ts @@ -6,6 +6,7 @@ import type { ComplianceScopeState, } from './compliance-scope-types' import type { CompanyProfile } from './types' +import { DEPARTMENT_DATA_CATEGORIES } from './vvt-profiling' /** * Block 1: Organisation & Reife @@ -579,6 +580,97 @@ const BLOCK_8_VVT: ScopeQuestionBlock = { ], } +/** + * Block 9: Datenkategorien pro Abteilung + * Generiert Fragen dynamisch aus DEPARTMENT_DATA_CATEGORIES + */ +const BLOCK_9_DATENKATEGORIEN: ScopeQuestionBlock = { + id: 'datenkategorien_detail', + title: 'Datenkategorien pro Abteilung', + description: 'Detaillierte Erfassung der Datenkategorien je Abteilung — basierend auf Ihrer Abteilungswahl in Block 8', + order: 9, + questions: [ + { + id: 'dk_dept_hr', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihre Personalabteilung?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer den HR-Bereich', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_hr.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 6, complexity: 4, assurance: 5 }, + mapsToVVTQuestion: 'dept_hr_categories', + }, + { + id: 'dk_dept_recruiting', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihr Recruiting?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer das Bewerbermanagement', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_recruiting.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 5, complexity: 3, assurance: 4 }, + mapsToVVTQuestion: 'dept_recruiting_categories', + }, + { + id: 'dk_dept_finance', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihre Finanzabteilung?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Finanzen & Buchhaltung', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_finance.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 6, complexity: 4, assurance: 5 }, + mapsToVVTQuestion: 'dept_finance_categories', + }, + { + id: 'dk_dept_sales', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihr Vertrieb?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Vertrieb & CRM', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_sales.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 5, complexity: 4, assurance: 4 }, + mapsToVVTQuestion: 'dept_sales_categories', + }, + { + id: 'dk_dept_marketing', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihr Marketing?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Marketing', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_marketing.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 6, complexity: 5, assurance: 5 }, + mapsToVVTQuestion: 'dept_marketing_categories', + }, + { + id: 'dk_dept_support', + type: 'multi', + question: 'Welche Datenkategorien verarbeitet Ihr Kundenservice?', + helpText: 'Waehlen Sie alle zutreffenden Datenkategorien fuer Support', + required: false, + options: DEPARTMENT_DATA_CATEGORIES.dept_support.categories.map(c => ({ + value: c.id, + label: `${c.label}${c.isArt9 ? ' (Art. 9)' : ''}`, + })), + scoreWeights: { risk: 5, complexity: 3, assurance: 4 }, + mapsToVVTQuestion: 'dept_support_categories', + }, + ], +} + /** * All question blocks in order */ @@ -591,6 +683,7 @@ export const SCOPE_QUESTION_BLOCKS: ScopeQuestionBlock[] = [ BLOCK_6_PRODUCT, BLOCK_7_AI_SYSTEMS, BLOCK_8_VVT, + BLOCK_9_DATENKATEGORIEN, ] /** diff --git a/admin-compliance/lib/sdk/compliance-scope-types.ts b/admin-compliance/lib/sdk/compliance-scope-types.ts index ef3b871..62389b6 100644 --- a/admin-compliance/lib/sdk/compliance-scope-types.ts +++ b/admin-compliance/lib/sdk/compliance-scope-types.ts @@ -49,7 +49,8 @@ export type ScopeQuestionBlockId = | 'processes' // Rechte & Prozesse | 'product' // Produktkontext | 'ai_systems' // KI-Systeme (aus Profil portiert) - | 'vvt'; // Verarbeitungstaetigkeiten (aus Profil portiert) + | 'vvt' // Verarbeitungstaetigkeiten (aus Profil portiert) + | 'datenkategorien_detail'; // Datenkategorien pro Abteilung (Block 9) /** * Eine einzelne Frage im Scope-Profiling