diff --git a/application/controllers/api/frontend/v1/tempus/Config.php b/application/controllers/api/frontend/v1/tempus/Config.php index e20716226..30be2cc6f 100644 --- a/application/controllers/api/frontend/v1/tempus/Config.php +++ b/application/controllers/api/frontend/v1/tempus/Config.php @@ -32,7 +32,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'] = [ @@ -61,7 +61,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); } @@ -108,6 +112,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(); } diff --git a/application/controllers/api/frontend/v1/tempus/Kalender.php b/application/controllers/api/frontend/v1/tempus/Kalender.php index 833d0c0aa..aa2687126 100644 --- a/application/controllers/api/frontend/v1/tempus/Kalender.php +++ b/application/controllers/api/frontend/v1/tempus/Kalender.php @@ -20,6 +20,7 @@ class Kalender extends FHCAPI_Controller 'getPlan' => self::PERM_LOGGED, 'getPlanByOrt' => self::PERM_LOGGED, 'getRaumvorschlag' => self::PERM_LOGGED, + 'getLehreinheiten' => 'lehre/lvplan:rw', 'getHistory' => 'lehre/lvplan:rw', 'deleteEntry' => 'lehre/lvplan:rw', 'syncToLecturer' => 'lehre/lvplan:rw', @@ -29,6 +30,8 @@ class Kalender extends FHCAPI_Controller 'getZeitwuensche' => self::PERM_LOGGED, 'getZeitsperren' => self::PERM_LOGGED, 'updateKalenderEvent' => 'lehre/lvplan:rw', + 'deleteOrtEntry' => 'lehre/lvplan:rw', + 'deleteFromKalenderEvent' => 'lehre/lvplan:rw', 'addKalenderEvent' => 'lehre/lvplan:rw', 'calculateMultiWeekPlan' => 'lehre/lvplan:rw', 'confirmMultiWeekPlan' => 'lehre/lvplan:rw', @@ -225,7 +228,24 @@ class Kalender extends FHCAPI_Controller if (isError($result)) $this->terminateWithError(getError($result), $result->code); - $this->terminateWithSuccess(); + $this->terminateWithSuccess(getData($result)); + } + + public function deleteOrtEntry() + { + $this->_ci->form_validation->set_data($_POST); + $this->_ci->form_validation->set_rules('kalender_id',"kalender_id","required"); + + if($this->_ci->form_validation->run() === FALSE) + $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); + + $kalender_id = $this->_ci->input->post('kalender_id', TRUE); + $result = $this->_ci->kalenderlib->updateOrt($kalender_id, []); + + if (isError($result)) + $this->terminateWithError(getError($result), $result->code); + + $this->terminateWithSuccess(getData($result)); } public function getRaumvorschlag() @@ -236,6 +256,19 @@ class Kalender extends FHCAPI_Controller $this->terminateWithSuccess($this->_ci->raumvorschlaglib->getVorschlaege($filter->kalender_id)); } + public function getLehreinheiten() + { + $this->_ci->form_validation->set_data($_GET); + $this->_ci->form_validation->set_rules('kalender_id', "kalender_id", "required"); + $kalender_id = $this->_ci->input->get('kalender_id', TRUE); + $result = $this->_ci->kalenderlib->getLehreinheiten($kalender_id); + + if (isError($result)) + $this->terminateWithError(getError($result)); + + $this->terminateWithSuccess(hasData($result) ? getData($result) : []); + } + public function getHistory() { $this->_ci->form_validation->set_data($_GET); @@ -396,6 +429,27 @@ class Kalender extends FHCAPI_Controller $this->terminateWithSuccess(getData($result)); } + public function deleteFromKalenderEvent() + { + $this->_ci->form_validation->set_data($_POST); + $this->_ci->form_validation->set_rules('kalender_id',"kalender_id","required"); + $this->_ci->form_validation->set_rules('lehreinheit_ids[]',"lehreinheit_ids","required"); + + + if($this->_ci->form_validation->run() === FALSE) + $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); + + $kalender_id = $this->_ci->input->post('kalender_id', TRUE); + $lehreinheit_ids = $this->_ci->input->post('lehreinheit_ids', TRUE); + + $result = $this->_ci->kalenderlib->deleteFromKalenderEvent($kalender_id, $lehreinheit_ids); + + if (isError($result)) + $this->terminateWithError(getError($result)); + + $this->terminateWithSuccess(getData($result)); + } + private function _checkFilter($filters) { diff --git a/application/controllers/api/frontend/v1/tempus/OperationalResourceToCalenderAPI.php b/application/controllers/api/frontend/v1/tempus/OperationalResourceToCalenderAPI.php new file mode 100644 index 000000000..376a0fedc --- /dev/null +++ b/application/controllers/api/frontend/v1/tempus/OperationalResourceToCalenderAPI.php @@ -0,0 +1,84 @@ + ['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 getAssignedResourcesByCalender($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']); + } +} diff --git a/application/controllers/api/frontend/v1/tempus/Reservierung.php b/application/controllers/api/frontend/v1/tempus/Reservierung.php index be22d96e9..1b1baa963 100644 --- a/application/controllers/api/frontend/v1/tempus/Reservierung.php +++ b/application/controllers/api/frontend/v1/tempus/Reservierung.php @@ -154,21 +154,23 @@ class Reservierung extends FHCAPI_Controller } public function addReservierung() { + $this->_ci->form_validation->set_rules('titel', "titel", "required", ['required' => $this->p->t('ui', 'error_fieldRequired')]); + $this->_ci->form_validation->set_rules('beschreibung', "beschreibung", "required", ['required' => $this->p->t('ui', 'error_fieldRequired')]); + $this->_ci->form_validation->set_rules('start_date', "start_date", "required", ['required' => $this->p->t('ui', 'error_fieldRequired')]); + $this->_ci->form_validation->set_rules('end_date', "end_date", "required", ['required' => $this->p->t('ui', 'error_fieldRequired')]); - $this->_ci->form_validation->set_data($_POST); - $this->_ci->form_validation->set_rules('titel',"titel","required"); - $this->_ci->form_validation->set_rules('beschreibung',"beschreibung","required"); - $this->_ci->form_validation->set_rules('ort_kurzbz[]',"ort_kurzbz","required"); - $this->_ci->form_validation->set_rules('start_date',"start_date","required"); - $this->_ci->form_validation->set_rules('end_date',"end_date","required"); - - - if($this->_ci->form_validation->run() === FALSE) + if ($this->_ci->form_validation->run() === FALSE) $this->terminateWithValidationErrors($this->_ci->form_validation->error_array()); + $ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE); + + if (empty($ort_kurzbz) || !is_array($ort_kurzbz)) + { + $this->terminateWithValidationErrors(['ort_kurzbz' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Raum'])]); + } + $titel = $this->_ci->input->post('titel', TRUE); $beschreibung = $this->_ci->input->post('beschreibung', TRUE); - $ort_kurzbz = $this->_ci->input->post('ort_kurzbz', TRUE); $start_date = $this->_ci->input->post('start_date', TRUE); $end_date = $this->_ci->input->post('end_date', TRUE); $teilnehmer = $this->_ci->input->post('teilnehmer', TRUE); diff --git a/application/controllers/system/MigrateKalender.php b/application/controllers/system/MigrateKalender.php index f9d7416e5..49e895dbc 100644 --- a/application/controllers/system/MigrateKalender.php +++ b/application/controllers/system/MigrateKalender.php @@ -264,7 +264,108 @@ class MigrateKalender extends CLI_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) { $result = $this->KalenderModel->insert( diff --git a/application/helpers/hlp_common_helper.php b/application/helpers/hlp_common_helper.php index 06cfa1cfd..b9ce6b200 100644 --- a/application/helpers/hlp_common_helper.php +++ b/application/helpers/hlp_common_helper.php @@ -570,3 +570,13 @@ 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)); +} \ No newline at end of file diff --git a/application/libraries/CollisionChecker.php b/application/libraries/CollisionChecker.php index b7921a6d9..ac9152d2b 100644 --- a/application/libraries/CollisionChecker.php +++ b/application/libraries/CollisionChecker.php @@ -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); } diff --git a/application/libraries/KalenderLib.php b/application/libraries/KalenderLib.php index efc401ad1..174063819 100644 --- a/application/libraries/KalenderLib.php +++ b/application/libraries/KalenderLib.php @@ -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'); @@ -38,6 +39,7 @@ class KalenderLib $end_date = date('Y-m-d', strtotime($end_date . ' +1 day')); $this->_ci->KalenderModel->addSelect('tbl_kalender.kalender_id, + tbl_kalender.eindeutige_gruppen_id, tbl_kalender.status_kurzbz, tbl_kalender.typ, tbl_kalender.von, @@ -134,6 +136,8 @@ class KalenderLib $this->_ci->KalenderModel->db->where('tbl_kalender.von >=', $start_date); $this->_ci->KalenderModel->db->where('tbl_kalender.bis <', $end_date); + + $this->_ci->KalenderModel->addOrder('tbl_kalender.eindeutige_gruppen_id', 'DESC'); } private function _mapEvents($data, $collisionCheck = true) @@ -155,6 +159,7 @@ class KalenderLib $bis = new DateTime($row->bis); $events[$id] = (object) [ + 'eindeutige_gruppen_id' => $row->eindeutige_gruppen_id, 'type' => $row->typ, 'beginn' => $von->format('H:i:s'), 'ende' => $bis->format('H:i:s'), @@ -179,7 +184,8 @@ class KalenderLib 'titel' => isset($row->titel) ? $row->titel : '', 'beschreibung' => isset($row->beschreibung) ? $row->beschreibung : '', 'topic' => [], - 'collisions' => false + 'collisions' => false, + 'has_assigned_resources' => isset($row->has_assigned_resources) ? $row->has_assigned_resources : false, ]; } @@ -436,6 +442,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); @@ -579,9 +593,20 @@ class KalenderLib public function addToKalenderEvent($target_kalender_id, $lehreinheit_id) { - $check = $this->_ci->KalenderLehreinheitModel->loadWhere(array('kalender_id' => $target_kalender_id, 'lehreinheit_id' => $lehreinheit_id)); + $entryResult = $this->_loadKalenderEntry($target_kalender_id); + if (isError($entryResult)) return $entryResult; - if (hasData($check)) + $kalender_entry = getData($entryResult); + + if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), + 'errorCode' => 'entry_not_editable' + ]); + } + + if (in_array($lehreinheit_id, $kalender_entry->lehreinheit_id)) { return error([ 'message' => $this->_ci->phraseslib->t('ui', 'already_present'), @@ -589,6 +614,12 @@ class KalenderLib ]); } + $lehreinheit_ids = $kalender_entry->lehreinheit_id; + $lehreinheit_ids[] = $lehreinheit_id; + + if (in_array($kalender_entry->status_kurzbz, array('live', 'preview'))) + return success($this->_createHistoryEntry($kalender_entry, null, null, null, $lehreinheit_ids)); + $kalenderlehreinheitresult = $this->_ci->KalenderLehreinheitModel->insert( array( 'kalender_id' => $target_kalender_id, @@ -602,6 +633,50 @@ class KalenderLib return success($target_kalender_id); } + + public function deleteFromKalenderEvent($kalender_id, $lehreinheit_ids) + { + $entryResult = $this->_loadKalenderEntry($kalender_id); + if (isError($entryResult)) return $entryResult; + + $kalender_entry = getData($entryResult); + + if (in_array($kalender_entry->status_kurzbz, array('todelete', 'deleted'))) + { + return error([ + 'message' => $this->_ci->phraseslib->t('ui', 'entry_not_editable'), + 'errorCode' => 'entry_not_editable' + ]); + } + + $remaining_ids = array_values(array_diff($kalender_entry->lehreinheit_id, $lehreinheit_ids)); + + if (in_array($kalender_entry->status_kurzbz, array('live', 'preview'))) + return success($this->_createHistoryEntry($kalender_entry, null, null, null, $remaining_ids)); + + $count = 0; + foreach ($lehreinheit_ids as $lehreinheit_id) + { + $check = $this->_ci->KalenderLehreinheitModel->loadWhere(array('kalender_id' => $kalender_id, 'lehreinheit_id' => $lehreinheit_id)); + + if (hasData($check)) + { + $kalenderlehreinheitresult = $this->_ci->KalenderLehreinheitModel->delete( + array( + 'kalender_id' => $kalender_id, + 'lehreinheit_id' => $lehreinheit_id + ) + ); + + if (isError($kalenderlehreinheitresult)) + return $kalenderlehreinheitresult; + + $count++; + } + } + return success($count); + } + private function _addWeeks($date_str, $weeks) { $d = new DateTime($date_str); @@ -633,6 +708,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') ) @@ -990,6 +1066,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( @@ -1090,6 +1241,18 @@ class KalenderLib $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->betriebsmittel_ids = $calendarResourcesIDs; $kalender_entry->ort_kurzbz = !is_null($ort_kurzbz) ? (array) $ort_kurzbz : $kalender_entry->ort_kurzbz; $kalender_entry->von = $start_time ?? $kalender_entry->von; $kalender_entry->bis = $end_time ?? $kalender_entry->bis; @@ -1098,7 +1261,9 @@ class KalenderLib if (!empty($errors)) return error($errors); - if (!is_null($ort_kurzbz) && !empty($ort_kurzbz)) + $result = $kalender_entry; + + if (!is_null($ort_kurzbz)) { $result = $this->updateOrt($kalender_id, $ort_kurzbz); if (isError($result)) return $result; @@ -1115,7 +1280,7 @@ class KalenderLib if (isError($result)) return $result; } - return success('Erfolgreich'); + return success($result); } public function updateZeit($kalender_id, $start_date, $end_date) @@ -1150,6 +1315,39 @@ class KalenderLib ); } + public function getLehreinheiten($kalender_id) + { + $entryResult = $this->_loadKalenderEntry($kalender_id); + if (isError($entryResult)) return $entryResult; + + $entryResult = getData($entryResult); + + $result = []; + + foreach ($entryResult->lehreinheit_id as $lehreinheit_id) + { + $this->_ci->LehreinheitModel->addSelect('lehrfach.kurzbz AS lehrfach_kurzbz, + tbl_lehreinheit.lehrform_kurzbz, + lehrfach.bezeichnung AS lehrfach_bezeichnung'); + $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung', 'tbl_lehreinheit.lehrveranstaltung_id = tbl_lehrveranstaltung.lehrveranstaltung_id', 'LEFT'); + $this->_ci->LehreinheitModel->addJoin('lehre.tbl_lehrveranstaltung lehrfach', 'tbl_lehreinheit.lehrfach_id = lehrfach.lehrveranstaltung_id', 'LEFT'); + $lehreinheit = $this->_ci->LehreinheitModel->load($lehreinheit_id); + + if (!hasData($lehreinheit)) + return error('Lehreinheit nicht gefunden'); + $lehreinheit = getData($lehreinheit)[0]; + + $topic = trim((isset($lehreinheit->lehrfach_kurzbz) ? $lehreinheit->lehrfach_kurzbz : '').' '.(isset($lehreinheit->lehrform_kurzbz) ? $lehreinheit->lehrform_kurzbz : '')); + + $result[] = (object)[ + 'lehreinheit_id' => $lehreinheit_id, + 'title' => $topic, + ]; + } + + return success($result); + } + public function updateOrt($kalender_id, $ort_kurzbz_array) { $ort_kurzbz_array = (array)$ort_kurzbz_array; @@ -1226,7 +1424,7 @@ class KalenderLib return success(); } - private function _createHistoryEntry($kalender_entry, $start_date = null, $end_date = null, $ort_kurzbz_array = null) + private function _createHistoryEntry($kalender_entry, $start_date = null, $end_date = null, $ort_kurzbz_array = null, $lehreinheit_id_array = null) { $old_id = $kalender_entry->kalender_id; @@ -1237,6 +1435,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') ) @@ -1247,7 +1446,7 @@ class KalenderLib $new_kalender_id = getData($kalenderresult); - $lehreinheit_ids = (array) $kalender_entry->lehreinheit_id; + $lehreinheit_ids = !is_null($lehreinheit_id_array) ? $lehreinheit_id_array : $kalender_entry->lehreinheit_id; foreach ($lehreinheit_ids as $lehreinheit_id) { @@ -1262,7 +1461,7 @@ class KalenderLib return $kalenderlehreinheitresult; } - if (!empty($ort_kurzbz_array)) + if (!is_null($ort_kurzbz_array)) { $new_orte = (array) $ort_kurzbz_array; $new_locations = array_fill(0, count($new_orte), null); @@ -1289,7 +1488,6 @@ class KalenderLib return success($new_kalender_id); } - private function _loadKalenderEntry($kalender_id) { $this->_ci->KalenderModel->addSelect(' @@ -1447,7 +1645,7 @@ class KalenderLib public function getHistory($kalender_id, $only_previous = false) { $dbModel = new DB_Model(); - $history_entries = $dbModel->execReadOnlyQuery(' + $history_entries = $dbModel->execReadOnlyQuery(" WITH RECURSIVE vorgaenger(kalender_id, vorgaenger_kalender_id) AS ( SELECT kalender_id, vorgaenger_kalender_id @@ -1470,21 +1668,27 @@ class KalenderLib SELECT k.kalender_id, k.vorgaenger_kalender_id FROM lehre.tbl_kalender k JOIN nachfolger ON k.vorgaenger_kalender_id = nachfolger.kalender_id + ), + orte AS ( + SELECT kalender_id, + STRING_AGG(COALESCE(ort_kurzbz, location), ', ' ORDER BY ort_kurzbz) AS ort + FROM lehre.tbl_kalender_ort + GROUP BY kalender_id ) SELECT tbl_kalender.*, - TO_CHAR(von, \'DD.MM.YYYY HH24:MI:SS\') as von, - TO_CHAR(bis, \'DD.MM.YYYY HH24:MI:SS\') as bis, - COALESCE(tbl_kalender_ort.ort_kurzbz, location) as ort + TO_CHAR(von, 'DD.MM.YYYY HH24:MI:SS') as von, + TO_CHAR(bis, 'DD.MM.YYYY HH24:MI:SS') as bis, + orte.ort FROM ( SELECT kalender_id FROM vorgaenger UNION SELECT kalender_id FROM nachfolger ) combined JOIN lehre.tbl_kalender USING(kalender_id) - LEFT JOIN lehre.tbl_kalender_ort USING(kalender_id) + LEFT JOIN orte USING(kalender_id) ORDER BY kalender_id DESC - ' . ($only_previous ? ' LIMIT 1' : ''), array($kalender_id, $kalender_id)); + " . ($only_previous ? ' LIMIT 1' : ''), array($kalender_id, $kalender_id)); return $history_entries; } diff --git a/application/libraries/RaumvorschlagLib.php b/application/libraries/RaumvorschlagLib.php index f36ef2cfa..8cb4aa8a4 100644 --- a/application/libraries/RaumvorschlagLib.php +++ b/application/libraries/RaumvorschlagLib.php @@ -62,6 +62,14 @@ class RaumvorschlagLib $kandidaten = $this->_getRaumkandidaten($event); if (empty($kandidaten)) return []; + $unique = []; + + foreach ($kandidaten as $raum) + { + $unique[$raum->ort_kurzbz] = $raum; + } + + $kandidaten = array_values($unique); $ratings = []; foreach ($kandidaten as $raum) @@ -82,7 +90,7 @@ class RaumvorschlagLib usort($ratings, function($a, $b) { - return $b['score'] - $a['score']; + return $b['score'] <=> $a['score']; }); return $ratings; @@ -213,21 +221,33 @@ class RaumvorschlagLib private function _getRaumkandidaten($event) { - $lehreinheit = $this->_ci->LehreinheitModel->load($event->lehreinheit_id[0]); - if (!hasData($lehreinheit)) return []; - $lehreinheit = getData($lehreinheit)[0]; + $raumtyp = null; + $raumtypalternativ = null; + if (!empty($event->lehreinheit_id)) + { + $lehreinheit = $this->_ci->LehreinheitModel->load($event->lehreinheit_id[0]); + if (hasData($lehreinheit)) + { + $lehreinheit = getData($lehreinheit)[0]; + $raumtyp = $lehreinheit->raumtyp ?? null; + $raumtypalternativ = $lehreinheit->raumtypalternativ ?? null; + } + } + + $this->_ci->KalenderModel->addDistinct('tbl_kalender_ort.ort_kurzbz'); $this->_ci->KalenderModel->addSelect('tbl_kalender_ort.ort_kurzbz'); $this->_ci->KalenderModel->addJoin('lehre.tbl_kalender_ort', 'tbl_kalender.kalender_id = tbl_kalender_ort.kalender_id'); $this->_ci->KalenderModel->db->where('tbl_kalender.von <', $event->isoend); $this->_ci->KalenderModel->db->where('tbl_kalender.bis >', $event->isostart); + $this->_ci->KalenderModel->db->where('tbl_kalender.kalender_id !=', $event->kalender_id); $this->_ci->KalenderModel->db->where_not_in('tbl_kalender.status_kurzbz', ['deleted']); $this->_ci->KalenderModel->db->where('tbl_kalender_ort.ort_kurzbz IS NOT NULL', null, false); $belegte = $this->_ci->KalenderModel->load(); $belegte_orte = hasData($belegte) ? array_column(getData($belegte), 'ort_kurzbz') : []; - if (empty($lehreinheit->raumtyp)) + if (empty($raumtyp)) { $raeume = $this->_getFreieRaeume(null, $belegte_orte); return hasData($raeume) ? getData($raeume) : []; @@ -235,14 +255,14 @@ class RaumvorschlagLib $vorschlaege = []; - $raeume = $this->_getFreieRaeume($lehreinheit->raumtyp, $belegte_orte); + $raeume = $this->_getFreieRaeume($raumtyp, $belegte_orte); if (hasData($raeume)) $vorschlaege = getData($raeume); - if (count($vorschlaege) < 5 && !empty($lehreinheit->raumtypalternativ)) + if (count($vorschlaege) < 5 && !empty($raumtypalternativ)) { $bereits_gefunden = array_merge($belegte_orte, array_column($vorschlaege, 'ort_kurzbz')); - $alternativ = $this->_getFreieRaeume($lehreinheit->raumtypalternativ, $bereits_gefunden); + $alternativ = $this->_getFreieRaeume($raumtypalternativ, $bereits_gefunden); if (!isError($alternativ) && hasData($alternativ)) $vorschlaege = array_merge($vorschlaege, getData($alternativ)); @@ -255,7 +275,12 @@ class RaumvorschlagLib { $this->_ci->OrtModel->addSelect('ort_kurzbz, stockwerk, standort_id'); $this->_ci->OrtModel->addJoin('public.tbl_ortraumtyp', 'ort_kurzbz'); - $this->_ci->OrtModel->db->where('raumtyp_kurzbz', $raumtyp); + + if (!empty($raumtyp)) + $this->_ci->OrtModel->db->where('raumtyp_kurzbz', $raumtyp); + else + $this->_ci->OrtModel->db->where('reservieren', true); + $this->_ci->OrtModel->db->where('aktiv', true); $this->_ci->OrtModel->db->where("ort_kurzbz NOT LIKE '\_%'", null, false); diff --git a/application/libraries/collision/checks/LectureCollisionCheck.php b/application/libraries/collision/checks/LectureCollisionCheck.php index ff8303adb..21635e203 100644 --- a/application/libraries/collision/checks/LectureCollisionCheck.php +++ b/application/libraries/collision/checks/LectureCollisionCheck.php @@ -9,7 +9,7 @@ class LectureCollisionCheck implements ICollisionCheck { $this->_ci =& get_instance(); $this->_ci->load->model('ressource/Kalender_model', 'KalenderModel'); - $this->_ci->load->model('ressource/zeitsperre_model', 'ZeitsperreModel'); + $this->_ci->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel'); $this->_ci->load->library('VariableLib', array('uid' => getAuthUID())); $this->_ci->load->library('PhrasesLib', array('ui')); diff --git a/application/libraries/collision/checks/ResourcesCollisionCheck.php b/application/libraries/collision/checks/ResourcesCollisionCheck.php new file mode 100644 index 000000000..0be789b7f --- /dev/null +++ b/application/libraries/collision/checks/ResourcesCollisionCheck.php @@ -0,0 +1,85 @@ +_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; + } +} \ No newline at end of file diff --git a/application/models/education/Lehreinheit_model.php b/application/models/education/Lehreinheit_model.php index 4c17b581e..b1c50db2f 100644 --- a/application/models/education/Lehreinheit_model.php +++ b/application/models/education/Lehreinheit_model.php @@ -416,6 +416,9 @@ EOSQL; JOIN lehre.tbl_kalender_lehreinheit USING(lehreinheit_id) JOIN lehre.tbl_kalender USING(kalender_id) WHERE tbl_lehreinheit.lehreinheit_id IN ($placeholders) + AND NOT EXISTS (SELECT 1 FROM lehre.tbl_kalender nachfolger + WHERE nachfolger.vorgaenger_kalender_id = tbl_kalender.kalender_id) + AND tbl_kalender.status_kurzbz NOT IN ('deleted', 'todelete', 'archived') GROUP BY tbl_kalender_lehreinheit.lehreinheit_id, tbl_kalender.von, tbl_kalender.bis, tbl_kalender.kalender_id ), raster AS ( diff --git a/application/models/ressource/BetriebsmittelKalender_model.php b/application/models/ressource/BetriebsmittelKalender_model.php new file mode 100644 index 000000000..f9a519bbf --- /dev/null +++ b/application/models/ressource/BetriebsmittelKalender_model.php @@ -0,0 +1,14 @@ +dbTable = 'lehre.tbl_betriebsmittel_kalender'; + $this->pk = 'betriebsmittel_kalender_id'; + } +} diff --git a/application/models/ressource/Betriebsmittel_model.php b/application/models/ressource/Betriebsmittel_model.php index 290c3491d..cf974086d 100644 --- a/application/models/ressource/Betriebsmittel_model.php +++ b/application/models/ressource/Betriebsmittel_model.php @@ -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($to, $from)); + } } diff --git a/application/models/ressource/Kalender_model.php b/application/models/ressource/Kalender_model.php index 2ca8e6ac1..d538b7313 100644 --- a/application/models/ressource/Kalender_model.php +++ b/application/models/ressource/Kalender_model.php @@ -11,4 +11,15 @@ class Kalender_model extends DB_Model $this->dbTable = 'lehre.tbl_kalender'; $this->pk = 'kalender_id'; } + + public function generateUniqueGroupId() + { + while (true) { + $uniqueGroupId = generateUUID(); + $result = $this->loadWhere(['eindeutige_gruppen_id' => $uniqueGroupId]); + if (!hasData($result)) { + return $uniqueGroupId; + } + } + } } diff --git a/application/views/Tempus.php b/application/views/Tempus.php index 0bd9cb697..438323eb3 100644 --- a/application/views/Tempus.php +++ b/application/views/Tempus.php @@ -24,9 +24,8 @@ 'public/css/components/function.css' ], 'customJSs' => [ - #'vendor/npm-asset/primevue/tree/tree.min.js', - #'vendor/npm-asset/primevue/toast/toast.min.js' - 'vendor/moment/luxonjs/luxon.min.js' + 'vendor/moment/luxonjs/luxon.min.js', + 'vendor/npm-asset/primevue/listbox/listbox.min.js' ], 'customJSModules' => [ 'public/js/apps/Tempus.js' diff --git a/public/js/api/factory/operationalResourceToCalender.js b/public/js/api/factory/operationalResourceToCalender.js new file mode 100644 index 000000000..618ab9e68 --- /dev/null +++ b/public/js/api/factory/operationalResourceToCalender.js @@ -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 . + */ + +export default { + getSchedulableResourcesByCalendar(calendarID) { + return { + method: "get", + url: "/api/frontend/v1/tempus/OperationalResourceToCalenderAPI/getSchedulableResourcesByCalendar/" + calendarID, + }; + }, + getAssignedResourcesByCalender(calenderID) { + return { + method: "get", + url: "/api/frontend/v1/tempus/OperationalResourceToCalenderAPI/getAssignedResourcesByCalender/" + calenderID, + }; + }, + storeResourcesToCalendarRelationship(calenderID, assignedResources) { + return { + method: "post", + url: "/api/frontend/v1/tempus/OperationalResourceToCalenderAPI/storeResourcesToCalendarRelationship", + params: { kalender_id: calenderID, assignedResources: assignedResources }, + }; + } +}; diff --git a/public/js/api/factory/tempus/kalender.js b/public/js/api/factory/tempus/kalender.js index 9d2746434..eb89542f6 100644 --- a/public/js/api/factory/tempus/kalender.js +++ b/public/js/api/factory/tempus/kalender.js @@ -81,6 +81,13 @@ export default { params: { kalender_id, updatedInfos} }; }, + deleteOrtEntry(kalender_id) { + return { + method: 'post', + url: '/api/frontend/v1/tempus/Kalender/deleteOrtEntry', + params: { kalender_id} + }; + }, addKalenderEvent(lehreinheit_id, ort_kurzbz, start_date, end_date) { return { method: 'post', @@ -117,6 +124,13 @@ export default { params: { kalender_id } }; }, + getLehreinheiten(kalender_id) { + return { + method: 'get', + url: '/api/frontend/v1/tempus/Kalender/getLehreinheiten', + params: { kalender_id } + }; + }, getHistory(kalender_id) { return { @@ -132,5 +146,12 @@ export default { params: { kalender_id } }; }, + deleteFromKalenderEvent(kalender_id, lehreinheit_ids) { + return { + method: 'post', + url: '/api/frontend/v1/tempus/Kalender/deleteFromKalenderEvent', + params: { kalender_id, lehreinheit_ids } + }; + }, }; diff --git a/public/js/components/Calendar/Base/Grid.js b/public/js/components/Calendar/Base/Grid.js index 0f0ed68b7..39a2f9c4a 100644 --- a/public/js/components/Calendar/Base/Grid.js +++ b/public/js/components/Calendar/Base/Grid.js @@ -27,7 +27,8 @@ export default { provide() { return { flipAxis: Vue.computed(() => this.flipAxis), - axisRow: Vue.computed(() => this.axisRow) + axisRow: Vue.computed(() => this.axisRow), + onDropEvent: Vue.computed(() => this.onDropEvent), }; }, props: { @@ -376,6 +377,13 @@ export default { return dropStart.startOf('day').plus(lastBlock.end); }, + onDropEvent(evt, items, date) + { + if (this.snapToGrid) + this.onDropSnap(evt, items, date) + else + this.onDropFree(evt, items, date) + }, onDropSnap(evt, items, date, part) { let obj = items; if (!obj?.orig) return; @@ -390,7 +398,7 @@ export default { const grabOffset = grabTime.diff(dropDay); const snappedPart = blocks.find(b => grabOffset >= b.start && grabOffset < b.end) || part; - const dropStart = dropDay.plus(snappedPart.start); + const dropStart = snappedPart ? dropDay.plus(snappedPart.start) : grabTime; let nettoDuration = this._getNettoDurationForDrop(obj); let dropEnd = this.calculateDropEnd(dropStart, nettoDuration); @@ -481,6 +489,7 @@ export default { class="fhc-calendar-base-grid" style="display:grid;width:100%;height:100%;overflow:auto" :style="'grid-template-' + axisRow + 's:auto' + (allDayEvents ? ' auto ' : ' ') + '1fr;grid-template-' + axisCol + 's:auto ' + styleGridCols" + data-cy="calendar-base-grid" >
null + }, + onDropEvent: { + from: "onDropEvent", + default: () => () => {} } }, props: { @@ -81,7 +85,8 @@ export default { activeContextActions() { if (this.isHeaderOrFooter) return []; const type = this.event.orig?.type ?? 'lehreinheit'; - return this.contextMenuActions[type] ?? this.contextMenuActions['default'] ?? []; + const actions = this.contextMenuActions[type] ?? this.contextMenuActions['default'] ?? []; + return actions.filter(action => !action.visible || action.visible(this.event.orig)); } }, methods: { @@ -128,13 +133,7 @@ export default { }); } - return this.onDrop({ - item: [obj], - start: this.event.start.toISO(), - end: this.event.end.toISO(), - ctrlKey: false, - targetKalenderId: null - }); + return this.onDropEvent(evt, items, this.event.start.startOf('day')); }, }, template:` @@ -149,6 +148,9 @@ export default { v-drop:move.lehreinheit.kalender.reservierung="onDropOnCard" v-cal-click:event="isHeaderOrFooter ? event : event.orig" @contextmenu.prevent="onRightClick" + :data-id="'event-' + event.orig.kalender_id" + :data-group-id="'event-group-' + event.orig.eindeutige_gruppen_id" + data-cy="calendar-event" >