diff --git a/application/config/stv.php b/application/config/stv.php
index 31ce3f521..1fdd5b28a 100644
--- a/application/config/stv.php
+++ b/application/config/stv.php
@@ -52,7 +52,11 @@ $config['tabs'] =
],
],
],
- ]
+ ],
+ 'exemptions' => [
+ //if true, Anrechnungen can be added and edited in tab Anrechnungen
+ 'editableAnrechnungen' => false,
+ ],
];
// List of fields to show when ZGV_DOKTOR_ANZEIGEN is defined
diff --git a/application/controllers/api/frontend/v1/stv/Anrechnungen.php b/application/controllers/api/frontend/v1/stv/Anrechnungen.php
new file mode 100644
index 000000000..85019334f
--- /dev/null
+++ b/application/controllers/api/frontend/v1/stv/Anrechnungen.php
@@ -0,0 +1,241 @@
+ ['admin:r', 'assistenz:r'],
+ 'deleteAnrechnung' => ['admin:rw', 'assistenz:rw'],
+ 'getLehrveranstaltungen' => ['admin:r', 'assistenz:r'],
+ 'getBegruendungen' => ['admin:r', 'assistenz:r'],
+ 'getLektoren' => ['admin:r', 'assistenz:r'],
+ 'getLvsKompatibel' => ['admin:r', 'assistenz:r'],
+ 'insertAnrechnung' => ['admin:rw', 'assistenz:rw'],
+ 'loadAnrechnung' => ['admin:rw', 'assistenz:rw'],
+ 'updateAnrechnung' => ['admin:rw', 'assistenz:rw'],
+ ]);
+
+ // Load Libraries
+ $this->load->library('VariableLib', ['uid' => getAuthUID()]);
+
+ // Load language phrases
+ $this->loadPhrases([
+ 'ui', 'lehre'
+ ]);
+
+ // Load models
+ $this->load->model('education/Anrechnung_model', 'AnrechnungsModel');
+ }
+
+ public function getAnrechnungen($prestudent_id)
+ {
+ $result = $this->AnrechnungsModel->getAnrechnungsData($prestudent_id);
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getBegruendungen()
+ {
+ $this->load->model('education/Anrechnungbegruendung_model', 'AnrechnungbegrueundungsModel');
+
+ $result = $this->AnrechnungbegrueundungsModel->load();
+ if (isError($result)) {
+ $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL);
+ }
+ $this->terminateWithSuccess(getData($result) ?: []);
+ }
+
+ public function getLehrveranstaltungen($prestudent_id)
+ {
+ $this->load->model('crm/Prestudentstatus_model', 'PrestudentstatusModel');
+ $result = $this->PrestudentstatusModel->getLastStatus($prestudent_id);
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $studienplan_id = current($data)->studienplan_id;
+
+ $this->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel');
+ $result = $this->LehrveranstaltungModel->getLvsByStudienplanId($studienplan_id);
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getLvsKompatibel($lehrveranstaltung_id)
+ {
+ $this->AnrechnungsModel->addJoin('lehre.tbl_lehrveranstaltung lv', 'ON (lv.lehrveranstaltung_id = lehre.tbl_anrechnung.lehrveranstaltung_id)');
+ $result = $this->AnrechnungsModel->loadWhere(
+ ['lehrveranstaltung_id_kompatibel' => $lehrveranstaltung_id]
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getLektoren($studiengang_kz)
+ {
+ $this->load->model('ressource/Mitarbeiter_model', 'MitarbeiterModel');
+
+ $result = $this->MitarbeiterModel->getLektoren($studiengang_kz);
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function insertAnrechnung()
+ {
+ $this->load->library('form_validation');
+
+ $prestudent_id = $this->input->post('prestudent_id');
+
+ if(!$prestudent_id)
+ {
+ return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Student UID']), self::ERROR_TYPE_GENERAL);
+ }
+
+ $formData = $this->input->post('formData');
+ $_POST['lehrveranstaltung_id'] =
+ (isset($formData['lehrveranstaltung_id']) && !empty($formData['lehrveranstaltung_id']))
+ ? $formData['lehrveranstaltung_id']
+ : null;
+ $_POST['lehrveranstaltung_id_kompatibel'] =
+ (isset($formData['lehrveranstaltung_id_kompatibel']) && !empty($formData['lehrveranstaltung_id_kompatibel']))
+ ? $formData['lehrveranstaltung_id_kompatibel']
+ : null;
+ $_POST['begruendung'] =
+ (isset($formData['begruendung_id']) && !empty($formData['begruendung_id']))
+ ? $formData['begruendung_id']
+ : null;
+ $_POST['genehmigtVon'] = (isset($formData['genehmigt_von']) && !empty($formData['genehmigt_von']))
+ ? $formData['genehmigt_von']
+ : null;
+
+ $this->form_validation->set_rules('lehrveranstaltung_id', 'Lehrveranstaltung_id', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehrveranstaltung'])
+ ]);
+
+ $this->form_validation->set_rules('begruendung', 'Begruendung', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Begruendung'])
+ ]);
+
+ if($_POST['begruendung'] == 2)
+ {
+ $this->form_validation->set_rules('lehrveranstaltung_id_kompatibel', 'Lehrveranstaltung_id', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehrveranstaltung Kompatibel'])
+ ]);
+ }
+
+ $this->form_validation->set_rules('genehmigtVon', 'GenehmigtVon', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'GenehmigtVon'])
+ ]);
+
+ if ($this->form_validation->run() == false)
+ {
+ $this->terminateWithValidationErrors($this->form_validation->error_array());
+ }
+
+ $result = $this->AnrechnungsModel->insert(
+ [
+ 'prestudent_id' => $prestudent_id,
+ 'lehrveranstaltung_id' => $_POST['lehrveranstaltung_id'],
+ 'lehrveranstaltung_id_kompatibel' => $_POST['lehrveranstaltung_id_kompatibel'],
+ 'begruendung_id' => $_POST['begruendung'],
+ 'genehmigt_von' => $_POST['genehmigtVon']
+ ]
+ );
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function loadAnrechnung($anrechnung_id)
+ {
+ $result = $this->AnrechnungsModel->loadWhere(
+ array('anrechnung_id' => $anrechnung_id)
+ );
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess(current($data));
+ }
+
+ public function updateAnrechnung()
+ {
+ $this->load->library('form_validation');
+
+ $anrechnung_id = $this->input->post('anrechnung_id');
+
+ if(!$anrechnung_id)
+ {
+ return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Anrechnung UID']), self::ERROR_TYPE_GENERAL);
+ }
+
+ $formData = $this->input->post('formData');
+ $_POST['lehrveranstaltung_id'] =
+ (isset($formData['lehrveranstaltung_id']) && !empty($formData['lehrveranstaltung_id']))
+ ? $formData['lehrveranstaltung_id']
+ : null;
+ $_POST['lehrveranstaltung_id_kompatibel'] =
+ (isset($formData['lehrveranstaltung_id_kompatibel']) && !empty($formData['lehrveranstaltung_id_kompatibel']))
+ ? $formData['lehrveranstaltung_id_kompatibel']
+ : null;
+ $_POST['begruendung'] = (isset($formData['begruendung_id']) && !empty($formData['begruendung_id'])) ? $formData['begruendung_id'] : null;
+ $_POST['genehmigtVon'] = (isset($formData['genehmigt_von']) && !empty($formData['genehmigt_von'])) ? $formData['genehmigt_von'] : null;
+
+ $this->form_validation->set_rules('lehrveranstaltung_id', 'Lehrveranstaltung_id', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehrveranstaltung'])
+ ]);
+
+ $this->form_validation->set_rules('begruendung', 'Begruendung', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Begruendung'])
+ ]);
+
+ if($_POST['begruendung'] == 2)
+ {
+ $this->form_validation->set_rules('lehrveranstaltung_id_kompatibel', 'Lehrveranstaltung_id', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Lehrveranstaltung Kompatibel'])
+ ]);
+ }
+
+ $this->form_validation->set_rules('genehmigtVon', 'GenehmigtVon', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'GenehmigtVon'])
+ ]);
+
+ if ($this->form_validation->run() == false)
+ {
+ $this->terminateWithValidationErrors($this->form_validation->error_array());
+ }
+
+ $result = $this->AnrechnungsModel->update(
+ [
+ 'anrechnung_id' => $anrechnung_id,
+ ],
+ [
+
+ 'lehrveranstaltung_id' => $_POST['lehrveranstaltung_id'],
+ 'lehrveranstaltung_id_kompatibel' => $_POST['lehrveranstaltung_id_kompatibel'],
+ 'begruendung_id' => $_POST['begruendung'],
+ 'genehmigt_von' => $_POST['genehmigtVon']
+ ]
+ );
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function deleteAnrechnung($anrechnung_id)
+ {
+ $result = $this->AnrechnungsModel->delete(
+ array('anrechnung_id' => $anrechnung_id)
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $this->terminateWithSuccess($data);
+ }
+}
diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php
index bcea93fb5..f717137a2 100644
--- a/application/controllers/api/frontend/v1/stv/Config.php
+++ b/application/controllers/api/frontend/v1/stv/Config.php
@@ -130,6 +130,12 @@ class Config extends FHCAPI_Controller
'component' => './Stv/Studentenverwaltung/Details/Pruefung.js'
];
+ $result['exemptions'] = [
+ 'title' => $this->p->t('lehre', 'anrechnungen'),
+ 'component' => './Stv/Studentenverwaltung/Details/Anrechnungen.js',
+ 'config' => $config['exemptions']
+ ];
+
$result['finalexam'] = [
'title' => $this->p->t('stv', 'tab_finalexam'),
'component' => './Stv/Studentenverwaltung/Details/Abschlusspruefung.js',
diff --git a/application/models/education/Anrechnung_model.php b/application/models/education/Anrechnung_model.php
index cbfdb6607..0b81be80c 100644
--- a/application/models/education/Anrechnung_model.php
+++ b/application/models/education/Anrechnung_model.php
@@ -202,4 +202,66 @@ class Anrechnung_model extends DB_Model
return success();
}
+
+ /**
+ * Get Anrechnungsdata for Table Anrechnungen
+ *
+ * @param $prestudent_id
+ * @return array
+ */
+ public function getAnrechnungsData($prestudent_id)
+ {
+ $qry = '
+ SELECT
+ lehre.tbl_anrechnung.anrechnung_id,
+ lehre.tbl_anrechnung.prestudent_id,
+ lehre.tbl_anrechnung.lehrveranstaltung_id,
+ lehre.tbl_lehrveranstaltung.bezeichnung AS bez_lehrveranstaltung,
+ lehre.tbl_anrechnung_begruendung.bezeichnung AS begruendung,
+ lehre.tbl_anrechnung_anrechnungstatus.status_kurzbz AS status,
+ genehmigt_von,
+ lehre.tbl_anrechnung.insertamum,
+ lehre.tbl_anrechnung.insertvon,
+ lehre.tbl_anrechnung.updateamum,
+ lehre.tbl_anrechnung.updatevon,
+ lehrveranstaltung_id_kompatibel,
+ lv_comp.bezeichnung as lehrveranstaltung_bez_kompatibel,
+ count(nz.notizzuordnung_id) AS notizen_anzahl
+ FROM
+ lehre.tbl_anrechnung
+ JOIN lehre.tbl_lehrveranstaltung USING (lehrveranstaltung_id)
+ LEFT JOIN lehre.tbl_lehrveranstaltung lv_comp ON (lehre.tbl_anrechnung.lehrveranstaltung_id_kompatibel = lv_comp.lehrveranstaltung_id)
+ JOIN lehre.tbl_anrechnung_begruendung USING (begruendung_id)
+ LEFT JOIN lehre.tbl_anrechnung_anrechnungstatus ON (lehre.tbl_anrechnung_anrechnungstatus.anrechnung_id = lehre.tbl_anrechnung.anrechnung_id)
+ AND lehre.tbl_anrechnung_anrechnungstatus.insertamum = (
+ SELECT MAX(insertamum)
+ FROM lehre.tbl_anrechnung_anrechnungstatus
+ WHERE anrechnung_id = lehre.tbl_anrechnung.anrechnung_id
+ )
+ LEFT JOIN lehre.tbl_anrechnungstatus USING (status_kurzbz)
+ LEFT JOIN public.tbl_notizzuordnung nz ON (nz.anrechnung_id = lehre.tbl_anrechnung.anrechnung_id)
+ WHERE
+ lehre.tbl_anrechnung.prestudent_id = ?
+ GROUP BY
+ nz.anrechnung_id,
+ lehre.tbl_anrechnung.anrechnung_id,
+ lehre.tbl_anrechnung.prestudent_id,
+ lehre.tbl_anrechnung.lehrveranstaltung_id,
+ bez_lehrveranstaltung,
+ begruendung,
+ status,
+ genehmigt_von,
+ lehre.tbl_anrechnung.insertamum,
+ lehre.tbl_anrechnung.insertvon,
+ lehre.tbl_anrechnung.updateamum,
+ lehre.tbl_anrechnung.updatevon,
+ lehrveranstaltung_id_kompatibel,
+ lehrveranstaltung_bez_kompatibel
+ ORDER BY
+ lehre.tbl_anrechnung.updateamum ASC
+ ';
+
+ return $this->execQuery($qry, array($prestudent_id));
+
+ }
}
diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php
index 056fb45d7..77ed7b4b0 100644
--- a/application/models/education/Lehrveranstaltung_model.php
+++ b/application/models/education/Lehrveranstaltung_model.php
@@ -988,4 +988,41 @@ class Lehrveranstaltung_model extends DB_Model
return $this->execQuery($qry, $params);
}
+
+ /**
+ * Gets lehrveranstaltungen of Studienplan
+ * @param $studienplan_id ID des Studienplans
+ * @param $semester Semester optional
+ * @return array|null
+ */
+ public function getLvsByStudienplanId($studienplan_id, $semester = null)
+ {
+ $params = array($studienplan_id);
+
+ $qry = "SELECT tbl_lehrveranstaltung.*,
+ tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id,
+ tbl_studienplan_lehrveranstaltung.semester as stpllv_semester,
+ tbl_studienplan_lehrveranstaltung.pflicht as stpllv_pflicht,
+ tbl_studienplan_lehrveranstaltung.koordinator as stpllv_koordinator,
+ tbl_studienplan_lehrveranstaltung.studienplan_lehrveranstaltung_id_parent,
+ tbl_studienplan_lehrveranstaltung.sort stpllv_sort,
+ tbl_studienplan_lehrveranstaltung.curriculum,
+ tbl_studienplan_lehrveranstaltung.export,
+ tbl_studienplan_lehrveranstaltung.genehmigung
+ FROM lehre.tbl_lehrveranstaltung
+ JOIN lehre.tbl_studienplan_lehrveranstaltung
+ USING(lehrveranstaltung_id)
+ WHERE tbl_studienplan_lehrveranstaltung.studienplan_id = ?
+ ";
+
+ if ($semester !== null)
+ {
+ $qry.= " AND tbl_studienplan_lehrveranstaltung.semester = ?";
+ $params[] = $semester;
+ }
+
+ $qry .= " ORDER BY stpllv_sort, semester, sort";
+
+ return $this->execQuery($qry, $params);
+ }
}
diff --git a/application/models/ressource/Mitarbeiter_model.php b/application/models/ressource/Mitarbeiter_model.php
index 836f5d65a..2b652c65b 100644
--- a/application/models/ressource/Mitarbeiter_model.php
+++ b/application/models/ressource/Mitarbeiter_model.php
@@ -261,9 +261,9 @@ class Mitarbeiter_model extends DB_Model
* @param $lehrveranstaltung_id
* @return array with Mitarbeiter and their Lehreinheiten
*/
- public function getMitarbeiterFromLV($lehrveranstaltung_id){
- //TODO(manu) maybe filter that in pruefungslist.js ?
- $qry = "SELECT DISTINCT
+ public function getMitarbeiterFromLV($lehrveranstaltung_id)
+ {
+ $qry = "SELECT DISTINCT
lehrveranstaltung_id, uid, vorname, wahlname, vornamen, nachname, titelpre, titelpost, kurzbz, mitarbeiter_uid
FROM
lehre.tbl_lehreinheitmitarbeiter, campus.vw_mitarbeiter, lehre.tbl_lehreinheit
@@ -278,4 +278,33 @@ class Mitarbeiter_model extends DB_Model
return $this->execQuery($qry, $parametersArray);
}
+
+ /**
+ * Get Lektoren by studiengang_kz
+ *
+ * @param $studiengang_kz
+ * @return array with Mitarbeiter
+ */
+ public function getLektoren($studiengang_kz)
+ {
+ $qry = "
+ SELECT DISTINCT
+ campus.vw_mitarbeiter.uid,
+ campus.vw_mitarbeiter.vorname,
+ campus.vw_mitarbeiter.nachname,
+ studiengang_kz,
+ tbl_studiengang.typ,
+ tbl_studiengang.kurzbz AS stg_kurzbz
+ FROM
+ campus.vw_mitarbeiter
+ JOIN public.tbl_benutzerfunktion USING (uid)
+ JOIN public.tbl_studiengang USING(oe_kurzbz)
+ WHERE studiengang_kz = ?
+ AND lektor is true
+ ORDER BY campus.vw_mitarbeiter.nachname";
+
+ $parametersArray = array($studiengang_kz);
+
+ return $this->execQuery($qry, $parametersArray);
+ }
}
diff --git a/public/js/api/factory/stv/exemptions.js b/public/js/api/factory/stv/exemptions.js
new file mode 100644
index 000000000..2bf8ee610
--- /dev/null
+++ b/public/js/api/factory/stv/exemptions.js
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2025 fhcomplete.org
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
{{$p.t('anrechnung', 'neueAnrechnung')}}
+{{$p.t('anrechnung', 'editAnrechnung')}}
+ + +