From f85f9a53c46e47607344cdd3c59b7953c4edebab Mon Sep 17 00:00:00 2001 From: Alexei Karpenko Date: Wed, 17 Jun 2026 19:13:41 +0200 Subject: [PATCH] prestudent status checks: added student history and student orgform checks, changed date input so text input is correctly sent --- .../api/frontend/v1/stv/Status.php | 129 ++++++++++++++++++ .../libraries/PrestudentstatusCheckLib.php | 109 ++++++++++++--- .../models/crm/Prestudentstatus_model.php | 26 ++-- .../Details/Status/Modal.js | 6 + 4 files changed, 246 insertions(+), 24 deletions(-) diff --git a/application/controllers/api/frontend/v1/stv/Status.php b/application/controllers/api/frontend/v1/stv/Status.php index 4006dc47a..5c5251429 100644 --- a/application/controllers/api/frontend/v1/stv/Status.php +++ b/application/controllers/api/frontend/v1/stv/Status.php @@ -377,6 +377,29 @@ class Status extends FHCAPI_Controller return $this->getDataOrTerminateWithError($result); }], + ['history_student', function () use ( + $isBerechtigtNoStudstatusCheck, + $prestudent_id, + $status_kurzbz, + $datum, + $studiensemester_kurzbz, + $ausbildungssemester + ) { + if ($isBerechtigtNoStudstatusCheck) + return true; // Skip if access right says so + + $result = $this->prestudentstatuschecklib->checkStatusHistoryStudent( + $prestudent_id, + $status_kurzbz, + $datum, + $studiensemester_kurzbz, + $ausbildungssemester, + '', + '' + ); + + return $this->getDataOrTerminateWithError($result); + }], ['history_abbrecher', function () use ( $isBerechtigtNoStudstatusCheck, $prestudent_id, @@ -456,6 +479,7 @@ class Status extends FHCAPI_Controller 'history_timesequence' => $this->p->t('lehre', 'error_statuseintrag_zeitabfolge'), 'history_laststatus' => $this->p->t('lehre', 'error_endstatus'), 'history_unterbrecher' => $this->p->t('lehre', 'error_consecutiveUnterbrecher'), + 'history_student' => $this->p->t('lehre', 'error_wrongStatusOrderBeforeStudent', [implode(', ', $this->prestudentstatuschecklib->getStatusAbfolgeVorStudent())]), 'history_abbrecher' => $this->p->t('lehre', 'error_consecutiveUnterbrecherAbbrecher'), 'history_diplomant' => $this->p->t('lehre', 'error_consecutiveDiplomandStudent'), 'wrong_personenkennzeichen' => $this->p->t('lehre', 'error_personenkennzeichenPasstNichtZuStudiensemester') @@ -1007,6 +1031,29 @@ class Status extends FHCAPI_Controller return $this->getDataOrTerminateWithError($result); }], + ['history_student', function () use ( + $isBerechtigtNoStudstatusCheck, + $prestudent_id, + $status_kurzbz, + $datum, + $studiensemester_kurzbz, + $ausbildungssemester + ) { + if ($isBerechtigtNoStudstatusCheck) + return true; // Skip if access right says so + + $result = $this->prestudentstatuschecklib->checkStatusHistoryStudent( + $prestudent_id, + $status_kurzbz, + new DateTime($datum), + $studiensemester_kurzbz, + $ausbildungssemester, + '', + '' + ); + + return $this->getDataOrTerminateWithError($result); + }], ['history_abbrecher', function () use ( $isBerechtigtNoStudstatusCheck, $prestudent_id, @@ -1057,6 +1104,31 @@ class Status extends FHCAPI_Controller return $this->getDataOrTerminateWithError($result); }], + ['history_student_orgform', function () use ( + $isBerechtigtNoStudstatusCheck, + $prestudent_id, + $status_kurzbz, + $datum, + $studiensemester_kurzbz, + $ausbildungssemester, + $studienplan_id + ) { + if ($isBerechtigtNoStudstatusCheck) + return true; // Skip if access right says so + + $result = $this->prestudentstatuschecklib->checkFirstStudentOrgform( + $prestudent_id, + $status_kurzbz, + new DateTime($datum), + $studiensemester_kurzbz, + $ausbildungssemester, + '', + '', + $studienplan_id + ); + + return $this->getDataOrTerminateWithError($result); + }], ['wrong_personenkennzeichen', function () use ( $isBerechtigtNoStudstatusCheck, $prestudent_id, @@ -1086,8 +1158,10 @@ class Status extends FHCAPI_Controller 'history_timesequence' => $this->p->t('lehre', 'error_statuseintrag_zeitabfolge'), 'history_laststatus' => $this->p->t('lehre', 'error_endstatus'), 'history_unterbrecher' => $this->p->t('lehre', 'error_consecutiveUnterbrecher'), + 'history_student' => $this->p->t('lehre', 'error_wrongStatusOrderBeforeStudent', [implode(', ', $this->prestudentstatuschecklib->getStatusAbfolgeVorStudent())]), 'history_abbrecher' => $this->p->t('lehre', 'error_consecutiveUnterbrecherAbbrecher'), 'history_diplomant' => $this->p->t('lehre', 'error_consecutiveDiplomandStudent'), + 'history_student_orgform' => $this->p->t('lehre', 'error_bewerberOrgformUngleichStudentOrgform'), 'wrong_personenkennzeichen' => $this->p->t('lehre', 'error_personenkennzeichenPasstNichtZuStudiensemester') ]); @@ -1180,6 +1254,7 @@ class Status extends FHCAPI_Controller $authUID = getAuthUID(); $studiensemester_kurzbz = $this->input->post('studiensemester_kurzbz') ?: $oldstatus->studiensemester_kurzbz; $ausbildungssemester = $this->input->post('ausbildungssemester') ?: $oldstatus->ausbildungssemester; + $studienplan_id = $this->input->post('studienplan_id'); $datum = $this->input->post('datum') ?: $oldstatus->datum; //Form Validation @@ -1336,6 +1411,31 @@ class Status extends FHCAPI_Controller return $this->getDataOrTerminateWithError($result); }], + ['history_student', function () use ( + $isBerechtigtNoStudstatusCheck, + $prestudent_id, + $status_kurzbz, + $datum, + $studiensemester_kurzbz, + $ausbildungssemester, + $key_studiensemester_kurzbz, + $key_ausbildungssemester + ) { + if ($isBerechtigtNoStudstatusCheck) + return true; // Skip if access right says so + + $result = $this->prestudentstatuschecklib->checkStatusHistoryStudent( + $prestudent_id, + $status_kurzbz, + new DateTime($datum), + $studiensemester_kurzbz, + $ausbildungssemester, + $key_studiensemester_kurzbz, + $key_ausbildungssemester + ); + + return $this->getDataOrTerminateWithError($result); + }], ['history_abbrecher', function () use ( $isBerechtigtNoStudstatusCheck, $prestudent_id, @@ -1390,6 +1490,33 @@ class Status extends FHCAPI_Controller return $this->getDataOrTerminateWithError($result); }], + ['history_student_orgform', function () use ( + $isBerechtigtNoStudstatusCheck, + $prestudent_id, + $status_kurzbz, + $datum, + $studiensemester_kurzbz, + $ausbildungssemester, + $key_studiensemester_kurzbz, + $key_ausbildungssemester, + $studienplan_id + ) { + if ($isBerechtigtNoStudstatusCheck) + return true; // Skip if access right says so + + $result = $this->prestudentstatuschecklib->checkFirstStudentOrgform( + $prestudent_id, + $status_kurzbz, + new DateTime($datum), + $studiensemester_kurzbz, + $ausbildungssemester, + $key_studiensemester_kurzbz, + $key_ausbildungssemester, + $studienplan_id + ); + + return $this->getDataOrTerminateWithError($result); + }], ['wrong_personenkennzeichen', function () use ( $isBerechtigtNoStudstatusCheck, $prestudent_id, @@ -1420,8 +1547,10 @@ class Status extends FHCAPI_Controller 'history_timesequence' => $this->p->t('lehre', 'error_statuseintrag_zeitabfolge'), 'history_laststatus' => $this->p->t('lehre', 'error_endstatus'), 'history_unterbrecher' => $this->p->t('lehre', 'error_consecutiveUnterbrecher'), + 'history_student' => $this->p->t('lehre', 'error_wrongStatusOrderBeforeStudent', [implode(', ', $this->prestudentstatuschecklib->getStatusAbfolgeVorStudent())]), 'history_abbrecher' => $this->p->t('lehre', 'error_consecutiveUnterbrecherAbbrecher'), 'history_diplomant' => $this->p->t('lehre', 'error_consecutiveDiplomandStudent'), + 'history_student_orgform' => $this->p->t('lehre', 'error_bewerberOrgformUngleichStudentOrgform'), 'wrong_personenkennzeichen' => $this->p->t('lehre', 'error_personenkennzeichenPasstNichtZuStudiensemester') ]); diff --git a/application/libraries/PrestudentstatusCheckLib.php b/application/libraries/PrestudentstatusCheckLib.php index a26601833..be8db1691 100644 --- a/application/libraries/PrestudentstatusCheckLib.php +++ b/application/libraries/PrestudentstatusCheckLib.php @@ -269,6 +269,14 @@ class PrestudentstatusCheckLib return success(getData($result) == "1"); } + /** + * Getter for status before student. + */ + public function getStatusAbfolgeVorStudent() + { + return $this->_statusAbfolgeVorStudent; + } + /** * Runs all checks on Status History and saves it in cache. * @@ -289,7 +297,8 @@ class PrestudentstatusCheckLib $new_studiensemester_kurzbz, $new_ausbildungssemester, $old_studiensemester_kurzbz, - $old_ausbildungssemester + $old_ausbildungssemester, + $new_studienplan_id = null ) { // Generate key for caching $primary = implode('|', [ @@ -299,7 +308,8 @@ class PrestudentstatusCheckLib $new_studiensemester_kurzbz, $new_ausbildungssemester, $old_studiensemester_kurzbz, - $old_ausbildungssemester + $old_ausbildungssemester, + $new_studienplan_id ]); if (isset($this->_cache_history[$primary])) @@ -315,7 +325,8 @@ class PrestudentstatusCheckLib $new_studiensemester_kurzbz, $new_ausbildungssemester, $old_studiensemester_kurzbz, - $old_ausbildungssemester + $old_ausbildungssemester, + $new_studienplan_id ); if (isError($result)) @@ -335,7 +346,8 @@ class PrestudentstatusCheckLib 'abbrechersemester' => true, 'diplomant' => true, 'student' => true, - 'studentPerskz' => true + 'studentPerskz' => true, + 'studentOrgform' => true ]; for ($n = 0, $c = 1; $c < $historyCount; $n++, $c++) { @@ -346,6 +358,7 @@ class PrestudentstatusCheckLib && !$checks['diplomant'] && !$checks['student'] && !$checks['studentPerskz'] + && !$checks['studentOrgform'] ) break; // early out @@ -382,10 +395,12 @@ class PrestudentstatusCheckLib if (($checks['diplomant'] || $checks['student'] + || $checks['studentOrgform'] || $checks['studentPerskz']) && $next->status_kurzbz == self::STUDENT_STATUS ) { - $restl_stati = array_unique(array_column(array_slice($history, $c), 'status_kurzbz')); + $restl_history = array_slice($history, $c); + $restl_stati = array_unique(array_column($restl_history, 'status_kurzbz')); // keine Studenten nach Diplomand Status if ($checks['diplomant'] @@ -393,28 +408,49 @@ class PrestudentstatusCheckLib ) $checks['diplomant'] = false; + $existingStati = array_intersect($restl_stati, $this->_statusAbfolgeVorStudent); // vor Studentenstatus müssen bestimmte Status vorhanden sein if ($checks['student'] - && array_values(array_intersect($restl_stati, $this->_statusAbfolgeVorStudent)) != array_values($this->_statusAbfolgeVorStudent) + && array_diff($existingStati, $this->_statusAbfolgeVorStudent) != array_diff($this->_statusAbfolgeVorStudent, $existingStati) ) + { $checks['student'] = false; + } // korrektes Personenkennzeichen zu erstem Student status - if ($checks['studentPerskz'] && !in_array(self::STUDENT_STATUS, $restl_stati)) + if (!in_array(self::STUDENT_STATUS, $restl_stati)) { - $this->_ci->StudentModel->addLimit('1'); - $result = $this->_ci->StudentModel->loadWhere(['prestudent_id' => $prestudent_id]); + if ($checks['studentPerskz']) + { + $this->_ci->StudentModel->addLimit('1'); + $result = $this->_ci->StudentModel->loadWhere(['prestudent_id' => $prestudent_id]); - if (isError($result)) - return $result; + if (isError($result)) + return $result; - if (!hasData($result)) - return error('Prestudent without student entry'); + if (!hasData($result)) + return error('Prestudent without student entry'); - $personenkennzeichen = getData($result)[0]->matrikelnr; + $personenkennzeichen = getData($result)[0]->matrikelnr; - $jahr = $this->_ci->StudiensemesterModel->getStudienjahrNumberFromStudiensemester($next->studiensemester_kurzbz); - $checks['studentPerskz'] = $jahr == mb_substr($personenkennzeichen, 0, 2); + $jahr = $this->_ci->StudiensemesterModel->getStudienjahrNumberFromStudiensemester($next->studiensemester_kurzbz); + $checks['studentPerskz'] = $jahr == mb_substr($personenkennzeichen, 0, 2); + } + + if ($checks['studentOrgform']) + { + $bewerber = null; + foreach ($restl_history as $h) + { + if ($h->status_kurzbz == 'Bewerber') + { + $bewerber = $h; + break; + } + } + + if (isset($bewerber)) $checks['studentOrgform'] = $bewerber->orgform_kurzbz == $next->orgform_kurzbz; + } } } } @@ -656,6 +692,47 @@ class PrestudentstatusCheckLib return success(getData($result)['student']); } + /** + * Checks if Orgform of student staus is the same as Bewerber Orgform. + * + * @param integer $prestudent_id + * @param string $status_kurzbz + * @param DateTime $new_date + * @param string $new_studiensemester_kurzbz + * @param integer $new_ausbildungssemester + * @param string $old_studiensemester_kurzbz + * @param integer $old_ausbildungssemester + * @param integer $new_studienplan_id + * + * @return stdClass + */ + public function checkFirstStudentOrgform( + $prestudent_id, + $status_kurzbz, + $new_date, + $new_studiensemester_kurzbz, + $new_ausbildungssemester, + $old_studiensemester_kurzbz, + $old_ausbildungssemester, + $new_studienplan_id + ) { + $result = $this->prepareStatusHistory( + $prestudent_id, + $status_kurzbz, + $new_date, + $new_studiensemester_kurzbz, + $new_ausbildungssemester, + $old_studiensemester_kurzbz, + $old_ausbildungssemester, + $new_studienplan_id + ); + + if (isError($result)) + return $result; + + return success(getData($result)['studentOrgform']); + } + /** * Checks if Personenkennzeichen is set correctly. * diff --git a/application/models/crm/Prestudentstatus_model.php b/application/models/crm/Prestudentstatus_model.php index c0ed8595e..7cdeb2a31 100644 --- a/application/models/crm/Prestudentstatus_model.php +++ b/application/models/crm/Prestudentstatus_model.php @@ -579,16 +579,20 @@ class Prestudentstatus_model extends DB_Model $new_studiensemester_kurzbz, $new_ausbildungssemester, $old_studiensemester_kurzbz, - $old_ausbildungssemester + $old_ausbildungssemester, + $new_studienplan_id ) { $new_date = $new_date->format('Y-m-d'); - $this->addSelect('status_kurzbz'); - $this->addSelect('studiensemester_kurzbz'); - $this->addSelect('ausbildungssemester'); - $this->addSelect('datum'); - $this->addSelect('insertamum'); - $this->addSelect('ext_id'); + $this->addSelect('tbl_prestudentstatus.status_kurzbz'); + $this->addSelect('tbl_prestudentstatus.studiensemester_kurzbz'); + $this->addSelect('tbl_prestudentstatus.ausbildungssemester'); + $this->addSelect('tbl_prestudentstatus.datum'); + $this->addSelect('tbl_prestudentstatus.insertamum'); + $this->addSelect('tbl_studienplan.orgform_kurzbz'); + $this->addSelect('tbl_prestudentstatus.ext_id'); + + $this->addJoin('lehre.tbl_studienplan', 'studienplan_id', 'LEFT'); if ($old_studiensemester_kurzbz || $old_ausbildungssemester) { $this->db->not_group_start(); @@ -607,13 +611,19 @@ class Prestudentstatus_model extends DB_Model $this->escape($status_kurzbz) . " AS status_kurzbz, " . $this->escape($new_studiensemester_kurzbz) . " AS studiensemester_kurzbz, " . $this->escape($new_ausbildungssemester) . " AS ausbildungssemester, " . - $this->escape($new_date) . "::date AS datum," . + $this->escape($new_date) . "::date AS datum, " . $this->escape(date('c')) . "::date AS insertamum," . + ( + is_numeric($new_studienplan_id) + ? "(SELECT orgform_kurzbz FROM lehre.tbl_studienplan WHERE studienplan_id = ".$this->escape($new_studienplan_id).")" + : "''" + ) . " AS orgform_kurzbz, ". "NULL AS ext_id"; $this->addJoin('public.tbl_studiensemester sem', 'studiensemester_kurzbz'); $this->addOrder('s.datum', 'DESC'); + $this->addOrder('sem.start', 'DESC'); $this->addOrder('s.insertamum', 'DESC'); $this->addOrder('s.ext_id', 'DESC'); diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Status/Modal.js b/public/js/components/Stv/Studentenverwaltung/Details/Status/Modal.js index 7507b0ebd..d217130bf 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Status/Modal.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Status/Modal.js @@ -281,7 +281,9 @@ export default{ auto-apply :enable-time-picker="false" text-input + locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" preview-format="dd.MM.yyyy" :teleport="true" :disabled="bisLocked" @@ -296,7 +298,9 @@ export default{ auto-apply :enable-time-picker="false" text-input + locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" preview-format="dd.MM.yyyy" :teleport="true" :disabled="bisLocked" @@ -310,7 +314,9 @@ export default{ :label="$p.t('lehre/bewerbung_abgeschickt_am')" auto-apply :enable-time-picker="false" + locale="de" format="dd.MM.yyyy" + model-type="yyyy-MM-dd" text-input preview-format="dd.MM.yyyy" :teleport="true"