t('Description'), 'tenant' => t('Tenant'), 'code' => t('Code'), 'cost_center' => t('Cost center'), 'active' => t('Active'), ]; } public function requiredTargets(): array { return ['description']; } public function autoMapAliases(): array { return [ 'description' => ['description', 'name', 'department', 'abteilung'], 'tenant' => ['tenant', 'tenant_uuid', 'mandant', 'mandant_uuid'], 'code' => ['code', 'department_code', 'abteilungscode'], 'cost_center' => ['cost_center', 'kostenstelle'], 'active' => ['active', 'status', 'aktiv'], ]; } public function supportsAssignments(): bool { return true; } public function requiresAssignmentPermission(): bool { return false; } public function validateMappedRow(array $mapped, array $context): array { $errors = []; $normalized = []; $normalized['description'] = trim((string) ($mapped['description'] ?? '')); if ($normalized['description'] === '') { $errors[] = ['code' => 'mapping_required_missing', 'message' => t('Required field is empty: description')]; } $normalized['code'] = trim((string) ($mapped['code'] ?? '')); $normalized['cost_center'] = trim((string) ($mapped['cost_center'] ?? '')); $activeRaw = trim((string) ($mapped['active'] ?? '')); $active = $this->parseActive($activeRaw); if ($activeRaw !== '' && $active === null) { $errors[] = ['code' => 'invalid_active']; } $normalized['active'] = $active ?? 1; $tenantRaw = trim((string) ($mapped['tenant'] ?? '')); $tenant = null; if ($tenantRaw !== '') { if (!$this->isUuid($tenantRaw)) { $errors[] = ['code' => 'invalid_tenant_uuid']; } else { $tenant = $this->gateway->findTenantByUuid($tenantRaw); if (!$tenant || (string) ($tenant['status'] ?? '') !== 'active') { $errors[] = ['code' => 'tenant_not_found']; } } } if (!$tenant) { $defaultTenantId = (int) ($context['default_tenant_id'] ?? 0); if ($defaultTenantId > 0) { $defaultTenant = $this->gateway->findTenantById($defaultTenantId); if ($defaultTenant && (string) ($defaultTenant['status'] ?? '') === 'active') { $tenant = $defaultTenant; } } } $tenantId = (int) ($tenant['id'] ?? 0); if ($tenantId <= 0) { $errors[] = ['code' => 'tenant_not_found']; } if ($tenantId > 0 && empty($context['is_global_admin'])) { $allowedTenantIds = $context['allowed_tenant_ids'] ?? []; if (!in_array($tenantId, $allowedTenantIds, true)) { $errors[] = ['code' => 'assignment_out_of_scope']; } } $normalized['tenant_id'] = $tenantId; return [ 'ok' => !$errors, 'normalized' => $normalized, 'errors' => $errors, ]; } public function dryRunRow(array $normalized, array $context): array { if ($this->codeExists($normalized)) { return ['status' => 'skipped', 'code' => 'code_exists']; } if ($this->departmentExists($normalized)) { return ['status' => 'skipped', 'code' => 'department_exists']; } return ['status' => 'would_create']; } public function commitRow(array $normalized, array $context): array { if ($this->codeExists($normalized)) { return ['status' => 'skipped', 'code' => 'code_exists']; } if ($this->departmentExists($normalized)) { return ['status' => 'skipped', 'code' => 'department_exists']; } $result = $this->departmentService->createFromAdmin([ 'description' => (string) ($normalized['description'] ?? ''), 'tenant_id' => (int) ($normalized['tenant_id'] ?? 0), 'code' => (string) ($normalized['code'] ?? ''), 'cost_center' => (string) ($normalized['cost_center'] ?? ''), 'active' => (int) ($normalized['active'] ?? 1), ], (int) ($context['current_user_id'] ?? 0)); if (!($result['ok'] ?? false)) { $errors = $result['errors'] ?? []; $message = is_array($errors) && isset($errors[0]) ? (string) $errors[0] : t('Department can not be created'); return ['status' => 'failed', 'code' => 'create_failed', 'message' => $message]; } return [ 'status' => 'created', 'uuid' => (string) ($result['uuid'] ?? ''), ]; } public function inFileDuplicateKey(array $normalized): ?string { $code = $this->lower(trim((string) ($normalized['code'] ?? ''))); if ($code !== '') { return 'code:' . $code; } $tenantId = (int) ($normalized['tenant_id'] ?? 0); $description = $this->lower(trim((string) ($normalized['description'] ?? ''))); if ($tenantId <= 0 || $description === '') { return null; } return 'tenant:' . $tenantId . '|description:' . $description; } public function rowIdentifier(array $normalized): string { $code = trim((string) ($normalized['code'] ?? '')); if ($code !== '') { return $code; } return trim((string) ($normalized['description'] ?? '')); } private function codeExists(array $normalized): bool { $code = trim((string) ($normalized['code'] ?? '')); if ($code === '') { return false; } return $this->gateway->existsDepartmentCode($code); } private function departmentExists(array $normalized): bool { $tenantId = (int) ($normalized['tenant_id'] ?? 0); $description = trim((string) ($normalized['description'] ?? '')); if ($tenantId <= 0 || $description === '') { return false; } return $this->gateway->existsDepartmentByTenantAndDescription($tenantId, $description); } private function parseActive(string $value): ?int { $value = $this->lower(trim($value)); if ($value === '') { return null; } if (in_array($value, ['1', 'true', 'yes', 'active', 'ja', 'aktiv'], true)) { return 1; } if (in_array($value, ['0', 'false', 'no', 'inactive', 'nein', 'inaktiv'], true)) { return 0; } return null; } private function isUuid(string $value): bool { return (bool) preg_match('/^[a-f0-9-]{36}$/i', $value); } private function lower(string $value): string { if (function_exists('mb_strtolower')) { return mb_strtolower($value, 'UTF-8'); } return strtolower($value); } }