From 7858d90d122631f4e8746a8cd8d08ade085db693 Mon Sep 17 00:00:00 2001 From: ma0068 Date: Mon, 1 Jun 2026 14:10:14 +0200 Subject: [PATCH] new workflow Terminate of Studierendenantrag - DB Update for new studierendenantrag_statustyp with studierendenantrag_statustyp_kurzbz = 'Storniert' - new permission student/studierendenantrag - possibility to set status to Terminated in case of new permission --- .../api/frontend/v1/studstatus/Leitung.php | 36 ++++++++++- .../controllers/lehre/Studierendenantrag.php | 5 +- application/libraries/AntragLib.php | 47 +++++++++++++++ .../Studierendenantragstatus_model.php | 1 + .../views/lehre/Antrag/Leitung/List.php | 1 + public/js/api/factory/studstatus/leitung.js | 9 ++- .../components/Studierendenantrag/Leitung.js | 17 +++++- .../Studierendenantrag/Leitung/Table.js | 49 ++++++++++++++- system/dbupdate_3.4.php | 1 + ...00_studstatus_stornierte_wiederholende.php | 15 +++++ system/phrasesupdate.php | 60 +++++++++++++++++++ 11 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 system/dbupdate_3.4/77000_studstatus_stornierte_wiederholende.php diff --git a/application/controllers/api/frontend/v1/studstatus/Leitung.php b/application/controllers/api/frontend/v1/studstatus/Leitung.php index 87099ad74..6cf0fca23 100644 --- a/application/controllers/api/frontend/v1/studstatus/Leitung.php +++ b/application/controllers/api/frontend/v1/studstatus/Leitung.php @@ -45,7 +45,8 @@ class Leitung extends FHCAPI_Controller 'unpauseAntrag' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], 'objectAntrag' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], 'approveObjection' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], - 'denyObjection' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'] + 'denyObjection' => ['student/antragfreigabe:w', 'student/studierendenantrag:w'], + 'terminateAntrag' => ['student/antragstornieren:w'] ]); // Libraries @@ -426,4 +427,37 @@ class Leitung extends FHCAPI_Controller return $this->terminateWithSuccess($studierendenantrag_id); } + + public function terminateAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + [ + 'required', + ['isEntitledToTerminateAntrag', [$this->antraglib, 'isEntitledToTerminateAntrag']], + ['antragCanBeTerminated', [$this->antraglib, 'antragCanBeTerminated']] + ], + [ + 'isEntitledToUnpauseAntrag' => $this->p->t('studierendenantrag', 'error_no_right'), + 'antragCanBeTerminated' => $this->p->t( + 'studierendenantrag', + 'error_not_terminated', + ['id' => $this->input->post('studierendenantrag_id')] + ) + ] + ); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->terminateAntrag($studierendenantrag_id, getAuthUID()); + $this->getDataOrTerminateWithError($result); + + return $this->terminateWithSuccess($studierendenantrag_id); + } } diff --git a/application/controllers/lehre/Studierendenantrag.php b/application/controllers/lehre/Studierendenantrag.php index 107c9af96..c88e8b7a9 100644 --- a/application/controllers/lehre/Studierendenantrag.php +++ b/application/controllers/lehre/Studierendenantrag.php @@ -86,9 +86,12 @@ class Studierendenantrag extends FHC_Controller $stgA = $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag') ?: []; + $permissionTerminateAntrag = $this->permissionlib->isBerechtigt('student/antragstornieren'); + $this->load->view('lehre/Antrag/Leitung/List', [ 'stgA' => $stgA, - 'stgL' => $stgL + 'stgL' => $stgL, + 'permissionTerminateAntrag' => $permissionTerminateAntrag ]); } diff --git a/application/libraries/AntragLib.php b/application/libraries/AntragLib.php index 3d8a2ea26..732691425 100644 --- a/application/libraries/AntragLib.php +++ b/application/libraries/AntragLib.php @@ -1948,6 +1948,22 @@ class AntragLib return $result; } + /** + * @param integer $antrag_id + * @param string $insertvon + * + * @return stdClass + */ + public function terminateAntrag($antrag_id, $insertvon) + { + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_TERMINATED, + 'insertvon' => $insertvon + ]); + return $result; + } + /** * @param integer $studierendenantrag_id @@ -2119,6 +2135,16 @@ class AntragLib return $this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe'); } + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToTerminateAntrag($antrag_id) + { + return ($this->hasAccessToAntrag($antrag_id, 'student/antragstornieren')); + } + /** * @param integer $antrag_id * @@ -2149,6 +2175,27 @@ class AntragLib return $this->_ci->StudierendenantragModel->isManuallyPaused($antrag_id); } + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function antragCanBeTerminated($antrag_id) + { + $this->_ci->StudierendenantragModel->db->where_not_in('campus.get_status_studierendenantrag(studierendenantrag_id)', [ + Studierendenantragstatus_model::STATUS_DEREGISTERED, + Studierendenantragstatus_model::STATUS_APPROVED, + Studierendenantragstatus_model::STATUS_TERMINATED + ]); + $result = $this->_ci->StudierendenantragModel->loadWhere([ + 'studierendenantrag_id' => $antrag_id, + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG + ]); + + return hasData($result); + } + + /** * @param integer $antrag_id * @param string|array $status diff --git a/application/models/education/Studierendenantragstatus_model.php b/application/models/education/Studierendenantragstatus_model.php index cf9cce1be..5e1c1e781 100644 --- a/application/models/education/Studierendenantragstatus_model.php +++ b/application/models/education/Studierendenantragstatus_model.php @@ -16,6 +16,7 @@ class Studierendenantragstatus_model extends DB_Model const STATUS_OBJECTION_DENIED = 'EinspruchAbgelehnt'; const STATUS_DEREGISTERED = 'Abgemeldet'; const STATUS_PAUSE = 'Pause'; + const STATUS_TERMINATED = 'Storniert'; const INSERTVON_ABMELDUNGSTGL = "AbmeldungStgl"; const INSERTVON_DEREGISTERED = "Studienabbruch"; diff --git a/application/views/lehre/Antrag/Leitung/List.php b/application/views/lehre/Antrag/Leitung/List.php index 1225b16b6..983a96ee5 100644 --- a/application/views/lehre/Antrag/Leitung/List.php +++ b/application/views/lehre/Antrag/Leitung/List.php @@ -44,6 +44,7 @@ $this->load->view( diff --git a/public/js/api/factory/studstatus/leitung.js b/public/js/api/factory/studstatus/leitung.js index 7f6bc9f54..d6fb5d09e 100644 --- a/public/js/api/factory/studstatus/leitung.js +++ b/public/js/api/factory/studstatus/leitung.js @@ -96,5 +96,12 @@ export default { url: '/api/frontend/v1/studstatus/leitung/denyObjection', params: antrag }; - } + }, + terminate(antrag) { + return { + method: 'post', + url: '/api/frontend/v1/studstatus/leitung/terminateAntrag', + params: antrag + }; + }, }; \ No newline at end of file diff --git a/public/js/components/Studierendenantrag/Leitung.js b/public/js/components/Studierendenantrag/Leitung.js index dc412c6c2..33bc29bea 100644 --- a/public/js/components/Studierendenantrag/Leitung.js +++ b/public/js/components/Studierendenantrag/Leitung.js @@ -18,7 +18,11 @@ export default { }, props: { stgL: Array, - stgA: Array + stgA: Array, + permissionTerminateAntrag: { + type: Boolean, + default: false + }, }, data() { return { @@ -223,6 +227,13 @@ export default { .then(this.showResults); } }, + actionTerminate(evt){ + var antraege = evt || this.selectedData; + this.$refs.loader.show(); + this + ._singleOrMultiApiCall(antraege, ApiStudstatusLeitung.terminate) + .then(this.showResults); + }, showResults(results) { let fulfilled = results.filter(res => res.status == 'fulfilled'); this.$refs.loader.hide(); @@ -270,6 +281,7 @@ export default { :stg-a="stgkzA" :stg-l="stgkzL" :filter="filter" + :permission-terminate-antrag="permissionTerminateAntrag" v-model:columnData="columns" v-model:selectedData="selectedData" @action:approve="actionApprove" @@ -280,7 +292,8 @@ export default { @action:objectionApprove="actionObjectionApprove" @action:cancel="actionCancel" @action:pause="actionPause" - @action:unpause="actionUnpause" + @action:unpause="actionUnpause" + @action:terminate="actionTerminate" @reload="reload" > diff --git a/public/js/components/Studierendenantrag/Leitung/Table.js b/public/js/components/Studierendenantrag/Leitung/Table.js index fa5f50939..50ebbf4b2 100644 --- a/public/js/components/Studierendenantrag/Leitung/Table.js +++ b/public/js/components/Studierendenantrag/Leitung/Table.js @@ -16,7 +16,11 @@ export default { columnData: Array, stgL: Array, stgA: Array, - filter: String + filter: String, + permissionTerminateAntrag: { + type: Boolean, + default: false + }, }, emits: [ 'update:columnData', @@ -29,7 +33,8 @@ export default { 'action:objectionApprove', 'action:cancel', 'action:pause', - 'action:unpause' + 'action:unpause', + 'action:terminate' ], data() { return { @@ -308,6 +313,26 @@ export default { button.className = "btn btn-outline-secondary"; button.addEventListener('click', () => this.$emit('action:pause', [cell.getData()])); container.append(button); + + // Terminate + if(this.permissionTerminateAntrag){ + button = document.createElement('button'); + icon = document.createElement('i'); + span = document.createElement('span'); + + icon.className = "fa-solid fa-xmark"; + icon.setAttribute('aria-hidden', true); + icon.setAttribute('title', this.$p.t('studierendenantrag', 'btn_terminate')); + + span.className = "fa-sr-only"; + span.append(this.$p.t('studierendenantrag', 'btn_terminate')); + + button.append(icon); + button.append(span); + button.className = "btn btn-outline-danger"; + button.addEventListener('click', () => this.$emit('action:terminate', [cell.getData()])); + container.append(button); + } } let canUnpause = data.status == 'Pause' && !['AbmeldungStgl', 'Studienabbruch'].includes(data.status_insertvon); @@ -337,6 +362,26 @@ export default { button.className = "btn btn-outline-secondary"; button.addEventListener('click', () => this.$emit('action:unpause', [cell.getData()])); container.append(button); + + // Terminate: show buttion + if(this.permissionTerminateAntrag){ + button = document.createElement('button'); + icon = document.createElement('i'); + span = document.createElement('span'); + + icon.className = "fa-solid fa-xmark"; + icon.setAttribute('aria-hidden', true); + icon.setAttribute('title', this.$p.t('studierendenantrag', 'btn_terminate')); + + span.className = "fa-sr-only"; + span.append(this.$p.t('studierendenantrag', 'btn_terminate')); + + button.append(icon); + button.append(span); + button.className = "btn btn-outline-danger"; + button.addEventListener('click', () => this.$emit('action:terminate', [cell.getData()])); + container.append(button); + } } if (data.typ == 'AbmeldungStgl' && data.status == 'Genehmigt') { diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 8b6af9f2d..c26cd7c31 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -94,6 +94,7 @@ require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php'); 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/77000_studstatus_stornierte_wiederholende.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/77000_studstatus_stornierte_wiederholende.php b/system/dbupdate_3.4/77000_studstatus_stornierte_wiederholende.php new file mode 100644 index 000000000..9e94a252b --- /dev/null +++ b/system/dbupdate_3.4/77000_studstatus_stornierte_wiederholende.php @@ -0,0 +1,15 @@ +db_query("SELECT 1 FROM campus.tbl_studierendenantrag_statustyp WHERE studierendenantrag_statustyp_kurzbz= 'Storniert';")) +{ + if($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO campus.tbl_studierendenantrag_statustyp(studierendenantrag_statustyp_kurzbz, bezeichnung) VALUES ('Storniert', '{\"Storniert\", \"Terminated\"}');"; + + if(!$db->db_query($qry)) + echo 'campus.tbl_studierendenantrag_statustyp: '.$db->db_last_error().'
'; + else + echo '
campus.tbl_studierendenantrag_statustyp: Zeile Storniert hinzugefuegt!
'; + } +} diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 5f6269365..84748fb5b 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -58177,6 +58177,66 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // ### Phrases Dashboard Admin END + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_terminated', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Storniert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Terminated', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_terminate', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Stornieren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Terminate', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_not_terminated', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Id {id} ist bereits storniert', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Id {id} is already terminated', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), );