From 77a1067ea569874c07ecde06d3f2f140d88986eb Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 3 Dec 2025 15:32:21 +0100 Subject: [PATCH 01/10] Studierendenverwaltung Projektbetreuer: default Stunden coming from global config, Stundensatz is requested each time BetreuerIn is selected --- application/config/stv.php | 13 ++- .../api/frontend/v1/stv/Projektbetreuer.php | 22 ++++- .../models/ressource/Stundensatz_model.php | 38 +++++--- .../Details/Projektarbeit/Projektbetreuer.js | 88 +++++++++++-------- 4 files changed, 104 insertions(+), 57 deletions(-) diff --git a/application/config/stv.php b/application/config/stv.php index e03c00084..2be148621 100644 --- a/application/config/stv.php +++ b/application/config/stv.php @@ -83,10 +83,15 @@ if (!defined('ZGV_DOKTOR_ANZEIGEN') || !ZGV_DOKTOR_ANZEIGEN) { $fieldsZgvDoktor ); } - -$config['tabs']['projektarbeit']['defaultProjektbetreuerStunden'] = '4.0'; -$config['tabs']['projektarbeit']['defaultProjektbetreuerStundenDiplom'] = '5.0'; -$config['tabs']['projektarbeit']['lvLektroinnenzuteilungFixangestelltStundensatz'] = true; +error_log(print_r(defined(FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_BACHELOR), true)); +$config['tabs']['projektarbeit']['defaultProjektbetreuerStunden'] = + defined('FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_BACHELOR') + ? FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_BACHELOR + : '0.0'; +$config['tabs']['projektarbeit']['defaultProjektbetreuerStundenDiplom'] = + defined('FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_MASTER') + ? FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_MASTER + : '0.0'; $config['tabs']['projektarbeit']['defaultProjektbetreuerStundensatz'] = '80.0'; $config['student_tab_order'] = [ diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index 98567ecde..a84248a35 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -114,18 +114,36 @@ class Projektbetreuer extends FHCAPI_Controller if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id)) return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]); + if (isset($projektbetreuer['stunden']) && !isEmptyString($projektbetreuer['stunden']) && !is_numeric($projektbetreuer['stunden'])) + { + return $this->terminateWithError( + $this->p->t('ui', 'error_fieldNotNumeric', ['field'=> 'Stunden', 'Field'=> 'Stunden']), self::ERROR_TYPE_GENERAL + ); + } + + if (isset($projektbetreuer['stundensatz']) && !isEmptyString($projektbetreuer['stundensatz']) && !is_numeric($projektbetreuer['stundensatz'])) + { + return $this->terminateWithError( + $this->p->t('ui', 'error_fieldNotNumeric', ['field'=> 'Stundensatz', 'Field'=> 'Stundensatz']), self::ERROR_TYPE_GENERAL + ); + } + $projektbetreuer = $this->input->post('projektbetreuer'); if ($this->_validate($projektbetreuer) == false) $this->terminateWithValidationErrors($this->form_validation->error_array()); $result = null; + $stunden = isset($projektbetreuer['stunden']) && !isEmptyString($projektbetreuer['stunden']) ? $projektbetreuer['stunden'] : null; + $stundensatz = + isset($projektbetreuer['stundensatz']) && !isEmptyString($projektbetreuer['stundensatz']) ? $projektbetreuer['stundensatz'] : null; + $betreuer = [ 'projektarbeit_id' => $projektarbeit_id, 'person_id' => $projektbetreuer['person_id'], 'note' => $projektbetreuer['note'], - 'stunden' => $projektbetreuer['stunden'], - 'stundensatz' => $projektbetreuer['stundensatz'], + 'stunden' => $stunden, + 'stundensatz' => $stundensatz, 'betreuerart_kurzbz' => $projektbetreuer['betreuerart_kurzbz'] ]; diff --git a/application/models/ressource/Stundensatz_model.php b/application/models/ressource/Stundensatz_model.php index c8ee367d8..2a7418924 100644 --- a/application/models/ressource/Stundensatz_model.php +++ b/application/models/ressource/Stundensatz_model.php @@ -47,7 +47,6 @@ class Stundensatz_model extends DB_Model { $this->load->config('stv'); - $useFixangestelltStundensatz = $this->config->item('tabs')['projektarbeit']['lvLektroinnenzuteilungFixangestelltStundensatz']; $defaultStundensatz = $this->config->item('tabs')['projektarbeit']['defaultProjektbetreuerStundensatz']; $stundensatz = ''; @@ -63,7 +62,7 @@ class Stundensatz_model extends DB_Model { $studiensemester = getData($result)[0]; - if (isset($useFixangestelltStundensatz) && !$useFixangestelltStundensatz) + if (defined('FAS_LV_LEKTORINNENZUTEILUNG_FIXANGESTELLT_STUNDENSATZ') && !FAS_LV_LEKTORINNENZUTEILUNG_FIXANGESTELLT_STUNDENSATZ) { // load Mitarbeiter $params = [$person_id]; @@ -83,19 +82,30 @@ class Stundensatz_model extends DB_Model if (hasData($result)) { - foreach (getData($result) as $ma) - { - if (!$ma->fixangestellt) - { - $stundensatzRes = $this->getStundensatzByDatum( - $ma->mitarbeiter_uid, $studiensemester->start, $studiensemester->ende, 'lehre' - ); + $ma = getData($result)[0]; - if (hasData($stundensatzRes)) - $stundensatz = getData($stundensatzRes)[0]->stundensatz; - else - $stundensatz = '0.00'; - } + $this->load->model('vertragsbestandteil/Dienstverhaeltnis_model','DienstverhaeltnisModel'); + $echterdv_result = $this->DienstverhaeltnisModel->existsDienstverhaeltnis( + $ma->mitarbeiter_uid, + $studiensemester->start, + $studiensemester->ende, + 'echterdv' + ); + + if (hasData($echterdv_result)) + { + $stundensatz = null; + } + else + { + $stundensatzRes = $this->getStundensatzByDatum( + $ma->mitarbeiter_uid, $studiensemester->start, $studiensemester->ende, 'lehre' + ); + + if (hasData($stundensatzRes)) + $stundensatz = getData($stundensatzRes)[0]->stundensatz; + else + $stundensatz = '0.00'; } } else diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index adcf2f2a8..f847a1e92 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -152,6 +152,7 @@ export default { stunden: null, stundensatz: null }, + defaultStundensatz: null, newMode: false, editMode: false, initialFormData: null, @@ -179,45 +180,40 @@ export default { actionEditProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) { this.editMode = true; this.newMode = false; - this.$api - .call(ApiStvProjektbetreuer.getDefaultStundensaetze(person_id, this.studiensemester_kurzbz)) - .then(result => { - this.resetForm(); + this.resetForm(); + this.getDefaultStundensaetze(person_id).finally(() => { - // get betreuer from tabulator list - let projektbetreuerListe = this.$refs.projektbetreuerTable.tabulator.getData(); - const idx = projektbetreuerListe.findIndex( - betr => - betr.person_id === person_id && - betr.projektarbeit_id === projektarbeit_id && - betr.betreuerart_kurzbz === betreuerart_kurzbz - ); + // get betreuer from tabulator list + let projektbetreuerListe = this.$refs.projektbetreuerTable.tabulator.getData(); + const idx = projektbetreuerListe.findIndex( + betr => + betr.person_id === person_id && + betr.projektarbeit_id === projektarbeit_id && + betr.betreuerart_kurzbz === betreuerart_kurzbz + ); - if (idx >= 0) { // if betreuer found + if (idx >= 0) { // if betreuer found - // set currently edited betreuer (deep copy) - this.formData = JSON.parse(JSON.stringify(projektbetreuerListe[idx])); + // set currently edited betreuer (deep copy) + this.formData = JSON.parse(JSON.stringify(projektbetreuerListe[idx])); - // set download link - if (this.formData.beurteilungDownloadLink !== null) this.beurteilungDownloadLink = this.formData.beurteilungDownloadLink; + // set download link + if (this.formData.beurteilungDownloadLink !== null) this.beurteilungDownloadLink = this.formData.beurteilungDownloadLink; - // set betreuer for autocomplete field - this.autocompleteSelectedBetreuer = { - person_id: this.formData.person_id, - name: this.formData.name, - vorname: this.formData.vorname, - nachname: this.formData.nachname, - vertrag_id: this.formData.vertrag_id - }; - } + // set betreuer for autocomplete field + this.autocompleteSelectedBetreuer = { + person_id: this.formData.person_id, + name: this.formData.name, + vorname: this.formData.vorname, + nachname: this.formData.nachname, + vertrag_id: this.formData.vertrag_id + }; + } - // set default stundensatz (if no other is set yet) - if (this.formData.stundensatz == null) this.formData.stundensatz = result.data; + // capture initial form data for detecting changes + this.captureFormData(); + }); - // capture initial form data for detecting changes - this.captureFormData(); - }) - .catch(this.$fhcAlert.handleSystemError); }, actionDeleteProjektbetreuer(betreuer_id, projektarbeit_id, person_id, betreuerart_kurzbz) { this.$fhcAlert @@ -239,6 +235,7 @@ export default { // default Stundensätze from config this.defaultFormDataValues.stunden = this.getDefaultStunden(projekttyp_kurzbz); this.defaultFormDataValues.stundensatz = this.config.defaultProjektbetreuerStundensatz; + this.defaultStundensatz = this.config.defaultProjektbetreuerStundensatz; // get other initial data this.$api @@ -271,7 +268,7 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); } else { - this.emptyBetreuer(); + this.emptyBetreuerList(); } }, saveProjektbetreuer() { @@ -297,11 +294,13 @@ export default { this.filteredBetreuer = result.data; }); }, - emptyBetreuer() { + emptyBetreuerList() { this.$refs.projektbetreuerTable.tabulator.clearData(); }, resetForm() { - this.formData = this.getDefaultFormData(); + const defaultFormData = this.getDefaultFormData(); + this.formData = defaultFormData; + this.defaultStundensatz = defaultFormData.stundensatz; if (this.beurteilungDownloadLink !== null) this.beurteilungDownloadLink = ''; this.autocompleteSelectedBetreuer = null; this.initialFormData = null; @@ -369,6 +368,16 @@ export default { this.formData.stunden = defaultStunden; } }, + getDefaultStundensaetze(person_id) { + return this.$api + .call(ApiStvProjektbetreuer.getDefaultStundensaetze(person_id, this.studiensemester_kurzbz)) + .then(result => { + // set default stundensatz (if no other is set yet) + this.defaultStundensatz = result.data; + this.formData.stundensatz = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + }, // check if form data has been modified since initial data has been captured formDataModified() { if (this.autocompleteSelectedBetreuer != null) return true; @@ -401,6 +410,9 @@ export default { // disable fields which are dependent on Vertrag status disableVertragFields(statusAkzeptiert) { this.vertragFieldsDisabled = statusAkzeptiert; + }, + onBetreuerSelected(ev) { + this.getDefaultStundensaetze(ev.value.person_id); } }, template: ` @@ -422,7 +434,7 @@ export default { > - +
@@ -502,7 +516,7 @@ export default { container-class="stv-details-projektarbeit-stundensatz" type="text" name="stundensatz" - :label="$p.t('projektarbeit', 'stundensatz')" + :label="$p.t('projektarbeit', 'stundensatz') + (defaultStundensatz ? ' (Default ' + defaultStundensatz + ')' : '')" :disabled="vertragFieldsDisabled" v-model="formData.stundensatz" > From c79ff24c89f77d400390bb3650fd115592e5d580 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 3 Dec 2025 19:29:42 +0100 Subject: [PATCH 02/10] Studierendenverwaltung Projektarbeit: added Studiensemester dropdown for Lv-Teil --- .../api/frontend/v1/stv/Projektarbeit.php | 14 +++- public/js/api/factory/stv/projektarbeit.js | 6 ++ .../Details/Projektarbeit/Details.js | 68 +++++++++++++++---- .../Details/Projektarbeit/Projektarbeit.js | 2 +- 4 files changed, 73 insertions(+), 17 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Projektarbeit.php b/application/controllers/api/frontend/v1/stv/Projektarbeit.php index 9cc604184..168472b62 100644 --- a/application/controllers/api/frontend/v1/stv/Projektarbeit.php +++ b/application/controllers/api/frontend/v1/stv/Projektarbeit.php @@ -17,7 +17,8 @@ class Projektarbeit extends FHCAPI_Controller 'getTypenProjektarbeit' => ['admin:r', 'assistenz:r'], 'getFirmen' => ['admin:r', 'assistenz:r'], 'getLehrveranstaltungen' => ['admin:r', 'assistenz:r'], - 'getNoten' => ['admin:r', 'assistenz:r'] + 'getNoten' => ['admin:r', 'assistenz:r'], + 'getStudiensemester' => ['admin:r', 'assistenz:r'] ]); // Load Libraries @@ -40,6 +41,7 @@ class Projektarbeit extends FHCAPI_Controller $this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel'); $this->load->model('education/Note_model', 'NoteModel'); $this->load->model('education/Projektbetreuer_model', 'BetreuerModel'); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); // load libraries $this->load->library('PermissionLib'); @@ -259,6 +261,16 @@ class Projektarbeit extends FHCAPI_Controller return $this->terminateWithSuccess(hasData($result) ? getData($result) : []); } + public function getStudiensemester() + { + $this->StudiensemesterModel->addOrder('start', 'DESC'); + $result = $this->StudiensemesterModel->load(); + + if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + + return $this->terminateWithSuccess(hasData($result) ? getData($result) : []); + } + /** * * @param diff --git a/public/js/api/factory/stv/projektarbeit.js b/public/js/api/factory/stv/projektarbeit.js index 4412f1842..e9b3e7efc 100644 --- a/public/js/api/factory/stv/projektarbeit.js +++ b/public/js/api/factory/stv/projektarbeit.js @@ -49,6 +49,12 @@ export default { url: 'api/frontend/v1/stv/projektarbeit/getNoten' }; }, + getStudiensemester() { + return { + method: 'get', + url: 'api/frontend/v1/stv/projektarbeit/getStudiensemester' + }; + }, loadProjektarbeit(projektarbeit_id) { return { method: 'get', diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js index 63b80ba37..d14f618e1 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js @@ -69,10 +69,13 @@ export default { final: true, anmerkung: null }, + studiensemester: null, arrTypen: [], arrFirmen: [], arrLvs: [], arrNoten: [], + arrStudiensemester: [], + additional_lehrveranstaltung_id: null, filteredFirmen: [], abortController: { firma: null @@ -103,7 +106,11 @@ export default { this.formData.anmerkung = null; this.$refs.formDetails.clearValidation(); }, - getFormData(statusNew, studiensemester_kurzbz, additional_lehrveranstaltung_id) { + getFormData(newProjektarbeit, studiensemester_kurzbz, additional_lehrveranstaltung_id) { + + this.additional_lehrveranstaltung_id = additional_lehrveranstaltung_id; + this.studiensemester = studiensemester_kurzbz || this.defaultSemester; + this.newProjektarbeit = newProjektarbeit; this.$api .call(ApiStvProjektarbeit.getTypenProjektarbeit()) @@ -112,18 +119,7 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); - this.$api - .call(ApiStvProjektarbeit.getLehrveranstaltungen( - this.student.uid, - statusNew ? this.student.studiengang_kz : null, - studiensemester_kurzbz ?? this.defaultSemester, - additional_lehrveranstaltung_id - )) - .then(result => { - this.arrLvs = result.data - } - ) - .catch(this.$fhcAlert.handleSystemError); + this.getLehrveranstaltungen(); this.$api .call(ApiStvProjektarbeit.getNoten()) @@ -132,7 +128,14 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); - if (statusNew) this.resetForm(); + this.$api + .call(ApiStvProjektarbeit.getStudiensemester()) + .then(result => { + this.arrStudiensemester = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + if (this.newProjektarbeit) this.resetForm(); }, loadProjektarbeit(projektarbeit_id) { @@ -178,6 +181,24 @@ export default { this.filteredFirmen = result.data; }); }, + getLehrveranstaltungen() { + return this.$api + .call(ApiStvProjektarbeit.getLehrveranstaltungen( + this.student.uid, + this.newProjektarbeit ? this.student.studiengang_kz : null, + this.studiensemester, + this.additional_lehrveranstaltung_id + )) + .then(result => { + this.arrLvs = result.data + } + ) + .catch(this.$fhcAlert.handleSystemError); + }, + studiensemesterChanged() { + this.formData.lehreinheit_id = null; + this.getLehrveranstaltungen(); + }, lvChanged(event) { this.formData.lehreinheit_id = null; }, @@ -281,7 +302,7 @@ export default {
+ + + +
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js index 31be69ff8..f831ece7d 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js @@ -258,7 +258,7 @@ export default { this.statusNew = true; this.editedProjektarbeit = null; this.toggleMenu('details'); - this.$refs.projektarbeitDetails.getFormData(this.statusNew); + this.$refs.projektarbeitDetails.getFormData(this.statusNew, null, null); this.$refs.projektarbeitModal.show(); }, actionEditProjektarbeit() { From 112e30ab39b60f8e61c957a5148bfabf007c2845 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Fri, 5 Dec 2025 22:01:35 +0100 Subject: [PATCH 03/10] Studierendenverwaltung Projektarbeit: added concatenated Betreuer, sorted autocomplete suggestions, added Gesamtnote, freigegeben check removed when lock date set, added phrases --- .../api/frontend/v1/stv/Projektarbeit.php | 5 +- .../api/frontend/v1/stv/Projektbetreuer.php | 52 +++++--- .../models/education/Projektarbeit_model.php | 19 ++- .../Details/Projektarbeit/Details.js | 8 +- .../Details/Projektarbeit/Projektarbeit.js | 21 ++- .../Details/Projektarbeit/Projektbetreuer.js | 43 ++++++- system/phrasesupdate.php | 120 ++++++++++++++++++ 7 files changed, 238 insertions(+), 30 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Projektarbeit.php b/application/controllers/api/frontend/v1/stv/Projektarbeit.php index 168472b62..1752cee92 100644 --- a/application/controllers/api/frontend/v1/stv/Projektarbeit.php +++ b/application/controllers/api/frontend/v1/stv/Projektarbeit.php @@ -55,10 +55,7 @@ class Projektarbeit extends FHCAPI_Controller $result = $this->ProjektarbeitModel->getProjektarbeit($student_uid); - if (isError($result)) - { - $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - } + if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); if (!hasData($result)) $this->terminateWithSuccess([]); diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index a84248a35..4eec773c5 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -50,21 +50,30 @@ class Projektbetreuer extends FHCAPI_Controller if (!isset($projektarbeit_id)) $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Projektarbeit ID']), self::ERROR_TYPE_GENERAL); - $this->ProjektbetreuerModel->addSelect( - 'projektarbeit_id, person_id, nachname, vorname, note, punkte, round(stunden, 1) AS stunden, - stundensatz, betreuerart_kurzbz, vertrag_id, titelpre, titelpost' - ); - $this->ProjektbetreuerModel->addSelect("CASE - WHEN EXISTS - (SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid) WHERE person_id=pers.person_id) - THEN 'Mitarbeiter' - WHEN EXISTS - (SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_student ON(uid=student_uid) WHERE person_id=pers.person_id) - THEN 'Student' - ELSE 'Person' - END AS status"); - $this->ProjektbetreuerModel->addJoin('public.tbl_person pers', 'person_id'); - $result = $this->ProjektbetreuerModel->loadWhere(['projektarbeit_id' => $projektarbeit_id]); + $qry = " + SELECT * FROM ( + SELECT + projektarbeit_id, person_id, nachname, vorname, note, punkte, round(stunden, 1) AS stunden, + stundensatz, betreuerart_kurzbz, vertrag_id, titelpre, titelpost, + CASE + WHEN EXISTS + (SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_mitarbeiter ON(uid=mitarbeiter_uid) WHERE person_id=pers.person_id) + THEN 'Mitarbeiter' + WHEN EXISTS + (SELECT 1 FROM public.tbl_benutzer JOIN public.tbl_student ON(uid=student_uid) WHERE person_id=pers.person_id) + THEN 'Student' + ELSE 'Person' + END AS status + FROM + lehre.tbl_projektbetreuer + JOIN public.tbl_person pers USING (person_id) + WHERE + projektarbeit_id = ? + ) betreuer + ORDER BY + CASE WHEN status = 'Mitarbeiter' THEN 0 WHEN status = 'Person' THEN 1 ELSE 2 END"; + + $result = $this->ProjektbetreuerModel->execReadOnlyQuery($qry, [$projektarbeit_id]); if (isError($result)) $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); @@ -245,7 +254,18 @@ class Projektbetreuer extends FHCAPI_Controller if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - return $this->terminateWithSuccess(hasData($result) ? $this->_addFullNameToBetreuer(getData($result)) : []); + // sort persons + if (!hasData($result)) $this->terminateWithSuccess([]); + + $persons = $this->_addFullNameToBetreuer(getData($result)); + usort($persons, function($a, $b) + { + $statusRanks = ['Mitarbeiter' => 0, 'Person' => 1, 'Student' => 2]; + return (isset($statusRanks[$a->status]) ? $statusRanks[$a->status] : count($statusRanks) + 1) + - (isset($statusRanks[$b->status]) ? $statusRanks[$b->status] : count($statusRanks) + 1); + }); + + return $this->terminateWithSuccess($persons); } public function getPerson() diff --git a/application/models/education/Projektarbeit_model.php b/application/models/education/Projektarbeit_model.php index 1ca2a18a8..414d1d1be 100644 --- a/application/models/education/Projektarbeit_model.php +++ b/application/models/education/Projektarbeit_model.php @@ -24,17 +24,28 @@ class Projektarbeit_model extends DB_Model public function getProjektarbeit($student_uid, $studiengang_kz = null, $studiensemester_kurzbz = null, $projekttyp = null, $final = null) { $qry = "SELECT - tbl_projektarbeit.*, tbl_projekttyp.bezeichnung, + pa.*, tbl_projekttyp.bezeichnung, tbl_lehreinheit.studiensemester_kurzbz, tbl_lehrveranstaltung.lehrveranstaltung_id, - tbl_firma.name AS firma_name + tbl_firma.name AS firma_name, + ( + SELECT + STRING_AGG(trim(COALESCE(titelpre,'')||' '||COALESCE(vorname,'')||' '||COALESCE(nachname,'')||' '||COALESCE(titelpost,'')), ', ') + FROM + lehre.tbl_projektbetreuer + JOIN public.tbl_person USING (person_id) + WHERE + projektarbeit_id = pa.projektarbeit_id + AND student_uid = pa.student_uid + GROUP BY projektarbeit_id + ) AS projektbetreuer FROM - lehre.tbl_projektarbeit + lehre.tbl_projektarbeit pa JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz) JOIN lehre.tbl_lehreinheit USING (lehreinheit_id) JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id) LEFT JOIN public.tbl_firma USING (firma_id) WHERE - tbl_projektarbeit.student_uid = ?"; + pa.student_uid = ?"; $params = array($student_uid); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js index d14f618e1..34dd99c8f 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Details.js @@ -195,12 +195,15 @@ export default { ) .catch(this.$fhcAlert.handleSystemError); }, + lvChanged(event) { + this.formData.lehreinheit_id = null; + }, studiensemesterChanged() { this.formData.lehreinheit_id = null; this.getLehrveranstaltungen(); }, - lvChanged(event) { - this.formData.lehreinheit_id = null; + gesperrtBisChanged(newSperrdatum) { + this.formData.freigegeben = newSperrdatum == null || newSperrdatum == ''; }, // enrich and modify data before sending getPreparedFormData() { @@ -400,6 +403,7 @@ export default { format="dd.MM.yyyy" model-type="yyyy-MM-dd" name="gesperrtbis" + @update:model-value="gesperrtBisChanged" >
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js index f831ece7d..5c41d6ecc 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js @@ -47,16 +47,25 @@ export default { { event: 'tableBuilt', handler: async() => { - await this.$p.loadCategory(['global', 'person', 'stv', 'ui', 'projektarbeit']); + await this.$p.loadCategory(['global', 'person', 'lehre', 'stv', 'ui', 'projektarbeit']); let cm = this.$refs.table.tabulator.columnManager; cm.getColumnByField('projekttyp_kurzbz').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'typKurzbezeichnung') + }); + cm.getColumnByField('bezeichnung').component.updateDefinition({ title: this.$p.t('projektarbeit', 'typ') }); + cm.getColumnByField('studiensemester_kurzbz').component.updateDefinition({ + title: this.$p.t('lehre', 'studiensemester') + }); cm.getColumnByField('titel').component.updateDefinition({ title: this.$p.t('projektarbeit', 'titel') }); + cm.getColumnByField('note').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'gesamtnote') + }); cm.getColumnByField('beginn').component.updateDefinition({ title: this.$p.t('projektarbeit', 'beginn') }); @@ -78,6 +87,12 @@ export default { cm.getColumnByField('firma_id').component.updateDefinition({ title: this.$p.t('projektarbeit', 'firmaId') }); + cm.getColumnByField('abgabedatum').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'abgabeEndupload') + }); + cm.getColumnByField('actions').component.updateDefinition({ + title: this.$p.t('global', 'aktionen') + }); } }, ], @@ -101,6 +116,7 @@ export default { {title: "Typ Kurzbz", field: "projekttyp_kurzbz", visible: false}, {title: "Studiensemester", field: "studiensemester_kurzbz"}, {title: "Titel", field: "titel"}, + {title: "Gesamtnote", field: "note"}, { title: "Abgabe Enduplad", field: "abgabedatum", @@ -183,6 +199,7 @@ export default { {title: "Anmerkung", field: "anmerkung", visible: false}, {title: "Lehreinheit ID", field: "lehreinheit_id", visible: false}, {title: "Student UID", field: "student_uid", visible: false}, + {title: "Projektbetreuer", field: "projektbetreuer", visible: false}, { title:"Final", field:"final", @@ -388,7 +405,7 @@ export default {
- +
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index f847a1e92..a36284f32 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -20,6 +20,7 @@ export default { Contact, Vertrag }, + emits: ['betreuerSaved'], provide() { return { configShowVertragsdetails: this.config.showVertragsdetails @@ -131,7 +132,43 @@ export default { { event: 'tableBuilt', handler: async() => { - await this.$p.loadCategory(['global', 'person', 'stv', 'projektarbeit', 'ui']); + await this.$p.loadCategory(['global', 'person', 'lehre', 'stv', 'projektarbeit', 'ui']); + + let cm = this.$refs.projektbetreuerTable.tabulator.columnManager; + + cm.getColumnByField('nachname').component.updateDefinition({ + title: this.$p.t('person', 'nachname') + }); + cm.getColumnByField('vorname').component.updateDefinition({ + title: this.$p.t('person', 'vorname') + }); + cm.getColumnByField('note').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'note') + }); + cm.getColumnByField('punkte').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'titel') + }); + cm.getColumnByField('stunden').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'stunden') + }); + cm.getColumnByField('stundensatz').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'stundensatz') + }); + cm.getColumnByField('betreuerart_kurzbz').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'betreuerart_kurzbz') + }); + cm.getColumnByField('person_id').component.updateDefinition({ + title: this.$p.t('system', 'person_id') + }); + cm.getColumnByField('vertrag_id').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'vertrag_id') + }); + cm.getColumnByField('projektarbeit_id').component.updateDefinition({ + title: this.$p.t('projektarbeit', 'projektarbeit_id') + }); + cm.getColumnByField('actions').component.updateDefinition({ + title: this.$p.t('global', 'actions') + }); // Force layout recalculation for handling overflow text this.$refs.projektbetreuerTable.tabulator.redraw(true); @@ -173,6 +210,7 @@ export default { methods: { actionNewProjektbetreuer() { this.resetForm(); + this.defaultStundensatz = this.config.defaultProjektbetreuerStundensatz; this.newMode = !this.newMode; this.editMode = false; this.captureFormData(); @@ -279,6 +317,7 @@ export default { this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); this.getProjektbetreuer(this.projektarbeit_id, this.studiensemester_kurzbz); this.resetModes(); + this.$emit('betreuerSaved'); }) .catch(this.$fhcAlert.handleSystemError); }, @@ -300,7 +339,6 @@ export default { resetForm() { const defaultFormData = this.getDefaultFormData(); this.formData = defaultFormData; - this.defaultStundensatz = defaultFormData.stundensatz; if (this.beurteilungDownloadLink !== null) this.beurteilungDownloadLink = ''; this.autocompleteSelectedBetreuer = null; this.initialFormData = null; @@ -404,6 +442,7 @@ export default { .then(response => { // set the new person in Betreuer autocomplete field this.autocompleteSelectedBetreuer = response.data; + this.getDefaultStundensaetze(this.autocompleteSelectedBetreuer.person_id); }) .catch(this.$fhcAlert.handleSystemError) }, diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 257b05c03..61fcc062a 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -51665,6 +51665,126 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'punkte', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Punkte', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'points', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'gesamtnote', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Gesamtnote', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'final grade', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'abgabeEndupload', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Abgabe Endupload', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'final submission upload', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'vertrag_id', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Vertrag ID', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'contract ID', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'projektarbeit_id', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Projektarbeit ID', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'project work ID', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'betreuerart_kurzbz', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Betreuerart Kurzbezeichnung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'project assessor short name', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // FHC-4 Projektarbeiten & Vertraege ENDE // ### DOKUMENTE ERSTELLEN PHRASEN START ### array( From 016d4a1927fa7e7a723c0e9bd833d431b6c51df8 Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Sat, 6 Dec 2025 01:28:38 +0100 Subject: [PATCH 04/10] Studierendenverwaltung Projektbetreuer: added phrases, added check for existing Projektbetreuer when saving --- .../api/frontend/v1/stv/Projektbetreuer.php | 35 +++++++++------- .../Details/Projektarbeit/Projektarbeit.js | 4 +- system/phrasesupdate.php | 40 +++++++++++++++++++ 3 files changed, 63 insertions(+), 16 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index 4eec773c5..777988b35 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -123,24 +123,31 @@ class Projektbetreuer extends FHCAPI_Controller if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id)) return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]); - if (isset($projektbetreuer['stunden']) && !isEmptyString($projektbetreuer['stunden']) && !is_numeric($projektbetreuer['stunden'])) - { - return $this->terminateWithError( - $this->p->t('ui', 'error_fieldNotNumeric', ['field'=> 'Stunden', 'Field'=> 'Stunden']), self::ERROR_TYPE_GENERAL - ); - } - - if (isset($projektbetreuer['stundensatz']) && !isEmptyString($projektbetreuer['stundensatz']) && !is_numeric($projektbetreuer['stundensatz'])) - { - return $this->terminateWithError( - $this->p->t('ui', 'error_fieldNotNumeric', ['field'=> 'Stundensatz', 'Field'=> 'Stundensatz']), self::ERROR_TYPE_GENERAL - ); - } - $projektbetreuer = $this->input->post('projektbetreuer'); if ($this->_validate($projektbetreuer) == false) $this->terminateWithValidationErrors($this->form_validation->error_array()); + // check if assessor has already been assigned + if ( + isset($projektbetreuer['person_id']) + && isset($projektbetreuer['person_id_old']) + && $projektbetreuer['person_id'] != $projektbetreuer['person_id_old'] + ) { + $this->ProjektbetreuerModel->addSelect('1'); + $betreuerRes = $this->ProjektbetreuerModel->loadWhere( + [ + 'person_id' => $projektbetreuer['person_id'], + 'projektarbeit_id' => $projektbetreuer['projektarbeit_id'], + 'betreuerart_kurzbz' => $projektbetreuer['betreuerart_kurzbz'] + ] + ); + + if (hasData($betreuerRes)) + { + return $this->terminateWithError($this->p->t('projektarbeit', 'betreuerZugewiesen'), self::ERROR_TYPE_GENERAL); + } + } + $result = null; $stunden = isset($projektbetreuer['stunden']) && !isEmptyString($projektbetreuer['stunden']) ? $projektbetreuer['stunden'] : null; diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js index 5c41d6ecc..3ff1ea97f 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektarbeit.js @@ -52,7 +52,7 @@ export default { let cm = this.$refs.table.tabulator.columnManager; cm.getColumnByField('projekttyp_kurzbz').component.updateDefinition({ - title: this.$p.t('projektarbeit', 'typKurzbezeichnung') + title: this.$p.t('projektarbeit', 'typ_kurzbz') }); cm.getColumnByField('bezeichnung').component.updateDefinition({ title: this.$p.t('projektarbeit', 'typ') @@ -199,7 +199,7 @@ export default { {title: "Anmerkung", field: "anmerkung", visible: false}, {title: "Lehreinheit ID", field: "lehreinheit_id", visible: false}, {title: "Student UID", field: "student_uid", visible: false}, - {title: "Projektbetreuer", field: "projektbetreuer", visible: false}, + {title: "Projektbetreuer", field: "projektbetreuer"}, { title:"Final", field:"final", diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 61fcc062a..278ad38e2 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -51785,6 +51785,46 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'typ_kurzbz', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Typ Kurzbezeichnung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'type short name', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'betreuerZugewiesen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Betreuer*In ist bereits zugewiesen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'This project assessor is already assigned', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // FHC-4 Projektarbeiten & Vertraege ENDE // ### DOKUMENTE ERSTELLEN PHRASEN START ### array( From 98bcbda53e3fd55e2c9bdf59f42a6f17149aa74e Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 10 Dec 2025 16:09:44 +0100 Subject: [PATCH 05/10] added check for Projektarbeitsbeurteilung when deleting Projektarbeit --- .../api/frontend/v1/stv/Projektbetreuer.php | 12 +++++++++++ system/dbupdate_3.4.php | 1 + ...69065_Projektarbeiten_Firmen_verwalten.php | 20 +++++++++++++++++++ system/phrasesupdate.php | 20 +++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 system/dbupdate_3.4/69065_Projektarbeiten_Firmen_verwalten.php diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index 777988b35..bfc76088c 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -208,6 +208,18 @@ class Projektbetreuer extends FHCAPI_Controller if (isError($validate)) return $this->terminateWithError(getError($validate), self::ERROR_TYPE_GENERAL); + $beurteilungDeleteSuccess = true; + + Events::trigger( + 'projektarbeitsbeurteilung_delete', + $projektarbeit_id, + function ($value) use (&$beurteilungDeleteSuccess) { + $beurteilungDeleteSuccess = $value; + } + ); + + if (!$beurteilungDeleteSuccess) return $this->terminateWithError($this->p->t('projektarbeit', 'error_paarbeitHatBeurteilung')); + $result = $this->ProjektbetreuerModel->delete( ['projektarbeit_id' => $projektarbeit_id, 'person_id' => $person_id, 'betreuerart_kurzbz' => $betreuerart_kurzbz] ); diff --git a/system/dbupdate_3.4.php b/system/dbupdate_3.4.php index f9c889110..72c4ae92a 100644 --- a/system/dbupdate_3.4.php +++ b/system/dbupdate_3.4.php @@ -85,6 +85,7 @@ require_once('dbupdate_3.4/66982_berufsschule.php'); require_once('dbupdate_3.4/40314_electronic_onboarding_anbindung_ida.php'); require_once('dbupdate_3.4/47972_pruefungsverwaltung_ects_angabe.php'); require_once('dbupdate_3.4/67490_studstatus_suche_abort_controller_haengt.php'); +require_once('dbupdate_3.4/69065_Projektarbeiten_Firmen_verwalten.php'); // *** Pruefung und hinzufuegen der neuen Attribute und Tabellen echo '

