add resources conflict checker and extend tempus tests with it

This commit is contained in:
Ivymaster
2026-06-19 11:41:56 +02:00
parent de60d0d09e
commit 273b1d3fe4
23 changed files with 1955 additions and 887 deletions
@@ -31,7 +31,7 @@ class Config extends FHCAPI_Controller
$config = [];
$result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['ignore_kollision', 'kollision_student', 'ignore_reservierung', 'ignore_zeitsperre']);
$result = $this->_ci->VariableModel->getVariables(getAuthUID(), ['ignore_kollision', 'kollision_student', 'ignore_reservierung', 'ignore_zeitsperre', 'ignore_resources_collisions']);
$data = $this->getDataOrTerminateWithError($result);
$config['ignore_kollision'] = [
@@ -60,7 +60,11 @@ class Config extends FHCAPI_Controller
"value" => ($data['ignore_zeitsperre'] ?? 'false') === 'true'
];
$config['ignore_resources_collisions'] = [
"type" => "checkbox",
"label" => $this->p->t('ui', 'ignore_resources_collisions'),
"value" => ($data['ignore_resources_collisions'] ?? 'false') === 'true'
];
$this->terminateWithSuccess($config);
}
@@ -109,6 +113,11 @@ class Config extends FHCAPI_Controller
'ignore_zeitsperre',
$this->input->post('ignore_zeitsperre') === true ? 'true' : 'false'
);
$this->_ci->VariableModel->setVariable(
getAuthUID(),
'ignore_resources_collisions',
$this->input->post('ignore_resources_collisions') === true ? 'true' : 'false'
);
$this->terminateWithSuccess();
}
@@ -0,0 +1,84 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
class OperationalResourceToCalenderAPI extends FHCAPI_Controller
{
/**
* Object initialization
*/
public function __construct()
{
parent::__construct([
'getAssignedResourcesByCalenderId' => ['admin:r', 'assistenz:r'],
'storeResourcesToCalendarRelationship' => ['admin:w', 'assistenz:w'],
'getSchedulableResourcesByCalendar' => ['admin:r', 'assistenz:r'],
]);
$this->load->model('ressource/BetriebsmittelKalender_model', 'BetriebsmittelKalenderModel');
$this->load->model('ressource/Betriebsmittel_model', 'BetriebsmittelModel');
$this->load->model('ressource/Kalender_model', 'KalenderModel');
$this->load->library('CollisionChecker');
$this->load->library('KalenderLib');
}
//------------------------------------------------------------------------------------------------------------------
// Public methods
public function getSchedulableResourcesByCalendar($calenderID)
{
if (!isset($calenderID)) $this->terminateWithError("Missing required parameter 'kalender_id'");
$result = $this->KalenderModel->loadWhere(['kalender_id' => $calenderID]);
if (isError($result)) $this->terminateWithError("Calendar with id '$calenderID' does not have a valid group id");
$calender = $this->getDataOrTerminateWithError($result)[0];
if (!isset($calender)) $this->terminateWithError("Calendar with id '$calenderID' not found");
$result = $this->BetriebsmittelModel->getSchedulableEntriesByDatetimeInterval($calender->von, $calender->bis);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function getAssignedResourcesByCalenderId($calenderID)
{
if (!isset($calenderID)) $this->terminateWithError("Missing required parameter 'kalender_id'");
$result = $this->KalenderModel->loadWhere(['kalender_id' => $calenderID]);
if (empty($result)) $this->terminateWithError("Calendar with id '$calenderID' not found");
$calenderGroupID = $this->getDataOrTerminateWithError($result)[0]->eindeutige_gruppen_id;
if (!isset($calenderGroupID)) $this->terminateWithError("Calendar with id '$calenderID' does not have a valid group id");
$this->BetriebsmittelKalenderModel->addSelect(['tbl_betriebsmittel_kalender.*', 'tbl_betriebsmittel.beschreibung', 'tbl_betriebsmittel.verplanen']);
$this->BetriebsmittelKalenderModel->addJoin('wawi.tbl_betriebsmittel', 'betriebsmittel_id');
$result = $this->BetriebsmittelKalenderModel->loadWhere([
'eindeutige_kalender_gruppen_id' => $calenderGroupID,
'tbl_betriebsmittel.verplanen' => true,
]);
$this->terminateWithSuccess($this->getDataOrTerminateWithError($result));
}
public function storeResourcesToCalendarRelationship()
{
$calenderID = $this->input->post('kalender_id');
$assignedResources = $this->input->post('assignedResources');
if (!isset($calenderID)) $this->terminateWithError("Missing required parameter 'kalender_id'", 'general');
if (!isset($assignedResources)) $this->terminateWithError("Missing required parameter 'assignedResources'", 'general');
$result = $this->KalenderModel->loadWhere(['kalender_id' => $calenderID]);
if (empty($result)) $this->terminateWithError("Calendar with id '$calenderID' not found");
$calendar = $this->getDataOrTerminateWithError($result)[0];
$calenderGroupID = $calendar->eindeutige_gruppen_id;
if (!isset($calenderGroupID)) $this->terminateWithError("Calendar with id '$calenderID' does not have a valid group id");
$result = $this->kalenderlib->addOperationalResourcesToKalenderEvent($calendar, $assignedResources);
if (isError($result))
$this->terminateWithError(getError($result));
$this->terminateWithSuccess(['message' => 'Resources assigned successfully']);
}
}
+102 -112
View File
@@ -19,6 +19,7 @@ class MigrateKalender extends Auth_Controller
parent::__construct(array(
'migrateStundenplan' => ['admin:rw'],
'migrateReservierung' => ['admin:rw'],
'migrateStundenplanBetriebsmittelEntries' => ['admin:rw']
));
$this->load->model('ressource/Kalender_model', 'KalenderModel');
$this->load->model('ressource/Kalender_Lehreinheit_model', 'KalenderLehreinheitModel');
@@ -147,66 +148,6 @@ class MigrateKalender extends Auth_Controller
$this->_insertSync($fehlende, $kalender_id);
}
}
/*if(hasData($SyncResult))
{
//bereits vorhanden
// TODO Update
}
else
{
// Neuen Eintrag anlegen
$von = $rowstpl->datum.' '.$rowstpl->beginn;
$bis = $rowstpl->datum.' '.$rowstpl->ende;
$typ = 'lehreinheit';
$status = 'live';
$insertamum = $rowstpl->insertamum;
$insertvon = $rowstpl->insertvon;
$updateamum = $rowstpl->updateamum;
$updatevon = $rowstpl->updatevon;
$resultKalenderInsert = $this->KalenderModel->insert(
array(
'von' => $von,
'bis' => $bis,
'typ' => $typ,
'status_kurzbz' => $status,
'vorgaenger_kalender_id' => null,
'insertamum' => $insertamum,
'insertvon' => $insertvon,
'updateamum' => $updateamum,
'updatevon' => $updatevon
)
);
if(isSuccess($resultKalenderInsert))
{
$kalender_id = getData($resultKalenderInsert);
$resultKalenderInsert = $this->KalenderLehreinheitModel->insert(
array(
'kalender_id' => $kalender_id,
'lehreinheit_id' => $rowstpl->lehreinheit_id,
)
);
$resultKalenderInsert = $this->KalenderOrtModel->insert(
array(
'kalender_id' => $kalender_id,
'ort_kurzbz' => $rowstpl->ort_kurzbz,
)
);
$resultSyncInsert = $this->SyncModel->insert(
array(
'stundenplandev_id' => $rowstpl->stundenplandev_id,
'kalender_id' => $kalender_id,
'lastupdate' => date('Y-m-d H:i:s')
)
);
}
}*/
}
}
}
@@ -267,58 +208,6 @@ class MigrateKalender extends Auth_Controller
JOIN lehre.tbl_stunde s_von ON s_von.stunde = b.stunde_von
JOIN lehre.tbl_stunde s_bis ON s_bis.stunde = b.stunde_bis
ORDER BY b.reservierung_id DESC;";
/*$qry = "WITH per_stunde AS (
SELECT
datum,
titel,
beschreibung, ort_kurzbz, studiengang_kz, stunde,
veranstaltung_id,
array_agg(DISTINCT uid) AS uids,
array_agg(DISTINCT reservierung_id::text) AS reservierung_ids,
array_agg(DISTINCT ROW(semester, verband, gruppe)::text) AS svg_kombis,
array_agg(DISTINCT gruppe_kurzbz) AS gruppen_kurzbz,
MIN(reservierung_id) AS reservierung_id,
MAX(insertamum) AS insertamum,
MAX(insertvon) AS insertvon
FROM campus.tbl_reservierung
WHERE datum >= ? AND datum <= ?
GROUP BY datum, titel, beschreibung, ort_kurzbz, studiengang_kz, stunde, veranstaltung_id
),
numbered AS (
SELECT
per_stunde.*,
stunde - ROW_NUMBER() OVER (PARTITION BY datum, titel, beschreibung, ort_kurzbz, studiengang_kz, veranstaltung_id ORDER BY stunde) AS grp
FROM per_stunde
),
grouped AS (
SELECT
MIN(reservierung_id) AS reservierung_id,
ort_kurzbz, studiengang_kz, datum,
MIN(stunde) AS stunde_von,
MAX(stunde) AS stunde_bis,
titel, beschreibung,
array_agg(DISTINCT gruppe_kurzbz_elem) AS gruppen_kurzbz,
array_agg(DISTINCT uid_elem) AS uids,
array_agg(DISTINCT res_id) AS reservierung_ids,
array_agg(DISTINCT svg_elem) AS svg_kombis,
veranstaltung_id,
MAX(insertamum) AS insertamum,
MAX(insertvon) AS insertvon
FROM numbered,
unnest(uids) AS uid_elem,
unnest(reservierung_ids) AS res_id,
unnest(gruppen_kurzbz) AS gruppe_kurzbz_elem,
unnest(svg_kombis) AS svg_elem
GROUP BY datum, titel, beschreibung, ort_kurzbz, studiengang_kz, veranstaltung_id, grp
)
SELECT
grouped.*,
(datum + s_von.beginn) AS von,
(datum + s_bis.ende) AS bis
FROM grouped
JOIN lehre.tbl_stunde s_von ON s_von.stunde = grouped.stunde_von
JOIN lehre.tbl_stunde s_bis ON s_bis.stunde = grouped.stunde_bis
ORDER BY grouped.reservierung_id DESC";*/
array_push($params, $von, $bis);
$reservierung_data = $db->execReadOnlyQuery($qry, $params);
@@ -367,6 +256,107 @@ class MigrateKalender extends Auth_Controller
}
}
public function migrateStundenplanBetriebsmittelEntries() {
$this->setKalendarEntriesGroupIDs();
$this->setKalendarEntriesGroupIDsForChildren();
$dbModel = new DB_Model();
$deleteOldImportedTempusEntriesQuery = "DELETE from lehre.tbl_betriebsmittel_kalender WHERE quelle != 'tempus_neu' OR quelle IS NULL;";
$createHelperTypeQuery = "DO $$
BEGIN
IF NOT EXISTS (
SELECT 1
FROM pg_type
WHERE typname = 'betriebsmittel_info'
) THEN
CREATE TYPE betriebsmittel_info AS (
betriebsmittel_id bigint,
insertamum timestamp,
insertvon text
);
END IF;
END $$;";
$query = $deleteOldImportedTempusEntriesQuery .
$createHelperTypeQuery .
"WITH test AS (
SELECT
tk.eindeutige_gruppen_id AS eindeutige_gruppen_id,
array_agg(tk.kalender_id),
array_agg(
ROW(tsb.betriebsmittel_id, tsb.insertamum, tsb.insertvon)::betriebsmittel_info
) AS betriebsmittel_data
FROM sync.tbl_stundenplandev_kalender AS sk
JOIN lehre.tbl_kalender tk ON tk.kalender_id = sk.kalender_id
JOIN lehre.tbl_stundenplan_betriebsmittel tsb ON tsb.stundenplandev_id = sk.stundenplandev_id
GROUP BY tk.eindeutige_gruppen_id
)
INSERT INTO lehre.tbl_betriebsmittel_kalender (
eindeutige_kalender_gruppen_id,
betriebsmittel_id,
insertamum,
insertvon
)
SELECT
t.eindeutige_gruppen_id,
bm.betriebsmittel_id,
bm.insertamum,
bm.insertvon
FROM test t
CROSS JOIN LATERAL unnest(t.betriebsmittel_data) AS bm
ON CONFLICT (eindeutige_kalender_gruppen_id, betriebsmittel_id) DO NOTHING;
;";
$dbModel->db->query($query);
}
private function setKalendarEntriesGroupIDs() {
$dbModel = new DB_Model();
$query = "UPDATE lehre.tbl_kalender
SET eindeutige_gruppen_id = gen_random_uuid()
WHERE vorgaenger_kalender_id IS NULL
AND eindeutige_gruppen_id IS NULL;";
$dbModel->db->query($query);
}
private function setKalendarEntriesGroupIDsForChildren() {
$dbModel = new DB_Model();
$query = "WITH RECURSIVE tree AS
(
SELECT
kalender_id,
vorgaenger_kalender_id,
kalender_id AS root_id,
eindeutige_gruppen_id as root_eindeutige_gruppen_id
FROM lehre.tbl_kalender
WHERE vorgaenger_kalender_id IS NULL
UNION ALL
SELECT
i.kalender_id,
i.vorgaenger_kalender_id,
t.root_id,
t.root_eindeutige_gruppen_id
FROM lehre.tbl_kalender i
JOIN tree t
ON i.vorgaenger_kalender_id = t.kalender_id
where i.eindeutige_gruppen_id is NULL
)
UPDATE lehre.tbl_kalender k
SET eindeutige_gruppen_id = t.root_eindeutige_gruppen_id
FROM tree t
WHERE k.kalender_id = t.kalender_id
AND k.eindeutige_gruppen_id IS NULL;";
$dbModel->db->query($query);
}
private function _insertKalender($block, $typ)
{
+11
View File
@@ -570,3 +570,14 @@ function buildDropdownEntryPrintArray($id, $name, $parameterurl, $uid=null, $ord
];
}
function generateUUID()
{
$data = openssl_random_pseudo_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
@@ -18,10 +18,12 @@ class CollisionChecker
$this->_ci->load->library('collision/checks/LectureCollisionCheck');
$this->_ci->load->library('collision/checks/VerbandCollisionCheck');
$this->_ci->load->library('collision/checks/StudentCollisionCheck');
$this->_ci->load->library('collision/checks/ResourcesCollisionCheck');
$this->register($this->_ci->roomcollisioncheck);
$this->register($this->_ci->lecturecollisioncheck);
$this->register($this->_ci->verbandcollisioncheck);
$this->register($this->_ci->studentcollisioncheck);
$this->register($this->_ci->resourcescollisioncheck);
Events::trigger('collision_register', $this);
}
+102 -1
View File
@@ -14,6 +14,7 @@ class KalenderLib
$this->_ci->load->model('ressource/Kalender_Event_model', 'KalenderEventModel');
$this->_ci->load->model('ressource/Kalender_Event_Teilnehmer_model', 'KalenderEventTeilnehmerModel');
$this->_ci->load->model('ressource/Kalender_Ort_model', 'KalenderOrtModel');
$this->_ci->load->model('ressource/BetriebsmittelKalender_model', 'BetriebsmittelKalenderModel');
$this->_ci->load->model('education/Lehreinheit_model', 'LehreinheitModel');
$this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
$this->_ci->load->model('education/LehreinheitMitarbeiter_model', 'LehreinheitMitarbeiterModel');
@@ -174,7 +175,8 @@ class KalenderLib
'titel' => isset($row->titel) ? $row->titel : '',
'beschreibung' => isset($row->beschreibung) ? $row->beschreibung : '',
'topic' => (isset($row->lehrfach_kurzbz) ? $row->lehrfach_kurzbz : '').' '.(isset($row->lehrform_kurzbz) ? $row->lehrform_kurzbz : ''),
'collisions' => false
'collisions' => false,
'has_assigned_resources' => isset($row->has_assigned_resources) ? $row->has_assigned_resources : false
];
}
@@ -345,6 +347,14 @@ class KalenderLib
WHERE nachfolger.vorgaenger_kalender_id = tbl_kalender.kalender_id)', null, false);
$this->_ci->KalenderModel->db->where_not_in('status_kurzbz', array('deleted'));
$this->_ci->KalenderModel->addSelect([
"(
SELECT COUNT(*) FROM lehre.tbl_betriebsmittel_kalender
WHERE tbl_betriebsmittel_kalender.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_gruppen_id
) AS has_assigned_resources"
]);
$data = $this->_ci->KalenderModel->load();
return $this->_mapEvents($data);
@@ -354,6 +364,7 @@ class KalenderLib
{
$this->_getBasePlan($start_date, $end_date);
$this->_ci->KalenderModel->db->where('status_kurzbz', 'live');
$data = $this->_ci->KalenderModel->load();
@@ -495,6 +506,7 @@ class KalenderLib
'bis' => $end_date,
'typ' => 'lehreinheit',
'status_kurzbz' => 'planning',
'eindeutige_gruppen_id' => $this->_ci->KalenderModel->generateUniqueGroupId(),
'insertvon' => getAuthUID(),
'insertamum' => date('Y-m-d H:i:s')
)
@@ -658,6 +670,81 @@ class KalenderLib
return $kalenderresult;
}
public function addOperationalResourcesToKalenderEvent($calendar, $assignedResources)
{
$checkerData = new stdClass();
$checkerData->kalender_id = $calendar->kalender_id;
$checkerData->betriebsmittel_ids = [];
$checkerData->von = $calendar->von;
$checkerData->bis = $calendar->bis;
foreach ($assignedResources as $assignedResource) {
array_push($checkerData->betriebsmittel_ids, $assignedResource['betriebsmittel_id']);
}
$errors = [];
$result = $this->_ci->collisionchecker->run($checkerData);
if (!empty($result))
{
$errors = $result;
}
if (!empty($errors)) {
return error($errors);
}
$this->_ci->db->trans_start();
$allowedResourceIDs = array_filter(array_map(
function ($resource) {
return $resource['betriebsmittel_id'] ?? null;
},
$assignedResources
));
if (!empty($allowedResourceIDs)) {
$placeholders = implode(',', array_fill(0, count($allowedResourceIDs), '?'));
$query = "
DELETE FROM lehre.tbl_betriebsmittel_kalender
WHERE eindeutige_kalender_gruppen_id = ?
AND betriebsmittel_id NOT IN ($placeholders)
";
$params = array_merge(
[$calendar->eindeutige_gruppen_id],
$allowedResourceIDs
);
$this->_ci->db->query($query, $params);
} else {
$this->_ci->db->where('eindeutige_kalender_gruppen_id', $calendar->eindeutige_gruppen_id);
$this->_ci->db->delete('lehre.tbl_betriebsmittel_kalender');
}
foreach ($assignedResources as $assignedResource) {
$data = [
'eindeutige_kalender_gruppen_id' => $calendar->eindeutige_gruppen_id,
'betriebsmittel_id' => $assignedResource['betriebsmittel_id'],
'anmerkung' => $assignedResource['anmerkung'] ?? null,
'quelle' => 'tempus_neu',
'updateamum' => date('c'),
'updatevon' => getAuthUid(),
];
if (isset($assignedResource['betriebsmittel_kalender_id'])) {
$result = $this->_ci->BetriebsmittelKalenderModel->update($assignedResource['betriebsmittel_kalender_id'], $data);
} else {
$result = $this->_ci->BetriebsmittelKalenderModel->insert(array_merge($data, [
'insertvon' => getAuthUid(),
'updateamum' => date('c'),
]));
}
}
$this->_ci->db->trans_complete();
return success('Operational resources added to calendar event successfully.');
}
private function _addTeilnehmerToEvent($kalender_id, $uid, $rolle)
{
return $this->_ci->KalenderEventTeilnehmerModel->insert(
@@ -748,9 +835,22 @@ class KalenderLib
$entryResult = $this->_loadKalenderEntry($kalender_id);
if (isError($entryResult)) return $entryResult;
$kalender_entry = getData($entryResult);
$calendarResources = $this->_ci->BetriebsmittelKalenderModel->loadWhere(['eindeutige_kalender_gruppen_id' => $kalender_entry->eindeutige_gruppen_id]);
if (isError($calendarResources)) return $calendarResources;
$calendarResourcesItems = getData($calendarResources);
$calendarResourcesIDs = [];
if (is_array($calendarResourcesItems)) {
$calendarResourcesIDs = array_map(function($resource) {
return $resource->betriebsmittel_id;
}, getData($calendarResources));
}
$kalender_entry->ort_kurzbz = $ort_kurzbz ?? $kalender_entry->ort_kurzbz;
$kalender_entry->betriebsmittel_ids = $calendarResourcesIDs;
$kalender_entry->von = $start_time ?? $kalender_entry->von;
$kalender_entry->bis = $end_time ?? $kalender_entry->bis;
@@ -888,6 +988,7 @@ class KalenderLib
'typ' => 'lehreinheit',
'status_kurzbz' => 'planning',
'vorgaenger_kalender_id' => $old_id,
'eindeutige_gruppen_id' => $kalender_entry->eindeutige_gruppen_id,
'insertvon' => getAuthUID(),
'insertamum' => date('Y-m-d H:i:s')
)
@@ -0,0 +1,85 @@
<?php
class ResourcesCollisionCheck implements ICollisionCheck
{
private $_ci;
public function __construct()
{
$this->_ci =& get_instance();
$this->_ci->load->model('ressource/Kalender_model', 'KalenderModel');
$this->_ci->load->library('VariableLib', array('uid' => getAuthUID()));
$this->_ci->load->library('PhrasesLib', array('ui'));
}
public function getName()
{
return 'resources';
}
public function check($data)
{
if (!isset($data->betriebsmittel_ids, $data->von, $data->bis, $data->kalender_id)) return [];
if (empty($data->betriebsmittel_ids)) return [];
if ($this->_ci->variablelib->getVar('ignore_kollision') === 'true') return [];
if ($this->_ci->variablelib->getVar('ignore_resources_collisions') === 'true') return [];
$this->_ci->KalenderModel->addSelect('kalender_id, von, bis, bm.beschreibung AS betriebsmittel_beschreibung');
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender bk', 'bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_gruppen_id');
$this->_ci->KalenderModel->addJoin('wawi.tbl_betriebsmittel bm', 'bm.betriebsmittel_id = bk.betriebsmittel_id');
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $data->kalender_id);
$this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false);
$this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']);
$this->_ci->KalenderModel->db->where_in('bk.betriebsmittel_id', $data->betriebsmittel_ids);
$result = $this->_ci->KalenderModel->loadWhere(array(
'von <' => $data->bis,
'bis >' => $data->von,
));
if (isError($result)) return [];
if (!hasData($result)) return [];
return array_map(function($row)
{
return [
'errorCode' => 'resource_collision',
'message' => $this->_ci->phraseslib->t('ui', 'resource_collision') . ': ' . $row->betriebsmittel_beschreibung . ' (' . date('d.m.Y H:i', strtotime($row->von)) . ' - ' . date('d.m.Y H:i', strtotime($row->bis)) . ')'
];
}, getData($result));
}
public function checkAll($kalender_ids)
{
if (empty($kalender_ids)) return [];
$this->_ci->KalenderModel->addSelect('DISTINCT ON (tbl_kalender.kalender_id) tbl_kalender.kalender_id');
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender current_bk', 'current_bk.eindeutige_kalender_gruppen_id = tbl_kalender.eindeutige_gruppen_id');
$this->_ci->KalenderModel->addJoin('lehre.tbl_betriebsmittel_kalender other_bk', 'other_bk.betriebsmittel_id = current_bk.betriebsmittel_id');
$this->_ci->KalenderModel->addJoin('lehre.tbl_kalender other_kalender', 'other_kalender.eindeutige_gruppen_id = other_bk.eindeutige_kalender_gruppen_id');
$this->_ci->KalenderModel->db->where('other_kalender.kalender_id != tbl_kalender.kalender_id', null, false);
$this->_ci->KalenderModel->db->where('other_kalender.von < tbl_kalender.bis', null, false);
$this->_ci->KalenderModel->db->where('other_kalender.bis > tbl_kalender.von', null, false);
$this->_ci->KalenderModel->db->where_not_in('other_kalender.status_kurzbz', ['archived', 'deleted', 'to_delete']);
$this->_ci->KalenderModel->db->where_in('tbl_kalender.kalender_id', $kalender_ids);
$this->_ci->KalenderModel->db->where('other_kalender.kalender_id NOT IN (SELECT vorgaenger_kalender_id FROM lehre.tbl_kalender WHERE vorgaenger_kalender_id IS NOT NULL)', null, false);
$result = $this->_ci->KalenderModel->load();
if (isError($result) || !hasData($result)) return [];
$grouped = [];
foreach (getData($result) as $row)
{
$grouped[$row->kalender_id][] = true;
}
return $grouped;
}
}
@@ -0,0 +1,14 @@
<?php
class BetriebsmittelKalender_model extends DB_Model
{
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->dbTable = 'lehre.tbl_betriebsmittel_kalender';
$this->pk = 'betriebsmittel_kalender_id';
}
}
@@ -31,4 +31,26 @@ class Betriebsmittel_model extends DB_Model
return $this->execQuery($qry);
}
public function getSchedulableEntriesByDatetimeInterval($from, $to)
{
$qry = "SELECT
*
FROM
wawi.tbl_betriebsmittel
WHERE
verplanen=true
AND
NOT EXISTS(
SELECT 1 FROM lehre.tbl_betriebsmittel_kalender
JOIN lehre.tbl_kalender ON tbl_kalender.eindeutige_gruppen_id = tbl_betriebsmittel_kalender.eindeutige_kalender_gruppen_id
WHERE
betriebsmittel_id=tbl_betriebsmittel.betriebsmittel_id
AND tbl_kalender.von <= ?
AND tbl_kalender.bis >= ?
)";
return $this->execQuery($qry, array($from, $to));
}
}
@@ -10,5 +10,18 @@ class Kalender_model extends DB_Model
parent::__construct();
$this->dbTable = 'lehre.tbl_kalender';
$this->pk = 'kalender_id';
$this->load->helper('hlp_common');
}
public function generateUniqueGroupId()
{
while (true) {
$uniqueGroupId = generateUUID();
$result = $this->loadWhere(['eindeutige_gruppen_id' => $uniqueGroupId]);
if (!hasData($result)) {
return $uniqueGroupId;
}
}
}
}
+1
View File
@@ -8,6 +8,7 @@
'primevue3' => true,
'tabulator5' => true,
'vuedatepicker11' => true,
'momentjs2' => true,
'phrases' => array(
'global',
'ui',
@@ -0,0 +1,38 @@
/**
* Copyright (C) 2025 fhcomplete.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
export default {
getSchedulableResourcesByCalendar(calendarID) {
return {
method: "get",
url: "/api/frontend/v1/tempus/OperationalResourceToCalenderAPI/getSchedulableResourcesByCalendar/" + calendarID,
};
},
getAssignedResourcesByCalenderId(calenderID) {
return {
method: "get",
url: "/api/frontend/v1/tempus/OperationalResourceToCalenderAPI/getAssignedResourcesByCalenderId/" + calenderID,
};
},
storeResourcesToCalendarRelationship(calenderID, assignedResources) {
return {
method: "post",
url: "/api/frontend/v1/tempus/OperationalResourceToCalenderAPI/storeResourcesToCalendarRelationship",
params: { kalender_id: calenderID, assignedResources: assignedResources },
};
}
};
@@ -64,8 +64,14 @@ export default {
template: /*html*/`
<div
:class="classes"
class="position-relative"
@wheel.stop
>
<div v-if="event.has_assigned_resources" class="position-absolute top-0 start-0 m-1" >
<i
class="fa-solid fa-table-list text-muted"
></i>
</div>
<div
v-if="!event.allDayEvent && event?.beginn && event?.ende"
class="event-time d-none d-xl-grid h-100"
File diff suppressed because it is too large Load Diff
+1
View File
@@ -96,6 +96,7 @@ require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php');
require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php');
require_once('dbupdate_3.4/70376_lohnguide.php');
require_once('dbupdate_3.4/75888_reihungstest_mehrfachdurchfuehrung.php');
require_once('dbupdate_3.4/77375_ressourcen.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
+87
View File
@@ -0,0 +1,87 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
$result = $db->db_query("SELECT 1 FROM information_schema.columns WHERE table_schema = 'lehre' AND table_name = 'tbl_kalender' AND column_name = 'eindeutige_gruppen_id'");
if($db->db_num_rows($result) === 0)
{
$qry = "ALTER TABLE lehre.tbl_kalender
ADD COLUMN IF NOT EXISTS eindeutige_gruppen_id UUID";
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br>lehre.tbl_kalender column eindeutige_gruppen_id hinzugefuegt';
}
$result = $db->db_query("SELECT count(*) FROM public.tbl_variablenname WHERE name = 'ignore_resources_collisions'");
if($db->db_num_rows($result) === 0)
{
$qry = "INSERT INTO public.tbl_variablenname
(name, defaultwert)
VALUES('ignore_resources_collisions', 'false')";
}
if(!$result = @$db->db_query("SELECT 1 FROM lehre.tbl_betriebsmittel_kalender LIMIT 1"))
{
$qry = '
CREATE TABLE lehre.tbl_betriebsmittel_kalender (
"betriebsmittel_kalender_id" INTEGER NOT NULL,
"eindeutige_kalender_gruppen_id" UUID NOT NULL,
"betriebsmittel_id" INTEGER NOT NULL,
"anmerkung" TEXT,
"quelle" VARCHAR(32),
"insertamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"insertvon" VARCHAR(32),
"updateamum" TIMESTAMP WITH TIME ZONE DEFAULT now(),
"updatevon" VARCHAR(32),
CONSTRAINT pk_betriebsmittel_kalender_id PRIMARY KEY("betriebsmittel_kalender_id"),
CONSTRAINT fk_betriebsmittel_id FOREIGN KEY("betriebsmittel_id") REFERENCES wawi.tbl_betriebsmittel("betriebsmittel_id") ON DELETE CASCADE,
CONSTRAINT eindeutige_kalender_gruppen_id_betriebsmittel_id UNIQUE (eindeutige_kalender_gruppen_id, betriebsmittel_id)
);';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_betriebsmittel_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br>lehre.tbl_betriebsmittel_kalender table created';
$db->db_query('CREATE SEQUENCE IF NOT EXISTS lehre.seq_tbl_betriebsmittel_kalender_betriebsmittel_kalender_id
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;');
$db->db_query("ALTER TABLE lehre.tbl_betriebsmittel_kalender ALTER COLUMN betriebsmittel_kalender_id SET DEFAULT nextval('lehre.seq_tbl_betriebsmittel_kalender_betriebsmittel_kalender_id');");
$qry = 'GRANT SELECT ON TABLE lehre.tbl_betriebsmittel_kalender TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_betriebsmittel_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_betriebsmittel_kalender';
$qry = 'GRANT USAGE ON lehre.seq_tbl_betriebsmittel_kalender_betriebsmittel_kalender_id TO web;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_betriebsmittel_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>web</strong> on lehre.tbl_betriebsmittel_kalender';
$qry = 'GRANT SELECT, UPDATE, INSERT, DELETE ON TABLE lehre.tbl_betriebsmittel_kalender TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_betriebsmittel_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_betriebsmittel_kalender';
$qry = 'GRANT USAGE, INSERT ON lehre.seq_tbl_betriebsmittel_kalender_betriebsmittel_kalender_id TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_betriebsmittel_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_betriebsmittel_kalender';
$qry = 'GRANT USAGE, UPDATE ON lehre.seq_tbl_betriebsmittel_kalender_betriebsmittel_kalender_id TO vilesci;';
if(!$db->db_query($qry))
echo '<strong>lehre.tbl_betriebsmittel_kalender: '.$db->db_last_error().'</strong><br>';
else
echo '<br>Granted privileges to <strong>vilesci</strong> on lehre.tbl_betriebsmittel_kalender';
}
+200
View File
@@ -47608,6 +47608,26 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'resource_collision',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ressourcen Kollision',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Resource Collision',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
@@ -47668,6 +47688,26 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'ignore_resources_collisions',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ressourcenkollisionen ignorieren',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Ignore resource collision',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
@@ -58616,6 +58656,166 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'failed_assigned_resources_fetch_error_message',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Fehler beim Abrufen der zugewiesenen Ressourcen. Bitte versuchen Sie es erneut.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Error fetching assigned resources. Please try again.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'failed_schedulable_resources_fetch_error_message',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Fehler beim Abrufen der zuweisbaren Ressourcen. Bitte versuchen Sie es erneut.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Error fetching schedulable resources. Please try again.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'failed_assigned_resources_save_error_message',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Fehler beim Speichern der zugewiesenen Ressourcen. Bitte versuchen Sie es erneut.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Error saving assigned resources. Please try again.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'resource_assignment_modal_title',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Ressourcen-Zuweisung',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Resource Assignment',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'assigned_resources_subtitle',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zugewiesene Ressourcen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Assigned Resources',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'no_assigned_resources',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Keine zugewiesenen Ressourcen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'No Assigned Resources',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'available_resources_label',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Verfügbare Ressourcen',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Available Resources',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'ui',
'phrase' => 'assigned_resources_save_success_message',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Zugewiesene Ressourcen erfolgreich gespeichert',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Assigned Resources successfully saved',
'description' => '',
'insertvon' => 'system'
)
)
),
// ### Phrases Dashboard Admin END
);
+11
View File
@@ -0,0 +1,11 @@
INSERT INTO wawi.tbl_betriebsmitteltyp
(betriebsmitteltyp, beschreibung, anzahl, kaution, typ_code, mastershapename)
VALUES('Inventar', NULL, NULL, NULL, NULL, NULL);
INSERT INTO wawi.tbl_betriebsmittel
(betriebsmittel_id, beschreibung, betriebsmitteltyp, nummer, reservieren, updateamum, updatevon, insertamum, insertvon, ext_id, inventarnummer, oe_kurzbz, ort_kurzbz, hersteller, seriennummer, bestellung_id, bestelldetail_id, afa, verwendung, anmerkung, leasing_bis, inventuramum, inventurvon, anschaffungswert, anschaffungsdatum, tiefe, hoehe, breite, nummer2, verplanen)
VALUES
(1, 'pc', 'Laptop', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00001', NULL, NULL, 'pc', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true),
(2, 'projector', 'Inventar', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00002', NULL, NULL, 'projector', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true),
(3, 'workstation', 'Inventar', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00003', NULL, NULL, 'workstation', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true),
(4, 'pc2', 'Laptop', NULL, false, '2024-03-07 08:17:33.000', 'demoadmin', '2015-12-01 14:55:32.000', 'demoadmin', NULL, '00004', NULL, NULL, 'pc2', NULL, NULL, NULL, 3, NULL, 'Angelegt von demoadmin für die test', NULL, '2015-12-01 14:55:32.000', 'demoadmin', NULL, NULL, NULL, NULL, NULL, NULL, true);
@@ -143,6 +143,7 @@ VALUES
(51006, 26);
-- ////////////////////////////////////////////////////////////////////////////////////////////////////
-- Calendar setup for Stundenraster disabled tests
INSERT INTO lehre.tbl_kalender
(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, insertamum, insertvon, updateamum, updatevon) VALUES
@@ -155,9 +156,45 @@ INSERT INTO lehre.tbl_kalender_lehreinheit
VALUES
(51002, 27),
(51006, 28);
-- ////////////////////////////////////////////////////////////////////////////////////////////////////
-- Calendar setup for resources tests
INSERT INTO lehre.tbl_kalender
(von, bis, typ, status_kurzbz, vorgaenger_kalender_id, eindeutige_gruppen_id, insertamum, insertvon, updateamum, updatevon)
VALUES
(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '19:30:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '20:15:00', 'lehreinheit', 'live', NULL, 'ec1bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'),
(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '20:15:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '21:00:00', 'lehreinheit', 'live', NULL, 'ec2bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'),
(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '17:50:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '18:35:00', 'lehreinheit', 'live', NULL, 'ec3bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin'),
(date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '18:35:00', date_trunc('week', CURRENT_DATE) + INTERVAL '6 day' + TIME '19:20:00', 'lehreinheit', 'live', NULL, 'ec4bd001-facf-4b21-a671-bcb2d4f037fd', now(), 'demoadmin', NULL, 'demoadmin')
;
INSERT INTO lehre.tbl_kalender_lehreinheit
(lehreinheit_id, kalender_id)
VALUES
(51004, 29),
(51006, 30),
(51006, 31),
(51004, 32);
INSERT INTO public.tbl_variablenname
(name, defaultwert)
VALUES('ignore_resources_collisions', 'false');
INSERT INTO public.tbl_variable
(name, uid, wert)
VALUES('ignore_resources_collisions', 'demoadmin', 'false');
INSERT INTO lehre.tbl_betriebsmittel_kalender
(eindeutige_kalender_gruppen_id, betriebsmittel_id, anmerkung, quelle, insertamum, insertvon, updateamum, updatevon)
VALUES
('ec1bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 1, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin'),
('ec2bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 1, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin'),
('ec3bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 2, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin'),
('ec4bd001-facf-4b21-a671-bcb2d4f037fd'::uuid, 3, '', 'tempus_neu', '2026-06-17 16:48:59.669', 'demoadmin', '2026-06-17 17:13:23.000', 'demoadmin');
-- //////////////////////////////////////////////////////////////////////////////////////////////////--
INSERT INTO lehre.tbl_kalender_event
(kalender_id, titel, beschreibung)
VALUES(26, 'Test reservation', NULL);
@@ -169,3 +206,5 @@ VALUES(26, 'teilnehmer', 'demolektor4', NULL, NULL, NULL, NULL, NULL, NULL, NULL
INSERT INTO campus.tbl_zeitsperre
(zeitsperre_id, zeitsperretyp_kurzbz, mitarbeiter_uid, bezeichnung, vondatum, vonstunde, bisdatum, bisstunde, vertretung_uid, updateamum, updatevon, insertamum, insertvon, erreichbarkeit_kurzbz, freigabeamum, freigabevon)
VALUES(1, 'DienstV', 'demolektor1', 'Test', date_trunc('week', CURRENT_DATE) + INTERVAL '5 day', NULL, date_trunc('week', CURRENT_DATE) + INTERVAL '5 day', NULL, 'demoadmin', '2007-04-02 11:04:31.000', 'demoadmin', '2007-04-02 11:04:31.000', 'demoadmin', 't', NULL, NULL);
@@ -3,33 +3,55 @@ import { getDateForDay } from "../../../../support/helpers/date";
const TARGETED_STUDY_PLAN_SHORT_CODE = "STG5";
const resetEventsBeforeTest = () => {
tempusApi
.getPlannerEvents(getDateForDay("monday"), getDateForDay("monday"))
.then((events) => {
events.forEach((event) => {
tempusApi.deleteKalenderEvent(event.kalender_id);
});
});
tempusApi
.getPlannerEvents(getDateForDay("tuesday"), getDateForDay("tuesday"))
.then((events) => {
events.forEach((event) => {
if (
event.type === "lehreinheit" &&
event.beginn === "20:15:00" &&
event.ende === "21:00:00"
) {
tempusApi.updateKalenderEvent(
event.kalender_id,
`${event.datum} 19:30`,
`${event.datum} 20:15`,
);
}
});
});
tempusApi
.getPlannerEvents(getDateForDay("sunday"), getDateForDay("sunday"))
.then((events) => {
events.forEach((event) => {
if (
event.type === "lehreinheit" &&
event.beginn === "18:35:00" &&
event.ende === "19:20:00" &&
event.lektor.some((lector) => lector.kurzbz === "DemoLKT1")
) {
tempusApi.updateKalenderEvent(
event.kalender_id,
`${event.datum} 17:50`,
`${event.datum} 18:35`,
);
}
});
});
};
describe("Tempus Kalender API", () => {
beforeEach(() => {
cy.login();
tempusApi
.getPlannerEvents(getDateForDay("monday"), getDateForDay("monday"))
.then((events) => {
events.forEach((event) => {
tempusApi.deleteKalenderEvent(event.kalender_id);
});
});
tempusApi
.getPlannerEvents(getDateForDay("tuesday"), getDateForDay("tuesday"))
.then((events) => {
events.forEach((event) => {
if (
event.type === "lehreinheit" &&
event.beginn === "20:15:00" &&
event.ende === "21:00:00"
) {
tempusApi.updateKalenderEvent(
event.kalender_id,
`${event.datum} 19:30`,
`${event.datum} 20:15`,
);
}
});
});
resetEventsBeforeTest();
});
it("event creation works for non collision case", () => {
@@ -97,7 +119,6 @@ describe("Tempus Kalender API", () => {
tempusApi
.createKalenderEvent(lehreinheitId, startDateTime, endDateTime)
.then((response) => {
console.log(response.body.errors);
expect(response.status).to.eq(500);
expect(response.body).to.have.nested.property(
"meta.status",
@@ -106,13 +127,7 @@ describe("Tempus Kalender API", () => {
expect(response.body.errors).to.be.an("array");
let hasTimeLockCollisionError = response.body.errors.some(
(error) =>
error.message
.toLowerCase()
.includes("zeitsperre kollision") ||
error.message
.toLowerCase()
.includes("time lock collision"),
(error) => error.errorCode === "absences_collision",
);
expect(
hasTimeLockCollisionError,
@@ -154,7 +169,6 @@ describe("Tempus Kalender API", () => {
tempusApi
.createKalenderEvent(lehreinheitId, startDateTime, endDateTime)
.then((response) => {
console.log(response.body.errors);
expect(response.status).to.eq(500);
expect(response.body).to.have.nested.property(
"meta.status",
@@ -162,11 +176,7 @@ describe("Tempus Kalender API", () => {
);
expect(response.body.errors).to.be.an("array");
let hasStudentGroupCollisionError = response.body.errors.some(
(error) =>
error.message
.toLowerCase()
.includes("verband kollision") ||
error.message.toLowerCase().includes("student collision"),
(error) => error.errorCode === "verband_collision",
);
expect(
hasStudentGroupCollisionError,
@@ -219,7 +229,6 @@ describe("Tempus Kalender API", () => {
endDateTime,
)
.then((response) => {
console.log(response.body.errors);
expect(response.status).to.eq(500);
expect(response.body).to.have.nested.property(
"meta.status",
@@ -227,13 +236,7 @@ describe("Tempus Kalender API", () => {
);
expect(response.body.errors).to.be.an("array");
let hasStudentCollisionError = response.body.errors.some(
(error) =>
error.message
.toLowerCase()
.includes("studierende kollision") ||
error.message
.toLowerCase()
.includes("student collision"),
(error) => error.errorCode === "student_collision",
);
expect(
hasStudentCollisionError,
@@ -286,7 +289,6 @@ describe("Tempus Kalender API", () => {
endDateTime,
)
.then((response) => {
console.log(response.body.errors);
expect(response.status).to.eq(500);
expect(response.body).to.have.nested.property(
"meta.status",
@@ -294,13 +296,7 @@ describe("Tempus Kalender API", () => {
);
expect(response.body.errors).to.be.an("array");
let hasLectorCollisionError = response.body.errors.some(
(error) =>
error.message
.toLowerCase()
.includes("lektorin kollision") ||
error.message
.toLowerCase()
.includes("lector collision"),
(error) => error.errorCode === "lector_collision",
);
expect(
hasLectorCollisionError,
@@ -390,9 +386,7 @@ describe("Tempus Kalender API", () => {
expect(response.body.errors).to.be.an("array");
let hasRoomCollisionError = response.body.errors.some(
(error) =>
error.message.toLowerCase().includes("raum kollision") ||
error.message.toLowerCase().includes("room collision"),
(error) => error.errorCode === "room_collision",
);
expect(
hasRoomCollisionError,
@@ -440,15 +434,12 @@ describe("Tempus Kalender API", () => {
"error",
);
expect(response.body.errors).to.be.an("array");
console.log(response.body.errors);
let hasStudentGroupCollisionError = response.body.errors.some(
(error) =>
error.message.toLowerCase().includes("verband kollision") ||
error.message.toLowerCase().includes("student collision"),
(error) => error.errorCode === "verband_collision",
);
expect(
hasStudentGroupCollisionError,
"response contains student collision error",
"response contains student group collision error",
).to.be.true;
});
});
@@ -490,13 +481,8 @@ describe("Tempus Kalender API", () => {
"error",
);
expect(response.body.errors).to.be.an("array");
console.log(response.body.errors);
let hasDirectStudentCollisionError = response.body.errors.some(
(error) =>
error.message
.toLowerCase()
.includes("studierende kollision") ||
error.message.toLowerCase().includes("student collision"),
(error) => error.errorCode === "student_collision",
);
expect(
hasDirectStudentCollisionError,
@@ -515,7 +501,6 @@ describe("Tempus Kalender API", () => {
tempusApi
.getPlannerEvents(getDateForDay("saturday"), getDateForDay("saturday"))
.then((events) => {
console.log(events);
const sourceEvent = events.find(
(event) =>
event.type === "lehreinheit" &&
@@ -543,11 +528,8 @@ describe("Tempus Kalender API", () => {
"error",
);
expect(response.body.errors).to.be.an("array");
console.log(response.body.errors);
let hasLectorCollisionError = response.body.errors.some(
(error) =>
error.message.toLowerCase().includes("lektorin kollision") ||
error.message.toLowerCase().includes("lector collision"),
(error) => error.errorCode === "lector_collision",
);
expect(
hasLectorCollisionError,
@@ -596,11 +578,7 @@ describe("Tempus Kalender API", () => {
expect(response.body.errors).to.be.an("array");
let hasTimeLockCollisionError = response.body.errors.some(
(error) =>
error.message
.toLowerCase()
.includes("zeitsperre kollision") ||
error.message.toLowerCase().includes("time lock collision"),
(error) => error.errorCode === "absences_collision",
);
expect(
hasTimeLockCollisionError,
@@ -611,6 +589,90 @@ describe("Tempus Kalender API", () => {
});
});
it("event update works for entries without collision", () => {
let settingsData = tempusApi.getSettingsData();
tempusApi.updateSettingsData(settingsData).then((response) => {
tempusApi
.getPlannerEvents(getDateForDay("sunday"), getDateForDay("sunday"))
.then((events) => {
const sourceEvent = events.find(
(event) =>
event.beginn === "17:50:00" &&
event.ende === "18:35:00" &&
event.lektor.some((lector) => lector.kurzbz === "DemoLKT1"),
);
expect(
sourceEvent,
"source event with fixed time and resources for collision test",
).to.exist;
const startDateTime = `${sourceEvent.datum} 18:35`;
const endDateTime = `${sourceEvent.datum} 19:20`;
tempusApi
.updateKalenderEvent(
sourceEvent.kalender_id,
startDateTime,
endDateTime,
)
.then((response) => {
console.log(response);
expect(response.status).to.eq(200);
expect(response.body).to.have.nested.property(
"meta.status",
"success",
);
});
});
});
});
it("prohibited event update due to resources collision", () => {
let settingsData = tempusApi.getSettingsData();
tempusApi.updateSettingsData(settingsData).then((response) => {
tempusApi
.getPlannerEvents(getDateForDay("sunday"), getDateForDay("sunday"))
.then((events) => {
const sourceEvent = events.find(
(event) =>
event.beginn === "19:30:00" &&
event.ende === "20:15:00" &&
event.lektor.some((lector) => lector.kurzbz === "DemoLKT4"),
);
expect(
sourceEvent,
"source event with fixed time and resources for collision test",
).to.exist;
const startDateTime = `${sourceEvent.datum} 20:15`;
const endDateTime = `${sourceEvent.datum} 21:00`;
tempusApi
.updateKalenderEvent(
sourceEvent.kalender_id,
startDateTime,
endDateTime,
)
.then((response) => {
console.log(response);
expect(response.status).to.eq(500);
expect(response.body).to.have.nested.property(
"meta.status",
"error",
);
expect(response.body.errors).to.be.an("array");
let hasResourcesCollisionError = response.body.errors.some(
(error) => error.errorCode === "resource_collision",
);
expect(
hasResourcesCollisionError,
"response contains resources collision error",
).to.be.true;
});
});
});
});
it("prohibited event update due to lector - reservation collision", () => {
let settingsData = tempusApi.getSettingsData();
settingsData.kollision_student = false;
@@ -647,13 +709,8 @@ describe("Tempus Kalender API", () => {
"error",
);
expect(response.body.errors).to.be.an("array");
console.log(response.body.errors);
let hasReservationCollisionError = response.body.errors.some(
(error) =>
error.message
.toLowerCase()
.includes("reservierung kollision") ||
error.message.toLowerCase().includes("reservation collision"),
(error) => error.errorCode === "reservation_collision",
);
expect(
hasReservationCollisionError,
@@ -700,15 +757,12 @@ describe("Tempus Kalender API", () => {
"error",
);
expect(response.body.errors).to.be.an("array");
console.log(response.body.errors);
let hasReservationCollisionError = response.body.errors.some(
(error) =>
error.message.toLowerCase().includes("lektorin kollision") ||
error.message.toLowerCase().includes("reservation collision"),
(error) => error.errorCode === "lector_collision",
);
expect(
hasReservationCollisionError,
"response contains reservation collision error",
"response contains lector collision error",
).to.be.true;
});
});
@@ -57,6 +57,19 @@ context("Tempus smoke tests", () => {
tempusPage.getRaumauswahlModal().should("be.visible");
});
it("shows resources modal when selecting Ressourcen zuordnen from event context menu", () => {
tempusPage.waitForCalendarToFinishLoading();
tempusPage
.getCalendarEventsWithLehreinheit()
.should("have.length.greaterThan", 0);
tempusPage.getCalendarEventsWithLehreinheit().first().rightclick();
tempusPage.getEventContextMenuOption("Ressourcen zuordnen").click();
waitForOk("@fetchResourceSuggestions");
tempusPage.getResourcesModal().should("be.visible");
});
it("shows history modal when selecting History from event context menu", () => {
tempusPage.waitForCalendarToFinishLoading();
tempusPage.getCalendarEvents().should("have.length.greaterThan", 0);
@@ -92,6 +92,7 @@ export const tempusApi = {
kollision_student: false,
ignore_reservierung: false,
ignore_zeitsperre: false,
ignore_resources_collisions: false,
}),
updateSettingsData: (options) =>
@@ -64,6 +64,10 @@ class TempusPage {
method: "GET",
url: "**/tempus/Kalender/getRaumvorschlag**",
}).as("fetchRoomSuggestions");
cy.intercept({
method: "GET",
url: "**/betriebsmittel/OperationalResourceToCalenderAPI/getSchedulableResourcesByCalendar/32",
}).as("fetchResourcesSuggestions");
cy.intercept({
method: "GET",
url: "**/tempus/coursepicker/getByStg**",
@@ -284,6 +288,10 @@ class TempusPage {
.closest(".bootstrap-modal.show");
getRaumauswahlRoomOptions = () =>
this.getRaumauswahlModal().find(".list-group-item");
getResourcesModal = () =>
cy
.get("[data-cy='resourcesAssignmentModal']")
.closest(".bootstrap-modal.show");
getCalendarEventRoom = (id) =>
this.getCalendarEventById(id).find(".event-place");
getStundenrasterToggle = () =>