add composer-unused, comprehensive docs, and project restructure
- Add icanhazstring/composer-unused as dev dependency for dependency hygiene checks - Add German documentation (docs/) covering architecture, conventions, workflows, and developer checklists - Add API layer (ApiAuth, ApiBootstrap, ApiResponse), audit, scheduler, custom fields, and SSO services - Add Microsoft OIDC SSO, API token management, and user lifecycle features - Add swagger-ui vendor integration and OpenAPI spec - Add production Docker setup and bin/ scripts - Update composer dependencies, config, templates, and frontend assets throughout Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
234
lib/Service/Import/Profile/DepartmentImportProfile.php
Normal file
234
lib/Service/Import/Profile/DepartmentImportProfile.php
Normal file
@@ -0,0 +1,234 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Service\Import\Profile;
|
||||
|
||||
use MintyPHP\Repository\Org\DepartmentRepository;
|
||||
use MintyPHP\Repository\Tenant\TenantRepository;
|
||||
use MintyPHP\Service\Org\DepartmentService;
|
||||
|
||||
class DepartmentImportProfile implements ImportProfileInterface
|
||||
{
|
||||
public function key(): string
|
||||
{
|
||||
return 'departments';
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return t('Departments');
|
||||
}
|
||||
|
||||
public function allowedTargets(): array
|
||||
{
|
||||
return [
|
||||
'description' => 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 = TenantRepository::findByUuid($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 = TenantRepository::find($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 = 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 DepartmentRepository::existsByCode($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 DepartmentRepository::existsByTenantAndDescription($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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user