diff --git a/application/controllers/api/frontend/v1/Lehrveranstaltung.php b/application/controllers/api/frontend/v1/Lehrveranstaltung.php index 098d44712..2e254bfc8 100644 --- a/application/controllers/api/frontend/v1/Lehrveranstaltung.php +++ b/application/controllers/api/frontend/v1/Lehrveranstaltung.php @@ -27,8 +27,8 @@ class Lehrveranstaltung extends FHCAPI_Controller public function __construct() { parent::__construct([ - 'loadByEmployee' => ['admin:r', 'assistenz:r'], - 'loadByStudiengang' => ['admin:r', 'assistenz:r'], + 'getByEmp' => ['admin:r', 'assistenz:r'], + 'getByStg' => ['admin:r', 'assistenz:r'], 'loadByLV' => ['admin:r', 'assistenz:r'], ]); @@ -37,6 +37,7 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->_ci->load->model('education/Lehreinheit_model', 'LehreinheitModel'); $this->_ci->load->model('education/Lehrveranstaltung_model', 'LehrveranstaltungModel'); + $this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel'); $this->_ci->load->library('VariableLib', ['uid' => $this->_uid]); @@ -46,12 +47,14 @@ class Lehrveranstaltung extends FHCAPI_Controller ) ); } - public function loadByEmployee($mitarbeiter_uid = null, $stg_kz = null) - { - if (is_null($mitarbeiter_uid)) - $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); - $studiensemester_kurzbz = $this->_ci->variablelib->getVar('semester_aktuell'); + public function getByEmp($studiensemester_kurzbz = null, $mitarbeiter_uid = null, $stg_kz = null) + { + + if (is_null($mitarbeiter_uid)) + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); $lehrveranstaltungen = $this->_ci->LehreinheitModel->getLvsByEmployee($mitarbeiter_uid, $studiensemester_kurzbz, $stg_kz); $lehrveranstaltungen_data = $this->getDataOrTerminateWithError($lehrveranstaltungen); @@ -72,8 +75,7 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->terminateWithSuccess($tree); } - - public function loadByStudiengang($studiengang_kz = null, $semester = null) + public function getByStg($studiensemester_kurzbz = null, $studiengang_kz = null, $semester = null) { if (is_null($studiengang_kz) || !preg_match("/^-?[1-9][0-9]*$/", (string)$studiengang_kz)) $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); @@ -87,7 +89,7 @@ class Lehrveranstaltung extends FHCAPI_Controller $this->_ci->load->model('organisation/Studienplan_model', 'StudienplanModel'); - $studiensemester_kurzbz = $this->_ci->variablelib->getVar('semester_aktuell'); + $studiensemester_kurzbz = $this->getStudiensemesterKurzbz($studiensemester_kurzbz); $studienplan_data = $this->_ci->StudienplanModel->getStudienplaeneBySemester($studiengang_kz, $studiensemester_kurzbz, $semester, $verband); $studienplan_ids = array(); @@ -107,7 +109,6 @@ class Lehrveranstaltung extends FHCAPI_Controller $lehrveranstaltungen_data = $this->_ci->LehrveranstaltungModel->getLvsByStudiengang($studienplan_ids, $placeholders, $only_ids, $studiengang_kz, $studiensemester_kurzbz, $semester, $verband); $lehrveranstaltungen_data = hasData($lehrveranstaltungen_data) ? getData($lehrveranstaltungen_data) : array(); - $tree = []; foreach ($lehrveranstaltungen_data as $row) { @@ -246,4 +247,19 @@ class Lehrveranstaltung extends FHCAPI_Controller } } } + + private function getStudiensemesterKurzbz($studiensemester_kurzbz = null) + { + if (!is_null($studiensemester_kurzbz)) + { + $studiensemester_result = $this->_ci->StudiensemesterModel->load($studiensemester_kurzbz); + + if (isError($studiensemester_result) || !hasData($studiensemester_result)) + $this->terminateWithError( $this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + + return getData($studiensemester_result)[0]->studiensemester_kurzbz; + } + + $this->terminateWithError($this->p->t('ui', 'ungueltigeParameter'), self::ERROR_TYPE_GENERAL); + } } diff --git a/application/models/education/Lehreinheit_model.php b/application/models/education/Lehreinheit_model.php index ce414bad2..c33b87ad4 100644 --- a/application/models/education/Lehreinheit_model.php +++ b/application/models/education/Lehreinheit_model.php @@ -356,6 +356,8 @@ EOSQL; gruppen.gruppen, mitarbeiter.lektoren, mitarbeiter.le_planstunden, + mitarbeiter.vorname, + mitarbeiter.nachname, mitarbeiter.semesterstunden, fachbereich.bezeichnung as fachbereich, UPPER(CONCAT(tbl_studiengang.typ,tbl_studiengang.kurzbz)) as studiengang, @@ -659,11 +661,15 @@ EOSQL; SELECT tbl_lehreinheitmitarbeiter.lehreinheit_id, STRING_AGG(m.kurzbz, ' ') AS lektoren, + STRING_AGG(tbl_person.vorname, ' ') AS vorname, + STRING_AGG(tbl_person.nachname, ' ') AS nachname, STRING_AGG(tbl_lehreinheitmitarbeiter.semesterstunden::text, ' ') AS semesterstunden, STRING_AGG(tbl_lehreinheitmitarbeiter.planstunden::text, ' ') AS le_planstunden FROM lehre.tbl_lehreinheitmitarbeiter JOIN public.tbl_mitarbeiter m USING (mitarbeiter_uid) JOIN lehreinheiten USING(lehreinheit_id) + JOIN public.tbl_benutzer ON mitarbeiter_uid = uid + JOIN public.tbl_person ON tbl_benutzer.person_id = tbl_person.person_id GROUP BY tbl_lehreinheitmitarbeiter.lehreinheit_id )"; } diff --git a/application/models/education/Lehrveranstaltung_model.php b/application/models/education/Lehrveranstaltung_model.php index 121de9233..303bebe1e 100644 --- a/application/models/education/Lehrveranstaltung_model.php +++ b/application/models/education/Lehrveranstaltung_model.php @@ -1200,19 +1200,29 @@ class Lehrveranstaltung_model extends DB_Model public function getAllOe($lv_id, $stg_kz = null) { - $qry = "SELECT DISTINCT oe_kurzbz + $qry = "(SELECT DISTINCT oe_kurzbz FROM lehre.tbl_studienplan_lehrveranstaltung JOIN lehre.tbl_studienplan USING(studienplan_id) JOIN lehre.tbl_studienordnung USING(studienordnung_id) JOIN public.tbl_studiengang USING(studiengang_kz) - WHERE lehrveranstaltung_id = ? "; + WHERE lehrveranstaltung_id = ?)"; $params = array($lv_id); if (!is_null($stg_kz)) { - $qry .= ' OR studiengang_kz = ?'; + $qry .= ' UNION + (SELECT oe_kurzbz + FROM public.tbl_studiengang WHERE studiengang_kz = ?)'; $params[] = $stg_kz; } + else + { + $qry .= ' UNION + (SELECT oe_kurzbz + FROM public.tbl_studiengang WHERE studiengang_kz = (SELECT tbl_lehrveranstaltung.studiengang_kz FROM lehre.tbl_lehrveranstaltung WHERE lehrveranstaltung_id = ?)) + '; + $params[] = $lv_id; + } return $this->execReadOnlyQuery($qry, $params); } diff --git a/public/js/api/lehrveranstaltung.js b/public/js/api/lehrveranstaltung.js index 0499572ba..8aa6becaf 100644 --- a/public/js/api/lehrveranstaltung.js +++ b/public/js/api/lehrveranstaltung.js @@ -7,16 +7,24 @@ export default { url: '/api/frontend/v1/Lehrveranstaltung/loadByLV/' + encodeURIComponent(lehrveranstaltung_id) }; }, - getByStg(studiengang_kz, semester) + getByStg(studiensemester_kurzbz, studiengang_kz, semester = null) { - return ("/api/frontend/v1/Lehrveranstaltung/loadByStudiengang/" + encodeURIComponent(studiengang_kz) + "/" + encodeURIComponent(semester)); - }, + let path = "/api/frontend/v1/Lehrveranstaltung/getByStg/" + encodeURIComponent(studiensemester_kurzbz) + "/" + encodeURIComponent(studiengang_kz); - getByEmpStg(mitarbeiter_uid, stg) + if (semester) + path += "/" + encodeURIComponent(semester); + + return path; + }, + getByEmp(studiensemester_kurzbz, mitarbeiter_uid, stg = null) { - return ("/api/frontend/v1/Lehrveranstaltung/loadByEmployee/" + encodeURIComponent(mitarbeiter_uid) + "/" + encodeURIComponent(stg)); - }, + let path = "/api/frontend/v1/Lehrveranstaltung/getByEmp/" + encodeURIComponent(studiensemester_kurzbz) + "/" + encodeURIComponent(mitarbeiter_uid); + if (stg) + path += "/" + encodeURIComponent(stg); + + return path; + }, getTable(url) { return { diff --git a/public/js/apps/LVVerwaltung.js b/public/js/apps/LVVerwaltung.js index c582f817e..2ab3fd98c 100644 --- a/public/js/apps/LVVerwaltung.js +++ b/public/js/apps/LVVerwaltung.js @@ -1,5 +1,6 @@ import LVVerwaltung from "../components/LVVerwaltung/LVVerwaltung.js"; import Phrasen from "../plugins/Phrasen.js"; +import {DEFAULT_MODE_RAUMINFO} from "../components/Cis/Mylv/RoomInformation"; const ciPath = FHC_JS_DATA_STORAGE_OBJECT.app_root.replace(/(https:|)(^|\/\/)(.*?\/)/g, '') + FHC_JS_DATA_STORAGE_OBJECT.ci_router; @@ -13,8 +14,35 @@ const router = VueRouter.createRouter({ }, { name: `byEmp`, - path: `/emp/:emp/:stg?`, - component: LVVerwaltung + path: `/emp/:studiensemester_kurzbz/:emp/:stg?/:semester?`, + component: LVVerwaltung, + props: route => { + let {emp, stg, semester, studiensemester_kurzbz} = route.params; + + if (emp === '') + emp = undefined; + + if (stg === '') + stg = undefined; + + if (studiensemester_kurzbz === '') + studiensemester_kurzbz = undefined; + + return { + studiensemester_kurzbz: studiensemester_kurzbz, + emp: emp, + stg: stg, + }; + }, + beforeEnter: (to, from, next) => { + const { studiensemester_kurzbz } = to.params; + const isSemester = /^(SS|WS)\d{4}$/.test(studiensemester_kurzbz); + + if (!isSemester) + return next({ path: '/' }); + else + next(); + } }, /*{ name: `byFachbereich`, @@ -23,13 +51,41 @@ const router = VueRouter.createRouter({ },*/ { name: `byStg`, - path: `/stg/:stg/:semester?`, - component: LVVerwaltung + path: '/stg/:studiensemester_kurzbz/:stg?/:semester?/', + component: LVVerwaltung, + props: route => { + let { studiensemester_kurzbz, stg, semester } = route.params; + + if (semester === '') + semester = undefined; + + if (studiensemester_kurzbz === '') + studiensemester_kurzbz = undefined; + + if (stg === '') + semester = undefined; + + return { + studiensemester_kurzbz: studiensemester_kurzbz, + stg: stg, + semester: semester != null ? Number(semester) : null, + }; + }, + beforeEnter: (to, from, next) => { + const studiensemester_kurzbz = to.params?.studiensemester_kurzbz + const isSemester = /^(SS|WS)\d{4}$/.test(studiensemester_kurzbz); + + if (!isSemester) + return next({ path: '/' }); + else + next(); + } }, { path: '/:pathMatch(.*)*', redirect: '/' - } + }, + ] }); diff --git a/public/js/components/LVVerwaltung/LVVerwaltung.js b/public/js/components/LVVerwaltung/LVVerwaltung.js index c7c34802d..7b8921c80 100644 --- a/public/js/components/LVVerwaltung/LVVerwaltung.js +++ b/public/js/components/LVVerwaltung/LVVerwaltung.js @@ -26,16 +26,22 @@ export default { defaultSemester: String, lvRoot: String, permissions: Object, - config: Object + config: Object, + stg: { type: String, required: false }, + semester: { type: Number, required: false, default: null }, + studiensemester_kurzbz: { type: String, required: false, default: null }, + emp: { type: String, required: false, default: null } }, computed: { - type(){ - return this.$route.params.type + selectedStudiensemester() { + return this.studiensemester_kurzbz != null + ? this.studiensemester_kurzbz + : this.defaultSemester; } }, provide() { return { - currentSemester: Vue.computed(() => this.studiensemesterKurzbz), + currentSemester: Vue.computed(() => this.selectedStudiensemester), dropdowns: this.dropdowns, configShowVertragsdetails: this.config.showVertragsdetails, configShowGewichtung: this.config.showGewichtung, @@ -49,18 +55,26 @@ export default { } }, mounted() { - this.updateFilter(this.$route); + this.updateFilter(); }, watch: { - '$route'(to) { - this.updateFilter(to); + stg() { + this.updateFilter(); + }, + semester() { + this.updateFilter(); + }, + selectedStudiensemester() { + this.updateFilter(); + }, + emp() { + this.updateFilter(); }, }, data() { return { selected: [], - studiensemesterKurzbz: this.defaultSemester, - stg: "", + studiengang: "", filter: {}, endpoint: ApiStudiengangTree, dropdowns: { @@ -95,20 +109,16 @@ export default { } }, methods: { - updateFilter(route) + updateFilter() { - let filter = { ...route.params, ...route.query }; - if (!filter.activeFilter) - { - if (filter.emp) - { - filter.activeFilter = 'employee'; - } - else if (filter.stg) - { - filter.activeFilter = 'verband'; - } + const filter = { + stg: this.stg, + emp: this.emp, + semester: this.semester, + studiensemester_kurzbz: this.selectedStudiensemester, + activeFilter: this.emp ? 'employee' : this.stg ? 'verband' : null } + this.filter = filter; }, handleRowClicked(data) @@ -117,18 +127,26 @@ export default { }, onSelectEmployee(emp) { - let stg = this.stg === '' ? null : this.stg; + const { stg, semester } = this.filter; - this.$router.push({ - name: 'byEmp', - params: { emp, stg, activeFilter: 'employee'}, - }); + let studiensemester_kurzbz = this.selectedStudiensemester; + const params = { emp }; + + if (stg) + params.stg = stg; + if (semester !== null) + params.semester = semester; + if (studiensemester_kurzbz) + params.studiensemester_kurzbz = studiensemester_kurzbz; + + this.$router.push({ name: 'byEmp', params }) }, onSelectVerband({link}) { let stg = null; let semester = null; + let studiensemester_kurzbz = this.selectedStudiensemester; if (typeof link === 'number') stg = link; @@ -137,28 +155,22 @@ export default { [stg, semester] = link.split('/'); } - this.stg = stg; + const routeName = this.filter.emp ? 'byEmp' : 'byStg'; + const params = { stg }; - if (this.filter && this.filter.emp) - { - this.$router.push({ - name: 'byEmp', - params: { emp: this.filter.emp, stg, activeFilter: 'employee' }, - }); - } - else - { - this.$router.push({ - name: 'byStg', - params: { stg, semester, activeFilter: 'verband' }, - }); - } + if (semester !== null) + params.semester = Number(semester); + if (studiensemester_kurzbz) + params.studiensemester_kurzbz = studiensemester_kurzbz; + if (this.filter.emp) + params.emp = this.filter.emp; + this.$router.push({ name: routeName, params }); this.selected = []; }, resetEmployeeFilter() { - const newParams = { ...this.$route.params, activeFilter: 'verband' }; + const newParams = { ...this.filter, activeFilter: 'verband' }; if (newParams.stg === '') this.$router.replace({ name: 'index' }); else @@ -166,24 +178,24 @@ export default { delete newParams.emp; newParams.semester = null; this.$router.replace({ name: 'byStg', params: newParams }); - } }, searchfunction(params) { return this.$api.call(ApiSearchbar.search(params)); }, studiensemesterChanged(newValue) { - this.studiensemesterKurzbz = newValue; - this.$refs.lvTable.reload(); + const routeName = this.filter.activeFilter === 'employee' ? 'byEmp' : 'byStg'; + const newParams = {...this.filter, studiensemester_kurzbz: newValue}; + this.$router.push({ name: routeName, params: newParams }); this.selected = []; }, }, created() { - if (this.$route.params.stg !== undefined) + if (this.stg !== undefined) { - this.selectedStudiengang = this.$route.params?.semester !== '' && this.$route.params?.semester - ? `${this.$route.params.stg}/${this.$route.params.semester}` - : this.$route.params.stg; + this.selectedStudiengang = this.semester !== '' && this.semester + ? `${this.stg}/${this.semester}` + : this.stg; } this.$p.loadCategory(['lehre', 'person', 'global']) @@ -250,7 +262,7 @@ export default { - +
diff --git a/public/js/components/LVVerwaltung/Setup/Table.js b/public/js/components/LVVerwaltung/Setup/Table.js index a1b2830f1..5b4d45c24 100644 --- a/public/js/components/LVVerwaltung/Setup/Table.js +++ b/public/js/components/LVVerwaltung/Setup/Table.js @@ -61,42 +61,6 @@ export default { }, data() { return { - fieldTitleMap: { - lv_kurzbz: ['lehre', 'kurzbz'], - tags: ['ui', 'tags'], - lehrveranstaltung_id: ['lehre', 'lehrveranstaltung_id'], - lv_bezeichnung: ['ui', 'bezeichnung'], - lv_bezeichnung_english: ['lehre', 'bezeichnungeng'], - lv_studiengang_kz: ['lehre', 'studiengangskennzahlLehre'], - studiengang: ['lehre', 'studiengang'], - semester: ['lehre', 'semester'], - sprache: ['global', 'sprache'], - lv_ects: ['lehre', 'ects'], - semesterstunden: ['lehre', 'semesterstunden'], - anmerkung: ['global', 'anmerkung'], - lehre: ['lehre', 'lehre'], - lehreverzeichnis: ['ui', 'lehreverzeichnis'], - aktiv: ['person', 'aktiv'], - planfaktor: ['ui', 'planfaktor'], - planlektoren: ['ui', 'planlektoren'], - planpersonalkosten: ['ui', 'planpersonalkosten'], - plankostenprolektor: ['ui', 'plankostenprolektor'], - orgform_kurzbz: ['lehre', 'organisationsform'], - studienplan_id: ['ui', 'studienplan_id'], - studienplan_bezeichnung: ['ui', 'studienplan_bezeichnung'], - lehrtyp_kurzbz: ['ui', 'lehrtyp_kurzbz'], - lehrform_kurzbz: ['lehre', 'lehrform'], - le_planstunden: ['lehre', 'leplanstunden'], - lehreinheit_id: ['lehre', 'lehreinheit_id'], - stundenblockung: ['lehre', 'stundenblockung'], - wochenrythmus: ['lehre', 'wochenrhytmus'], - startkw: ['lehre', 'startkw'], - raumtyp: ['lehre', 'raumtyp'], - raumtypalternativ: ['lehre', 'raumtypalternativ'], - gruppen: ['lehre', 'gruppen'], - lektoren: ['lehre', 'lehrende'], - }, - expanded: [], selectedColumnValues: [], tagEndpoint: ApiTag, @@ -168,7 +132,7 @@ export default { headerFilterFunc: extendedHeaderFilter, }, layout: 'fitDataStretch', - persistenceID: 'lehrveranstaltungen_2025_07_11_v1', + persistenceID: 'lehrveranstaltungen_2025_07_31_v1', selectableRowsRangeMode: 'click', selectableRows: true, rowContextMenu: (component, e) => { @@ -348,7 +312,7 @@ export default { {title: this.$p.t('lehre', 'raumtyp'), field: "raumtyp", headerFilter: true, headerFilterFuncParams: {field: 'raumtyp'}, visible: false}, {title: this.$p.t('lehre', 'raumtypalternativ'), field: "raumtypalternativ", headerFilter: true, headerFilterFuncParams: {field: 'raumtypalternativ'}, visible: false}, {title: this.$p.t('lehre', 'gruppen'), field: "gruppen", headerFilter: true, headerFilterFuncParams: {field: 'gruppen'}}, - {title: this.$p.t('lehre', 'lehrende'), field: "lektoren", headerFilter: true, headerFilterFuncParams: {field: 'lektoren'}}, + {title: this.$p.t('lehre', 'lehrende'), field: "lektoren", headerFilter: true, headerFilterFuncParams: {field: ['lektoren', 'vorname', 'nachname']}}, ], } @@ -417,18 +381,14 @@ export default { { if (this.filter.activeFilter === 'employee' && this.filter.emp) { - return this.$api.getUri(ApiLv.getByEmpStg( - this.filter.emp, - this.filter.stg - )); + const { emp, stg, studiensemester_kurzbz } = this.filter; + return this.$api.getUri(ApiLv.getByEmp(studiensemester_kurzbz, emp, stg)); } if (this.filter.activeFilter === 'verband' && this.filter.stg) { - return this.$api.getUri(ApiLv.getByStg( - this.filter.stg, - this.filter.semester - )); + const { stg, semester, studiensemester_kurzbz } = this.filter; + return this.$api.getUri(ApiLv.getByStg(studiensemester_kurzbz, stg, semester)); } }, resetEmployeeFilter() @@ -437,16 +397,6 @@ export default { delete newFilter.emp; newFilter.activeFilter = 'verband'; }, - buildParams() - { - const params = {}; - for (const [key, value] of Object.entries(this.filter)) { - if (value !== undefined && value !== null) { - params[key] = value; - } - } - return params; - }, showLehreinheitModal() { this.resetModal(); this.$refs.lehreinheitModal.show(); @@ -649,7 +599,7 @@ export default { let rootRows = this.$refs.table.tabulator.getRows(true); var lastRow = rootRows[rootRows.length - 1]; - lastRow.treeCollapse(true) + lastRow?.treeCollapse(true) this.currentTreeLevel = 0; }, diff --git a/public/js/tabulator/filters/extendedHeaderFilter.js b/public/js/tabulator/filters/extendedHeaderFilter.js index 5dfcce205..1112d36f3 100644 --- a/public/js/tabulator/filters/extendedHeaderFilter.js +++ b/public/js/tabulator/filters/extendedHeaderFilter.js @@ -41,6 +41,17 @@ function parseFilterExpression(expression) export function extendedHeaderFilter(headerValue, rowValue, rowData, filterParams) { + const fields = Array.isArray(filterParams?.field) + ? filterParams.field + : [filterParams?.field]; + + if (fields.length > 1 && rowData) + { + rowValue = fields + .map(f => rowData[f] ?? '') + .filter(Boolean) + .join(' '); + } if (typeof headerValue === 'boolean') { return rowValue === headerValue;