diff --git a/application/controllers/api/frontend/v1/funktionen/Funktionen.php b/application/controllers/api/frontend/v1/funktionen/Funktionen.php
new file mode 100644
index 000000000..b2787072b
--- /dev/null
+++ b/application/controllers/api/frontend/v1/funktionen/Funktionen.php
@@ -0,0 +1,328 @@
+ ['admin:r', 'assistenz:r'],
+ 'getAllUserFunctions' => ['admin:r', 'assistenz:r'],
+ 'getOrgHeads' => ['admin:r', 'assistenz:r'],
+ 'getOrgetsForCompany' => ['admin:r', 'assistenz:r'],
+ 'getAllOrgUnits' => ['admin:r', 'assistenz:r'],
+ 'loadFunction' => ['admin:r', 'assistenz:r'],
+ 'insertFunction' => ['admin:rw', 'assistenz:rw'],
+ 'updateFunction' => ['admin:rw', 'assistenz:rw'],
+ 'deleteFunction' => ['admin:rw', 'assistenz:rw'],
+ )
+ );
+
+ // Load Libraries
+ $this->load->library('VariableLib', ['uid' => getAuthUID()]);
+ $this->load->library('form_validation');
+
+ // Load language phrases
+ $this->loadPhrases([
+ 'ui',
+ ]);
+
+ // Load models
+ $this->load->model('extensions/FHC-Core-Personalverwaltung/Api_model', 'ApiModel');
+ $this->load->model('ressource/Funktion_model', 'FunktionModel');
+ $this->load->model('person/Benutzerfunktion_model', 'BenutzerfunktionModel');
+
+ $this->load->model('organisation/Organisationseinheit_model', 'OrganisationseinheitModel');
+ }
+
+ public function getAllFunctions()
+ {
+ $this->FunktionModel->addSelect("funktion_kurzbz");
+ $this->FunktionModel->addSelect("beschreibung");
+ $this->FunktionModel->addSelect("aktiv");
+ $this->FunktionModel->addSelect("beschreibung AS label");
+ $this->FunktionModel->addOrder("beschreibung");
+ $result = $this->FunktionModel->load();
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getOrgHeads()
+ {
+ $result = $this->OrganisationseinheitModel->getHeads();
+
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function getAllUserFunctions($uid)
+ {
+ if(!$uid)
+ {
+ $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'UID']), self::ERROR_TYPE_GENERAL);
+ }
+
+ $sql = "
+ SELECT
+ dv.dienstverhaeltnis_id,
+ un.bezeichnung || ' (' || TO_CHAR(dv.von, 'DD.MM.YYYY') || CASE WHEN dv.bis IS NOT NULL THEN ' - '
+ || TO_CHAR(dv.bis, 'DD.MM.YYYY') ELSE '' END || ')' AS dienstverhaeltnis_unternehmen ,
+ '[' || oet.bezeichnung || '] ' || oe.bezeichnung AS funktion_oebezeichnung,
+ f.beschreibung AS funktion_beschreibung,
+ bf.*,
+ fb.bezeichnung AS fachbereich_bezeichnung,
+ CASE
+ WHEN
+ bf.datum_bis IS NOT NULL AND bf.datum_bis::date < now()::date
+ THEN
+ false
+ ELSE
+ true
+ END aktiv
+ FROM
+ public.tbl_benutzerfunktion bf
+ JOIN
+ public.tbl_organisationseinheit oe ON oe.oe_kurzbz = bf.oe_kurzbz
+ JOIN
+ public.tbl_organisationseinheittyp oet ON oe.organisationseinheittyp_kurzbz = oet.organisationseinheittyp_kurzbz
+ JOIN
+ public.tbl_funktion f ON f.funktion_kurzbz = bf.funktion_kurzbz
+ LEFT JOIN
+ hr.tbl_vertragsbestandteil_funktion vf ON vf.benutzerfunktion_id = bf.benutzerfunktion_id
+ LEFT JOIN
+ hr.tbl_vertragsbestandteil v ON vf.vertragsbestandteil_id = v.vertragsbestandteil_id
+ LEFT JOIN
+ hr.tbl_dienstverhaeltnis dv ON v.dienstverhaeltnis_id = dv.dienstverhaeltnis_id
+ LEFT JOIN
+ public.tbl_organisationseinheit un ON dv.oe_kurzbz = un.oe_kurzbz
+ LEFT JOIN
+ public.tbl_fachbereich fb ON fb.fachbereich_kurzbz = bf.fachbereich_kurzbz
+ WHERE
+ bf.uid = ?
+ ORDER BY
+ bf.datum_von, bf.datum_von ASC";
+
+ $benutzerfunktionen = $this->BenutzerfunktionModel->execReadOnlyQuery($sql, array($uid));
+ $data = $this->getDataOrTerminateWithError($benutzerfunktionen);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ /*
+ * returns list of all organisation units
+ * as key value list to be used in select or autocomplete
+ */
+ public function getAllOrgUnits()
+ {
+ $sql = "
+ SELECT
+ oe.oe_kurzbz, oe.aktiv,
+ '[' || COALESCE(oet.bezeichnung, oet.organisationseinheittyp_kurzbz) ||
+ '] ' || COALESCE(oe.bezeichnung, oe.oe_kurzbz) AS label
+ FROM public.tbl_organisationseinheit oe
+ JOIN public.tbl_organisationseinheittyp oet ON oe.organisationseinheittyp_kurzbz = oet.organisationseinheittyp_kurzbz
+ ORDER BY oet.bezeichnung ASC, oe.bezeichnung ASC";
+
+ $result = $this->OrganisationseinheitModel->execReadOnlyQuery($sql);
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ /*
+ * return list of child orgets for a given company orget_kurzbz
+ * as key value list to be used in select or autocomplete
+ */
+ public function getOrgetsForCompany($companyOrgetkurzbz = null)
+ {
+ $sql = "
+ SELECT
+ oe.oe_kurzbz, oe.aktiv,
+ '[' || COALESCE(oet.bezeichnung, oet.organisationseinheittyp_kurzbz) ||
+ '] ' || COALESCE(oe.bezeichnung, oe.oe_kurzbz) AS label
+ FROM (
+ WITH RECURSIVE oes(oe_kurzbz, oe_parent_kurzbz) as
+ (
+ SELECT oe_kurzbz, oe_parent_kurzbz FROM public.tbl_organisationseinheit
+ WHERE oe_kurzbz=?
+ UNION ALL
+ SELECT o.oe_kurzbz, o.oe_parent_kurzbz FROM public.tbl_organisationseinheit o, oes
+ WHERE o.oe_parent_kurzbz=oes.oe_kurzbz
+ )
+ SELECT oe_kurzbz
+ FROM oes
+ GROUP BY oe_kurzbz
+ ) c
+ JOIN public.tbl_organisationseinheit oe ON oe.oe_kurzbz = c.oe_kurzbz
+ JOIN public.tbl_organisationseinheittyp oet ON oe.organisationseinheittyp_kurzbz = oet.organisationseinheittyp_kurzbz
+ ORDER BY oet.bezeichnung ASC, oe.bezeichnung ASC";
+
+ $childorgets = $this->OrganisationseinheitModel->execReadOnlyQuery($sql, array($companyOrgetkurzbz));
+ $data = $this->getDataOrTerminateWithError($childorgets);
+
+ $this->terminateWithSuccess($data);
+ }
+
+ public function loadFunction($benutzerfunktion_id)
+ {
+ $this->BenutzerfunktionModel->addSelect("*");
+ $result = $this->BenutzerfunktionModel->loadWhere(
+ array('benutzerfunktion_id' => $benutzerfunktion_id)
+ );
+ $data = $this->getDataOrTerminateWithError($result);
+
+ $this->terminateWithSuccess(current($data));
+ }
+
+ public function insertFunction()
+ {
+ $this->load->library('form_validation');
+ $authUID = getAuthUID();
+
+ $uid = $this->input->post('uid');
+
+ if(!$uid)
+ {
+ return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'UID']), self::ERROR_TYPE_GENERAL);
+ }
+
+ $formData = $this->input->post('formData');
+
+ $datum_von = $formData['datum_von'] ?? null;
+ $datum_bis = $formData['datum_bis'] ?? null;
+ $formData['oe_kurzbz'] = is_array($formData['oe_kurzbz']) ? $formData['oe_kurzbz']['oe_kurzbz'] : $formData['oe_kurzbz'];
+ $formData['funktion_kurzbz'] = is_array($formData['funktion_kurzbz'])
+ ? $formData['funktion_kurzbz']['funktion_kurzbz']
+ : $formData['funktion_kurzbz'];
+ $bezeichnung = $formData['bezeichnung'] ?? null;
+ $wochenstunden = $formData['wochenstunden'] ?? null;
+
+ $this->form_validation->set_data($formData);
+ $this->form_validation->set_rules('datum_von', 'VonDatum', 'required|is_valid_date', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'VonDatum']),
+ 'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'VonDatum'])
+ ]);
+ $this->form_validation->set_rules('datum_bis', 'BisDatum', 'is_valid_date', [
+ 'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'BisDatum'])
+ ]);
+ $this->form_validation->set_rules('oe_kurzbz', 'Organisationseinheit', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Organisationseinheit'])
+ ]);
+ $this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Funktion'])
+ ]);
+ $this->form_validation->set_rules('wochenstunden', 'Wochenstunden', 'numeric', [
+ 'numeric' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Wochenstunden'])
+ ]);
+
+ if ($this->form_validation->run() == false)
+ {
+ $this->terminateWithValidationErrors($this->form_validation->error_array());
+ }
+
+ $result = $this->BenutzerfunktionModel->insert([
+ 'uid' => $uid,
+ 'datum_von' => $datum_von,
+ 'datum_bis' => $datum_bis ,
+ 'oe_kurzbz' => $formData['oe_kurzbz'],
+ 'funktion_kurzbz' => $formData['funktion_kurzbz'],
+ 'bezeichnung' => $bezeichnung,
+ 'wochenstunden' => $wochenstunden,
+ 'insertamum' => date('c'),
+ 'insertvon' => $authUID,
+ ]);
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $this->terminateWithSuccess($data);
+ }
+
+ public function updateFunction()
+ {
+ $this->load->library('form_validation');
+ $authUID = getAuthUID();
+
+ $uid = $this->input->post('uid');
+
+ if(!$uid)
+ {
+ return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'UID']), self::ERROR_TYPE_GENERAL);
+ }
+ $benutzerfunktion_id = $this->input->post('benutzerfunktion_id');
+
+ if(!$benutzerfunktion_id)
+ {
+ return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Benutzerfunktion ID']), self::ERROR_TYPE_GENERAL);
+ }
+
+ $formData = $this->input->post('formData');
+
+ $datum_von = $formData['datum_von'] ?? null;
+ $datum_bis = $formData['datum_bis'] ?? null;
+ $formData['oe_kurzbz'] = is_array($formData['oe_kurzbz']) ? $formData['oe_kurzbz']['oe_kurzbz'] : $formData['oe_kurzbz'];
+ $formData['funktion_kurzbz'] = is_array($formData['funktion_kurzbz'])
+ ? $formData['funktion_kurzbz']['funktion_kurzbz']
+ : $formData['funktion_kurzbz'];
+ $bezeichnung = $formData['bezeichnung'] ?? null;
+ $wochenstunden = $formData['wochenstunden'] ?? null;
+
+ $this->form_validation->set_data($formData);
+ $this->form_validation->set_rules('datum_von', 'VonDatum', 'required|is_valid_date', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'VonDatum']),
+ 'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'VonDatum'])
+ ]);
+ $this->form_validation->set_rules('datum_bis', 'BisDatum', 'is_valid_date', [
+ 'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'BisDatum'])
+ ]);
+ $this->form_validation->set_rules('oe_kurzbz', 'Organisationseinheit', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Organisationseinheit'])
+ ]);
+ $this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required', [
+ 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Funktion'])
+ ]);
+ $this->form_validation->set_rules('wochenstunden', 'Wochenstunden', 'numeric', [
+ 'numeric' => $this->p->t('ui', 'error_fieldNotNumeric', ['field' => 'Wochenstunden'])
+ ]);
+
+ if ($this->form_validation->run() == false)
+ {
+ $this->terminateWithValidationErrors($this->form_validation->error_array());
+ }
+
+ $result = $this->BenutzerfunktionModel->update(
+ [
+ 'benutzerfunktion_id' => $benutzerfunktion_id,
+ ],
+ [
+ 'uid' => $uid,
+ 'datum_von' => $datum_von,
+ 'datum_bis' => $datum_bis ,
+ 'oe_kurzbz' => $formData['oe_kurzbz'],
+ 'funktion_kurzbz' => $formData['funktion_kurzbz'],
+ 'bezeichnung' => $bezeichnung,
+ 'wochenstunden' => $wochenstunden,
+ 'updateamum' => date('c'),
+ 'updatevon' => $authUID,
+ ]
+ );
+
+ $data = $this->getDataOrTerminateWithError($result);
+ $this->terminateWithSuccess($data);
+ }
+
+ public function deleteFunction($benutzerfunktion_id)
+ {
+ $result = $this->BenutzerfunktionModel->delete(
+ array('benutzerfunktion_id' => $benutzerfunktion_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 813edad10..a9c49fa3f 100644
--- a/application/controllers/api/frontend/v1/stv/Config.php
+++ b/application/controllers/api/frontend/v1/stv/Config.php
@@ -170,6 +170,11 @@ class Config extends FHCAPI_Controller
'component' => './Stv/Studentenverwaltung/Details/Aufnahmetermine.js'
];
+ $result['functions'] = [
+ 'title' => $this->p->t('stv', 'tab_functions'),
+ 'component' => './Stv/Studentenverwaltung/Details/Funktionen.js'
+ ];
+
Events::trigger('stv_conf_student', function & () use (&$result) {
return $result;
});
diff --git a/application/models/organisation/Organisationseinheit_model.php b/application/models/organisation/Organisationseinheit_model.php
index 1b1a826aa..ba91964f6 100644
--- a/application/models/organisation/Organisationseinheit_model.php
+++ b/application/models/organisation/Organisationseinheit_model.php
@@ -191,7 +191,7 @@ class Organisationseinheit_model extends DB_Model
/**
* @param string $oe_kurzbz
- *
+ *
* @return stdClass
*/
public function getWithType($oe_kurzbz)
@@ -203,18 +203,14 @@ class Organisationseinheit_model extends DB_Model
}
/**
- * Get OEs by eventQuery string. Use with autocomplete event queries.
- * @param $eventQuery String
- * @return array
+ * get highest organisation units
*/
- public function getAutocompleteSuggestions($eventQuery)
+ public function getHeads()
{
- $this->addSelect('oe_kurzbz');
- $this->addSelect('organisationseinheittyp_kurzbz, oe_kurzbz, bezeichnung, aktiv, lehre');
- $this->addOrder('organisationseinheittyp_kurzbz, bezeichnung');
+ $this->addSelect('*');
+ $this->addSelect('oe_kurzbz as head');
+ $result = $this->loadWhere(array('oe_parent_kurzbz' => null, 'aktiv' => true));
- return $this->loadWhere("
- oe_kurzbz ILIKE '%". $this->escapeLike($eventQuery). "%'
- ");
+ return $result;
}
}
diff --git a/application/views/Studentenverwaltung.php b/application/views/Studentenverwaltung.php
index 8e4c523d6..01e611657 100644
--- a/application/views/Studentenverwaltung.php
+++ b/application/views/Studentenverwaltung.php
@@ -15,11 +15,14 @@
'notiz',
),
'customCSSs' => [
+ #datepicker fuer component functions
'public/css/components/vue-datepicker.css',
'public/css/components/primevue.css',
- 'public/css/Studentenverwaltung.css'
+ 'public/css/Studentenverwaltung.css',
+ 'public/css/components/function.css'
],
'customJSs' => [
+ 'vendor/vuejs/vuedatepicker_js/vue-datepicker.iife.js'
#'vendor/npm-asset/primevue/tree/tree.min.js',
#'vendor/npm-asset/primevue/toast/toast.min.js'
],
diff --git a/public/css/components/function.css b/public/css/components/function.css
new file mode 100644
index 000000000..3e5e4a632
--- /dev/null
+++ b/public/css/components/function.css
@@ -0,0 +1,3 @@
+.item-inactive {
+ text-decoration: line-through;
+}
\ No newline at end of file
diff --git a/public/js/api/factory/functions.js b/public/js/api/factory/functions.js
new file mode 100644
index 000000000..ef0be43cb
--- /dev/null
+++ b/public/js/api/factory/functions.js
@@ -0,0 +1,89 @@
+/**
+ * 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 .
+ */
+export default {
+ getOrgHeads() {
+ var url = 'api/frontend/v1/funktionen/Funktionen/getOrgHeads';
+ return {
+ method: 'get',
+ url,
+ };
+ },
+ getOrgetsForCompany(unternehmen) {
+ var url = 'api/frontend/v1/funktionen/Funktionen/getOrgetsForCompany'
+ + '/' + unternehmen;
+ return {
+ method: 'get',
+ url,
+ };
+ },
+ getAllOrgUnits(filterStudent) {
+ var url = 'api/frontend/v1/funktionen/Funktionen/getAllOrgUnits';
+ return {
+ method: 'get',
+ url,
+ };
+ },
+ getAllUserFunctions(mitarbeiter_uid) {
+ var url = 'api/frontend/v1/funktionen/Funktionen/getAllUserFunctions'
+ + '/' + mitarbeiter_uid;
+ return {
+ method: 'get',
+ url,
+ };
+ },
+ getAllFunctions() {
+ var url = 'api/frontend/v1/funktionen/Funktionen/getAllFunctions';
+ return {
+ method: 'get',
+ url,
+ };
+ },
+ addFunction(params) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/funktionen/Funktionen/insertFunction/',
+ params
+ };
+ },
+ loadFunction(benutzerfunktion_id) {
+ var url = 'api/frontend/v1/funktionen/Funktionen/loadFunction'
+ + '/' + benutzerfunktion_id;
+ return {
+ method: 'get',
+ url,
+ };
+ },
+ updateFunction(params) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/funktionen/Funktionen/updateFunction/',
+ params
+ };
+ },
+ deleteFunction(benutzerfunktion_id) {
+ return {
+ method: 'post',
+ url: 'api/frontend/v1/funktionen/Funktionen/deleteFunction/' + benutzerfunktion_id
+ };
+ },
+ getOes(head, searchString) {
+ return {
+ method: 'get',
+ url: 'api/frontend/v1/funktionen/Funktionen/searchOes/' + head + '/' + searchString
+ };
+ },
+};
\ No newline at end of file
diff --git a/public/js/components/Funktionen/Funktionen.js b/public/js/components/Funktionen/Funktionen.js
new file mode 100644
index 000000000..4fdf5b6e4
--- /dev/null
+++ b/public/js/components/Funktionen/Funktionen.js
@@ -0,0 +1,588 @@
+import {CoreFilterCmpt} from "../filter/Filter.js";
+import FormInput from "../Form/Input.js";
+import FormForm from "../Form/Form.js";
+import BsModal from "../Bootstrap/Modal.js";
+import PvAutoComplete from "../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js";
+
+import ApiCoreFunktion from '../../api/factory/functions.js';
+
+export default {
+ name: 'FunctionComponent',
+ components: {
+ CoreFilterCmpt,
+ FormInput,
+ FormForm,
+ BsModal,
+ PvAutoComplete
+ },
+ props: {
+ modelValue: {
+ type: Object,
+ default: () => ({}),
+ required: false
+ },
+ config: {type: Object, default: () => ({}), required: false},
+ readonlyMode: {type: Boolean, required: false, default: false},
+ personID: {type: Number, required: true},
+ personUID: {type: String, required: true},
+ writePermission: {type: Boolean, required: false},
+ showDvCompany: {type: Boolean, required: false, default: true},
+ saveFunctionAsCopy: {type: Boolean, required: false, default: false},
+ stylePv21: {type: Boolean, required: false, default: false},
+ companyLinkFormatter: {type: Function || null, default: null}
+ },
+ data(){
+ return {
+ tabulatorOptions: {
+ ajaxURL: 'dummy',
+ ajaxRequestFunc: () => this.$api.call(
+ ApiCoreFunktion.getAllUserFunctions(this.personUID)
+ ),
+ ajaxResponse: (url, params, response) => response.data,
+ columns: [
+ {
+ title: "dienstverhaeltnis_unternehmen",
+ field: "dienstverhaeltnis_unternehmen",
+ headerFilter: "list",
+ headerFilterParams: {valuesLookup: true, autocomplete: true, sort: "asc"},
+ },
+ {
+ title: "funktion_beschreibung", field: "funktion_beschreibung", headerFilter: "list",
+ headerFilterParams: {valuesLookup: true, autocomplete: true, sort: "asc"},
+ },
+ {
+ title: "funktion_oebezeichnung", field: "funktion_oebezeichnung", headerFilter: "list",
+ headerFilterParams: {valuesLookup: true, autocomplete: true, sort: "asc"}
+ },
+ {title: "wochenstunden", field: "wochenstunden", headerFilter: true},
+ {
+ title: "Von",
+ field: "datum_von",
+ headerFilter: true,
+ formatter: function (cell) {
+ const dateStr = cell.getValue();
+ if (!dateStr) return "";
+
+ const date = new Date(dateStr);
+ return date.toLocaleString("de-DE", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ });
+ },
+ },
+ {
+ title: "Bis",
+ field: "datum_bis",
+ headerFilter: true,
+ formatter: function (cell) {
+ const dateStr = cell.getValue();
+ if (!dateStr) return "";
+
+ const date = new Date(dateStr);
+ return date.toLocaleString("de-DE", {
+ day: "2-digit",
+ month: "2-digit",
+ year: "numeric",
+ });
+ },
+ },
+ {title: "bezeichnung", field: "bezeichnung", headerFilter: true},
+ {title: "aktiv", field: "aktiv", visible: false},
+ {title: "benutzerfunktion_id", field: "benutzerfunktion_id", visible: false},
+ {title: "uid", field: "uid", visible: false},
+ {
+ //title: 'Aktionen', field: 'actions',
+ minWidth: 150, // Ensures Action-buttons will be always fully displayed
+ formatter: (cell, formatterParams, onRendered) => {
+ let container = document.createElement('div');
+ container.className = "d-flex gap-2";
+
+ if( cell.getRow().getData().dienstverhaeltnis_unternehmen === null ) {
+ let button = document.createElement('button');
+ button.className = 'btn btn-outline-secondary btn-action';
+ if(this.stylePv21)
+ button.innerHTML = '';
+ else
+ button.innerHTML = '';
+ button.title = this.$p.t('ui', 'bearbeiten');
+ button.addEventListener('click', (event) =>
+ this.actionEditFunction(cell.getData().benutzerfunktion_id)
+ );
+ if(this.readonlyMode === true) button.disabled = true;
+ container.append(button);
+ }
+ if( cell.getRow().getData().dienstverhaeltnis_unternehmen === null ) {
+ let button = document.createElement('button');
+ button.className = 'btn btn-outline-secondary btn-action';
+ button.innerHTML = '';
+ button.title = this.$p.t('ui', 'loeschen');
+ button.addEventListener('click', () =>
+ this.actionDeleteFunction(cell.getData().benutzerfunktion_id)
+ );
+ if(this.readonlyMode === true) button.disabled = true;
+ container.append(button);
+ }
+
+ if (cell.getRow().getData().dienstverhaeltnis_unternehmen === null && this.saveFunctionAsCopy) {
+ let button = document.createElement('button');
+ button.className = 'btn btn-outline-secondary btn-action';
+ button.innerHTML = '';
+ button.title = this.$p.t('ui', 'saveAsCopy');
+ button.addEventListener('click', () =>
+ this.actionCopyFunction(cell.getData().benutzerfunktion_id)
+ );
+ if(this.readonlyMode === true) button.disabled = true;
+ container.append(button);
+ }
+
+ return container;
+ },
+ frozen: true
+ }
+ ],
+ layout: 'fitDataFill',
+ layoutColumnsOnNewData: false,
+ height: '300',
+ persistenceID: 'core-functions',
+ },
+ tabulatorEvents: [
+ {
+ event: 'tableBuilt',
+ handler: async () => {
+ await this.$p.loadCategory(['global', 'lehre', 'person', 'ui']);
+ let cm = this.$refs.table.tabulator.columnManager;
+
+ //Field Company: if visible show link to dv
+ const column = cm.getColumnByField('dienstverhaeltnis_unternehmen');
+ const companyDv = {
+ title: this.$p.t('person', 'dv_unternehmen'),
+ width: 140,
+ visible: this.showDvCompany,
+ formatter: this.companyLinkFormatter
+ };
+ column.component.updateDefinition(companyDv);
+
+ cm.getColumnByField('funktion_beschreibung').component.updateDefinition({
+ title: this.$p.t('person', 'zuordnung_taetigkeit'),
+ width: 140
+ });
+ cm.getColumnByField('funktion_oebezeichnung').component.updateDefinition({
+ title: this.$p.t('lehre', 'organisationseinheit'),
+ width: 140
+ });
+ cm.getColumnByField('wochenstunden').component.updateDefinition({
+ title: this.$p.t('person', 'wochenstunden')
+ });
+
+ const columnDatumVon = cm.getColumnByField('datum_von');
+ const fieldVonDatum = {
+ title: this.$p.t('ui', 'from')
+ };
+
+ columnDatumVon.component.updateDefinition(fieldVonDatum);
+
+ const columnDatumBis = cm.getColumnByField('datum_bis');
+ const fieldBisDatum = {
+ title: this.$p.t('global', 'bis'),
+ };
+ columnDatumBis.component.updateDefinition(fieldBisDatum);
+
+ cm.getColumnByField('bezeichnung').component.updateDefinition({
+ title: this.$p.t('ui', 'bezeichnung'),
+ width: 140
+ });
+
+ }
+ }
+ ],
+ isFilterSet: true,
+ listOrgHeads: [],
+ listOrgUnits: [], //Old
+ listAllOrgUnits: [],
+ listOrgUnits_GST: [],
+ listOrgUnits_GMBH: [],
+ formData: {
+ head: 'gst',
+ oe_kurzbz: '',
+ funktion_kurzbz: null,
+ label:'',
+ //funktion_label: '',
+ funktion: null,
+ },
+ statusNew: true,
+ listAllFunctions: [],
+ abortController: {
+ oes: null,
+ functions: null
+ },
+ filteredOes: [],
+ filteredFunctions: [],
+ newBtnStyle: '',
+ selectedFunction: null,
+ selectedOe: null
+ }
+ },
+ watch: {
+ selectedFunction(newVal) {
+ this.formData.funktion_kurzbz = newVal?.funktion_kurzbz || '';
+ },
+ selectedOe(newVal) {
+ this.formData.oe_kurzbz = newVal?.oe_kurzbz || '';
+ }
+ },
+ methods: {
+ onSwitchChange() {
+ if (this.isFilterSet) {
+ this.$refs.table.tabulator.setFilter("aktiv", "=", true);
+ }
+ else {
+ this.$refs.table.tabulator.clearFilter();
+ this.isFilterSet = false;
+ }
+ },
+ actionNewFunction(){
+ this.resetModal();
+ this.statusNew = true;
+ this.formData.datum_von = new Date();
+ this.$refs.functionModal.show();
+ },
+ actionCopyFunction(benutzerfunktion_id) {
+ this.statusNew = true;
+ this.loadFunction(benutzerfunktion_id).then(() => {
+ this.$refs.functionModal.show();
+ });
+ },
+ actionDeleteFunction(benutzerfunktion_id) {
+ this.$fhcAlert
+ .confirmDelete()
+ .then(result => result
+ ? benutzerfunktion_id
+ : Promise.reject({handled: true}))
+ .then(this.deleteFunction)
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+ actionEditFunction(benutzerfunktion_id) {
+ this.resetModal();
+ this.statusNew = false;
+ this.loadFunction(benutzerfunktion_id).then(() => {
+ //set selectedFunction and selectedOd to enable viewing label in primevue autocomplete fields
+ this.selectedFunction = this.listAllFunctions.find(
+ item => item.funktion_kurzbz === this.formData.funktion_kurzbz
+ );
+ this.selectedOe = this.listAllOrgUnits.find(
+ item => item.oe_kurzbz === this.formData.oe_kurzbz
+ );
+ });
+ this.$refs.functionModal.show();
+ },
+ addFunction() {
+ const dataToSend = {
+ uid: this.personUID,
+ formData: this.formData
+ };
+ return this.$refs.functionData
+ .call(ApiCoreFunktion.addFunction(dataToSend))
+ .then(response => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
+ this.hideModal('functionModal');
+ this.resetModal();
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ this.reload();
+ });
+ },
+ loadFunction(benutzerfunktion_id) {
+ return this.$api
+ .call(ApiCoreFunktion.loadFunction(benutzerfunktion_id))
+ .then(result => {
+ this.formData = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ },
+ updateFunction(benutzerfunktion_id){
+ const dataToSend = {
+ uid: this.personUID,
+ formData: this.formData,
+ benutzerfunktion_id: benutzerfunktion_id
+ };
+ return this.$refs.functionData
+ .call(ApiCoreFunktion.updateFunction(dataToSend))
+ .then(response => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
+ this.hideModal('functionModal');
+ this.resetModal();
+ }).catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ this.reload();
+ });
+ },
+ deleteFunction(benutzerfunktion_id) {
+ return this.$api
+ .call(ApiCoreFunktion.deleteFunction(benutzerfunktion_id))
+ .then(response => {
+ this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
+ })
+ .catch(this.$fhcAlert.handleSystemError)
+ .finally(() => {
+ this.reload();
+ });
+ },
+ hideModal(modalRef) {
+ this.$refs[modalRef].hide();
+ },
+ reload() {
+ this.$refs.table.reloadTable();
+ },
+ resetModal(){
+ this.formData = {};
+ this.formData.head = 'gst';
+ this.formData.oe_kurzbz = '';
+ this.formData.funktion_kurzbz = '';
+ },
+ filterFunctions(event) {
+ const query = event.query.toLowerCase();
+ this.filteredFunctions = this.listAllFunctions.filter(item =>
+ item.label.toLowerCase().includes(query)
+ )
+ },
+ filterOes(event) {
+ const query = event.query.toLowerCase();
+
+ if(!this.formData.head)
+ this.$fhcAlert.alertError(this.$p.t('ui', 'bitteUnternehmenWaehlen'));
+
+ if(this.formData.head == 'gst') {
+ this.filteredOes = this.listOrgUnits_GST.filter(item =>
+ item.label.toLowerCase().includes(query)
+ );
+ }
+ if(this.formData.head == 'gmbh') {
+ this.filteredOes = this.listOrgUnits_GMBH.filter(item =>
+ item.label.toLowerCase().includes(query)
+ );
+ }
+ },
+
+ styleNewButton(){
+ if(this.stylePv21) {
+ this.newBtnStyle = "btn-sm";
+ }
+ },
+ //helper function: workaround to trigger validation if input is not a number
+ normalizeStunden() {
+ if (this.formData.wochenstunden === null || this.formData.wochenstunden === '') {
+ this.formData.wochenstunden = 'xxx'
+ }
+ }
+ },
+ created() {
+ this.$api
+ .call(ApiCoreFunktion.getOrgHeads())
+ .then(result => {
+ this.listOrgHeads = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+
+ this.$api
+ .call(ApiCoreFunktion.getAllFunctions())
+ .then(result => {
+ this.listAllFunctions = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ this.$api
+ .call(ApiCoreFunktion.getAllOrgUnits())
+ .then(result => {
+ this.listAllOrgUnits = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ this.$api
+ .call(ApiCoreFunktion.getOrgetsForCompany('gst'))
+ .then(result => {
+ this.listOrgUnits_GST = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+ this.$api
+ .call(ApiCoreFunktion.getOrgetsForCompany('gmbh'))
+ .then(result => {
+ this.listOrgUnits_GMBH = result.data;
+ })
+ .catch(this.$fhcAlert.handleSystemError);
+
+ this.styleNewButton();
+
+ },
+ template: `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $p.t('funktion', 'addFunktion') }}
+ {{ $p.t('funktion', 'editFunktion') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ slotProps.option.label}}
+
+
+
+
+
+
+
+
+ {{slotProps.option.label}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `
+}
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/Funktionen.js b/public/js/components/Stv/Studentenverwaltung/Details/Funktionen.js
new file mode 100644
index 000000000..f968ebecc
--- /dev/null
+++ b/public/js/components/Stv/Studentenverwaltung/Details/Funktionen.js
@@ -0,0 +1,24 @@
+import PersonFunctions from "../../../Funktionen/Funktionen.js";
+
+export default {
+ components: {
+ PersonFunctions,
+ },
+ props: {
+ modelValue: Object,
+ },
+ template: `
+ `,
+};
diff --git a/public/js/components/Stv/Studentenverwaltung/Details/FunktionenOld.js b/public/js/components/Stv/Studentenverwaltung/Details/FunktionenOld.js
new file mode 100644
index 000000000..5188c8d1b
--- /dev/null
+++ b/public/js/components/Stv/Studentenverwaltung/Details/FunktionenOld.js
@@ -0,0 +1,28 @@
+import PersonFunctions from "../../../Funktionen/Funktionen_composition_api";
+
+//import PersonFunctions from "../../../Funktionen/Funktionen.js";
+
+export default {
+ components: {
+ PersonFunctions,
+ },
+ props: {
+ modelValue: Object,
+ },
+ template: `
+ `,
+};
diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php
index 1251efff5..4901047b9 100644
--- a/system/phrasesupdate.php
+++ b/system/phrasesupdate.php
@@ -44252,6 +44252,148 @@ and represent the current state of research on the topic. The prescribed citatio
)
),
// FHC4 STUDIERENDENVERWALTUNG ANZAHL ENDE ---------------------------------------------------------------
+ // FHC-4 Studierendenverwaltung FUNCTIONS START
+ array(
+ 'app' => 'core',
+ 'category' => 'stv',
+ 'phrase' => 'tab_functions',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Funktionen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Functions',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'funktion',
+ 'phrase' => 'filter_active',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Nur aktive anzeigen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Display only active',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'funktion',
+ 'phrase' => 'addFunktion',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Funktion hinzufügen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Add function',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'funktion',
+ 'phrase' => 'editFunktion',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Funktion bearbeiten',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Edit function',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'ui',
+ 'phrase' => 'saveAsCopy',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Als Kopie speichern',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Save as copy',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'ui',
+ 'phrase' => 'error_fieldMustBePositiveInteger',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Das Eingabefeld {field} darf nur positive Ganzzahlen enthalten.',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'The input field {field} may only contain positive integers',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ array(
+ 'app' => 'core',
+ 'category' => 'ui',
+ 'phrase' => 'bitteUnternehmenWaehlen',
+ 'insertvon' => 'system',
+ 'phrases' => array(
+ array(
+ 'sprache' => 'German',
+ 'text' => 'Vor Änderung der Organisationseinheit bitte Unternehmen wählen',
+ 'description' => '',
+ 'insertvon' => 'system'
+ ),
+ array(
+ 'sprache' => 'English',
+ 'text' => 'Before changing the organizational unit, please select company',
+ 'description' => '',
+ 'insertvon' => 'system'
+ )
+ )
+ ),
+ // FHC-4 Studierendenverwaltung FUNCTIONS END
);