Pruefe Tabellen und Attribute!

'; diff --git a/system/dbupdate_3.4/69065_Projektarbeiten_Firmen_verwalten.php b/system/dbupdate_3.4/69065_Projektarbeiten_Firmen_verwalten.php new file mode 100644 index 000000000..52b546846 --- /dev/null +++ b/system/dbupdate_3.4/69065_Projektarbeiten_Firmen_verwalten.php @@ -0,0 +1,20 @@ +db_query("SELECT 1 FROM system.tbl_berechtigung WHERE berechtigung_kurzbz = 'paarbeit/beurteilung_loeschen';")) +{ + if($db->db_num_rows($result) == 0) + { + $qry = "INSERT INTO system.tbl_berechtigung(berechtigung_kurzbz, beschreibung) VALUES('paarbeit/beurteilung_loeschen', 'Berechtigung zum Löschen von Projektarbeitsbeurteilung');"; + + if(!$db->db_query($qry)) + { + echo 'system.tbl_berechtigung '.$db->db_last_error().'
'; + } + else + { + echo 'system.tbl_berechtigung: Added permission for paarbeit/beurteilung_loeschen
'; + } + } +} \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 278ad38e2..34f5c7b5e 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -51825,6 +51825,26 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + array( + 'app' => 'core', + 'category' => 'projektarbeit', + 'phrase' => 'error_paarbeitHatBeurteilung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Für diese Projektarbeit ist bereits eine Projektarbeitsbeurteilung eingetragen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'This project work has already been assessed', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // FHC-4 Projektarbeiten & Vertraege ENDE // ### DOKUMENTE ERSTELLEN PHRASEN START ### array( From 32b73b72871029edbfa1839ada4f6a7b81dffc3c Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 10 Dec 2025 18:03:04 +0100 Subject: [PATCH 06/10] Studierendenverwaltung Projektarbeiten: delete checks bugfixes, layout guidelines --- .../api/frontend/v1/stv/Projektbetreuer.php | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index bfc76088c..6aa86e4d4 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -3,7 +3,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); use \DateTime as DateTime; -use CI3_Events as Events; +use \CI3_Events as Events; class Projektbetreuer extends FHCAPI_Controller { @@ -128,11 +128,8 @@ class Projektbetreuer extends FHCAPI_Controller if ($this->_validate($projektbetreuer) == false) $this->terminateWithValidationErrors($this->form_validation->error_array()); // check if assessor has already been assigned - if ( - isset($projektbetreuer['person_id']) - && isset($projektbetreuer['person_id_old']) - && $projektbetreuer['person_id'] != $projektbetreuer['person_id_old'] - ) { + if (isset($projektbetreuer['person_id'])) + { $this->ProjektbetreuerModel->addSelect('1'); $betreuerRes = $this->ProjektbetreuerModel->loadWhere( [ @@ -142,15 +139,15 @@ class Projektbetreuer extends FHCAPI_Controller ] ); - if (hasData($betreuerRes)) - { + if (hasData($betreuerRes) + && (!isset($projektbetreuer['person_id_old']) || $projektbetreuer['person_id'] != $projektbetreuer['person_id_old'])) { return $this->terminateWithError($this->p->t('projektarbeit', 'betreuerZugewiesen'), self::ERROR_TYPE_GENERAL); } } $result = null; - $stunden = isset($projektbetreuer['stunden']) && !isEmptyString($projektbetreuer['stunden']) ? $projektbetreuer['stunden'] : null; + $stunden = isset($projektbetreuer['stunden']) && !isEmptyString($projektbetreuer['stunden']) ? $projektbetreuer['stunden'] : null; $stundensatz = isset($projektbetreuer['stundensatz']) && !isEmptyString($projektbetreuer['stundensatz']) ? $projektbetreuer['stundensatz'] : null; @@ -193,18 +190,26 @@ class Projektbetreuer extends FHCAPI_Controller $betreuerart_kurzbz = $this->input->post('betreuerart_kurzbz'); if (!isset($projektarbeit_id) || !is_numeric($projektarbeit_id)) - return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> $this->p->t('projektarbeit', 'projektarbeit').' ID'], self::ERROR_TYPE_GENERAL)); + { + return $this->terminateWithError( + $this->p->t('ui', 'error_missingId', ['id'=> $this->p->t('projektarbeit', 'projektarbeit').' ID'], self::ERROR_TYPE_GENERAL) + ); + } if (!isset($person_id) || !is_numeric($person_id)) return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Person ID'], self::ERROR_TYPE_GENERAL)); if (!isset($betreuerart_kurzbz)) - return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> $this->p->t('projektarbeit', 'betreuerart')], self::ERROR_TYPE_GENERAL)); + { + return $this->terminateWithError( + $this->p->t('ui', 'error_missingId', ['id'=> $this->p->t('projektarbeit', 'betreuerart')], self::ERROR_TYPE_GENERAL) + ); + } if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id)) return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]); - $validate = $this->_validateDelete($projektarbeit_id, $person_id); + $validate = $this->_validateDelete($projektarbeit_id, $person_id, $betreuerart_kurzbz); if (isError($validate)) return $this->terminateWithError(getError($validate), self::ERROR_TYPE_GENERAL); @@ -231,7 +236,7 @@ class Projektbetreuer extends FHCAPI_Controller $this->outputJson($result); } - return $this->terminateWithSuccess(current(getData($result)) ? : null); + return $this->terminateWithSuccess(getData($result)); } public function getBetreuerarten() @@ -277,8 +282,7 @@ class Projektbetreuer extends FHCAPI_Controller if (!hasData($result)) $this->terminateWithSuccess([]); $persons = $this->_addFullNameToBetreuer(getData($result)); - usort($persons, function($a, $b) - { + usort($persons, function ($a, $b) { $statusRanks = ['Mitarbeiter' => 0, 'Person' => 1, 'Student' => 2]; return (isset($statusRanks[$a->status]) ? $statusRanks[$a->status] : count($statusRanks) + 1) - (isset($statusRanks[$b->status]) ? $statusRanks[$b->status] : count($statusRanks) + 1); @@ -367,10 +371,12 @@ class Projektbetreuer extends FHCAPI_Controller * @param * @return object success or error */ - private function _validateDelete($projektarbeit_id, $person_id) + private function _validateDelete($projektarbeit_id, $person_id, $betreuerart_kurzbz) { $this->ProjektbetreuerModel->addSelect('vertrag_id'); - $result = $this->ProjektbetreuerModel->loadWhere(['projektarbeit_id' => $projektarbeit_id, 'person_id' => $person_id]); + $result = $this->ProjektbetreuerModel->loadWhere( + ['projektarbeit_id' => $projektarbeit_id, 'person_id' => $person_id, 'betreuerart_kurzbz' => $betreuerart_kurzbz] + ); if (isError($result)) return $result; From 80f80ab8e8106ab8d85bb96d40e36cf0c5518dee Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 10 Dec 2025 19:43:15 +0100 Subject: [PATCH 07/10] added comments to Projektbetreuer --- .../api/frontend/v1/stv/Projektbetreuer.php | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php index 6aa86e4d4..904fa6167 100644 --- a/application/controllers/api/frontend/v1/stv/Projektbetreuer.php +++ b/application/controllers/api/frontend/v1/stv/Projektbetreuer.php @@ -43,6 +43,9 @@ class Projektbetreuer extends FHCAPI_Controller $this->load->library('PermissionLib'); } + /** + * Gets Projektbetreuer data for a Projektarbeit. + */ public function getProjektbetreuer() { $projektarbeit_id = $this->input->get('projektarbeit_id'); @@ -95,6 +98,7 @@ class Projektbetreuer extends FHCAPI_Controller //~ } //~ } + // add thesis download link (from external extension) foreach ($projektbetreuer as $pb) { $downloadLink = null; @@ -113,6 +117,9 @@ class Projektbetreuer extends FHCAPI_Controller $this->terminateWithSuccess($this->_addFullNameToBetreuer($projektbetreuer)); } + /** + * Saves (adds or updates) a single Projektbetreuer for a Projektarbeit. + */ public function saveProjektbetreuer() { $projektarbeit_id = $this->input->post('projektarbeit_id'); @@ -183,6 +190,9 @@ class Projektbetreuer extends FHCAPI_Controller $this->terminateWithSuccess(hasData($result) ? getData($result) : []); } + /** + * Delete a Projektbetreuer assignment to a Projektarbeit. + */ public function deleteProjektbetreuer() { $projektarbeit_id = $this->input->post('projektarbeit_id'); @@ -206,6 +216,7 @@ class Projektbetreuer extends FHCAPI_Controller ); } + // check permission if (!$this->ProjektarbeitModel->hasBerechtigungForProjektarbeit($projektarbeit_id)) return $this->_outputAuthError([$this->router->method => ['admin:rw', 'assistenz:rw']]); @@ -213,6 +224,7 @@ class Projektbetreuer extends FHCAPI_Controller if (isError($validate)) return $this->terminateWithError(getError($validate), self::ERROR_TYPE_GENERAL); + // check if there is a Projektarbeitsbeurteilung - if yes, it is handled (possibly deleted) by external extension. $beurteilungDeleteSuccess = true; Events::trigger( @@ -223,6 +235,7 @@ class Projektbetreuer extends FHCAPI_Controller } ); + // if there is still a Beurteilung, Projektarbeit cannot be deleted - return with error if (!$beurteilungDeleteSuccess) return $this->terminateWithError($this->p->t('projektarbeit', 'error_paarbeitHatBeurteilung')); $result = $this->ProjektbetreuerModel->delete( @@ -239,6 +252,9 @@ class Projektbetreuer extends FHCAPI_Controller return $this->terminateWithSuccess(getData($result)); } + /** + * Get all active Betreuerarten. + */ public function getBetreuerarten() { $result = $this->BetreuerartModel->loadWhere(['aktiv' => true]); @@ -248,6 +264,9 @@ class Projektbetreuer extends FHCAPI_Controller return $this->terminateWithSuccess(hasData($result) ? getData($result) : []); } + /** + * Get all Noten. + */ public function getNoten() { $result = $this->NoteModel->load(); @@ -257,6 +276,9 @@ class Projektbetreuer extends FHCAPI_Controller return $this->terminateWithSuccess(hasData($result) ? getData($result) : []); } + /** + * Get default Stundensätze for an employee in a semester. + */ public function getDefaultStundensaetze() { $person_id = $this->input->get('person_id'); @@ -267,6 +289,9 @@ class Projektbetreuer extends FHCAPI_Controller return $this->terminateWithSuccess($result); } + /** + * Get all Projektbetreuer by search string. + */ public function getProjektbetreuerBySearchQuery() { $searchString = $this->input->get('searchString'); @@ -278,10 +303,11 @@ class Projektbetreuer extends FHCAPI_Controller if (isError($result)) return $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); - // sort persons if (!hasData($result)) $this->terminateWithSuccess([]); $persons = $this->_addFullNameToBetreuer(getData($result)); + + // sort persons by type (employees first) usort($persons, function ($a, $b) { $statusRanks = ['Mitarbeiter' => 0, 'Person' => 1, 'Student' => 2]; return (isset($statusRanks[$a->status]) ? $statusRanks[$a->status] : count($statusRanks) + 1) @@ -291,6 +317,9 @@ class Projektbetreuer extends FHCAPI_Controller return $this->terminateWithSuccess($persons); } + /** + * Get person info by Id. + */ public function getPerson() { $person_id = $this->input->get('person_id'); @@ -316,9 +345,7 @@ class Projektbetreuer extends FHCAPI_Controller } /** - * - * @param - * @return object success or error + * Validate list of Projektbetreuer. */ public function validateProjektbetreuer() { @@ -338,9 +365,9 @@ class Projektbetreuer extends FHCAPI_Controller } /** - * - * @param - * @return object success or error + * Validation funciton for checking Projektbetreuer input. + * @param $formData Betreuer data + * @return bool true when data is valid */ private function _validate($formData) { @@ -367,12 +394,15 @@ class Projektbetreuer extends FHCAPI_Controller } /** - * - * @param - * @return object success or error + * Check possibility of deletion of a Projektbetreuer. + * @param projektarbeit_id + * @param person_id + * @param betreuerart_kurzbz + * @return object success when delete possible, error otherwise */ private function _validateDelete($projektarbeit_id, $person_id, $betreuerart_kurzbz) { + // check if contract exists $this->ProjektbetreuerModel->addSelect('vertrag_id'); $result = $this->ProjektbetreuerModel->loadWhere( ['projektarbeit_id' => $projektarbeit_id, 'person_id' => $person_id, 'betreuerart_kurzbz' => $betreuerart_kurzbz] @@ -380,15 +410,16 @@ class Projektbetreuer extends FHCAPI_Controller if (isError($result)) return $result; + // if contract exists, no deletion is possible if (hasData($result) && getData($result)[0]->vertrag_id != null) return error($this->p->t('projektarbeit', 'error_betreuerHatVertrag')); return success(); } /** - * - * @param - * @return object success or error + * Add full name to array with Betreuer. + * @param $betreuerArr + * @return array including Betreuer with their full names */ private function _addFullNameToBetreuer($betreuerArr) { From 1c511f9c66c6f2c0494a897ce7189b2251fbbbec Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Thu, 11 Dec 2025 11:42:31 +0100 Subject: [PATCH 08/10] Projektarbeit controller: added comments --- .../api/frontend/v1/stv/Projektarbeit.php | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Projektarbeit.php b/application/controllers/api/frontend/v1/stv/Projektarbeit.php index 1752cee92..db7d99752 100644 --- a/application/controllers/api/frontend/v1/stv/Projektarbeit.php +++ b/application/controllers/api/frontend/v1/stv/Projektarbeit.php @@ -47,6 +47,9 @@ class Projektarbeit extends FHCAPI_Controller $this->load->library('PermissionLib'); } + /** + * Get projekt works for a uid. + */ public function getProjektarbeit() { $student_uid = $this->input->get('uid'); @@ -78,6 +81,9 @@ class Projektarbeit extends FHCAPI_Controller $this->terminateWithSuccess($projektarbeiten); } + /** + * Load a single Projektarbeit by id. + */ public function loadProjektarbeit() { $projektarbeit_id = $this->input->get('projektarbeit_id'); @@ -100,6 +106,9 @@ class Projektarbeit extends FHCAPI_Controller $this->terminateWithSuccess(current($data)); } + /** + * Inwert a Projektarbeit. + */ public function insertProjektarbeit() { $student_uid = $this->input->post('uid'); @@ -127,6 +136,9 @@ class Projektarbeit extends FHCAPI_Controller $this->terminateWithSuccess($data); } + /** + * Update a Projektarbeit by ID. + */ public function updateProjektarbeit() { $projektarbeit_id = $this->input->post('projektarbeit_id'); @@ -156,6 +168,9 @@ class Projektarbeit extends FHCAPI_Controller $this->terminateWithSuccess($data); } + /** + * Delete Projektarbeit by ID after validation. + */ public function deleteProjektarbeit() { $projektarbeit_id = $this->input->post('projektarbeit_id'); @@ -184,6 +199,9 @@ class Projektarbeit extends FHCAPI_Controller return $this->terminateWithSuccess(current(getData($result)) ? : null); } + /** + * Get all active projekt work types. + */ public function getTypenProjektarbeit() { $result = $this->ProjekttypModel->loadWhere(['aktiv' => true]); @@ -193,6 +211,9 @@ class Projektarbeit extends FHCAPI_Controller return $this->terminateWithSuccess(hasData($result) ? getData($result) : []); } + /** + * Gets companies by search string. + */ public function getFirmen() { $searchString = $this->input->get('searchString'); @@ -207,6 +228,9 @@ class Projektarbeit extends FHCAPI_Controller return $this->terminateWithSuccess(hasData($result) ? getData($result) : []); } + /** + * Get Lehrveranstaltungen by params, incling lehreinheiten for a specific Studiensemester.. + */ public function getLehrveranstaltungen() { $student_uid = $this->input->get('student_uid'); @@ -217,6 +241,7 @@ class Projektarbeit extends FHCAPI_Controller if (!isset($student_uid)) $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL); if (!isset($studiensemester_kurzbz)) $this->terminateWithError('Studiensemster missing', self::ERROR_TYPE_GENERAL); + // get Lvs $lvsResult = $this->LehrveranstaltungModel->getLvsForProjektarbeit($student_uid, $studiengang_kz, $additional_lehrveranstaltung_id); if (isError($lvsResult)) return $this->terminateWithError($lvsResult, self::ERROR_TYPE_GENERAL); @@ -225,6 +250,7 @@ class Projektarbeit extends FHCAPI_Controller foreach ($lvs as $lv) { + // add Lehreinheiten for each Lv for the semester $lehreinheiten = $this->LehreinheitModel->getLesForLv( $lv->lehrveranstaltung_id, $studiensemester_kurzbz ); @@ -249,6 +275,9 @@ class Projektarbeit extends FHCAPI_Controller return $this->terminateWithSuccess($lvs); } + /** + * Get all noten. + */ public function getNoten() { $result = $this->NoteModel->load(); @@ -258,6 +287,9 @@ class Projektarbeit extends FHCAPI_Controller return $this->terminateWithSuccess(hasData($result) ? getData($result) : []); } + /** + * Get all Studiensemester, sorted. + */ public function getStudiensemester() { $this->StudiensemesterModel->addOrder('start', 'DESC'); @@ -269,9 +301,9 @@ class Projektarbeit extends FHCAPI_Controller } /** - * - * @param - * @return object success or error + * Validate Projektarbeit data. + * @param formData + * @return bool true if data valid */ private function _validate($formData) { @@ -302,9 +334,9 @@ class Projektarbeit extends FHCAPI_Controller } /** - * - * @param - * @return object success or error + * Extract Projektarbeit data from passed form data. + * @param formData + * @return array */ private function _getProjektarbeitArr($formData) { @@ -326,9 +358,9 @@ class Projektarbeit extends FHCAPI_Controller } /** - * - * @param - * @return object success or error + * Check if deletion of a Projektarbeit is possible. + * @param $projektarbeit_id + * @return object success if deletion possible, error otherwise. */ private function _validateDelete($projektarbeit_id) { @@ -349,6 +381,11 @@ class Projektarbeit extends FHCAPI_Controller return success(); } + /** + * Checks permissions for a student. + * @param $student_uid + * @return bool true if authorized + */ private function _hasBerechtigungForStudent($student_uid) { if (!$student_uid) From 95027937bdbe47a52cd9e6ff4fd7b43fa04f1996 Mon Sep 17 00:00:00 2001 From: Harald Bamberger Date: Thu, 11 Dec 2025 12:52:05 +0100 Subject: [PATCH 09/10] remove debug code --- application/config/stv.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/config/stv.php b/application/config/stv.php index df1646fa0..6af6deb90 100644 --- a/application/config/stv.php +++ b/application/config/stv.php @@ -87,7 +87,7 @@ if (!defined('ZGV_DOKTOR_ANZEIGEN') || !ZGV_DOKTOR_ANZEIGEN) { $fieldsZgvDoktor ); } -error_log(print_r(defined(FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_BACHELOR), true)); + $config['tabs']['projektarbeit']['defaultProjektbetreuerStunden'] = defined('FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_BACHELOR') ? FAS_STUDIERENDE_PROJEKTARBEIT_DEFAULT_BETREUER_STUNDEN_BACHELOR From 91f141326abed549f7a1bcc40f912fadaa5880df Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Thu, 11 Dec 2025 15:38:53 +0100 Subject: [PATCH 10/10] Studierendenverwaltung Projektarbeiten PRojektbetreuer: replaced "formdata modified" check with edit mode check --- .../Details/Projektarbeit/Projektbetreuer.js | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js index b830caa66..bbbf3be71 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js @@ -209,10 +209,10 @@ export default { }, methods: { actionNewProjektbetreuer() { - this.resetForm(); - this.defaultStundensatz = this.config.defaultProjektbetreuerStundensatz; this.newMode = !this.newMode; this.editMode = false; + this.resetForm(); + this.defaultStundensatz = this.config.defaultProjektbetreuerStundensatz; this.captureFormData(); }, actionEditProjektbetreuer(projektarbeit_id, person_id, betreuerart_kurzbz) { @@ -397,12 +397,10 @@ export default { }, setDefaultStunden(projekttyp_kurzbz) { this.projekttyp_kurzbz = projekttyp_kurzbz; - // if form data has not already been modified by user, set the default stunden - if (!this.formDataModified()) { + // set default stunden if not in edit mode (so hours of editen person are not overwritten) + if (!this.editMode) + { let defaultStunden = this.getDefaultStunden(projekttyp_kurzbz); - // adapt initial form data so it does not count as modified - if (this.initialFormData) this.initialFormData.stunden = defaultStunden; - // set default Stunden this.formData.stunden = defaultStunden; } }, @@ -416,17 +414,6 @@ export default { }) .catch(this.$fhcAlert.handleSystemError); }, - // check if form data has been modified since initial data has been captured - formDataModified() { - if (this.autocompleteSelectedBetreuer != null) return true; - - for (const prop in this.initialFormData) { - if (typeof this.formData[prop] == 'undefined') return true; - if (this.formData[prop] != this.initialFormData[prop]) return true; - } - - return false; - }, actionNewPerson() { this.$refs.newPersonModal.reset(); this.$refs.newPersonModal.open();