From e574998897eb12d1768e77ed924431d6c0a46363 Mon Sep 17 00:00:00 2001 From: Johann Hoffmann Date: Thu, 11 Jun 2026 17:10:33 +0200 Subject: [PATCH] fetch both erst & zweitbetreuer in abgabetool betreueransicht; fix ProjektarbeitModel->getProjektbetreuerEmail by explicitely only querying erstbetreuer, which fixes rare cases where endupload crashes when the zweitbetreuer has no contact email as private mail AND randomly gets retrieved as first row by the query; fix race condition on the semester filter default value in assistenz view; block all edit/delete operations in flat table for any termin which already has a file uplaoded (abgabetermin != null) or has a note (due to quality gate), to avoid confusing UI implementations; fix STV Projektarbeit bug where betreuer form options where not loading correctly when editing existing projektarbeiten; --- .../controllers/api/frontend/v1/Abgabe.php | 27 ++-- .../models/education/Projektarbeit_model.php | 153 ++++++++++++++---- .../Cis/Abgabetool/AbgabetoolAssistenz.js | 27 ++-- .../Cis/Abgabetool/AbgabetoolMitarbeiter.js | 23 ++- .../Details/Projektarbeit/Projektbetreuer.js | 15 ++ public/js/helpers/EmailHelpers.js | 1 - system/phrasesupdate.php | 20 +++ 7 files changed, 210 insertions(+), 56 deletions(-) diff --git a/application/controllers/api/frontend/v1/Abgabe.php b/application/controllers/api/frontend/v1/Abgabe.php index 6e0ae2eef..faddcfec9 100644 --- a/application/controllers/api/frontend/v1/Abgabe.php +++ b/application/controllers/api/frontend/v1/Abgabe.php @@ -734,7 +734,7 @@ class Abgabe extends FHCAPI_Controller $showAllBool = false; } - $projektarbeiten = $this->ProjektarbeitModel->getMitarbeiterProjektarbeiten(getAuthUID(), $showAllBool); + $projektarbeiten = $this->ProjektarbeitModel->getMitarbeiterProjektarbeiten(getAuthPersonId(), $showAllBool); $mapFunc = function($projektarbeit) { @@ -749,18 +749,15 @@ class Abgabe extends FHCAPI_Controller forEach($projektarbeiten->retval as $pa) { - $result = $this->ProjektarbeitModel->getProjektbetreuerAnrede($pa->betreuer_person_id); - $anredeArr = $this->getDataOrTerminateWithError($result, 'general'); - $pa->betreuer = $anredeArr[0]; - $oldLink = ''; // show this when paIsCurrent == false -> moodle course template $newLink = ''; // get curated path for betreuer type - $returnFunc = function ( $resultOld, $resultNew) use (&$oldLink, &$newLink) { + $returnFunc = function ($resultOld, $resultNew) use (&$oldLink, &$newLink) { $newLink = $resultNew; $oldLink = $resultOld; }; - Events::trigger('projektbeurteilung_formular_link', $pa->betreuerart_kurzbz, APP_ROOT, $pa->projektarbeit_id, $pa->student_uid, $returnFunc); + $own_betreuerart_kurzbz = $pa->betreuer_person_id == getAuthPersonId() ? $pa->betreuer_betreuerart_kurzbz : $pa->zweitbetreuer_betreuerart_kurzbz; + Events::trigger('projektbeurteilung_formular_link', $own_betreuerart_kurzbz, APP_ROOT, $pa->projektarbeit_id, $pa->student_uid, $returnFunc); $pa->beurteilungLinkNew = $newLink; $pa->beurteilungLinkOld = $oldLink; @@ -1224,10 +1221,10 @@ class Abgabe extends FHCAPI_Controller private function getProjektbetreuerEmailByProjektarbeitID($projektarbeit_id) { $this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel'); $result = $this->ProjektarbeitModel->getProjektbetreuerEmail($projektarbeit_id); - if(count($result->retval) > 0) { + if(hasData($result)) { $email = getData($result); return $email[0]->uid ? $email[0]->uid.'@'.DOMAIN : $email[0]->private_email; - } else return ''; + } else return null; } @@ -1595,13 +1592,6 @@ class Abgabe extends FHCAPI_Controller $this->terminateWithError($this->p->t('abgabetool','c4fehlerAktualitaetProjektarbeitv2'), 'general'); } - // Link to Abgabetool - if (defined('CIS4') && CIS4) { - $ci3BootstrapFilePath = "cis.php"; - } else { - $ci3BootstrapFilePath = "index.ci.php"; - } - $path = $this->config->item('URL_MITARBEITER'); $url = APP_ROOT.$path; @@ -1631,8 +1621,6 @@ class Abgabe extends FHCAPI_Controller $maildata['token'] = ""; $email = $this->getProjektbetreuerEmailByProjektarbeitID($projektarbeit_id); - - if(!$email) $this->terminateWithError('early fail', 'general'); $mailres = sendSanchoMail( 'ParbeitsbeurteilungEndupload', @@ -1654,6 +1642,7 @@ class Abgabe extends FHCAPI_Controller // Zweitbegutachter holen $this->load->model('education/Projektbetreuer_model', 'ProjektbetreuerModel'); $zweitbegutachterRetval = getData($this->ProjektbetreuerModel->getZweitbegutachterWithToken($bperson_id, $projektarbeit_id, $studentUser->uid)); + $this->addMeta('$zweitbegutachterRetval', $zweitbegutachterRetval); if ($zweitbegutachterRetval && count($zweitbegutachterRetval) > 0) { @@ -1694,6 +1683,8 @@ class Abgabe extends FHCAPI_Controller $zweitbetmaildata['bewertunglink'] = $projektarbeitIsCurrent ? "

Zur Beurteilung der Arbeit

" : ""; $zweitbetmaildata['token'] = $projektarbeitIsCurrent && isset($begutachterMitToken->zugangstoken) && !$intern ? "

Zugangstoken: " . $begutachterMitToken->zugangstoken . "

" : ""; + $this->addMeta('$zweitbetmaildata', $zweitbetmaildata); + $mailres = sendSanchoMail( 'ParbeitsbeurteilungEndupload', $zweitbetmaildata, diff --git a/application/models/education/Projektarbeit_model.php b/application/models/education/Projektarbeit_model.php index f59855ba0..17f693795 100644 --- a/application/models/education/Projektarbeit_model.php +++ b/application/models/education/Projektarbeit_model.php @@ -251,7 +251,8 @@ class Projektarbeit_model extends DB_Model LEFT JOIN public.tbl_benutzer ben USING (person_id) LEFT JOIN public.tbl_mitarbeiter ma ON ben.uid = ma.mitarbeiter_uid WHERE (ben.aktiv OR ben.aktiv IS NULL) - AND projektarbeit_id = ?"; + AND projektarbeit_id = ? + AND lehre.tbl_projektbetreuer.betreuerart_kurzbz = ANY('{Erstbetreuer,Erstbegutachter,Senatsvorsitz}')"; return $this->execReadOnlyQuery($qry, [$projektarbeit_id]); } @@ -305,34 +306,132 @@ class Projektarbeit_model extends DB_Model * Get a List of Projektarbeiten of a mitarbeiter with zuordnung * used by the mitarbeiter cis4 abgabetool. */ - public function getMitarbeiterProjektarbeiten($uid, $showAll){ - $qry = "SELECT - * - FROM - (SELECT tbl_person.vorname, tbl_person.nachname, tbl_studiengang.typ, tbl_studiengang.kurzbz, - tbl_projektarbeit.projekttyp_kurzbz, tbl_projekttyp.bezeichnung, tbl_projektarbeit.titel, tbl_projektarbeit.projektarbeit_id, tbl_projektarbeit.note, - tbl_projektbetreuer.person_id as betreuer_person_id, tbl_projektbetreuer.betreuerart_kurzbz, tbl_betreuerart.beschreibung AS betreuerart_beschreibung, - tbl_benutzer.uid, tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz, public.tbl_student.student_uid - FROM lehre.tbl_projektarbeit - LEFT JOIN lehre.tbl_projektbetreuer using(projektarbeit_id) - LEFT JOIN lehre.tbl_betreuerart using(betreuerart_kurzbz) - LEFT JOIN public.tbl_benutzer on(uid=student_uid) - LEFT JOIN public.tbl_student on(public.tbl_benutzer.uid=public.tbl_student.student_uid) - LEFT JOIN public.tbl_person on(tbl_benutzer.person_id=tbl_person.person_id) - LEFT JOIN lehre.tbl_lehreinheit using(lehreinheit_id) - LEFT JOIN lehre.tbl_lehrveranstaltung using(lehrveranstaltung_id) - LEFT JOIN public.tbl_studiengang on(lehre.tbl_lehrveranstaltung.studiengang_kz=public.tbl_studiengang.studiengang_kz) - LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz) - WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom') - AND tbl_projektbetreuer.person_id IN (SELECT person_id FROM public.tbl_benutzer - WHERE public.tbl_benutzer.person_id=lehre.tbl_projektbetreuer.person_id - AND public.tbl_benutzer.uid= ? ) - ".($showAll?'':' AND public.tbl_benutzer.aktiv AND lehre.tbl_projektarbeit.note IS NULL ')." + public function getMitarbeiterProjektarbeiten($person_id, $showAll){ + $qry = "WITH secondary_betreuer AS ( + SELECT DISTINCT ON (pb.projektarbeit_id) + pb.projektarbeit_id, + pb.person_id AS zweitbetreuer_person_id, + pb.betreuerart_kurzbz AS zweitbetreuer_betreuerart_kurzbz, + ba.beschreibung AS zweitbetreuer_betreuerart_beschreibung, + p.titelpre AS zweitbetreuer_titelpre, + p.vorname AS zweitbetreuer_vorname, + p.nachname AS zweitbetreuer_nachname, + p.titelpost AS zweitbetreuer_titelpost, + trim( + COALESCE(p.titelpre, '') || ' ' || + COALESCE(p.vorname, '') || ' ' || + COALESCE(p.nachname, '') || ' ' || + COALESCE(p.titelpost, '') + ) AS zweitbetreuer_full_name + FROM lehre.tbl_projektbetreuer pb + JOIN public.tbl_person p ON p.person_id = pb.person_id + LEFT JOIN public.tbl_benutzer b ON b.person_id = p.person_id + LEFT JOIN lehre.tbl_betreuerart ba ON ba.betreuerart_kurzbz = pb.betreuerart_kurzbz + WHERE pb.betreuerart_kurzbz = ANY('{Zweitbetreuer,Zweitbegutachter,Senatsmitglied}') + ORDER BY pb.projektarbeit_id + ), + primary_betreuer AS ( + SELECT DISTINCT ON (pb.projektarbeit_id) + pb.projektarbeit_id, + pb.person_id AS betreuer_person_id, + pb.betreuerart_kurzbz AS betreuer_betreuerart_kurzbz, + ba.beschreibung AS betreuer_betreuerart_beschreibung, + p.titelpre AS betreuer_titelpre, + p.vorname AS betreuer_vorname, + p.nachname AS betreuer_nachname, + p.titelpost AS betreuer_titelpost, + trim( + COALESCE(p.titelpre, '') || ' ' || + COALESCE(p.vorname, '') || ' ' || + COALESCE(p.nachname, '') || ' ' || + COALESCE(p.titelpost, '') + ) AS betreuer_full_name + FROM lehre.tbl_projektbetreuer pb + JOIN public.tbl_person p ON p.person_id = pb.person_id + LEFT JOIN public.tbl_benutzer b ON b.person_id = p.person_id + LEFT JOIN lehre.tbl_betreuerart ba ON ba.betreuerart_kurzbz = pb.betreuerart_kurzbz + WHERE pb.betreuerart_kurzbz = ANY('{Erstbetreuer,Erstbegutachter,Senatsvorsitz}') + ORDER BY pb.projektarbeit_id + ) + + SELECT + * + FROM + (SELECT DISTINCT ON (tbl_projektarbeit.projektarbeit_id) + student_person.vorname, student_person.nachname, + tbl_studiengang.typ, tbl_studiengang.kurzbz, + tbl_projektarbeit.projekttyp_kurzbz, tbl_projekttyp.bezeichnung, + tbl_projektarbeit.titel, tbl_projektarbeit.projektarbeit_id, tbl_projektarbeit.note, + student_benutzer.uid, tbl_student.matrikelnr, tbl_lehreinheit.studiensemester_kurzbz, public.tbl_student.student_uid, + + + ( + SELECT orgform_kurzbz + FROM public.tbl_prestudentstatus + WHERE prestudent_id = ( + SELECT prestudent_id FROM public.tbl_student + WHERE student_uid = student_benutzer.uid LIMIT 1 + ) + ORDER BY datum DESC, insertamum DESC, ext_id DESC + LIMIT 1 + ) AS organisationsform, + ( + SELECT COALESCE(tbl_studienplan.orgform_kurzbz, + tbl_prestudentstatus.orgform_kurzbz, + tbl_studiengang.orgform_kurzbz) + FROM public.tbl_prestudent + JOIN public.tbl_prestudentstatus USING (prestudent_id) + JOIN public.tbl_studiensemester USING (studiensemester_kurzbz) + JOIN public.tbl_studiengang USING (studiengang_kz) + LEFT JOIN lehre.tbl_studienplan USING (studienplan_id) + WHERE prestudent_id = public.tbl_student.prestudent_id + ORDER BY tbl_prestudentstatus.datum DESC + LIMIT 1 + ) AS orgform, + ( + SELECT status_kurzbz + FROM public.tbl_prestudentstatus + WHERE prestudent_id = public.tbl_student.prestudent_id + ORDER BY datum DESC, insertamum DESC, ext_id DESC + LIMIT 1 + ) AS studienstatus, + + pb.betreuer_person_id, + pb.betreuer_betreuerart_kurzbz, + pb.betreuer_betreuerart_beschreibung, + pb.betreuer_titelpre, + pb.betreuer_vorname, + pb.betreuer_nachname, + pb.betreuer_titelpost, + pb.betreuer_full_name, + + sb.zweitbetreuer_person_id, + sb.zweitbetreuer_betreuerart_kurzbz, + sb.zweitbetreuer_betreuerart_beschreibung, + sb.zweitbetreuer_titelpre, + sb.zweitbetreuer_vorname, + sb.zweitbetreuer_nachname, + sb.zweitbetreuer_titelpost, + sb.zweitbetreuer_full_name + FROM lehre.tbl_projektarbeit + LEFT JOIN public.tbl_benutzer student_benutzer ON student_benutzer.uid = lehre.tbl_projektarbeit.student_uid + LEFT JOIN public.tbl_person student_person ON student_benutzer.person_id = student_person.person_id + LEFT JOIN public.tbl_student ON student_benutzer.uid = public.tbl_student.student_uid + LEFT JOIN lehre.tbl_lehreinheit USING (lehreinheit_id) + LEFT JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id) + LEFT JOIN public.tbl_studiengang ON public.tbl_student.studiengang_kz = public.tbl_studiengang.studiengang_kz + LEFT JOIN lehre.tbl_projekttyp USING (projekttyp_kurzbz) + LEFT JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id) + LEFT JOIN primary_betreuer pb ON pb.projektarbeit_id = tbl_projektarbeit.projektarbeit_id + LEFT JOIN secondary_betreuer sb ON sb.projektarbeit_id = tbl_projektarbeit.projektarbeit_id + WHERE (projekttyp_kurzbz='Bachelor' OR projekttyp_kurzbz='Diplom') + AND (pb.betreuer_person_id = ? OR sb.zweitbetreuer_person_id = ?) + ".($showAll?'':' AND student_benutzer.aktiv AND lehre.tbl_projektarbeit.note IS NULL ')." AND betreuerart_kurzbz IN ('Betreuer', 'Begutachter', 'Erstbegutachter', 'Zweitbegutachter', 'Erstbetreuer', 'Senatsvorsitz', 'Senatsmitglied') - ORDER BY tbl_projektarbeit.projektarbeit_id, betreuerart_kurzbz desc) as xy + ORDER BY tbl_projektarbeit.projektarbeit_id, betreuerart_kurzbz desc) as xy ORDER BY nachname;"; - return $this->execReadOnlyQuery($qry, array($uid)); + return $this->execReadOnlyQuery($qry, array($person_id, $person_id)); } /** @@ -460,7 +559,7 @@ class Projektarbeit_model extends DB_Model LEFT JOIN lehre.tbl_projektbetreuer USING (projektarbeit_id) LEFT JOIN public.tbl_person betreuer_person ON betreuer_person.person_id = lehre.tbl_projektbetreuer.person_id LEFT JOIN public.tbl_benutzer betreuer_benutzer ON betreuer_person.person_id = betreuer_benutzer.person_id - LEFT JOIN secondary_betreuer sb ON sb.projektarbeit_id = tbl_projektarbeit.projektarbeit_id -- ← THE NEW LINE + LEFT JOIN secondary_betreuer sb ON sb.projektarbeit_id = tbl_projektarbeit.projektarbeit_id WHERE (projekttyp_kurzbz = 'Bachelor' OR projekttyp_kurzbz = 'Diplom') AND student_benutzer.aktiv diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index a88f0f10e..6a2d73466 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -611,7 +611,7 @@ export const AbgabetoolAssistenz = { return filterVal.some(val => val == noteId); // loose equality: filter vals are numbers, noteId might be string }, handleFilterActiveChanged(active) { - if(!active && this.allSemOption) { + if(!active && this.allSemOption && this.stateRestored) { this.curSem = this.allSemOption } }, @@ -1361,6 +1361,11 @@ export const AbgabetoolAssistenz = { // calculate Abgabetermin time diff to now and assign last and next to projekt projekt.abgabetermine.forEach(termin => { + // only set this if it has not been set yet and abgabetermin has a note (qgate) + if(!termin.noteBackend && termin.note) { + termin.noteBackend = termin.note + } + termin.bezeichnung = this.abgabeTypeOptions.find(opt => opt.paabgabetyp_kurzbz === termin.paabgabetyp_kurzbz) // while already looping through each termin, calculate datestyle beforehand @@ -1851,20 +1856,17 @@ export const AbgabetoolAssistenz = { if(typeof termin.note !== 'object') { termin.note = this.allowedNotenOptions.find(opt => opt.note == termin.note) } - - // only set this if it has not been set yet and abgabetermin has a note (qgate) - if(!termin.noteBackend && termin.note) { - termin.noteBackend = termin.note - } termin.file = [] // assistenz should be able to edit every abgabe // update 21-01-2026: actually blocking operations on finished projektarbeiten seems like a decent idea + const terminHasAbgabe = termin.abgabedatum != null + const terminHasNote = termin.noteBackend termin.allowedToSave = paIsBenotet ? false : true // assistenz are not allowed to delete deadlines with existing submissions - termin.allowedToDelete = paIsBenotet ? false : !termin.abgabedatum && !termin.note + termin.allowedToDelete = paIsBenotet || terminHasNote || terminHasAbgabe ? false : true }) @@ -2100,8 +2102,14 @@ export const AbgabetoolAssistenz = { if (!this.projektarbeiten) return []; return this.projektarbeiten.flatMap(pa => pa.abgabetermine.map(termin => { - const allowedToSave = pa.note !== null ? false : true - const allowedToDelete = pa.note !== null ? false : !termin.abgabedatum && !termin.note + const terminHasAbgabe = termin.abgabedatum != null + const terminHasNote = termin.noteBackend + + // IN multiedit changing anything for a termin with an existing note is not allowed anymore, to avoid + // confusing UX behaviour why some fields could be edited and others not -> just edit in detail view + const allowedToSave = pa.note !== null || terminHasNote || terminHasAbgabe ? false : true + const allowedToDelete = pa.note !== null || terminHasNote || terminHasAbgabe ? false : true + return { allowedToSave, allowedToDelete, @@ -2713,6 +2721,7 @@ export const AbgabetoolAssistenz = { >