feat(sdk): Company Profile Wizard verbessert
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 32s
CI / test-python-backend-compliance (push) Successful in 34s
CI / test-python-document-crawler (push) Successful in 25s
CI / test-python-dsms-gateway (push) Successful in 19s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 32s
CI / test-python-backend-compliance (push) Successful in 34s
CI / test-python-document-crawler (push) Successful in 25s
CI / test-python-dsms-gateway (push) Successful in 19s
- B2B2C als Geschaeftsmodell hinzugefuegt - URL-Felder bei Offering-Auswahl (Website, Shop, App, SaaS) — optional - Schritt-spezifische Erklaerungen in "Warum diese Fragen?" - Firmenname ohne Rechtsform, Templates bauen automatisch zusammen - Gruendungsjahr springt auf 2000 statt 1800 - SDK-Abdeckung Panel und Profil-loeschen Button entfernt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,6 @@ import {
|
||||
AI_INTEGRATION_TYPE_LABELS,
|
||||
HUMAN_OVERSIGHT_LABELS,
|
||||
CRITICAL_SECTOR_LABELS,
|
||||
SDKCoverageAssessment,
|
||||
} from '@/lib/sdk/types'
|
||||
|
||||
// =============================================================================
|
||||
@@ -103,60 +102,6 @@ const MACHINE_BUILDER_INDUSTRIES = [
|
||||
const isMachineBuilderIndustry = (industry: string) =>
|
||||
MACHINE_BUILDER_INDUSTRIES.includes(industry)
|
||||
|
||||
// =============================================================================
|
||||
// HELPER: ASSESS SDK COVERAGE
|
||||
// =============================================================================
|
||||
|
||||
function assessSDKCoverage(profile: Partial<CompanyProfile>): SDKCoverageAssessment {
|
||||
const coveredRegulations: string[] = ['DSGVO', 'BDSG', 'TTDSG', 'AI Act']
|
||||
const partiallyCoveredRegulations: string[] = []
|
||||
const notCoveredRegulations: string[] = []
|
||||
const reasons: string[] = []
|
||||
const recommendations: string[] = []
|
||||
|
||||
// Check target markets
|
||||
const targetMarkets = profile.targetMarkets || []
|
||||
|
||||
if (targetMarkets.includes('worldwide')) {
|
||||
notCoveredRegulations.push('CCPA (Kalifornien)', 'LGPD (Brasilien)', 'POPIA (Südafrika)')
|
||||
reasons.push('Weltweiter Betrieb erfordert Kenntnisse lokaler Datenschutzgesetze')
|
||||
recommendations.push('Für außereuropäische Märkte empfehlen wir die Konsultation lokaler Rechtsanwälte')
|
||||
}
|
||||
|
||||
if (targetMarkets.includes('eu_uk')) {
|
||||
partiallyCoveredRegulations.push('UK GDPR', 'UK AI Framework')
|
||||
reasons.push('UK-Recht weicht nach Brexit teilweise von EU-Recht ab')
|
||||
recommendations.push('Prüfen Sie UK-spezifische Anpassungen Ihrer Datenschutzerklärung')
|
||||
}
|
||||
|
||||
// Check company size
|
||||
if (profile.companySize === 'enterprise' || profile.companySize === 'large') {
|
||||
coveredRegulations.push('NIS2')
|
||||
reasons.push('Als größeres Unternehmen können NIS2-Pflichten relevant sein')
|
||||
}
|
||||
|
||||
// Check offerings
|
||||
const offerings = profile.offerings || []
|
||||
if (offerings.includes('webshop')) {
|
||||
coveredRegulations.push('Fernabsatzrecht')
|
||||
recommendations.push('Widerrufsbelehrung und AGB-Generator sind im SDK enthalten')
|
||||
}
|
||||
|
||||
// Determine if fully covered
|
||||
const requiresLegalCounsel = notCoveredRegulations.length > 0 || targetMarkets.includes('worldwide')
|
||||
const isFullyCovered = !requiresLegalCounsel && notCoveredRegulations.length === 0
|
||||
|
||||
return {
|
||||
isFullyCovered,
|
||||
coveredRegulations,
|
||||
partiallyCoveredRegulations,
|
||||
notCoveredRegulations,
|
||||
requiresLegalCounsel,
|
||||
reasons,
|
||||
recommendations,
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// STEP COMPONENTS
|
||||
// =============================================================================
|
||||
@@ -178,7 +123,7 @@ function StepBasicInfo({
|
||||
type="text"
|
||||
value={data.companyName || ''}
|
||||
onChange={e => onChange({ companyName: e.target.value })}
|
||||
placeholder="Ihre Firma GmbH"
|
||||
placeholder="Ihre Firma (ohne Rechtsform)"
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
/>
|
||||
</div>
|
||||
@@ -222,9 +167,15 @@ function StepBasicInfo({
|
||||
<input
|
||||
type="number"
|
||||
value={data.foundedYear || ''}
|
||||
onChange={e => onChange({ foundedYear: parseInt(e.target.value) || null })}
|
||||
onChange={e => {
|
||||
const val = parseInt(e.target.value)
|
||||
onChange({ foundedYear: isNaN(val) ? null : val })
|
||||
}}
|
||||
onFocus={e => {
|
||||
if (!data.foundedYear) onChange({ foundedYear: 2000 })
|
||||
}}
|
||||
placeholder="2020"
|
||||
min="1800"
|
||||
min="1900"
|
||||
max={new Date().getFullYear()}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
/>
|
||||
@@ -233,6 +184,27 @@ function StepBasicInfo({
|
||||
)
|
||||
}
|
||||
|
||||
// URL fields shown when specific offerings are selected
|
||||
const OFFERING_URL_CONFIG: Partial<Record<OfferingType, { label: string; placeholder: string; hint: string }>> = {
|
||||
website: { label: 'Website-Domain', placeholder: 'https://www.beispiel.de', hint: 'Ihre Unternehmenswebsite' },
|
||||
webshop: { label: 'Online-Shop URL', placeholder: 'https://shop.beispiel.de', hint: 'URL zu Ihrem Online-Shop' },
|
||||
app_mobile: { label: 'App-Store Links', placeholder: 'https://apps.apple.com/... oder https://play.google.com/...', hint: 'Apple App Store und/oder Google Play Store Link' },
|
||||
software_saas: { label: 'SaaS-Portal URL', placeholder: 'https://app.beispiel.de', hint: 'Login-/Registrierungsseite Ihres Kundenportals' },
|
||||
app_web: { label: 'Web-App URL', placeholder: 'https://app.beispiel.de', hint: 'URL zu Ihrer Web-Anwendung' },
|
||||
}
|
||||
|
||||
// Step-specific explanations for "Warum diese Fragen?"
|
||||
const STEP_EXPLANATIONS: Record<number, string> = {
|
||||
1: 'Rechtsform und Gründungsjahr bestimmen, welche Meldepflichten und Schwellenwerte für Ihr Unternehmen gelten (z.B. NIS2, AI Act).',
|
||||
2: 'Ihr Geschäftsmodell und Ihre Angebote bestimmen, welche DSGVO-Pflichten greifen: B2C erfordert z.B. strengere Einwilligungsregeln, Webshops brauchen Cookie-Banner und Datenschutzerklärungen, SaaS-Angebote eine Auftragsverarbeitung.',
|
||||
3: 'Die Unternehmensgröße bestimmt, ob Sie einen DSB benennen müssen (ab 20 MA), ob NIS2-Pflichten greifen und welche Audit-Anforderungen gelten.',
|
||||
4: 'Standorte und Zielmärkte bestimmen, welche nationalen Datenschutzgesetze zusätzlich zur DSGVO greifen (z.B. BDSG, DSG-AT, UK GDPR, CCPA).',
|
||||
5: 'Ob Sie Verantwortlicher oder Auftragsverarbeiter sind, bestimmt Ihre DSGVO-Pflichten grundlegend. KI-Nutzung löst zusätzliche AI-Act-Pflichten aus.',
|
||||
6: 'Ihre IT-Systeme und KI-Anwendungen werden für das Verarbeitungsverzeichnis (VVT), die technisch-organisatorischen Maßnahmen (TOM) und die KI-Risikobewertung benötigt.',
|
||||
7: 'Regulierungsrahmen und Prüfzyklen definieren, welche Compliance-Module für Sie aktiviert werden und in welchem Rhythmus Audits stattfinden.',
|
||||
8: 'Als Maschinenbauer gelten zusätzliche Anforderungen: CE-Kennzeichnung, Maschinenverordnung, Produktsicherheit und ggf. Hochrisiko-KI im Sinne des AI Act.',
|
||||
}
|
||||
|
||||
function StepBusinessModel({
|
||||
data,
|
||||
onChange,
|
||||
@@ -243,19 +215,29 @@ function StepBusinessModel({
|
||||
const toggleOffering = (offering: OfferingType) => {
|
||||
const current = data.offerings || []
|
||||
if (current.includes(offering)) {
|
||||
onChange({ offerings: current.filter(o => o !== offering) })
|
||||
// Remove offering and its URL
|
||||
const urls = { ...(data.offeringUrls || {}) }
|
||||
delete urls[offering]
|
||||
onChange({ offerings: current.filter(o => o !== offering), offeringUrls: urls })
|
||||
} else {
|
||||
onChange({ offerings: [...current, offering] })
|
||||
}
|
||||
}
|
||||
|
||||
const updateOfferingUrl = (offering: string, url: string) => {
|
||||
onChange({ offeringUrls: { ...(data.offeringUrls || {}), [offering]: url } })
|
||||
}
|
||||
|
||||
// Offerings that are selected and have URL config
|
||||
const selectedWithUrls = (data.offerings || []).filter(o => o in OFFERING_URL_CONFIG)
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-4">
|
||||
Geschäftsmodell <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
|
||||
{Object.entries(BUSINESS_MODEL_LABELS).map(([value, label]) => (
|
||||
<button
|
||||
key={value}
|
||||
@@ -268,9 +250,9 @@ function StepBusinessModel({
|
||||
}`}
|
||||
>
|
||||
<div className="text-2xl mb-2">
|
||||
{value === 'B2B' ? '🏢' : value === 'B2C' ? '👥' : '🏢👥'}
|
||||
{value === 'B2B' ? '🏢' : value === 'B2C' ? '👥' : value === 'B2B2C' ? '🏢🔗👥' : '🏢👥'}
|
||||
</div>
|
||||
<div className="font-medium">{label}</div>
|
||||
<div className="font-medium text-sm">{label}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -298,6 +280,31 @@ function StepBusinessModel({
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* URL fields for selected offerings */}
|
||||
{selectedWithUrls.length > 0 && (
|
||||
<div className="space-y-4">
|
||||
<label className="block text-sm font-medium text-gray-700">
|
||||
Zugehörige URLs
|
||||
</label>
|
||||
{selectedWithUrls.map(offering => {
|
||||
const config = OFFERING_URL_CONFIG[offering]!
|
||||
return (
|
||||
<div key={offering}>
|
||||
<label className="block text-sm text-gray-600 mb-1">{config.label}</label>
|
||||
<input
|
||||
type="url"
|
||||
value={(data.offeringUrls || {})[offering] || ''}
|
||||
onChange={e => updateOfferingUrl(offering, e.target.value)}
|
||||
placeholder={config.placeholder}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
|
||||
/>
|
||||
<p className="text-xs text-gray-400 mt-1">{config.hint}</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1234,134 +1241,6 @@ function StepMachineBuilder({
|
||||
// COVERAGE ASSESSMENT COMPONENT
|
||||
// =============================================================================
|
||||
|
||||
function CoverageAssessmentPanel({ profile }: { profile: Partial<CompanyProfile> }) {
|
||||
const assessment = assessSDKCoverage(profile)
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-xl border border-gray-200 p-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">SDK-Abdeckung</h3>
|
||||
|
||||
{/* Status */}
|
||||
<div
|
||||
className={`p-4 rounded-lg mb-4 ${
|
||||
assessment.isFullyCovered
|
||||
? 'bg-green-50 border border-green-200'
|
||||
: 'bg-amber-50 border border-amber-200'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{assessment.isFullyCovered ? (
|
||||
<>
|
||||
<svg
|
||||
className="w-5 h-5 text-green-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
/>
|
||||
</svg>
|
||||
<span className="font-medium text-green-800">Vollständig durch SDK abgedeckt</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg
|
||||
className="w-5 h-5 text-amber-600"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
<span className="font-medium text-amber-800">Teilweise Einschränkungen</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Covered Regulations */}
|
||||
{assessment.coveredRegulations.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<div className="text-sm font-medium text-gray-700 mb-2">Abgedeckte Regulierungen</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{assessment.coveredRegulations.map(reg => (
|
||||
<span key={reg} className="px-2 py-1 bg-green-100 text-green-700 text-sm rounded-full">
|
||||
{reg}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Not Covered */}
|
||||
{assessment.notCoveredRegulations.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<div className="text-sm font-medium text-gray-700 mb-2">Nicht abgedeckt</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{assessment.notCoveredRegulations.map(reg => (
|
||||
<span key={reg} className="px-2 py-1 bg-red-100 text-red-700 text-sm rounded-full">
|
||||
{reg}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Recommendations */}
|
||||
{assessment.recommendations.length > 0 && (
|
||||
<div className="mt-4 p-4 bg-blue-50 rounded-lg">
|
||||
<div className="text-sm font-medium text-blue-800 mb-2">Empfehlungen</div>
|
||||
<ul className="text-sm text-blue-700 space-y-1">
|
||||
{assessment.recommendations.map((rec, i) => (
|
||||
<li key={i} className="flex items-start gap-2">
|
||||
<span>•</span>
|
||||
<span>{rec}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Legal Counsel Warning */}
|
||||
{assessment.requiresLegalCounsel && (
|
||||
<div className="mt-4 p-4 bg-amber-50 border border-amber-200 rounded-lg">
|
||||
<div className="flex items-start gap-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-amber-600 mt-0.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
|
||||
/>
|
||||
</svg>
|
||||
<div>
|
||||
<div className="font-medium text-amber-800">Rechtsberatung empfohlen</div>
|
||||
<div className="text-sm text-amber-700 mt-1">
|
||||
Basierend auf Ihrem Profil empfehlen wir die Konsultation eines spezialisierten
|
||||
Rechtsanwalts für Bereiche, die über den Scope dieses SDKs hinausgehen.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// GENERATE DOCUMENTS BUTTON
|
||||
// =============================================================================
|
||||
@@ -1443,6 +1322,7 @@ export default function CompanyProfilePage() {
|
||||
foundedYear: null,
|
||||
businessModel: undefined,
|
||||
offerings: [],
|
||||
offeringUrls: {},
|
||||
companySize: undefined,
|
||||
employeeCount: '',
|
||||
annualRevenue: '',
|
||||
@@ -1486,6 +1366,7 @@ export default function CompanyProfilePage() {
|
||||
foundedYear: data.founded_year || undefined,
|
||||
businessModel: data.business_model || undefined,
|
||||
offerings: data.offerings || [],
|
||||
offeringUrls: data.offering_urls || {},
|
||||
companySize: data.company_size || undefined,
|
||||
employeeCount: data.employee_count || '',
|
||||
annualRevenue: data.annual_revenue || '',
|
||||
@@ -1552,6 +1433,7 @@ export default function CompanyProfilePage() {
|
||||
founded_year: formData.foundedYear || null,
|
||||
business_model: formData.businessModel || 'B2B',
|
||||
offerings: formData.offerings || [],
|
||||
offering_urls: formData.offeringUrls || {},
|
||||
company_size: formData.companySize || 'small',
|
||||
employee_count: formData.employeeCount || '',
|
||||
annual_revenue: formData.annualRevenue || '',
|
||||
@@ -1695,6 +1577,7 @@ export default function CompanyProfilePage() {
|
||||
foundedYear: null,
|
||||
businessModel: undefined,
|
||||
offerings: [],
|
||||
offeringUrls: {},
|
||||
companySize: undefined,
|
||||
employeeCount: '',
|
||||
annualRevenue: '',
|
||||
@@ -1894,15 +1777,13 @@ export default function CompanyProfilePage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Sidebar: Coverage Assessment */}
|
||||
{/* Sidebar */}
|
||||
<div className="lg:col-span-1">
|
||||
<CoverageAssessmentPanel profile={formData} />
|
||||
|
||||
{/* Info Box */}
|
||||
<div className="mt-6 bg-blue-50 rounded-xl border border-blue-200 p-6">
|
||||
{/* Step-specific explanation */}
|
||||
<div className="bg-blue-50 rounded-xl border border-blue-200 p-6">
|
||||
<div className="flex items-start gap-3">
|
||||
<svg
|
||||
className="w-5 h-5 text-blue-600 mt-0.5"
|
||||
className="w-5 h-5 text-blue-600 mt-0.5 flex-shrink-0"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -1917,8 +1798,7 @@ export default function CompanyProfilePage() {
|
||||
<div>
|
||||
<div className="font-medium text-blue-800">Warum diese Fragen?</div>
|
||||
<div className="text-sm text-blue-700 mt-1">
|
||||
Diese Informationen helfen uns, die für Ihr Unternehmen relevanten Regulierungen
|
||||
zu identifizieren und ehrlich zu kommunizieren, wo unsere Grenzen liegen.
|
||||
{STEP_EXPLANATIONS[currentStep] || 'Diese Informationen helfen uns, die für Ihr Unternehmen relevanten Regulierungen zu identifizieren.'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1933,18 +1813,6 @@ export default function CompanyProfilePage() {
|
||||
<GenerateDocumentsButton />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Delete Profile Button */}
|
||||
{formData.companyName && (
|
||||
<div className="mt-6">
|
||||
<button
|
||||
onClick={() => setShowDeleteConfirm(true)}
|
||||
className="w-full px-4 py-2 text-sm text-red-600 border border-red-200 rounded-lg hover:bg-red-50 transition-colors"
|
||||
>
|
||||
Profil löschen (Art. 17 DSGVO)
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@ export type CustomerType = 'new' | 'existing'
|
||||
// COMPANY PROFILE (Business Context - collected before use cases)
|
||||
// =============================================================================
|
||||
|
||||
export type BusinessModel = 'B2B' | 'B2C' | 'B2B_B2C'
|
||||
export type BusinessModel = 'B2B' | 'B2C' | 'B2B_B2C' | 'B2B2C'
|
||||
|
||||
export type OfferingType =
|
||||
| 'app_mobile' // Mobile App
|
||||
@@ -154,6 +154,7 @@ export interface CompanyProfile {
|
||||
// Business Model
|
||||
businessModel: BusinessModel
|
||||
offerings: OfferingType[]
|
||||
offeringUrls: Partial<Record<string, string>> // e.g. { website: 'https://...', webshop: 'https://...' }
|
||||
|
||||
// Size & Scope
|
||||
companySize: CompanySize
|
||||
@@ -204,6 +205,7 @@ export const BUSINESS_MODEL_LABELS: Record<BusinessModel, string> = {
|
||||
B2B: 'B2B (Geschäftskunden)',
|
||||
B2C: 'B2C (Privatkunden)',
|
||||
B2B_B2C: 'B2B und B2C',
|
||||
B2B2C: 'B2B2C (über Partner an Endkunden)',
|
||||
}
|
||||
|
||||
export const OFFERING_TYPE_LABELS: Record<OfferingType, { label: string; description: string }> = {
|
||||
|
||||
@@ -48,7 +48,8 @@ def _get_template_context(db, tid: str) -> dict:
|
||||
resp = row_to_response(row)
|
||||
# Build flat context
|
||||
return {
|
||||
"company_name": resp.company_name,
|
||||
"company_name": f"{resp.company_name} {resp.legal_form}".strip() if resp.legal_form else resp.company_name,
|
||||
"company_name_short": resp.company_name,
|
||||
"legal_form": resp.legal_form,
|
||||
"industry": resp.industry,
|
||||
"business_model": resp.business_model,
|
||||
|
||||
Reference in New Issue
Block a user