mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-12-29 08:00:28 +01:00
463 lines
16 KiB
PHP
463 lines
16 KiB
PHP
|
<?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;
|
||
|
}
|
||
|
}
|