diff --git a/application/controllers/api/frontend/v1/Abgabe.php b/application/controllers/api/frontend/v1/Abgabe.php index af598a345..43dc18d1c 100644 --- a/application/controllers/api/frontend/v1/Abgabe.php +++ b/application/controllers/api/frontend/v1/Abgabe.php @@ -511,10 +511,11 @@ class Abgabe extends FHCAPI_Controller return $projektarbeit->projektarbeit_id; }; $projektarbeiten_ids = array_map($mapFunc, $projektarbeiten->retval); - - $ret = $this->ProjektarbeitModel->getProjektarbeitenAbgabetermine($projektarbeiten_ids); - $projektabgaben = $this->getDataOrTerminateWithError($ret, 'general'); + if(count($projektarbeiten_ids) > 0) { + $ret = $this->ProjektarbeitModel->getProjektarbeitenAbgabetermine($projektarbeiten_ids); + $projektabgaben = $this->getDataOrTerminateWithError($ret, 'general'); + } forEach($projektarbeiten->retval as $pa) { @@ -846,9 +847,10 @@ class Abgabe extends FHCAPI_Controller private function getProjektbetreuerEmailByProjektarbeitID($projektarbeit_id) { $this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel'); $result = $this->ProjektarbeitModel->getProjektbetreuerEmail($projektarbeit_id); - $email = $this->getDataOrTerminateWithError($result, 'general'); - - return $email[0]->uid ? $email[0]->uid.'@'.DOMAIN : $email[0]->private_email; + if(count($result->retval) > 0) { + $email = getData($result); + return $email[0]->uid ? $email[0]->uid.'@'.DOMAIN : $email[0]->private_email; + } else return ''; } diff --git a/application/controllers/api/frontend/v1/Documents.php b/application/controllers/api/frontend/v1/Documents.php index 7b2fc4a15..13c0a2eba 100644 --- a/application/controllers/api/frontend/v1/Documents.php +++ b/application/controllers/api/frontend/v1/Documents.php @@ -208,7 +208,6 @@ class Documents extends FHCAPI_Controller $this->load->model('system/Vorlage_model', 'VorlageModel'); $result = $this->VorlageModel->load($xsl); - $this->addMeta("ress", $result); $vorlage = current($this->getDataOrTerminateWithError($result)); if (!$vorlage) show_404(); @@ -221,7 +220,7 @@ class Documents extends FHCAPI_Controller 'gedruckt' => true, 'insertamum' => date('c'), 'insertvon' => getAuthUID(), - 'uid' => $this->input->post_get('uid') ?: '', + 'uid' => $this->input->post_get('uid') ?: null, 'archiv' => true, 'signiert' => !!$sign_user, 'stud_selfservice' => $vorlage->stud_selfservice @@ -251,6 +250,9 @@ class Documents extends FHCAPI_Controller 'studiensemester_kurzbz' => $ss, 'student_uid' => $akteData['uid'] ]); + + if (!hasData($result)) $this->terminateWithError($this->p->t("stv", "error_noLehrverbandAssigned")); + $res = current($this->getDataOrTerminateWithError($result)); $studiengang_kz = $res->studiengang_kz; @@ -332,6 +334,7 @@ class Documents extends FHCAPI_Controller if ($prestudent_id) { $this->load->model('crm/prestudent_model', 'PrestudentModel'); $this->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT'); + $this->PrestudentModel->addSelect('tbl_prestudent.*, UPPER(typ || kurzbz) AS kuerzel'); $result = $this->PrestudentModel->load($prestudent_id); $prestudent = current($this->getDataOrTerminateWithError($result)); diff --git a/application/controllers/jobs/AbgabetoolJob.php b/application/controllers/jobs/AbgabetoolJob.php index 9b59a72e7..b81053032 100644 --- a/application/controllers/jobs/AbgabetoolJob.php +++ b/application/controllers/jobs/AbgabetoolJob.php @@ -495,6 +495,10 @@ class AbgabetoolJob extends JOB_Controller // get all new or changed termine in interval $result = $this->_ci->PaabgabeModel->findAbgabenNewOrUpdatedSince($interval, $relevantTypes); $retval = getData($result); + if(!$retval) { + $this->_ci->logInfo("Keine Emails an Betreuer über neue oder veränderte Termine versandt"); + return; + } // group changed/new abgaben for projektarbeiten $projektarbeiten = []; @@ -557,6 +561,8 @@ class AbgabetoolJob extends JOB_Controller $anredeFillString = $data->anrede == "Herr" ? "r" : ""; $fullFormattedNameString = $data->first; + $relevantCounter = 0; // workaround to check if a betreuer needs to have any notification about relevant + // abgaben at all to avoid sending empty emails since we filter on certain conditions forEach($tupelArr as $tupel) { $projektarbeit_id = $tupel[0]; $betreuerRow = $tupel[1]; @@ -575,6 +581,8 @@ class AbgabetoolJob extends JOB_Controller continue; } + $relevantCounter++; + // format the Student Name $s = $relevantAbgaben[0]; $nameParts = []; @@ -633,6 +641,11 @@ class AbgabetoolJob extends JOB_Controller // done with building the change list, now send it $betreuerRow = $tupelArr[0][1]; + if($relevantCounter == 0) { + $this->_ci->logInfo('No Relevant Abgaben to notify Betreuer PersonID: "'.$betreuerRow->person_id.'".'); + continue; + } + $path = $this->_ci->config->item('URL_MITARBEITER'); $url = CIS_ROOT.$path; diff --git a/include/lehrelisthelper.class.php b/include/lehrelisthelper.class.php index b1821391f..f511b27cc 100644 --- a/include/lehrelisthelper.class.php +++ b/include/lehrelisthelper.class.php @@ -270,6 +270,8 @@ class LehreListHelper } else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von > $stsemdatumvon || $row->von == '')) { // if bis datum is not yet known but von is available already $zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')'; + } else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von <= $stsemdatumvon || $row->von == '') && ($row->bis == '' || $row->bis > date('Y-m-d'))){ + $zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')'; } diff --git a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js index d5caf97a6..bb5c6a710 100644 --- a/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js +++ b/public/js/components/Cis/Abgabetool/AbgabetoolAssistenz.js @@ -180,7 +180,7 @@ export const AbgabetoolAssistenz = { // frozen: true, // width: 40 // }, - {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', headerFilter: false, headerSort: false, formatter: this.formAction, tooltip:false, minWidth: 150, cssClass: 'sticky-col'}, + {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', headerFilter: false, headerSort: false, formatter: this.formAction, tooltip:false, minWidth: 100, cssClass: 'sticky-col'}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'student_vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1}, {title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'student_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1}, @@ -226,7 +226,7 @@ export const AbgabetoolAssistenz = { field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false}, ], persistence: false, - persistenceID: "abgabetool_2026_02_26" + persistenceID: "abgabetool_2026_03_16" }, abgabeTableEventHandlers: [ { @@ -645,7 +645,7 @@ export const AbgabetoolAssistenz = { actionButtons.className = "d-flex gap-3"; // you can keep Bootstrap gap if loaded actionButtons.style.display = "flex"; actionButtons.style.alignItems = "stretch"; // buttons stretch to full height - actionButtons.style.justifyContent = "center"; + actionButtons.style.justifyContent = "start"; actionButtons.style.height = "100%"; // full grid cell height const val = cell.getValue(); @@ -675,8 +675,20 @@ export const AbgabetoolAssistenz = { createButton('fa fa-timeline', 'abgabetool/c4termineTimeLine', () => this.openTimeline(val)) ); + if(val.latestTerminWithUpload) { + actionButtons.append( + createButton('fa fa-download', 'abgabetool/c4downloadLatestAbgabe', () => this.downloadAbgabe(val.latestTerminWithUpload.paabgabe_id, val.student_uid, val.projektarbeit_id)) + ) + } + return actionButtons; }, + downloadAbgabe(paabgabe_id, student_uid, projektarbeit_id) { + const url = `/api/frontend/v1/Abgabe/getStudentProjektarbeitAbgabeFile?paabgabe_id=${paabgabe_id}&student_uid=${student_uid}&projektarbeit_id=${projektarbeit_id}`; + + window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url) + // this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid)) + }, undoSelection(cell) { // checks if cells row is selected and unselects -> imitates columns which dont trigger row selection @@ -780,6 +792,8 @@ export const AbgabetoolAssistenz = { // TODO: mehrsprachig englisch projekt.note_bez = opt.bezeichnung } + + const latestTerminWithUpload = this.findLatestTerminWithUpload(projekt) return { ...projekt, @@ -787,6 +801,7 @@ export const AbgabetoolAssistenz = { details: { student_uid: projekt.student_uid, projektarbeit_id: projekt.projektarbeit_id, + latestTerminWithUpload: latestTerminWithUpload ?? null }, pkz: this.buildPKZ(projekt), beurteilung: projekt.beurteilungLink ?? null, @@ -800,6 +815,15 @@ export const AbgabetoolAssistenz = { } }) }, + findLatestTerminWithUpload(projekt) { + const withAbgabedatumSorted = projekt?.abgabetermine?.filter(t => t.abgabedatum != null)?.sort((a,b) => a < b) + + if(withAbgabedatumSorted.length) { + return withAbgabedatumSorted[0] + } + + return null + }, createInfoString(data) { let str = ''; @@ -1413,9 +1437,12 @@ export const AbgabetoolAssistenz = {
-
+

{{$p.t('abgabetool/abgabetoolTitle')}}

+
+ +
+
+ +
projekarbeit.projektarbeit_id == details.projektarbeit_id) + const projektarbeiten = this.projektarbeiten?.retval ?? this.projektarbeiten + const pa = projektarbeiten.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id) + let paIsBenotet = false if(pa.note !== undefined && pa.note !== null) { // check if the note is not defined as a non final projektarbeit note diff --git a/public/js/components/Form/Input.js b/public/js/components/Form/Input.js index 3c3fa45d5..4ad555ab7 100644 --- a/public/js/components/Form/Input.js +++ b/public/js/components/Form/Input.js @@ -170,6 +170,7 @@ export default { return this.$attrs.modelValue; }, set(v) { + this.clearValidationForThisName() if (!this.$attrs.hasOwnProperty('modelValue')) this.modelValueDummy = v; this.$emit('update:modelValue', v); @@ -242,9 +243,9 @@ export default { template: ` - - - + + 'pep', + 'category' => 'ui', + 'phrase' => 'geplZeitraum', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'geplanter Zeitraum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'planned Period', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'bitteAuswaehlen', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Bitte auswählen...', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please select...', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'hinweisLehrende', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Hinweis für Lehrende', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Note for Lecturers', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'lehreinheiten', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lehreinheiten', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Teaching Units', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'lead', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Leitung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Lead', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'teamlead', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Team / Leitung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Team / Lead', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'ausblick_lvplanung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ausblick auf Ihre mögliche LV-Planung', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Preview of Your Potential Course Planning', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + + array( + 'app' => 'pep', + 'category' => 'ui', + 'phrase' => 'detailselfoverview', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Achtung: die vorliegenden Informationen stellen eine Vorabplanung dar und sind als Anfrage an Sie gedacht.

+ Die Beauftragung der tatsächlichen Lehrveranstaltungen erfolgt durch Ihre Kompetenzfeldleitung.

+ Ihre aktuell gültigen Lehraufträge und den LV Plan des aktuellen Semesters (Termine) finden Sie wie gewohnt unter „mein CIS“ -> „LV-Plan Hauptmenü“ bzw. „Lehrauftragsverwaltung“.', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Please note: The information provided represents a preliminary planning and is intended as an inquiry to you.

+ The official assignment of the actual courses will be carried out by your Competence Field Manager.

+ Your currently valid teaching assignments and the course schedule for the current semester (dates) can be found as usual under “My CIS” → “Schedule Main Menu” or “Teaching Assignment Administration”', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( 'app' => 'pep', 'category' => 'ui', @@ -44726,9 +44892,9 @@ array( 'phrases' => array( array( 'sprache' => 'German', - 'text' => "Für den gesamten Studiengang verbindlicher Termin. - - Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich, + 'text' => "Für den gesamten Studiengang verbindlicher Termin. + + Liegt ein Termin in der Vergangenheit, kann nichts mehr hochgeladen werden. Ist es dennoch erforderlich, haben Studierende bei der Studiengangsassistenz um eine Korrektur dieses Termins anzusuchen.", 'description' => '', 'insertvon' => 'system' @@ -44919,7 +45085,7 @@ array( array( 'sprache' => 'German', 'text' => "Verspätete Projektabgabe ist bei Terminen, welche von der Studiengangsassistenz für den gesamten Studiengang fixiert wurden nicht erlaubt! - + Um einen Endupload durchführen zu können, müssen Sie ein positiv benotetes Quality Gate 1 & Quality Gate 2 in der relevanten Projektarbeit absolviert haben.", 'description' => '', 'insertvon' => 'system' @@ -45234,6 +45400,46 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4downloadLatestAbgabe', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => "Zuletzt getätigte Abgabe herunterladen", + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Download latest uploaded File', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4termineTimeLine', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zeitstrahl Termine', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Timeline Deadlines', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'abgabetool', @@ -46695,6 +46901,26 @@ array( ) ) ), + array( + 'app' => 'core', + 'category' => 'abgabetool', + 'phrase' => 'c4noZuordnungBetreuerStudent', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Keine Zuordnung oder Berechtigung für die Projektarbeit gefunden!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'No assignment or authorization found for the project!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), // ABGABETOOL PHRASEN END array( 'app' => 'core', @@ -56948,6 +57174,27 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), // ### Refactor Messages END + // + array( + 'app' => 'core', + 'category' => 'stv', + 'phrase' => 'error_noLehrverbandAssigned', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'StudentIn ist in diesem Semester keinem Lehrverband zugeteilt', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Student has no assignment to any teaching association', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), array( 'app' => 'core', 'category' => 'lehre', @@ -56968,6 +57215,7 @@ I have been informed that I am under no obligation to consent to the transmissio ) ) ), + );