From 64ce3d1f6af1ce5c1146845d1ab4aa7788c1902d Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Tue, 4 Jul 2023 10:34:14 +0200 Subject: [PATCH] Cleaned up commit from 'feature-27351/Digitalisierung_Formulare_Abmeldung_Unterbrechung_Wiederholung' --- application/config/studierendenantrag.php | 152 ++ .../components/Antrag/Abmeldung.php | 201 ++ .../controllers/components/Antrag/Leitung.php | 358 ++++ .../components/Antrag/Unterbrechung.php | 230 +++ .../components/Antrag/Wiederholung.php | 369 ++++ .../controllers/components/Phrasen.php | 23 + application/controllers/jobs/AntragJob.php | 406 ++++ .../controllers/lehre/Antrag/Attachment.php | 82 + .../controllers/lehre/Antrag/Wiederholung.php | 49 + .../controllers/lehre/Studierendenantrag.php | 208 ++ application/helpers/hlp_common_helper.php | 16 + application/libraries/AntragLib.php | 1476 ++++++++++++++ application/libraries/DmsLib.php | 2 + application/libraries/PrestudentLib.php | 356 ++++ application/models/crm/Konto_model.php | 43 +- .../models/crm/Prestudentstatus_model.php | 90 + .../models/education/Pruefung_model.php | 139 +- .../education/Studierendenantrag_model.php | 229 +++ ...dierendenantraglehrveranstaltung_model.php | 79 + .../Studierendenantragstatus_model.php | 52 + .../models/education/Zeugnisnote_model.php | 81 +- .../models/organisation/Studiengang_model.php | 53 +- .../models/organisation/Studienplan_model.php | 34 +- .../organisation/Studiensemester_model.php | 30 + application/models/person/Person_model.php | 13 + application/views/lehre/Antrag/Create.php | 54 + .../views/lehre/Antrag/Leitung/List.php | 58 + .../views/lehre/Antrag/Student/List.php | 151 ++ .../lehre/Antrag/Wiederholung/Student.php | 43 + .../lehre/Antrag/Wiederholung/getLvs.rdf.php | 31 + .../lehre/Antrag/Wiederholung/moveLvs.rdf.php | 17 + application/views/templates/FHC-Common.php | 2 +- application/views/templates/FHC-Header.php | 3 + cis/private/pdfExport.php | 2 + cis/private/profile/dokumente.php | 22 + composer.json | 27 +- composer.lock | 463 ++++- content/fas.xul.php | 35 +- content/student/studentnotenoverlay.xul.php | 79 + content/student/studentoverlay.js.php | 320 ++- include/dokument_export.class.php | 4 + include/studierendenantrag.class.php | 147 ++ locale/de-AT/fas.dtd | 12 + public/css/Fhc.css | 79 + public/css/cis_bs5.css | 66 + public/js/apps/lehre/Antrag.js | 22 + public/js/apps/lehre/Antrag/Leitung.js | 8 + public/js/apps/lehre/Antrag/Lvzuweisung.js | 8 + public/js/apps/lehre/Antrag/Student.js | 8 + public/js/components/Bootstrap/Alert.js | 44 + public/js/components/Bootstrap/Confirm.js | 22 + public/js/components/Bootstrap/Modal.js | 109 + public/js/components/Bootstrap/Prompt.js | 37 + public/js/components/Fetch.js | 17 +- public/js/components/Loader.js | 62 + .../components/Studierendenantrag/Antrag.js | 60 + .../Studierendenantrag/Form/Abmeldung.js | 285 +++ .../Studierendenantrag/Form/Unterbrechung.js | 359 ++++ .../Studierendenantrag/Form/Wiederholung.js | 212 ++ .../Studierendenantrag/Infoblock.js | 19 + .../components/Studierendenantrag/Leitung.js | 312 +++ .../Studierendenantrag/Leitung/Actions.js | 87 + .../Leitung/Actions/Columns.js | 25 + .../Studierendenantrag/Leitung/Actions/New.js | 118 ++ .../Studierendenantrag/Leitung/GrundPopup.js | 62 + .../Studierendenantrag/Leitung/Header.js | 24 + .../Studierendenantrag/Leitung/LvPopup.js | 142 ++ .../Studierendenantrag/Leitung/Table.js | 388 ++++ .../Studierendenantrag/Lvzuweisung.js | 242 +++ .../components/Studierendenantrag/Status.js | 15 + public/js/components/vueDatepicker.js.php | 12 + public/js/mixins/Phrasen.js | 85 + public/js/tabulator/filters/Dates.js | 43 + rdf/AntragAbmeldung.xml.php | 68 + rdf/AntragUnterbrechung.xml.php | 71 + system/dbupdate_3.4.php | 1 + .../27351_digitalisierung_formulare.php | 289 +++ system/phrasesupdate.php | 1784 +++++++++++++++++ system/vorlage_zip/AntragAbmeldung.odt | Bin 0 -> 37277 bytes system/vorlage_zip/AntragUnterbrechung.odt | Bin 0 -> 34155 bytes system/xsl/AntragAbmeldung.xsl | 368 ++++ system/xsl/AntragUnterbrechung.xsl | 459 +++++ 82 files changed, 12192 insertions(+), 61 deletions(-) create mode 100644 application/config/studierendenantrag.php create mode 100644 application/controllers/components/Antrag/Abmeldung.php create mode 100644 application/controllers/components/Antrag/Leitung.php create mode 100644 application/controllers/components/Antrag/Unterbrechung.php create mode 100644 application/controllers/components/Antrag/Wiederholung.php create mode 100644 application/controllers/components/Phrasen.php create mode 100644 application/controllers/jobs/AntragJob.php create mode 100644 application/controllers/lehre/Antrag/Attachment.php create mode 100644 application/controllers/lehre/Antrag/Wiederholung.php create mode 100644 application/controllers/lehre/Studierendenantrag.php create mode 100644 application/libraries/AntragLib.php create mode 100644 application/libraries/PrestudentLib.php create mode 100644 application/models/education/Studierendenantrag_model.php create mode 100644 application/models/education/Studierendenantraglehrveranstaltung_model.php create mode 100644 application/models/education/Studierendenantragstatus_model.php create mode 100644 application/views/lehre/Antrag/Create.php create mode 100644 application/views/lehre/Antrag/Leitung/List.php create mode 100644 application/views/lehre/Antrag/Student/List.php create mode 100644 application/views/lehre/Antrag/Wiederholung/Student.php create mode 100644 application/views/lehre/Antrag/Wiederholung/getLvs.rdf.php create mode 100644 application/views/lehre/Antrag/Wiederholung/moveLvs.rdf.php create mode 100644 include/studierendenantrag.class.php create mode 100644 public/css/Fhc.css create mode 100644 public/css/cis_bs5.css create mode 100644 public/js/apps/lehre/Antrag.js create mode 100644 public/js/apps/lehre/Antrag/Leitung.js create mode 100644 public/js/apps/lehre/Antrag/Lvzuweisung.js create mode 100644 public/js/apps/lehre/Antrag/Student.js create mode 100644 public/js/components/Bootstrap/Alert.js create mode 100644 public/js/components/Bootstrap/Confirm.js create mode 100644 public/js/components/Bootstrap/Modal.js create mode 100644 public/js/components/Bootstrap/Prompt.js create mode 100644 public/js/components/Loader.js create mode 100644 public/js/components/Studierendenantrag/Antrag.js create mode 100644 public/js/components/Studierendenantrag/Form/Abmeldung.js create mode 100644 public/js/components/Studierendenantrag/Form/Unterbrechung.js create mode 100644 public/js/components/Studierendenantrag/Form/Wiederholung.js create mode 100644 public/js/components/Studierendenantrag/Infoblock.js create mode 100644 public/js/components/Studierendenantrag/Leitung.js create mode 100644 public/js/components/Studierendenantrag/Leitung/Actions.js create mode 100644 public/js/components/Studierendenantrag/Leitung/Actions/Columns.js create mode 100644 public/js/components/Studierendenantrag/Leitung/Actions/New.js create mode 100644 public/js/components/Studierendenantrag/Leitung/GrundPopup.js create mode 100644 public/js/components/Studierendenantrag/Leitung/Header.js create mode 100644 public/js/components/Studierendenantrag/Leitung/LvPopup.js create mode 100644 public/js/components/Studierendenantrag/Leitung/Table.js create mode 100644 public/js/components/Studierendenantrag/Lvzuweisung.js create mode 100644 public/js/components/Studierendenantrag/Status.js create mode 100644 public/js/components/vueDatepicker.js.php create mode 100644 public/js/mixins/Phrasen.js create mode 100644 public/js/tabulator/filters/Dates.js create mode 100644 rdf/AntragAbmeldung.xml.php create mode 100644 rdf/AntragUnterbrechung.xml.php create mode 100644 system/dbupdate_3.4/27351_digitalisierung_formulare.php create mode 100644 system/vorlage_zip/AntragAbmeldung.odt create mode 100644 system/vorlage_zip/AntragUnterbrechung.odt create mode 100644 system/xsl/AntragAbmeldung.xsl create mode 100644 system/xsl/AntragUnterbrechung.xsl diff --git a/application/config/studierendenantrag.php b/application/config/studierendenantrag.php new file mode 100644 index 000000000..85a3e1658 --- /dev/null +++ b/application/config/studierendenantrag.php @@ -0,0 +1,152 @@ + null, 'dokument_kurzbz' => null, 'kategorie_kurzbz' => null]; +$config['unterbrechung_dms'] = ['oe_kurzbz' => null, 'dokument_kurzbz' => null, 'kategorie_kurzbz' => 'Akte']; + +/** + * UPLOAD + */ + +/** + * Allowed filetypes for attachment upload in unterbrechung antrag + * + * @var array An array of fileextensions + */ +$config['unterbrechung_dms_filetypes'] = ['jpg', 'pdf']; + + +/** + * GRADES + */ + +/** + * On wiederholung the student must repeat certain lvs. + * This lvs will be graded with this id + * + * @var integer tbl_note.note + */ +$config['wiederholung_note_angerechnet'] = 19; + +/** + * On wiederholung the student can not attend certain lvs. + * Those lvs will be graded with this id + * + * @var integer tbl_note.note + */ +$config['wiederholung_note_nicht_zugelassen'] = 20; + + +/** + * JOBS + */ + +/** + * The Job will remind for every Unterbrecher who has a + * wiedereinstieg_datum between the date the Job is run + * and the modified date + * e.g.: If the Job is running on 2023-04-20 and the modifier + * is '+3 days' it will remind of everyone that + * has a wiedereinstiegs_datum between 2023-04-20 and 2023-04-23 + * + * @var string A string formated as PHP DateTime modifier + * @see https://www.php.net/manual/de/datetime.modify.php + */ +$config['unterbrechung_job_remind_wiedereinstieg_date_modifier'] = '+3 days'; + +/** + * The Job will sent a request to everyone who faild the 3rd committee exam + * and respecting the given conditions (not repeated yet, stg not in blacklist) + * to decide if he/she will repeat or not + * + * First request + * + * @var string A string formated as PHP DateTime modifier + * @see https://www.php.net/manual/de/datetime.modify.php + */ +$config['wiederholung_job_request_1_date_modifier'] = '+0 days'; + +/** + * Second request + * + * @var string A string formated as PHP DateTime modifier + * @see https://www.php.net/manual/de/datetime.modify.php + */ +$config['wiederholung_job_request_2_date_modifier'] = '+3 weeks'; + +/** + * Final deadline - after this the student will be abgemeldet if he hasn't chosen yet + * + * @var string A string formated as PHP DateTime modifier + * @see https://www.php.net/manual/de/datetime.modify.php + */ +$config['wiederholung_job_deadline_date_modifier'] = '+1 month'; + +/** + * Objection period - the student will be abgemeldet if he hasn't objected in this period + * + * @var string A string formated as PHP DateTime modifier + * @see https://www.php.net/manual/de/datetime.modify.php + */ +$config['abmeldung_job_deadline_date_modifier'] = '+3 weeks'; + + + +/** + * System User - uid of a user that is allowed to set prestudentstatus + * TODO(chris): DEBUG! CHANGE THIS! + * + * @var string + */ +$config['antrag_job_systemuser'] = 'ma0168'; + + +/** + * WHITELISTS + */ + +/** + * List of stati who entitle a prestudent to create an Antrag + * + * @var array Array of tbl_status.status_kurzbz's + */ +$config['antrag_prestudentstatus_whitelist'] = ['Student', 'Diplomand']; + + +/** + * BLACKLISTS + */ + +/** + * List of Statusgründe that prevent a prestudent from create an Wiederholungsantrag + * + * @var array An array of tbl_status_grund.statusgrund_id's + */ +$config['status_gruende_wiederholer'] = [16, 15]; + +/** + * Blacklisted for abmeldung anträge + * + * @var array An array of tbl_studiengang.studiengang_kz's + */ +$config['stgkz_blacklist_abmeldung'] = []; + +/** + * Blacklisted for unterbrechung anträge + * + * @var array An array of tbl_studiengang.studiengang_kz's + */ +$config['stgkz_blacklist_unterbrechung'] = []; + +/** + * Blacklisted for wiederholung anträge + * + * @var array An array of tbl_studiengang.studiengang_kz's + */ +$config['stgkz_blacklist_wiederholung'] = []; diff --git a/application/controllers/components/Antrag/Abmeldung.php b/application/controllers/components/Antrag/Abmeldung.php new file mode 100644 index 000000000..9130c1d7d --- /dev/null +++ b/application/controllers/components/Antrag/Abmeldung.php @@ -0,0 +1,201 @@ +load->library('AuthLib'); + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Retrieves data of the current studiengang for the current user + */ + + public function getDetailsForNewAntrag($prestudent_id) + { + if (!$this->antraglib->isEntitledToCreateAntragFor($prestudent_id, true)) { + $this->output->set_status_header(403); + return $this->outputJsonError('Forbidden'); + } + $result = $this->antraglib->getPrestudentAbmeldeBerechtigt($prestudent_id); + if (isError($result)) { + $this->output->set_status_header(500); + return $this->outputJsonError(getError($result)); + } + $result = $result->retval; + if (!$result) { + $this->output->set_status_header(403); + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_no_student')); + } + elseif ($result == -3) + { + $this->output->set_status_header(403); + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_stg_blacklist')); + } + elseif ($result == -1) + { + $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id, Studierendenantrag_model::TYP_ABMELDUNG); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $data = getData($result); + $data->canCancel = (boolean)$this->antraglib->isEntitledToCancelAntrag($data->studierendenantrag_id); + + return $this->outputJsonSuccess($data); + } + + $result = $this->antraglib->getDetailsForNewAntrag($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $this->outputJsonSuccess(getData($result)); + } + + public function createAntrag() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules('studiensemester', 'Studiensemester', 'required'); + $this->form_validation->set_rules('prestudent_id', 'Prestudent ID', 'required'); + $this->form_validation->set_rules('grund', 'Grund', 'required'); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $grund = $this->input->post('grund'); + $studiensemester = $this->input->post('studiensemester'); + $prestudent_id = $this->input->post('prestudent_id'); + + $result = $this->antraglib->getPrestudentAbmeldeBerechtigt($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(['db' => getError($result)]); + } + $result = $result->retval; + if (!$result) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_no_student')]); + } + elseif ($result == -3) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_stg_blacklist')]); + } + elseif ($result < 0) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_antrag_exists')]); + } + + $result = $this->antraglib->createAbmeldung($prestudent_id, $studiensemester, getAuthUID(), $grund); + if (isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + $result = $this->antraglib->getDetailsForAntrag(getData($result)); + if (!hasData($result)) + return $this->outputJsonSuccess(true); + + $data = getData($result); + $data->canCancel = (boolean)$this->antraglib->isEntitledToCancelAntrag($data->studierendenantrag_id); + + $this->outputJsonSuccess($data); + } + + public function cancelAntrag() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules('antrag_id', 'Antrag ID', 'required'); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $antrag_id = $this->input->post('antrag_id'); + if(!$this->antraglib->isEntitledToCancelAntrag($antrag_id)) + { + $this->output->set_status_header(403); + + return $this->outputJsonError('Forbidden'); + } + + $result = $this->antraglib->cancelAntrag($antrag_id, getAuthUID()); + if(isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + $result = $this->antraglib->getDetailsForAntrag($antrag_id); + + if (!hasData($result)) + return $this->outputJsonSuccess($antrag_id); + $this->outputJsonSuccess(getData($result)); + } + + public function getStudiengaengeAssistenz() + { + $this->load->library('PermissionLib'); + + $studiengaenge = $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag'); + + $result = $this->antraglib->getAbmeldeBerechtigtForStg($studiengaenge); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + $result = getData($result); + if (!$result) { + return $this->outputJsonSuccess([]); + } + + $sortedStudents = []; + foreach ($result as $item) { + if (!isset($sortedStudents[$item->studiengang_kz])) + $sortedStudents[$item->studiengang_kz] = [ + 'bezeichnung' => $item->bezeichnung, + 'orgform' => $item->orgform, + 'studenten' => [] + ]; + $sortedStudents[$item->studiengang_kz]['studenten'][] = [ + 'semester' => $item->semester, + 'prestudent_id' => $item->prestudent_id, + 'name' => trim($item->vorname . ' ' . $item->nachname), + 'vorname' => $item->vorname, + 'nachname' => $item->nachname, + 'studiensemester_kurzbz' => $item->studiensemester_kurzbz + ]; + } + + $this->outputJsonSuccess($sortedStudents); + } +} diff --git a/application/controllers/components/Antrag/Leitung.php b/application/controllers/components/Antrag/Leitung.php new file mode 100644 index 000000000..90a677c31 --- /dev/null +++ b/application/controllers/components/Antrag/Leitung.php @@ -0,0 +1,358 @@ +load->library('AuthLib'); + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + } + + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getAntraege($studiengang = null) + { + + if($studiengang) + $studiengaenge = [$studiengang]; + else { + $studiengaenge =$this->permissionlib->getSTG_isEntitledFor('student/antragfreigabe'); + if(!is_array($studiengaenge)) + $studiengaenge = []; + + + $stgsNeuanlage = $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag'); + if(!is_array($stgsNeuanlage)) + $stgsNeuanlage = []; + + $studiengaenge = array_unique(array_merge($studiengaenge, $stgsNeuanlage)); + } + + + $antraege = []; + if ($studiengaenge) { + $result = $this->StudierendenantragModel->loadForStudiengaenge($studiengaenge); + if (isError($result)) { + $this->output->set_status_header(500); + return $this->outputJson('Internal Server Error'); + } + $antraege = getData($result); + } + + $this->outputJson($antraege); + } + + public function reopenAntrag() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToReopenAntrag', + [ + 'isEntitledToReopenAntrag' => $this->p->t('studierendenantrag','error_no_right') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->reopenWiederholung($studierendenantrag_id, getAuthUID()); + + if (isError($result)) + return $this->outputJsonError(['studierendenantrag_id' => getError($result)]); + + $this->outputJsonSuccess($studierendenantrag_id); + } + + public function objectAntrag() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToObjectAntrag|callback_canBeObjected', + [ + 'isEntitledToObjectAntrag' => $this->p->t('studierendenantrag','error_no_right'), + 'canBeObjected' => $this->p->t('studierendenantrag','error_no_objection') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->objectAbmeldung($studierendenantrag_id, getAuthUID()); + + if (isError($result)) + return $this->outputJsonError(['studierendenantrag_id' => getError($result)]); + + $this->outputJsonSuccess($studierendenantrag_id); + } + + public function objectionDeny() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToObjectAntrag|callback_isObjected', + [ + 'isEntitledToObjectAntrag' => $this->p->t('studierendenantrag','error_no_right'), + 'isObjected' => $this->p->t('studierendenantrag','error_not_objected') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->approveAbmeldung([$studierendenantrag_id], getAuthUID()); + + if (isError($result)) + return $this->outputJsonError(['studierendenantrag_id' => getError($result)]); + + $this->outputJsonSuccess($studierendenantrag_id); + } + + public function objectionApprove() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToObjectAntrag|callback_isObjected', + [ + 'isEntitledToObjectAntrag' => $this->p->t('studierendenantrag','error_no_right'), + 'isObjected' => $this->p->t('studierendenantrag','error_not_objected') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->cancelAntrag($studierendenantrag_id, getAuthUID()); + + if (isError($result)) + return $this->outputJsonError(['studierendenantrag_id' => getError($result)]); + + $this->outputJsonSuccess($studierendenantrag_id); + } + + public function isEntitledToReopenAntrag($studierendenantrag_id) + { + return $this->antraglib->isEntitledToReopenAntrag($studierendenantrag_id); + } + + public function isEntitledToObjectAntrag($studierendenantrag_id) + { + return $this->antraglib->isEntitledToObjectAntrag($studierendenantrag_id); + } + + public function isEntitledToRejectAntrag($studierendenantrag_id) + { + return $this->antraglib->isEntitledToRejectAntrag($studierendenantrag_id); + } + + public function canBeObjected($studierendenantrag_id) + { + return $this->antraglib->hasStatus($studierendenantrag_id, Studierendenantragstatus_model::STATUS_APPROVED_STGL); + } + + public function isObjected($studierendenantrag_id) + { + return $this->antraglib->hasStatus($studierendenantrag_id, Studierendenantragstatus_model::STATUS_OBJECTED); + } + + + public function approveAbmeldung() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToApproveAntrag', + [ + 'isEntitledToApproveAntrag' => $this->p->t('studierendenantrag','error_no_right') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->approveAbmeldung([$studierendenantrag_id], getAuthUID()); + if (isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + return $this->outputJsonSuccess($studierendenantrag_id); + } + + public function approveUnterbrechung() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToApproveAntrag', + [ + 'isEntitledToApproveAntrag' => $this->p->t('studierendenantrag','error_no_right') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->approveUnterbrechung([$studierendenantrag_id], getAuthUID()); + if (isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + return $this->outputJsonSuccess($studierendenantrag_id); + } + + public function rejectUnterbrechung() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToRejectAntrag', + [ + 'isEntitledToRejectAntrag' => $this->p->t('studierendenantrag','error_no_right') + ] + ); + $this->form_validation->set_rules('grund', 'Grund', 'required'); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + $grund = $this->input->post('grund'); + + $result = $this->antraglib->rejectUnterbrechung([$studierendenantrag_id], getAuthUID(), $grund); + if (isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + return $this->outputJsonSuccess($studierendenantrag_id); + } + + public function approveWiederholung() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules( + 'studierendenantrag_id', + 'Studierenden Antrag', + 'required|callback_isEntitledToApproveAntrag', + [ + 'isEntitledToApproveAntrag' => $this->p->t('studierendenantrag','error_no_right') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $studierendenantrag_id = $this->input->post('studierendenantrag_id'); + + $result = $this->antraglib->approveWiederholung($studierendenantrag_id, getAuthUID()); + if (isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + return $this->outputJsonSuccess($studierendenantrag_id); + } + + public function isEntitledToApproveAntrag($studierendenantrag_id) + { + return $this->antraglib->isEntitledToApproveAntrag($studierendenantrag_id); + } + + public function getHistory($studierendenantrag_id) + { + if (!$this->antraglib->isEntitledToSeeHistoryForAntrag($studierendenantrag_id)) { + $this->output->set_status_header(403); + return $this->outputJson('Forbidden'); + } + + $result = $this->antraglib->getAntragHistory($studierendenantrag_id); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $this->outputJsonSuccess(getData($result) ?: []); + } +} diff --git a/application/controllers/components/Antrag/Unterbrechung.php b/application/controllers/components/Antrag/Unterbrechung.php new file mode 100644 index 000000000..78594fa65 --- /dev/null +++ b/application/controllers/components/Antrag/Unterbrechung.php @@ -0,0 +1,230 @@ +load->config('studierendenantrag'); + + // Libraries + $this->load->library('AuthLib'); + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + } + + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + public function getDetailsForNewAntrag($prestudent_id) + { + if (!$this->antraglib->isEntitledToCreateAntragFor($prestudent_id, false)) { + $this->output->set_status_header(403); + return $this->outputJsonError('Forbidden'); + } + $result = $this->antraglib->getPrestudentUnterbrechungsBerechtigt($prestudent_id); + if (isError($result)) { + $this->output->set_status_header(500); + return $this->outputJsonError(getError($result)); + } + $result = $result->retval; + if (!$result) { + $this->output->set_status_header(403); + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_no_student')); + } + elseif ($result == -1) + { + $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id, Studierendenantrag_model::TYP_UNTERBRECHUNG); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + return $this->outputJsonSuccess(getData($result)); + } + elseif ($result == -2) + { + $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $result = getData($result); + $this->output->set_status_header(400); + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_antrag_pending', ['typ' => $this->p->t('studierendenantrag','antrag_typ_' . $result->typ)])); + } + elseif ($result == -3) + { + $this->output->set_status_header(403); + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_stg_blacklist')); + } + $result = $this->antraglib->getDetailsForNewAntrag($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $data = getData($result); + + $data->studiensemester = $this->antraglib->getSemesterForUnterbrechung($data->studiengang_kz, $data->studiensemester_kurzbz, $data->semester); + + $this->outputJsonSuccess($data); + } + + public function getDetailsForAntrag($studierendenantrag_id) + { + if (!$this->antraglib->isEntitledToShowAntrag($studierendenantrag_id)) return show_404(); + + $result = $this->antraglib->getDetailsForAntrag($studierendenantrag_id); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $data = getData($result); + + if ($data->typ !== Studierendenantrag_model::TYP_UNTERBRECHUNG) + return show_404(); + + $this->outputJsonSuccess($data); + } + + public function createAntrag() + { + $this->load->library('form_validation'); + + $this->form_validation->set_rules('studiensemester', 'Studiensemester', 'required'); + $this->form_validation->set_rules('prestudent_id', 'Prestudent ID', 'required'); + $this->form_validation->set_rules('grund', 'Grund', 'required'); + $this->form_validation->set_rules( + 'datum_wiedereinstieg', + 'Datum Wiedereinstieg', + 'required|callback_isValidDate|callback_isDateInFuture', + [ + 'isValidDate' => $this->p->t('ui', 'error_invalid_date'), + 'isDateInFuture' => $this->p->t('ui', 'error_invalid_date') + ] + ); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $grund = $this->input->post('grund'); + $studiensemester = $this->input->post('studiensemester'); + $prestudent_id = $this->input->post('prestudent_id'); + $datum_wiedereinstieg = $this->input->post('datum_wiedereinstieg'); + $dms_id = null; + + $result = $this->antraglib->getPrestudentUnterbrechungsBerechtigt($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(['db' => getError($result)]); + } + $result = $result->retval; + if (!$result) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_no_student')]); + } + elseif ($result == -3) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_stg_blacklist')]); + } + elseif ($result < 0) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_antrag_exists')]); + } + + if(isset($_FILES['attachment']) && (!isset($_FILES['attachment']['error']) || $_FILES['attachment']['error'] != UPLOAD_ERR_NO_FILE)) + { + $this->load->library('DmsLib'); + + $dms = $this->config->item('unterbrechung_dms'); + if (!count(array_filter($dms, function ($v) { + return $v !== null; + }))) + $dms = ['kategorie_kurzbz' => 'Akte']; + $dms['version'] = 0; + + $allowed_filetypes = $this->config->item('unterbrechung_dms_filetypes') ?: ['*']; + $result = $this->dmslib->upload($dms, 'attachment', $allowed_filetypes); + if(isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + $dms_id = getData($result)['dms_id']; + } + + $result = $this->antraglib->createUnterbrechung($prestudent_id, $studiensemester, getAuthUID(), $grund, $datum_wiedereinstieg, $dms_id); + if(isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + $antragId = getData($result); + $result = $this->antraglib->getDetailsForAntrag($antragId); + + if(!hasData($result)) + return $this->outputJsonSuccess($antragId); + $this->outputJsonSuccess(getData($result)); + } + + public function cancelAntrag() + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules('antrag_id', 'Antrag ID', 'required'); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $antrag_id = $this->input->post('antrag_id'); + + $result = $this->antraglib->cancelAntrag($antrag_id, getAuthUID()); + if (isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + $result = $this->antraglib->getDetailsForAntrag($antrag_id); + + if (!hasData($result)) + return $this->outputJsonSuccess($antrag_id); + $this->outputJsonSuccess(getData($result)); + } + + public function isValidDate($date) + { + try { + new DateTime($date); + } catch (Exception $e) { + return false; + } + return true; + } + + public function isDateInFuture($date) + { + return new DateTime() < new DateTime($date); + } +} diff --git a/application/controllers/components/Antrag/Wiederholung.php b/application/controllers/components/Antrag/Wiederholung.php new file mode 100644 index 000000000..7b5d8f887 --- /dev/null +++ b/application/controllers/components/Antrag/Wiederholung.php @@ -0,0 +1,369 @@ +load->config('studierendenantrag'); + + // Libraries + $this->load->library('AuthLib'); + $this->load->library('PermissionLib'); + $this->load->library('AntragLib'); + + $requiredPermissions = [ + 'saveLvs' => ['student/studierendenantrag:w'], + 'getLvsAsRdf' => ['student/studierendenantrag:r', 'student/noten:r'], + 'moveLvsToZeugnis' => ['student/studierendenantrag:w', 'student/noten:w'] + ]; + + if (isset($requiredPermissions[$this->router->method])) { + if (!$this->permissionlib->isEntitled($requiredPermissions, $this->router->method)) { + $this->output->set_status_header(REST_Controller::HTTP_FORBIDDEN); + $this->outputJson('Forbidden'); + exit; + } + } + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + } + + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Retrieves data of the current studiengang for the current user + */ + + public function getDetailsForNewAntrag($prestudent_id) + { + if (!$this->antraglib->isEntitledToCreateAntragFor($prestudent_id, false)) { + $this->output->set_status_header(REST_Controller::HTTP_FORBIDDEN); + return $this->outputJsonError('Forbidden'); + } + $result = $this->antraglib->getPrestudentWiederholungsBerechtigt($prestudent_id); + if (isError($result)) { + $this->output->set_status_header(REST_Controller::HTTP_INTERNAL_SERVER_ERROR); + return $this->outputJsonError(getError($result)); + } + $result = $result->retval; + if (!$result) { + // TODO(chris): ERROR Message + $this->output->set_status_header(REST_Controller::HTTP_FORBIDDEN); + return $this->outputJsonError($this->p->t('studierendenantrag','error_no_student_no_failed_exam')); + } + elseif ($result == -1) + { + $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id, Studierendenantrag_model::TYP_WIEDERHOLUNG); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $data = getData($result); + + $result = $this->antraglib->getFailedExamForPrestudent($prestudent_id); + // NOTE(chris): error handling for this function should already happenden in antraglib->getPrestudentWiederholungsBerechtigt() + $pruefungsdata = current(getData($result)); + + $data->studiensemester_kurzbz = $pruefungsdata->studiensemester_kurzbz; + $data->lvbezeichnung = $pruefungsdata->lvbezeichnung; + $data->pruefungsdatum = $pruefungsdata->datum; + + return $this->outputJsonSuccess($data); + } + elseif ($result == -2) + { + $result = $this->antraglib->getDetailsForLastAntrag($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $result = getData($result); + $this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST); + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_antrag_pending', ['typ' => $this->p->t('studierendenantrag','antrag_typ_' . $result->typ)])); + } + elseif ($result == -3) + { + $this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST); + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_stg_blacklist')); + } + + $result = $this->antraglib->getDetailsForNewAntrag($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $data = getData($result); + + $result = $this->antraglib->getFailedExamForPrestudent($prestudent_id); + // NOTE(chris): error handling for this function should already happenden in antraglib->getPrestudentWiederholungsBerechtigt() + $pruefungsdata = current(getData($result)); + + $data->studiensemester_kurzbz = $pruefungsdata->studiensemester_kurzbz; + $data->lvbezeichnung = $pruefungsdata->lvbezeichnung; + $data->pruefungsdatum = $pruefungsdata->datum; + + $this->outputJsonSuccess($data); + } + + public function createAntrag() + { + $this->createAntragWithStatus(true); + } + + public function cancelAntrag() + { + $this->createAntragWithStatus(false); + } + + protected function createAntragWithStatus($repeat) + { + $this->load->library('form_validation'); + + $_POST = json_decode($this->input->raw_input_stream, true); + + $this->form_validation->set_rules('prestudent_id', 'Prestudent ID', 'required'); + $this->form_validation->set_rules('studiensemester', 'Studiensemester', 'required'); + + if ($this->form_validation->run() == false) + { + return $this->outputJsonError($this->form_validation->error_array()); + } + + $prestudent_id = $this->input->post('prestudent_id'); + $studiensemester = $this->input->post('studiensemester'); + + $result = $this->antraglib->getPrestudentWiederholungsBerechtigt($prestudent_id); + if (isError($result)) { + return $this->outputJsonError(['db' => getError($result)]); + } + $result = $result->retval; + if (!$result) + { + // TODO(chris): ERROR Message + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_no_student')]); + } + elseif ($result == -2) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_antrag_exists')]); + } + elseif ($result == -3) + { + return $this->outputJsonError(['db' => $this->p->t('studierendenantrag', 'error_stg_blacklist')]); + } + + $result = $this->antraglib->createWiederholung($prestudent_id, $studiensemester, getAuthUID(), $repeat); + if(isError($result)) + { + return $this->outputJsonError(['db' => getError($result)]); + } + + $antragId = getData($result); + $result = $this->antraglib->getDetailsForAntrag($antragId); + + if(!hasData($result)) + return $this->outputJsonSuccess(true); + + $data = getData($result); + + $result = $this->antraglib->getFailedExamForPrestudent($prestudent_id); + // NOTE(chris): error handling for this function should already happenden in antraglib->getPrestudentWiederholungsBerechtigt() + $pruefungsdata = current(getData($result)); + + $data->studiensemester_kurzbz = $pruefungsdata->studiensemester_kurzbz; + $data->lvbezeichnung = $pruefungsdata->lvbezeichnung; + $data->pruefungsdatum = $pruefungsdata->datum; + + $this->outputJsonSuccess($data); + } + + + public function getLvs($antrag_id) + { + $result = $this->antraglib->getLvsForAntrag($antrag_id); + if (isError($result)) { + $error = getError($result); + if ($error == 'Forbidden') + $this->output->set_status_header(REST_Controller::HTTP_FORBIDDEN); + return $this->outputJsonError(getError($result)); + } + $lvs = getData($result); + + $this->outputJsonSuccess($lvs); + } + + public function saveLvs() + { + $result = $this->getPostJSON(); + $antragsLvs = array_merge($result->forbiddenLvs, $result->mandatoryLvs); + + $insert = array_map(function ($lv) { + return [ + 'studierendenantrag_id' => $lv->studierendenantrag_id, + 'lehrveranstaltung_id' => $lv->lehrveranstaltung_id, + 'note' => $lv->zugelassen ? ($lv->zugelassen == 1 ? 0 : $this->config->item('wiederholung_note_angerechnet')) : $this->config->item('wiederholung_note_nicht_zugelassen'), + 'anmerkung' => $lv->anmerkung, + 'insertvon' => getAuthUID(), + 'studiensemester_kurzbz' => $lv->studiensemester_kurzbz + ]; + }, $antragsLvs); + + $antrag_ids = array_unique(array_map(function ($lv) { + return $lv['studierendenantrag_id']; + }, $insert)); + + foreach ($antrag_ids as $antrag_id) { + $result = $this->StudierendenantragModel->loadIdAndStatusWhere([ + 'studierendenantrag_id' => $antrag_id + ]); + if (isError($result)) + return $this->outputJsonError(getError($result)); + if (!hasData($result)) + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_no_antrag_found', ['id' => $antrag_id])); + $antrag = current(getData($result)); + if ($antrag->status != Studierendenantragstatus_model::STATUS_CREATED && $antrag->status != Studierendenantragstatus_model::STATUS_LVSASSIGNED) + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_antrag_locked')); + } + + if(!$antragsLvs) + return $this->outputJsonError($this->p->t('studierendenantrag', 'error_no_lv')); + + $result = $this->antraglib->saveLvs($insert); + + if (isError($result)) + return $this->outputJsonError(getError($result)); + + $this->outputJsonSuccess(getData($result)); + } + + public function getLvsAsRdf($prestudent_id) + { + // header für no cache + $this->output->set_header("Cache-Control: no-cache"); + $this->output->set_header("Cache-Control: post-check=0, pre-check=0", false); + $this->output->set_header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); + $this->output->set_header("Pragma: no-cache"); + $this->output->set_header("Content-type: application/xhtml+xml"); + + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + $sem_akt = $this->variablelib->getVar('semester_aktuell'); + + + $result = $this->antraglib->getLvsForPrestudent($prestudent_id, $sem_akt); + if (isError($result)) { + return $this->outputJsonError(getError($result)); + } + + $lvs = getData($result) ?: []; + $rdf_url = 'http://www.technikum-wien.at/antragnote'; + + $this->load->view('lehre/Antrag/Wiederholung/getLvs.rdf.php', [ + 'url' => $rdf_url, + 'lvs' => $lvs + ]); + } + + public function moveLvsToZeugnis() + { + $anzahl = $this->input->post('anzahl'); + $student_uid = $this->input->post('student_uid'); + $this->load->model('education/Studierendenantraglehrveranstaltung_model', 'StudierendenantraglehrveranstaltungModel'); + $this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); + + $errormsg = array(); + + for($i=0; $i<$anzahl; $i++) + { + $id = $this->input->post('studierendenantrag_lehrveranstaltung_id_' . $i); + $result =$this->StudierendenantraglehrveranstaltungModel->load($id); + if(isError($result)) + { + $errormsg[] = getError($result); + } + elseif(!hasData($result)) + { + $errormsg[] = $this->p->t('studierendenantrag', 'error_no_lv_in_application'); + } + else + { + $antragLv = getData($result)[0]; + $result= $this->ZeugnisnoteModel->load([ + 'lehrveranstaltung_id'=> $antragLv->lehrveranstaltung_id, + 'student_uid'=> $student_uid, + 'studiensemester_kurzbz' => $antragLv->studiensemester_kurzbz + ]); + if(isError($result)) + { + $errormsg[] = getError($result); + } + else + { + if (hasData($result)) + { + $result = $this->ZeugnisnoteModel->update( + [ + 'lehrveranstaltung_id'=> $antragLv->lehrveranstaltung_id, + 'student_uid'=> $student_uid, + 'studiensemester_kurzbz' => $antragLv->studiensemester_kurzbz + ], + [ + 'note'=> $antragLv->note, + 'uebernahmedatum' => date('c'), + 'benotungsdatum' => $antragLv->insertamum, + 'updateamum' => date('c'), + 'bemerkung'=>$antragLv->anmerkung, + 'updatevon'=>getAuthUID() + ] + ); + } + else + { + $result = $this->ZeugnisnoteModel->insert([ + 'lehrveranstaltung_id'=> $antragLv->lehrveranstaltung_id, + 'student_uid'=> $student_uid, + 'studiensemester_kurzbz' => $antragLv->studiensemester_kurzbz, + 'note'=> $antragLv->note, + 'uebernahmedatum' => date('c'), + 'benotungsdatum' => $antragLv->insertamum, + 'insertamum' => date('c'), + 'bemerkung'=>$antragLv->anmerkung, + 'insertvon'=>getAuthUID() + ]); + } + if(isError($result)) + { + $errormsg[] = getError($result); + } + } + } + } + + if($errormsg) + $return = false; + else + $return = true; + + $this->load->view('lehre/Antrag/Wiederholung/moveLvs.rdf.php', [ + 'return' => $return, + 'errormsg' => $errormsg + ]); + } +} diff --git a/application/controllers/components/Phrasen.php b/application/controllers/components/Phrasen.php new file mode 100644 index 000000000..52de719f7 --- /dev/null +++ b/application/controllers/components/Phrasen.php @@ -0,0 +1,23 @@ +load->library('PhrasesLib', [$module], 'pj'); + $this->outputJsonSuccess(json_decode($this->pj->getJSON())); + } + +} diff --git a/application/controllers/jobs/AntragJob.php b/application/controllers/jobs/AntragJob.php new file mode 100644 index 000000000..6b5b018e6 --- /dev/null +++ b/application/controllers/jobs/AntragJob.php @@ -0,0 +1,406 @@ +load->config('studierendenantrag'); + + // Loads SanchoHelper + $this->load->helper('hlp_sancho_helper'); + + //Load Model + $this->load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); + $this->load->model('education/Studierendenantragstatus_model', 'StudierendenantragstatusModel'); + $this->load->model('education/Pruefung_model', 'PruefungModel'); + $this->load->model('person/Kontakt_model', 'KontaktModel'); + } + + /** + * Send reminder to Assistant for Wiedereinstieg Unterbrecher + * + */ + public function sendReminderWiedereinstieg() + { + $now = new DateTime(); + $modifier = $this->config->item('unterbrechung_job_remind_wiedereinstieg_date_modifier'); + if (!$modifier) + return $this->logError('Konnte Job nicht starten: Config "unterbrechung_job_remind_wiedereinstieg_date_modifiers" nicht gesetzt'); + + $end = new DateTime(); + $end->modify($modifier); + + $this->logInfo(sprintf( + 'Start Job sendReminderWiedereinstieg (Wiedereinstieg zwischen %s - %s)', + $now->format('Y-m-d'), + $end->format('Y-m-d') + )); + + $result = $this->StudierendenantragModel->getAntraegeWhereWiedereinstiegBetween($now, $end); + + if(isError($result)) + { + $this->logError(getError($result)); + $this->logInfo('Ende Job sendReminderWiedereinstieg'); + return; + } + + $antraege = getData($result) ?: []; + $count = 0; + foreach ($antraege as $antrag) + { + $datum = new DateTime($antrag->datum_wiedereinstieg); + $data = array( + 'prestudent' => $antrag->prestudent_id, + 'name' => trim($antrag->vorname . ' '. $antrag->nachname), + 'datum_wiedereinstieg' => $datum->format('d.m.Y') + ); + + if(sendSanchoMail('Sancho_Mail_Antrag_U_Reminder', $data, $antrag->email, 'Reminder: Unterbrechung Wiedereinstieg')) + { + $count++; + $this->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag->studierendenantrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_REMINDERSENT, + 'insertvon' => 'AntragJob' + ]); + } + } + $this->logInfo($count . ' Reminder gesendet - Ende Job sendReminderWiedereinstieg'); + } + + /** + * Set Wiederholer after deadline to Abbrecher + * + */ + public function handleWiederholerDeadline() + { + $this->logInfo('Start Job handleWiederholerDeadline'); + + $this->load->library('PrestudentLib'); + + $insertvon = $this->config->item('antrag_job_systemuser'); + if (!$insertvon) { + $this->logError('Config "antrag_job_systemuser" nicht gesetzt'); + $this->logInfo('Ende Job handleWiederholerDeadline'); + return; + } + + $modifier_deadline = $this->config->item('wiederholung_job_deadline_date_modifier'); + if (!$modifier_deadline) { + $this->logError('Config "wiederholung_job_deadline_date_modifier" nicht gesetzt'); + $this->logInfo('Ende Job handleWiederholerDeadline'); + return; + } + + $dateDeadline = new DateTime(); + $dateDeadline->sub(DateInterval::createFromDateString($modifier_deadline)); + + $result = $this->PruefungModel->getAllPrestudentsWhereCommitteeExamFailed( + [ + null, + Studierendenantragstatus_model::STATUS_REQUESTSENT_1, + Studierendenantragstatus_model::STATUS_REQUESTSENT_2 + ], + $dateDeadline, + null + ); + + if(isError($result)) + { + $this->logError(getError($result)); + } + else + { + $prestudents = getData($result) ?: []; + $count = 0; + + $prestudents = $this->prestudentsGetUnique($prestudents); + + foreach ($prestudents as $prestudent) + { + // TODO(chris): DEBUG REMOVE! + if ($prestudent->studiensemester_kurzbz == 'WS2021') + var_dump([$prestudent->prestudent_id, $prestudent->studiensemester_kurzbz, $prestudent->lvbezeichnung]); + $result = success(); + // TODO(chris): find out how to filter old/wrong datasets!!! + #$result = $this->prestudentlib->setAbbrecher($prestudent->prestudent_id, $prestudent->studiensemester_kurzbz, $insertvon); + if (isError($result)) + $this->logError(getError($result)); + else + $count++; + } + $this->logInfo($count . " Students set to Abbrecher"); + } + + $this->logInfo('Ende Job handleWiederholerDeadline'); + + } + + /** + * Set Abmeldungen after deadline to Abbrecher + * + */ + public function handleAbmeldungenStglDeadline() + { + $this->logInfo('Start Job handleAbmeldungenStglDeadline'); + + $this->load->library('AntragLib'); + + $insertvon = $this->config->item('antrag_job_systemuser'); + if (!$insertvon) { + $this->logError('Config "antrag_job_systemuser" nicht gesetzt'); + $this->logInfo('Ende Job handleAbmeldungenStglDeadline'); + return; + } + + $modifier_deadline = $this->config->item('abmeldung_job_deadline_date_modifier'); + if (!$modifier_deadline) { + $this->logError('Config "abmeldung_job_deadline_date_modifier" nicht gesetzt'); + $this->logInfo('Ende Job handleAbmeldungenStglDeadline'); + return; + } + + $dateDeadline = new DateTime(); + $dateDeadline->sub(DateInterval::createFromDateString($modifier_deadline)); + + $result = $this->StudierendenantragModel->getWithLastStatusWhere( + [ + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED_STGL, + 's.insertamum <=' => $dateDeadline->format('c') + ] + ); + + if(isError($result)) + { + $this->logError(getError($result)); + } + else + { + $antraege = getData($result) ?: []; + $count = 0; + + foreach ($antraege as $antrag) + { + $result = $this->antraglib->approveAbmeldung([$antrag->studierendenantrag_id], $insertvon); + + if (isError($result)) + $this->logError(getError($result)); + else + $count++; + } + $this->logInfo($count . " Students set to Abbrecher"); + } + $this->logInfo('Ende Job handleAbmeldungenStglDeadline'); + } + + /** + * Send Request to Student do Decide between Wiederholung and Verzicht + * + */ + public function sendAufforderungWiederholer() + { + $this->logInfo('Start Job sendAufforderungWiederholer'); + + $modifier_request_1 = $this->config->item('wiederholung_job_request_1_date_modifier'); + $modifier_request_2 = $this->config->item('wiederholung_job_request_2_date_modifier'); + $modifier_deadline = $this->config->item('wiederholung_job_deadline_date_modifier'); + + if ($modifier_deadline) + { + $dateDeadline = new DateTime(); + $dateDeadline->sub(DateInterval::createFromDateString($modifier_deadline)); + } + else + $dateDeadline = null; + + //first request + if ($modifier_request_1) + $this->sendReminder( + 'Request1', + null, + Studierendenantragstatus_model::STATUS_REQUESTSENT_1, + $dateDeadline, + $modifier_request_1, + $modifier_deadline, + 'Aufforderung: Bekanntgabe Wiederholung' + ); + else + $this->logError('Config "wiederholung_job_request_1_date_modifier" nicht gesetzt'); + + //second request + if ($modifier_request_2) + $this->sendReminder( + 'Request2', + Studierendenantragstatus_model::STATUS_REQUESTSENT_1, + Studierendenantragstatus_model::STATUS_REQUESTSENT_2, + $dateDeadline, + $modifier_request_2, + $modifier_deadline, + 'Reminder Aufforderung: Bekanntgabe Wiederholung' + ); + else + $this->logError('Config "wiederholung_job_request_2_date_modifier" nicht gesetzt'); + + $this->logInfo('Ende Job sendAufforderungWiederholer'); + } + + protected function prestudentsGetUnique($prestudents) { + $result = []; + foreach ($prestudents as $prestudent) { + if (!isset($result[$prestudent->prestudent_id])) + $result[$prestudent->prestudent_id] = $prestudent; + else { + if ($result[$prestudent->prestudent_id]->datum > $prestudent->datum) + $result[$prestudent->prestudent_id] = $prestudent; + } + } + return $result; + } + + protected function sendReminder($name, $status_from, $status_to, $deadline, $date_modifier, $modifier_deadline, $subject) + { + $this->logInfo('Start Job sendAufforderungWiederholer ' . $name); + + $dateStichtag = new DateTime(); + $dateStichtag->sub(DateInterval::createFromDateString($date_modifier)); + + $result = $this->PruefungModel->getAllPrestudentsWhereCommitteeExamFailed($status_from, $dateStichtag, $deadline); + + if(isError($result)) + { + $this->logError(getError($result)); + } + else + { + $prestudents = getData($result) ?: []; + $count = 0; + + $prestudents = $this->prestudentsGetUnique($prestudents); + + foreach ($prestudents as $prestudent) + { + $stg_kz = $prestudent->studiengang_kz; + if (in_array($stg_kz, $this->config->item('stgkz_blacklist_wiederholung'))) + continue; + $url = site_url('lehre/Studierendenantrag/wiederholung/' . $prestudent->prestudent_id); + $email = $this->KontaktModel->getZustellKontakt($prestudent->person_id, ['email']); + if (isError($email)) { + $this->logError(getError($email)); + } else { + $email = getData($email); + + if (!$email) { + $this->logError('No email contact found for person_id: ' . $prestudent->person_id); + } + else + { + $email = current($email)->kontakt; + $fristende = new DateTime($prestudent->datum); + $fristende->add(DateInterval::createFromDateString($modifier_deadline)); + + $dataMail = array( + 'name'=> trim($prestudent->vorname . ' '. $prestudent->nachname), + 'pers_kz'=> $prestudent->matrikelnr, + 'studiengang' => $prestudent->bezeichnung, + 'lvbezeichnung' => $prestudent->lvbezeichnung, + 'datum_kp' => $prestudent->datum, + 'studiensemester'=> $prestudent->studiensemester_kurzbz, + 'orgform'=> $prestudent->orgform, + 'url' => $url, + 'fristablauf' => $fristende->format('d.m.Y') + ); + if(sendSanchoMail('Sancho_Mail_Antrag_W_' . $name, $dataMail, $email, $subject)) + { + $antrag_id = null; + $result = $this->StudierendenantragModel->loadWhere([ + 'prestudent_id' => $prestudent->prestudent_id, + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG + ]); + if (isError($result)) + $this->logError(getError($result)); + elseif (hasData($result)) + $antrag_id = current(getData($result) ?: []) -> studierendenantrag_id; + if ($antrag_id == null) + { + $result = $this->StudierendenantragModel->insert([ + 'prestudent_id' => $prestudent->prestudent_id, + 'studiensemester_kurzbz'=> $prestudent->studiensemester_kurzbz, + 'datum' => date('c'), + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, + 'insertvon' => 'AntragJob' + ]); + if (isError($result)) + $this->logError(getError($result)); + else + $antrag_id = getData($result); + } + if ($antrag_id) + { + $result = $this->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => $status_to, + 'insertvon' => 'AntragJob' + ]); + if (isError($result)) + $this->logError(getError($result)); + } + $count++; + } + } + } + } + $this->logInfo($count . " Mails '" . $subject . "' sent"); + } + $this->logInfo('Ende Job sendAufforderungWiederholer ' . $name); + } + + + + // TODO(chris): REMOVE DEBUG! + + /** + * Writes a cronjob info log + */ + protected function logInfo($response, $parameters = null) + { + echo $response . "\n"; + } + + /** + * Writes a cronjob debug log + */ + protected function logDebug($response, $parameters = null) + { + echo $response . "\n"; + } + + /** + * Writes a cronjob warning log + */ + protected function logWarning($response, $parameters = null) + { + echo $response . "\n"; + } + + /** + * Writes a cronjob error log + */ + protected function logError($response, $parameters = null) + { + echo $response . "\n"; + } + + +} diff --git a/application/controllers/lehre/Antrag/Attachment.php b/application/controllers/lehre/Antrag/Attachment.php new file mode 100644 index 000000000..073a03df0 --- /dev/null +++ b/application/controllers/lehre/Antrag/Attachment.php @@ -0,0 +1,82 @@ +load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); + + $this->load->library('DmsLib'); + $this->load->library('AuthLib'); + $this->load->library('PermissionLib'); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + + /** + * @param integer $dms_id + * + * @return void + */ + public function show($dms_id) + { + $result = $this->StudierendenantragModel->loadWhere(['dms_id' => $dms_id]); + if (!getData($result)) + return show_404(); + + if (!$this->permissionlib->isBerechtigt('student/antragfreigabe')) + { + $isSamePerson = false; + $antraege = getData($result); + $this->load->model('crm/Prestudent_model', 'PrestudentModel'); + foreach ($antraege as $antrag) + { + $prestudent = $this->PrestudentModel->load($antrag->prestudent_id); + if(hasData($prestudent)) + { + if(current(getData($prestudent))->person_id == getAuthPersonId()) + { + $isSamePerson = true; + break; + } + } + } + + if ($isSamePerson == false) + { + $this->output->set_status_header(REST_Controller::HTTP_FORBIDDEN); // set the HTTP header as unauthorized + + $this->load->library('EPrintfLib'); // loads the EPrintfLib to format the output + + // Prints the main error message + $this->eprintflib->printError('You are not allowed to access to this content'); + // Prints the called controller name + $this->eprintflib->printInfo('Controller name: '.$this->router->class); + // Prints the called controller method name + $this->eprintflib->printInfo('Method name: '.$this->router->method); + // Prints the required permissions needed to access to this method + $this->eprintflib->printInfo('Required permissions: student/antragfreigabe'); + + return show_error('You are not entitled to read this document'); + } + } + + $result = $this->dmslib->download($dms_id); + if (isError($result)) + return show_error(getError($result)); + + $this->outputFile(getData($result)); + } +} diff --git a/application/controllers/lehre/Antrag/Wiederholung.php b/application/controllers/lehre/Antrag/Wiederholung.php new file mode 100644 index 000000000..5b22641f7 --- /dev/null +++ b/application/controllers/lehre/Antrag/Wiederholung.php @@ -0,0 +1,49 @@ + 'student/studierendenantrag:w' + ]); + + $this->load->library('AntragLib'); + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + public function assistenz($antrag_id, $frame = false) + { + + $result = $this->antraglib->getDetailsForAntrag($antrag_id); + + if (isError($result)) + return show_error(getError($result)); + + if (!hasData($result)) + return show_404(); + + $this->load->view('lehre/Antrag/Wiederholung/Student', [ + 'antrag_id' => $antrag_id, + 'antrag' => getData($result), + 'frame' => $frame + ]); + } +} diff --git a/application/controllers/lehre/Studierendenantrag.php b/application/controllers/lehre/Studierendenantrag.php new file mode 100644 index 000000000..16b47d592 --- /dev/null +++ b/application/controllers/lehre/Studierendenantrag.php @@ -0,0 +1,208 @@ +load->library('AuthLib'); + $this->load->library('AntragLib'); + + // Load Models + $this->load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); + + // Load language phrases + $this->loadPhrases([ + 'studierendenantrag' + ]); + + if (strtolower($this->router->method) === 'leitung') + $this->_isAllowed([ + 'leitung' => ['student/studierendenantrag:r', 'student/antragfreigabe:r'] + ]); + } + + // ----------------------------------------------------------------------------------------------------------------- + // Public methods + public function index() + { + $dataAntrag = $this->StudierendenantragModel->loadForPerson(getAuthPersonId()); + if (isError($dataAntrag)) + return show_error(getError($dataAntrag)); + $dataAntrag = (getData($dataAntrag) ? : []); + $prestudentenArr = array(); + + foreach ($dataAntrag as $antrag) + { + if (!isset($prestudentenArr[$antrag->prestudent_id])) + { + $prestudentenArr[$antrag->prestudent_id] = array( + 'allowedNewTypes' => array(), + 'antraege'=> array(), + 'bezeichnungStg' => $antrag->bezeichnung, + 'bezeichnungOrgform' => $antrag->orgform + ); + $result = $this->antraglib->getPrestudentAbmeldeBerechtigt($antrag->prestudent_id); + if (getData($result) == 1) + $prestudentenArr[$antrag->prestudent_id]['allowedNewTypes'][] = 'Abmeldung'; + + $result = $this->antraglib->getPrestudentUnterbrechungsBerechtigt($antrag->prestudent_id); + if (getData($result) == 1) + $prestudentenArr[$antrag->prestudent_id]['allowedNewTypes'][] = 'Unterbrechung'; + + $result = $this->antraglib->getPrestudentWiederholungsBerechtigt($antrag->prestudent_id); + if (getData($result) == 1) + $prestudentenArr[$antrag->prestudent_id]['allowedNewTypes'][] = 'Wiederholung'; + } + if ($antrag->studierendenantrag_id == null) + continue; + $prestudentenArr[$antrag->prestudent_id]['antraege'][] = $antrag; + } + + $this->load->view('lehre/Antrag/Student/List', [ + 'antraege' => $prestudentenArr + ]); + } + + public function leitung() + { + $studiengaenge = $this->permissionlib->getSTG_isEntitledFor('student/antragfreigabe'); + $stgsNeuanlage = $this->permissionlib->getSTG_isEntitledFor('student/studierendenantrag'); + + $stgL = []; + if ($studiengaenge) { + $result = $this->StudierendenantragModel->loadForStudiengaenge($studiengaenge); + if (isError($result)) + return show_error(getError($result)); + $antraege = getData($result) ?: []; + + foreach ($antraege as $antrag) { + if (!isset($stgL[$antrag->studiengang_kz])) { + $stgL[$antrag->studiengang_kz] = new stdClass(); + $stgL[$antrag->studiengang_kz]->bezeichnung = $antrag->bezeichnung; + $stgL[$antrag->studiengang_kz]->orgform = $antrag->orgform; + $stgL[$antrag->studiengang_kz]->studiengang_kz = $antrag->studiengang_kz; + } + } + } + + $stgA = []; + if ($stgsNeuanlage) { + $result = $this->StudierendenantragModel->loadForStudiengaenge($stgsNeuanlage); + if (isError($result)) + return show_error(getError($result)); + $antraege = getData($result) ?: []; + + foreach ($antraege as $antrag) { + if (!isset($stgA[$antrag->studiengang_kz])) { + $stgA[$antrag->studiengang_kz] = new stdClass(); + $stgA[$antrag->studiengang_kz]->bezeichnung = $antrag->bezeichnung; + $stgA[$antrag->studiengang_kz]->orgform = $antrag->orgform; + $stgA[$antrag->studiengang_kz]->studiengang_kz = $antrag->studiengang_kz; + } + } + } + + $this->load->view('lehre/Antrag/Leitung/List', [ + 'stgA' => $stgA, + 'stgL' => $stgL + ]); + } + + public function abmeldung($prestudent_id, $studierendenantrag_id = null) + { + $this->load->view('lehre/Antrag/Create', [ + 'prestudent_id' => $prestudent_id, + 'studierendenantrag_id' => $studierendenantrag_id, + 'antrag_type' => 'Abmeldung' + ]); + } + + public function unterbrechung($prestudent_id, $studierendenantrag_id = null) + { + $this->load->view('lehre/Antrag/Create', [ + 'prestudent_id' => $prestudent_id, + 'studierendenantrag_id' => $studierendenantrag_id, + 'antrag_type' => 'Unterbrechung' + ]); + } + + public function wiederholung($prestudent_id, $studierendenantrag_id = null) + { + $this->load->view('lehre/Antrag/Create', [ + 'prestudent_id' => $prestudent_id, + 'studierendenantrag_id' => $studierendenantrag_id, + 'antrag_type' => 'Wiederholung' + ]); + } + + /** + * Checks if the caller is allowed to access to this content with the given permissions + * If it is not allowed will set the HTTP header with code 401 + * Wrapper for permissionlib->isEntitled + */ + private function _isAllowed($requiredPermissions) + { + // Loads permission lib + $this->load->library('PermissionLib'); + + // Checks if this user is entitled to access to this content + if (!$this->permissionlib->isEntitled($requiredPermissions, $this->router->method)) + { + $this->output->set_status_header(REST_Controller::HTTP_UNAUTHORIZED); // set the HTTP header as unauthorized + + $this->load->library('EPrintfLib'); // loads the EPrintfLib to format the output + + // Prints the main error message + $this->eprintflib->printError('You are not allowed to access to this content'); + // Prints the called controller name + $this->eprintflib->printInfo('Controller name: '.$this->router->class); + // Prints the called controller method name + $this->eprintflib->printInfo('Method name: '.$this->router->method); + // Prints the required permissions needed to access to this method + $this->eprintflib->printInfo('Required permissions: '.$this->_rpsToString($requiredPermissions, $this->router->method)); + + exit; // immediately terminate the execution + } + } + + /** + * Converts an array of permissions to a string that contains them as a comma separated list + * Ex: ", , " + */ + private function _rpsToString($requiredPermissions, $method) + { + $strRequiredPermissions = ''; // string that contains all the required permissions needed to access to this method + + if (isset($requiredPermissions[$method])) // if the called method is present in the permissions array + { + // If it is NOT then convert it into an array + $rpsMethod = $requiredPermissions[$method]; + if (!is_array($rpsMethod)) + { + $rpsMethod = array($rpsMethod); + } + + // Copy all the permissions into $strRequiredPermissions separated by a comma + for ($i = 0; $i < count($rpsMethod); $i++) + { + $strRequiredPermissions .= $rpsMethod[$i].', '; + } + + $strRequiredPermissions = rtrim($strRequiredPermissions, ', '); + } + + return $strRequiredPermissions; + } +} diff --git a/application/helpers/hlp_common_helper.php b/application/helpers/hlp_common_helper.php index 913d29b54..707d055ea 100644 --- a/application/helpers/hlp_common_helper.php +++ b/application/helpers/hlp_common_helper.php @@ -18,6 +18,8 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); +use \DateTime as DateTime; + // ------------------------------------------------------------------------ // Collection of utility functions for general purpose // ------------------------------------------------------------------------ @@ -405,3 +407,17 @@ function findResource($path, $resource, $subdir = false, $extraDir = null) return null; } +/** + * check if String can be converted to a date + */ +function isValidDate($dateString) +{ + try + { + return (new DateTime($dateString)) !== false; + } + catch(Exception $e) + { + return false; + } +} diff --git a/application/libraries/AntragLib.php b/application/libraries/AntragLib.php new file mode 100644 index 000000000..38504772c --- /dev/null +++ b/application/libraries/AntragLib.php @@ -0,0 +1,1476 @@ +_ci =& get_instance(); + + // Configs + $this->_ci->load->config('studierendenantrag'); + + // Models + $this->_ci->load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); + $this->_ci->load->model('education/Studierendenantragstatus_model', 'StudierendenantragstatusModel'); + $this->_ci->load->model('education/Studierendenantraglehrveranstaltung_model', 'StudierendenantraglehrveranstaltungModel'); + $this->_ci->load->model('organisation/Studiengang_model', 'StudiengangModel'); + $this->_ci->load->model('crm/Prestudent_model', 'PrestudentModel'); + $this->_ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + $this->_ci->load->model('person/Person_model', 'PersonModel'); + $this->_ci->load->model('education/Pruefung_model', 'PruefungModel'); + + // Helper + $this->_ci->load->helper('hlp_sancho_helper'); + + // Libraries + $this->_ci->load->library('PermissionLib'); + $this->_ci->load->library('PrestudentLib'); + } + + /** + * @param integer $antrag_id + * @param string $insertvon + * + * @return stdClass + */ + public function cancelAntrag($antrag_id, $insertvon) + { + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CANCELLED, + 'insertvon' => $insertvon + ]); + + return $result; + } + + /** + * NOTE(chris): permissions & verification must be handled outside + * + * @param integer $prestudent_id + * @param string $studiensemester_kurzbz + * @param string $insertvon + * @param string $grund + * + * @return stdClass + */ + public function createAbmeldung($prestudent_id, $studiensemester_kurzbz, $insertvon, $grund) + { + $result = $this->_ci->PrestudentModel->load($prestudent_id); + if (isError($result)) + return $result; + if(!hasData($result)) + return error("Kein Prestudent gefunden: " . $prestudent_id); + + $prestudent = getData($result)[0]; + if($prestudent->person_id == getAuthPersonId()) + $status = Studierendenantragstatus_model::STATUS_CREATED; + else + $status = Studierendenantragstatus_model::STATUS_CREATED_STGL; + + $result = $this->_ci->StudierendenantragModel->insert([ + 'prestudent_id' => $prestudent_id, + 'studiensemester_kurzbz'=> $studiensemester_kurzbz, + 'datum' => date('c'), + 'typ' => Studierendenantrag_model::TYP_ABMELDUNG, + 'insertvon' => $insertvon, + 'grund' => $grund + ]); + + if (isError($result)) + return $result; + + $antrag_id = getData($result); + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => $status, + 'insertvon' => $insertvon + ]); + + if (isError($result)) + return $result; + + return success($antrag_id); + } + + /** + * @param array $studierendenantrag_ids + * @param string $insertvon + * + * @return stdClass + */ + public function approveAbmeldung($studierendenantrag_ids, $insertvon) + { + $errors = []; + foreach ($studierendenantrag_ids as $studierendenantrag_id) { + + $result = $this->_ci->StudierendenantragModel->loadIdAndStatusWhere(['studierendenantrag_id' => $studierendenantrag_id]); + if (isError($result)) + { + $errors[] = getError($result); + continue; + } + if(!hasData($result)) + { + $errors[] = 'no Antrag found for '. $studierendenantrag_id; + continue; + } + $status = getData($result)[0]; + + if($status->status == Studierendenantragstatus_model::STATUS_CREATED_STGL) + { + $status_approved = Studierendenantragstatus_model::STATUS_APPROVED_STGL; + } + else + $status_approved = Studierendenantragstatus_model::STATUS_APPROVED; + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $studierendenantrag_id, + 'studierendenantrag_statustyp_kurzbz' => $status_approved, + 'insertvon' => $insertvon + ]); + if (isError($result)) + $errors[] = getError($result); + else { + $resultPrestudent = $this->_ci->StudierendenantragModel->load($studierendenantrag_id); + if (isError($resultPrestudent)) + { + $errors[] = getError($resultPrestudent); + continue; + } + if ($status_approved == Studierendenantragstatus_model::STATUS_APPROVED) + { + $antrag = getData($resultPrestudent)[0]; + + $resultPrestudentStatus = $this->_ci->PrestudentstatusModel->getLastStatusWithStgEmail($antrag->prestudent_id); + if (isError($resultPrestudentStatus)) + $errors[] = getError($resultPrestudentStatus); + + else { + $prestudent_status = getData($resultPrestudentStatus)[0]; + + $vorlage ='Sancho_Mail_Antrag_A_Approve'; + $subject = 'Abmeldung freigegeben'; + + $result = $this->_ci->prestudentlib->setAbbrecher($antrag->prestudent_id, $antrag->studiensemester_kurzbz, $insertvon); + if (isError($result)) + { + $errors[] = getError($result); + return $errors; + } + + $result = $this->_ci->PersonModel->loadPrestudent($antrag->prestudent_id); + $data = [ + 'nameStudent' => 'Student*in' + ]; + if (hasData($result)) { + $person = current(getData($result)); + $data['nameStudent'] = trim($person->vorname . ' ' . $person->nachname); + } + // NOTE(chris): Sancho mail + sendSanchoMail($vorlage, $data, $prestudent_status->email, $subject); + } + } + } + } + + if (count($errors)) + return error(implode(',', $errors)); + + return success(); + } + + /** + * NOTE(chris): permissions & verification must be handled outside + * + * @param integer $prestudent_id + * @param string $studiensemester_kurzbz + * @param string $insertvon + * @param string $grund + * @param string $datum_wiedereinstieg + * + * @return stdClass + */ + public function createUnterbrechung($prestudent_id, $studiensemester_kurzbz, $insertvon, $grund, $datum_wiedereinstieg, $dms_id) + { + $datum_wiedereinstieg = new DateTime($datum_wiedereinstieg); + $datum_wiedereinstieg = $datum_wiedereinstieg->format("Y-m-d"); + $result = $this->_ci->StudierendenantragModel->insert([ + 'prestudent_id' => $prestudent_id, + 'studiensemester_kurzbz'=> $studiensemester_kurzbz, + 'datum' => date('c'), + 'typ' => Studierendenantrag_model::TYP_UNTERBRECHUNG, + 'insertvon' => $insertvon, + 'grund' => $grund, + 'datum_wiedereinstieg' => $datum_wiedereinstieg, + 'dms_id' => $dms_id + ]); + + if (isError($result)) + return $result; + + $antrag_id = getData($result); + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_CREATED, + 'insertvon' => $insertvon + ]); + + if (isError($result)) + return $result; + + return success($antrag_id); + } + + + /** + * @param array $studierendenantrag_ids + * @param string $insertvon + * + * @return stdClass + */ + public function approveUnterbrechung($studierendenantrag_ids, $insertvon) + { + $this->_ci->load->model('person/Kontakt_model', 'KontaktModel'); + + $errors = []; + + foreach ($studierendenantrag_ids as $studierendenantrag_id) + { + $data = $this->getDataForUnterbrechung($studierendenantrag_id); + + if (isError($data)) { + $error_msg = getError($data); + if (is_array($error_msg) && isset($error_msg['message'])) + $error_msg = $error_msg['message']; + + $errors['failed_' . $studierendenantrag_id] = 'Could not approve Unterbrechung for studierendenantrag_id: ' . + $studierendenantrag_id . + '
Details:
' . + $error_msg; + } else { + $data = getData($data); + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $studierendenantrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED, + 'insertvon' => $insertvon + ]); + if (isError($result)) + { + $errors['failed_' . $studierendenantrag_id] = 'Could not approve Unterbrechung for studierendenantrag_id: ' . + $studierendenantrag_id . + '
Details:
' . + getError($result)['message']; + } + else + { + $resultAntrag = $this->_ci->StudierendenantragModel->load($studierendenantrag_id); + if (isError($resultAntrag)) + return $resultAntrag; + $resultAntrag = getData($resultAntrag); + if (!$resultAntrag) + return error('No antrag found with id: ' . $studierendenantrag_id); + $resultAntrag = current($resultAntrag); + + // Prestudentstatus und Unterbrechungsfolgeaktionen setzen + $result = $this->_ci->prestudentlib->setUnterbrecher($resultAntrag->prestudent_id, $resultAntrag->studiensemester_kurzbz, $studierendenantrag_id); + + //Mail + $subject = 'Unterbrechung freigegeben'; + $mail = []; + + if (isset($data['errors']['person_id'])) + { + //send assistenz mit id + $errors[] = 'Mail to student not sent and student name not found
Details:
' . $data['errors']['person_id']; + $mail['ass'] = 'Student/in (' . $data['antrag']->prestudent_id . ')'; + } + elseif (isset($data['errors']['email'])) + { + if (isset($data['errors']['person'])) + { + //send assistenz mit id + $errors[] = 'Mail to student not sent and student name not found
Details:
' . + $data['errors']['email'] . + '
' . + $data['errors']['person']; + $mail['ass'] = 'Student/in (' . $data['antrag']->prestudent_id . ')'; + } + else + { + //send assistenz mit name + $errors[] = 'Mail to student not sent
Details:
' . $data['errors']['email']; + $mail['ass'] = trim($data['person']->vorname . ' ' . $data['person']->nachname); + } + } + else + { + if (isset($data['errors']['person'])) + { + //send assistenz mit id & student mit "Student/in" + $errors[] = 'Student name not found
Details:
' . $data['errors']['person']; + $mail['ass'] = 'Student/in (' . $data['antrag']->prestudent_id . ')'; + $mail['stu'] = 'Student/in'; + } + else + { + //send normal + $mail['ass'] = $mail['stu'] = trim($data['person']->vorname . ' ' . $data['person']->nachname); + } + } + $mailVorlage = 'Sancho_Mail_Antrag_U_Approve'; + if ($data['studienbeitrag']) + $mailVorlage .= '_SB'; + if (isset($mail['ass'])) { + // NOTE(chris): Sancho mail + if (!sendSanchoMail( + $mailVorlage, + [ + 'name' => $mail['ass'] + ], + $data['prestudent_status']->email, + $subject + )) { + $errors[] = 'Failed to send email to ' . $data['prestudent_status']->email; + } + } + if (isset($mail['stu'])) { + // NOTE(chris): Sancho mail + if (!sendSanchoMail( + $mailVorlage, + [ + 'name' => $mail['stu'] + ], + $data['email'], + $subject + )) { + $errors[] = 'Failed to send email to ' . $data['email']; + } + } + } + } + } + + if (count($errors)) + return error($errors); + + return success(); + } + + /** + * @param array $studierendenantrag_ids + * @param string $insertvon + * @param string $grund + * + * @return stdClass + */ + public function rejectUnterbrechung($studierendenantrag_ids, $insertvon, $grund) + { + $this->_ci->load->model('person/Kontakt_model', 'KontaktModel'); + + $errors = []; + + foreach ($studierendenantrag_ids as $studierendenantrag_id) { + $data = $this->getDataForUnterbrechung($studierendenantrag_id); + + if (isError($data)) { + $error_msg = getError($data); + if (is_array($error_msg) && isset($error_msg['message'])) + $error_msg = $error_msg['message']; + + $errors['failed_' . $studierendenantrag_id] = 'Could not reject Unterbrechung for studierendenantrag_id: ' . + $studierendenantrag_id . + '
Details:
' . + $error_msg; + } else { + $data = getData($data); + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $studierendenantrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_REJECTED, + 'insertvon' => $insertvon, + 'grund' => $grund + ]); + if (isError($result)) { + $errors['failed_' . $studierendenantrag_id] = 'Could not reject Unterbrechung for studierendenantrag_id: ' . + $studierendenantrag_id . + '
Details:
' . + getError($result)['message']; + } else { + $name = ''; + + if (isset($data['errors']['person_id']) || isset($data['errors']['email'])) { + $error_msg = []; + if (isset($data['errors']['person_id'])) + $error_msg[] = $data['errors']['person_id']; + if (isset($data['errors']['email'])) + $error_msg[] = $data['errors']['email']; + $error_msg = 'Mail to student not sent
Details:
' . implode('
', $error_msg); + $errors[] = $error_msg; + } else { + if (isset($data['errors']['person'])) { + //send student mit "Student/in" + $errors[] = 'Student name not found
Details:
' . $data['errors']['person']; + $name = 'Student/in'; + } else { + //send normal + $name = trim($data['person']->vorname . ' ' . $data['person']->nachname); + } + } + if ($name) + // NOTE(chris): Sancho mail + if (!sendSanchoMail( + 'Sancho_Mail_Antrag_U_Reject', + [ + 'name' => $name, + 'grund' => $grund, + 'abmeldungLink' => site_url('lehre/Studierendenantrag/abmeldung/' . $data['prestudent_status']->prestudent_id) + ], + $data['email'], + 'Unterbrechung abgelehnt' + )) + $errors[] = 'Failed to send email to ' . $data['email']; + } + } + } + + if (count($errors)) + return error($errors); + + return success(); + } + + /** + * @param integer $studierendenantrag_id + * + * @return array + */ + private function getDataForUnterbrechung($studierendenantrag_id) + { + $result = []; + $errors = []; + + $res = $this->_ci->StudierendenantragModel->load($studierendenantrag_id); + if (isError($res)) + return $res; + + $res = getData($res); + if (!$res) + return error('No Studierendenantrag found for studierendenantrag_id: ' . $studierendenantrag_id); + + $result['antrag'] = $antrag = current($res); + + + $res = $this->_ci->PrestudentstatusModel->getLastStatusWithStgEmail($antrag->prestudent_id); + if (isError($res)) + return $res; + + $res = getData($res); + if (!$res) + return error('No Prestudentstatus found for prestudent_id: ' . $antrag->prestudent_id); + + $result['prestudent_status'] = current($res); + + + $res = $this->_ci->PrestudentModel->load($antrag->prestudent_id); + + if (isError($res)) { + $errors['person_id'] = getError($res); + } else { + $res = getData($res); + if (!$res) { + $errors['person_id'] = 'No Prestudent found for prestudent_id: ' . $antrag->prestudent_id; + } else { + $person_id = current($res)->person_id; + + $res = $this->_ci->PersonModel->load($person_id); + if (isError($res)) { + $errors['person'] = getError($res); + } else { + $res = getData($res); + if (!$res) { + $errors['person'] = 'No Person found for person_id: ' . $person_id; + } else { + $result['person'] = current($res); + } + } + + $res = $this->_ci->KontaktModel->getZustellKontakt($person_id, ['email']); + if (isError($res)) { + $errors['email'] = getError($res); + } else { + $res = getData($res); + + if (!$res) { + $errors['email'] = 'No email contact found for person_id: ' . $person_id; + } else { + $result['email'] = current($res)->kontakt; + } + } + } + } + + $result['studienbeitrag'] = false; + if (!isset($errors['person_id'])) { + $date_target = new DateTime( + $this->_ci->config->item('frist_rueckzahlung_studiengebuer_' . substr($result['antrag']->studiensemester_kurzbz, 0, 2)) . + substr($result['antrag']->studiensemester_kurzbz, 2) + ); + $date_created = new DateTime($result['antrag']->datum); + if ($date_created < $date_target) { + $this->_ci->load->model('crm/Konto_model', 'KontoModel'); + $result['studienbeitrag'] = $this->_ci->KontoModel->checkStudienbeitragFromPerson($person_id, $result['antrag']->studiensemester_kurzbz); + } + } + + $result['errors'] = $errors; + + return success($result); + } + + /** + * @param integer $prestudent_id + * @param string $studiensemester_kurzbz + * @param string $insertvon + * @param boolean $repeat + * + * @return stdClass + */ + public function createWiederholung($prestudent_id, $studiensemester_kurzbz, $insertvon, $repeat) + { + $result = $this->_ci->StudierendenantragModel->loadIdAndStatusWhere([ + 'prestudent_id' => $prestudent_id, + 'studiensemester_kurzbz'=> $studiensemester_kurzbz, + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG + ]); + + $antrag_id = null; + if (hasData($result)) { + $antrag = current(getData($result)); + if ($antrag->status == Studierendenantragstatus_model::STATUS_REOPENED || + $antrag->status == Studierendenantragstatus_model::STATUS_REQUESTSENT_1 || + $antrag->status == Studierendenantragstatus_model::STATUS_REQUESTSENT_2) + { + $antrag_id = $antrag->studierendenantrag_id; + } + else + { + return error('Antrag bereits vorhanden!'); + } + } + + if ($antrag_id === null) { + $result = $this->_ci->StudierendenantragModel->insert([ + 'prestudent_id' => $prestudent_id, + 'studiensemester_kurzbz'=> $studiensemester_kurzbz, + 'datum' => date('c'), + 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, + 'insertvon' => $insertvon + ]); + + if (isError($result)) + return $result; + + $antrag_id = getData($result); + } + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => $repeat ? Studierendenantragstatus_model::STATUS_CREATED : Studierendenantragstatus_model::STATUS_PASS, + 'insertvon' => $insertvon + ]); + + if ($repeat) { + $res = $this->_ci->PrestudentstatusModel->getLastStatusWithStgEmail($prestudent_id); + if (isError($res)) + return $res; + $res = getData($res); + if (!$res) + return error('No Prestudentstatus found for prestudent_id: ' . $prestudent_id); + + $prestudent_status = current($res); + $email = $prestudent_status->email; + // NOTE(chris): Sancho mail + sendSanchoMail( + 'Sancho_Mail_Antrag_W_New', + [ + 'antrag_id' => $antrag_id, + 'lvzuweisungLink' => site_url('lehre/Antrag/Wiederholung/assistenz/' . $antrag_id) + ], + $email, + 'Neue*r Wiederholer*in' + ); + } + + if (isError($result)) + return $result; + + return success($antrag_id); + } + + /** + * @param integer $studierendenantrag_id + * @param string $insertvon + * + * @return stdClass + */ + public function reopenWiederholung($studierendenantrag_id, $insertvon) + { + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $studierendenantrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_REOPENED, + 'insertvon' => $insertvon + ]); + return $result; + } + + /** + * @param integer $studierendenantrag_id + * @param string $objectedvon + * + * @return stdClass + */ + public function objectAbmeldung($studierendenantrag_id, $objectedvon) + { + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $studierendenantrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_OBJECTED, + 'insertvon' => $objectedvon + ]); + return $result; + } + + public function getWiederholungsAntraege($status) + { + $studiengaenge = $this->_ci->permissionlib->getSTG_isEntitledFor('student/studierendenantrag'); + $result = $this->_ci->StudierendenantragModel->loadForStudiengaenge( + $studiengaenge, + Studierendenantrag_model::TYP_WIEDERHOLUNG, + $status + ); + if (!getData($result)) + return $result; + $result = getData($result); + $grouped = []; + + foreach ($result as $item) { + if (!isset($grouped[$item->studiengang_kz])) { + $grouped[$item->studiengang_kz] = [ + 'bezeichnung' => $item->bezeichnung, + 'bezeichnung_mehrsprachig' => $item->bezeichnung_mehrsprachig, + 'antraege' => [] + ]; + } + $grouped[$item->studiengang_kz]['antraege'][] = $item; + } + + return success($grouped); + } + + public function getLvsForAntrag($antrag_id) + { + $this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel'); + + $result = $this->_ci->StudierendenantragModel->load($antrag_id); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No antrag found with id: ' . $antrag_id); + $antrag = current($result); + + + $result = $this->_ci->StudierendenantragModel->getStgAndSem($antrag_id); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No studiengang and ausbildungssemester found for antrag with id: ' . $antrag_id); + $result = current($result); + $studiengang_kz = $result->studiengang_kz; + $orgform_kurzbz = $result->orgform_kurzbz; + $ausbildungssemester = $result->ausbildungssemester; + + // NOTE(chris): check permission + $allowedStgs = $this->_ci->permissionlib->getSTG_isEntitledFor('student/studierendenantrag') ?: []; + if (!in_array($studiengang_kz, $allowedStgs)) { + $allowedStgs = $this->_ci->permissionlib->getSTG_isEntitledFor('student/antragfreigabe') ?: []; + if (!in_array($studiengang_kz, $allowedStgs)) { + if(!$this->isOwnAntrag($antrag_id)) + return error('Forbidden'); + } + } + + + $result = $this->_ci->StudiensemesterModel->getNextFrom($antrag->studiensemester_kurzbz); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No studiensemster found after: ' . $antrag->studiensemester_kurzbz); + $semA = current($result)->studiensemester_kurzbz; + + $result = $this->_ci->StudiensemesterModel->getNextFrom($semA); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No studiensemster found after: ' . $semA); + $semB = current($result)->studiensemester_kurzbz; + + $result = $this->_ci->StudierendenantraglehrveranstaltungModel->loadWhere(['studierendenantrag_id' => $antrag_id]); + if (isError($result)) + return $result; + $result = getData($result) ?: []; + + $lvszugewiesen = array(); + foreach ($result as $lv) + { + $lvszugewiesen[$lv->lehrveranstaltung_id] = $lv; + } + + $result = $this->getLvsByStgStsemAndSem( + $studiengang_kz, + $orgform_kurzbz, + $semA, + $ausbildungssemester + 1, + $antrag->prestudent_id, + $antrag->studiensemester_kurzbz + ); + if (isError($result)) + return $result; + $lvsA = getData($result) ?: []; + foreach($lvsA as $lv) + { + if (isset($lvszugewiesen[$lv->lehrveranstaltung_id]) && + ($lvszugewiesen[$lv->lehrveranstaltung_id]->note == $this->_ci->config->item('wiederholung_note_nicht_zugelassen'))) + { + $lv->antrag_zugelassen = true; + $lv->antrag_anmerkung = $lvszugewiesen[$lv->lehrveranstaltung_id]->anmerkung; + } + } + + $result = $this->getLvsByStgStsemAndSem( + $studiengang_kz, + $orgform_kurzbz, + $semB, + $ausbildungssemester, + $antrag->prestudent_id, + $antrag->studiensemester_kurzbz + ); + if (isError($result)) + return $result; + $lvsB = getData($result) ?: []; + foreach($lvsB as $lv) + { + if(isset($lvszugewiesen[$lv->lehrveranstaltung_id]) && ($lvszugewiesen[$lv->lehrveranstaltung_id]->note == 0)) + { + $lv->antrag_anmerkung = $lvszugewiesen[$lv->lehrveranstaltung_id]->anmerkung; + $lv->antrag_zugelassen = true; + } + // TODO(manu): eventuelle Änderungen taggen + } + + return success([ + '1' . $semA => $lvsA ?: [], + '2' . $semB => $lvsB ?: [] + ]); + } + + public function getLvsByStgStsemAndSem( + $studiengang_kz, + $orgform_kurzbz, + $studiensemester_kurzbz, + $ausbildungssemester, + $prestudent_id, + $note_stsem + ) { + $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel'); + + $result = $this->_ci->StudienplanModel->getStudienplaeneBySemester( + $studiengang_kz, + $studiensemester_kurzbz, + $ausbildungssemester, + $orgform_kurzbz + ); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No studienplan found for stg: ' . + $studiengang_kz . + ', studiensemester: ' . + $studiensemester_kurzbz . + ', ausbildungssemester: ' . + ($ausbildungssemester)); + if (count($result) > 1) + return error('Multiple studienplaene found for stg: ' . + $studiengang_kz . + ', studiensemester: ' . + $studiensemester_kurzbz . + ', ausbildungssemester: ' . + $ausbildungssemester); + $studienplan = current($result); + + return $this->_ci->StudienplanModel->getStudienplanLehrveranstaltungForPrestudent( + $studienplan->studienplan_id, + $ausbildungssemester, + $prestudent_id, + $note_stsem + ); + } + + /** + * Checks if a prestudent can submit an Antrag for Abmeldung + * + * @param integer $prestudent_id + * + * @return \stdClass on success retval 0 means not a student; retval 1 means Berechtigt; retval -1 means has already an Antrag pending; retval -2 means other Antrag pending; retval -3 means in blacklist stg + */ + public function getPrestudentAbmeldeBerechtigt($prestudent_id) + { + $result = $this->_ci->PrestudentModel->load($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(0); + $result = current(getData($result)); + $stg_kz = $result->studiengang_kz; + if (in_array($stg_kz, $this->_ci->config->item('stgkz_blacklist_abmeldung'))) + return success(-3); + + $result = $this->_ci->PrestudentstatusModel->getLastStatus($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(0); + $result = current(getData($result)); + $datumStatus = $result->datum; + + if (!in_array($result->status_kurzbz, $this->_ci->config->item('antrag_prestudentstatus_whitelist'))) { + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere(['prestudent_id' => $prestudent_id, 'typ' => Studierendenantrag_model::TYP_ABMELDUNG, 'campus.get_status_studierendenantrag(studierendenantrag_id)' => Studierendenantragstatus_model::STATUS_APPROVED]); + if (isError($result)) + return $result; + if (hasData($result)) + return success(-1); + + return success(0); + } + + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere(['prestudent_id' => $prestudent_id]); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(1); + $result= getData($result); + foreach ($result as $antrag) + { + if ($antrag->typ == Studierendenantrag_model::TYP_ABMELDUNG) + { + if ($antrag->status == Studierendenantragstatus_model::STATUS_CREATED || $antrag->status == Studierendenantragstatus_model::STATUS_CREATED_STGL) + return success(-1); + elseif ($antrag->status == Studierendenantragstatus_model::STATUS_APPROVED && $antrag->datum > $datumStatus) + return success(-1); + } + if ($antrag->typ == Studierendenantrag_model::TYP_WIEDERHOLUNG) + { + if($antrag->status == Studierendenantragstatus_model::STATUS_PASS) + return success(-2); + } + } + + return success(1); + } + + /** + * Checks if a prestudent can submit an Antrag for Unterbrechung + * + * @param integer $prestudent_id + * @param string $studiensemester_kurzbz (optional) + * + * @return \stdClass on success retval 0 means not a student; retval 1 means Berechtigt; retval -1 means has already an Antrag pending; retval -2 means other Antrag pending; retval -3 means in blacklist stg + */ + public function getPrestudentUnterbrechungsBerechtigt($prestudent_id, $studiensemester_kurzbz = null) + { + $result = $this->_ci->PrestudentModel->load($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(0); + $result = current(getData($result)); + $stg_kz = $result->studiengang_kz; + if (in_array($stg_kz, $this->_ci->config->item('stgkz_blacklist_unterbrechung'))) + return success(-3); + + $result = $this->_ci->PrestudentstatusModel->getLastStatus($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(0); + $result = current(getData($result)); + $datumStatus = $result->datum; + if (!in_array($result->status_kurzbz, $this->_ci->config->item('antrag_prestudentstatus_whitelist'))) { + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere(['prestudent_id' => $prestudent_id, 'typ' => Studierendenantrag_model::TYP_UNTERBRECHUNG, 'campus.get_status_studierendenantrag(studierendenantrag_id)' => Studierendenantragstatus_model::STATUS_APPROVED]); + if (isError($result)) + return $result; + if (hasData($result)) + return success(-1); + + return success(0); + } + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere(['prestudent_id' => $prestudent_id]); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(1); + $result= getData($result); + foreach ($result as $antrag) + { + if ($antrag->typ == Studierendenantrag_model::TYP_ABMELDUNG) + { + if($antrag->status == Studierendenantragstatus_model::STATUS_CREATED || $antrag->status == Studierendenantragstatus_model::STATUS_CREATED_STGL) + return success(-2); + elseif($antrag->status == Studierendenantragstatus_model::STATUS_APPROVED && $antrag->datum > $datumStatus) + return success(-2); + } + if ($studiensemester_kurzbz && $antrag->typ == Studierendenantrag_model::TYP_UNTERBRECHUNG) + { + // NOTE(chris): check if this is an old or canceled one + if ($antrag->studiensemester_kurzbz == $studiensemester_kurzbz && $antrag->status != Studierendenantragstatus_model::STATUS_CANCELLED) + return success(-1); + } + if ($antrag->typ == Studierendenantrag_model::TYP_WIEDERHOLUNG) + { + if($antrag->status == Studierendenantragstatus_model::STATUS_PASS) + return success(-2); + } + } + + return success(1); + } + + /** + * Checks if a prestudent can submit an Antrag for Wiederholung + * + * @param integer $prestudent_id + * + * @return \stdClass on success retval 0 means not a student; retval 1 means Berechtigt; retval -1 means has already an Antrag pending; retval -2 means other Antrag pending; retval -3 means in blacklist stg + */ + public function getPrestudentWiederholungsBerechtigt($prestudent_id) + { + $result = $this->_ci->PrestudentModel->load($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(0); + $result = current(getData($result)); + $stg_kz = $result->studiengang_kz; + if (in_array($stg_kz, $this->_ci->config->item('stgkz_blacklist_wiederholung'))) + return success(-3); + + $result = $this->getFailedExamForPrestudent($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(0); + + $result = $this->_ci->PrestudentstatusModel->getLastStatus($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(0); + + $result = current(getData($result)); + $datumStatus = $result->datum; + if (!in_array($result->status_kurzbz, $this->_ci->config->item('antrag_prestudentstatus_whitelist'))) { + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere(['prestudent_id' => $prestudent_id, 'typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, 'campus.get_status_studierendenantrag(studierendenantrag_id)' => Studierendenantragstatus_model::STATUS_APPROVED]); + if (isError($result)) + return $result; + if (hasData($result)) + return success(-1); + + return success(0); + } + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere(['prestudent_id' => $prestudent_id]); + if (isError($result)) + return $result; + if (!hasData($result)) + return success(1); + $result= getData($result); + foreach ($result as $antrag) + { + if ($antrag->typ == Studierendenantrag_model::TYP_ABMELDUNG) + { + if($antrag->status == Studierendenantragstatus_model::STATUS_CREATED || $antrag->status == Studierendenantragstatus_model::STATUS_CREATED_STGL) + return success(-2); + elseif($antrag->status == Studierendenantragstatus_model::STATUS_APPROVED && $antrag->datum > $datumStatus) + return success(-2); + } + if ($antrag->typ == Studierendenantrag_model::TYP_WIEDERHOLUNG) + { + return success(-1); + } + } + + return success(1); + } + + /** + * Gets details for a new Antrag + * + * @param integer $prestudent_id + * + * @return \stdClass + */ + public function getDetailsForNewAntrag($prestudent_id) + { + $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return error('No Studentstatus found for: ' . $prestudent_id); + $result = current(getData($result)); + return success($result); + } + + public function getDetailsForLastAntrag($prestudent_id, $typ = null) + { + $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return error('No Studentstatus found for: ' . $prestudent_id); + $resultDetails = current(getData($result)); + + $where = [ + 'prestudent_id' => $prestudent_id + ]; + if ($typ) + $where['typ'] = $typ; + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere($where); + if (isError($result)) + return $result; + + $antraege = getData($result) ?: []; + $resultAntrag = null; + foreach ($antraege as $antrag) { + if ($antrag->status != Studierendenantragstatus_model::STATUS_CANCELLED) { + $resultAntrag = $antrag; + break; + } + } + if (!$resultAntrag) + return error('No Antrag ' . trim(($typ ?: '') . ' ') . 'found for: ' . $prestudent_id); + + $resultDetails->status = $resultAntrag->status; + $resultDetails->statustyp = $resultAntrag->statustyp; + $resultDetails->grund = $resultAntrag->grund; + $resultDetails->studierendenantrag_id = $resultAntrag->studierendenantrag_id; + $resultDetails->typ = $resultAntrag->typ; + + return success($resultDetails); + } + + public function getDetailsForAntrag($studierendenantrag_id) + { + $where = [ + 'studierendenantrag_id' => $studierendenantrag_id + ]; + + $result = $this->_ci->StudierendenantragModel->loadWithStatusWhere($where); + if (isError($result)) + return $result; + + if (!hasData($result)) + return error("No Antrag found with id: " . $studierendenantrag_id); + $resultAntrag = current(getData($result)); + + $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails($resultAntrag->prestudent_id, $resultAntrag->studiensemester_kurzbz); + if (isError($result)) + return $result; + if (!hasData($result)) { + $result = $this->_ci->PrestudentstatusModel->loadLastWithStgDetails($resultAntrag->prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return error('No Studentstatus found for: ' . $resultAntrag->prestudent_id); + } + $resultDetails = current(getData($result)); + + $resultDetails->status = $resultAntrag->status; + $resultDetails->statustyp = $resultAntrag->statustyp; + $resultDetails->grund = $resultAntrag->grund; + $resultDetails->studierendenantrag_id = $resultAntrag->studierendenantrag_id; + $resultDetails->typ = $resultAntrag->typ; + $resultDetails->dms_id = $resultAntrag->dms_id; + $resultDetails->datum_wiedereinstieg = $resultAntrag->datum_wiedereinstieg; + + return success($resultDetails); + } + + public function getSemesterForUnterbrechung($studiengang_kz, $studiensemester_kurzbz, $ausbildungssemester) + { + $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel'); + $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $result = $this->_ci->StudienplanModel->getStudienplaeneBySemester($studiengang_kz, $studiensemester_kurzbz, $ausbildungssemester); + if (!hasData($result)) + return []; + + $studienplaene = getData($result); + $studienplan_ids = array_map(function ($studienplan) { + return $studienplan->studienplan_id; + }, $studienplaene); + + $result = $this->_ci->StudiensemesterModel->getFollowingSemester($studienplan_ids, $studiensemester_kurzbz, $ausbildungssemester); + if (!hasData($result)) + return []; + + $stsems = getData($result); + + $result = $this->_ci->StudiensemesterModel->loadWhere(); + if (!hasData($result)) + return []; + $result = getData($result); + usort($result, function($a, $b) { + return $a->start > $b->start ? 1 : -1; + }); + foreach ($stsems as $stsem) { + $stsem->wiedereinstieg = array_filter($result, function ($sem) use ($stsem) { + return $sem->start > $stsem->ende; + }); + } + + return $stsems; + } + + public function getAbmeldeBerechtigtForStg($studiengaenge) + { + $blacklist = $this->_ci->config->item('stgkz_blacklist_abmeldung'); + $studiengaenge = array_diff($studiengaenge, $blacklist); + return $this->_ci->StudiengangModel->getAktivePrestudenten($studiengaenge, [ + Studierendenantrag_model::TYP_ABMELDUNG + ]); + } + + public function getFailedExamForPrestudent($prestudent_id) + { + return $this->_ci->PruefungModel->loadWhereCommitteeExamFailedForPrestudent($prestudent_id); + } + + public function saveLvs($lvArray) + { + $result = $this->_ci->StudierendenantraglehrveranstaltungModel->deleteWhere(['studierendenantrag_id' => $lvArray[0]['studierendenantrag_id']]); + if (isError($result)) + return $result; + + $result = $this->_ci->StudierendenantraglehrveranstaltungModel->insertBatch($lvArray); + if (isError($result)) + return $result; + + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $lvArray[0]['studierendenantrag_id'], + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_LVSASSIGNED, + 'insertvon' => $lvArray[0]['insertvon'] + ]); + if (isError($result)) + return $result; + + $antrag_status_id = getData($result); + $result = $this->_ci->StudierendenantragstatusModel->loadWithTyp($antrag_status_id); + + return $result; + } + + public function approveWiederholung($antrag_id, $insertvon) + { + $result = $this->_ci->StudierendenantragstatusModel->insert([ + 'studierendenantrag_id' => $antrag_id, + 'studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED, + 'insertvon' => $insertvon + ]); + + if (isError($result)) { + return $result; + } + + $result = $this->_ci->StudierendenantragModel->getStgEmail($antrag_id); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No studiengang email found for: ' . $antrag_id); + + $email = current($result)->email; + + $result = $this->_ci->StudierendenantragModel->getStgAndSem($antrag_id); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No studiengang found for: ' . $antrag_id); + + $result = current($result); + $studiengang_kz = $result->studiengang_kz; + $semester = $result->ausbildungssemester; + + $result = $this->_ci->StudiengangModel->load($studiengang_kz); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No studiengang found for: ' . $antrag_id); + + $stg = current($result); + + $result = $this->_ci->StudierendenantragModel->load($antrag_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return error('No Antrag found for: ' . $antrag_id); + $result = current(getData($result)); + $prestudent_id = $result->prestudent_id; + + $result = $this->_ci->PersonModel->loadPrestudent($prestudent_id); + if (isError($result)) + return $result; + if (!hasData($result)) + return error('No Person found for prestudent: ' . $prestudent_id); + $result = current(getData($result)); + $student = trim($result->vorname . ' ' . $result->nachname); + + $result = $this->_ci->PersonModel->getFullName($insertvon); + if (isError($result)) + return $result; + $mitarbeiter = $insertvon; + if (hasData($result)) { + $mitarbeiter = getData($result); + } + + + // NOTE(chris): Sancho mail + if (!sendSanchoMail( + 'Sancho_Mail_Antrag_W_Approve', + [ + 'antrag_id' => $antrag_id, + 'stg' => $stg->bezeichnung, + 'sem' => $semester, + 'student' => $student, + 'mitarbeiter' => $mitarbeiter + ], + $email, + 'Wiederholung von Stgleitung freigegeben' + )) + return error('Email konnte nicht versendet werden an '. $email); + + return success(); + } + + public function getAntragHistory($antrag_id) + { + $result = $this->_ci->StudierendenantragstatusModel->loadWithTypWhere([ + 'studierendenantrag_id' => $antrag_id + ]); + return $result; + } + + + /** + * @param integer $studierendenantrag_id + * + * @return boolean + */ + protected function isOwnAntrag($studierendenantrag_id) + { + if ($studierendenantrag_id == null) + return false; + $result = $this->_ci->StudierendenantragModel->loadForPerson(getAuthPersonId()); + if (!hasData($result)) + return false; + $antraege = array_map(function ($antrag) { + return $antrag->studierendenantrag_id; + }, getData($result)); + + return in_array($studierendenantrag_id, $antraege); + } + + /** + * @param integer $studierendenantrag_id + * @param string $permission either 'student/antragfreigabe' or 'student/studierendenantrag' + * + * @return boolean + */ + protected function hasAccessToAntrag($studierendenantrag_id, $permission) + { + $studiengaenge = $this->_ci->permissionlib->getSTG_isEntitledFor($permission); + if (!$studiengaenge) + return false; + $result = $this->_ci->StudierendenantragModel->isInStudiengang($studierendenantrag_id, $studiengaenge); + return (boolean)getData($result); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToShowAntrag($antrag_id) + { + return + ( + $this->isOwnAntrag($antrag_id) || + $this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe') || + $this->hasAccessToAntrag($antrag_id, 'student/studierendenantrag') + ); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToSeeHistoryForAntrag($antrag_id) + { + return + ( + $this->isOwnAntrag($antrag_id) || + $this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe') || + $this->hasAccessToAntrag($antrag_id, 'student/studierendenantrag') + ); + } + + /** + * @param integer $prestudent_id + * @param boolean $checkAssistencePermission + * + * @return boolean + */ + public function isEntitledToCreateAntragFor($prestudent_id, $checkAssistencePermission = false) + { + $result = $this->_ci->PrestudentModel->load($prestudent_id); + if (!hasData($result)) + return false; + + $result = getData($result)[0]; + $person_id = $result->person_id; + + if (getAuthPersonId() == $person_id) + return true; + + if ($checkAssistencePermission) + { + $studiengaenge = $this->_ci->permissionlib->getSTG_isEntitledFor('student/studierendenantrag'); + if (in_array($result->studiengang_kz, $studiengaenge ?: [])) + return true; + } + + return false; + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToCancelAntrag($antrag_id) + { + return $this->isOwnAntrag($antrag_id); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToReopenAntrag($antrag_id) + { + return $this->hasAccessToAntrag($antrag_id, 'student/studierendenantrag'); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToObjectAntrag($antrag_id) + { + return ($this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe') || $this->hasAccessToAntrag($antrag_id, 'student/studierendenantrag')); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToApproveAntrag($antrag_id) + { + return $this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe'); + } + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function isEntitledToRejectAntrag($antrag_id) + { + return $this->hasAccessToAntrag($antrag_id, 'student/antragfreigabe'); + } + + /** + * @param integer $antrag_id + * @param string|array $status + * + * @return boolean + */ + public function hasStatus($antrag_id, $status) + { + $result = $this->_ci->StudierendenantragModel->getWithLastStatusWhere(['s.studierendenantrag_id' => $antrag_id]); + if (!hasData($result)) + return false; + $lastStatus = getData($result)[0]; + + if (!is_array($status)) + $status = [$status]; + + return in_array($lastStatus->studierendenantrag_statustyp_kurzbz, $status); + } + + + /** + * @param integer $antrag_id + * + * @return boolean + */ + public function getLvsForPrestudent($prestudent_id, $studiensemester_kurzbz) + { + $result = $this->_ci->StudierendenantraglehrveranstaltungModel->getLvsForPrestudent($prestudent_id, $studiensemester_kurzbz); + return $result; + } +} diff --git a/application/libraries/DmsLib.php b/application/libraries/DmsLib.php index fccfe503b..774ebdc79 100644 --- a/application/libraries/DmsLib.php +++ b/application/libraries/DmsLib.php @@ -595,6 +595,8 @@ class DmsLib if (isError($insDmsResult)) return $insDmsResult; $upload_data['dms_id'] = getData($insDmsResult); + if(isset($upload_data['file_type']) && !isset($dms['mimetype'])) + $dms['mimetype'] = $upload_data['file_type']; // Insert DMS version $insVersionResult = $this->_ci->DmsVersionModel->insert( diff --git a/application/libraries/PrestudentLib.php b/application/libraries/PrestudentLib.php new file mode 100644 index 000000000..6dff4e812 --- /dev/null +++ b/application/libraries/PrestudentLib.php @@ -0,0 +1,356 @@ +_ci =& get_instance(); + + // // Configs + // $this->_ci->load->config('studierendenantrag'); + + // // Models + $this->_ci->load->model('crm/Prestudent_model', 'PrestudentModel'); + $this->_ci->load->model('crm/Student_model', 'StudentModel'); + $this->_ci->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel'); + $this->_ci->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); + $this->_ci->load->model('organisation/Lehrverband_model', 'LehrverbandModel'); + $this->_ci->load->model('education/Studentlehrverband_model', 'StudentlehrverbandModel'); + $this->_ci->load->model('person/Benutzer_model', 'BenutzerModel'); + + } + + public function setAbbrecher($prestudent_id, $studiensemester_kurzbz, $insertvon = null) + { + if (!$insertvon) + $insertvon = getAuthUID(); + + $result = $this->_ci->PrestudentstatusModel->getLastStatus($prestudent_id, $studiensemester_kurzbz); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('Kein Prestudent in diesem Studiensemester gefunden'); + + $prestudent_status = current($result); + + $result = $this->_ci->StudentModel->loadWhere(['prestudent_id' => $prestudent_id]); + + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No student found for ' . $prestudent_id); + + $student = current($result); + + //Status updaten + $result = $this->_ci->PrestudentstatusModel->insert([ + 'prestudent_id' => $prestudent_id, + 'status_kurzbz' => Prestudentstatus_model::STATUS_ABBRECHER, + 'studiensemester_kurzbz' => $studiensemester_kurzbz, + 'ausbildungssemester' => $prestudent_status->ausbildungssemester, + 'datum' => date('c'), + 'insertvon' => $insertvon, + 'insertamum' => date('c'), + 'orgform_kurzbz'=> $prestudent_status->orgform_kurzbz, + 'studienplan_id'=> $prestudent_status->studienplan_id, + 'bestaetigtvon' => $insertvon, + 'bestaetigtam' => date('c') + ]); + + if (isError($result)) + return $result; + + //Verband anlegen + $result = $this->_ci->LehrverbandModel->load([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => 'A', + 'gruppe' => '' + ]); + + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + { + $result = $this->_ci->LehrverbandModel->load([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => '', + 'gruppe' => '' + ]); + if (isError($result)) + return $result; + $result = getData($result); + + if(!$result) + { + $this->_ci->LehrverbandModel->insert([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => '', + 'gruppe' => '', + 'bezeichnung' => 'Ab-Unterbrecher', + 'aktiv' => true, + ]); + } + + $this->_ci->LehrverbandModel->insert([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => 'A', + 'gruppe' => '', + 'bezeichnung' => 'Abbrecher', + 'aktiv' => true + ]); + } + + //noch nicht eingetragene Zeugnisnoten auf 9 setzen + $result = $this->_ci->ZeugnisnoteModel->getZeugnisnoten($student->student_uid, $studiensemester_kurzbz); + if (isError($result)) + return $result; + $result = getData($result) ?: []; + + foreach ($result as $lv) + { + if (!$lv->note) + { + $result = $this->_ci->ZeugnisnoteModel->insert([ + 'note' => 9, + 'studiensemester_kurzbz' => $lv->studiensemester_kurzbz, + 'student_uid' => $lv->uid, + 'lehrveranstaltung_id' => $lv->lehrveranstaltung_id + ]); + if (isError($result)) { + $result = $this->_ci->ZeugnisnoteModel->update([ + 'studiensemester_kurzbz' => $lv->studiensemester_kurzbz, + 'student_uid' => $lv->uid, + 'lehrveranstaltung_id' => $lv->lehrveranstaltung_id + ],[ + 'note' => 9 + ]); + + if (isError($result)) + return $result; + } + } + } + + + //Update Aktionen + + //StudentModel updaten + $this->_ci->StudentModel->update([ + 'student_uid' => $student->student_uid + ], [ + 'verband' => 'A', + 'gruppe' => '', + 'semester' => 0, + 'updatevon' => $insertvon, + 'updateamum' => date('c') + ]); + + //Studentlehrverband setzen + $this->_ci->StudentlehrverbandModel->update([ + 'studiensemester_kurzbz' => $studiensemester_kurzbz, + 'student_uid' => $student->student_uid + ],[ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => 'A', + 'gruppe' => '', + 'updateamum' => date('c'), + 'updatevon' => $insertvon + ]); + + //Benutzer inaktiv setzen + $this->_ci->BenutzerModel->update([ + 'uid' => $student->student_uid + ],[ + 'aktiv' => false, + 'updateaktivvon' => $insertvon, + 'updateaktivam' => date('c'), + 'updatevon' => $insertvon, + 'updateamum' => date('c') + ]); + + return success(); + } + + public function setUnterbrecher($prestudent_id, $studiensemester_kurzbz, $studierendenantrag_id, $insertvon = null) + { + if (!$insertvon) + $insertvon = getAuthUID(); + + $result = $this->_ci->PrestudentstatusModel->getLastStatus($prestudent_id, $studiensemester_kurzbz); + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('Kein Prestudent in diesem Studiensemester gefunden'); + + $prestudent_status = current($result); + + $result = $this->_ci->StudentModel->loadWhere(['prestudent_id' => $prestudent_id]); + + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + return error('No student found for ' . $prestudent_id); + + $student = current($result); + + $resultAntrag = $this->_ci->StudierendenantragModel->load($studierendenantrag_id); + if (isError($resultAntrag)) + return $resultAntrag; + $resultAntrag = getData($resultAntrag); + if (!$resultAntrag) + return error('No antrag found with id: ' . $studierendenantrag_id); + + $antrag = current($resultAntrag); + + //Status updaten + $result = $this->_ci->PrestudentstatusModel->insert([ + 'prestudent_id' => $prestudent_id, + 'status_kurzbz' => Prestudentstatus_model::STATUS_UNTERBRECHER, + 'studiensemester_kurzbz' => $studiensemester_kurzbz, + 'ausbildungssemester' => $prestudent_status->ausbildungssemester, + 'datum' => date('c'), + 'insertvon' => $insertvon, + 'insertamum' => date('c'), + 'orgform_kurzbz'=> $prestudent_status->orgform_kurzbz, + 'studienplan_id'=> $prestudent_status->studienplan_id, + 'bestaetigtvon' => $insertvon, + 'bestaetigtam' => date('c'), + 'anmerkung'=> 'Wiedereinstieg ' . $antrag->datum_wiedereinstieg + ]); + + //error try manu + if (isError($result)) + return $result; + + //Verband anlegen + $result = $this->_ci->LehrverbandModel->load([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => 'B', + 'gruppe' => '' + ]); + + if (isError($result)) + return $result; + $result = getData($result); + if (!$result) + { + $result = $this->_ci->LehrverbandModel->load([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => '', + 'gruppe' => '' + ]); + if (isError($result)) + return $result; + $result = getData($result); + + if(!$result) + { + $this->_ci->LehrverbandModel->insert([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => '', + 'gruppe' => '', + 'bezeichnung' => 'Ab-Unterbrecher', + 'aktiv' => true, + ]); + } + + $this->_ci->LehrverbandModel->insert([ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => 'B', + 'gruppe' => '', + 'bezeichnung' => 'Unterbrecher', + 'aktiv' => true + ]); + } + + //noch nicht eingetragene Zeugnisnoten auf 9 setzen + $result = $this->_ci->ZeugnisnoteModel->getZeugnisnoten($student->student_uid, $studiensemester_kurzbz); + if (isError($result)) + return $result; + $result = getData($result) ?: []; + + foreach ($result as $lv) + { + if (!$lv->note) + { + $result = $this->_ci->ZeugnisnoteModel->insert([ + 'note' => 9, + 'studiensemester_kurzbz' => $lv->studiensemester_kurzbz, + 'student_uid' => $lv->uid, + 'lehrveranstaltung_id' => $lv->lehrveranstaltung_id + ]); + if (isError($result)) { + $result = $this->_ci->ZeugnisnoteModel->update([ + 'studiensemester_kurzbz' => $lv->studiensemester_kurzbz, + 'student_uid' => $lv->uid, + 'lehrveranstaltung_id' => $lv->lehrveranstaltung_id + ],[ + 'note' => 9 + ]); + + if (isError($result)) + return $result; + } + } + } + + + //Update Aktionen + + //StudentModel updaten + $this->_ci->StudentModel->update([ + 'student_uid' => $student->student_uid + ], [ + 'verband' => 'B', + 'gruppe' => '', + 'semester' => 0, + 'updatevon' => $insertvon, + 'updateamum' => date('c') + ]); + + //Studentlehrverband setzen + $this->_ci->StudentlehrverbandModel->update([ + 'studiensemester_kurzbz' => $studiensemester_kurzbz, + 'student_uid' => $student->student_uid + ],[ + 'studiengang_kz' => $student->studiengang_kz, + 'semester' => 0, + 'verband' => 'B', + 'gruppe' => '', + 'updateamum' => date('c'), + 'updatevon' => $insertvon + ]); + + return success(); + } + +} diff --git a/application/models/crm/Konto_model.php b/application/models/crm/Konto_model.php index 4b2a259c9..e76ed9e7a 100644 --- a/application/models/crm/Konto_model.php +++ b/application/models/crm/Konto_model.php @@ -100,8 +100,8 @@ class Konto_model extends DB_Model public function checkStudienbeitrag($uid, $stsem, $buchungstypen) { - $query = 'SELECT tbl_konto.buchungsnr, - tbl_konto.buchungsdatum + $query = 'SELECT tbl_konto.buchungsnr, + tbl_konto.buchungsdatum FROM public.tbl_konto, public.tbl_benutzer, public.tbl_student @@ -117,10 +117,47 @@ class Konto_model extends DB_Model FROM public.tbl_konto skonto WHERE skonto.buchungsnr = tbl_konto.buchungsnr_verweis OR skonto.buchungsnr_verweis = tbl_konto.buchungsnr_verweis - ) + ) ORDER BY buchungsnr DESC LIMIT 1 '; return $this->execQuery($query); } + + /** + * check if student has paid studienbeitrag for certain semester + * + * @param $person_id person_id + * @param $stsem stsem + * + * @return boolean + */ + public function checkStudienbeitragFromPerson($person_id, $stsem) + { + $this->addOrder('buchungsnr'); + $this->addLimit(1); + $result = $this->loadWhere([ + 'person_id'=>$person_id, + 'studiensemester_kurzbz' => $stsem, + 'buchungstyp_kurzbz' => 'Studiengebuehr' + ]); + + if (!getData($result)) + return false; + + $data = getData($result)[0]; + + $this->resetQuery(); + + $this->addSelect('sum(betrag) as differenz'); + $this->db->or_where('buchungsnr', $data->buchungsnr); + $this->db->or_where('buchungsnr_verweis', $data->buchungsnr); + + $result = $this->load(); + if (!getData($result)) + return false; + + $data = getData($result)[0]; + return $data->differenz >= 0; + } } diff --git a/application/models/crm/Prestudentstatus_model.php b/application/models/crm/Prestudentstatus_model.php index f39a41fda..620b897e6 100644 --- a/application/models/crm/Prestudentstatus_model.php +++ b/application/models/crm/Prestudentstatus_model.php @@ -2,6 +2,10 @@ class Prestudentstatus_model extends DB_Model { + + const STATUS_ABBRECHER = 'Abbrecher'; + const STATUS_UNTERBRECHER = 'Unterbrecher'; + /** * Constructor */ @@ -226,4 +230,90 @@ class Prestudentstatus_model extends DB_Model return $this->execQuery($query, $parametersArray); } + + /** + * get Email of relevant Studiengang of prestudent + */ + public function getLastStatusWithStgEmail($prestudent_id, $studiensemester_kurzbz = '', $status_kurzbz = '') + { + $this->addSelect('tbl_prestudentstatus.*, + tbl_studienplan.bezeichnung AS studienplan_bezeichnung, + tbl_studienplan.orgform_kurzbz AS orgform, + tbl_studienplan.sprache, + tbl_orgform.bezeichnung_mehrsprachig AS bezeichnung_orgform, + tbl_status.bezeichnung_mehrsprachig, + tbl_status_grund.bezeichnung_mehrsprachig AS bezeichnung_statusgrund, + tbl_studiengang.email'); + $this->addJoin('lehre.tbl_studienplan', 'studienplan_id', 'LEFT'); + $this->addJoin('lehre.tbl_studienordnung', 'studienordnung_id', 'LEFT'); + $this->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT'); + $this->addJoin('public.tbl_status', 'tbl_status.status_kurzbz = tbl_prestudentstatus.status_kurzbz'); + $this->addJoin('public.tbl_status_grund', 'statusgrund_id', 'LEFT'); + $this->addJoin('bis.tbl_orgform', 'tbl_studienplan.orgform_kurzbz = tbl_orgform.orgform_kurzbz', 'LEFT'); + $this->db->where('tbl_status.status_kurzbz = tbl_prestudentstatus.status_kurzbz'); + + $where = array('prestudent_id' => $prestudent_id); + if ($studiensemester_kurzbz) + $where['studiensemester_kurzbz'] = $studiensemester_kurzbz; + if ($status_kurzbz) + $where['tbl_prestudentstatus.status_kurzbz'] = $status_kurzbz; + + $this->addOrder('datum', 'DESC'); + $this->addOrder('insertamum', 'DESC'); + $this->addOrder('ext_id', 'DESC'); + $this->addLimit(1); + + return $this->loadWhere($where); + } + + public function loadLastWithStgDetails($prestudent_id, $studiensemester_kurzbz = null) + { + $this->load->config('studierendenantrag'); + + $lang = getUserLanguage(); + + $this->addSelect($this->dbTable . '.prestudent_id'); + $this->addSelect($this->dbTable . '.ausbildungssemester AS semester'); + $this->addSelect($this->dbTable . '.studiensemester_kurzbz'); + $this->addSelect('s.matrikelnr'); + $this->addSelect('ss.studienjahr_kurzbz'); + $this->addSelect('pers.vorname'); + $this->addSelect('pers.nachname'); + $this->addSelect('TRIM(CONCAT(pers.vorname, \' \', pers.nachname)) AS name'); + $this->addSelect('pers.person_id'); + $this->addSelect('g.studiengang_kz'); + $this->addSelect('g.bezeichnung'); + $this->addSelect('o.orgform_kurzbz'); + $this->addSelect( + 'o.bezeichnung_mehrsprachig[(SELECT index FROM public.tbl_sprache WHERE sprache=\'' . $lang . '\')] AS orgform_bezeichnung', + false + ); + + $this->addJoin('public.tbl_student s', 'prestudent_id'); + $this->addJoin('public.tbl_prestudent p', 'prestudent_id'); + $this->addJoin('public.tbl_studiensemester ss', 'studiensemester_kurzbz'); + $this->addJoin('public.tbl_person pers', 'person_id'); + $this->addJoin('public.tbl_studiengang g', 'p.studiengang_kz=g.studiengang_kz'); + $this->addJoin('bis.tbl_orgform o', 'g.orgform_kurzbz=o.orgform_kurzbz'); + + $this->addOrder($this->dbTable . '.datum', 'DESC'); + $this->addOrder($this->dbTable . '.insertamum', 'DESC'); + $this->addOrder($this->dbTable . '.ext_id', 'DESC'); + + $this->addLimit(1); + + $this->db->where_in($this->dbTable . '.status_kurzbz', $this->config->item('antrag_prestudentstatus_whitelist')); + + $whereArr = [ + $this->dbTable . '.prestudent_id' => $prestudent_id, + 'g.aktiv' => true + ]; + + if ($studiensemester_kurzbz !== null) + { + $whereArr[$this->dbTable. '.studiensemester_kurzbz'] = $studiensemester_kurzbz; + } + + return $this->loadWhere($whereArr); + } } diff --git a/application/models/education/Pruefung_model.php b/application/models/education/Pruefung_model.php index e3776c4ad..4236a5c6a 100644 --- a/application/models/education/Pruefung_model.php +++ b/application/models/education/Pruefung_model.php @@ -29,11 +29,148 @@ class Pruefung_model extends DB_Model JOIN lehre.tbl_pruefung prfg USING (student_uid) JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id) JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) - JOIN public.tbl_studiengang stg ON prst.studiengang_kz = stg.studiengang_kz + JOIN public.tbl_studiengang stg ON prst.studiengang_kz = stg.studiengang_kz WHERE pers.person_id = ? AND le.studiensemester_kurzbz = ? ORDER BY prfg.datum, pruefung_id'; return $this->execQuery($qry, array($person_id, $studiensemester_kurzbz)); } + + /** + * @param integer $prestudent_id student_uid + * + * @return stdClass + */ + public function loadWhereCommitteeExamFailedForPrestudent($prestudent_id) + { + $this->load->config('studierendenantrag'); + + $this->dbTable = 'lehre.tbl_pruefung p'; + + $this->addSelect('p.datum'); + $this->addSelect('pers.vorname'); + $this->addSelect('pers.nachname'); + $this->addSelect('s.matrikelnr'); + $this->addSelect('g.bezeichnung'); + $this->addSelect('g.studiengang_kz'); + $this->addSelect('o.bezeichnung_mehrsprachig'); + $this->addSelect('ps.prestudent_id'); + $this->addSelect('lv.bezeichnung as lvbezeichnung'); + $this->addSelect('le.studiensemester_kurzbz'); + $this->addSelect('a.typ'); + $this->addSelect('campus.get_status_studierendenantrag(a.studierendenantrag_id) status'); + + $this->addJoin('lehre.tbl_note n', 'note'); + $this->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id'); + $this->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id'); + $this->addJoin('public.tbl_student s', 'student_uid'); + $this->addJoin('public.tbl_prestudent ps', 'prestudent_id'); + $this->addJoin('public.tbl_person pers', 'person_id'); + $this->addJoin('public.tbl_studiengang g', 'ps.studiengang_kz=g.studiengang_kz'); + $this->addJoin('bis.tbl_orgform o', 'g.orgform_kurzbz=o.orgform_kurzbz'); + $this->addJoin('campus.tbl_studierendenantrag a', 'ps.prestudent_id=a.prestudent_id', 'LEFT'); + + $this->db->where("n.positiv", false); + $this->db->where("p.pruefungstyp_kurzbz", 'kommPruef'); + $this->db->where("ps.prestudent_id", $prestudent_id); + $this->db->where_in("get_rolle_prestudent(ps.prestudent_id, NULL)", $this->config->item('antrag_prestudentstatus_whitelist')); + $this->db->where("g.aktiv", true); + + $this->db->where('lv.studiengang_kz not in( + SELECT ps.studiengang_kz + FROM + public.tbl_student s + JOIN public.tbl_prestudent ps USING (prestudent_id) + JOIN public.tbl_prestudentstatus pss USING (prestudent_id) + WHERE pss.statusgrund_id in ? + and ps.prestudent_id = ?)', null, false); + + $sql = $this->db->get_compiled_select($this->dbTable); + + return $this->execQuery($sql, [$this->config->item('status_gruende_wiederholer'), $prestudent_id]); + } + + public function getAllPrestudentsWhereCommitteeExamFailed($status, $maxDate, $minDate) + { + $this->load->config('studierendenantrag'); + $this->load->model('education/Studierendenantrag_model', 'StudierendenantragModel'); + + $this->dbTable = 'lehre.tbl_pruefung p'; + + $sprache_index = "SELECT index FROM public.tbl_sprache WHERE sprache='" . getUserLanguage() . "' LIMIT 1"; + + $this->addSelect('p.datum'); + $this->addSelect('pers.vorname'); + $this->addSelect('pers.nachname'); + $this->addSelect('pers.person_id'); + $this->addSelect('s.matrikelnr'); + $this->addSelect('g.bezeichnung'); + $this->addSelect('g.studiengang_kz'); + $this->addSelect('o.bezeichnung_mehrsprachig[(' . $sprache_index . ')] AS orgform', false); + $this->addSelect('ps.prestudent_id'); + $this->addSelect('lv.bezeichnung as lvbezeichnung'); + $this->addSelect('le.studiensemester_kurzbz'); + $this->addSelect('a.typ'); + $this->addSelect('campus.get_status_studierendenantrag(a.studierendenantrag_id) status'); + + $this->addJoin('lehre.tbl_note n', 'note'); + $this->addJoin('lehre.tbl_lehreinheit le', 'lehreinheit_id'); + $this->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id'); + $this->addJoin('public.tbl_student s', 'student_uid'); + $this->addJoin('public.tbl_prestudent ps', 'prestudent_id'); + $this->addJoin('public.tbl_person pers', 'person_id'); + $this->addJoin('public.tbl_benutzer b', 's.student_uid=b.uid'); + $this->addJoin('public.tbl_studiengang g', 'ps.studiengang_kz=g.studiengang_kz'); + $this->addJoin('bis.tbl_orgform o', 'g.orgform_kurzbz=o.orgform_kurzbz'); + $this->db->join('campus.tbl_studierendenantrag a', 'ps.prestudent_id=a.prestudent_id and a.typ = ?', 'LEFT', false); + + $this->db->where("n.positiv", false); + $this->db->where("p.pruefungstyp_kurzbz", 'kommPruef'); + $this->db->where_in("get_rolle_prestudent(ps.prestudent_id, null)", $this->config->item('antrag_prestudentstatus_whitelist')); + + if ($maxDate) + $this->db->where("p.datum < ", $maxDate->format('c')); + if ($minDate) + $this->db->where("p.datum > ", $minDate->format('c')); + $this->db->where("g.aktiv", true); + $this->db->where("b.aktiv", true); + + $this->db->where('lv.studiengang_kz not in( + SELECT ps.studiengang_kz + FROM + public.tbl_prestudent ps1 + JOIN public.tbl_prestudentstatus pss USING (prestudent_id) + WHERE pss.statusgrund_id in ? + AND ps.prestudent_id = ps1.prestudent_id)', null, false); + + // NOTE(chris): is Wiederholer without set statusgrund (legacy?) + $this->db->where('(SELECT COUNT(*) FROM (SELECT DISTINCT studiensemester_kurzbz FROM tbl_prestudentstatus _s WHERE ausbildungssemester=get_absem_prestudent(ps.prestudent_id, le.studiensemester_kurzbz) AND prestudent_id=ps.prestudent_id) a) = 1', null, false); + + if (is_array($status)) { + if (in_array(null, $status)) { + $status = array_filter($status); + if (count($status)) { + $this->db->group_start(); + $this->db->where_in('campus.get_status_studierendenantrag(a.studierendenantrag_id)', $status); + $this->db->or_where('campus.get_status_studierendenantrag(a.studierendenantrag_id)', null); + $this->db->group_end(); + } else { + $this->db->where('campus.get_status_studierendenantrag(a.studierendenantrag_id)', null); + } + } else { + $this->db->where_in('campus.get_status_studierendenantrag(a.studierendenantrag_id)', $status); + } + } else { + $this->db->where('campus.get_status_studierendenantrag(a.studierendenantrag_id)', $status); + } + + $sql = $this->db->get_compiled_select($this->dbTable); + + $statusgruende = $this->config->item('status_gruende_wiederholer'); + if (!is_array($statusgruende)) + $statusgruende = []; + + return $this->execQuery($sql, [Studierendenantrag_model::TYP_WIEDERHOLUNG, $statusgruende]); + } } diff --git a/application/models/education/Studierendenantrag_model.php b/application/models/education/Studierendenantrag_model.php new file mode 100644 index 000000000..bf993ca67 --- /dev/null +++ b/application/models/education/Studierendenantrag_model.php @@ -0,0 +1,229 @@ +dbTable = 'campus.tbl_studierendenantrag'; + $this->pk = 'studierendenantrag_id'; + + $this->load->config('studierendenantrag'); + + $this->load->model('education/Studierendenantragstatus_model', 'StudierendenantragstatusModel'); + } + + public function loadCreatedForStudiengaenge($studiengaenge, $typ) + { + return $this->loadForStudiengaenge($studiengaenge, $typ, $this->StudierendenantragstatusModel::STATUS_CREATED); + } + + public function loadForStudiengaenge($studiengaenge, $typ = null, $status = null) + { + $sql = "SELECT index FROM public.tbl_sprache WHERE sprache='" . getUserLanguage() . "' LIMIT 1"; + + $this->addSelect('stg.bezeichnung'); + $this->addSelect('bezeichnung_mehrsprachig[(' . $sql . ')] AS orgform', false); + $this->addSelect('s.studierendenantrag_id'); + $this->addSelect('matrikelnr'); + $this->addSelect('studienjahr_kurzbz'); + $this->addSelect('vorname'); + $this->addSelect('nachname'); + $this->addSelect('prestudent_id'); + $this->addSelect('p.studiengang_kz'); + $this->addSelect('semester'); + $this->addSelect($this->dbTable . '.grund'); + $this->addSelect('datum'); + $this->addSelect('datum_wiedereinstieg'); + $this->addSelect($this->dbTable . '.typ'); + $this->addSelect('st.studierendenantrag_statustyp_kurzbz as status'); + $this->addSelect('dms_id'); + $this->addSelect('st.bezeichnung[(' . $sql . ')] as statustyp'); + + $this->addJoin('public.tbl_prestudent p', 'prestudent_id'); + $this->addJoin('public.tbl_student', 'prestudent_id'); + $this->addJoin('public.tbl_person', 'person_id'); + $this->addJoin('public.tbl_studiengang stg', 'p.studiengang_kz=stg.studiengang_kz'); + $this->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz'); + $this->addJoin('bis.tbl_orgform', 'orgform_kurzbz'); + $this->addJoin('campus.tbl_studierendenantrag_status as s','campus.get_status_id_studierendenantrag('.$this->dbTable .'.studierendenantrag_id) = studierendenantrag_status_id' ); + $this->addJoin('campus.tbl_studierendenantrag_statustyp as st','studierendenantrag_statustyp_kurzbz' ); + + $this->db->where_in('p.studiengang_kz', $studiengaenge); + + $where = []; + if ($status !== null) + $where['st.studierendenantrag_statustyp_kurzbz'] = $status; + if ($typ !== null) + $where[$this->dbTable . '.typ'] = $typ; + + return $this->loadWhere($where); + } + + public function isInStudiengang($studierendenantrag_id, $studiengaenge) + { + $this->addJoin('public.tbl_prestudent', 'prestudent_id'); + + $this->db->where_in('studiengang_kz', $studiengaenge); + + return $this->load($studierendenantrag_id); + } + + public function loadIdAndStatusWhere($where) + { + $this->addSelect('studierendenantrag_id'); + $this->addSelect('campus.get_status_studierendenantrag(studierendenantrag_id) status'); + return $this->loadWhere($where); + } + + public function loadWithStatusWhere($where) + { + $lang = 'SELECT index FROM public.tbl_sprache WHERE sprache=' . $this->escape(getUserLanguage()); + + $this->addSelect('*'); + $this->addSelect('campus.get_status_studierendenantrag(studierendenantrag_id) status'); + $this->addSelect('t.bezeichnung[(' . $lang . ')] statustyp'); + + $this->addJoin( + 'campus.tbl_studierendenantrag_statustyp t', + 'campus.get_status_studierendenantrag(studierendenantrag_id)=t.studierendenantrag_statustyp_kurzbz' + ); + + $this->addOrder('datum', 'DESC'); + + return $this->loadWhere($where); + } + + /** + * Get the studiengang and ausbildungssemester the student was in + * for the studiensemester the antrag was committed for + * + * @param integer $antrag_id + * + * @return stdClass + */ + public function getStgAndSem($antrag_id) + { + $this->addSelect('p.studiengang_kz'); + $this->addSelect('s.ausbildungssemester'); + $this->addSelect('s.orgform_kurzbz'); + + $this->addJoin( + 'public.tbl_prestudentstatus s', + $this->dbTable . '.prestudent_id=s.prestudent_id AND ' . $this->dbTable . '.studiensemester_kurzbz=s.studiensemester_kurzbz' + ); + $this->addJoin('public.tbl_prestudent p', $this->dbTable . '.prestudent_id=p.prestudent_id'); + + $this->addOrder('s.datum', 'DESC'); + $this->addOrder('s.insertamum', 'DESC'); + $this->addOrder('s.ext_id', 'DESC'); + + $this->addLimit(1); + + $this->db->where_in('s.status_kurzbz', $this->config->item('antrag_prestudentstatus_whitelist')); + + return $this->loadWhere([ + $this->pk => $antrag_id + ]); + } + + /** + * Get the studiengang the student is in + * + * @param integer $antrag_id + * + * @return stdClass + */ + public function getStg($antrag_id) + { + $this->addSelect('p.studiengang_kz'); + $this->addJoin('public.tbl_prestudent p', 'prestudent_id'); + + $this->addLimit(1); + + return $this->load( + $antrag_id + ); + } + + public function getStgEmail($antrag_id) + { + $this->addJoin('public.tbl_prestudent p', 'prestudent_id'); + $this->addJoin('public.tbl_studiengang sg', 'studiengang_kz'); + $this->addSelect('sg.email'); + + return $this->load($antrag_id); + } + + public function loadForPerson($person_id) + { + $lang = 'SELECT index FROM public.tbl_sprache WHERE sprache=' . $this->escape(getUserLanguage()); + $this->addSelect('stg.bezeichnung'); + $this->addSelect('bezeichnung_mehrsprachig[(' . $lang . ')] as orgform'); + $this->addSelect('p.studiengang_kz'); + $this->addSelect('st.studierendenantrag_statustyp_kurzbz as status'); + $this->addSelect('st.bezeichnung[(' . $lang . ')] as status_bezeichnung'); + $this->addSelect('p.prestudent_id'); + $this->addSelect($this->dbTable . '.studierendenantrag_id'); + $this->addSelect($this->dbTable . '.studiensemester_kurzbz'); + $this->addSelect($this->dbTable . '.datum'); + $this->addSelect($this->dbTable . '.typ'); + $this->addSelect($this->dbTable . '.insertamum'); + $this->addSelect($this->dbTable . '.insertvon'); + $this->addSelect($this->dbTable . '.datum_wiedereinstieg'); + $this->addSelect($this->dbTable . '.grund'); + $this->addSelect($this->dbTable . '.dms_id'); + + $this->addJoin('public.tbl_prestudent p', 'prestudent_id', 'RIGHT'); + $this->addJoin('public.tbl_studiengang stg', 'p.studiengang_kz=stg.studiengang_kz'); + $this->addJoin('bis.tbl_orgform', 'orgform_kurzbz'); + $this->addJoin('campus.tbl_studierendenantrag_statustyp st', 'campus.get_status_studierendenantrag(studierendenantrag_id)=st.studierendenantrag_statustyp_kurzbz','LEFT'); + + $this->db->where_in('public.get_rolle_prestudent(p.prestudent_id, null)', $this->config->item('antrag_prestudentstatus_whitelist')); + + return $this->loadWhere([ + 'p.person_id' => $person_id + ]); + } + + public function getAntraegeWhereWiedereinstiegBetween($start, $end) + { + $this->addSelect('sg.email'); + $this->addSelect('vorname'); + $this->addSelect('nachname'); + $this->addSelect($this->dbTable.'.*'); + + $this->addJoin( + 'campus.tbl_studierendenantrag_status s', + 'campus.get_status_id_studierendenantrag(' . $this->dbTable . '.studierendenantrag_id)=s.studierendenantrag_status_id' + ); + $this->addJoin('public.tbl_prestudent p', 'prestudent_id'); + $this->addJoin('public.tbl_person', 'person_id'); + $this->addJoin('public.tbl_studiengang sg', 'studiengang_kz'); + + $this->db->where('datum_wiedereinstieg >=', $start->format('Y-m-d')); + $this->db->where('datum_wiedereinstieg <=', $end->format('Y-m-d')); + + return $this->loadWhere([ + 's.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED, + $this->dbTable.'.typ' => self::TYP_UNTERBRECHUNG, + ]); + } + + public function getWithLastStatusWhere($where) + { + $this->addJoin( + 'campus.tbl_studierendenantrag_status s', + 'campus.get_status_id_studierendenantrag(' . $this->dbTable . '.studierendenantrag_id)=s.studierendenantrag_status_id' + ); + + return $this->loadWhere($where); + } +} diff --git a/application/models/education/Studierendenantraglehrveranstaltung_model.php b/application/models/education/Studierendenantraglehrveranstaltung_model.php new file mode 100644 index 000000000..88d591053 --- /dev/null +++ b/application/models/education/Studierendenantraglehrveranstaltung_model.php @@ -0,0 +1,79 @@ +dbTable = 'campus.tbl_studierendenantrag_lehrveranstaltung'; + $this->pk = 'studierendenantrag_lehrveranstaltung_id'; + } + + public function insertBatch($data) + { + // Check class properties + if (is_null($this->dbTable)) return error('The given database table name is not valid', EXIT_MODEL); + + // DB-INSERT + $insert = $this->db->insert_batch($this->dbTable, $data); + + if ($insert) + { + return success(); + } + else + { + return error($this->db->error(), EXIT_DATABASE); + } + } + + public function deleteWhere($where) + { + if (is_null($this->dbTable)) return error('The given database table name is not valid', EXIT_MODEL); + + $delete = $this->db->delete($this->dbTable, $where); + + if ($delete) + { + return success(); + } + else + { + return error($this->db->error(), EXIT_DATABASE); + } + } + + public function getLvsForPrestudent($prestudent_id, $studiensemester_kurzbz) + { + $this->addSelect($this->dbTable . '.*'); + $this->addSelect('a.prestudent_id'); + $this->addSelect('lv.bezeichnung as lv_bezeichnung'); + $this->addSelect('stat.insertamum as freigabedatum'); + $this->addSelect('n.bezeichnung as note_bezeichnung'); + $this->addSelect('stg.bezeichnung as stg_bezeichnung'); + + $this->addJoin('campus.tbl_studierendenantrag a', 'studierendenantrag_id'); + $this->addJoin('lehre.tbl_note n', 'note'); + $this->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id'); + $this->addJoin('public.tbl_prestudent ps', 'prestudent_id'); + $this->addJoin('public.tbl_studiengang stg', 'ps.studiengang_kz = stg.studiengang_kz'); + $this->addJoin( + 'campus.tbl_studierendenantrag_status stat', + 'stat.studierendenantrag_status_id = campus.get_status_id_studierendenantrag(a.studierendenantrag_id)' + ); + $this->addJoin('public.tbl_student s', 'prestudent_id'); + $this->addJoin('lehre.tbl_zeugnisnote z', 'z.lehrveranstaltung_id=lv.lehrveranstaltung_id AND z.student_uid=s.student_uid AND z.studiensemester_kurzbz=a.studiensemester_kurzbz', 'LEFT'); + + return $this->loadWhere([ + 'ps.prestudent_id' => $prestudent_id, + 'a.typ' => Studierendenantrag_model::TYP_WIEDERHOLUNG, + 'stat.studierendenantrag_statustyp_kurzbz' => Studierendenantragstatus_model::STATUS_APPROVED, + 'n.note <> ' => 0, + $this->dbTable . '.studiensemester_kurzbz' => $studiensemester_kurzbz, + '(n.note<>19 OR z.note IS NOT NULL)' => null + ]); + } +} diff --git a/application/models/education/Studierendenantragstatus_model.php b/application/models/education/Studierendenantragstatus_model.php new file mode 100644 index 000000000..6994c4624 --- /dev/null +++ b/application/models/education/Studierendenantragstatus_model.php @@ -0,0 +1,52 @@ +dbTable = 'campus.tbl_studierendenantrag_status'; + $this->pk = 'studierendenantrag_status_id'; + } + + public function loadWithTyp($studierendenantrag_status_id) + { + $lang = 'SELECT index FROM public.tbl_sprache WHERE sprache=' . $this->escape(getUserLanguage()); + + $this->addSelect($this->dbTable . '.*'); + $this->addSelect('bezeichnung[(' . $lang . ')] AS typ'); + + $this->addJoin('campus.tbl_studierendenantrag_statustyp', 'studierendenantrag_statustyp_kurzbz'); + + return $this->load($studierendenantrag_status_id); + } + + public function loadWithTypWhere($where) + { + $lang = 'SELECT index FROM public.tbl_sprache WHERE sprache=' . $this->escape(getUserLanguage()); + + $this->addSelect($this->dbTable . '.*'); + $this->addSelect('bezeichnung[(' . $lang . ')] AS typ'); + + $this->addJoin('campus.tbl_studierendenantrag_statustyp', 'studierendenantrag_statustyp_kurzbz'); + + return $this->loadWhere($where); + } +} diff --git a/application/models/education/Zeugnisnote_model.php b/application/models/education/Zeugnisnote_model.php index a4185a7f4..b4d909e37 100644 --- a/application/models/education/Zeugnisnote_model.php +++ b/application/models/education/Zeugnisnote_model.php @@ -102,7 +102,7 @@ class Zeugnisnote_model extends DB_Model JOIN lehre.tbl_zeugnisnote zgnisnote USING (student_uid) JOIN lehre.tbl_note note ON zgnisnote.note = note.note JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) - JOIN public.tbl_studiengang stg ON prst.studiengang_kz = stg.studiengang_kz + JOIN public.tbl_studiengang stg ON prst.studiengang_kz = stg.studiengang_kz WHERE pers.person_id = ? AND zgnisnote.studiensemester_kurzbz = ?"; @@ -140,4 +140,83 @@ class Zeugnisnote_model extends DB_Model return $this->execQuery($qry, $params); } + + /** + * Gets courses (Zeugnisnoten) for a student. + * @param string $student_uid, + * @param string $studiensemester_kurzbz + * + * @return object + */ + public function getZeugnisnoten($student_uid, $studiensemester_kurzbz) + { + $params = array(); + $where=''; + + if ($student_uid != null) + { + $where .= " AND uid=?"; + $params[] = $student_uid; + } + if ($studiensemester_kurzbz !=null) + { + $where.=" AND vw_student_lehrveranstaltung.studiensemester_kurzbz= ?"; + $params[] = $studiensemester_kurzbz; + } + + $where2=''; + + if ($student_uid != null) + { + $where2 .= " AND student_uid=?"; + $params[] = $student_uid; + } + if ($studiensemester_kurzbz !=null) + { + $where2 .= " AND studiensemester_kurzbz= ?"; + $params[] = $studiensemester_kurzbz; + } + + $qry = "SELECT vw_student_lehrveranstaltung.lehrveranstaltung_id, uid, + vw_student_lehrveranstaltung.studiensemester_kurzbz, note, punkte, uebernahmedatum, benotungsdatum, + vw_student_lehrveranstaltung.ects, vw_student_lehrveranstaltung.semesterstunden, + tbl_zeugnisnote.updateamum, tbl_zeugnisnote.updatevon, tbl_zeugnisnote.insertamum, + tbl_zeugnisnote.insertvon, tbl_zeugnisnote.ext_id, + vw_student_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, + vw_student_lehrveranstaltung.bezeichnung_english as lehrveranstaltung_bezeichnung_english, + tbl_note.bezeichnung as note_bezeichnung, + tbl_note.positiv as note_positiv, + tbl_zeugnisnote.bemerkung as bemerkung, + vw_student_lehrveranstaltung.sort, + vw_student_lehrveranstaltung.zeugnis, + vw_student_lehrveranstaltung.studiengang_kz, + vw_student_lehrveranstaltung.lv_lehrform_kurzbz, + tbl_lehrveranstaltung.sws + FROM + ( + campus.vw_student_lehrveranstaltung LEFT JOIN lehre.tbl_zeugnisnote + ON(uid=student_uid + AND vw_student_lehrveranstaltung.studiensemester_kurzbz=tbl_zeugnisnote.studiensemester_kurzbz + AND vw_student_lehrveranstaltung.lehrveranstaltung_id=tbl_zeugnisnote.lehrveranstaltung_id + ) + ) LEFT JOIN lehre.tbl_note USING(note) + JOIN lehre.tbl_lehrveranstaltung ON(vw_student_lehrveranstaltung.lehrveranstaltung_id=tbl_lehrveranstaltung.lehrveranstaltung_id) + WHERE true $where + + UNION + SELECT lehre.tbl_lehrveranstaltung.lehrveranstaltung_id,student_uid AS uid,studiensemester_kurzbz, note, punkte, + uebernahmedatum, benotungsdatum,lehre.tbl_lehrveranstaltung.ects,lehre.tbl_lehrveranstaltung.semesterstunden, tbl_zeugnisnote.updateamum, tbl_zeugnisnote.updatevon, tbl_zeugnisnote.insertamum, + tbl_zeugnisnote.insertvon, tbl_zeugnisnote.ext_id, lehre.tbl_lehrveranstaltung.bezeichnung as lehrveranstaltung_bezeichnung, lehre.tbl_lehrveranstaltung.bezeichnung_english as lehrveranstaltung_bezeichnung_english, + tbl_note.bezeichnung as note_bezeichnung, tbl_note.positiv as note_positiv, tbl_zeugnisnote.bemerkung as bemerkung, tbl_lehrveranstaltung.sort, tbl_lehrveranstaltung.zeugnis, tbl_lehrveranstaltung.studiengang_kz, + tbl_lehrveranstaltung.lehrform_kurzbz as lv_lehrform_kurzbz, tbl_lehrveranstaltung.sws + FROM + lehre.tbl_zeugnisnote + JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id) + JOIN lehre.tbl_note USING(note) + WHERE true $where2 + + ORDER BY sort"; + + return $this->execQuery($qry, $params); + } } diff --git a/application/models/organisation/Studiengang_model.php b/application/models/organisation/Studiengang_model.php index f4f8b3c9e..ea9e5857e 100644 --- a/application/models/organisation/Studiengang_model.php +++ b/application/models/organisation/Studiengang_model.php @@ -511,10 +511,10 @@ class Studiengang_model extends DB_Model public function getStudiengangTyp($studiengang_kz, $typ = null) { $query = "SELECT DISTINCT(sgt.*) - FROM tbl_studiengangstyp sgt JOIN tbl_studiengang sg on sgt.typ = sg.typ + FROM tbl_studiengangstyp sgt JOIN tbl_studiengang sg on sgt.typ = sg.typ WHERE studiengang_kz IN ?"; - $params[] = $studiengang_kz; + $params = [$studiengang_kz]; if (!is_null($typ)) { @@ -524,4 +524,53 @@ class Studiengang_model extends DB_Model return $this->execQuery($query, $params); } + + /** + * @param array $studiengang_kzs + * @param array $not_antrag_typ (optional) If the prestudent has an antrag with one of the specified types it will be excluded from the result + * @param array $prestudent_stati (optional) + * + * @return stdClass + */ + public function getAktivePrestudenten($studiengang_kzs, $not_antrag_typ = null) + { + $this->load->config('studierendenantrag'); + + $sql = "SELECT index FROM public.tbl_sprache WHERE sprache='" . getUserLanguage() . "' LIMIT 1"; + + $this->addSelect($this->dbTable . '.studiengang_kz'); + $this->addSelect($this->dbTable . '.bezeichnung'); + $this->addSelect('o.orgform_kurzbz'); + $this->addSelect('o.bezeichnung_mehrsprachig[(' . $sql . ')] AS orgform', false); + $this->addSelect('ps.ausbildungssemester AS semester'); + $this->addSelect('ps.studiensemester_kurzbz'); + $this->addSelect('p.prestudent_id'); + $this->addSelect('pers.vorname'); + $this->addSelect('pers.nachname'); + + $this->addJoin('public.tbl_prestudent p', 'studiengang_kz'); + $this->addJoin( + 'public.tbl_prestudentstatus ps', + 'ps.prestudent_id=p.prestudent_id + AND ps.studiensemester_kurzbz=get_stdsem_prestudent(p.prestudent_id, NULL) + AND ps.ausbildungssemester=get_absem_prestudent(p.prestudent_id, NULL) + AND ps.status_kurzbz=get_rolle_prestudent(p.prestudent_id, NULL)' + ); + $this->addJoin('bis.tbl_orgform o', $this->dbTable . '.orgform_kurzbz=o.orgform_kurzbz'); + $this->addJoin('public.tbl_person pers', 'person_id'); + + $this->db->where_in($this->dbTable . '.studiengang_kz', $studiengang_kzs); + $this->db->where_in('ps.status_kurzbz', $this->config->item('antrag_prestudentstatus_whitelist')); + $this->db->where($this->dbTable . ".aktiv", true); + + if ($not_antrag_typ !== null && is_array($not_antrag_typ)) { + $this->addJoin('campus.tbl_studierendenantrag a', 'a.prestudent_id=p.prestudent_id', 'LEFT'); + $this->db->group_start(); + $this->db->where_not_in('a.typ', $not_antrag_typ); + $this->db->or_where('a.typ IS NULL'); + $this->db->group_end(); + } + + return $this->load(); + } } diff --git a/application/models/organisation/Studienplan_model.php b/application/models/organisation/Studienplan_model.php index 0cc23b85d..66ec06ba8 100644 --- a/application/models/organisation/Studienplan_model.php +++ b/application/models/organisation/Studienplan_model.php @@ -45,7 +45,7 @@ class Studienplan_model extends DB_Model $whereArray["tbl_studienplan.sprache"] = $sprache; } - return $this->StudienplanModel->loadWhere($whereArray); + return $this->loadWhere($whereArray); } public function getStudienplanLehrveranstaltung($studienplan_id, $semester) @@ -53,6 +53,38 @@ class Studienplan_model extends DB_Model $this->addJoin('lehre.tbl_studienplan_lehrveranstaltung', 'studienplan_id'); $this->addJoin('lehre.tbl_lehrveranstaltung', 'lehrveranstaltung_id'); $this->addOrder('tbl_lehrveranstaltung.sort'); + + return $this->loadWhere(array( + 'studienplan_id' => $studienplan_id, + 'tbl_studienplan_lehrveranstaltung.semester' => $semester + )); + } + + public function getStudienplanLehrveranstaltungForPrestudent($studienplan_id, $semester, $prestudent_id, $note_stsem) + { + $lang = 'SELECT index FROM public.tbl_sprache WHERE sprache=' . $this->escape(getUserLanguage()); + $sql = 'SELECT student_uid FROM public.tbl_student WHERE prestudent_id=' . $this->escape($prestudent_id); + + $this->addSelect($this->dbTable . '.*'); + $this->addSelect('lv.*'); + $this->addSelect('COALESCE(n.bezeichnung_mehrsprachig[(' . $lang . ')], NULL) AS note'); + $this->addSelect('n.positiv'); + $this->addSelect('lehre.tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id'); + $this->addSelect('lehre.tbl_studienplan_lehrveranstaltung.sort plan_sort'); + $this->addSelect('lehre.tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id_parent'); + + $this->addJoin('lehre.tbl_studienplan_lehrveranstaltung', 'studienplan_id'); + $this->addJoin('lehre.tbl_lehrveranstaltung lv', 'lehrveranstaltung_id'); + $this->addJoin( + 'lehre.tbl_zeugnisnote zn', + 'zn.lehrveranstaltung_id=lv.lehrveranstaltung_id AND zn.student_uid=(' . $sql . ') AND zn.studiensemester_kurzbz=' . $this->escape($note_stsem), + 'LEFT' + ); + $this->addJoin('lehre.tbl_note n', 'n.note=zn.note', 'LEFT'); + + $this->addOrder('lehre.tbl_studienplan_lehrveranstaltung.sort'); + $this->addOrder('lv.sort'); + return $this->loadWhere(array( 'studienplan_id' => $studienplan_id, 'tbl_studienplan_lehrveranstaltung.semester' => $semester diff --git a/application/models/organisation/Studiensemester_model.php b/application/models/organisation/Studiensemester_model.php index bb9b94c92..6daa76b59 100644 --- a/application/models/organisation/Studiensemester_model.php +++ b/application/models/organisation/Studiensemester_model.php @@ -204,4 +204,34 @@ class Studiensemester_model extends DB_Model return $this->execQuery($query, array($studiensemester_kurzbz)); } + + public function getFollowingSemester($studienplan_ids, $studiensemester_kurzbz, $ausbildungssemester) + { + $query = ' + WITH RECURSIVE following(studiensemester_kurzbz, semester, start) AS ( + SELECT studiensemester_kurzbz, ?, start + FROM public.tbl_studiensemester + WHERE studiensemester_kurzbz=? + UNION ALL + SELECT * FROM ( + SELECT s.studiensemester_kurzbz, s.semester, ss.start + FROM lehre.tbl_studienplan_semester s + JOIN public.tbl_studiensemester ss USING(studiensemester_kurzbz) + INNER JOIN following a ON(s.semester=a.semester+1 AND ss.start > a.start) + WHERE studienplan_id IN ? + ORDER BY start ASC + LIMIT 1 + ) wrapper + ) + SELECT sem.*, following.semester + FROM following + LEFT JOIN ' . $this->dbTable . ' sem USING(studiensemester_kurzbz) + ORDER BY start'; + + return $this->execQuery($query, [ + $ausbildungssemester, + $studiensemester_kurzbz, + $studienplan_ids + ]); + } } diff --git a/application/models/person/Person_model.php b/application/models/person/Person_model.php index 8875fd4c5..b946ad11f 100644 --- a/application/models/person/Person_model.php +++ b/application/models/person/Person_model.php @@ -335,4 +335,17 @@ class Person_model extends DB_Model return $this->execQuery($qry, array($person_id, $person_id, $person_id)); } + + public function loadPrestudent($prestudent_id) + { + $this->addSelect($this->dbTable . '.*'); + + $this->addJoin('public.tbl_prestudent p', 'person_id'); + + $this->addLimit(1); + + return $this->loadWhere([ + 'prestudent_id' => $prestudent_id + ]); + } } diff --git a/application/views/lehre/Antrag/Create.php b/application/views/lehre/Antrag/Create.php new file mode 100644 index 000000000..87c9c3af6 --- /dev/null +++ b/application/views/lehre/Antrag/Create.php @@ -0,0 +1,54 @@ + 'Antrag auf Änderung des Studierendenstatus', + 'cis' => true, + 'vue3' => true, + 'axios027' => true, + 'bootstrap5' => true, + 'fontawesome6' => true, + 'phrases' => array( + ), + 'customJSModules' => array('public/js/apps/lehre/Antrag.js'), + 'customCSSs' => array( + 'public/css/Fhc.css', + 'vendor/vuepic/vue-datepicker-css/main.css' + ), + 'customJSs' => array( + ) +); + +$this->load->view( + 'templates/FHC-Header', + $sitesettings +); +?> + +
+
+

p->t('studierendenantrag', 'antrag_header'); ?>

+
+ +
+
+ + +
+
+ + +
+
+
+ +load->view( + 'templates/FHC-Footer', + $sitesettings +); diff --git a/application/views/lehre/Antrag/Leitung/List.php b/application/views/lehre/Antrag/Leitung/List.php new file mode 100644 index 000000000..a0c6cdab0 --- /dev/null +++ b/application/views/lehre/Antrag/Leitung/List.php @@ -0,0 +1,58 @@ + 'Anträge auf Änderung des Studierendenstatus', + 'cis' => true, + 'vue3' => true, + 'axios027' => true, + 'bootstrap5' => true, + 'tabulator5' => true, + 'fontawesome6' => true, + 'primevue3' => true, + 'phrases' => array( + 'global', + 'studierendenantrag', + 'lehre', + 'person', + ), + 'customJSModules' => array('public/js/apps/lehre/Antrag/Leitung.js'), + 'customCSSs' => array( + 'public/css/Fhc.css' + ), + 'customJSs' => array( + ) +); + +$this->load->view( + 'templates/FHC-Header', + $sitesettings +); +?> + +
+
+

