Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\Resubmission\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Resubmission\Exception\StageNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Exception\ResubmissionNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Exception\ViewNotFoundException;
|
||||
|
||||
final class ResubmissionGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
*/
|
||||
public function __construct(Database $database)
|
||||
{
|
||||
$this->db = $database;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function existsResubmission($resubmissionId)
|
||||
{
|
||||
$sql = 'SELECT w.id FROM `wiedervorlage` AS `w` WHERE w.id = :resubmission_id';
|
||||
$check = $this->db->fetchValue($sql, ['resubmission_id' => (int)$resubmissionId]);
|
||||
|
||||
return (int)$check === (int)$resubmissionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @throws ResubmissionNotFoundException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getViewIdByResubmission($resubmissionId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT ws.view AS `view_id`
|
||||
FROM `wiedervorlage` AS `w`
|
||||
LEFT JOIN `wiedervorlage_stages` AS `ws` ON w.stages = ws.id
|
||||
WHERE w.id = :resubmission_id';
|
||||
$viewId = $this->db->fetchValue($sql, ['resubmission_id' => (int)$resubmissionId]);
|
||||
|
||||
if ($viewId === false) {
|
||||
throw new ResubmissionNotFoundException(sprintf('Resubmission not found: ID%s', $resubmissionId));
|
||||
}
|
||||
|
||||
return (int)$viewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $stageId
|
||||
*
|
||||
* @throws StageNotFoundException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getViewIdByStage($stageId)
|
||||
{
|
||||
$sql = 'SELECT ws.view AS `view_id` FROM `wiedervorlage_stages` AS `ws` WHERE ws.id = :stage_id';
|
||||
$viewId = $this->db->fetchValue($sql, ['stage_id' => (int)$stageId]);
|
||||
|
||||
if ($viewId === false) {
|
||||
throw new StageNotFoundException(sprintf('Stage not found: ID%s', $stageId));
|
||||
}
|
||||
|
||||
return (int)$viewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $viewId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStagesByView($viewId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
ws.id,
|
||||
IF(ws.kurzbezeichnung != \'\', ws.kurzbezeichnung, ws.name) AS `shortname`,
|
||||
ws.name AS `longname`
|
||||
FROM `wiedervorlage_stages` AS `ws`
|
||||
LEFT JOIN `wiedervorlage_view` AS `wv` ON ws.view = wv.id AND wv.active = 1
|
||||
WHERE ws.view = :view_id
|
||||
ORDER BY ws.sort, ws.id';
|
||||
$stages = $this->db->fetchAll($sql, ['view_id' => (int)$viewId]);
|
||||
|
||||
$rank = 1;
|
||||
foreach ($stages as &$stage) {
|
||||
$stage['rank'] = $rank;
|
||||
$rank++;
|
||||
}
|
||||
unset($stage);
|
||||
|
||||
return $stages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle Geschwister-Stages ermitteln
|
||||
*
|
||||
* D.h. alle Stages ermitteln die sich in der gleichen View befinden wie die übergebene Stage
|
||||
*
|
||||
* @param int $stageId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getSiblingStages($stageId)
|
||||
{
|
||||
$viewId = $this->getViewIdByStage($stageId);
|
||||
|
||||
return $this->getStagesByView($viewId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $stageId
|
||||
*
|
||||
* @throws StageNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStage($stageId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
ws.id,
|
||||
IF(ws.kurzbezeichnung != \'\', ws.kurzbezeichnung, ws.name) AS `shortname`,
|
||||
ws.name AS `longname`
|
||||
FROM `wiedervorlage_stages` AS `ws`
|
||||
WHERE ws.id = :stage_id';
|
||||
$stage = $this->db->fetchRow($sql, ['stage_id' => (int)$stageId]);
|
||||
if (empty($stage)) {
|
||||
throw new StageNotFoundException(sprintf('Stage ID "%s" not found', $stageId));
|
||||
}
|
||||
|
||||
return $stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @throws ResubmissionNotFoundException
|
||||
* @throws StageNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStageByResubmission($resubmissionId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
w.id AS resubmission_id,
|
||||
ws.view AS view_id,
|
||||
ws.id,
|
||||
IF(ws.kurzbezeichnung != \'\', ws.kurzbezeichnung, ws.name) AS `shortname`,
|
||||
ws.name AS `longname`
|
||||
FROM `wiedervorlage` AS `w`
|
||||
LEFT JOIN `wiedervorlage_stages` AS `ws` ON w.stages = ws.id
|
||||
WHERE w.id = :resubmissionId';
|
||||
$result = $this->db->fetchRow($sql, ['resubmissionId' => (int)$resubmissionId]);
|
||||
|
||||
if (empty($result['resubmission_id'])) {
|
||||
throw new ResubmissionNotFoundException(
|
||||
sprintf(
|
||||
'Resubmission not found: ID%s',
|
||||
$resubmissionId
|
||||
)
|
||||
);
|
||||
}
|
||||
if (empty($result['id'])) {
|
||||
throw new StageNotFoundException(
|
||||
sprintf(
|
||||
'Stage not found for resubmission: Resubmission-ID%s',
|
||||
$resubmissionId
|
||||
)
|
||||
);
|
||||
}
|
||||
unset($result['resubmission_id']);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId Wiedervorlagen-ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStagesByResubmission($resubmissionId)
|
||||
{
|
||||
// View-ID der aktuellen Stage ermitteln; View-ID darf 0 sein; 0 = Standard-View
|
||||
$viewId = $this->getViewIdByResubmission($resubmissionId);
|
||||
|
||||
$sql =
|
||||
'SELECT
|
||||
ws.id,
|
||||
IF(ws.kurzbezeichnung != \'\', ws.kurzbezeichnung, ws.name) AS `shortname`,
|
||||
ws.name AS `longname`
|
||||
FROM `wiedervorlage_stages` AS `ws`
|
||||
LEFT JOIN `wiedervorlage_view` AS `wv` ON ws.view = wv.id AND wv.active = 1
|
||||
WHERE ws.view = :view_id
|
||||
ORDER BY ws.sort, ws.id';
|
||||
|
||||
return $this->db->fetchAll($sql, ['view_id' => $viewId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getStagesWithViews()
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
ws.id,
|
||||
ws.name AS stage_name,
|
||||
wv.name AS view_name
|
||||
FROM `wiedervorlage_stages` AS `ws`
|
||||
LEFT JOIN `wiedervorlage_view` AS `wv` ON wv.id = ws.view
|
||||
ORDER BY ws.view, ws.sort ';
|
||||
$stages = $this->db->fetchAll($sql);
|
||||
|
||||
$result = [];
|
||||
foreach ($stages as $stage) {
|
||||
$viewName = $stage['view_name'] !== null ? $stage['view_name'] : 'Standard';
|
||||
$result[] = [
|
||||
'id' => (int)$stage['id'],
|
||||
'label' => sprintf('%s > %s', $viewName, $stage['stage_name']),
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $sourceStageId ID der Stage von der aus verschoben wird
|
||||
* @param int $targetStageId ID der Stage in die verschoben werden soll
|
||||
*
|
||||
* @throws StageNotFoundException
|
||||
*
|
||||
* @return int Positiver Wert = Target-Stage befindet sich in aufsteigender Position
|
||||
* Negativer Wert = Target-Stage befindet sich vor der Ursprungs-Stage
|
||||
* Null = Source- und Target-Stage sind identisch
|
||||
*/
|
||||
public function getDistanceBetweenStages($sourceStageId, $targetStageId)
|
||||
{
|
||||
$sourceStageId = (int)$sourceStageId;
|
||||
$targetStageId = (int)$targetStageId;
|
||||
$stages = $this->getSiblingStages($targetStageId);
|
||||
|
||||
// Prüfen ob übergebene Stage-ID überhaupt valide ist; Muss in der selben Ansicht/View sein
|
||||
$validStageIds = array_column($stages, 'id');
|
||||
$isTargetStageIdValid = in_array($targetStageId, $validStageIds, true);
|
||||
if ($isTargetStageIdValid === false) {
|
||||
throw new StageNotFoundException(sprintf('Target stage ID "%s" not found', $targetStageId));
|
||||
}
|
||||
|
||||
$targetStageRank = 0;
|
||||
$sourceStageRank = 0;
|
||||
$rank = 1;
|
||||
foreach ($stages as $stage) {
|
||||
if ((int)$stage['id'] === $sourceStageId) {
|
||||
$sourceStageRank = $rank;
|
||||
}
|
||||
if ((int)$stage['id'] === $targetStageId) {
|
||||
$targetStageRank = $rank;
|
||||
}
|
||||
$rank++;
|
||||
}
|
||||
|
||||
return $targetStageRank - $sourceStageRank;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getById($resubmissionId)
|
||||
{
|
||||
if (!is_numeric($resubmissionId)) {
|
||||
throw new ResubmissionNotFoundException(
|
||||
sprintf(
|
||||
'Resubmission not found: ID%s',
|
||||
$resubmissionId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$sql = 'SELECT w.id,
|
||||
w.adresse,
|
||||
w.projekt,
|
||||
w.parameter,
|
||||
w.abgeschlossen,
|
||||
w.action,
|
||||
w.adresse_mitarbeiter,
|
||||
w.bearbeiter,
|
||||
w.beschreibung,
|
||||
w.bezeichnung,
|
||||
w.betrag,
|
||||
w.ergebnis,
|
||||
w.erinnerung,
|
||||
w.erinnerung_empfaenger,
|
||||
w.datum_erinnerung,
|
||||
w.zeit_erinnerung,
|
||||
w.datum_status,
|
||||
w.datum_abschluss,
|
||||
w.datum_angelegt,
|
||||
w.stages,
|
||||
w.prio,
|
||||
w.color,
|
||||
w.chance,
|
||||
w.status,
|
||||
w.module,
|
||||
w.link,
|
||||
w.oeffentlich,
|
||||
w.erinnerung_per_mail,
|
||||
w.zeit_angelegt
|
||||
FROM `wiedervorlage` AS `w` WHERE w.id = :resubmission_id';
|
||||
|
||||
return $this->db->fetchRow($sql, ['resubmission_id' => (int)$resubmissionId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
*
|
||||
* @throws ViewNotFoundException
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getViewIdByNameAndDescription(string $name, string $description): int
|
||||
{
|
||||
$viewId = $this->db->fetchValue(
|
||||
'SELECT wv.id FROM `wiedervorlage_view` AS `wv`
|
||||
WHERE wv.name = :name AND wv.shortname = :desc AND wv.active = :active AND wv.project = 0',
|
||||
[
|
||||
'name' => $name,
|
||||
'desc' => $description,
|
||||
'active' => 1,
|
||||
]
|
||||
);
|
||||
|
||||
if ($viewId === false) {
|
||||
throw new ViewNotFoundException(sprintf('View not found for name:%s and desc:%s', $name, $description));
|
||||
}
|
||||
|
||||
return (int)$viewId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $viewId
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getMaxSortByViewId(int $viewId): int
|
||||
{
|
||||
$maxPosition = $this->db->fetchValue(
|
||||
'SELECT MAX(ws.sort) FROM `wiedervorlage_stages` AS `ws` WHERE ws.`view` = :id',
|
||||
['id' => $viewId]
|
||||
);
|
||||
|
||||
return (int)$maxPosition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\Resubmission\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Resubmission\Exception\ResubmissionTaskNotFoundException;
|
||||
|
||||
final class ResubmissionTaskGateway
|
||||
{
|
||||
/** @var ResubmissionGateway $gateway */
|
||||
private $gateway;
|
||||
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* @param Database $db
|
||||
* @param ResubmissionGateway $gateway
|
||||
*/
|
||||
public function __construct(Database $db, ResubmissionGateway $gateway)
|
||||
{
|
||||
$this->gateway = $gateway;
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId Wiedervorlagen-ID
|
||||
*
|
||||
* @return array Empty array if no result
|
||||
*/
|
||||
public function getTasksByResubmission($resubmissionId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
a.id,
|
||||
CASE
|
||||
WHEN a.status = \'offen\' THEN \'open\'
|
||||
WHEN a.status = \'inbearbeitung\' THEN \'processing\'
|
||||
WHEN a.status = \'abgeschlossen\' THEN \'completed\'
|
||||
END AS `state`,
|
||||
CASE
|
||||
WHEN a.prio = \'1\' THEN \'high\'
|
||||
WHEN a.prio = \'-1\' THEN \'low\'
|
||||
ELSE \'medium\'
|
||||
END AS `priority`,
|
||||
a.adresse AS `address_id`,
|
||||
a.initiator AS `creator_address_id`,
|
||||
a.aufgabe AS `title`,
|
||||
a.beschreibung AS `description`,
|
||||
DATE_FORMAT(a.abgabe_bis, \'%d.%m.%Y\') AS `submission_date`,
|
||||
TIME_FORMAT(a.abgabe_bis_zeit, \'%H:%i\') AS `submission_time`,
|
||||
wa.required_completion_stage_id
|
||||
FROM `aufgabe` AS `a`
|
||||
INNER JOIN `wiedervorlage_aufgabe` AS `wa` ON wa.task_id = a.id
|
||||
INNER JOIN `wiedervorlage` AS `w` ON wa.resubmission_id = w.id
|
||||
WHERE w.id = :resubmission_id';
|
||||
|
||||
return $this->db->fetchAll($sql, ['resubmission_id' => (int)$resubmissionId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ist die Aufgabe der übergebenen Wiedervorlage zugeordnet?
|
||||
*
|
||||
* Gleichzeitig wird auch geprüft ob Aufgabe existiert.
|
||||
*
|
||||
* @param int $taskId
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isTaskAssigendToResubmission($taskId, $resubmissionId)
|
||||
{
|
||||
$taskId = (int)$taskId;
|
||||
$resubmissionId = (int)$resubmissionId;
|
||||
if ($taskId <= 0 || $resubmissionId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$sql = 'SELECT a.id
|
||||
FROM `aufgabe` AS `a`
|
||||
INNER JOIN `wiedervorlage_aufgabe` AS `wa` ON wa.task_id = a.id
|
||||
WHERE wa.resubmission_id = :resubmission_id
|
||||
AND a.id = :task_id';
|
||||
$taskIdCheck = $this->db->fetchValue($sql, [
|
||||
'task_id' => $taskId,
|
||||
'resubmission_id' => $resubmissionId,
|
||||
]);
|
||||
|
||||
return $taskId === $taskIdCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $taskId
|
||||
*
|
||||
* @throws ResubmissionTaskNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTask($taskId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
a.id,
|
||||
w.stages AS `stage_id`,
|
||||
a.projekt AS `project_id`,
|
||||
a.teilprojekt AS `subproject_id`,
|
||||
a.adresse AS `employee_id`,
|
||||
CONCAT(adr.mitarbeiternummer, \' \', adr.name) AS `employee_name`,
|
||||
CONCAT(a.kunde, \' \', cus.name, \' (Kdr: \', cus.kundennummer, \')\') as `customer`,
|
||||
a.aufgabe AS `title`,
|
||||
a.beschreibung AS `description`,
|
||||
DATE_FORMAT(a.abgabe_bis, \'%d.%m.%Y\') AS `submission_date`,
|
||||
TIME_FORMAT(a.abgabe_bis_zeit, \'%H:%i\') AS `submission_time`,
|
||||
CASE
|
||||
WHEN a.status = \'offen\' THEN \'open\'
|
||||
WHEN a.status = \'inbearbeitung\' THEN \'processing\'
|
||||
WHEN a.status = \'abgeschlossen\' THEN \'completed\'
|
||||
END AS `state`,
|
||||
CASE
|
||||
WHEN a.prio = \'1\' THEN \'high\'
|
||||
WHEN a.prio = \'-1\' THEN \'low\'
|
||||
ELSE \'medium\'
|
||||
END AS `priority`,
|
||||
wa.required_completion_stage_id
|
||||
FROM `aufgabe` AS `a`
|
||||
INNER JOIN `wiedervorlage_aufgabe` AS `wa` ON wa.task_id = a.id
|
||||
INNER JOIN `wiedervorlage` AS `w` ON wa.resubmission_id = w.id
|
||||
LEFT JOIN `adresse` AS adr ON a.adresse = adr.id
|
||||
LEFT JOIN `adresse` AS cus ON a.kunde = cus.id
|
||||
WHERE a.id = :task_id';
|
||||
|
||||
$result = $this->db->fetchRow($sql, ['task_id' => (int)$taskId,]);
|
||||
|
||||
if (empty($result)) {
|
||||
throw new ResubmissionTaskNotFoundException(sprintf('Task ID%s not found.', $taskId));
|
||||
}
|
||||
|
||||
if (empty($result['submission_date']) || $result['submission_date'] === '00.00.0000') {
|
||||
$result['submission_date'] = null;
|
||||
}
|
||||
if (empty($result['submission_time']) || $result['submission_time'] === '00:00') {
|
||||
$result['submission_time'] = null;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId Wiedervorlagen-ID
|
||||
* @param int $taskId Aufgaben-ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStages($resubmissionId, $taskId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT w.stages AS `stage_id`, wa.required_completion_stage_id
|
||||
FROM `wiedervorlage` AS `w`
|
||||
INNER JOIN `wiedervorlage_aufgabe` AS `wa` ON w.id = wa.resubmission_id
|
||||
WHERE w.id = :resubmission_id AND wa.task_id = :task_id';
|
||||
$resubmission = $this->db->fetchRow($sql, [
|
||||
'resubmission_id' => (int)$resubmissionId,
|
||||
'task_id' => (int)$taskId,
|
||||
]);
|
||||
|
||||
// View-ID der aktuellen Stage ermitteln; View-ID darf 0 sein; 0 = Standard-View
|
||||
$viewId = $this->gateway->getViewIdByStage($resubmission['stage_id']);
|
||||
|
||||
// Alle Stages im gleichen View laden
|
||||
$stages = $this->gateway->getStagesByView($viewId);
|
||||
|
||||
$rank = 1;
|
||||
$requiredBefore = false;
|
||||
foreach ($stages as &$stage) {
|
||||
$stage['rank'] = $rank++;
|
||||
$stage['current'] = (int)$stage['id'] === (int)$resubmission['stage_id'];
|
||||
$stage['required'] = (int)$stage['id'] === (int)$resubmission['required_completion_stage_id'];
|
||||
if ((int)$stage['id'] === (int)$resubmission['required_completion_stage_id']) {
|
||||
$requiredBefore = true;
|
||||
}
|
||||
$stage['required_before'] = $requiredBefore;
|
||||
}
|
||||
unset($stage);
|
||||
|
||||
return $stages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\Resubmission\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Resubmission\Exception\ResubmissionNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Exception\TaskTemplateNotFoundException;
|
||||
|
||||
final class ResubmissionTaskTemplateGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var ResubmissionGateway $resubmissionGateway */
|
||||
private $resubmissionGateway;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
* @param ResubmissionGateway $resubmissionGateway
|
||||
*/
|
||||
public function __construct(Database $database, ResubmissionGateway $resubmissionGateway)
|
||||
{
|
||||
$this->db = $database;
|
||||
$this->resubmissionGateway = $resubmissionGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $taskTemplateId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function existsTaskTemplate($taskTemplateId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT COUNT(wav.id) AS `task_template_count`
|
||||
FROM `wiedervorlage_aufgabe_vorlage` AS `wav`
|
||||
WHERE wav.id = :task_template_id';
|
||||
$taskTemplateCount = (int)$this->db->fetchValue($sql, ['task_template_id' => (int)$taskTemplateId]);
|
||||
|
||||
return $taskTemplateCount === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $taskTemplateId
|
||||
*
|
||||
* @throws TaskTemplateNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTaskTemplateById($taskTemplateId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
wav.id,
|
||||
wav.required_from_stage_id,
|
||||
wav.add_task_at_stage_id,
|
||||
CONCAT(adr.mitarbeiternummer, \' \', adr.name) AS `employee`,
|
||||
wav.project_id,
|
||||
wav.subproject_id,
|
||||
wav.title,
|
||||
wav.submission_date_days,
|
||||
TIME_FORMAT(wav.submission_time, \'%H:%i\') AS `submission_time`,
|
||||
wav.state,
|
||||
wav.priority,
|
||||
wav.description
|
||||
|
||||
FROM `wiedervorlage_aufgabe_vorlage` AS `wav`
|
||||
LEFT JOIN `adresse` AS `adr` ON wav.employee_address_id = adr.id
|
||||
WHERE wav.id = :task_template_id';
|
||||
|
||||
$result = $this->db->fetchRow($sql, ['task_template_id' => (int)$taskTemplateId]);
|
||||
if (empty($result)) {
|
||||
throw new TaskTemplateNotFoundException(sprintf(
|
||||
'Task template not found: ID %s', $taskTemplateId
|
||||
));
|
||||
}
|
||||
|
||||
if (empty($result['required_from_stage_id'])) {
|
||||
$result['required_from_stage_id'] = 0;
|
||||
}
|
||||
if (empty($result['add_task_at_stage_id'])) {
|
||||
$result['add_task_at_stage_id'] = 0;
|
||||
}
|
||||
if (empty($result['employee_address_id'])) {
|
||||
$result['employee_address_id'] = 0;
|
||||
}
|
||||
if (empty($result['project_id'])) {
|
||||
$result['project_id'] = 0;
|
||||
}
|
||||
if (empty($result['subproject_id'])) {
|
||||
$result['subproject_id'] = 0;
|
||||
}
|
||||
if (empty($result['submission_time']) || $result['submission_time'] === '00:00') {
|
||||
$result['submission_time'] = null;
|
||||
}
|
||||
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
function getTaskTemplatesByStageId($stageId){
|
||||
$sql =
|
||||
'SELECT
|
||||
wav.id,
|
||||
wav.required_from_stage_id,
|
||||
wav.add_task_at_stage_id,
|
||||
IFNULL(wav.employee_address_id, 0) AS employee_address_id,
|
||||
wav.project_id,
|
||||
wav.subproject_id,
|
||||
wav.title,
|
||||
wav.submission_date_days,
|
||||
TIME_FORMAT(wav.submission_time, \'%H:%i\') AS `submission_time`,
|
||||
wav.state,
|
||||
wav.priority,
|
||||
wav.description
|
||||
|
||||
FROM `wiedervorlage_aufgabe_vorlage` AS `wav`
|
||||
LEFT JOIN `adresse` AS `adr` ON wav.employee_address_id = adr.id
|
||||
WHERE wav.add_task_at_stage_id = :stage_id';
|
||||
|
||||
$result = $this->db->fetchAll($sql, ['stage_id' => (int)$stageId]);
|
||||
if (empty($result)) {
|
||||
throw new TaskTemplateNotFoundException(sprintf(
|
||||
'Task template with following stage id not found: ID %s', $stageId
|
||||
));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\Resubmission\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Resubmission\Data\TaskTemplateData;
|
||||
use Xentral\Modules\Resubmission\Exception\TaskTemplateNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Exception\ValidationFailedException;
|
||||
|
||||
final class ResubmissionTaskTemplateService
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var ResubmissionTaskTemplateGateway $taskTemplateGateway */
|
||||
private $taskTemplateGateway;
|
||||
|
||||
/** @var ResubmissionGateway $resubmissionGateway */
|
||||
private $resubmissionGateway;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
* @param ResubmissionTaskTemplateGateway $taskTemplateGateway
|
||||
* @param ResubmissionGateway $resubmissionGateway
|
||||
*/
|
||||
public function __construct(
|
||||
Database $database,
|
||||
ResubmissionTaskTemplateGateway $taskTemplateGateway,
|
||||
ResubmissionGateway $resubmissionGateway
|
||||
) {
|
||||
$this->db = $database;
|
||||
$this->taskTemplateGateway = $taskTemplateGateway;
|
||||
$this->resubmissionGateway = $resubmissionGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaskTemplateData $config
|
||||
*
|
||||
* @throws ValidationFailedException
|
||||
*
|
||||
* @return int Inserted id
|
||||
*/
|
||||
public function createTaskTemplate(TaskTemplateData $config)
|
||||
{
|
||||
if ($config->id !== null) {
|
||||
$errorMsg = sprintf('The "id" property must be null. Given value: "%s".', $config->id);
|
||||
throw ValidationFailedException::fromErrors(['id' => [$errorMsg]]);
|
||||
}
|
||||
|
||||
// Prüfen ob add_task_at_stage_id und required_from_stage_id im gleichen View sind
|
||||
if ($config->addTaskAtStageId > 0 && $config->requiredFromStageId > 0) {
|
||||
$addTaskAtViewId = $this->resubmissionGateway->getViewIdByStage($config->addTaskAtStageId);
|
||||
$requiredFromViewId = $this->resubmissionGateway->getViewIdByStage($config->requiredFromStageId);
|
||||
if ($addTaskAtViewId !== $requiredFromViewId) {
|
||||
$errorMsg = 'The "add_task_at_stage_id" and the "required_from_stage_id" must be ';
|
||||
$errorMsg .= 'on the same View.';
|
||||
throw ValidationFailedException::fromErrors(['add_task_at_stage_id' => [$errorMsg]]);
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'INSERT INTO `wiedervorlage_aufgabe_vorlage`
|
||||
(
|
||||
`id`, `required_from_stage_id`, `add_task_at_stage_id`, `employee_address_id`,
|
||||
`project_id`, `subproject_id`, `title`, `submission_date_days`, `submission_time`,
|
||||
`state`, `priority`, `description`
|
||||
) VALUES (
|
||||
NULL, :required_from_stage_id, :add_task_at_stage_id, :employee_address_id,
|
||||
:project_id, :subproject_id, :title, :submission_date_days, :submission_time,
|
||||
:state, :priority, :description
|
||||
)';
|
||||
$bindValues = [
|
||||
'required_from_stage_id' => $config->requiredFromStageId,
|
||||
'add_task_at_stage_id' => $config->addTaskAtStageId,
|
||||
'employee_address_id' => $config->employeeAddressId,
|
||||
'project_id' => $config->projectId,
|
||||
'subproject_id' => $config->subprojectId,
|
||||
'title' => $config->title,
|
||||
'submission_date_days' => $config->submissionDateDays,
|
||||
'submission_time' => $config->submissionTime,
|
||||
'state' => $config->state,
|
||||
'priority' => $config->priority,
|
||||
'description' => $config->description
|
||||
];
|
||||
|
||||
$this->db->perform($sql, $bindValues);
|
||||
$taskTemplateId = $this->db->lastInsertId();
|
||||
|
||||
return $taskTemplateId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TaskTemplateData $config
|
||||
*
|
||||
* @throws TaskTemplateNotFoundException
|
||||
* @throws ValidationFailedException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modifyTaskTemplate(TaskTemplateData $config)
|
||||
{
|
||||
if (!$this->taskTemplateGateway->existsTaskTemplate($config->id)) {
|
||||
throw new TaskTemplateNotFoundException(sprintf(
|
||||
'Task template not found: ID%s', $config->id
|
||||
));
|
||||
}
|
||||
|
||||
// Prüfen ob add_task_at_stage_id und required_from_stage_id im gleichen View sind
|
||||
if ($config->addTaskAtStageId > 0 && $config->requiredFromStageId > 0) {
|
||||
$addTaskAtViewId = $this->resubmissionGateway->getViewIdByStage($config->addTaskAtStageId);
|
||||
$requiredFromViewId = $this->resubmissionGateway->getViewIdByStage($config->requiredFromStageId);
|
||||
if ($addTaskAtViewId !== $requiredFromViewId) {
|
||||
$errorMsg = 'The "add_task_at_stage_id" and the "required_from_stage_id" must be ';
|
||||
$errorMsg .= 'on the same View.';
|
||||
throw ValidationFailedException::fromErrors(['add_task_at_stage_id' => [$errorMsg]]);
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'UPDATE `wiedervorlage_aufgabe_vorlage`
|
||||
SET
|
||||
`required_from_stage_id` = :required_from_stage_id,
|
||||
`add_task_at_stage_id` = :add_task_at_stage_id,
|
||||
`employee_address_id` = :employee_address_id,
|
||||
`project_id` = :project_id,
|
||||
`subproject_id` = :subproject_id,
|
||||
`title` = :title,
|
||||
`submission_date_days` = :submission_date_days,
|
||||
`submission_time` = :submission_time,
|
||||
`state` = :state,
|
||||
`priority` = :priority,
|
||||
`description` = :description
|
||||
WHERE `id` = :id
|
||||
LIMIT 1';
|
||||
$bindValues = [
|
||||
'id' => $config->id,
|
||||
'required_from_stage_id' => $config->requiredFromStageId,
|
||||
'add_task_at_stage_id' => $config->addTaskAtStageId,
|
||||
'employee_address_id' => $config->employeeAddressId,
|
||||
'project_id' => $config->projectId,
|
||||
'subproject_id' => $config->subprojectId,
|
||||
'title' => $config->title,
|
||||
'submission_date_days' => $config->submissionDateDays,
|
||||
'submission_time' => $config->submissionTime,
|
||||
'state' => $config->state,
|
||||
'priority' => $config->priority,
|
||||
'description' => $config->description
|
||||
];
|
||||
|
||||
$this->db->perform($sql, $bindValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $taskTemplateId
|
||||
*
|
||||
* @throws TaskTemplateNotFoundException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteTaskTemplateById($taskTemplateId)
|
||||
{
|
||||
if (!$this->taskTemplateGateway->existsTaskTemplate($taskTemplateId)) {
|
||||
throw new TaskTemplateNotFoundException(sprintf(
|
||||
'Task template not found: ID%s', $taskTemplateId
|
||||
));
|
||||
}
|
||||
|
||||
$sql = 'DELETE FROM `wiedervorlage_aufgabe_vorlage` WHERE `id` = :id LIMIT 1';
|
||||
$bindValues = ['id' => (int)$taskTemplateId];
|
||||
|
||||
$this->db->perform($sql, $bindValues);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\Resubmission\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Resubmission\Exception\ResubmissionNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Exception\TextFieldConfigNotFoundException;
|
||||
|
||||
final class ResubmissionTextFieldGateway
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var ResubmissionGateway $resubmissionGateway */
|
||||
private $resubmissionGateway;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
* @param ResubmissionGateway $resubmissionGateway
|
||||
*/
|
||||
public function __construct(Database $database, ResubmissionGateway $resubmissionGateway)
|
||||
{
|
||||
$this->db = $database;
|
||||
$this->resubmissionGateway = $resubmissionGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $configId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function existsConfig($configId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT COUNT(wfk.id) AS `config_count`
|
||||
FROM `wiedervorlage_freifeld_konfiguration` AS `wfk`
|
||||
WHERE wfk.id = :config_id';
|
||||
$configCount = (int)$this->db->fetchValue($sql, ['config_id' => (int)$configId]);
|
||||
|
||||
return $configCount === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $configId
|
||||
*
|
||||
* @throws TextFieldConfigNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConfigById($configId)
|
||||
{
|
||||
$sql =
|
||||
'SELECT
|
||||
wfk.id,
|
||||
wfk.title,
|
||||
wfk.available_from_stage_id,
|
||||
wfk.required_from_stage_id,
|
||||
wfk.show_in_pipeline,
|
||||
wfk.show_in_tables
|
||||
FROM `wiedervorlage_freifeld_konfiguration` AS `wfk`
|
||||
WHERE wfk.id = :config_id';
|
||||
|
||||
$result = $this->db->fetchRow($sql, ['config_id' => (int)$configId]);
|
||||
if (empty($result)) {
|
||||
throw new TextFieldConfigNotFoundException(sprintf(
|
||||
'Text field config not found: ID%s', $configId
|
||||
));
|
||||
}
|
||||
|
||||
if (empty($result['available_from_stage_id'])) {
|
||||
$result['available_from_stage_id'] = 0;
|
||||
}
|
||||
if (empty($result['required_from_stage_id'])) {
|
||||
$result['required_from_stage_id'] = 0;
|
||||
}
|
||||
$result['show_in_pipeline'] = (int)$result['show_in_pipeline'] === 1;
|
||||
$result['show_in_tables'] = (int)$result['show_in_tables'] === 1;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @example Rückgabe: ['freifeld1' => 'Mein Freifeld 1', 'freifeld2' => 'Mein Freifeld 2']
|
||||
*
|
||||
* @param int $viewId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTextFieldsForTableView($viewId)
|
||||
{
|
||||
$stages = $this->resubmissionGateway->getStagesByView($viewId);
|
||||
$stageIds = array_column($stages, 'id');
|
||||
|
||||
if (empty($stageIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql =
|
||||
'SELECT CONCAT(\'freifeld\', wfk.id) AS name, wfk.title
|
||||
FROM `wiedervorlage_freifeld_konfiguration` AS `wfk`
|
||||
WHERE wfk.show_in_tables = 1
|
||||
AND (
|
||||
wfk.available_from_stage_id = 0
|
||||
OR wfk.available_from_stage_id IN (:stages_ids)
|
||||
OR wfk.required_from_stage_id IN (:stages_ids)
|
||||
)';
|
||||
|
||||
return $this->db->fetchPairs($sql, ['stages_ids' => $stageIds]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @example Rückgabe: ['freifeld1' => 'Mein Freifeld 1', 'freifeld2' => 'Mein Freifeld 2']
|
||||
*
|
||||
* @param int $viewId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTextFieldsForPipelineView($viewId)
|
||||
{
|
||||
$stages = $this->resubmissionGateway->getStagesByView($viewId);
|
||||
$stageIds = array_column($stages, 'id');
|
||||
|
||||
if (empty($stageIds)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sql =
|
||||
'SELECT CONCAT(\'freifeld\', wfk.id) AS name, wfk.title
|
||||
FROM `wiedervorlage_freifeld_konfiguration` AS `wfk`
|
||||
WHERE wfk.show_in_pipeline = 1
|
||||
AND (
|
||||
wfk.available_from_stage_id = 0
|
||||
OR wfk.available_from_stage_id IN (:stages_ids)
|
||||
OR wfk.required_from_stage_id IN (:stages_ids)
|
||||
)';
|
||||
|
||||
return $this->db->fetchPairs($sql, ['stages_ids' => $stageIds]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle Freitextfelder die in der übergebenen Stage ein Pflichtfeld sind
|
||||
*
|
||||
* @param int $stageId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getRequiredTextFieldsForStage($stageId)
|
||||
{
|
||||
// Schritt 1
|
||||
// Alle Stage-IDs ermittlen für die wir die Freitextfelder laden sollen
|
||||
// d.h. alle Stages die in der Reihenfolge vor der aktuellen Stage kommen; inklusive der aktuellen Stage
|
||||
$validStages = $this->getStagesUntilStageId($stageId);
|
||||
$validStageIds = array_column($validStages, 'id');
|
||||
|
||||
// Schritt 2
|
||||
// Alle Pflicht-Freitextfelder für die Stages aus Schritt 1 ermitteln
|
||||
$sql =
|
||||
'SELECT
|
||||
wfk.id AS `config_id`,
|
||||
wfk.title AS `label`,
|
||||
wfk.available_from_stage_id,
|
||||
wfk.required_from_stage_id,
|
||||
ws.name AS `required_from_stage_name`,
|
||||
wfk.show_in_pipeline,
|
||||
wfk.show_in_tables
|
||||
FROM `wiedervorlage_freifeld_konfiguration` AS `wfk`
|
||||
LEFT JOIN `wiedervorlage_stages` AS `ws` ON wfk.required_from_stage_id = ws.id
|
||||
WHERE wfk.required_from_stage_id IN (:valid_stage_ids)';
|
||||
|
||||
return $this->db->fetchAll($sql, [
|
||||
'valid_stage_ids' => $validStageIds,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle Stages die in der Reihenfolge vor der übergebenen Stage liegen
|
||||
*
|
||||
* @refactor ResubmissionGateway
|
||||
*
|
||||
* @param int $stageId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getStagesUntilStageId($stageId)
|
||||
{
|
||||
// Alle Stages in der gleichen View ermitteln
|
||||
$stageId = (int)$stageId;
|
||||
$stages = $this->resubmissionGateway->getSiblingStages($stageId);
|
||||
|
||||
// Rang der übergebenen Stage ermitteln
|
||||
$currentRank = 0;
|
||||
foreach ($stages as $stage) {
|
||||
if ($stage['id'] === $stageId) {
|
||||
$currentRank = $stage['rank'];
|
||||
}
|
||||
}
|
||||
|
||||
// Alle Stages filtern die VOR der übergebenen Stage liegen (inklusive der übergebenen Stage)
|
||||
$validStages = [];
|
||||
foreach ($stages as $stage) {
|
||||
if ($stage['rank'] <= $currentRank) {
|
||||
$validStages[] = $stage;
|
||||
}
|
||||
}
|
||||
|
||||
return $validStages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ermittelt alle Freifelder die das Verschieben einer Wiedervorlage blockieren
|
||||
*
|
||||
* @param int $resubmissionId
|
||||
* @param int $targetStageId
|
||||
*
|
||||
* @return array Empty array if none item is blocking
|
||||
*/
|
||||
public function getBlockingTextFieldsForTargetStage($resubmissionId, $targetStageId)
|
||||
{
|
||||
$resubmissionId = (int)$resubmissionId;
|
||||
$targetStageId = (int)$targetStageId;
|
||||
|
||||
$textfields = $this->getTextFieldsForResubmission($resubmissionId);
|
||||
|
||||
$blocking = [];
|
||||
foreach ($textfields as $textfield) {
|
||||
if (!empty($textfield['content'])) {
|
||||
continue; // Textfeld ist ausgefüllt > Textfeld darf in jede Stage geschoben werden
|
||||
}
|
||||
if ((int)$textfield['required_from_stage_id'] === 0) {
|
||||
continue; // Textfeld hat keine Fertigstellungs-Stage hinterlegt > Textfeld darf in jede Stage geschoben werden
|
||||
}
|
||||
|
||||
$distance = $this->resubmissionGateway->getDistanceBetweenStages(
|
||||
$targetStageId, $textfield['required_from_stage_id']
|
||||
);
|
||||
if ($distance <= 0) {
|
||||
$blocking[] = [
|
||||
'config_id' => $textfield['config_id'],
|
||||
'content_id' => $textfield['content_id'],
|
||||
'label' => $textfield['label'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $blocking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lädt alle Freitextfelder für eine Wiedervorlage
|
||||
*
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @throws ResubmissionNotFoundException
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTextFieldsForResubmission($resubmissionId)
|
||||
{
|
||||
$stage = $this->resubmissionGateway->getStageByResubmission($resubmissionId);
|
||||
$viewId = (int)$stage['view_id']; // View-ID `0` ist zulässig
|
||||
|
||||
// 1. Alle Stage-IDs ermittlen für die wir die Freitextfelder laden sollen
|
||||
// d.h. alle Stages die in der Reihenfolge vor der aktuellen Stage kommen; inklusive der aktuellen Stage
|
||||
$validStages = $this->getStagesUntilStageId($stage['id']);
|
||||
$validStageIds = array_column($validStages, 'id');
|
||||
|
||||
// 2. Alle Freitextfelder für die Stages aus Schritt 1 ermitteln
|
||||
$sql =
|
||||
'SELECT
|
||||
wfk.id AS `config_id`,
|
||||
wfi.id AS `content_id`,
|
||||
wfk.title AS `label`,
|
||||
wfk.available_from_stage_id,
|
||||
wfk.required_from_stage_id,
|
||||
wfk.show_in_pipeline,
|
||||
wfk.show_in_tables
|
||||
FROM `wiedervorlage_freifeld_konfiguration` AS `wfk`
|
||||
LEFT JOIN `wiedervorlage_freifeld_inhalt` AS `wfi`
|
||||
ON wfi.resubmission_id = :resubmission_id
|
||||
LEFT JOIN `wiedervorlage_stages` AS ws1
|
||||
ON wfk.required_from_stage_id = ws1.id
|
||||
LEFT JOIN `wiedervorlage_stages` AS ws2
|
||||
ON wfk.available_from_stage_id = ws2.id
|
||||
WHERE wfk.available_from_stage_id = 0
|
||||
OR wfk.available_from_stage_id IN (:valid_stage_ids)
|
||||
OR wfk.required_from_stage_id IN (:valid_stage_ids)
|
||||
ORDER BY wfk.required_from_stage_id != 0 DESC, ws1.sort, ws2.sort, wfk.title';
|
||||
// Erklärung Sortierung:
|
||||
// 1. `wfk.required_from_stage_id != 0 DESC` = Felder mit Pflicht-Stage oben anzeigen
|
||||
// 2. `ws1.sort, ws2.sort` = Felder nach Reihenfolge der Stages anzeigen
|
||||
|
||||
$textFields = $this->db->fetchAll($sql, [
|
||||
'resubmission_id' => $resubmissionId,
|
||||
'valid_stage_ids' => $validStageIds,
|
||||
'view_id' => $viewId,
|
||||
]);
|
||||
|
||||
// Freitextfeld-Inhalte ergänzen
|
||||
$contents = $this->getTextFieldContentsForResubmission($resubmissionId);
|
||||
foreach ($textFields as &$textField) {
|
||||
$configId = (int)$textField['config_id'];
|
||||
$textField['content'] = $contents[$configId];
|
||||
}
|
||||
unset($textField);
|
||||
|
||||
return $textFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTextFieldContentsForResubmission($resubmissionId)
|
||||
{
|
||||
$columnNames = $this->getTextFieldColumnNames();
|
||||
|
||||
$contentsAll = $this->db->fetchRow(
|
||||
'SELECT wfi.* FROM `wiedervorlage_freifeld_inhalt` AS `wfi`
|
||||
WHERE wfi.resubmission_id = :resubmission_id LIMIT 1',
|
||||
['resubmission_id' => (int)$resubmissionId]
|
||||
);
|
||||
|
||||
$contents = [];
|
||||
foreach ($columnNames as $configId => $columnName) {
|
||||
if (isset($contentsAll[$columnName])) {
|
||||
$contents[$configId] = $contentsAll[$columnName];
|
||||
} else {
|
||||
$contents[$configId] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $tableAlias
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getTextFieldColumnNames($tableAlias = null)
|
||||
{
|
||||
$sql = 'SELECT wfk.id FROM `wiedervorlage_freifeld_konfiguration` AS `wfk` WHERE 1';
|
||||
$configIds = $this->db->fetchCol($sql);
|
||||
|
||||
$columnNames = [];
|
||||
$columnPrefix = $tableAlias !== null ? sprintf('%s.', $tableAlias) : '';
|
||||
foreach ($configIds as $configId) {
|
||||
$columnNames[$configId] = sprintf('%sfreifeld%s', $columnPrefix, $configId);
|
||||
}
|
||||
|
||||
return $columnNames;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
|
||||
namespace Xentral\Modules\Resubmission\Service;
|
||||
|
||||
use Xentral\Components\Database\Database;
|
||||
use Xentral\Modules\Resubmission\Data\FreeTextFieldConfigData;
|
||||
use Xentral\Modules\Resubmission\Data\FreeTextFieldContentData;
|
||||
use Xentral\Modules\Resubmission\Exception\TextFieldConfigNotFoundException;
|
||||
use Xentral\Modules\Resubmission\Exception\TextFieldRequiredException;
|
||||
use Xentral\Modules\Resubmission\Exception\ValidationFailedException;
|
||||
use Xentral\Modules\Resubmission\Exception\ResubmissionNotFoundException;
|
||||
|
||||
final class ResubmissionTextFieldService
|
||||
{
|
||||
/** @var Database $db */
|
||||
private $db;
|
||||
|
||||
/** @var ResubmissionTextFieldGateway $textFieldGateway */
|
||||
private $textFieldGateway;
|
||||
|
||||
/** @var ResubmissionGateway $resubmissionGateway */
|
||||
private $resubmissionGateway;
|
||||
|
||||
/**
|
||||
* @param Database $database
|
||||
* @param ResubmissionTextFieldGateway $textFieldGateway
|
||||
* @param ResubmissionGateway $resubmissionGateway
|
||||
*/
|
||||
public function __construct(
|
||||
Database $database,
|
||||
ResubmissionTextFieldGateway $textFieldGateway,
|
||||
ResubmissionGateway $resubmissionGateway
|
||||
) {
|
||||
$this->db = $database;
|
||||
$this->textFieldGateway = $textFieldGateway;
|
||||
$this->resubmissionGateway = $resubmissionGateway;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FreeTextFieldConfigData $config
|
||||
*
|
||||
* @throws ValidationFailedException
|
||||
*
|
||||
* @return int Inserted id
|
||||
*/
|
||||
public function createConfig(FreeTextFieldConfigData $config)
|
||||
{
|
||||
if ($config->id !== null) {
|
||||
$errorMsg = sprintf('The "id" property must be null. Given value: "%s".', $config->id);
|
||||
throw ValidationFailedException::fromErrors(['id' => [$errorMsg]]);
|
||||
}
|
||||
|
||||
// @todo 1. Prüfen ob available_from_stage_id vor required_from_stage_id kommt;
|
||||
// @todo Nur wenn available_from_stage_id > 0 UND required_from_stage_id > 0
|
||||
|
||||
// Prüfen ob available_from_stage_id und required_from_stage_id im gleichen View sind
|
||||
if ($config->availableFromStageId > 0 && $config->requiredFromStageId > 0) {
|
||||
$availableFromViewId = $this->resubmissionGateway->getViewIdByStage($config->availableFromStageId);
|
||||
$requiredFromViewId = $this->resubmissionGateway->getViewIdByStage($config->requiredFromStageId);
|
||||
if ($availableFromViewId !== $requiredFromViewId) {
|
||||
$errorMsg = 'The "available_from_stage_id" and the "required_from_stage_id" must be ';
|
||||
$errorMsg .= 'on the same View.';
|
||||
throw ValidationFailedException::fromErrors(['available_from_stage_id' => [$errorMsg]]);
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'INSERT INTO `wiedervorlage_freifeld_konfiguration`
|
||||
(
|
||||
`id`, `title`, `show_in_pipeline`, `show_in_tables`,
|
||||
`available_from_stage_id`, `required_from_stage_id`, `created_at`, `updated_at`
|
||||
) VALUES (
|
||||
NULL, :title, :show_in_pipeline, :show_in_tables,
|
||||
:available_from_stage_id, :required_from_stage_id, NOW(), NULL
|
||||
)';
|
||||
$bindValues = [
|
||||
'title' => $config->title,
|
||||
'show_in_pipeline' => $config->showInPipeline === true ? 1 : 0,
|
||||
'show_in_tables' => $config->showInTables === true ? 1 : 0,
|
||||
'available_from_stage_id' => $config->availableFromStageId,
|
||||
'required_from_stage_id' => $config->requiredFromStageId,
|
||||
];
|
||||
|
||||
$this->db->perform($sql, $bindValues);
|
||||
$configId = $this->db->lastInsertId();
|
||||
|
||||
// Sicherstellen dass Freitext-Spalte existiert
|
||||
$this->checkCreateContentColumn($configId);
|
||||
|
||||
return $configId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FreeTextFieldConfigData $config
|
||||
*
|
||||
* @throws TextFieldConfigNotFoundException
|
||||
* @throws ValidationFailedException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function modifyConfig(FreeTextFieldConfigData $config)
|
||||
{
|
||||
if (!$this->textFieldGateway->existsConfig($config->id)) {
|
||||
throw new TextFieldConfigNotFoundException(sprintf(
|
||||
'Text field config not found: ID%s', $config->id
|
||||
));
|
||||
}
|
||||
|
||||
// @todo Prüfen ob available_from_stage_id vor required_from_stage_id kommt;
|
||||
// @todo Nur wenn available_from_stage_id > 0 UND required_from_stage_id > 0
|
||||
|
||||
// Prüfen ob available_from_stage_id und required_from_stage_id im gleichen View sind
|
||||
if ($config->availableFromStageId > 0 && $config->requiredFromStageId > 0) {
|
||||
$availableFromViewId = $this->resubmissionGateway->getViewIdByStage($config->availableFromStageId);
|
||||
$requiredFromViewId = $this->resubmissionGateway->getViewIdByStage($config->requiredFromStageId);
|
||||
if ($availableFromViewId !== $requiredFromViewId) {
|
||||
$errorMsg = 'The "available_from_stage_id" and the "required_from_stage_id" must be ';
|
||||
$errorMsg .= 'on the same View.';
|
||||
throw ValidationFailedException::fromErrors(['available_from_stage_id' => [$errorMsg]]);
|
||||
}
|
||||
}
|
||||
|
||||
// Sicherstellen dass Freitext-Spalte existiert
|
||||
$this->checkCreateContentColumn($config->id);
|
||||
|
||||
$sql = 'UPDATE `wiedervorlage_freifeld_konfiguration`
|
||||
SET
|
||||
`title` = :title,
|
||||
`show_in_pipeline` = :show_in_pipeline,
|
||||
`show_in_tables` = :show_in_tables,
|
||||
`available_from_stage_id` = :available_from_stage_id,
|
||||
`required_from_stage_id` = :required_from_stage_id,
|
||||
`updated_at` = NOW()
|
||||
WHERE `id` = :id
|
||||
LIMIT 1';
|
||||
$bindValues = [
|
||||
'id' => $config->id,
|
||||
'title' => $config->title,
|
||||
'show_in_pipeline' => $config->showInPipeline === true ? 1 : 0,
|
||||
'show_in_tables' => $config->showInTables === true ? 1 : 0,
|
||||
'available_from_stage_id' => $config->availableFromStageId,
|
||||
'required_from_stage_id' => $config->requiredFromStageId,
|
||||
];
|
||||
|
||||
$this->db->perform($sql, $bindValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $configId
|
||||
*
|
||||
* @throws TextFieldConfigNotFoundException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function deleteConfigById($configId)
|
||||
{
|
||||
if (!$this->textFieldGateway->existsConfig($configId)) {
|
||||
throw new TextFieldConfigNotFoundException(sprintf(
|
||||
'Text field config not found: ID%s', $configId
|
||||
));
|
||||
}
|
||||
|
||||
$sql = 'DELETE FROM `wiedervorlage_freifeld_konfiguration` WHERE `id` = :id LIMIT 1';
|
||||
$bindValues = ['id' => (int)$configId];
|
||||
|
||||
// Inhalts-Tabelle `wiedervorlage_freifeld_inhalt` nicht anpassen, sonst Datenverlust
|
||||
|
||||
$this->db->perform($sql, $bindValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alle Freifeld-Inhalte für eine Wiedervorlage speichern
|
||||
*
|
||||
* WICHTIG: Es müssen alle Pflicht-Freitexte mitgeschickt werden
|
||||
* Optionale Felder die nicht mitgeschickt werden, werden nicht verändert.
|
||||
*
|
||||
* @example $contents = [123 => 'Inhalt für das Freifeld mit der Freifeld-Config-ID 123']
|
||||
*
|
||||
* @param int $resubmissionId
|
||||
* @param array $contents
|
||||
*
|
||||
* @throws ResubmissionNotFoundException
|
||||
* @throws TextFieldRequiredException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function saveAllFieldContents($resubmissionId, array $contents)
|
||||
{
|
||||
if (!$this->resubmissionGateway->existsResubmission($resubmissionId)) {
|
||||
throw new ResubmissionNotFoundException(sprintf('Resubmission not found: ID%s', $resubmissionId));
|
||||
}
|
||||
|
||||
$stage = $this->resubmissionGateway->getStageByResubmission($resubmissionId);
|
||||
$requiredFields = $this->textFieldGateway->getRequiredTextFieldsForStage($stage['id']);
|
||||
|
||||
// Prüfen ob Pflichtfeld leer
|
||||
foreach ($requiredFields as $requiredField) {
|
||||
$configId = (int)$requiredField['config_id'];
|
||||
if (empty($contents[$configId])) {
|
||||
throw TextFieldRequiredException::onEmpty(
|
||||
$requiredField['label'],
|
||||
$requiredField['required_from_stage_name']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($contents as $configId => $content) {
|
||||
$textfield = new FreeTextFieldContentData();
|
||||
$textfield->resubmissionId = (int)$resubmissionId;
|
||||
$textfield->configId = (int)$configId;
|
||||
$textfield->content = (string)$content;
|
||||
$this->updateFieldContent($textfield);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vorhandenen Freifeld-Inhalt bearbeiten
|
||||
*
|
||||
* @param FreeTextFieldContentData $textfield
|
||||
*
|
||||
* @throws ValidationFailedException
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function updateFieldContent(FreeTextFieldContentData $textfield)
|
||||
{
|
||||
$errors = $textfield->validate();
|
||||
if (!empty($errors)) {
|
||||
throw ValidationFailedException::fromErrors($errors);
|
||||
}
|
||||
|
||||
// Sicherstellen dass Freitext-Zeile für Wiedervorlage existiert
|
||||
$contentId = $this->getCreateContentRowId($textfield->resubmissionId);
|
||||
$columnName = sprintf('freifeld%s', (int)$textfield->configId);
|
||||
|
||||
$sql = sprintf(
|
||||
'UPDATE `wiedervorlage_freifeld_inhalt`
|
||||
SET %s = :content
|
||||
WHERE `id` = :content_id AND `resubmission_id` = :resubmission_id
|
||||
LIMIT 1',
|
||||
$this->db->escapeIdentifier($columnName)
|
||||
);
|
||||
$bindValues = [
|
||||
'content_id' => $contentId,
|
||||
'resubmission_id' => $textfield->resubmissionId,
|
||||
'content' => !empty($textfield->content) ? $textfield->content : null,
|
||||
];
|
||||
|
||||
$this->db->perform($sql, $bindValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Holt die ID einer Freifeld-Zeile; Zeile wird angelegt wenn nicht vorhanden
|
||||
*
|
||||
* @param int $resubmissionId
|
||||
*
|
||||
* @return int Primary ID aus Freifeld-Inhalts-Tabelle
|
||||
*/
|
||||
private function getCreateContentRowId($resubmissionId)
|
||||
{
|
||||
$sql = 'SELECT wfi.id FROM `wiedervorlage_freifeld_inhalt` AS `wfi` WHERE resubmission_id = :resubmission_id';
|
||||
$contentId = (int)$this->db->fetchValue($sql, ['resubmission_id' => (int)$resubmissionId]);
|
||||
|
||||
if ($contentId === 0) {
|
||||
$this->db->perform(
|
||||
'INSERT INTO `wiedervorlage_freifeld_inhalt` (`id`, `resubmission_id`) VALUES (NULL, :resubmission_id)',
|
||||
['resubmission_id' => (int)$resubmissionId]
|
||||
);
|
||||
$contentId = (int)$this->db->lastInsertId();
|
||||
}
|
||||
|
||||
return $contentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stellt sicher dass eine Freifeld-Spalte für eine Config-ID existiert
|
||||
*
|
||||
* @param int $configId
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function checkCreateContentColumn($configId)
|
||||
{
|
||||
if (!$this->textFieldGateway->existsConfig($configId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->existsContentColumn($configId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->db->exec(sprintf(
|
||||
'ALTER TABLE `wiedervorlage_freifeld_inhalt`
|
||||
ADD `freifeld%s` VARCHAR(255) NULL DEFAULT NULL; ',
|
||||
(int)$configId
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob die Freifeld-Spalte für eine Config-ID existiert
|
||||
*
|
||||
* @param int $configId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function existsContentColumn($configId)
|
||||
{
|
||||
$columnName = 'freifeld' . (int)$configId;
|
||||
$exists = $this->db->fetchAll(sprintf(
|
||||
'SHOW COLUMNS FROM `wiedervorlage_freifeld_inhalt` LIKE %s;',
|
||||
$this->db->escapeString($columnName)
|
||||
));
|
||||
|
||||
return count($exists) === 1;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user