Module tasks + knowledgecenter_update aus Kundenprojekt übernehmen (bereinigt)
Beide Module wurden aus einem anderen Kundenprojekt kopiert und bereinigt: Knowledgecenter: - Bild-Cache (1.985 Dateien) geleert, Ordnerstruktur mit .gitkeep behalten - Files-Gallery-Lizenzschlüssel entfernt (config.php) - Kundenspezifische Kategorien gelöscht: Teil I/II/III QM-Struktur, nummerierte QM-Kapitel, Gremien (Präsidium/Finanzausschuss/Landesausschuss), Sitzungsservice-Serie, Protokoll-Abteilungen, Testeinträge - 56 generische Kategorien bleiben (Downloads, Onboarding, Personalthemen …) Tasks: - Alle Betriebsdaten gelöscht (114 Tasks, 914 Submissions, 579 Assignments) - Task-Definitionen und Verlinkungen geleert - Kategoriestruktur bleibt (Allgemein, IT, Personal, Buchhaltung …) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
139
module/tasks/service/CertificateService.php
Normal file
139
module/tasks/service/CertificateService.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
class CertificateService
|
||||
{
|
||||
private TaskCertificateRepository $certificateRepo;
|
||||
private TaskAssignmentRepository $assignmentRepo;
|
||||
private TaskDefinitionRepository $definitionRepo;
|
||||
private CycleCalculatorService $cycleCalculator;
|
||||
|
||||
public function __construct(
|
||||
TaskCertificateRepository $certificateRepo,
|
||||
TaskAssignmentRepository $assignmentRepo,
|
||||
TaskDefinitionRepository $definitionRepo,
|
||||
CycleCalculatorService $cycleCalculator
|
||||
) {
|
||||
$this->certificateRepo = $certificateRepo;
|
||||
$this->assignmentRepo = $assignmentRepo;
|
||||
$this->definitionRepo = $definitionRepo;
|
||||
$this->cycleCalculator = $cycleCalculator;
|
||||
}
|
||||
|
||||
public function issueForAssignment(int $assignmentId, int $submissionId): int
|
||||
{
|
||||
$existing = $this->certificateRepo->findByAssignmentId($assignmentId);
|
||||
if ($existing !== null) {
|
||||
return (int) $existing['id'];
|
||||
}
|
||||
|
||||
$assignment = $this->assignmentRepo->findById($assignmentId);
|
||||
if ($assignment === null) {
|
||||
throw new RuntimeException('Assignment nicht gefunden');
|
||||
}
|
||||
|
||||
$task = $this->definitionRepo->findById((int) $assignment['task_id']);
|
||||
if ($task === null) {
|
||||
throw new RuntimeException('Aufgabe nicht gefunden');
|
||||
}
|
||||
|
||||
if ((int) $task['certificate_enabled'] !== 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$validFrom = new DateTimeImmutable();
|
||||
$validUntil = null;
|
||||
|
||||
$validityMode = $this->resolveValidityMode($task);
|
||||
if ($validityMode === 'days') {
|
||||
$validityDays = (int) ($task['certificate_validity_days'] ?? 0);
|
||||
if ($validityDays > 0) {
|
||||
$validUntil = $validFrom->modify('+' . $validityDays . ' days')->format('Y-m-d H:i:s');
|
||||
}
|
||||
} elseif ($validityMode === 'cycle') {
|
||||
$validUntil = $this->resolveCycleValidUntil($task, $assignment, $validFrom);
|
||||
}
|
||||
|
||||
return $this->certificateRepo->create([
|
||||
'task_id' => (int) $assignment['task_id'],
|
||||
'assignment_id' => $assignmentId,
|
||||
'submission_id' => $submissionId,
|
||||
'main_contact_id' => (int) $assignment['main_contact_id'],
|
||||
'certificate_code' => $this->buildCertificateCode(),
|
||||
'valid_from' => $validFrom->format('Y-m-d H:i:s'),
|
||||
'valid_until' => $validUntil,
|
||||
'status' => 'valid',
|
||||
]);
|
||||
}
|
||||
|
||||
public function listUserCertificates(int $mainContactId): array
|
||||
{
|
||||
return $this->certificateRepo->listForUser($mainContactId);
|
||||
}
|
||||
|
||||
public function listUserTaskCertificates(int $mainContactId, int $taskId): array
|
||||
{
|
||||
if ($taskId <= 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this->certificateRepo->listForUserTask($mainContactId, $taskId);
|
||||
}
|
||||
|
||||
public function getUserTaskCertificateSummary(int $mainContactId, array $taskIds): array
|
||||
{
|
||||
return $this->certificateRepo->summarizeForUserTasks($mainContactId, $taskIds);
|
||||
}
|
||||
|
||||
public function refreshExpiredCertificates(): int
|
||||
{
|
||||
return $this->certificateRepo->markExpiredByDate((new DateTimeImmutable())->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
private function buildCertificateCode(): string
|
||||
{
|
||||
return strtoupper(bin2hex(random_bytes(8)));
|
||||
}
|
||||
|
||||
private function resolveCycleValidUntil(array $task, array $assignment, DateTimeImmutable $reference): ?string
|
||||
{
|
||||
$assignmentCycleEnd = trim((string) ($assignment['cycle_end_at'] ?? ''));
|
||||
if ($assignmentCycleEnd !== '') {
|
||||
try {
|
||||
return (new DateTimeImmutable($assignmentCycleEnd))->format('Y-m-d H:i:s');
|
||||
} catch (Throwable $throwable) {
|
||||
// fallback to calculated cycle
|
||||
}
|
||||
}
|
||||
|
||||
if ((string) ($task['frequency_type'] ?? 'once') !== 'recurring') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cycle = $this->cycleCalculator->resolveCurrentCycle($task, $reference);
|
||||
if ($cycle === [] || !($cycle['cycle_end_at'] ?? null) instanceof DateTimeImmutable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $cycle['cycle_end_at']->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
private function resolveValidityMode(array $task): string
|
||||
{
|
||||
$mode = (string) ($task['certificate_validity_mode'] ?? '');
|
||||
if (in_array($mode, ['cycle', 'days', 'permanent'], true)) {
|
||||
return $mode;
|
||||
}
|
||||
|
||||
$days = (int) ($task['certificate_validity_days'] ?? 0);
|
||||
if ($days > 0) {
|
||||
return 'days';
|
||||
}
|
||||
|
||||
if ((string) ($task['frequency_type'] ?? 'once') === 'recurring') {
|
||||
return 'cycle';
|
||||
}
|
||||
|
||||
return 'permanent';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user