OpenXE/classes/Modules/Resubmission/Service/ResubmissionTaskService.php

463 lines
16 KiB
PHP
Raw Normal View History

2021-05-21 08:49:41 +02:00
<?php
namespace Xentral\Modules\Resubmission\Service;
use Xentral\Components\Database\Database;
use Xentral\Components\Database\Exception\DatabaseExceptionInterface;
use Xentral\Modules\Resubmission\Data\ResubmissionTaskData;
use Xentral\Modules\Resubmission\Exception\InvalidArgumentException;
use Xentral\Modules\Resubmission\Exception\ResubmissionNotFoundException;
use Xentral\Modules\Resubmission\Exception\ResubmissionTaskNotFoundException;
use Xentral\Modules\Resubmission\Exception\TaskMustBeCompletedException;
final class ResubmissionTaskService
{
/** @var Database $db */
private $db;
/** @var ResubmissionTaskGateway $taskGateway */
private $taskGateway;
/** @var ResubmissionGateway $resubmissionGateway */
private $resubmissionGateway;
/**
* @param Database $database
* @param ResubmissionTaskGateway $taskGateway
* @param ResubmissionGateway $resubmissionGateway
*/
public function __construct(
Database $database,
ResubmissionTaskGateway $taskGateway,
ResubmissionGateway $resubmissionGateway
) {
$this->db = $database;
$this->taskGateway = $taskGateway;
$this->resubmissionGateway = $resubmissionGateway;
}
/**
* Aufgabe auf "abgeschlossen" stellen
*
* @param int $taskId
* @param int $resubmissionId
*
* @throws ResubmissionTaskNotFoundException
*
* @return void
*/
public function markTaskAsCompleted($taskId, $resubmissionId)
{
if (!$this->taskGateway->isTaskAssigendToResubmission($taskId, $resubmissionId)) {
throw new ResubmissionTaskNotFoundException(sprintf(
'Task not found. Task-ID: %s - Resubmission-ID: %s', $taskId, $resubmissionId
));
}
$sql = 'UPDATE `aufgabe`
SET `status` = :state, `abgeschlossen_am` = :completion_date, `logdatei` = NOW()
WHERE `id` = :task_id
LIMIT 1';
$this->db->perform($sql, [
'state' => 'abgeschlossen',
'completion_date' => date('Y-m-d'),
'task_id' => (int)$taskId,
]);
}
/**
* Aufgabe auf "offen" stellen
*
* @param int $taskId
* @param int $resubmissionId
*
* @throws ResubmissionTaskNotFoundException
* @throws TaskMustBeCompletedException
*
* @return void
*/
public function markTaskAsOpen($taskId, $resubmissionId)
{
if (!$this->taskGateway->isTaskAssigendToResubmission($taskId, $resubmissionId)) {
throw new ResubmissionTaskNotFoundException(sprintf(
'Task not found. Task-ID: %s - Resubmission-ID: %s', $taskId, $resubmissionId
));
}
$task = $this->taskGateway->getTask($taskId);
// Vor der Änderung prüfen ob Änderung überhaupt gültig wäre
$check = $this->isTaskStageChangeAllowed(
$task['stage_id'],
$task['required_completion_stage_id'],
ResubmissionTaskData::STATE_OPEN
);
if (!$check) {
$currentStage = $this->resubmissionGateway->getStage($task['stage_id']);
$requiredStage = $this->resubmissionGateway->getStage($task['required_completion_stage_id']);
throw TaskMustBeCompletedException::onChangingStateToOpen(
$requiredStage['shortname'],
$currentStage['shortname']
);
}
$sql = 'UPDATE `aufgabe`
SET `status` = :state, `abgeschlossen_am` = :completion_date, `logdatei` = NOW()
WHERE `id` = :task_id
LIMIT 1';
$this->db->fetchAffected($sql, [
'state' => 'offen',
'completion_date' => '0000-00-00',
'task_id' => (int)$taskId,
]);
}
/**
* @param ResubmissionTaskData $task
*
* @throws ResubmissionTaskNotFoundException
* @throws TaskMustBeCompletedException
* @throws DatabaseExceptionInterface
*
* @return void
*/
public function createTask(ResubmissionTaskData $task)
{
if (!$this->resubmissionGateway->existsResubmission($task->getResubmissionId())) {
throw new ResubmissionNotFoundException(sprintf(
'Resubmission not found. ID: %s', $task->getResubmissionId()
));
}
// Vor der Erstellung prüfen ob Task-Status überhaupt gültig wäre
$currentStage = $this->resubmissionGateway->getStageByResubmission($task->getResubmissionId());
$check = $this->isTaskStageChangeAllowed(
$currentStage['id'],
$task->getRequiredCompletionStageId(),
$task->getState()
);
if (!$check) {
$requiredStage = $this->resubmissionGateway->getStage($task->getRequiredCompletionStageId());
throw TaskMustBeCompletedException::onCreation(
$requiredStage['shortname'],
$currentStage['shortname']
);
}
$priority = $this->translatePriorityToDbValue($task->getPriority());
$state = $this->translateStateToDbValue($task->getState());
$insert = $this->db->insert();
$insert->into('aufgabe');
$insert->col('aufgabe', $task->getTitle());
$insert->col('prio', $priority);
$insert->col('status', $state);
$insert->col('initiator', (int)$task->getCreatorAddressId());
// Optionale Felder
$insert->col('adresse', (int)$task->getEmployeeAddressId());
$insert->col('kunde', (int)$task->getCustomerAddressId());
$insert->col('projekt', (int)$task->getProjectId());
$insert->col('teilprojekt', (int)$task->getSubProjectId());
$insert->col('beschreibung', (string)$task->getDescription());
if ($task->getSubmissionDateTime() !== null) {
$insert->col('abgabe_bis', $task->getSubmissionDateTime()->format('Y-m-d'));
$insert->col('abgabe_bis_zeit', $task->getSubmissionDateTime()->format('H:i:s'));
} else {
$insert->col('abgabe_bis', '0000-00-00');
$insert->col('abgabe_bis_zeit', '00:00:00');
}
$insert->col('angelegt_am', date('Y-m-d'));
$insert->set('logdatei', 'NOW()');
$this->db->beginTransaction();
try {
$this->db->perform($insert->getStatement(), $insert->getBindValues());
$insertId = $this->db->lastInsertId();
$this->db->perform(
'INSERT INTO `wiedervorlage_aufgabe` (`task_id`, `resubmission_id`, `required_completion_stage_id`)
VALUES (:task_id, :resubmission_id, :required_completion_stage_id)',
[
'task_id' => $insertId,
'resubmission_id' => $task->getResubmissionId(),
'required_completion_stage_id' => $task->getRequiredCompletionStageId(),
]
);
$this->db->commit();
//
} catch (DatabaseExceptionInterface $exception) {
$this->db->rollBack();
throw $exception;
}
}
/**
* @param ResubmissionTaskData $task
*
* @throws ResubmissionTaskNotFoundException
* @throws TaskMustBeCompletedException
*
* @return void
*/
public function editTask(ResubmissionTaskData $task)
{
if (!$this->taskGateway->isTaskAssigendToResubmission($task->getId(), $task->getResubmissionId())) {
throw new ResubmissionTaskNotFoundException(sprintf(
'Task not found. Task-ID: %s - Resubmission-ID: %s', $task->getId(), $task->getResubmissionId()
));
}
// Vor der Änderung prüfen ob Änderung überhaupt gültig wäre
$currentStage = $this->resubmissionGateway->getStageByResubmission($task->getResubmissionId());
$check = $this->isTaskStageChangeAllowed(
$currentStage['id'],
$task->getRequiredCompletionStageId(),
$task->getState()
);
if (!$check) {
$requiredStage = $this->resubmissionGateway->getStage($task->getRequiredCompletionStageId());
throw TaskMustBeCompletedException::onModification(
$requiredStage['shortname'],
$currentStage['shortname']
);
}
$priority = $this->translatePriorityToDbValue($task->getPriority());
$state = $this->translateStateToDbValue($task->getState());
$update = $this->db->update();
$update->table('aufgabe');
$update->col('aufgabe', $task->getTitle());
$update->col('prio', $priority);
$update->col('status', $state);
if ($task->getEmployeeAddressId() !== null) {
$update->col('adresse', $task->getEmployeeAddressId());
}
if ($task->getProjectId() !== null) {
$update->col('projekt', $task->getProjectId());
}
if ($task->getSubProjectId() !== null) {
$update->col('teilprojekt', $task->getSubProjectId());
}
if ($task->getDescription() !== null) {
$update->col('beschreibung', $task->getDescription());
} else {
$update->col('beschreibung', '');
}
if ($task->getCustomerAddressId() !== null) {
$update->col('kunde', $task->getCustomerAddressId());
} else {
$update->col('kunde', 0);
}
if ($task->getCompletionDateTime() !== null) {
if ($task->getCompletionDateTime()->getTimestamp() > 0) {
$update->col('abgeschlossen_am', $task->getCompletionDateTime()->format('Y-m-d'));
} else {
$update->col('abgeschlossen_am', '0000-00-00');
}
}
if ($task->getSubmissionDateTime() !== null) {
$update->col('abgabe_bis', $task->getSubmissionDateTime()->format('Y-m-d'));
$update->col('abgabe_bis_zeit', $task->getSubmissionDateTime()->format('H:i:s'));
} else {
$update->col('abgabe_bis', '0000-00-00');
$update->col('abgabe_bis_zeit', '00:00:00');
}
$update->set('logdatei', 'NOW()');
$update->where('id = ?', $task->getId());
$update->limit(1);
try {
$this->db->beginTransaction();
$this->db->perform($update->getStatement(), $update->getBindValues());
$this->db->perform(
'UPDATE `wiedervorlage_aufgabe` SET `required_completion_stage_id` = :required_completion_stage_id
WHERE `task_id` = :task_id AND `resubmission_id` = :resubmission_id LIMIT 1',
[
'task_id' => $task->getId(),
'resubmission_id' => $task->getResubmissionId(),
'required_completion_stage_id' => $task->getRequiredCompletionStageId(),
]
);
$this->db->commit();
//
} catch (DatabaseExceptionInterface $exception) {
$this->db->rollBack();
throw $exception;
}
}
/**
* Aufgabe löschen
*
* @param int $taskId
* @param int $resubmissionId
*
* @throws ResubmissionTaskNotFoundException
*
* @return void
*/
public function deleteTask($taskId, $resubmissionId)
{
$taskId = (int)$taskId;
$resubmissionId = (int)$resubmissionId;
if (!$this->taskGateway->isTaskAssigendToResubmission($taskId, $resubmissionId)) {
throw new ResubmissionTaskNotFoundException(sprintf(
'Task not found. Task-ID: %s - Resubmission-ID: %s', $taskId, $resubmissionId
));
}
$this->db->beginTransaction();
try {
// Aufgabe löschen
$sql = 'DELETE FROM `aufgabe` WHERE `id` = :task_id LIMIT 1 ';
$this->db->perform($sql, ['task_id' => $taskId]);
// Verknüpfung zur Wiedervorlage löschen
$sql =
'DELETE FROM `wiedervorlage_aufgabe`
WHERE `task_id` = :task_id AND `resubmission_id` = :resubmission_id
LIMIT 1 ';
$this->db->perform($sql, [
'resubmission_id' => $resubmissionId,
'task_id' => $taskId,
]);
$this->db->commit();
} catch (DatabaseExceptionInterface $exception) {
$this->db->rollBack();
throw $exception;
}
}
/**
* Ermittelt alle Aufgaben die das Verschieben einer Wiedervorlage blockieren
*
* @param int $resubmissionId
* @param int $targetStageId
*
* @return array Empty array if none item is blocking
*/
public function getBlockingTasksForTargetStage($resubmissionId, $targetStageId)
{
$resubmissionId = (int)$resubmissionId;
$targetStageId = (int)$targetStageId;
$tasks = $this->taskGateway->getTasksByResubmission($resubmissionId);
$blocking = [];
foreach ($tasks as $task) {
if ($task['state'] === ResubmissionTaskData::STATE_COMPLETED) {
continue; // Aufgabe ist bereits abgeschlossen > Aufgabe darf in jede Stage geschoben werden
}
if ($task['required_completion_stage_id'] === 0) {
continue; // Aufgabe hat keine Fertigstellungs-Stage hinterlegt > Aufgabe darf in jede Stage geschoben werden
}
$distance = $this->resubmissionGateway->getDistanceBetweenStages(
$targetStageId,
$task['required_completion_stage_id']
);
if ($distance <= 0) {
$blocking[] = [
'id' => $task['id'],
'title' => $task['title'],
'state' => $task['state'],
];
}
}
return $blocking;
}
/**
* Prüft ob eine Aufgabe in die Target-Stage wecheln darf
*
* @param int $currentStageId ID der Stage von der aus verschoben wird
* @param int $targetStageId ID der Stage in die verschoben werden soll
* @param string $targetState Aufgaben-Status der zugewiesen soll bzw. aktuell gesetzt ist
*
* @throws InvalidArgumentException
*
* @return bool true = Aufgabe darf auf Target-Stage wechseln
* false = Aufgabe muss abgeschlossen sein, um auf die Target-Stage wechseln zu dürfen
*/
private function isTaskStageChangeAllowed($currentStageId, $targetStageId, $targetState)
{
$currentStageId = (int)$currentStageId;
$targetStageId = (int)$targetStageId;
if (!in_array($targetState, ResubmissionTaskData::getValidStates(), true)) {
throw new InvalidArgumentException(sprintf('Target state is invalid: "%s"', $targetState));
}
// Aufgabe auf abgeschlossen stellen => Immer OK; solange die Aufgabe existiert
if ($targetState === ResubmissionTaskData::STATE_COMPLETED) {
return true;
}
// Es ist keine Stage-ID festgelegt bei der die Aufgabe abgeschlossen sein muss
// > Alles Roger, solange die Aufgabe existiert
if ($targetStageId === 0) {
return true;
}
$distance = $this->resubmissionGateway->getDistanceBetweenStages($currentStageId, $targetStageId);
return $distance > 0;
}
/**
* @param string $state
*
* @return string|null
*/
private function translateStateToDbValue($state)
{
$dbValue = null;
switch ($state) {
case ResubmissionTaskData::STATE_COMPLETED:
$dbValue = 'abgeschlossen';
break;
case ResubmissionTaskData::STATE_PROCESSING:
$dbValue = 'inbearbeitung';
break;
case ResubmissionTaskData::STATE_OPEN:
$dbValue = 'offen';
break;
}
return $dbValue;
}
/**
* @param string $priority
*
* @return int|null
*/
private function translatePriorityToDbValue($priority)
{
$dbValue = null;
switch ($priority) {
case ResubmissionTaskData::PRIORITY_HIGH:
$dbValue = 1;
break;
case ResubmissionTaskData::PRIORITY_LOW:
$dbValue = -1;
break;
case ResubmissionTaskData::PRIORITY_MEDIUM:
$dbValue = 0;
break;
}
return $dbValue;
}
}