p->t('studierendenantrag', 'antraege_header'); ?>

+
+ +
+
+ + + + +
+
+
+
+
+ +load->view( + 'templates/FHC-Footer', + $sitesettings +); diff --git a/application/views/lehre/Antrag/Student/List.php b/application/views/lehre/Antrag/Student/List.php new file mode 100644 index 000000000..26b0ec3f1 --- /dev/null +++ b/application/views/lehre/Antrag/Student/List.php @@ -0,0 +1,151 @@ + 'Antrag auf Änderung des Studierendenstatus', + 'cis' => true, + 'vue3' => true, + 'axios027' => true, + 'bootstrap5' => true, + 'fontawesome6' => true, + 'phrases' => array( + ), + 'customJSModules' => array('public/js/apps/lehre/Antrag/Student.js'), + 'customCSSs' => array( + 'public/css/Fhc.css' + ), + 'customJSs' => array( + ) +); + +$this->load->view( + 'templates/FHC-Header', + $sitesettings +); +?> + +
+ +
+

p->t('studierendenantrag', 'antraege_header'); ?>

+
+ +
+
+ + $array){ ?> +

()

+ + + + p->t('studierendenantrag', 'btn_new'); ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
#p->t('studierendenantrag', 'antrag_typ'); ?>p->t('studierendenantrag', 'antrag_status'); ?>p->t('studierendenantrag', 'antrag_studiensemester'); ?>p->t('studierendenantrag', 'antrag_erstelldatum'); ?>p->t('studierendenantrag', 'antrag_datum_wiedereinstieg'); ?>p->t('studierendenantrag', 'antrag_grund'); ?>p->t('studierendenantrag', 'antrag_dateianhaenge'); ?> 
studierendenantrag_id; ?>p->t('studierendenantrag', 'antrag_typ_' . $antrag->typ); ?>status_bezeichnung; ?>studiensemester_kurzbz; ?>datum))->format('d.m.Y'); ?>datum_wiedereinstieg ? (new DateTime($antrag->datum_wiedereinstieg))->format('d.m.Y') : ''; ?> + grund){ ?> + + p->t('studierendenantrag', 'antrag_grund'); ?> + + + + + + + dms_id) {?> + + p->t('studierendenantrag', 'antrag_anhang'); ?> + + + + + typ != Studierendenantrag_model::TYP_WIEDERHOLUNG && $antrag->status == Studierendenantragstatus_model::STATUS_APPROVED) { ?> + + + typ == Studierendenantrag_model::TYP_WIEDERHOLUNG && $antrag->status == Studierendenantragstatus_model::STATUS_APPROVED) { ?> + + p->t('studierendenantrag', 'btn_show_lvs'); ?> + + + p->t('studierendenantrag', 'my_lvs'); ?> + + +
+ + + + +
+
+
+
+
+ +load->view( + 'templates/FHC-Footer', + $sitesettings +); diff --git a/application/views/lehre/Antrag/Wiederholung/Student.php b/application/views/lehre/Antrag/Wiederholung/Student.php new file mode 100644 index 000000000..1aa72ee69 --- /dev/null +++ b/application/views/lehre/Antrag/Wiederholung/Student.php @@ -0,0 +1,43 @@ + 'Antrag Wiederholung vom Studium', + 'cis' => true, + 'vue3' => true, + 'axios027' => true, + 'bootstrap5' => true, + 'fontawesome6' => true, + 'tabulator5' => true, + 'phrases' => array( + 'ui', + 'lehre', + 'global' + ), + 'customJSModules' => array('public/js/apps/lehre/Antrag/Lvzuweisung.js'), + 'customCSSs' => array( + ), + 'customJSs' => array( + ) +); + +$this->load->view( + 'templates/FHC-Header', + $sitesettings +); +?> + +
+ +
+

