diff --git a/application/controllers/api/frontend/v1/lv/Noten.php b/application/controllers/api/frontend/v1/lv/Noten.php new file mode 100644 index 000000000..614ee6609 --- /dev/null +++ b/application/controllers/api/frontend/v1/lv/Noten.php @@ -0,0 +1,84 @@ + 'student/noten:r', + 'getTeacherProposal' => 'student/noten:r', + ]); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + + // Load Phrases + $this->loadPhrases([ + 'stv', + 'person', + 'lehre' + ]); + } + + public function getCertificate($lv_id, $studiensemester_kurzbz = null) + { + if (is_null($lv_id) || !ctype_digit((string)$lv_id)) + $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel'); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $result = $this->LehrveranstaltungModel->loadWhere([ + 'lehrveranstaltung_id' => $lv_id + ]); + + $lehrveranstaltung = $this->getDataOrTerminateWithError($result); + + if (!$lehrveranstaltung) + $this->terminateWithSuccess([]); + + if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) + { + $this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester')); + } + + $result = $this->ZeugnisnoteModel->getZeugnisnoten(null, $studiensemester_kurzbz, $lehrveranstaltung[0]->lehrveranstaltung_id); + + $grades = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($grades); + } + + public function getTeacherProposal($lv_id, $studiensemester_kurzbz = null) + { + if (is_null($lv_id) || !ctype_digit((string)$lv_id)) + $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $this->load->model('education/lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->load->model('education/Lvgesamtnote_model', 'LvgesamtnoteModel'); + $this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); + + $result = $this->LehrveranstaltungModel->loadWhere([ + 'lehrveranstaltung_id' => $lv_id + ]); + + $lehrveranstaltung = $this->getDataOrTerminateWithError($result); + + if (!$lehrveranstaltung) + $this->terminateWithSuccess([]); + + if ($studiensemester_kurzbz !== null && !$this->StudiensemesterModel->isValidStudiensemester($studiensemester_kurzbz)) + { + $this->terminateWithError($studiensemester_kurzbz . ' - ' . $this->p->t('lehre', 'error_noStudiensemester')); + } + + $result = $this->LvgesamtnoteModel->getLvGesamtNoten($lv_id, null, $studiensemester_kurzbz); + + $grades = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($grades); + } + +} diff --git a/application/controllers/api/frontend/v1/lv/Setup.php b/application/controllers/api/frontend/v1/lv/Setup.php index fe8450f06..e506a9d1b 100644 --- a/application/controllers/api/frontend/v1/lv/Setup.php +++ b/application/controllers/api/frontend/v1/lv/Setup.php @@ -42,6 +42,7 @@ class Setup extends FHCAPI_Controller $this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); $this->_ci->load->library('VariableLib', ['uid' => $this->_uid]); + $this->_ci->load->helper('hlp_document'); } public function getLETabs() @@ -81,6 +82,18 @@ class Setup extends FHCAPI_Controller 'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/LVTermine.js', 'config' => [] ); + $tabs['noten'] = array ( + 'title' => 'Noten', + 'component' => APP_ROOT . 'public/js/components/LVVerwaltung/Tabs/Noten.js', + 'config' => [ + 'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE, + 'edit' => 'both', // Possible values: both|header|inline + 'delete' => 'inline', // Possible values: both|header|inline + 'documents' => 'inline', // Possible values: both|header|inline + 'documentslist' => gradesDocumentsList(), + 'semesterSelect' => false + ] + ); $this->terminateWithSuccess($tabs); } diff --git a/application/controllers/api/frontend/v1/stv/Config.php b/application/controllers/api/frontend/v1/stv/Config.php index 3bf48bf5b..05e642182 100644 --- a/application/controllers/api/frontend/v1/stv/Config.php +++ b/application/controllers/api/frontend/v1/stv/Config.php @@ -299,6 +299,7 @@ class Config extends FHCAPI_Controller public function student() { + $this->load->helper('hlp_document'); $result = []; $config = $this->config->item('tabs'); @@ -372,9 +373,9 @@ class Config extends FHCAPI_Controller 'config' => [ 'usePoints' => defined('CIS_GESAMTNOTE_PUNKTE') && CIS_GESAMTNOTE_PUNKTE, 'edit' => 'both', // Possible values: both|header|inline - 'delete' => 'both', // Possible values: both|header|inline - 'documents' => 'both', // Possible values: both|header|inline - 'documentslist' => $this->gradesDocumentsList() + 'delete' => 'inline', // Possible values: both|header|inline + 'documents' => 'inline', // Possible values: both|header|inline + 'documentslist' => gradesDocumentsList() ] ]; @@ -613,188 +614,6 @@ class Config extends FHCAPI_Controller ] + $this->kontoColumns(); } - /** - * Helper function to generate the default documentslist config for the - * grades tab. - * - * The resulting array consists of elements which are associative arrays - * that can have the following entries: - * title (required) on the first level this can be HTML code. - * permissioncheck (optional) an URL to an FHCAPI endpoint which returns - * true or false. - * link (optional) an URL that will be called if "action" and - * "children" are not defined. - * action (optional) an associative array that describes an - * POST action that will be called if "children" is - * not defined. - * It can have the following entries: - * - url (required) an URL to an FHCAPI endpoint. - * - post (optional) an associative array with the POST data to - * be sent. - * - response (optional) a string that will be displayed on success. - * children (optional) an array of child elements - * - * All strings that start with { and end with } in the URLs and the - * actions post parameter will be replaced with the corresponding - * attribute of the current dataset (e.G: {uid} will be replaced with the - * uid of the current dataset) - * - * @return array - */ - protected function gradesDocumentsList() - { - $permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}"); - - $title_ger = $this->p->t("global", "deutsch"); - $title_eng = $this->p->t("global", "englisch"); - $title_ff = $this->p->t("stv", "document_certificate"); - $title_lv = $this->p->t("stv", "document_coursecertificate"); - - $link_ff = "documents/export/" . - "zertifikat.rdf.php/" . - "Zertifikat" . - "?stg_kz={studiengang_kz_lv}" . - "&uid={uid}" . - "&ss={studiensemester_kurzbz}" . - "&lvid={lehrveranstaltung_id}"; - $link_lv_ger = "documents/export/" . - "lehrveranstaltungszeugnis.rdf.php/" . - "LVZeugnis" . - "?stg_kz={studiengang_kz}" . - "&uid={uid}" . - "&ss={studiensemester_kurzbz}" . - "&lvid={lehrveranstaltung_id}"; - $link_lv_eng = "documents/export/" . - "lehrveranstaltungszeugnis.rdf.php/" . - "LVZeugnisEng" . - "?stg_kz={studiengang_kz}" . - "&uid={uid}" . - "&ss={studiensemester_kurzbz}" . - "&lvid={lehrveranstaltung_id}"; - - $archive_url = "api/frontend/v1/documents/archiveSigned"; - $archive_response = $this->p->t("stv", "document_signed_and_archived"); - $archive_post_ff = [ - "xml" => "zertifikat.rdf.php", - "xsl" => "Zertifikat", - "stg_kz" => "{studiengang_kz_lv}", - "uid" => "{uid}", - "ss" => "{studiensemester_kurzbz}", - "lvid" => "{lehrveranstaltung_id}" - ]; - $archive_post_lv_ger = [ - "xml" => "lehrveranstaltungszeugnis.rdf.php", - "xsl" => "LVZeugnis", - "stg_kz" => "{studiengang_kz}", - "uid" => "{uid}", - "ss" => "{studiensemester_kurzbz}", - "lvid" => "{lehrveranstaltung_id}" - ]; - $archive_post_lv_eng = [ - "xml" => "lehrveranstaltungszeugnis.rdf.php", - "xsl" => "LVZeugnisEng", - "stg_kz" => "{studiengang_kz}", - "uid" => "{uid}", - "ss" => "{studiensemester_kurzbz}", - "lvid" => "{lehrveranstaltung_id}" - ]; - - $list = [ - [ - 'title' => '', - 'children' => [ - [ - 'title' => $title_ff, - 'link' => site_url($link_ff) - ], - [ - 'title' => $title_lv, - 'children' => [ - [ - 'title' => $title_ger, - 'link' => site_url($link_lv_ger), - 'children' => [ - [ - 'title' => 'PDF', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_ger) - ], - [ - 'title' => 'DOC', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_ger . "&output=doc") - ], - [ - 'title' => 'ODT', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_ger . "&output=odt") - ] - ] - ], - [ - 'title' => $title_eng, - 'link' => site_url($link_lv_eng), - 'children' => [ - [ - 'title' => 'PDF', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_eng) - ], - [ - 'title' => 'DOC', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_eng . "&output=doc") - ], - [ - 'title' => 'ODT', - 'permissioncheck' => $permissioncheck, - 'link' => site_url($link_lv_eng . "&output=odt") - ] - ] - ] - ] - ] - ] - ], - [ - 'title' => '', - 'children' => [ - [ - 'title' => $title_ff, - 'action' => [ - 'url' => site_url($archive_url), - 'post' => $archive_post_ff, - 'response' => $archive_response - ] - ], - [ - 'title' => $title_lv, - 'children' => [ - [ - 'title' => $title_ger, - 'action' => [ - 'url' => site_url($archive_url), - 'post' => $archive_post_lv_ger, - 'response' => $archive_response - ] - ], - [ - 'title' => $title_eng, - 'action' => [ - 'url' => site_url($archive_url), - 'post' => $archive_post_lv_eng, - 'response' => $archive_response - ] - ] - ] - ] - ] - ] - ]; - - return $list; - } - /** * Sort tab list * diff --git a/application/helpers/hlp_document_helper.php b/application/helpers/hlp_document_helper.php new file mode 100644 index 000000000..5b457fe82 --- /dev/null +++ b/application/helpers/hlp_document_helper.php @@ -0,0 +1,188 @@ +load->library('PhrasesLib', array('stv'), 'p'); + + $permissioncheck = site_url("api/frontend/v1/documents/permissionAlternativeFormat/{studiengang_kz}"); + + $title_ger = $ci->p->t("global", "deutsch"); + $title_eng = $ci->p->t("global", "englisch"); + $title_ff = $ci->p->t("stv", "document_certificate"); + $title_lv = $ci->p->t("stv", "document_coursecertificate"); + + $link_ff = "documents/export/" . + "zertifikat.rdf.php/" . + "Zertifikat" . + "?stg_kz={studiengang_kz_lv}" . + "&uid={uid}" . + "&ss={studiensemester_kurzbz}" . + "&lvid={lehrveranstaltung_id}"; + $link_lv_ger = "documents/export/" . + "lehrveranstaltungszeugnis.rdf.php/" . + "LVZeugnis" . + "?stg_kz={studiengang_kz}" . + "&uid={uid}" . + "&ss={studiensemester_kurzbz}" . + "&lvid={lehrveranstaltung_id}"; + $link_lv_eng = "documents/export/" . + "lehrveranstaltungszeugnis.rdf.php/" . + "LVZeugnisEng" . + "?stg_kz={studiengang_kz}" . + "&uid={uid}" . + "&ss={studiensemester_kurzbz}" . + "&lvid={lehrveranstaltung_id}"; + + $archive_url = "api/frontend/v1/documents/archiveSigned"; + $archive_response = $ci->p->t("stv", "document_signed_and_archived"); + $archive_post_ff = [ + "xml" => "zertifikat.rdf.php", + "xsl" => "Zertifikat", + "stg_kz" => "{studiengang_kz_lv}", + "uid" => "{uid}", + "ss" => "{studiensemester_kurzbz}", + "lvid" => "{lehrveranstaltung_id}" + ]; + $archive_post_lv_ger = [ + "xml" => "lehrveranstaltungszeugnis.rdf.php", + "xsl" => "LVZeugnis", + "stg_kz" => "{studiengang_kz}", + "uid" => "{uid}", + "ss" => "{studiensemester_kurzbz}", + "lvid" => "{lehrveranstaltung_id}" + ]; + $archive_post_lv_eng = [ + "xml" => "lehrveranstaltungszeugnis.rdf.php", + "xsl" => "LVZeugnisEng", + "stg_kz" => "{studiengang_kz}", + "uid" => "{uid}", + "ss" => "{studiensemester_kurzbz}", + "lvid" => "{lehrveranstaltung_id}" + ]; + + $list = [ + [ + 'title' => '', + 'children' => [ + [ + 'title' => $title_ff, + 'link' => site_url($link_ff) + ], + [ + 'title' => $title_lv, + 'children' => [ + [ + 'title' => $title_ger, + 'link' => site_url($link_lv_ger), + 'children' => [ + [ + 'title' => 'PDF', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_ger) + ], + [ + 'title' => 'DOC', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_ger . "&output=doc") + ], + [ + 'title' => 'ODT', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_ger . "&output=odt") + ] + ] + ], + [ + 'title' => $title_eng, + 'link' => site_url($link_lv_eng), + 'children' => [ + [ + 'title' => 'PDF', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_eng) + ], + [ + 'title' => 'DOC', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_eng . "&output=doc") + ], + [ + 'title' => 'ODT', + 'permissioncheck' => $permissioncheck, + 'link' => site_url($link_lv_eng . "&output=odt") + ] + ] + ] + ] + ] + ] + ], + [ + 'title' => '', + 'children' => [ + [ + 'title' => $title_ff, + 'action' => [ + 'url' => site_url($archive_url), + 'post' => $archive_post_ff, + 'response' => $archive_response + ] + ], + [ + 'title' => $title_lv, + 'children' => [ + [ + 'title' => $title_ger, + 'action' => [ + 'url' => site_url($archive_url), + 'post' => $archive_post_lv_ger, + 'response' => $archive_response + ] + ], + [ + 'title' => $title_eng, + 'action' => [ + 'url' => site_url($archive_url), + 'post' => $archive_post_lv_eng, + 'response' => $archive_response + ] + ] + ] + ] + ] + ] + ]; + + return $list; +} diff --git a/application/models/education/Lvgesamtnote_model.php b/application/models/education/Lvgesamtnote_model.php index c30045ff0..0a7638869 100644 --- a/application/models/education/Lvgesamtnote_model.php +++ b/application/models/education/Lvgesamtnote_model.php @@ -29,10 +29,14 @@ class Lvgesamtnote_model extends DB_Model $this->addSelect("lv.bezeichnung AS lehrveranstaltung_bezeichnung"); $this->addSelect("lv.studiengang_kz"); $this->addSelect("UPPER(stg.typ || stg.kurzbz) AS studiengang"); + $this->addSelect("person.vorname"); + $this->addSelect("person.nachname"); $this->addJoin("lehre.tbl_note n", "note"); $this->addJoin("lehre.tbl_lehrveranstaltung lv", "lehrveranstaltung_id"); $this->addJoin("public.tbl_studiengang stg", "studiengang_kz"); + $this->addJoin("public.tbl_benutzer benutzer", "uid = student_uid", "LEFT"); + $this->addJoin("public.tbl_person person", "person_id", "LEFT"); $this->db->where($this->dbTable . ".freigabedatum <", "NOW()", false); diff --git a/application/models/education/Zeugnisnote_model.php b/application/models/education/Zeugnisnote_model.php index f32713dec..694c5d5c0 100644 --- a/application/models/education/Zeugnisnote_model.php +++ b/application/models/education/Zeugnisnote_model.php @@ -148,7 +148,7 @@ class Zeugnisnote_model extends DB_Model * * @return object */ - public function getZeugnisnoten($student_uid, $studiensemester_kurzbz) + public function getZeugnisnoten($student_uid, $studiensemester_kurzbz, $lehrveranstaltung_id = null) { $params = array(); $where=''; @@ -163,6 +163,11 @@ class Zeugnisnote_model extends DB_Model $where.=" AND vw_student_lehrveranstaltung.studiensemester_kurzbz= ?"; $params[] = $studiensemester_kurzbz; } + if($lehrveranstaltung_id != null) + { + $where .= " AND vw_student_lehrveranstaltung.lehrveranstaltung_id = ?"; + $params[] = $lehrveranstaltung_id; + } $where2=''; @@ -176,6 +181,11 @@ class Zeugnisnote_model extends DB_Model $where2 .= " AND studiensemester_kurzbz= ?"; $params[] = $studiensemester_kurzbz; } + if($lehrveranstaltung_id != null) + { + $where2 .=" AND lehrveranstaltung_id = ?"; + $params[] = $lehrveranstaltung_id; + } $qry = "SELECT a.*, @@ -188,7 +198,10 @@ class Zeugnisnote_model extends DB_Model lv.semester AS semester_lv, lv.ects AS ects_lv, lv.zeugnis, - lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english + lv.bezeichnung_english AS lehrveranstaltung_bezeichnung_english, + s.verband, + person.vorname, + person.nachname FROM ( SELECT vw_student_lehrveranstaltung.lehrveranstaltung_id, uid, vw_student_lehrveranstaltung.studiensemester_kurzbz, note, punkte, uebernahmedatum, benotungsdatum, @@ -231,6 +244,8 @@ class Zeugnisnote_model extends DB_Model ORDER BY sort ) a LEFT JOIN public.tbl_student s ON (a.uid = s.student_uid) + LEFT JOIN public.tbl_benutzer benutzer ON benutzer.uid = s.student_uid + LEFT JOIN public.tbl_person person ON benutzer.person_id = person.person_id LEFT JOIN public.tbl_studiengang stg1 ON (s.studiengang_kz = stg1.studiengang_kz) LEFT JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id) LEFT JOIN public.tbl_studiengang stg2 ON (lv.studiengang_kz = stg2.studiengang_kz)"; diff --git a/public/js/api/lehrveranstaltung/noten.js b/public/js/api/lehrveranstaltung/noten.js new file mode 100644 index 000000000..fdcbb6f1e --- /dev/null +++ b/public/js/api/lehrveranstaltung/noten.js @@ -0,0 +1,25 @@ +import Grades from '../factory/stv/grades.js'; + +export default { + ...Grades, + getCertificate(lv_id, studiensemester_kurzbz) { + let url = 'api/frontend/v1/lv/noten/getCertificate/' + encodeURIComponent(lv_id); + if (!!studiensemester_kurzbz) { + url = url + '/' + encodeURIComponent(studiensemester_kurzbz); + } + return { + method: 'get', + url: url + }; + }, + getTeacherProposal(lv_id, studiensemester_kurzbz) { + let url = 'api/frontend/v1/lv/noten/getTeacherProposal/' + encodeURIComponent(lv_id); + if (!!studiensemester_kurzbz) { + url = url + '/' + encodeURIComponent(studiensemester_kurzbz); + } + return { + method: 'get', + url: url + }; + }, +}; \ No newline at end of file diff --git a/public/js/components/LVVerwaltung/Tabs/Noten.js b/public/js/components/LVVerwaltung/Tabs/Noten.js new file mode 100644 index 000000000..8b01a017d --- /dev/null +++ b/public/js/components/LVVerwaltung/Tabs/Noten.js @@ -0,0 +1,116 @@ +import NotenZeugnis from "../../Stv/Studentenverwaltung/Details/Noten/Zeugnis.js"; +import NotenTeacher from "../../Stv/Studentenverwaltung/Details/Noten/Teacher.js"; +import NotenRepeater from "../../Stv/Studentenverwaltung/Details/Noten/Repeater.js"; +import ApiLVNoten from "../../../api/lehrveranstaltung/noten.js"; +import { highlightGesamtnote } from "../../../helpers/DocumentHelper.js"; +const LOCAL_STORAGE_ID = 'lv_details_noten_2025_12_02_stdsem_all'; + +export default { + name: "LVTabNoten", + components: { + NotenZeugnis, + NotenTeacher, + NotenRepeater + }, + provide() { + return { + config: this.config + } + }, + props: { + modelValue: Object, + config: Object + }, + data() { + return { + stdsem: '', + endpoint: ApiLVNoten, + tabulatorOptions: { + visibleColumns: { + vorname: true, + nachname: true, + lehrveranstaltung_bezeichnung: false + }, + headerFilter: { + vorname: true, + nachname: true, + lehrveranstaltung_bezeichnung: false + }, + persistenceZeugnisID: 'lv-details-noten-zeugnis-2025120401', + persistenceTeacherID: 'lv-details-noten-teacher-2025120401', + }, + zeugnisLoaded: false, + teacherLoaded: false, + }; + }, + methods: { + reload() { + this.zeugnisLoaded = false; + this.teacherLoaded = false; + + this.$refs.zeugnis.$refs.table.reloadTable(); + this.$refs.teacher.$refs.table.reloadTable(); + }, + saveStdsem(event) { + window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value); + }, + onZeugnisLoaded() { + this.zeugnisLoaded = true; + this.checkHighlight(); + }, + onTeacherLoaded() { + this.teacherLoaded = true; + this.checkHighlight(); + }, + checkHighlight() + { + if (!this.zeugnisLoaded || !this.teacherLoaded) + return; + + if (!this.$refs.zeugnis || !this.$refs.teacher) + return; + + let zeugnisTable = this.$refs.zeugnis.$refs.table.tabulator; + let teacherTable = this.$refs.teacher.$refs.table.tabulator; + + if (!zeugnisTable || !teacherTable) + return; + + highlightGesamtnote(zeugnisTable, teacherTable); + } + }, + created() { + const savedPath = window.localStorage.getItem(LOCAL_STORAGE_ID); + this.stdsem = savedPath || ''; + }, + template: ` +
+
+ +
+
+
+ +
+
+ +
+
+
` +}; \ No newline at end of file diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js index 71b8096ec..3217f192c 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten.js @@ -2,6 +2,9 @@ import NotenZeugnis from './Noten/Zeugnis.js'; import NotenTeacher from './Noten/Teacher.js'; import NotenRepeater from './Noten/Repeater.js'; +import ApiStvGrades from '../../../../api/factory/stv/grades.js'; +import { highlightGesamtnote } from "../../../../helpers/DocumentHelper"; + const LOCAL_STORAGE_ID = 'stv_details_noten_2024-11-25_stdsem_all'; export default { @@ -22,7 +25,10 @@ export default { }, data() { return { - stdsem: '' + stdsem: '', + endpoint: ApiStvGrades, + zeugnisLoaded: false, + teacherLoaded: false, }; }, methods: { @@ -33,6 +39,30 @@ export default { }, saveStdsem(event) { window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value); + }, + onZeugnisLoaded() { + this.zeugnisLoaded = true; + this.checkHighlight(); + }, + onTeacherLoaded() { + this.teacherLoaded = true; + this.checkHighlight(); + }, + checkHighlight() + { + if (!this.zeugnisLoaded || !this.teacherLoaded) + return; + + if (!this.$refs.zeugnis || !this.$refs.teacher) + return; + + let zeugnisTable = this.$refs.zeugnis.$refs.table.tabulator; + let teacherTable = this.$refs.teacher.$refs.table.tabulator; + + if (!zeugnisTable || !teacherTable) + return; + + highlightGesamtnote(zeugnisTable, teacherTable); } }, created() { @@ -49,10 +79,10 @@ export default {
- +
- +
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js index e566d3a09..f25d291cf 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Teacher.js @@ -1,13 +1,12 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js"; -import ApiStvGrades from '../../../../../api/factory/stv/grades.js'; - export default { components: { CoreFilterCmpt }, emits: [ - "copied" + "copied", + "loaded" ], inject: { currentSemester: { @@ -16,47 +15,127 @@ export default { } }, props: { - student: Object, - allSemester: Boolean + endpoint: { + type: Object, + required: true + }, + id: { + type: [Number, String], + required: true + }, + allSemester: Boolean, + optionalTabulatorOptions: Object, }, data() { return { - tabulatorEvents: [] + tabulatorEvents: [ + { + event: "dataProcessed", + handler: () => this.$emit("loaded"), + }, + ] }; }, computed: { tabulatorOptions() { return { ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call(ApiStvGrades.getTeacherProposal( - this.student.prestudent_id, + ajaxRequestFunc: () => this.$api.call(this.endpoint.getTeacherProposal( + this.id, (!this.allSemester ? this.currentSemester : null) )), ajaxResponse: (url, params, response) => { return response.data || []; }, columns: [ - { field: 'lehrveranstaltung_bezeichnung', title: this.$p.t('lehre/lehrveranstaltung') }, - { field: 'note_bezeichnung', title: this.$p.t('lehre/note') }, - { field: 'mitarbeiter_uid', title: this.$p.t('profil/mitarbeiterIn'), visible: false }, - { field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false }, - { field: 'freigabedatum', title: this.$p.t('stv/grades_approvaldate'), visible: false }, - { field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false }, - { field: 'note', title: this.$p.t('stv/grades_numericgrade'), visible: false }, - { field: 'student_uid', title: this.$p.t('profil/studentIn'), visible: false }, - { field: 'lehrveranstaltung_id', title: this.$p.t('lehre/lehrveranstaltung_id'), visible: false }, - { field: 'punkte', title: this.$p.t('stv/grades_points'), visible: false } + { + field: 'lehrveranstaltung_bezeichnung', + title: this.$p.t('lehre/lehrveranstaltung'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_bezeichnung ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_bezeichnung || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'note_bezeichnung', + title: this.$p.t('lehre/note'), + visible: this.optionalTabulatorOptions?.visibleColumns?.note_bezeichnung ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.note_bezeichnung || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'mitarbeiter_uid', + title: this.$p.t('profil/mitarbeiterIn'), + visible: this.optionalTabulatorOptions?.visibleColumns?.mitarbeiter_uid ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.mitarbeiter_uid || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'benotungsdatum', + title: this.$p.t('stv/grades_gradingdate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.benotungsdatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.benotungsdatum || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'freigabedatum', + title: this.$p.t('stv/grades_approvaldate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.freigabedatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.freigabedatum || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'studiensemester_kurzbz', + title: this.$p.t('lehre/studiensemester'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiensemester_kurzbz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiensemester_kurzbz || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'note', + title: this.$p.t('stv/grades_numericgrade'), + visible: this.optionalTabulatorOptions?.visibleColumns?.note ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.note || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'student_uid', + title: this.$p.t('profil/studentIn'), + visible: this.optionalTabulatorOptions?.visibleColumns?.student_uid ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.student_uid || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'lehrveranstaltung_id', + title: this.$p.t('lehre/lehrveranstaltung_id'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_id ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_id || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'punkte', + title: this.$p.t('stv/grades_points'), + visible: this.optionalTabulatorOptions?.visibleColumns?.punkte ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.punkte || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'vorname', + title: this.$p.t('person/vorname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.vorname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.vorname || this.optionalTabulatorOptions?.headerFilter || false + }, + { + field: 'nachname', + title: this.$p.t('person/nachname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.nachname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.nachname || this.optionalTabulatorOptions?.headerFilter || false + } ], + columnDefaults: { + headerFilter: this.optionalTabulatorOptions?.headerFilter ?? false, + }, layout: 'fitDataStretch', height: '100%', selectable: true, + selectableRows: true, // needed for Tabulator v6 + selectableRowsRangeMode: 'click', // needed for Tabulator v6 selectableRangeMode: 'click', - persistenceID: 'stv-details-noten-teacher' + persistenceID: this.optionalTabulatorOptions?.persistenceTeacherID ?? 'stv-details-noten-teacher-2025120401', }; } }, watch: { - student(n) { + id() { this.$refs.table.reloadTable(); }, allSemester(n) { @@ -67,7 +146,7 @@ export default { copyGrades(selected) { const promises = selected.map( grade => this.$api - .call(ApiStvGrades.copyTeacherProposalToCertificate(grade), { + .call(this.endpoint.copyTeacherProposalToCertificate(grade), { errorHeader: grade.lehrveranstaltung_bezeichnung }) .then(() => { diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js index 4d0996b6b..8e921c668 100644 --- a/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js +++ b/public/js/components/Stv/Studentenverwaltung/Details/Noten/Zeugnis.js @@ -2,8 +2,6 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js"; import ZeugnisActions from './Zeugnis/Actions.js'; import ZeugnisDocuments from './Zeugnis/Documents.js'; -import ApiStvGrades from '../../../../../api/factory/stv/grades.js'; - export default { name: 'Zeugnis', components: { @@ -11,6 +9,7 @@ export default { ZeugnisActions, ZeugnisDocuments }, + emits: ['loaded'], inject: { config: { from: 'config', @@ -22,7 +21,15 @@ export default { } }, props: { - student: Object, + endpoint: { + type: Object, + required: true + }, + id: { + type: [Number, String], + required: true + }, + optionalTabulatorOptions: Object, allSemester: Boolean }, data() { @@ -35,6 +42,10 @@ export default { return item; }) }, + { + event: "dataProcessed", + handler: () => this.$emit("loaded") + }, { event: "rowSelected", handler: row => row.getElement().style.zIndex = 12 @@ -53,7 +64,7 @@ export default { computed: { tabulatorOptions() { const listPromise = this.$api - .call(ApiStvGrades.list()) + .call(this.endpoint.list()) .then(res => res.data.map(({bezeichnung: label, note: value}) => ({label, value}))); let gradeField = { @@ -86,7 +97,7 @@ export default { note_bezeichnung })) // send to backend - .then(data => this.$api.call(ApiStvGrades.updateCertificate(data))) + .then(data => this.$api.call(this.endpoint.updateCertificate(data))) // get bezeichnung again .then(() => listPromise) .then(list => list.find(el => el.value == note)) @@ -109,7 +120,7 @@ export default { if (filterTerm) { return this.$api .call( - ApiStvGrades.getGradeFromPoints( + this.endpoint.getGradeFromPoints( filterTerm, cell.getData().lehrveranstaltung_id, this.currentSemester @@ -142,27 +153,146 @@ export default { .then(() => node.innerText = this.$p.t('ui/loading')) .catch(this.$fhcAlert.handleSystemError); gradeField.editorParams.placeholderLoading = node; + gradeField.visible = this.optionalTabulatorOptions?.visibleColumns?.note ?? true + gradeField.headerFiler = this.optionalTabulatorOptions?.headerFilter?.note || this.optionalTabulatorOptions?.note || false } const columns = [ - { field: 'zeugnis', title: this.$p.t('stv/grades_zeugnis'), formatter: 'tickCross' }, - { field: 'lehrveranstaltung_bezeichnung', title: this.$p.t('lehre/lehrveranstaltung') }, - gradeField, - { field: 'uebernahmedatum', title: this.$p.t('stv/grades_takeoverdate'), visible: false }, - { field: 'benotungsdatum', title: this.$p.t('stv/grades_gradingdate'), visible: false }, - { field: 'studiensemester_kurzbz', title: this.$p.t('lehre/studiensemester'), visible: false }, - { field: 'note_number', title: this.$p.t('stv/grades_numericgrade'), visible: false, formatter: cell => cell.getData().note, tooltip: (evt, cell) => cell.getData().note }, - { field: 'lehrveranstaltung_id', title: this.$p.t('lehre/lehrveranstaltung_id'), visible: false }, - { field: 'studiengang', title: this.$p.t('lehre/studiengang'), visible: false }, - { field: 'studiengang_kz', title: this.$p.t('lehre/studiengangskennzahlLehre'), visible: false }, - { field: 'studiengang_lv', title: this.$p.t('stv/grades_studiengang_lv'), visible: false }, - { field: 'studiengang_kz_lv', title: this.$p.t('stv/grades_studiengang_kz_lv'), visible: false }, - { field: 'semester_lv', title: this.$p.t('stv/grades_semester_lv'), visible: false }, - { field: 'ects_lv', title: this.$p.t('lehre/ects'), visible: false }, - { field: 'lehrform', title: this.$p.t('lehre/lehrform'), visible: false }, - { field: 'kurzbz', title: this.$p.t('lehre/kurzbz'), visible: false }, - { field: 'punkte', title: this.$p.t('stv/grades_points'), visible: false }, - { field: 'lehrveranstaltung_bezeichnung_english', title: this.$p.t('stv/grades_lehrveranstaltung_bezeichnung_english'), visible: false } + { + field: 'zeugnis', + title: this.$p.t('stv/grades_zeugnis'), + formatter: 'tickCross', + visible: this.optionalTabulatorOptions?.visibleColumns?.zeugnis ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.zeugnis || this.optionalTabulatorOptions?.zeugnis || false + }, + { + field: 'lehrveranstaltung_bezeichnung', + title: this.$p.t('lehre/lehrveranstaltung'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_bezeichnung ?? true, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_bezeichnung || this.optionalTabulatorOptions?.lehrveranstaltung_bezeichnung || false + + }, + gradeField, + { + field: 'uebernahmedatum', + title: this.$p.t('stv/grades_takeoverdate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.uebernahmedatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.uebernahmedatum || this.optionalTabulatorOptions?.uebernahmedatum || false + + }, + { + field: 'benotungsdatum', + title: this.$p.t('stv/grades_gradingdate'), + visible: this.optionalTabulatorOptions?.visibleColumns?.benotungsdatum ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.benotungsdatum || this.optionalTabulatorOptions?.benotungsdatum || false + + }, + { + field: 'studiensemester_kurzbz', + title: this.$p.t('lehre/studiensemester'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiensemester_kurzbz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiensemester_kurzbz || this.optionalTabulatorOptions?.studiensemester_kurzbz || false + + }, + { + field: 'note_number', + title: this.$p.t('stv/grades_numericgrade'), + visible: this.optionalTabulatorOptions?.visibleColumns?.note_number ?? false, + formatter: cell => cell.getData().note, tooltip: (evt, cell) => cell.getData().note, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.note_number || this.optionalTabulatorOptions?.note_number || false + + }, + { + field: 'lehrveranstaltung_id', + title: this.$p.t('lehre/lehrveranstaltung_id'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_id ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_id || this.optionalTabulatorOptions?.lehrveranstaltung_id || false + + }, + { + field: 'studiengang', + title: this.$p.t('lehre/studiengang'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang || this.optionalTabulatorOptions?.studiengang || false + + }, + { + field: 'studiengang_kz', + title: this.$p.t('lehre/studiengangskennzahlLehre'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang_kz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang_kz || this.optionalTabulatorOptions?.studiengang_kz || false + + }, + { + field: 'studiengang_lv', + title: this.$p.t('stv/grades_studiengang_lv'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang_lv || this.optionalTabulatorOptions?.studiengang_lv || false + + }, + { + field: 'studiengang_kz_lv', + title: this.$p.t('stv/grades_studiengang_kz_lv'), + visible: this.optionalTabulatorOptions?.visibleColumns?.studiengang_kz_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.studiengang_kz_lv || this.optionalTabulatorOptions?.studiengang_kz_lv || false + + }, + { + field: 'semester_lv', + title: this.$p.t('stv/grades_semester_lv'), + visible: this.optionalTabulatorOptions?.visibleColumns?.semester_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.semester_lv || this.optionalTabulatorOptions?.semester_lv || false + + }, + { + field: 'ects_lv', + title: this.$p.t('lehre/ects'), + visible: this.optionalTabulatorOptions?.visibleColumns?.ects_lv ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.ects_lv || this.optionalTabulatorOptions?.ects_lv || false + + }, + { + field: 'lehrform', + title: this.$p.t('lehre/lehrform'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrform ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrform || this.optionalTabulatorOptions?.lehrform || false + + }, + { + field: 'kurzbz', + title: this.$p.t('lehre/kurzbz'), + visible: this.optionalTabulatorOptions?.visibleColumns?.kurzbz ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.kurzbz || this.optionalTabulatorOptions?.kurzbz || false + + }, + { + field: 'punkte', + title: this.$p.t('stv/grades_points'), + visible: this.optionalTabulatorOptions?.visibleColumns?.punkte ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.punkte || this.optionalTabulatorOptions?.punkte || false + + }, + { + field: 'vorname', + title: this.$p.t('person/vorname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.vorname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.vorname || this.optionalTabulatorOptions?.vorname || false + + }, + { + field: 'nachname', + title: this.$p.t('person/nachname'), + visible: this.optionalTabulatorOptions?.visibleColumns?.nachname ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.nachname || this.optionalTabulatorOptions?.nachname || false + + }, + { + field: 'lehrveranstaltung_bezeichnung_english', + title: this.$p.t('stv/grades_lehrveranstaltung_bezeichnung_english'), + visible: this.optionalTabulatorOptions?.visibleColumns?.lehrveranstaltung_bezeichnung_english ?? false, + headerFilter: this.optionalTabulatorOptions?.headerFilter?.lehrveranstaltung_bezeichnung_english || this.optionalTabulatorOptions?.lehrveranstaltung_bezeichnung_english || false + + } ]; const hasDocuments = ['both', 'inline'].includes(this.config.documents); @@ -208,8 +338,8 @@ export default { return { ajaxURL: 'dummy', - ajaxRequestFunc: () => this.$api.call(ApiStvGrades.getCertificate( - this.student.prestudent_id, + ajaxRequestFunc: () => this.$api.call(this.endpoint.getCertificate( + this.id, (!this.allSemester ? this.currentSemester : null) )), ajaxResponse: (url, params, response) => { @@ -218,9 +348,11 @@ export default { columns, height: '100%', layout: 'fitDataStretchFrozen', - selectable: 1, + selectable: true, selectableRangeMode: 'click', - persistenceID: 'stv-details-noten-zeugnis-2025112401', + selectableRowsRangeMode: 'click', //needed for Tabulator v6 + selectableRows: true, //needed for Tabulator v6 + persistenceID: this.optionalTabulatorOptions?.persistenceZeugnisID ?? 'stv-details-noten-zeugnis-2025120302', persistence:{ columns: ["width", "visible", "frozen"] } @@ -228,7 +360,8 @@ export default { } }, watch: { - student(n) { + id() + { this.$refs.table.reloadTable(); }, allSemester(n) { @@ -239,7 +372,7 @@ export default { setGrade(data) { this.$api .call( - ApiStvGrades.updateCertificate(data), + this.endpoint.updateCertificate(data), { errorHeader: data.lehrveranstaltung_bezeichnung } ) .then(this.$refs.table.reloadTable) @@ -252,7 +385,7 @@ export default { .confirmDelete() .then(result => result ? data : Promise.reject({handled:true})) .then(data => this.$api.call( - ApiStvGrades.deleteCertificate(data), + this.endpoint.deleteCertificate(data), { errorHeader: data.lehrveranstaltung_bezeichnung } )) .then(this.$refs.table.reloadTable) @@ -261,7 +394,7 @@ export default { } }, created() { - this.$p.loadCategory(['global', 'stv', 'lehre']) + this.$p.loadCategory(['global', 'stv', 'lehre', 'person']) .then(() => { if (this.$refs.table.tableBuilt) this.$refs.table.tabulator.columnManager.setColumns(this.tabulatorOptions.columns); diff --git a/public/js/helpers/DocumentHelper.js b/public/js/helpers/DocumentHelper.js new file mode 100644 index 000000000..c37c632d5 --- /dev/null +++ b/public/js/helpers/DocumentHelper.js @@ -0,0 +1,44 @@ +export function highlightGesamtnote(zeugnisTable, teacherTable) +{ + let zeugnisData = zeugnisTable.getData(); + + let studentsWithNote = new Set(); + + zeugnisData.forEach(row => { + let student = row.uid; + let lv = row.lehrveranstaltung_id; + let note = row.note; + + if (!student || !lv) + return; + + if (note == null || note === "") + return; + + let key = `${student}_${lv}`; + studentsWithNote.add(key); + }); + + teacherTable.deselectRow(); + + teacherTable.getRows().forEach(row => { + let data = row.getData(); + + let student = data.student_uid; + let lv = data.lehrveranstaltung_id; + let note = data.note; + + if (!student || !lv) + return; + + if (note == null || note === "") + return; + + let key = `${student}_${lv}`; + + if (!studentsWithNote.has(key)) + { + row.select(); + } + }); +} \ No newline at end of file