p->t('studierendenantrag', 'title_lvzuweisen', ['name' => $antrag->name]);?>

+
+ +
+ status != Studierendenantragstatus_model::STATUS_CREATED && $antrag->status != Studierendenantragstatus_model::STATUS_LVSASSIGNED) ? ' disabled' : ''; ?>> +
+
+ +load->view( + 'templates/FHC-Footer', + $sitesettings +); diff --git a/application/views/lehre/Antrag/Wiederholung/getLvs.rdf.php b/application/views/lehre/Antrag/Wiederholung/getLvs.rdf.php new file mode 100644 index 000000000..24eb1e23f --- /dev/null +++ b/application/views/lehre/Antrag/Wiederholung/getLvs.rdf.php @@ -0,0 +1,31 @@ + + + + + + freigabedatum); ?> + insertamum); ?> + + + studierendenantrag_lehrveranstaltung_id; ?>]]> + lehrveranstaltung_id; ?>]]> + prestudent_id; ?>]]> + insertvon; ?>]]> + studiensemester_kurzbz; ?>]]> + note; ?>]]> + format('c'); ?>]]> + format('d.m.Y'); ?>]]> + format('c'); ?>]]> + format('d.m.Y'); ?>]]> + note_bezeichnung; ?>]]> + lv_bezeichnung; ?>]]> + stg_bezeichnung; ?>]]> + + + + + + diff --git a/application/views/lehre/Antrag/Wiederholung/moveLvs.rdf.php b/application/views/lehre/Antrag/Wiederholung/moveLvs.rdf.php new file mode 100644 index 000000000..4ed31218d --- /dev/null +++ b/application/views/lehre/Antrag/Wiederholung/moveLvs.rdf.php @@ -0,0 +1,17 @@ + + + + + + + ]]> + + + + + + diff --git a/application/views/templates/FHC-Common.php b/application/views/templates/FHC-Common.php index dd1d8ae1c..0928f516a 100644 --- a/application/views/templates/FHC-Common.php +++ b/application/views/templates/FHC-Common.php @@ -34,6 +34,7 @@ // Internal resources $ajaxlib = isset($ajaxlib) ? $ajaxlib : false; $bootstrapper = isset($bootstrapper) ? $bootstrapper : false; + $cis = isset($cis) ? $cis : false; $dialoglib = isset($dialoglib) ? $dialoglib : false; $filtercomponent = isset($filtercomponent) ? $filtercomponent : false; $filterwidget = isset($filterwidget) ? $filterwidget : false; @@ -43,4 +44,3 @@ $tablewidget = isset($tablewidget) ? $tablewidget : false; $udfs = isset($udfs) ? $udfs : false; $widgets = isset($widgets) ? $widgets : false; - diff --git a/application/views/templates/FHC-Header.php b/application/views/templates/FHC-Header.php index aa63541c5..b87a6e6d0 100644 --- a/application/views/templates/FHC-Header.php +++ b/application/views/templates/FHC-Header.php @@ -118,6 +118,9 @@ // HTML Widget CSS if ($widgets === true) generateCSSsInclude('public/css/Widgets.css'); + // CIS + if ($cis === true) generateCSSsInclude('public/css/cis_bs5.css'); + // Eventually required CSS generateCSSsInclude($customCSSs); // Eventually required CSS ?> diff --git a/cis/private/pdfExport.php b/cis/private/pdfExport.php index 4535ff74d..6db6f885a 100644 --- a/cis/private/pdfExport.php +++ b/cis/private/pdfExport.php @@ -144,6 +144,8 @@ if (isset($_GET['projektarbeit_id'])) $params .= '&projektarbeit_id='. $_GET['projektarbeit_id']; if (isset($_GET['betreuerart_kurzbz'])) $params .= '&betreuerart_kurzbz='. $_GET['betreuerart_kurzbz']; +if (isset($_GET['id'])) + $params .= '&id='. $_GET['id']; // Logeintrag bei Download von Zahlungsbestaetigungen diff --git a/cis/private/profile/dokumente.php b/cis/private/profile/dokumente.php index f75529561..16fc9a085 100644 --- a/cis/private/profile/dokumente.php +++ b/cis/private/profile/dokumente.php @@ -31,6 +31,7 @@ require_once('../../../include/akte.class.php'); require_once('../../../include/datum.class.php'); require_once('../../../include/benutzerberechtigung.class.php'); require_once('../../../include/webservicelog.class.php'); +require_once('../../../include/studierendenantrag.class.php'); $sprache = getSprache(); $p = new phrasen($sprache); @@ -228,6 +229,27 @@ echo '

'; // Wenn es für das übergebene Studiensemester keinen PreStudentStatus gibt, werden nur Abschlussdokumente angezeigt if (in_array($stsem, $stsem_arr)) { + $studierendenantrag = new studierendenantrag(); + if ($studierendenantrag->loadUserAntrag($student_studiengang->prestudent_id, $stsem) && $studierendenantrag->result) { + // TODO(chris): Phrasen! + echo '

' . $p->t('tools/studierendenantrag') . '

'; + echo ' + + + + + + + '; + foreach ($studierendenantrag->result as $antrag) { + $path = "../pdfExport.php?xsl=Antrag" . $antrag->typ . "&xml=Antrag" . $antrag->typ . ".xml.php&uid=" . $uid . "&id=" . $antrag->studierendenantrag_id; + echo ''; + echo ''; + } + echo '
'.$p->t('global/name').'
' . $p->t('tools/studierendenantrag_' . $antrag->typ) . '
'; + } + + $konto = new konto(); $buchungstypen = array(); diff --git a/composer.json b/composer.json index 825ed5625..739ee4492 100644 --- a/composer.json +++ b/composer.json @@ -26,6 +26,28 @@ "wiki": "https://wiki.fhcomplete.info/doku.php" }, "repositories": [ + { + "type": "package", + "package": { + "name": "vuepic/vue-datepicker-js", + "version": "4.0.0", + "dist": { + "url": "https://unpkg.com/@vuepic/vue-datepicker@4.0.0/dist/vue-datepicker.iife.js", + "type": "file" + } + } + }, + { + "type": "package", + "package": { + "name": "vuepic/vue-datepicker-css", + "version": "4.0.0", + "dist": { + "url": "https://unpkg.com/@vuepic/vue-datepicker@4.0.0/dist/main.css", + "type": "file" + } + } + }, { "type": "package", "package": { @@ -420,7 +442,9 @@ "twbs/bootstrap5": "5.1.*", "vuejs/vuejs3": "3.2.33", - "vuejs/vuerouter4": "4.1.3" + "vuejs/vuerouter4": "4.1.3", + "vuepic/vue-datepicker-js": "4.*", + "vuepic/vue-datepicker-css": "4.*" }, "config": { "bin-dir": "vendor/bin" @@ -432,4 +456,3 @@ "sebastian/phpcpd": "3.*" } } - diff --git a/composer.lock b/composer.lock index 72f13c76f..30d011aae 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d530cc4d7bd81812535eb64e87ba04f4", + "content-hash": "03cfe5f5e33ab66a5009b6ffcb2c5fa0", "packages": [ { "name": "afarkas/html5shiv", @@ -35,6 +35,10 @@ ], "description": "Defacto way to enable use of HTML5 sectioning elements in legacy Internet Explorer.", "homepage": "http://paulirish.com/2011/the-history-of-the-html5-shiv/", + "support": { + "issues": "https://github.com/aFarkas/html5shiv/issues", + "source": "https://github.com/aFarkas/html5shiv/tree/3.7.3" + }, "time": "2015-07-20T20:04:00+00:00" }, { @@ -130,6 +134,16 @@ "keywords": [ "qr code" ], + "support": { + "issues": "https://github.com/chillerlan/php-qrcode/issues", + "source": "https://github.com/chillerlan/php-qrcode/tree/v2.0.x" + }, + "funding": [ + { + "url": "https://ko-fi.com/codemasher", + "type": "ko_fi" + } + ], "time": "2020-04-12T07:38:35+00:00" }, { @@ -178,6 +192,10 @@ "helper", "trait" ], + "support": { + "issues": "https://github.com/chillerlan/php-traits/issues", + "source": "https://github.com/chillerlan/php-traits" + }, "abandoned": true, "time": "2018-06-22T00:30:47+00:00" }, @@ -218,6 +236,10 @@ ], "description": "REST Server for the CodeIgniter framework", "homepage": "https://github.com/chriskacerguis/codeigniter-restserver", + "support": { + "issues": "https://github.com/chriskacerguis/codeigniter-restserver/issues", + "source": "https://github.com/chriskacerguis/codeigniter-restserver" + }, "time": "2017-09-23T16:44:55+00:00" }, { @@ -236,6 +258,10 @@ }, "type": "library", "notification-url": "https://packagist.org/downloads/", + "support": { + "issues": "https://github.com/akiyatkin/tablesorter/issues", + "source": "https://github.com/akiyatkin/tablesorter/tree/master" + }, "time": "2016-09-02T11:31:54+00:00" }, { @@ -269,6 +295,13 @@ ], "description": "The CodeIgniter framework", "homepage": "https://codeigniter.com", + "support": { + "forum": "http://forum.codeigniter.com/", + "issues": "https://github.com/bcit-ci/CodeIgniter/issues", + "slack": "https://codeigniterchat.slack.com", + "source": "https://github.com/bcit-ci/CodeIgniter", + "wiki": "https://github.com/bcit-ci/CodeIgniter/wiki" + }, "time": "2022-03-03T13:21:49+00:00" }, { @@ -573,6 +606,10 @@ ], "description": "Shim repository for Angular.js", "homepage": "http://angularjs.org", + "support": { + "issues": "https://github.com/components/angular.js/issues", + "source": "https://github.com/components/angular.js/tree/master" + }, "time": "2015-06-07T20:10:38+00:00" }, { @@ -615,6 +652,13 @@ ], "description": "jQuery JavaScript Library", "homepage": "http://jquery.com", + "support": { + "forum": "http://forum.jquery.com", + "irc": "irc://irc.freenode.org/jquery", + "issues": "https://github.com/jquery/jquery/issues", + "source": "https://github.com/jquery/jquery", + "wiki": "http://docs.jquery.com/" + }, "time": "2021-03-20T19:13:42+00:00" }, { @@ -700,6 +744,10 @@ } ], "description": "jQuery UI is a curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library. Whether you're building highly interactive web applications or you just need to add a date picker to a form control, jQuery UI is the perfect choice.", + "support": { + "issues": "https://github.com/components/jqueryui/issues", + "source": "https://github.com/components/jqueryui/tree/master" + }, "time": "2016-09-16T05:47:55+00:00" }, { @@ -749,6 +797,10 @@ "captcha", "security" ], + "support": { + "issues": "https://github.com/dapphp/securimage/issues", + "source": "https://github.com/dapphp/securimage/tree/master" + }, "abandoned": true, "time": "2018-03-09T06:07:41+00:00" }, @@ -812,6 +864,12 @@ "rdfa", "sparql" ], + "support": { + "forum": "http://groups.google.com/group/easyrdf/", + "irc": "irc://chat.freenode.net/easyrdf", + "issues": "http://github.com/njh/easyrdf/issues", + "source": "https://github.com/easyrdf/easyrdf/tree/0.9.1" + }, "time": "2015-02-27T09:45:49+00:00" }, { @@ -890,6 +948,10 @@ "faker", "fixtures" ], + "support": { + "issues": "https://github.com/fzaninotto/Faker/issues", + "source": "https://github.com/fzaninotto/Faker/tree/v1.9.2" + }, "abandoned": true, "time": "2020-12-11T09:56:16+00:00" }, @@ -1012,6 +1074,10 @@ "json", "schema" ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/master" + }, "time": "2014-08-25T02:48:14+00:00" }, { @@ -1046,6 +1112,10 @@ } ], "description": "A framework-agnostic PHP Implementation for generating simple forms based on json-schema", + "support": { + "issues": "https://github.com/kingsquare/json-schema-form/issues", + "source": "https://github.com/kingsquare/json-schema-form/tree/master" + }, "time": "2014-07-10T12:27:19+00:00" }, { @@ -1106,6 +1176,10 @@ "keywords": [ "markdown" ], + "support": { + "issues": "https://github.com/michelf/php-markdown/issues", + "source": "https://github.com/michelf/php-markdown/tree/lib" + }, "time": "2015-03-01T12:03:08+00:00" }, { @@ -1153,20 +1227,24 @@ "uri", "url" ], + "support": { + "issues": "https://github.com/lanthaler/IRI/issues", + "source": "https://github.com/lanthaler/IRI/tree/master" + }, "time": "2014-01-21T13:43:39+00:00" }, { "name": "ml/json-ld", - "version": "1.2.0", + "version": "1.2.1", "source": { "type": "git", "url": "https://github.com/lanthaler/JsonLD.git", - "reference": "c74a1aed5979ed1cfb1be35a55a305fd30e30b93" + "reference": "537e68e87a6bce23e57c575cd5dcac1f67ce25d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/lanthaler/JsonLD/zipball/c74a1aed5979ed1cfb1be35a55a305fd30e30b93", - "reference": "c74a1aed5979ed1cfb1be35a55a305fd30e30b93", + "url": "https://api.github.com/repos/lanthaler/JsonLD/zipball/537e68e87a6bce23e57c575cd5dcac1f67ce25d8", + "reference": "537e68e87a6bce23e57c575cd5dcac1f67ce25d8", "shasum": "" }, "require": { @@ -1202,7 +1280,11 @@ "JSON-LD", "jsonld" ], - "time": "2020-06-16T17:45:06+00:00" + "support": { + "issues": "https://github.com/lanthaler/JsonLD/issues", + "source": "https://github.com/lanthaler/JsonLD/tree/1.2.1" + }, + "time": "2022-09-29T08:45:17+00:00" }, { "name": "moment/momentjs", @@ -1262,6 +1344,14 @@ "sorting", "table" ], + "support": { + "docs": "https://mottie.github.io/tablesorter/docs/index.html", + "email": "wowmotty@gmail.com", + "irc": "irc://irc.freenode.org/tablesorter", + "issues": "https://github.com/Mottie/tablesorter/issues", + "source": "https://github.com/Mottie/tablesorter", + "wiki": "https://github.com/Mottie/tablesorter/wiki" + }, "time": "2020-03-03T13:46:03+00:00" }, { @@ -1312,6 +1402,10 @@ "rest", "restful" ], + "support": { + "issues": "https://github.com/nategood/httpful/issues", + "source": "https://github.com/nategood/httpful/tree/v0.2.20" + }, "time": "2015-10-26T16:11:30+00:00" }, { @@ -1365,6 +1459,12 @@ "plaintext", "textile" ], + "support": { + "irc": "irc://irc.freenode.net/textile", + "issues": "https://github.com/textile/php-textile/issues", + "source": "https://github.com/textile/php-textile", + "wiki": "https://github.com/textile/php-textile/wiki" + }, "time": "2022-05-01T17:05:16+00:00" }, { @@ -1390,10 +1490,10 @@ }, { "name": "npm-asset/primevue", - "version": "3.15.0", + "version": "3.29.2", "dist": { "type": "tar", - "url": "https://registry.npmjs.org/primevue/-/primevue-3.15.0.tgz" + "url": "https://registry.npmjs.org/primevue/-/primevue-3.29.2.tgz" }, "type": "npm-asset", "license": [ @@ -1420,16 +1520,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "2.0.37", + "version": "2.0.44", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "c812fbb4d6b4d7f30235ab7298a12f09ba13b37c" + "reference": "149f608243f8133c61926aae26ce67d2b22b37e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/c812fbb4d6b4d7f30235ab7298a12f09ba13b37c", - "reference": "c812fbb4d6b4d7f30235ab7298a12f09ba13b37c", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/149f608243f8133c61926aae26ce67d2b22b37e5", + "reference": "149f608243f8133c61926aae26ce67d2b22b37e5", "shasum": "" }, "require": { @@ -1444,7 +1544,8 @@ "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.", "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.", "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.", - "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations." + "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations.", + "ext-xml": "Install the XML extension to load XML formatted public keys." }, "type": "library", "autoload": { @@ -1507,7 +1608,34 @@ "x.509", "x509" ], - "time": "2022-04-04T04:57:45+00:00" + "support": { + "issues": "https://github.com/phpseclib/phpseclib/issues", + "source": "https://github.com/phpseclib/phpseclib/tree/2.0.44" + }, + "funding": [ + { + "url": "https://github.com/terrafrost", + "type": "github" + }, + { + "url": "https://www.patreon.com/phpseclib", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib", + "type": "tidelift" + } + ], + "time": "2023-06-13T08:41:47+00:00" + }, + { + "name": "popperjs/core", + "version": "2.10.2", + "dist": { + "type": "file", + "url": "https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js" + }, + "type": "library" }, { "name": "rmariuzzo/jquery-checkboxes", @@ -1587,6 +1715,23 @@ "polyfill", "portable" ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-10-23T09:01:57+00:00" }, { @@ -1706,6 +1851,10 @@ "keywords": [ "templating" ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/1.x" + }, "time": "2020-02-11T05:59:23+00:00" }, { @@ -1725,6 +1874,24 @@ "url": "https://unpkg.com/vue-router@4.1.3/dist/vue-router.global.js" }, "type": "library" + }, + { + "name": "vuepic/vue-datepicker-css", + "version": "4.0.0", + "dist": { + "type": "file", + "url": "https://unpkg.com/@vuepic/vue-datepicker@4.0.0/dist/main.css" + }, + "type": "library" + }, + { + "name": "vuepic/vue-datepicker-js", + "version": "4.0.0", + "dist": { + "type": "file", + "url": "https://unpkg.com/@vuepic/vue-datepicker@4.0.0/dist/vue-datepicker.iife.js" + }, + "type": "library" } ], "packages-dev": [ @@ -1779,6 +1946,24 @@ "regex", "regular expression" ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/1.0.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], "time": "2022-01-21T20:24:37+00:00" }, { @@ -1826,20 +2011,39 @@ "Xdebug", "performance" ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/2.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], "time": "2022-02-24T20:20:32+00:00" }, { "name": "nikic/php-parser", - "version": "v4.14.0", + "version": "v4.16.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1" + "reference": "19526a33fb561ef417e822e85f08a00db4059c17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1", - "reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/19526a33fb561ef417e822e85f08a00db4059c17", + "reference": "19526a33fb561ef417e822e85f08a00db4059c17", "shasum": "" }, "require": { @@ -1878,20 +2082,24 @@ "parser", "php" ], - "time": "2022-05-31T20:59:12+00:00" + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.16.0" + }, + "time": "2023-06-25T14:52:30+00:00" }, { "name": "pdepend/pdepend", - "version": "2.10.3", + "version": "2.14.0", "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "da3166a06b4a89915920a42444f707122a1584c9" + "reference": "1121d4b04af06e33e9659bac3a6741b91cab1de1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/da3166a06b4a89915920a42444f707122a1584c9", - "reference": "da3166a06b4a89915920a42444f707122a1584c9", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/1121d4b04af06e33e9659bac3a6741b91cab1de1", + "reference": "1121d4b04af06e33e9659bac3a6741b91cab1de1", "shasum": "" }, "require": { @@ -1925,26 +2133,42 @@ "BSD-3-Clause" ], "description": "Official version of pdepend to be handled with Composer", - "time": "2022-02-23T07:53:09+00:00" + "keywords": [ + "PHP Depend", + "PHP_Depend", + "dev", + "pdepend" + ], + "support": { + "issues": "https://github.com/pdepend/pdepend/issues", + "source": "https://github.com/pdepend/pdepend/tree/2.14.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/pdepend/pdepend", + "type": "tidelift" + } + ], + "time": "2023-05-26T13:15:18+00:00" }, { "name": "phpmd/phpmd", - "version": "2.12.0", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "c0b678ba71902f539c27c14332aa0ddcf14388ec" + "reference": "dad0228156856b3ad959992f9748514fa943f3e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/c0b678ba71902f539c27c14332aa0ddcf14388ec", - "reference": "c0b678ba71902f539c27c14332aa0ddcf14388ec", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/dad0228156856b3ad959992f9748514fa943f3e3", + "reference": "dad0228156856b3ad959992f9748514fa943f3e3", "shasum": "" }, "require": { "composer/xdebug-handler": "^1.0 || ^2.0 || ^3.0", "ext-xml": "*", - "pdepend/pdepend": "^2.10.3", + "pdepend/pdepend": "^2.12.1", "php": ">=5.3.9" }, "require-dev": { @@ -1997,20 +2221,31 @@ "phpmd", "pmd" ], - "time": "2022-03-24T13:33:01+00:00" + "support": { + "irc": "irc://irc.freenode.org/phpmd", + "issues": "https://github.com/phpmd/phpmd/issues", + "source": "https://github.com/phpmd/phpmd/tree/2.13.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/phpmd/phpmd", + "type": "tidelift" + } + ], + "time": "2022-09-10T08:44:15+00:00" }, { "name": "phpmetrics/phpmetrics", - "version": "v2.8.1", + "version": "v2.8.2", "source": { "type": "git", "url": "https://github.com/phpmetrics/PhpMetrics.git", - "reference": "e279f7317390f642339941b693359e9a181817a7" + "reference": "4b77140a11452e63c7a9b98e0648320bf6710090" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/e279f7317390f642339941b693359e9a181817a7", - "reference": "e279f7317390f642339941b693359e9a181817a7", + "url": "https://api.github.com/repos/phpmetrics/PhpMetrics/zipball/4b77140a11452e63c7a9b98e0648320bf6710090", + "reference": "4b77140a11452e63c7a9b98e0648320bf6710090", "shasum": "" }, "require": { @@ -2061,7 +2296,11 @@ "quality", "testing" ], - "time": "2022-03-24T10:19:51+00:00" + "support": { + "issues": "https://github.com/PhpMetrics/PhpMetrics/issues", + "source": "https://github.com/phpmetrics/PhpMetrics/tree/v2.8.2" + }, + "time": "2023-03-08T15:03:36+00:00" }, { "name": "phpunit/php-timer", @@ -2110,6 +2349,10 @@ "keywords": [ "timer" ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/master" + }, "time": "2017-02-26T11:10:40+00:00" }, { @@ -2159,6 +2402,10 @@ "container-interop", "psr" ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/master" + }, "time": "2017-02-14T16:28:37+00:00" }, { @@ -2206,6 +2453,9 @@ "psr", "psr-3" ], + "support": { + "source": "https://github.com/php-fig/log/tree/1.1.4" + }, "time": "2021-05-03T11:20:27+00:00" }, { @@ -2245,6 +2495,10 @@ ], "description": "FinderFacade is a convenience wrapper for Symfony's Finder component.", "homepage": "https://github.com/sebastianbergmann/finder-facade", + "support": { + "issues": "https://github.com/sebastianbergmann/finder-facade/issues", + "source": "https://github.com/sebastianbergmann/finder-facade/tree/master" + }, "abandoned": true, "time": "2017-11-18T17:31:49+00:00" }, @@ -2296,6 +2550,11 @@ ], "description": "Copy/Paste Detector (CPD) for PHP code.", "homepage": "https://github.com/sebastianbergmann/phpcpd", + "support": { + "issues": "https://github.com/sebastianbergmann/phpcpd/issues", + "source": "https://github.com/sebastianbergmann/phpcpd/tree/master" + }, + "abandoned": true, "time": "2017-11-16T08:49:28+00:00" }, { @@ -2339,6 +2598,10 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/master" + }, "time": "2016-10-03T07:35:21+00:00" }, { @@ -2390,6 +2653,11 @@ "phpcs", "standards" ], + "support": { + "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", + "source": "https://github.com/squizlabs/PHP_CodeSniffer", + "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" + }, "time": "2021-12-12T21:44:58+00:00" }, { @@ -2449,6 +2717,23 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/config/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-10-24T10:57:07+00:00" }, { @@ -2516,6 +2801,23 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/console/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-10-24T10:57:07+00:00" }, { @@ -2567,6 +2869,23 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/debug/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "abandoned": "symfony/error-handler", "time": "2020-10-24T10:57:07+00:00" }, @@ -2634,6 +2953,23 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dependency-injection/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-10-24T10:57:07+00:00" }, { @@ -2679,6 +3015,23 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-10-24T10:57:07+00:00" }, { @@ -2723,6 +3076,23 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v3.4.47" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-11-16T17:02:08+00:00" }, { @@ -2786,6 +3156,23 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.19.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], "time": "2020-10-23T09:01:57+00:00" }, { @@ -2829,6 +3216,10 @@ ], "description": "The classes contained within this repository extend the standard DOM to use exceptions at all occasions of errors instead of PHP warnings or notices. They also add various custom methods and shortcuts for convenience and to simplify the usage of DOM.", "homepage": "https://github.com/theseer/fDOMDocument", + "support": { + "issues": "https://github.com/theseer/fDOMDocument/issues", + "source": "https://github.com/theseer/fDOMDocument/tree/1.6.7" + }, "abandoned": true, "time": "2022-01-25T23:10:35+00:00" } @@ -2842,5 +3233,5 @@ "php": ">=5.6.40" }, "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } diff --git a/content/fas.xul.php b/content/fas.xul.php index 80efdb2f9..abe7f613e 100644 --- a/content/fas.xul.php +++ b/content/fas.xul.php @@ -140,6 +140,8 @@ foreach($addon_obj->result as $addon) + + @@ -507,19 +509,38 @@ foreach($addon_obj->result as $addon) label = "&menu-dokumente-zutrittskarte.label;" command = "menu-dokumente-zutrittskarte:command" accesskey = "&menu-dokumente-zutrittskarte.accesskey;"/> + + + + + + - + + accesskey = "&menu-dokumente-inskriptionsbestaetigungeng.accesskey;" + /> + + + + + + ` +} diff --git a/public/js/components/Bootstrap/Prompt.js b/public/js/components/Bootstrap/Prompt.js new file mode 100644 index 000000000..74e5c8b3f --- /dev/null +++ b/public/js/components/Bootstrap/Prompt.js @@ -0,0 +1,37 @@ + +import BsAlert from './Alert'; + +export default { + mixins: [ + BsAlert + ], + props: { + placeholder: String, + default: String + }, + data: () => ({ + value: '', + result: false + }), + created() { + if (this.default) + this.value = this.default; + }, + popup(msg, options) { + if (typeof options === 'string') + options = { default: options }; + return BsAlert.popup.bind(this)(msg, options); + }, + template: ` + + + ` +} diff --git a/public/js/components/Fetch.js b/public/js/components/Fetch.js index 32d5616d0..9a34e1a3f 100644 --- a/public/js/components/Fetch.js +++ b/public/js/components/Fetch.js @@ -53,7 +53,9 @@ export const CoreFetchCmpt = { * */ fetchData: function() { - this.loading = true; // loader started + this.loading = true; // loader started + this.error = false; + this.errorMessage = null; // Checks if the apifunction is a callable function if (typeof this.apiFunction == "function") @@ -91,13 +93,16 @@ export const CoreFetchCmpt = { * */ successHandler: function(response) { - this.$emit('dataFetched', response.data); // trigger the event dataFetched + this.$emit('dataFetched', response ? response.data : undefined); // trigger the event dataFetched }, /** * */ errorHandler: function(error) { - this.setError(error.message); + if (error.response.data.retval) + this.setError(error.response.data.retval); + else + this.setError(error.message); }, /** * @@ -107,12 +112,12 @@ export const CoreFetchCmpt = { } }, template: ` - +
Loading...
- +
{{ errorMessage }}
+ ` }; - diff --git a/public/js/components/Loader.js b/public/js/components/Loader.js new file mode 100644 index 000000000..ce4dee638 --- /dev/null +++ b/public/js/components/Loader.js @@ -0,0 +1,62 @@ +import BsModal from './Bootstrap/Modal.js'; + +export default { + components: { + BsModal + }, + props: { + timeout: { + type: Number, + default: 300 + } + }, + data() { + return { + t: null, + state: 0 + } + }, + methods: { + show() { + switch (this.state) { + case 0: + if (this.timeout) { + this.state = 1; + this.t = window.setTimeout(() => this.$refs.modal.show(), this.timeout); + return; + } else + return this.$refs.modal.show(); + case 4: + return window.setTimeout(() => this.show(), 1); + } + }, + hide() { + switch (this.state) { + case 1: + return window.clearTimeout(this.t); + case 2: + return window.setTimeout(() => this.hide(), 1); + case 3: + this.$refs.modal.hide(); + } + } + }, + mounted() { + this.$refs.modal.$refs.modal.addEventListener('show.bs.modal', () => { + this.state = 2; + }); + this.$refs.modal.$refs.modal.addEventListener('shown.bs.modal', () => { + this.state = 3; + }); + this.$refs.modal.$refs.modal.addEventListener('hide.bs.modal', () => { + this.state = 4; + }); + this.$refs.modal.$refs.modal.addEventListener('hidden.bs.modal', () => { + this.state = 0; + }); + }, + template: ` + + Loading... + ` +} \ No newline at end of file diff --git a/public/js/components/Studierendenantrag/Antrag.js b/public/js/components/Studierendenantrag/Antrag.js new file mode 100644 index 000000000..6619b74e7 --- /dev/null +++ b/public/js/components/Studierendenantrag/Antrag.js @@ -0,0 +1,60 @@ +import StudierendenantragAbmeldung from './Form/Abmeldung.js'; +import StudierendenantragUnterbrechung from './Form/Unterbrechung.js'; +import StudierendenantragWiederholung from './Form/Wiederholung.js'; +import Phrasen from '../../mixins/Phrasen.js'; + +export default { + components: { + StudierendenantragAbmeldung, + StudierendenantragUnterbrechung, + StudierendenantragWiederholung + }, + mixins: [ + Phrasen + ], + emits: [ + 'update:infoArray', + 'update:statusMsg', + 'update:statusSeverity' + ], + props: { + antragType: String, + prestudentId: Number, + studierendenantragId: Number, + infoArray: Array, + statusMsg: String, + statusSeverity: String + }, + data() { + return { + status: '' + }; + }, + computed: { + typeComponent() { + return 'Studierendenantrag' + this.antragType; + }, + infoText() { + return this.p.t('studierendenantrag/info_' + this.antragType + '_' + this.status); + } + }, + template: ` +
+
+ {{p.t('studierendenantrag', 'title_' + antragType)}} +
+ + + +
+ ` +} diff --git a/public/js/components/Studierendenantrag/Form/Abmeldung.js b/public/js/components/Studierendenantrag/Form/Abmeldung.js new file mode 100644 index 000000000..0de6e3d16 --- /dev/null +++ b/public/js/components/Studierendenantrag/Form/Abmeldung.js @@ -0,0 +1,285 @@ +import {CoreFetchCmpt} from '../../Fetch.js'; +import Phrasen from '../../../mixins/Phrasen.js'; + +var _uuid = 0; + +export default { + components: { + CoreFetchCmpt + }, + mixins: [ + Phrasen + ], + emits: [ + 'setInfos', + 'setStatus' + ], + props: { + prestudentId: Number + }, + data() { + return { + data: null, + saving: false, + errors: { + grund: [], + default: [] + } + } + }, + computed: { + statusSeverity() { + switch (this.data.status) + { + case 'Erstellt': return 'info'; + case 'Genehmigt': return 'success'; + default: return 'info'; + } + } + }, + methods: { + load() { + return axios.get( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Abmeldung/getDetailsForNewAntrag/' + + this.prestudentId + ).then( + result => { + this.data = result.data.retval; + if (this.data.status) { + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + } + return result; + } + ); + }, + createAntrag() { + bootstrap.Modal.getOrCreateInstance(this.$refs.modal).hide(); + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_saving')}), + severity: 'warning' + }); + this.saving = true; + for(var k in this.errors) + this.errors[k] = []; + axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Abmeldung/createAntrag/', { + studiensemester: this.data.studiensemester_kurzbz, + prestudent_id: this.data.prestudent_id, + grund: this.$refs.grund.value + } + ).then( + result => { + if (result.data.error) + { + for (var k in result.data.retval) + { + if (this.errors[k] !== undefined) + this.errors[k].push(result.data.retval[k]); + else + this.errors.default.push(result.data.retval[k]); + } + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_error')}), + severity: 'danger' + }); + } + else + { + if (result.data.retval === true) + document.location += ""; + this.data = result.data.retval; + if (this.data.status) { + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + } + else + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_open')}), + severity:'success' + }); + } + this.saving = false; + } + ); + }, + cancelAntrag() { + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_cancelling')}), + severity: 'warning' + }); + this.saving = true; + for(var k in this.errors) + this.errors[k] = []; + axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Abmeldung/cancelAntrag/', { + antrag_id: this.data.studierendenantrag_id + } + ).then( + result => { + if (result.data.error) + { + for (var k in result.data.retval) + { + if (this.errors[k] !== undefined) + this.errors[k].push(result.data.retval[k]); + else + this.errors.default.push(result.data.retval[k]); + } + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_error')}), + severity:'danger' + }); + } + else + { + if (Number.isInteger(result.data.retval)) { + document.location = document.location.replace(/abmeldung\/([0-9]*)\/[0-9]*[\/]?$/, 'abmeldung/$1') + "/" + result.data.retval; + } + this.data = result.data.retval; + if (this.data.status) { + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + } + else + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_cancelled')}), + severity: 'danger' + }); + } + this.saving = false; + } + ); + } + }, + created() { + this.uuid = _uuid++; + }, + template: ` +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{p.t('lehre', 'studiengang')}}{{data.bezeichnung}}
{{p.t('lehre', 'organisationsform')}}{{data.orgform_bezeichnung}}
{{p.t('projektarbeitsbeurteilung', 'nameStudierende')}}{{data.name}}
{{p.t('person', 'personenkennzeichen')}}{{data.matrikelnr}}
{{p.t('lehre', 'studienjahr')}}{{data.studienjahr_kurzbz}}
{{p.t('lehre', 'semester')}}{{data.semester}}
+
+
+
{{p.t('studierendenantrag', 'antrag_grund')}}:
+
{{data.grund}}
+
+
+ + +
+ {{errors.grund.join(".")}} +
+
+
+ + + + +
+
+ +
+
+ ` +} diff --git a/public/js/components/Studierendenantrag/Form/Unterbrechung.js b/public/js/components/Studierendenantrag/Form/Unterbrechung.js new file mode 100644 index 000000000..2034361bc --- /dev/null +++ b/public/js/components/Studierendenantrag/Form/Unterbrechung.js @@ -0,0 +1,359 @@ +import {CoreFetchCmpt} from '../../Fetch.js'; +import VueDatepicker from '../../vueDatepicker.js.php'; +import Phrasen from '../../../mixins/Phrasen.js'; + +var _uuid = 0; + +export default { + components: { + CoreFetchCmpt, + VueDatepicker + }, + mixins: [ + Phrasen + ], + emits: [ + 'setInfos', + 'setStatus' + ], + props: { + prestudentId: Number, + studierendenantragId: Number + }, + data() { + return { + data: null, + saving: false, + errors: { + grund: [], + studiensemester: [], + datum_wiedereinstieg: [], + default: [] + }, + stsem: null, + siteUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + } + }, + computed: { + statusSeverity() { + switch (this.data.status) + { + case 'Erstellt': return 'info'; + case 'Genehmigt': return 'success'; + case 'Zurückgezogen': return 'danger'; + default: return 'info'; + } + }, + loadUrl() { + if (this.studierendenantragId) + return '/components/Antrag/Unterbrechung/getDetailsForAntrag/'+ + this.studierendenantragId; + return '/components/Antrag/Unterbrechung/getDetailsForNewAntrag/' + + this.prestudentId; + }, + datumWsFormatted() { + if(!this.data.datum_wiedereinstieg) + return ''; + let datum = new Date(this.data.datum_wiedereinstieg); + return datum.toLocaleDateString(); + } + }, + methods: { + load() { + return axios.get( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + this.loadUrl + ).then( + result => { + this.data = result.data.retval; + if (this.data.status) { + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + } + return result; + } + ); + }, + createAntrag() { + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_saving')}), + severity: 'warning' + }); + this.saving = true; + for(var k in this.errors) + this.errors[k] = []; + + var formData = new FormData(); + var attachment = this.$refs.attachment; + formData.append("attachment", attachment.files[0]); + formData.append("studiensemester", this.stsem !== null && this.data.studiensemester[this.stsem].studiensemester_kurzbz); + formData.append("prestudent_id", this.data.prestudent_id); + formData.append("grund", this.$refs.grund.value); + formData.append("datum_wiedereinstieg", this.$refs.datum_wiedereinstieg && this.$refs.datum_wiedereinstieg.value); + + axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Unterbrechung/createAntrag/', + formData, + { + headers: { + 'Content-Type': 'multipart/form-data' + } + } + ).then( + result => { + if (result.data.error) + { + for (var k in result.data.retval) + { + if (this.errors[k] !== undefined) + this.errors[k].push(result.data.retval[k]); + else + this.errors.default.push(result.data.retval[k]); + } + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_error')}), + severity: 'danger' + }); + } + else + { + if (Number.isInteger(result.data.retval)) + document.location += "/" + result.data.retval; + this.data = result.data.retval; + if (this.data.status) { + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + } + else + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_created')}), + severity: 'info' + }); + } + this.saving = false; + } + ); + }, + cancelAntrag() { + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_cancelling')}), + severity: 'warning' + }); + this.saving = true; + for(var k in this.errors) + this.errors[k] = []; + axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Unterbrechung/cancelAntrag/', { + antrag_id: this.data.studierendenantrag_id + } + ).then( + result => { + if (result.data.error) + { + for (var k in result.data.retval) + { + if (this.errors[k] !== undefined) + this.errors[k].push(result.data.retval[k]); + else + this.errors.default.push(result.data.retval[k]); + } + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_error')}), + severity: 'danger' + }); + } + else + { + if (Number.isInteger(result.data.retval)) { + document.location = document.location.replace(/unterbrechung\/([0-9]*)\/[0-9]*[\/]?$/, 'unterbrechung/$1') + "/" + result.data.retval; + } + this.data = result.data.retval; + if (this.data.status) { + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + } + else + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t('studierendenantrag', 'status_cancelled')}), + severity: 'danger' + }); + } + this.saving = false; + } + ); + } + }, + created() { + this.uuid = _uuid++; + }, + template: ` +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{p.t('lehre', 'studiengang')}}{{data.bezeichnung}}
{{p.t('lehre', 'organisationsform')}}{{data.orgform_bezeichnung}}
{{p.t('projektarbeitsbeurteilung', 'nameStudierende')}}{{data.name}}
{{p.t('person', 'personenkennzeichen')}}{{data.matrikelnr}}
{{p.t('lehre', 'studienjahr')}}{{data.studienjahr_kurzbz}}{{stsem === null ? '' : data.studiensemester[stsem].studienjahr_kurzbz}}
{{p.t('lehre', 'semester')}}{{data.semester}}{{stsem === null ? '' : data.studiensemester[stsem].semester}}
+
+ +
+ +
+ {{data.studiensemester_kurzbz}} +
+
+ +
+ {{errors.studiensemester.join(".")}} +
+
+
+
+ + +
+ {{datumWsFormatted}} +
+
+ + +
+ +
+ {{errors.datum_wiedereinstieg.join(".")}} +
+
+
+
{{p.t('studierendenantrag', 'antrag_grund')}}:
+
{{data.grund}}
+
+
+ + +
+ {{errors.grund.join(".")}} +
+
+
+ +
+ {{p.t('studierendenantrag', 'antrag_dateianhaenge')}} + {{p.t('studierendenantrag', 'no_attachments')}} +
+
+ + +
+
+
+ + +
+
+ +
+
+ ` +} diff --git a/public/js/components/Studierendenantrag/Form/Wiederholung.js b/public/js/components/Studierendenantrag/Form/Wiederholung.js new file mode 100644 index 000000000..4cbc32d88 --- /dev/null +++ b/public/js/components/Studierendenantrag/Form/Wiederholung.js @@ -0,0 +1,212 @@ +import {CoreFetchCmpt} from '../../Fetch.js'; +import VueDatepicker from '../../vueDatepicker.js.php'; +import Phrasen from '../../../mixins/Phrasen.js'; + +var _uuid = 0; + +export default { + components: { + CoreFetchCmpt, + VueDatepicker + }, + mixins: [ + Phrasen + ], + emits: [ + 'setInfos', + 'setStatus', + 'update:status' + ], + props: { + status: String, + prestudentId: Number, + studierendenantragId: Number + }, + data() { + return { + data: null, + saving: false, + errors: { + grund: [], + default: [] + }, + siteUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router, + infos: [] + } + }, + computed: { + statusSeverity() { + switch (this.data.status) + { + case 'Erstellt': return 'info'; + case 'Genehmigt': return 'success'; + case 'Verzichtet': return 'danger'; + default: return 'info'; + } + }, + loadUrl() { + return '/components/Antrag/Wiederholung/getDetailsForNewAntrag/' + + this.prestudentId; + }, + datumPruefungFormatted() { + if(!this.data.pruefungsdatum) + return ''; + let datum = new Date(this.data.pruefungsdatum); + return datum.toLocaleDateString(); + } + }, + methods: { + load() { + return axios.get( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + this.loadUrl + ).then( + result => { + this.data = result.data.retval; + if (!this.data.status || this.data.status == 'ErsteAufforderungVersandt' || this.data.status == 'ZweiteAufforderungVersandt') + this.data.status = 'Offen'; + this.$emit('update:status', this.data.status); + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + return result; + } + ); + }, + createAntrag() { + this.createAntragWithStatus(true); + }, + cancelAntrag() { + this.createAntragWithStatus(false); + }, + createAntragWithStatus(repeat) { + let func = repeat ? 'createAntrag' : 'cancelAntrag'; + let nextState = repeat ? 'Erstellt' : 'Verzichtet'; + + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t_ref('studierendenantrag', 'status_saving')}), + severity: 'warning' + }); + this.saving = true; + for(var k in this.errors) + this.errors[k] = []; + + axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Wiederholung/' + func + '/', + { + prestudent_id: this.data.prestudent_id, + studiensemester: this.data.studiensemester_kurzbz + } + ).then( + result => { + if (result.data.error) + { + for (var k in result.data.retval) + { + if (this.errors[k] !== undefined) + this.errors[k].push(result.data.retval[k]); + else + this.errors.default.push(result.data.retval[k]); + } + this.$emit('setStatus', { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.p.t_ref('studierendenantrag', 'status_error')}), + severity: 'danger' + }); + } + else + { + if (result.data.retval === true) + document.location += ""; + this.data = result.data.retval; + if (!this.data.status) + this.data.status = nextState; + this.$emit('update:status', this.data.status); + this.$emit("setStatus", { + msg: this.p.t_ref('studierendenantrag', 'status_x', {status: this.data.statustyp}), + severity: this.statusSeverity + }); + } + this.saving = false; + } + ); + } + }, + created() { + this.uuid = _uuid++; + }, + mounted() { + this.infos = [...Array(5).keys()].map(n => ({ + body: this.p.t_ref('studierendenantrag', 'infotext_Wiederholung_' + n) + })); + this.$emit('setInfos', this.infos); + }, + template: ` +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
{{p.t('lehre', 'studiengang')}}{{data.bezeichnung}}
{{p.t('lehre', 'organisationsform')}}{{data.orgform_bezeichnung}}
{{p.t('projektarbeitsbeurteilung', 'nameStudierende')}}{{data.name}}
{{p.t('person', 'personenkennzeichen')}}{{data.matrikelnr}}
{{p.t('studierendenantrag', 'antrag_Wiederholung_pruefung')}}{{data.lvbezeichnung}}
{{p.t('studierendenantrag', 'antrag_Wiederholung_pruefung_date')}}{{datumPruefungFormatted}}
+
+ +
+ + +
+
+ +
+
+ ` +} diff --git a/public/js/components/Studierendenantrag/Infoblock.js b/public/js/components/Studierendenantrag/Infoblock.js new file mode 100644 index 000000000..59e3cce96 --- /dev/null +++ b/public/js/components/Studierendenantrag/Infoblock.js @@ -0,0 +1,19 @@ +export default { + props: { + infos: Array, + }, + template: ` +
+ +
+ ` +} diff --git a/public/js/components/Studierendenantrag/Leitung.js b/public/js/components/Studierendenantrag/Leitung.js new file mode 100644 index 000000000..f08da3879 --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung.js @@ -0,0 +1,312 @@ +import LeitungHeader from './Leitung/Header.js'; +import LeitungActions from './Leitung/Actions.js'; +import LeitungTable from './Leitung/Table.js'; +import GrundPopup from './Leitung/GrundPopup.js'; +import LvPopup from './Leitung/LvPopup.js'; +import BsAlert from '../Bootstrap/Alert.js'; +import FhcLoader from '../Loader.js'; +import Phrasen from '../../mixins/Phrasen.js'; + +export default { + components: { + LeitungHeader, + LeitungTable, + LeitungActions, + FhcLoader + }, + mixins: [Phrasen], + props: { + stgL: Array, + stgA: Array + }, + data() { + return { + filter: undefined, + selectedData: [], + columns: [] + } + }, + computed: { + stgs(){ + if(!this.stgL || !this.stgA) + return[]; + let undesiredOutput = [...this.stgL, ...this.stgA]; + let desiredOutput = undesiredOutput.reduce( + (accumulator, currentValue)=> + { + accumulator[currentValue.studiengang_kz] = currentValue; + return accumulator; + }, + {} + ); + return Object.values(desiredOutput); + + }, + stgkzL() + { + if (!this.stgL) + return []; + return this.stgL.map(stg => stg.studiengang_kz); + }, + stgkzA() + { + if (!this.stgA) + return []; + return this.stgA.map(stg => stg.studiengang_kz); + } + }, + methods: { + changeFilter(evt) { + this.filter = evt.target.value || undefined; + this.reload(); + }, + reload() { + this.$refs.table.reload(this.filter); + }, + download() { + this.$refs.table.download(); + }, + actionApprove(evt, oks) { + var antraege = evt || [...this.selectedData]; + + if (!oks) { + oks = []; + } + var currentAntrag = antraege.shift(); + if (currentAntrag) { + if (currentAntrag.typ != 'Wiederholung') + { + oks.push(currentAntrag); + this.actionApprove(antraege, oks); + } + else + { + let countAntrage = 0; + LvPopup + .popup(this.p.t('studierendenantrag','title_show_lvs', currentAntrag), { + antragId: currentAntrag.studierendenantrag_id, + footer: true, + dialogClass: 'modal-lg', + countRemaining : antraege.filter(antrag => antrag.typ == 'Wiederholung').length + }) + .then(result => { + if (result[0]) { + oks.push(currentAntrag); + if (result[1]) + while (antraege.length) + oks.push(antraege.pop()); + } else if (result[1]) { + while (antraege.length) { + currentAntrag = antraege.pop(); + if (currentAntrag.typ != 'Wiederholung') + oks.push(currentAntrag); + } + } + this.actionApprove(antraege, oks); + }) + .catch(() => {}); + } + } else { + this.$refs.loader.show(); + axios + .all( + oks.map( + antrag => axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/approve' + antrag.typ, + { + studierendenantrag_id: antrag.studierendenantrag_id + } + ) + ) + ) + .then(this.showValidation) + .catch(this.showError); + } + }, + actionReject(evt, gruende) { + var antraege = evt || this.selectedData; + if(!gruende) + { + gruende = []; + } + var currentAntrag = antraege.pop(); + if(currentAntrag) + GrundPopup.popup(this.p.t('studierendenantrag', 'title_grund', {id: currentAntrag.studierendenantrag_id})).then(result => + { + currentAntrag.grund = result[0]; + gruende.push(currentAntrag); + if(result[1]) + { + while (antraege.length) + { + currentAntrag = antraege.pop(); + currentAntrag.grund = result[0]; + gruende.push(currentAntrag); + } + } + this.actionReject(antraege, gruende); + }) + .catch(() => {}); + else + { + this.$refs.loader.show(); + axios + .all( + gruende.map( + antrag => axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/reject' + antrag.typ, + { + studierendenantrag_id: antrag.studierendenantrag_id, + grund: antrag.grund + } + ) + ) + ) + .then(this.showValidation) + .catch(this.showError); + } + }, + actionReopen(evt) { + var antraege = evt || this.selectedData; + this.$refs.loader.show(); + axios + .all( + antraege.map( + antrag => axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/reopenAntrag/', + { + studierendenantrag_id: antrag.studierendenantrag_id + } + ) + ) + ) + .then(this.showValidation) + .catch(this.showError); + }, + actionObject(evt) { + var antraege = evt || this.selectedData; + this.$refs.loader.show(); + axios + .all( + antraege.map( + antrag => axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/objectAntrag/', + { + studierendenantrag_id: antrag.studierendenantrag_id + } + ) + ) + ) + .then(this.showValidation) + .catch(this.showError); + }, + actionoObjectionDeny(evt, gruende) { + var antraege = evt || this.selectedData; + this.$refs.loader.show(); + axios + .all( + antraege.map( + antrag => axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/objectionDeny/', + { + studierendenantrag_id: antrag.studierendenantrag_id + } + ) + ) + ) + .then(this.showValidation) + .catch(this.showError); + }, + actionObjectionApprove(evt, gruende) { + var antraege = evt || this.selectedData; + this.$refs.loader.show(); + axios + .all( + antraege.map( + antrag => axios.post( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/objectionApprove/', + { + studierendenantrag_id: antrag.studierendenantrag_id + } + ) + ) + ) + .then(this.showValidation) + .catch(this.showError); + }, + showValidation(results) { + var errors = results.filter(res => res.data.error); + this.$refs.loader.hide(); + if (errors.length) { + let errorMsg = errors.map( + error => + 'Antrag ' + + JSON.parse(error.config.data).studierendenantrag_id + + '\n' + + Object.values(error.data.retval).join('\n') + ).join('\n'); + + BsAlert.popup(errorMsg, {dialogClass: 'alert alert-danger'}); + } + this.reload(); + }, + showError(error) { + this.$refs.loader.hide(); + let msg = error.response.data; + if (msg.replace(/^\s+/, '').substr(0, 9) == ' + + + + + + + + + + + + ` +} diff --git a/public/js/components/Studierendenantrag/Leitung/Actions.js b/public/js/components/Studierendenantrag/Leitung/Actions.js new file mode 100644 index 000000000..71ada5c20 --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung/Actions.js @@ -0,0 +1,87 @@ +import ActionsNew from './Actions/New.js'; +import ActionsColumns from './Actions/Columns.js'; +import Phrasen from '../../../mixins/Phrasen.js'; + +export default { + components: { + ActionsNew, + ActionsColumns + }, + mixins: [Phrasen], + props: { + selectedData: Array, + columns: Array, + stgL: Array, + stgA: Array + }, + emits: [ + 'reload', + 'download', + 'action:approve', + 'action:reject', + 'action:reopen' + ], + data() { + return { + currentStudent: '' + } + }, + computed: { + selectedCanBeApproved() { + if (!this.selectedData.length) + return false; + if (!this.selectedData.every(val => this.stgL.includes(val.studiengang_kz))) + return false; + return this.selectedData.filter(row => { + return (row.typ == 'Wiederholung' && row.status == 'Lvszugewiesen') || (row.typ != 'Wiederholung' && (row.status == 'Erstellt' || row.status == 'ErstelltStgl')); + }).length == this.selectedData.length; + }, + selectedCanBeRejected() { + if (!this.selectedData.length) + return false; + if (!this.selectedData.every(val => this.stgL.includes(val.studiengang_kz))) + return false; + return this.selectedData.filter(row => { + return (row.typ == 'Unterbrechung' && row.status == 'Erstellt'); + }).length == this.selectedData.length; + }, + selectedCanBeReopened() { + if (!this.selectedData.length) + return false; + if (!this.selectedData.every(val => this.stgA.includes(val.studiengang_kz))) + return false; + return this.selectedData.filter(row => { + return (row.typ == 'Wiederholung' && row.status == 'Verzichtet'); + }).length == this.selectedData.length; + }, + newUrl() { + return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + this.currentStudent; + } + }, + methods: { + hideModal() { + bootstrap.Modal.getInstance(this.$refs.modal).hide(); + } + }, + template: ` +
+
+ + + {{p.t('table', 'with_selected', {count: selectedData.length})}} + + + +
+
+ + +
+
+ +
+
+ ` +} diff --git a/public/js/components/Studierendenantrag/Leitung/Actions/Columns.js b/public/js/components/Studierendenantrag/Leitung/Actions/Columns.js new file mode 100644 index 000000000..df2b2a0de --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung/Actions/Columns.js @@ -0,0 +1,25 @@ +export default { + props: { + columns: Array + }, + methods: { + toggleColumn(col) { + col.visible = !col.visible; + col.original.toggle() + }, + show() { + + } + }, + template: ` + + ` +} \ No newline at end of file diff --git a/public/js/components/Studierendenantrag/Leitung/Actions/New.js b/public/js/components/Studierendenantrag/Leitung/Actions/New.js new file mode 100644 index 000000000..8a98a6aa5 --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung/Actions/New.js @@ -0,0 +1,118 @@ +import BsAlert from '../../../Bootstrap/Alert.js'; +import BsModal from '../../../Bootstrap/Modal.js'; +import Phrasen from '../../../../mixins/Phrasen.js'; + +export default { + components: { + BsModal + }, + mixins: [ + Phrasen + ], + emits: [ + 'reload' + ], + data() { + return { + data: [], + student: '', + stg: '' + } + }, + computed: { + newUrl() { + return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/lehre/Studierendenantrag/abmeldung/' + this.student; + }, + students() { + if (!this.stg) + return []; + if (!this.data[this.stg]) + return []; + return this.data[this.stg].studenten.sort( + (a, b) => a.nachname == b.nachname ? + a.vorname > b.vorname : + a.nachname > b.nachname + ); + }, + hasNoData() { + return !Object.values(this.data).length; + } + }, + methods: { + openForm() { + bootstrap.Modal.getInstance(this.$refs.modal).hide(); + BsModal.popup(Vue.h('iframe', { + src: this.newUrl, + class: 'position-absolute top-0 start-0 w-100 h-100' + }), { + dialogClass: 'modal-fullscreen' + }, this.p.t('studierendenantrag', 'antrag_header')).then(() => { + this.loadSelects(); + this.$emit('reload'); + }); + }, + loadSelects() { + return axios.get( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Abmeldung/getStudiengaengeAssistenz/' + ).then( + result => { + if (result.data.error) { + BsAlert.popup(result.data.retval, {dialogClass: 'alert alert-danger'}); + } else { + this.data = result.data.retval; + } + return result; + } + ); + } + }, + created() { + return this.loadSelects(); + }, + template: ` +
+ + +
+ ` +} diff --git a/public/js/components/Studierendenantrag/Leitung/GrundPopup.js b/public/js/components/Studierendenantrag/Leitung/GrundPopup.js new file mode 100644 index 000000000..d560dfb00 --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung/GrundPopup.js @@ -0,0 +1,62 @@ + +import BsAlert from '../../Bootstrap/Alert.js'; +import Phrasen from '../../../mixins/Phrasen.js'; + +export default { + mixins: [ + BsAlert, + Phrasen + ], + props: { + placeholder: String, + default: String + }, + data: () => ({ + value: '', + result: false, + check: false, + isInvalid: false + }), + methods: { + submit(){ + if(!this.value) { + this.isInvalid = true; + } + else { + this.result = [this.value, this.check]; + this.hide(); + } + return + } + }, + created() { + if (this.default) + this.value = this.default; + }, + popup(msg, options) { + if (typeof options === 'string') + options = { default: options }; + return BsAlert.popup.bind(this)(msg, options); + }, + template: ` + + + + ` +} diff --git a/public/js/components/Studierendenantrag/Leitung/Header.js b/public/js/components/Studierendenantrag/Leitung/Header.js new file mode 100644 index 000000000..388a93b7d --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung/Header.js @@ -0,0 +1,24 @@ +import Phrasen from '../../../mixins/Phrasen.js'; + +export default { + mixins: [Phrasen], + props: { + stgs: Array + }, + emits: [ + 'input' + ], + template: ` +
+

{{p.t('studierendenantrag', 'studierendenantraege')}}

+
+ +
+
+ ` +} diff --git a/public/js/components/Studierendenantrag/Leitung/LvPopup.js b/public/js/components/Studierendenantrag/Leitung/LvPopup.js new file mode 100644 index 000000000..045e98729 --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung/LvPopup.js @@ -0,0 +1,142 @@ +import BsAlert from '../../Bootstrap/Alert.js'; +import {CoreFetchCmpt} from "../../Fetch.js"; +import Phrasen from '../../../mixins/Phrasen.js'; + +export default { + components: { + CoreFetchCmpt + }, + mixins: [ + BsAlert, + Phrasen + ], + props: { + footer: Boolean, + antragId: Number, + countRemaining: Number + }, + data: () => ({ + lvs: null, + refresh: true, + result: false, + check: false + }), + computed: { + lvzugelassen() { + let zwischen = {}; + for (let k in this.lvs){ + zwischen[k] = this.lvs[k].filter(lv=>lv.antrag_zugelassen); + } + return zwischen; + }, + lvzugelassenLength() { + return Object.values(this.lvzugelassen).reduce((result, current) => result + current.length, 0); + } + }, + methods: { + setlvs(param) { + if(param.error) + { + this.$refs.fetchCompt.error = true; + this.$refs.fetchCompt.errorMessage = param.retval; + } + else + this.lvs = param.retval; + }, + loadlvs() { + if (!this.antragId) + return new Promise(() => {}); + return axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Antrag/Wiederholung/getLvs/' + this.antragId); + }, + submit(result) { + this.result = [result, this.check]; + this.hide(); + } + }, + watch: { + antragId() { + Vue.nextTick(() => { + this.refresh = !this.refresh; + }); + } + }, + popup(msg, options) { + if (typeof options === 'string') + options = { default: options }; + return BsAlert.popup.bind(this)(msg, options); + }, + template: ` + + + + ` +} diff --git a/public/js/components/Studierendenantrag/Leitung/Table.js b/public/js/components/Studierendenantrag/Leitung/Table.js new file mode 100644 index 000000000..309678ae1 --- /dev/null +++ b/public/js/components/Studierendenantrag/Leitung/Table.js @@ -0,0 +1,388 @@ +import BsModal from '../../Bootstrap/Modal.js'; +import {CoreFetchCmpt} from '../../Fetch.js'; +import LvPopup from './LvPopup.js'; +import Phrasen from '../../../mixins/Phrasen.js'; +import { dateFilter } from '../../../tabulator/filters/Dates.js'; + +export default { + components: { + BsModal, + CoreFetchCmpt, + LvPopup + }, + mixins: [Phrasen], + props: { + selectedData: Array, + columnData: Array, + stgL: Array, + stgA: Array + }, + emits: [ + 'update:columnData', + 'update:selectedData', + 'action:approve', + 'action:reject', + 'action:reopen', + 'action:object', + 'action:objectionDeny', + 'action:objectionApprove' + ], + data() { + return { + ajaxUrl: FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/getAntraege/', + table: null, + lastHistoryClickedId: null, + historyData: [], + lvsData: null + } + }, + methods: { + reload(stg) { + this.table.replaceData(this.ajaxUrl + (stg || '')); + }, + download() { + this.table.download("csv", "data.csv"); + }, + getHistory() { + if (this.lastHistoryClickedId === null) + return null; + return axios.get( + FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/components/Antrag/Leitung/getHistory/' + + this.lastHistoryClickedId + ).then(res => { + this.historyData = res.data.retval.sort((a, b) => a.insertamum > b.insertamum); + }); + }, + showHistoryGrund(grund) { + this.$refs.modalGrund.$el.addEventListener( + 'hidden.bs.modal', + this.$refs.history.show, + { + once: true + } + ); + this.$refs.modalGrundPre.innerHTML = grund; + }, + showLVs(data) { + this.lvsData = data; + this.$refs.lvList.show(); + } + }, + mounted() { + function dateFormatter (cell) { + let val = cell.getValue(); + if (!val) + return ''; + let date = new Date(val); + return date.toLocaleDateString(); + } + + this.table = new Tabulator(this.$refs.table, { + placeholder:"Keine zu bearbeitenden Datensätze", + movableColumns: true, + maxHeight: '50vh', + layout: "fitDataStretch", // TODO(chris): wont work when changed + ajaxURL: this.ajaxUrl, + persistence: { // NOTE(chris): do not store column titles + sort: true, //persist column sorting + filter: true, //persist filters + headerFilter: true, //persist header filters + group: true, //persist row grouping + page: true, //persist page + columns: ["width", "visible"], //persist columns + }, + persistenceID: 'studierendenantrag_leitung', + columns: [ + { + formatter: 'rowSelection', + titleFormatter: 'rowSelection', + titleFormatterParams: { + rowRange: 'active' + }, + hozAlign: 'center', + headerSort: false + }, + { + field: 'studierendenantrag_id', + title: '#' + }, + { + field: 'bezeichnung', + title: this.p.t('lehre', 'studiengang'), + headerFilter: 'list', + headerFilterParams: { + valuesLookup: true, + clearable: true, + autocomplete: true, + } + }, + { + field: 'orgform', + title: this.p.t('lehre', 'organisationsform'), + headerFilter: 'list', + headerFilterParams: { + valuesLookup: true, + clearable: true, + autocomplete: true, + } + }, + { + field: 'typ', + title: this.p.t('studierendenantrag', 'antrag_typ'), + headerFilter: 'list', + headerFilterParams: { + valuesLookup: true, + clearable: true, + autocomplete: true, + }, + formatter: (cell, formatterParams, onRendered) => { + return this.p.t('studierendenantrag','antrag_typ_' + cell.getValue()); + } + }, + { + field: 'statustyp', + title: this.p.t('studierendenantrag', 'antrag_status'), + headerFilter: 'list', + headerFilterParams: { + valuesLookup: true, + clearable: true, + autocomplete: true, + } + }, + { + field: 'matrikelnr', + title: this.p.t('person', 'personenkennzeichen'), + headerFilter: 'input' + }, + { + field: 'prestudent_id', + title: this.p.t('lehre', 'prestudent'), + headerFilter: 'input' + }, + { + field: 'name', + title: this.p.t('global', 'name'), + mutator: (value, data) => (data.vorname + ' ' + data.nachname).replace(/^\s*(.*)\s*$/, '$1'), + headerFilter: 'input' + }, + { + field: 'datum', + title: this.p.t('global', 'datum'), + formatter: dateFormatter, + headerFilterFunc: 'dates', + headerFilter: dateFilter + }, + { + field: 'datum_wiedereinstieg', + title: this.p.t('studierendenantrag', 'antrag_datum_wiedereinstieg'), + formatter: dateFormatter, + headerFilterFunc: 'dates', + headerFilter: dateFilter + }, + { + field: 'grund', + title: this.p.t('studierendenantrag', 'antrag_grund'), + formatter: (cell, formatterParams, onRendered) => { + let link = document.createElement('a'), + val = cell.getValue(); + link.href = "#modal-grund"; + link.setAttribute('data-bs-toggle', 'modal'); + link.innerHTML = this.p.t('studierendenantrag', 'antrag_grund'); + link.addEventListener('click', () => { + this.$refs.modalGrundPre.innerHTML = val; + }); + + return val ? link : ''; + } + }, + { + field: 'dms_id', + title: this.p.t('studierendenantrag', 'antrag_dateianhaenge'), + formatter: (cell, formatterParams, onRendered) => { + let val = cell.getValue(); + if (!val) + return ''; + return ' ' + this.p.t('studierendenantrag', 'antrag_anhang') + ''; + } + }, + { + field: 'actions', + formatter: (cell, formatterParams, onRendered) => { + let container = document.createElement('div'), + data = cell.getData(); + + container.className = "d-flex gap-2"; + + if((data.typ == 'Abmeldung' || data.typ == 'Unterbrechung') && (data.status == 'Genehmigt')) + { + // NOTE(chris): Download PDF + let button = document.createElement('a'); + button.innerHTML = ''; + button.className = "btn btn-outline-secondary"; + button.target = "_blank"; + button.href = FHC_JS_DATA_STORAGE_OBJECT.app_root + + 'content/pdfExport.php?xml=Antrag' + data.typ + '.xml.php&xsl=Antrag' + data.typ + '&id='+ data.studierendenantrag_id +'&output=pdf'; + container.append(button); + } + if(data.typ == 'Abmeldung' && data.status == 'GenehmigtStgl') + { + // NOTE(chris): Object + let button = document.createElement('button'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_object'); + button.className = "btn btn-outline-secondary"; + button.addEventListener('click',() => this.$emit('action:object', [cell.getData()])); + container.append(button); + } + + if(data.typ == 'Abmeldung' && data.status == 'Beeinsprucht') + { + // NOTE(chris): Deny Objection + let button = document.createElement('button'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_objection_deny'); + button.className = "btn btn-outline-secondary"; + button.addEventListener('click',() => this.$emit('action:objectionDeny', [cell.getData()])); + container.append(button); + + // NOTE(chris): Approve Objection + button = document.createElement('button'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_objection_approve'); + button.className = "btn btn-outline-secondary"; + button.addEventListener('click',() => this.$emit('action:objectionApprove', [cell.getData()])); + container.append(button); + } + + if (this.stgA.includes(data.studiengang_kz)) + { + // NOTE(chris): Reopen + if (data.typ == 'Wiederholung' && data.status == 'Verzichtet') { + let button = document.createElement('button'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_reopen'); + button.className = "btn btn-outline-secondary"; + button.addEventListener('click',() => this.$emit('action:reopen', [cell.getData()])); + container.append(button); + } + // NOTE(chris): Lv Zuweisen + if (data.typ == 'Wiederholung' && (data.status == 'Erstellt' || data.status == 'Lvszugewiesen')) { + let button = document.createElement('a'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_lvzuweisen'); + button.className = "btn btn-outline-secondary"; + button.href = FHC_JS_DATA_STORAGE_OBJECT.app_root + + FHC_JS_DATA_STORAGE_OBJECT.ci_router + + '/lehre/Antrag/Wiederholung/assistenz/' + + cell.getData().studierendenantrag_id + '/frame'; + button.onclick = e => { + e.preventDefault(); + BsModal.popup(Vue.h('iframe', { + src: button.href, + class: 'position-absolute top-0 start-0 w-100 h-100' + }), { + dialogClass: 'modal-fullscreen' + }, this.p.t('studierendenantrag', 'title_lvzuweisen', cell.getData())).then(() => { + this.$emit('reload'); + }); + }; + container.append(button); + } + } + + if (this.stgL.includes(data.studiengang_kz)) + { + // NOTE(chris): Approve + if ((data.typ == 'Wiederholung' && data.status == 'Lvszugewiesen') || (data.typ != 'Wiederholung' && (data.status == 'Erstellt' || data.status == 'ErstelltStgl'))) { + let button = document.createElement('button'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_approve'); + button.className = "btn btn-outline-secondary"; + button.addEventListener('click', () => this.$emit('action:approve', [cell.getData()])); + container.append(button); + } + // NOTE(chris): Reject (Unterbrechung braucht grund) + if (data.status == 'Erstellt' && data.typ == 'Unterbrechung') { + let button = document.createElement('button'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_reject'); + button.className = "btn btn-outline-secondary"; + button.addEventListener('click', () => this.$emit('action:reject', [cell.getData()])); + container.append(button); + } + } + + // NOTE(chris): Show LVs + if (data.typ == 'Wiederholung' && (data.status == 'Lvszugewiesen' || data.status == 'Genehmigt')) { + let button = document.createElement('button'); + button.innerHTML = this.p.t('studierendenantrag', 'btn_show_lvs'); + button.className = "btn btn-outline-secondary"; + button.addEventListener('click', () => this.showLVs(cell.getData())); + container.append(button); + } + + // TODO(chris): not yet perfect + onRendered(() => { + cell.getColumn().setWidth(true); + }); + + return container; + } + } + ] + }); + this.table.on("tableBuilt", () => { + let columns = this.table.getColumns(); + let columnData = []; + for (let col of columns) { + let def = col.getDefinition(); + if (def.title) { + columnData.push({ + title: def.title, + visible: col.isVisible(), + original: col + }); + } + } + this.$emit('update:columnData', columnData); + }); + this.table.on("rowSelectionChanged", data => { + this.$emit('update:selectedData', data); + }); + this.table.on("cellClick", (e, cell) => { + if (cell.getColumn().getField() == 'statustyp') { + this.lastHistoryClickedId = cell.getData().studierendenantrag_id; + this.$refs.historyLoader.fetchData(); + this.$refs.history.show(); + } + }); + }, + template: ` +
+
+ + +

+		
+ + + + + + + + + + +
{{(new Date(status.insertamum)).toLocaleString()}}{{status.insertvon}}{{status.typ}} + + {{p.t('studierendenantrag', 'antrag_grund')}} + +
+
+
+ + {{p.t('studierendenantrag', 'title_show_lvs', lvsData ? lvsData : {name: ''}) }} + +
+ ` +} diff --git a/public/js/components/Studierendenantrag/Lvzuweisung.js b/public/js/components/Studierendenantrag/Lvzuweisung.js new file mode 100644 index 000000000..cbc97b79e --- /dev/null +++ b/public/js/components/Studierendenantrag/Lvzuweisung.js @@ -0,0 +1,242 @@ +import StudierendenantragStatus from './Status.js'; +import Phrasen from '../../mixins/Phrasen.js'; + +export default { + components: { + StudierendenantragStatus + }, + mixins: [Phrasen], + props: { + antragId: Number, + initialStatusCode: String, + initialStatusMsg: String, + disabled: Boolean + }, + data() { + return { + lvs: [], + isloading: false, + statusCode: '', + statusMsg: '' + } + }, + computed: { + lvs1() { + return this.lvs[Object.keys(this.lvs).filter(key => key.substr(0, 1) == 1)] || []; + }, + lvs2() { + return this.lvs[Object.keys(this.lvs).filter(key => key.substr(0, 1) == 2)] || []; + }, + lvs1sem(){ + return (Object.keys(this.lvs).filter(key => key.substr(0, 1) == 1).pop() || "1").substr(1); + }, + lvs2sem(){ + return (Object.keys(this.lvs).filter(key => key.substr(0, 1) == 2).pop() || "2").substr(1); + }, + statusSeverity() { + switch (this.statusCode) { + case 0: return 'danger'; + default: return 'info'; + } + } + }, + methods: { + save() { + this.isloading = true; + const forbiddenLvs = this.lvs1.filter(lv => lv.antrag_zugelassen && !lv._children).map(lv => ({ + studierendenantrag_id: this.antragId, + lehrveranstaltung_id: lv.lehrveranstaltung_id, + zugelassen: 0, + anmerkung: lv.antrag_anmerkung || "", + studiensemester_kurzbz: this.lvs1sem + })); + const mandatoryLvs = this.lvs2.filter(lv => !lv._children).map(lv => ({ + studierendenantrag_id: this.antragId, + lehrveranstaltung_id: lv.lehrveranstaltung_id, + zugelassen:lv.antrag_zugelassen ? 1 : 2, + anmerkung: lv.antrag_anmerkung || "", + studiensemester_kurzbz: this.lvs2sem + })); + axios.post(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Antrag/Wiederholung/saveLvs/', {forbiddenLvs, mandatoryLvs}) + .then(response => { + if(!response.data.error) { + this.addAlert('Speichern erfolgreich', 'alert-success'); + this.statusCode = response.data.retval[0].studierendenantrag_statustyp_kurzbz; + this.statusMsg = response.data.retval[0].typ; + } else { + this.addAlert(response.data.retval, 'alert-danger'); + this.statusCode = 0; + this.statusMsg = 'Error'; + } + this.isloading = false; + }).catch(error => { + this.addAlert(error.message, 'alert-danger'); + this.statusCode = 0; + this.statusMsg = 'Error'; + this.isloading = false; + }).finally(() => { + window.scrollTo(0, 0); + }); + }, + addAlert(text, type) { + const para = document.createElement("p"); + para.innerText = text; + para.className = "alert " + type + " alert-dismissible fade show"; + const btn = document.createElement("button"); + btn.className = "btn-close"; + btn.type = "button"; + btn.setAttribute("aria-label", "Close"); + btn.setAttribute("data-bs-dismiss", "alert"); + para.appendChild(btn); + + this.$refs.alertbox.appendChild(para); + } + }, + created() { + this.statusCode = this.initialStatusCode; + this.statusMsg = this.initialStatusMsg; + }, + mounted() { + axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Antrag/Wiederholung/getLvs/' + this.antragId).then( + result => { + if(result.data.error) + { + this.addAlert(result.data.retval, 'alert-danger'); + this.isloading = true; + } + else + { + let res = {}; + for (var k in result.data.retval) { + let lvs = result.data.retval[k].reduce((obj,lv) => { + obj[lv.studienplan_lehrveranstaltung_id] = lv; + return obj; + }, {}); + for (var lv of Object.values(lvs)) { + if (!lv.studienplan_lehrveranstaltung_id_parent) + continue; + if (!lvs[lv.studienplan_lehrveranstaltung_id_parent]) + console.error('parent not available'); + else { + if (!lvs[lv.studienplan_lehrveranstaltung_id_parent]._children) + lvs[lv.studienplan_lehrveranstaltung_id_parent]._children = []; + lvs[lv.studienplan_lehrveranstaltung_id_parent]._children.push(lv); + } + } + res[k] = Object.values(lvs).filter(lv => !lv.studienplan_lehrveranstaltung_id_parent); + let current = res[k]; + let index = k.substr(0,1); + var table = new Tabulator(this.$refs["lvtable" + k.substr(0,1)], { + data: current, + dataTree: true, + dataTreeStartExpanded:true, //start with an expanded tree + dataTreeChildIndent:15, + columns: [ + {title: this.p.t('ui','bezeichnung'), field: "bezeichnung"}, + {title: this.p.t('lehre','lehrform'), field: "lehrform_kurzbz"}, + {title: "ECTS", field: "ects"}, + {title: this.p.t('lehre','note'), field: "note", formatter:(cell, formatterParams, onRendered)=>cell.getValue() || "---"}, + {title: (index==1) ? this.p.t('studierendenantrag','lv_nicht_zulassen') : this.p.t('studierendenantrag','lv_wiederholen'), field: "antrag_zugelassen", formatter: (cell, formatterParams, onRendered) => { + let data = cell.getData(); + if(data._children || !data.zeugnis) + return ""; + let input = document.createElement('input'); + input.className = "form-check-input"; + input.type = "checkbox"; + input.role = "switch"; + input.checked = cell.getValue(); + input.addEventListener('input', () => { + lvs[data.studienplan_lehrveranstaltung_id].antrag_zugelassen = input.checked; + cell.getRow().reformat(); + }); + if (this.disabled) { + input.disabled = true; + } + + let div = document.createElement('div'); + div.className = 'form-check form-switch'; + div.append(input); + + return div; + }}, + { + title: this.p.t('global','anmerkung'), + field: "antrag_anmerkung", + headerSort:false, + titleFormatter:(cell, formatterParams, onRendered)=>{ + let link = document.createElement('a'); + link.addEventListener('click', (e) => { + e.preventDefault(); + }); + + link.href ="#"; + link.title = this.p.t('studierendenantrag','anmerkung_tooltip'); + new bootstrap.Tooltip(link); + let tooltip = document.createElement('span'); + tooltip.innerHTML = this.p.t('global','anmerkung') + " "; + tooltip.append(link); + + let icon = document.createElement('i'); + link.append(icon); + icon.className = "fa fa-info-circle"; + icon.setAttribute("aria-hidden", "true"); + icon.style.minWidth = '1em'; + + return tooltip; + + }, + formatter: (cell, formatterParams, onRendered) => { + if (this.disabled) { + return cell.getValue() || ""; + } + var data = cell.getData(); + if (lvs[data.studienplan_lehrveranstaltung_id].antrag_zugelassen) + { + let input = document.createElement('input'); + input.className = "form-control"; + input.type = "text"; + input.value = cell.getValue() || ""; + input.addEventListener('input', () => { + lvs[data.studienplan_lehrveranstaltung_id].antrag_zugelassen = input.value; + }); + return input; + } + else + { + return ""; + } + } + } + ] + }); + } + this.lvs = result.data.retval; + } + } + ); + }, + template: ` +
+
+ + + {{p.t('studierendenantrag', 'title_lv_nicht_zugelassen')}} + {{lvs1sem}} + +
+
+ + + {{p.t('studierendenantrag', 'title_lv_wiederholen')}} + {{lvs2sem}} + +
+
+ + +
+
+ +
+ ` +} diff --git a/public/js/components/Studierendenantrag/Status.js b/public/js/components/Studierendenantrag/Status.js new file mode 100644 index 000000000..7449fd376 --- /dev/null +++ b/public/js/components/Studierendenantrag/Status.js @@ -0,0 +1,15 @@ +export default { + props: { + msg: String, + severity: String + }, + computed: { + severityClass() { + return 'alert-' + this.severity; + } + }, + template: ` + + ` +} diff --git a/public/js/components/vueDatepicker.js.php b/public/js/components/vueDatepicker.js.php new file mode 100644 index 000000000..e0814ac53 --- /dev/null +++ b/public/js/components/vueDatepicker.js.php @@ -0,0 +1,12 @@ + e.category == category).reduce((res, elem) => { + if (!res[elem.phrase]) + res[elem.phrase] = elem.text; + return res; + }, {}); +} +function reloadRefs(category) { + while (loadingModules[category].length) { + var v = loadingModules[category].pop(); + v[0].value = getValueForLoadedPhrase(category, v[1], v[2]); + Vue.triggerRef(v[0]); + /*Vue.unref(v);*/ + } +} +function loadLazy(category, val, phrase, params) { + // NOTE(chris): load module if it's not loaded yet + if (loadingModules[category]) { + loadingModules[category].push([val, phrase, params]); + if (categories[category]) // NOTE(chris): this is for safety in case the loading finished the moment before the val was pushed into the array + reloadRefs(category); + return; + } + loadingModules[category] = [[val, phrase, params]]; + + axios.get(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/components/Phrasen/loadModule/' + category).then(res => { + if (res.data.retval) + categories[category] = extractCategory(res.data.retval, category); + else + categories[category] = {}; + + reloadRefs(category); + }).catch(err => console.error(err)); +} +function getValueForLoadedPhrase(category, phrase, params) { + let result = categories[category][phrase]; + if (!result) + return '<< PHRASE ' + phrase + '>>'; + if (params) + result = result.replace(/\{([^}]*)\}/g, (match, p1) => params[p1] === undefined ? match : params[p1]); + return result; +} + +const phrasen = { + t_ref(category, phrase, params) { + if (params === undefined && ( + (Array.isArray(category) && category.length == 2) || + (category.split && category.split('/').length == 2)) + ) { + params = phrase; + [category, phrase] = category.split ? category.split('/') : category; + } + if (phrase === undefined) { + console.error('invalid input'); + return ''; + } + if (!categories[category]) { + if (window.FHC_JS_PHRASES_STORAGE_OBJECT !== undefined) + categories[category] = extractCategory(FHC_JS_PHRASES_STORAGE_OBJECT, category); + + if (!categories[category] || Object.keys(categories[category]).length === 0) { + categories[category] = undefined; + let val = Vue.ref(''); + loadLazy(category, val, phrase, params); + return val; + } + } + var result = getValueForLoadedPhrase(category, phrase, params); + return Vue.ref(result); + }, + t(category, phrase, params) { + return Vue.unref(this.t_ref(category, phrase, params)); + } +}; + +export default { + data: () => { + return { + p: phrasen + } + } +} diff --git a/public/js/tabulator/filters/Dates.js b/public/js/tabulator/filters/Dates.js new file mode 100644 index 000000000..7c9f268f0 --- /dev/null +++ b/public/js/tabulator/filters/Dates.js @@ -0,0 +1,43 @@ +if (!primevue) { + console.error('PrimeVue not loaded!'); +} + +// NOTE(chris): Click on clear button gives an error. This is a bug in primevue => fixed in current version +Tabulator.extendModule('filter', 'filters', { + "dates": (headerValue, rowValue) => { + if (!headerValue) + return true; + let v = new Date(rowValue); + if (Array.isArray(headerValue)) { + if (headerValue[1]) { + return v >= headerValue[0] && v <= headerValue[1].setHours(23, 59, 59, 999); + } + return v.toDateString() == headerValue[0].toDateString(); + } + return v.toDateString() == headerValue.toDateString(); + } +}); +function dateFilter(cell, onRendered, success) { + let div = document.createElement('div'); + + Vue.createApp({ + components: { + PrimevueCalendar: primevue.calendar + }, + data() { + return { + val: null + } + }, + watch: { + val(n) { + success(n); + } + }, + template: `` + }).use(primevue.config.default).mount(div); + + return div; +} + +export { dateFilter as 'dateFilter' }; diff --git a/rdf/AntragAbmeldung.xml.php b/rdf/AntragAbmeldung.xml.php new file mode 100644 index 000000000..b89fa51df --- /dev/null +++ b/rdf/AntragAbmeldung.xml.php @@ -0,0 +1,68 @@ +db_add_param($id) . " + AND campus.tbl_studierendenantrag.typ = 'Abmeldung' AND campus.get_status_studierendenantrag(campus.tbl_studierendenantrag.studierendenantrag_id) = 'Genehmigt';"; + $not_found_error = 'Studierendenantrag not found'. $id; + } elseif(isset($_GET['uid']) && isset($_GET['prestudent_id'])) { + $uid = $_GET['uid']; + $uid = explode(';', $uid); + $uid = (array_filter($uid, 'strlen')); + + $prestudent_id = $_GET['prestudent_id']; + $prestudent_id = explode(';', $prestudent_id); + $prestudent_id = (array_filter($prestudent_id, 'strlen')); + + $where = " WHERE campus.tbl_studierendenantrag.prestudent_id in (" . $db->db_implode4SQL($prestudent_id) . ") + AND campus.tbl_studierendenantrag.typ = 'Abmeldung' AND campus.get_status_studierendenantrag(campus.tbl_studierendenantrag.studierendenantrag_id) = 'Genehmigt';"; + $not_found_error = 'Studierendenantrag not found for: ' . implode(',', $uid); + } else + die('wrong parameters'); +} +else + die('Format not supported'); + + +// TODO(chris): mehrsprachig +$query = " + SELECT tbl_studiengang.bezeichnung, bezeichnung_mehrsprachig[1], studierendenantrag_id, matrikelnr, studienjahr_kurzbz, vorname, nachname, studiengang_kz, semester, tbl_studierendenantrag.grund + FROM + campus.tbl_studierendenantrag + JOIN public.tbl_student USING (prestudent_id) + JOIN public.tbl_benutzer ON tbl_student.student_uid=uid + JOIN public.tbl_person USING (person_id) + JOIN public.tbl_studiengang USING (studiengang_kz) + JOIN public.tbl_studiensemester USING (studiensemester_kurzbz) + JOIN bis.tbl_orgform ON (tbl_orgform.orgform_kurzbz = tbl_studiengang.orgform_kurzbz)" . $where; + + +if (!$db->db_query($query) || !$db->db_num_rows()) + die('' . $not_found_error . ''); + +?> + + + db_fetch_object()) { ?> + + bezeichnung;?> + bezeichnung_mehrsprachig;?> + vorname. " " . $row->nachname);?> + matrikelnr;?> + studienjahr_kurzbz;?> + semester;?> + grund;?> + + + diff --git a/rdf/AntragUnterbrechung.xml.php b/rdf/AntragUnterbrechung.xml.php new file mode 100644 index 000000000..50c9f4c6a --- /dev/null +++ b/rdf/AntragUnterbrechung.xml.php @@ -0,0 +1,71 @@ +db_add_param($id) . " + AND campus.tbl_studierendenantrag.typ = 'Unterbrechung' AND campus.get_status_studierendenantrag(campus.tbl_studierendenantrag.studierendenantrag_id) = 'Genehmigt';"; + $not_found_error = 'Studierendenantrag not found'. $id; + } elseif(isset($_GET['uid']) && isset($_GET['prestudent_id'])) { + $uid = $_GET['uid']; + $uid = explode(';', $uid); + $uid = (array_filter($uid, 'strlen')); + + $prestudent_id = $_GET['prestudent_id']; + $prestudent_id = explode(';', $prestudent_id); + $prestudent_id = (array_filter($prestudent_id, 'strlen')); + + $where = " WHERE campus.tbl_studierendenantrag.prestudent_id in (" . $db->db_implode4SQL($prestudent_id) . ") + AND campus.tbl_studierendenantrag.typ = 'Unterbrechung' AND campus.get_status_studierendenantrag(campus.tbl_studierendenantrag.studierendenantrag_id) = 'Genehmigt';"; + $not_found_error = 'Studierendenantrag not found for: ' . implode(',', $uid); + } else + die('wrong parameters'); +} +else + die('Format not supported'); + + +// TODO(chris): mehrsprachig +$query = " + SELECT tbl_studiengang.bezeichnung, bezeichnung_mehrsprachig[1], studierendenantrag_id, matrikelnr, studienjahr_kurzbz, vorname, nachname, studiengang_kz, semester, tbl_studierendenantrag.grund, datum_wiedereinstieg, datum + FROM + campus.tbl_studierendenantrag + JOIN public.tbl_student USING (prestudent_id) + JOIN public.tbl_benutzer ON tbl_student.student_uid=uid + JOIN public.tbl_person USING (person_id) + JOIN public.tbl_studiengang USING (studiengang_kz) + JOIN public.tbl_studiensemester USING (studiensemester_kurzbz) + JOIN bis.tbl_orgform ON (tbl_orgform.orgform_kurzbz = tbl_studiengang.orgform_kurzbz)" . $where; + + +if (!$db->db_query($query) || !$db->db_num_rows()) + die('' . $not_found_error . ''); + +?> + + + db_fetch_object()) { ?> + + bezeichnung;?> + bezeichnung_mehrsprachig;?> + vorname. " " . $row->nachname);?> + matrikelnr;?> + studienjahr_kurzbz;?> + semester;?> + grund;?> + datum_wiedereinstieg))->format('d.m.Y');?> + datum))->format('d.m.Y');?> + + + diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index 9fda09bba..013972154 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -39,6 +39,7 @@ require_once('dbupdate_3.4/27107_vilesci_erfassung_abwesenheiten_reinigung.php') require_once('dbupdate_3.4/24913_tabelle_raumtyp_neues_attribut_aktiv.php'); require_once('dbupdate_3.4/28089_plausichecks_in_extension_hinzufuegen.php'); require_once('dbupdate_3.4/29133_einzelne_studiengaenge_aus_issuechecks_ausnehmen.php'); +require_once('dbupdate_3.4/27351_digitalisierung_formulare.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/27351_digitalisierung_formulare.php b/system/dbupdate_3.4/27351_digitalisierung_formulare.php new file mode 100644 index 000000000..a40b457e2 --- /dev/null +++ b/system/dbupdate_3.4/27351_digitalisierung_formulare.php @@ -0,0 +1,289 @@ +db_query("SELECT 1 FROM campus.tbl_studierendenantrag_statustyp LIMIT 1")) +{ + $qry = "CREATE TABLE campus.tbl_studierendenantrag_statustyp ( + studierendenantrag_statustyp_kurzbz VARCHAR(32) NOT NULL, + bezeichnung VARCHAR(128)[] NOT NULL, + CONSTRAINT tbl_studierendenantrag_statustyp_pk PRIMARY KEY(studierendenantrag_statustyp_kurzbz) + ); + + GRANT SELECT, INSERT ON campus.tbl_studierendenantrag_statustyp TO vilesci; + GRANT SELECT ON campus.tbl_studierendenantrag_statustyp TO web;"; + + if(!$db->db_query($qry)) + echo 'campus.tbl_studierendenantrag_statustyp: '.$db->db_last_error().'
'; + else + echo '
campus.tbl_studierendenantrag_statustyp: table created'; +} + +if($result = @$db->db_query("SELECT 1 FROM campus.tbl_studierendenantrag_statustyp WHERE studierendenantrag_statustyp_kurzbz = 'Erstellt' ")) +{ + if($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO campus.tbl_studierendenantrag_statustyp + (studierendenantrag_statustyp_kurzbz, bezeichnung) + VALUES + ('Erstellt', '{\"Erstellt\",\"Created\"}'), + ('ErstelltStgl', '{\"Erstellt\",\"Created\"}'), + ('Genehmigt', '{\"Genehmigt\",\"Approved\"}'), + ('GenehmigtStgl', '{\"Vorläufig Genehmigt\",\"Provisionally Approved\"}'), + ('Beeinsprucht', '{\"Beeinsprucht\",\"Objected\"}'), + ('Abgelehnt', '{\"Abgelehnt\",\"Rejected\"}'), + ('Verzichtet', '{\"Verzichtet\",\"Pass\"}'), + ('Offen', '{\"Offen\",\"Reopened\"}'), + ('Zurückgezogen', '{\"Zurückgezogen\",\"Cancelled\"}'), + ('EmailVersandt', '{\"Email Versandt\",\"Reminder Sent\"}'), + ('ErsteAufforderungVersandt', '{\"1.Aufforderung Versandt\",\"1st Request Sent\"}'), + ('ZweiteAufforderungVersandt', '{\"2.Aufforderung Versandt\",\"2nd Request Sent\"}'), + ('Lvszugewiesen', '{\"Lvszugewiesen\",\"Lvsassigned\"}'); + "; + if (!$db->db_query($qry)) + echo 'campus.tbl_studierendenantrag_statustyp (insert): '.$db->db_last_error().'
'; + else + echo '
campus.tbl_studierendenantrag_statustyp: table prefilled'; + } +} + +if(!$result = @$db->db_query("SELECT 1 FROM campus.tbl_studierendenantrag LIMIT 1")) +{ + $qry = "CREATE TABLE campus.tbl_studierendenantrag ( + studierendenantrag_id INTEGER NOT NULL, + prestudent_id INTEGER NOT NULL, + studiensemester_kurzbz VARCHAR(32) NOT NULL, + datum TIMESTAMP NULL, + typ VARCHAR(32) NOT NULL, + insertamum TIMESTAMP DEFAULT NOW(), + insertvon VARCHAR(32) NOT NULL, + datum_wiedereinstieg TIMESTAMP NULL, + grund TEXT NULL, + dms_id INTEGER NULL, + CONSTRAINT tbl_studierendenantrag_pk PRIMARY KEY(studierendenantrag_id) + ); + CREATE SEQUENCE campus.tbl_studierendenantrag_studierendenantrag_id_seq + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + ALTER TABLE campus.tbl_studierendenantrag ALTER COLUMN studierendenantrag_id SET DEFAULT nextval('campus.tbl_studierendenantrag_studierendenantrag_id_seq'); + + GRANT SELECT, INSERT ON campus.tbl_studierendenantrag TO vilesci; + GRANT SELECT, INSERT ON campus.tbl_studierendenantrag TO web; + GRANT SELECT, UPDATE ON campus.tbl_studierendenantrag_studierendenantrag_id_seq TO vilesci; + GRANT SELECT, UPDATE ON campus.tbl_studierendenantrag_studierendenantrag_id_seq TO web;"; + + if(!$db->db_query($qry)) + echo 'campus.tbl_studierendenantrag: '.$db->db_last_error().'
'; + else + echo '
campus.tbl_studierendenantrag: table created'; +} + +if(!$result = @$db->db_query("SELECT 1 FROM campus.tbl_studierendenantrag_status LIMIT 1")) +{ + $qry = "CREATE TABLE campus.tbl_studierendenantrag_status ( + studierendenantrag_status_id INTEGER NOT NULL, + studierendenantrag_id INTEGER NOT NULL, + studierendenantrag_statustyp_kurzbz VARCHAR(32) NOT NULL, + insertamum TIMESTAMP DEFAULT NOW(), + insertvon VARCHAR(32) NOT NULL, + grund TEXT NULL, + CONSTRAINT tbl_studierendenantrag_status_pk PRIMARY KEY(studierendenantrag_status_id), + CONSTRAINT tbl_studierendenantrag_fk FOREIGN KEY (studierendenantrag_id) REFERENCES campus.tbl_studierendenantrag(studierendenantrag_id) ON UPDATE CASCADE ON DELETE RESTRICT, + CONSTRAINT tbl_studierendenantrag_statustyp_fk FOREIGN KEY (studierendenantrag_statustyp_kurzbz) REFERENCES campus.tbl_studierendenantrag_statustyp(studierendenantrag_statustyp_kurzbz) ON UPDATE CASCADE ON DELETE RESTRICT + ); + CREATE SEQUENCE campus.tbl_studierendenantrag_status_studierendenantrag_status_id_seq + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + ALTER TABLE campus.tbl_studierendenantrag_status ALTER COLUMN studierendenantrag_status_id SET DEFAULT nextval('campus.tbl_studierendenantrag_status_studierendenantrag_status_id_seq'); + + GRANT SELECT, INSERT ON campus.tbl_studierendenantrag_status TO vilesci; + GRANT SELECT, INSERT ON campus.tbl_studierendenantrag_status TO web; + GRANT SELECT, UPDATE ON campus.tbl_studierendenantrag_status_studierendenantrag_status_id_seq TO vilesci; + GRANT SELECT, UPDATE ON campus.tbl_studierendenantrag_status_studierendenantrag_status_id_seq TO web;"; + + if(!$db->db_query($qry)) + echo 'campus.tbl_studierendenantrag_status: '.$db->db_last_error().'
'; + else + echo '
campus.tbl_studierendenantrag_status: table created'; +} + +if(!$result = @$db->db_query("SELECT 1 FROM campus.tbl_studierendenantrag_lehrveranstaltung LIMIT 1")) +{ + $qry = "CREATE TABLE campus.tbl_studierendenantrag_lehrveranstaltung ( + studierendenantrag_lehrveranstaltung_id INTEGER NOT NULL, + studierendenantrag_id INTEGER NOT NULL, + lehrveranstaltung_id INTEGER NOT NULL, + studiensemester_kurzbz VARCHAR(16) NOT NULL, + note SMALLINT NOT NULL, + anmerkung TEXT NULL, + insertamum TIMESTAMP DEFAULT NOW(), + insertvon VARCHAR(32) NOT NULL, + CONSTRAINT tbl_studierendenantrag_lehrveranstaltung_pk PRIMARY KEY(studierendenantrag_lehrveranstaltung_id), + CONSTRAINT tbl_studiensemester_fk FOREIGN KEY (studiensemester_kurzbz) REFERENCES public.tbl_studiensemester(studiensemester_kurzbz) ON UPDATE CASCADE ON DELETE RESTRICT, + CONSTRAINT tbl_note_fk FOREIGN KEY (note) REFERENCES lehre.tbl_note(note) ON UPDATE CASCADE ON DELETE RESTRICT, + CONSTRAINT tbl_studierendenantrag_fk FOREIGN KEY (studierendenantrag_id) REFERENCES campus.tbl_studierendenantrag(studierendenantrag_id) ON UPDATE CASCADE ON DELETE RESTRICT + ); + CREATE SEQUENCE campus.tbl_studierendenantrag_lehrveranstaltung_studierendenantrag_lehrveranstaltung_id_seq + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + ALTER TABLE campus.tbl_studierendenantrag_lehrveranstaltung ALTER COLUMN studierendenantrag_lehrveranstaltung_id SET DEFAULT nextval('campus.tbl_studierendenantrag_lehrveranstaltung_studierendenantrag_lehrveranstaltung_id_seq'); + + GRANT SELECT, INSERT, DELETE ON campus.tbl_studierendenantrag_lehrveranstaltung TO vilesci; + GRANT SELECT, INSERT ON campus.tbl_studierendenantrag_lehrveranstaltung TO web; + GRANT SELECT, UPDATE ON campus.tbl_studierendenantrag_lehrveranstaltung_studierendenantrag_lehrveranstaltung_id_seq TO vilesci; + GRANT SELECT, UPDATE ON campus.tbl_studierendenantrag_lehrveranstaltung_studierendenantrag_lehrveranstaltung_id_seq TO web;"; + + if(!$db->db_query($qry)) + echo 'campus.tbl_studierendenantrag_lehrveranstaltung: '.$db->db_last_error().'
'; + else + echo '
campus.tbl_studierendenantrag_lehrveranstaltung: table created'; +} + +if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz = 'student/studierendenantrag';")) +{ + if($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES('student/studierendenantrag', 'Berechtigung für Bearbeiten Studierendenanträge');"; + + if(!$db->db_query($qry)) + echo 'system.tbl_berechtigung '.$db->db_last_error().'
'; + else + echo ' system.tbl_berechtigung: Added permission for student/studierendenantrag
'; + } +} + +if($result = @$db->db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz = 'student/antragfreigabe';")) +{ + if($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES('student/antragfreigabe', 'Berechtigung für Freigabe der Studierendenanträge');"; + + if(!$db->db_query($qry)) + echo 'system.tbl_berechtigung '.$db->db_last_error().'
'; + else + echo ' system.tbl_berechtigung: Added permission for student/antragfreigabe
'; + } +} + +if (!$result = @$db->db_query("SELECT campus.get_status_studierendenantrag(0)")) { + $qry = 'CREATE FUNCTION campus.get_status_studierendenantrag(integer) RETURNS character varying + LANGUAGE plpgsql + AS $_$ + DECLARE i_studierendenantrag_id ALIAS FOR $1; + DECLARE rec RECORD; + BEGIN + SELECT INTO rec studierendenantrag_statustyp_kurzbz + FROM campus.tbl_studierendenantrag_status + WHERE studierendenantrag_id=i_studierendenantrag_id + ORDER BY insertamum desc + LIMIT 1; + + RETURN rec.studierendenantrag_statustyp_kurzbz; + END; + $_$; + + ALTER FUNCTION campus.get_status_studierendenantrag(integer) OWNER TO fhcomplete;'; + + if(!$db->db_query($qry)) + echo 'campus.get_status_studierendenantrag(integer): '.$db->db_last_error().'
'; + else + echo '
campus.get_status_studierendenantrag(integer): function created'; +} + +if (!$result = @$db->db_query("SELECT campus.get_status_id_studierendenantrag(0)")) { + $qry = 'CREATE FUNCTION campus.get_status_id_studierendenantrag(integer) RETURNS integer + LANGUAGE plpgsql + AS $_$ + DECLARE i_studierendenantrag_id ALIAS FOR $1; + DECLARE rec RECORD; + BEGIN + SELECT INTO rec studierendenantrag_status_id + FROM campus.tbl_studierendenantrag_status + WHERE studierendenantrag_id=i_studierendenantrag_id + ORDER BY insertamum desc + LIMIT 1; + + RETURN rec.studierendenantrag_status_id; + END; + $_$; + + ALTER FUNCTION campus.get_status_id_studierendenantrag(integer) OWNER TO fhcomplete;'; + + if(!$db->db_query($qry)) + echo 'campus.get_status_id_studierendenantrag(integer): '.$db->db_last_error().'
'; + else + echo '
campus.get_status_id_studierendenantrag(integer): function created'; +} + +if (!$result = @$db->db_query("SELECT public.get_absem_prestudent(0, null)")) { + $qry = 'CREATE FUNCTION public.get_absem_prestudent(integer, character varying) RETURNS integer + LANGUAGE plpgsql + AS $_$ + DECLARE i_prestudent_id ALIAS FOR $1; + DECLARE cv_studiensemester_kurzbz ALIAS FOR $2; + DECLARE rec RECORD; + BEGIN + IF (cv_studiensemester_kurzbz IS NULL) THEN + SELECT INTO rec ausbildungssemester + FROM public.tbl_prestudentstatus + WHERE prestudent_id=i_prestudent_id + ORDER BY datum desc,insertamum desc, ext_id desc + LIMIT 1; + ELSE + SELECT INTO rec ausbildungssemester + FROM tbl_prestudentstatus + WHERE prestudent_id=i_prestudent_id AND studiensemester_kurzbz=cv_studiensemester_kurzbz + ORDER BY datum desc,insertamum desc, ext_id desc + LIMIT 1; + END IF; + + RETURN rec.ausbildungssemester; + END; + $_$; + + ALTER FUNCTION public.get_absem_prestudent(integer, character varying) OWNER TO fhcomplete;'; + + if(!$db->db_query($qry)) + echo 'public.get_absem_prestudent(integer, character varying): '.$db->db_last_error().'
'; + else + echo '
public.get_absem_prestudent(integer, character varying): function created'; +} +if (!$result = @$db->db_query("SELECT public.get_stdsem_prestudent(0, null)")) { + $qry = 'CREATE FUNCTION public.get_stdsem_prestudent(integer, character varying) RETURNS character varying + LANGUAGE plpgsql + AS $_$ + DECLARE i_prestudent_id ALIAS FOR $1; + DECLARE cv_studiensemester_kurzbz ALIAS FOR $2; + DECLARE rec RECORD; + BEGIN + IF (cv_studiensemester_kurzbz IS NULL) THEN + SELECT INTO rec studiensemester_kurzbz + FROM public.tbl_prestudentstatus + WHERE prestudent_id=i_prestudent_id + ORDER BY datum desc,insertamum desc, ext_id desc + LIMIT 1; + ELSE + SELECT INTO rec studiensemester_kurzbz + FROM tbl_prestudentstatus + WHERE prestudent_id=i_prestudent_id AND studiensemester_kurzbz=cv_studiensemester_kurzbz + ORDER BY datum desc,insertamum desc, ext_id desc + LIMIT 1; + END IF; + + RETURN rec.studiensemester_kurzbz; + END; + $_$; + + ALTER FUNCTION public.get_stdsem_prestudent(integer, character varying) OWNER TO fhcomplete;'; + + if(!$db->db_query($qry)) + echo 'public.get_stdsem_prestudent(integer, character varying): '.$db->db_last_error().'
'; + else + echo '
public.get_stdsem_prestudent(integer, character varying): function created'; +} diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 0bc748c80..f6df3afad 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -5774,6 +5774,26 @@ When on hold, the date is only a reminder.', ) ) ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'error_invalid_date', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Das Datumsformat ist ungültig oder liegt außerhalb des gültigen Bereichs.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The date format is invalid or out of range.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'ui', @@ -14446,6 +14466,26 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'schliessen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Schließen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => "Close", + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'global', @@ -18123,6 +18163,1750 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'infotext_Wiederholung_0', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Gemäß § 16 Abs. 1 FHG steht Studierenden einmalig das Recht auf Wiederholung eines Studienjahres in Folge einer negativ beurteilten kommissionellen Prüfung zu.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'According to § 16 paragraph 1 FHG, students have the right to repeat an academic year as a result of a negative examination before a committee.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'infotext_Wiederholung_1', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die Wiederholung ist bei der Studiengangsleitung binnen eines Monats ab Mitteilung des negativen Prüfungsergebnisses bekannt zu geben.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The head of the degree program must be notified of the repetition within one month of notification of the negative examination result.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'infotext_Wiederholung_2', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Infolge der Weiterentwicklung der Qualität des Studienganges kann es zu Änderungen der Studienbedingungen im Zuge der Wiederholung eines Studienjahres kommen (z.B. Studienplan, Prüfungsordnung etc.).', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'As a result of the further development of the quality of the course, there may be changes to the study conditions in the course of repeating an academic year (e.g. study plan, examination regulations, etc.).', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'infotext_Wiederholung_3', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die Studiengangsleitung legt Prüfungen und Lehrveranstaltungen für die Wiederholung des Studienjahres fest.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The head of the degree program determines examinations and courses for the repetition of the academic year.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'infotext_Wiederholung_4', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nicht bestandene Prüfungen und Lehrveranstaltungen sind jedenfalls, bestandene Prüfungen und Lehrveranstaltungen nur, sofern es der Zweck des Studiums erforderlich macht, zu wiederholen oder erneut zu besuchen. Wird eine Lehrveranstaltung nach der letzten Wiederholungsmöglichkeit negativ beurteilt, darf dieder Studierende an keiner kommissionellen Wiederholungsprüfung im Semester der negativ beurteilen Lehrveranstaltung und im Folgesemester mehr teilnehmen (nicht-kommissionelle Wiederholungsprüfungen können wahrgenommen werden)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'In any case, failed examinations and courses are to be repeated or attended again only if the purpose of the course makes it necessary. If a course is assessed negatively after the last opportunity to repeat it, the student may no longer take part in a board re-examination in the semester in which the course was assessed as negative and in the following semester (non-board re-examinations can be taken).', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'info_Wiederholung_Erstellt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Info für Wiederholung Erstellt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Info for Wiederholung Erstellt', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_Wiederholung_pruefung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Negativ beurteilte kommissionelle Prüfung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Negative assessment by a committee', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_Wiederholung_pruefung_date', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Datum der Beurteilung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'date of assessment', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_Wiederholung_button_yes', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ich gebe hiermit die Wiederholung des Studienjahres bekannt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'I hereby announce the repetition of the academic year', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_Wiederholung_button_no', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ich werde das Studienjahr nicht wiederholen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'I will not repeat the academic year', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antraege_header', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anträge auf Änderung des Studierendenstatus', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Requests for changing the Studierendenstatus', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_header', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Antrag auf Änderung des Studierendenstatus', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Request for changing the Studierendenstatus', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_new', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Neu Anlegen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Create new', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'cancel', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Abbrechen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Cancel', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'ok', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ok', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Ok', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_create', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anlegen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Create', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_create_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Vom Studium abmelden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Deregister from your studies', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_create_Unterbrechung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Das Studium unterbrechen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Interrupt your studies', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_cancel', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Antrag zurückziehen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Cancel Application', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'warning_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ihr CIS-Account ist noch 21 Tage aktiv. Wir bitten Sie, alle benötigten Dateien (Zeugnisse, Studienerfolgsbestätigungen, Studienbestätigungen, etc.) innerhalb dieses Zeitraums herunterzuladen. Für die Ausstellung von Duplikaten fallen nach Inaktivsetzung des CIS-Accounts Kosten an.' . "
\n" . + 'Bitte retournieren Sie baldmöglichst entlehnte Bücher an die Bibliothek.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Your CIS account is still active for 21 days. We ask you to download all required files (certificates, confirmations of academic success, confirmation of studies, etc.) within this period. There is a charge for issuing duplicates after the CIS account has been deactivated.' . "
\n" . + 'Please return borrowed books to the library as soon as possible.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Abmeldung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'De-registration', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_Unterbrechung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Unterbrechung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Interruption', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_Wiederholung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Wiederholung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Repetition', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_new_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Neue Abmeldung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'New de-registration', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_typ', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Typ', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Type', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_status', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Status', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Status', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_studiensemester', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studiensemester', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Semester', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_erstelldatum', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Erstelldatum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Createdate', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'studierendenantraege', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studierendenanträge', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Applications', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_datum_wiedereinstieg', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Datum Wiedereinstieg', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Date of return', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_grund', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Grund', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Reason', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_dateianhaenge', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dateianhänge', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Attachments', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_anhang', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Anhang', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Attachment', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_typ_Abmeldung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Abmeldung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Deregistration', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_typ_Unterbrechung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Unterbrechung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Break', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'antrag_typ_Wiederholung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Wiederholung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Repetition', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_show_lvs', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LVs anzeigen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Show LVs', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_edit', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bearbeiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Edit', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_reopen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Erneut Freischalten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Reopen', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_object', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Beeinspruchen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Object', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_objection_deny', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Einspruch abgelehnt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Objection rejected', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_objection_approve', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Einspruch stattgegeben', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Objection granted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_lvzuweisen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'LVs zuweisen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Assign Lvs', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_approve', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Genehmigen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Approve', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_reject', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ablehnen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Reject', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'skip', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Überspringen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Skip', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'select_studiensemester', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studiensemester auswählen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Select a semester', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_save_lvs', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zuweisungen speichern', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Save assignments', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'btn_download_antrag', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'PDF herunterladen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Download PDF', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'table', + 'phrase' => 'download', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Download', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Download', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'table', + 'phrase' => 'reload', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Tabelle neu laden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Reload table', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_student', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sie sind derzeit in keinem Studium', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'You are currently not enrolled', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_student_no_failed_exam', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sie sind derzeit in keinem Studium oder haben keine negativ beurteilte kommissionelle Prüfung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'You are not currently studying or have not passed a board examination with negative reasons', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'no_attachments', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Dateianhänge', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No Attachments', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'prestudent', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Prestudent', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Prestudent', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'lehre', + 'phrase' => 'studienjahr', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Studienjahr', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Academic year', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_lvzuweisen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrveranstaltungen zuweisen für {name}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Assign Courses for {name}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_lv_nicht_zugelassen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Angabe aller Lehrveranstaltungen, zu denen die Person nicht zugelassen ist', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Details of all courses to which the person is not admitted', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_lv_wiederholen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Angabe aller zu wiederholenden bzw. erneut zu besuchenden Lehrveranstaltungen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Specification of all courses to be repeated or attended again', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_show_lvs', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehrveranstaltungen für {name}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Courses for {name}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'my_lvs', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Meine Lehrveranstaltungen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'My Courses', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'table', + 'phrase' => 'with_selected', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Mit {count} ausgewählten: ', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'With {count} selected: ', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'lv_nicht_zulassen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Nicht zulassen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Don\'t allow ', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'lv_wiederholen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Wiederholen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Repeat', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_history', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Statusverlauf für Antrag #{id} ', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'History for Application #{id}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'anmerkung_tooltip', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Die Spalte Anmerkung kann verwendet werden, um Besonderheiten z.B. bei einem Studienplanwechsel zu dokumentieren (Änderung von LV-Titel, ECTS…) ', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The Comment column can be used to document special features, e.g. when changing the curriculum (change of course title, ECTS...)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'fuer_alle_uebernehmen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'für alle übernehmen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'apply for all', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'fuer_x_uebernehmen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Auswahl für {count} weitere(n) Wiederholungsanträge übernehmen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Adopt selection for {count} further repeat applications', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'title_grund', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Grund für Ablehnung von Antrag #{id}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Reason for rejection of Application #{id}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_lvs', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Lehrveranstaltungen zugewiesen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No courses assigned', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_x', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Status: {status}', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Status: {status}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_saving', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Daten werden gespeichert...', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Saving...', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_error', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Fehler', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Error', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_open', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Offen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Open', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_created', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Erstellt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Created', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_cancelling', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zurückziehen...', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Cancelling...', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'status_cancelled', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zurückgezogen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Cancelled', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_stg_blacklist', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Für diesen Studiengang sind keine Anträge möglich', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No applications are accepted for this course', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_antrag_exists', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es gibt bereits einen bestehenden Antrag', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'There is already an existing application', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_antrag_pending', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es gibt bereits einen bestehenden Antrag vom Typ {typ}' , + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'There is already an existing application of type {typ}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_antrag_found', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Kein Antrag mit AntragsId {id} gefunden' , + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No application found with ApplicationId {id}', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_antrag_locked', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Dieser Antrag ist gesperrt' , + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'This request is locked', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_lv', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Lehrveranstaltung ausgewählt' , + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No course selected', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_lv_in_application', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Lehrveranstaltung im Antrag gefunden' , + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No course found in application', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_right', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Berechtigung, den Antrag zu bearbeiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No authorization to edit the application', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_no_objection', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Berechtigung, den Antrag zu beeinspruchen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No authorization to object the application', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'studierendenantrag', + 'phrase' => 'error_not_objected', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Antrag ist nicht beeinsprucht', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Application is not objected', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + ); diff --git a/system/vorlage_zip/AntragAbmeldung.odt b/system/vorlage_zip/AntragAbmeldung.odt new file mode 100644 index 0000000000000000000000000000000000000000..2e11ca60ae49eb67a843672bf6e2c87e5d31517c GIT binary patch literal 37277 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 literal 0 HcmV?d00001 diff --git a/system/vorlage_zip/AntragUnterbrechung.odt b/system/vorlage_zip/AntragUnterbrechung.odt new file mode 100644 index 0000000000000000000000000000000000000000..aeaa82105e7a130979d9a86f82ee5d622b610b7c GIT binary patch literal 34155 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

VY%NO_G+4oZkl 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Studiengang: + + + + + + Organisationsform: + + + + + + + + Abmeldung vom Studium durch Studierende + + + + + + + Name der*des Studierenden + + + + + + + + + + + Personenkennzeichen + + + + + + + + + + Studienjahr + + + + + + + + + + Semester + + + + + + + + + + Grund der Abmeldung: + + + + + + + + + + + + + + + Wir weisen Sie darauf hin, dass Ihr FHTW Account noch 21 Tage aktiv ist. Wir bitten Sie, alle benötigte Dateien (Zeugnisse, Studienerfolgsbestätigungen, Studienbestätigungen, etc.) innerhalb dieses Zeitraums herunterzuladen. Für die Ausstellung von Duplikaten fallen nach Inaktivsetzung des CIS-Accounts Kosten an. + + + Sie sind gem. Ausbildungsvertrag verpflichtet, unverzüglich alle zur Verfügung gestellten Gerätschaften, Bücher, Schlüssel und sonstige Materialien zurückzugeben. + + Bei Abmeldung vor dem 01.09. bzw. 15.02. und bereits eingezahltem Studienbeitrag für das kommende Semester: Wir informieren Sie darüber, dass der Studienbeitrag für das kommende Semester von Ihnen zurückgefordert werden kann. Bitte geben Sie uns dafür innerhalb von 14 Tagen Ihre Bankdaten an folgende E-Mail-Adresse bekannt: billing@technikum-wien.at. + + + + + + + + \ No newline at end of file diff --git a/system/xsl/AntragUnterbrechung.xsl b/system/xsl/AntragUnterbrechung.xsl new file mode 100644 index 000000000..f1ab10d91 --- /dev/null +++ b/system/xsl/AntragUnterbrechung.xsl @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Studiengang: + + + + + + + + Organisationsform: + + + + + + + + + Antrag auf Unterbrechung des Studiums + + + + + + + + Name der*des Studierenden + + + + + + + + + + + + + + Personenkennzeichen + + + + + + + + + + + + + + Studienjahr + + + + + + + + + + + + + + Aktuelles Semester + + + + + + + + + + + + Grund der Unterbrechung: + + + + + + + + + + + + + Wiedereinstieg am + + + + + + + + + + + + + + + + + + + + Datum: + + + + + + + + + + + + + + + + Infolge der Weiterentwicklung der Qualität des Studienganges kann es zu Änderungen der Studienbedingungen beim Wiedereinstieg kommen (z. B. Studienplan, Prüfungsordnung etc.) + + + Falls Sie das Studium im Wintersemester vor dem 15.10. bzw. im Sommersemester vor dem 15.3. beenden, wird Ihnen der Studienbeitrag für das aktuelle Semester rückerstattet. Bitte geben Sie uns innerhalb von 14 Tagen Ihre Bankdaten an folgende E-Mail-Adresse bekannt: + + + billing@technikum-wien.at + + + . + + + + + + \ No newline at end of file