diff --git a/application/controllers/Cis/Zeitsperren.php b/application/controllers/Cis/Zeitsperren.php new file mode 100644 index 000000000..baa09b8c6 --- /dev/null +++ b/application/controllers/Cis/Zeitsperren.php @@ -0,0 +1,30 @@ + ['basis/cis:r'], + ]); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + } + + /** + * index loads the view Zeitsperren + * @access public + * @return void + */ + public function index() + { + $viewData = array( + 'uid'=>getAuthUID(), + ); + + $this->load->view('CisRouterView/CisRouterView.php', ['viewData' => $viewData, 'route' => 'zeitsperren']); + } +} diff --git a/application/controllers/api/frontend/v1/Zeitsperren.php b/application/controllers/api/frontend/v1/Zeitsperren.php new file mode 100644 index 000000000..66efa81a7 --- /dev/null +++ b/application/controllers/api/frontend/v1/Zeitsperren.php @@ -0,0 +1,367 @@ + self::PERM_LOGGED, + 'getTypenZeitsperren' => self::PERM_LOGGED, + 'getTypenErreichbarkeit' => self::PERM_LOGGED, + 'getStunden' => self::PERM_LOGGED, + 'loadZeitsperre' => self::PERM_LOGGED, + 'add' => self::PERM_LOGGED, + 'update' => self::PERM_LOGGED, + 'delete' => self::PERM_LOGGED, + ]); + + // Load Libraries + $this->load->library('VariableLib', ['uid' => getAuthUID()]); + $this->load->library('form_validation'); + + // Load language phrases + $this->loadPhrases([ + 'ui', + 'person', + 'zeitsperren' + ]); + + // Load models + $this->load->model('ressource/Zeitsperre_model', 'ZeitsperreModel'); + $this->load->model('ressource/Zeitsperretyp_model', 'ZeitsperretypModel'); + $this->load->model('ressource/Erreichbarkeit_model', 'ErreichbarkeitModel'); + $this->load->model('ressource/Stunde_model', 'StundeModel'); + $this->load->model('ressource/Zeitaufzeichnung_model', 'ZeitaufzeichnungModel'); + } + + public function getZeitsperrenUser($uid) + { + //check if $uid is passedUser + $loggedInUser = getAuthUID(); + if($loggedInUser != $uid) { + $this->load->library('PermissionLib'); + $isAdmin = $this->permissionlib->isBerechtigt('admin'); + if(!$isAdmin) { + $this->terminateWithError($this->p->t('ui', 'noAdmin'), self::ERROR_TYPE_GENERAL); + } + } + + $result = $this->ZeitsperreModel->getZeitsperrenUser($uid); + + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + $this->terminateWithSuccess((getData($result) ?: [])); + } + + public function getTypenZeitsperren() + { + $this->ZeitsperretypModel->addOrder('beschreibung', 'ASC'); + $result = $this->ZeitsperretypModel->load(); + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + $this->terminateWithSuccess((getData($result) ?: [])); + } + + public function getTypenErreichbarkeit() + { + $result = $this->ErreichbarkeitModel->load(); + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + $this->terminateWithSuccess((getData($result) ?: [])); + } + + public function getStunden() + { + $this->StundeModel->addOrder('stunde', 'ASC'); + $result = $this->StundeModel->load(); + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + $this->terminateWithSuccess((getData($result) ?: [])); + } + + public function loadZeitsperre($zeitsperre_id) + { + $this->ZeitsperreModel->addSelect( + 'campus.tbl_zeitsperre.*, typ.*, + ma.person_id AS ma_person_id, ma.vorname AS ma_vorname, ma.nachname AS ma_nachname, + ma.titelpre AS ma_titelpre, ma.titelpost AS ma_titelpost' + ); + $this->ZeitsperreModel->addJoin('campus.tbl_zeitsperretyp typ', 'ON (typ.zeitsperretyp_kurzbz = campus.tbl_zeitsperre.zeitsperretyp_kurzbz)'); + $this->ZeitsperreModel->addJoin('public.tbl_benutzer ben', 'ON (ben.uid = campus.tbl_zeitsperre.vertretung_uid)', 'LEFT'); + $this->ZeitsperreModel->addJoin('public.tbl_person ma', 'ON (ma.person_id = ben.person_id)', 'LEFT'); + $result = $this->ZeitsperreModel->loadWhere( + array('zeitsperre_id' => $zeitsperre_id) + ); + + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + $this->terminateWithSuccess((current(getData($result)) ?: [])); + } + + public function add($mitarbeiter_uid) + { + $loggedInUser = getAuthUID(); + + if($mitarbeiter_uid != $loggedInUser) + $this->terminateWithError($this->p->t('ui', 'noPermission'), self::ERROR_TYPE_GENERAL); + + $this->form_validation->set_rules('zeitsperretyp_kurzbz', 'Grund Zeitsperre', 'required', [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'Grund Zeitsperre']) + ]); + + $this->form_validation->set_rules('vondatum', '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('bisdatum', 'BisDatum', 'required|is_valid_date|callback_check_von_bis_datum|callback_check_diff_intval', [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field' => 'BisDatum']), + 'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field' => 'BisDatum']), + 'check_von_bis_datum' => $this->p->t('zeitsperre', 'error_VonDatumGroesserAlsBisDatum'), + 'check_diff_intval' => $this->p->t('zeitsperre', 'error_zeitraumAuffallendHoch') + ]); + + if ($this->form_validation->run() == false) + { + $this->terminateWithValidationErrors($this->form_validation->error_array()); + } + + $bezeichnung = $this->input->post('bezeichnung'); + $vondatum = $this->input->post('vondatum'); + $vonstunde = $this->input->post('vonstunde'); + $bisdatum = $this->input->post('bisdatum'); + $bisstunde = $this->input->post('bisstunde'); + //$vonIso = $this->input->post('vonISO'); //Timestamp für Stunde + //$bisIso = $this->input->post('bisISO'); //Timestamp für Stunde + $erreichbarkeit_kurzbz = $this->input->post('erreichbarkeit_kurzbz'); + $vertretung_uid = $this->input->post('vertretung_uid'); + $zeitsperretyp_kurzbz = $this->input->post('zeitsperretyp_kurzbz'); + + //check if existing zeitsperre + $result = $this->ZeitsperreModel->getSperreByDate($mitarbeiter_uid, $vondatum, $vonstunde, true); + $data = $this->getDataOrTerminateWithError($result); + + if(hasData($result)) + { + $this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitsperre', ['typ'=> current($data)->zeitsperretyp_kurzbz]), self::ERROR_TYPE_GENERAL); + } + + //check if existing zeitaufzeichnung + if(in_array($zeitsperretyp_kurzbz, Zeitsperre_model::BLOCKIERENDE_ZEITSPERREN)) + { + $result = $this->ZeitsperreModel->existsZeitaufzeichnung($mitarbeiter_uid, $vondatum, $bisdatum); + + if(hasData($result)) + $this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitaufzeichnung'), self::ERROR_TYPE_GENERAL); + } + + $result = $this->ZeitsperreModel->insert( + [ + 'mitarbeiter_uid' => $mitarbeiter_uid, + 'bezeichnung' => $bezeichnung, + 'vondatum' => $vondatum, + 'vonstunde' => $vonstunde, + 'bisdatum' => $bisdatum, + 'bisstunde' => $bisstunde, + 'erreichbarkeit_kurzbz' => $erreichbarkeit_kurzbz, + 'zeitsperretyp_kurzbz' => $zeitsperretyp_kurzbz, + 'vertretung_uid' => $vertretung_uid, + 'insertvon' => $loggedInUser, + 'insertamum' => date('c'), + ] + ); + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + + public function update($zeitsperre_id) + { + //check if loggedin User is owner of the zeitsperre + $loggedInUser = getAuthUID(); + $result = $this->ZeitsperreModel->load($zeitsperre_id); + $data = $this->getDataOrTerminateWithError($result); + $uid = current($data)->mitarbeiter_uid; + + if($uid != $loggedInUser) + $this->terminateWithError($this->p->t('ui', 'noPermission'), self::ERROR_TYPE_GENERAL); + + if(!$zeitsperre_id) + { + return $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id'=> 'Zeitsperre_id']), self::ERROR_TYPE_GENERAL); + } + //get current params + $array_update = [ + 'bezeichnung', + 'vondatum', + 'vonstunde', + 'bisdatum', + 'bisstunde', + // 'vonISO', //Timestamp für Stunde + // 'bisISO', //Timestamp für Stunde + 'erreichbarkeit_kurzbz', + 'vertretung_uid', + 'zeitsperretyp_kurzbz', + 'mitarbeiter_uid', + ]; + $post = $this->input->post(); + $update = []; + + foreach ($array_update as $prop) + { + if (array_key_exists($prop, $post)) + { + $update[$prop] = $post[$prop]; + } + } + + // Validation + $rulesDefined = false; //necessary, otherwise CI validation will always be triggered, even without rules + foreach ($update as $key => $val) { + switch ($key) { + case 'zeitsperretyp_kurzbz': + $this->form_validation->set_rules( + $key, + 'Grund Zeitsperre', + 'required', + ['required' => $this->p->t('ui', 'error_fieldRequired', ['field'=>'Grund Zeitsperre'])] + ); + $rulesDefined = true; + break; + case 'vondatum': + $this->form_validation->set_rules( + $key, + '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']) + ] + ); + $rulesDefined = true; + break; + case 'bisdatum': + $rules = 'required|is_valid_date'; + if (array_key_exists('vondatum', $update)) { + $rules .= '|callback_check_von_bis_datum|callback_check_diff_intval'; + } + $this->form_validation->set_rules( + $key, + 'BisDatum', + $rules, + [ + 'required' => $this->p->t('ui', 'error_fieldRequired', ['field'=>'BisDatum']), + 'is_valid_date' => $this->p->t('ui', 'error_notValidDate', ['field'=>'BisDatum']), + 'check_von_bis_datum' => $this->p->t('zeitsperre', 'error_VonDatumGroesserAlsBisDatum'), + 'check_diff_intval' => $this->p->t('zeitsperre', 'error_zeitraumAuffallendHoch') + ] + ); + $rulesDefined = true; + break; + } + } + + if ($rulesDefined && $this->form_validation->run() == false) { + $this->terminateWithValidationErrors($this->form_validation->error_array()); + } + + if(array_key_exists('vondatum', $post) || array_key_exists('bisdatum', $post)) + { + $result = $this->ZeitsperreModel->load($zeitsperre_id); + $data = $this->getDataOrTerminateWithError($result); + $data = current($data); + + $mitarbeiter_uid = array_key_exists('mitarbeiter_uid', $post) ? $update['mitarbeiter_uid'] : $data->mitarbeiter_uid; + $vondatum = array_key_exists('vondatum', $post) ? $update['vondatum'] : $data->vondatum; + $bisdatum = array_key_exists('bisdatum', $post) ? $update['bisdatum'] : $data->bisdatum; + $vonstunde = array_key_exists('vonstunde', $post) ? $update['vonstunde'] : $data->vonstunde; + $zeitsperretyp_kurzbz = array_key_exists('zeitsperretyp_kurzbz', $post) ? $update['zeitsperretyp_kurzbz'] : $data->zeitsperretyp_kurzbz; + + $result = $this->ZeitsperreModel->getSperreByDate($mitarbeiter_uid, $vondatum, $vonstunde, true); + $data = $this->getDataOrTerminateWithError($result); + + if(hasData($result)) + { + $this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitsperre', ['typ'=> current($data)->zeitsperretyp_kurzbz]), self::ERROR_TYPE_GENERAL); + } + + //check if existing zeitaufzeichnung + if(in_array($zeitsperretyp_kurzbz, Zeitsperre_model::BLOCKIERENDE_ZEITSPERREN)) + { + $result = $this->ZeitsperreModel->existsZeitaufzeichnung($mitarbeiter_uid, $vondatum, $bisdatum); + + if(hasData($result)) + $this->terminateWithError($this->p->t('zeitsperren', 'error_existingZeitaufzeichnung'), self::ERROR_TYPE_GENERAL); + } + } + + if (!empty($update)) { + $update['updatevon'] = $loggedInUser; + $update['updateamum'] = date('c'); + $result = $this->ZeitsperreModel->update($zeitsperre_id, $update); + + $data = $this->getDataOrTerminateWithError($result); + + $this->terminateWithSuccess($data); + } + else + $this->terminateWithSuccess("no update"); + } + + public function delete($zeitsperre_id) + { + + if (!is_numeric($zeitsperre_id) || (int)$zeitsperre_id <= 0) + { + $this->terminateWithError($this->p->t('ui', 'error_missingId', ['id' => 'Zeitsperre_id']), self::ERROR_TYPE_GENERAL); + } + + //check if loggedin User is owner of the zeitsperre + $loggedInUser = getAuthUID(); + $result = $this->ZeitsperreModel->load($zeitsperre_id); + $data = $this->getDataOrTerminateWithError($result); + $uid = current($data)->mitarbeiter_uid; + + if($uid != $loggedInUser) + $this->terminateWithError($this->p->t('ui', 'noPermission'), self::ERROR_TYPE_GENERAL); + + $result = $this->ZeitsperreModel->delete( + array('zeitsperre_id' => $zeitsperre_id) + ); + + if (isError($result)) { + $this->terminateWithError(getError($result), self::ERROR_TYPE_GENERAL); + } + $this->terminateWithSuccess((getData($result) ?: [])); + } + + public function check_von_bis_datum($bisdatum) + { + $vondatum = $this->input->post('vondatum'); + + return $vondatum <= $bisdatum; + } + + public function check_diff_intval($bisdatum) + { + $vondatum = $this->input->post('vondatum'); + + // Intervall in days + $vonTs = strtotime($vondatum); + $bisTs = strtotime($bisdatum); + + $tage = ($bisTs - $vonTs) / 86400; + + // if intervall > 14 + return $tage <= 14; + } + + +} diff --git a/application/models/ressource/Zeitsperre_model.php b/application/models/ressource/Zeitsperre_model.php index 078d29d8b..c7dc9dfff 100644 --- a/application/models/ressource/Zeitsperre_model.php +++ b/application/models/ressource/Zeitsperre_model.php @@ -12,6 +12,8 @@ class Zeitsperre_model extends DB_Model $this->pk = 'zeitsperre_id'; } + const BLOCKIERENDE_ZEITSPERREN = ['Krank','Urlaub','ZA','DienstV','PflegeU','DienstF','CovidSB','CovidKS']; + /** * Save or update Zeitsperre. * @@ -61,4 +63,128 @@ class Zeitsperre_model extends DB_Model return $this->execQuery($qry); } + + /** + * get Zeitsperren of a user + * + * @param $uid mitarbeiteruid + * @param $bisgrenze @true show only entries of actual business year (1.9.- 31.8.) + * + * @return array + */ + public function getZeitsperrenUser($uid, $bisgrenze = true) + { + $qry = " + SELECT + tbl_zeitsperre.*, tbl_zeitsperretyp.*, tbl_erreichbarkeit.farbe AS erreichbarkeit_farbe, + tbl_erreichbarkeit.beschreibung AS erreichbarkeit_beschreibung, + CONCAT (ps.vorname, ' ', ps.nachname) as vertretung + FROM (campus.tbl_zeitsperre JOIN campus.tbl_zeitsperretyp USING (zeitsperretyp_kurzbz)) + LEFT JOIN campus.tbl_erreichbarkeit USING (erreichbarkeit_kurzbz) + LEFT JOIN public.tbl_benutzer ON campus.tbl_zeitsperre.vertretung_uid = public.tbl_benutzer.uid + LEFT JOIN public.tbl_person ps USING (person_id) + WHERE mitarbeiter_uid= ? + "; + + if($bisgrenze) + { + $qry.=" + AND ( + (date_part('month',vondatum)>=9 AND date_part('year', vondatum)>='".(date('Y')-1)."') + OR + (date_part('month',vondatum)<9 AND date_part('year', vondatum)>='".(date('Y'))."') + )"; + } + + $qry.= " ORDER BY vondatum DESC"; + + return $this->execQuery($qry, array('mitarbeiter_uid' => $uid)); + } + + /** + * check a date for existing zeitsperre + * + * @param $uid mitarbeiteruid + * @param $datum datum to check + * @param $stunde stunde (default = null) + * @param bool $nurblockierend if only hr relevante zeitsperren have to be checked + * + * @return array + */ + public function getSperreByDate($uid, $datum, $stunde = null, $nurblockierend = false) + { + $parametersArray = [$datum, $datum]; + + $qry = " + SELECT + * + FROM + campus.tbl_zeitsperre + WHERE + vondatum <= ? + AND bisdatum>= ?"; + + if($nurblockierend) + { + $qry .= " AND zeitsperretyp_kurzbz IN ('" + . implode("','", self::BLOCKIERENDE_ZEITSPERREN) + . "')"; + } + + if(!is_null($stunde)) + { + $parametersArray = array_merge( + $parametersArray, + [$datum, $stunde, $datum, $datum, $stunde, $datum] + ); + + $qry.=" AND + ((vondatum= ? AND vonstunde<= ? OR vonstunde is null OR vondatum<> ?) AND + (bisdatum= ? AND bisstunde>= ? OR bisstunde is null OR bisdatum<> ?))"; + } + + array_push($parametersArray, $uid); + + $qry .= "AND mitarbeiter_uid= ? "; + + return $this->execQuery($qry, $parametersArray); + } + + /** + * check a date for existing zeitsperre + * + * @param $uid mitarbeiteruid + * @param $vondatum datum in Format IS0 + * @param $bisdatum datum in Format ISO + * + * @return array + */ + public function existsZeitaufzeichnung($uid, $vonDay, $bisDay) + { + try { + $from = new DateTime($vonDay); + $to = new DateTime($bisDay); + } catch (Exception $e) { + throw new Exception("Invalid date format"); + } + + //remove hour stamps + $from->setTime(0, 0, 0); + $to->setTime(0, 0, 0)->modify('+1 day'); + + $fromSql = $from->format('Y-m-d'); + $toSql = $to->format('Y-m-d'); + $params = [$uid, $fromSql, $toSql]; + + $qry = " + SELECT * + FROM campus.tbl_zeitaufzeichnung + WHERE uid = ? + AND start >= ? + AND ende < ? "; + + $result = $this->execQuery($qry, $params); + + return $result; + } } diff --git a/application/views/CisRouterView/CisRouterView.php b/application/views/CisRouterView/CisRouterView.php index e0b0e2bb3..1aa8c398e 100644 --- a/application/views/CisRouterView/CisRouterView.php +++ b/application/views/CisRouterView/CisRouterView.php @@ -25,7 +25,8 @@ $includesArray = array( 'public/css/components/abgabetool/abgabe.css', 'public/css/Cis4/Cms.css', 'public/css/Cis4/Studium.css', - 'public/css/Cis4/Benotungstool.css' + 'public/css/Cis4/Benotungstool.css', + 'public/css/Cis4/Zeitsperren.css', ), 'customJSs' => array( 'vendor/npm-asset/primevue/accordion/accordion.min.js', diff --git a/public/css/Cis4/Zeitsperren.css b/public/css/Cis4/Zeitsperren.css new file mode 100644 index 000000000..dac3d3988 --- /dev/null +++ b/public/css/Cis4/Zeitsperren.css @@ -0,0 +1,39 @@ +/* Repositioning clear icon Datepicker for not being outside the textfield */ +/* Wrapper */ +.dp__input_wrap { + position: relative; +} + +/* calender-Icon left */ +.dp__input_icon { + position: absolute; + left: 10px; + right: auto; + top: 50%; + transform: translateY(-50%); +} + +/* Clear-Icon right */ +.dp__clear_icon { + position: absolute; + right: 10px; + left: auto; + top: 50%; + transform: translateY(-50%); +} + +/* padding for Icons */ +.dp__input { + padding-left: 36px !important; + padding-right: 36px !important; +} + +.info-feedback { + display: block; + font-size: 0.875em; + color: #0d6efd; /* Bootstrap primary */ +} + +.is-info { + border-color: #0d6efd; +} diff --git a/public/js/api/factory/cis/zeitsperren.js b/public/js/api/factory/cis/zeitsperren.js new file mode 100644 index 000000000..2a73a5e79 --- /dev/null +++ b/public/js/api/factory/cis/zeitsperren.js @@ -0,0 +1,70 @@ +/** + * Copyright (C) 2026 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 { + getTimelocksUser(uid) { + return { + method: 'get', + url: '/api/frontend/v1/Zeitsperren/getZeitsperrenUser/' + uid + }; + }, + getTypenZeitsperren(){ + return { + method: 'get', + url: '/api/frontend/v1/Zeitsperren/getTypenZeitsperren/' + }; + }, + getTypenErreichbarkeit(){ + return { + method: 'get', + url: '/api/frontend/v1/Zeitsperren/getTypenErreichbarkeit/' + }; + }, + getStunden(){ + return { + method: 'get', + url: '/api/frontend/v1/Zeitsperren/getStunden/' + }; + }, + addZeitsperre(uid, params) { + return { + method: 'post', + url: '/api/frontend/v1/Zeitsperren/add/' + uid, + params + }; + }, + editZeitsperre(zeitsperre_id, params) { + return { + method: 'post', + url: '/api/frontend/v1/Zeitsperren/update/' + zeitsperre_id, + params + }; + }, + loadZeitsperre(zeitsperre_id) { + return { + method: 'get', + url: '/api/frontend/v1/Zeitsperren/loadZeitsperre/' + zeitsperre_id + }; + }, + deleteZeitsperre(zeitsperre_id) { + return { + method: 'post', + url: '/api/frontend/v1/Zeitsperren/delete/' + zeitsperre_id + }; + } + +}; \ No newline at end of file diff --git a/public/js/apps/Cis/Cis.js b/public/js/apps/Cis/Cis.js index 58c8f4356..8885eb945 100644 --- a/public/js/apps/Cis/Cis.js +++ b/public/js/apps/Cis/Cis.js @@ -21,6 +21,7 @@ import StgOrgLvPlan from "../../components/Cis/LvPlan/StgOrg.js"; import OtherLvPlan from "../../components/Cis/LvPlan/OtherLvPlan.js"; import PaabgabeUebersicht from "../../components/Cis/ProjektabgabeUebersicht/ProjektabgabeUebersicht.js"; import Benotungstool from "../../components/Cis/Benotungstool/Benotungstool.js"; +import Zeitsperren from "../../components/Cis/Zeitsperren/Zeitsperren.js"; import ApiRouteInfo from '../../api/factory/routeinfo.js'; import {capitalize} from "../../helpers/StringHelpers.js"; @@ -260,6 +261,12 @@ const router = VueRouter.createRouter({ }; }, }, + { + path: `/Cis/Zeitsperren`, + name: 'Zeitsperren', + component: Zeitsperren, + props: true + }, ] }) diff --git a/public/js/components/Cis/Zeitsperren/Zeitsperren.js b/public/js/components/Cis/Zeitsperren/Zeitsperren.js new file mode 100644 index 000000000..489860970 --- /dev/null +++ b/public/js/components/Cis/Zeitsperren/Zeitsperren.js @@ -0,0 +1,673 @@ +import {CoreFilterCmpt} from "../../filter/Filter.js"; +import FormForm from '../../Form/Form.js'; +import FormInput from '../../Form/Input.js'; +import PvAutoComplete from '../../../../../index.ci.php/public/js/components/primevue/autocomplete/autocomplete.esm.min.js'; + +import ApiAuthinfo from '../../../api/factory/authinfo.js'; +import ApiTimelocks from "../../../api/factory/cis/zeitsperren.js"; +import ApiStvAbschlusspruefung from "../../../api/factory/stv/abschlusspruefung"; + +export default { + name: 'ZeitsperrenComponent', + components: { + CoreFilterCmpt, + FormForm, + FormInput, + PvAutoComplete + }, + data(){ + return { + uid: null, + statusNew: true, + timeRecordingLockedUntil: '2015-08-31', //TODO(Manu) check if needed + typesTimeLocks: ["Urlaub", "PflegeU", "ZA", "Krank", "DienstF", "DienstV", "CovidSB", "CovidKS"], + typesHideStunden: ["Urlaub", "ZA", "Krank", "DienstF", "DienstV", "CovidSB", "CovidKS"], + listTypenZeitsperren: [], + listTypenErreichbarkeit: [], + listStunden: [], + tabulatorOptions: null, + tabulatorEvents: [], + originalData: {}, + zeitsperreData: { + vondatum : new Date(), + bisdatum: new Date(), + vonISO : "00:00:00", //later + bisISO: "23:59:59", //later + erreichbarkeit_kurzbz: 'n', + zeitsperretyp_kurzbz: 'Arzt', + vonstunde: null, + bisstunde: null + }, + changedData: {}, + selectedVertretung: null, + filteredMitarbeiter: [], + abortController: { + mitarbeiter: null + }, + }; + }, + computed: { + dienstverhinderungen() { + return { + "Eheschließung": "a) " + this.$p.t('zeitsperren', 'eheschliessung'), + "Geburt eigenes Kind": "b) " + this.$p.t('zeitsperren', 'geburt'), + "Heirat Kind/Geschwister": "c) " + this.$p.t('zeitsperren', 'heirat'), + "Eigene Sponsion/Promotion": "d) " + this.$p.t('zeitsperren', 'sponsion'), + "Lebensbedr. Erkrankung P/K/E": "e) " + this.$p.t('zeitsperren', 'erkrankung_lebensbedr'), + "Ableben P/K/E": "f) " + this.$p.t('zeitsperren', 'ableben'), + "Bestattung G/S/G": "g) " + this.$p.t('zeitsperren', 'bestattung'), + "Wohnungswechsel": "h) " + this.$p.t('zeitsperren', 'umzug'), + "Bundesheer": "i) " + this.$p.t('zeitsperren', 'bundesheer'), + "Volksschultag": "j) " + this.$p.t('zeitsperren', 'volksschultag')}; + }, + showInfo(){ + return this.zeitsperreData.zeitsperretyp_kurzbz === 'DienstF'; + }, + }, + methods: { + actionEditZeitsperre(zeitsperre_id){ + this.statusNew = false; + return this.$api + .call(ApiTimelocks.loadZeitsperre(zeitsperre_id)) + .then(response => { + this.originalData = structuredClone(response.data); + this.zeitsperreData = structuredClone(response.data); + + this.selectedVertretung = { + label: this.getPersonLabel(this.zeitsperreData.ma_titelpre, this.zeitsperreData.ma_nachname, this.zeitsperreData.ma_vorname, this.zeitsperreData.ma_titelpost, this.zeitsperreData.vertretung_uid), + person_id: this.zeitsperreData.ma_person_id, + mitarbeiter_uid: this.zeitsperreData.vertretung_uid + }; + }) + .catch(this.$fhcAlert.handleSystemError); + }, + actionDeleteZeitsperre(zeitsperre_id){ + this.$fhcAlert + .confirmDelete() + .then(result => result + ? zeitsperre_id + : Promise.reject({ handled: true }) + ) + .then(() => this.deleteZeitsperre(zeitsperre_id)) + .catch(this.$fhcAlert.handleSystemError); + }, + saveZeitsperre(zeitsperreId = null) { + const isNew = !zeitsperreId; + + let payload = isNew + ? { ...this.zeitsperreData } // add + : this.getChangedFields(this.originalData, // edit + this.zeitsperreData); + + if (!isNew && Object.keys(payload).length === 0) { + return Promise.resolve(); + } + + const request = isNew + ? ApiTimelocks.addZeitsperre(this.uid, payload) + : ApiTimelocks.editZeitsperre(zeitsperreId, payload); + + return this.$refs.dataZeitsperre + .call(request) + .then(() => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave')); + + this.reset(); + this.reload(); + }) + .catch(this.$fhcAlert.handleSystemError); + }, + deleteZeitsperre(zeitsperre_id){ + return this.$api + .call(ApiTimelocks.deleteZeitsperre(zeitsperre_id)) + .then(response => { + this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete')); + }) + .catch(this.$fhcAlert.handleSystemError) + .finally(()=> { + this.reload(); + }); + }, + searchMitarbeiter(event) { + if (this.abortController.mitarbeiter) { + this.abortController.mitarbeiter.abort(); + } + + this.abortController.mitarbeiter = new AbortController(); + + return this.$api + .call(ApiStvAbschlusspruefung.getMitarbeiter(event.query)) + .then(result => { + this.filteredMitarbeiter = []; + for (let mitarbeiter of result.data.retval) { + this.filteredMitarbeiter.push( + { + label: this.getPersonLabel( + mitarbeiter.titelpre, + mitarbeiter.nachname, + mitarbeiter.vorname, + mitarbeiter.titelpost, + mitarbeiter.mitarbeiter_uid + ), + person_id: mitarbeiter.person_id, + mitarbeiter_uid: mitarbeiter.mitarbeiter_uid + } + ); + } + }); + }, + getPersonLabel(titelpre, nachname, vorname, titelpost, uid) { + if(!uid) + return ''; + return nachname + ' ' + vorname + (titelpre ? ' ' + titelpre : '') + (titelpost ? ' ' + titelpost : '') + (uid ? ' (' + uid + ')' : ''); + }, + reload() { + if (this.$refs.table) + this.$refs.table.reloadTable(); + }, + reset(){ + this.statusNew = true; + this.selectedVertretung = null; + this.zeitsperreData = { + vondatum : new Date(), + bisdatum: new Date(), + vonISO : "00:00:00", //later + bisISO: "23:59:59", //later + erreichbarkeit_kurzbz: 'n', + zeitsperretyp_kurzbz: 'Arzt', + vonstunde: null, + bisstunde: null + }; + this.originalData = {}; + }, + handleChangeVonStunde(){ + let stunde = this.zeitsperreData.vonstunde; + const result = this.listStunden.find(item => item.stunde === stunde); + if (!result) { + this.zeitsperreData.vonISO = '00:00:00'; + return; + } + this.zeitsperreData.vonISO = result.beginn; + }, + handleChangeBisStunde(){ + let stunde = this.zeitsperreData.bisstunde; + const result = this.listStunden.find(item => item.stunde === stunde); + if (!result) { + this.zeitsperreData.bisISO = '23:59:59'; + return; + } + this.zeitsperreData.bisISO = result.ende; + }, + handleStunden(){ + if (this.typesHideStunden.includes(this.zeitsperreData.zeitsperretyp_kurzbz)){ + this.zeitsperreData.vonstunde = null; + this.zeitsperreData.bisstunde = null; + this.zeitsperreData.vonISO = '00:00:00'; + this.zeitsperreData.bisISO = '23:59:59'; + } + }, + copyDateForBis(){ + this.zeitsperreData.bisdatum = this.zeitsperreData.vondatum; + }, + getChangedFields(original, current) { + const diff = {}; + + Object.keys(current).forEach((key) => { + if (current[key] !== original[key]) { + diff[key] = current[key]; + } + }); + return diff; + }, + }, + watch: { + selectedVertretung(newVal) { + this.zeitsperreData.vertretung_uid = newVal?.mitarbeiter_uid || null; + }, + 'zeitsperreData.zeitsperretyp_kurzbz'(newVal) { + if (newVal === 'DienstV') { + // set first key as default + if (!this.zeitsperreData.bezeichnung) { + const firstKey = Object.keys(this.dienstverhinderungen)[0]; + this.zeitsperreData.bezeichnung = firstKey; + } + } else { + this.zeitsperreData.bezeichnung = ''; + } + } + }, + created() { + this.$api.call(ApiAuthinfo.getAuthUID()).then(res => { + + //check if there is a user, passed via route + const urlUid = this.$route?.query?.uid; + this.uid = urlUid ? urlUid : res.data.uid; + + this.tabulatorOptions = { + ajaxURL: 'dummy', + ajaxRequestFunc: () => + this.$api.call(ApiTimelocks.getTimelocksUser(this.uid)), + ajaxResponse: (url, params, response) => response.data, + columns: [ + {title:"bezeichnung", field:"bezeichnung"}, + {title:"Grund", field:"beschreibung"}, + {title:"Von", field:"vondatum", + 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:"bisdatum", + 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:"vonstunde", field:"vonstunde", visible: false}, + {title:"bisstunde", field:"bisstunde", visible: false}, + {title:"Vertretung", field:"vertretung"}, + {title:"Erreichbarkeit", field:"erreichbarkeit_beschreibung"}, + {title:"zeitsperre_id", field:"zeitsperre_id", visible: false}, + {title:"mitarbeiter_uid", field:"mitarbeiter_uid", visible: false}, + {title:"freigabeamum", field:"freigabeamum", visible: false, + formatter: function (cell) { + const value = cell.getValue(); + return value === null + ? '' + : ''; + } + }, + {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"; + + let button = document.createElement('button'); + button.className = 'btn btn-outline-secondary btn-action'; + button.innerHTML = ''; + button.title = this.$p.t('ui', 'bearbeiten'); + button.addEventListener('click', (event) => + this.actionEditZeitsperre(cell.getData().zeitsperre_id) + ); + if(cell.getData().zeitsperretyp_kurzbz == 'DienstV' || cell.getData().zeitsperretyp_kurzbz == 'ZVerfueg'){ + button.disabled = true; + } + //TODO(Manu) check if needed + if(this.typesTimeLocks.includes(cell.getData().zeitsperretyp_kurzbz) && (cell.getData().vondatum < this.timeRecordingLockedUntil)){ + button.disabled = true; + } + container.append(button); + + 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.deleteZeitsperre(cell.getData().zeitsperre_id) + this.actionDeleteZeitsperre(cell.getData().zeitsperre_id) + ); + if(cell.getData().zeitsperretyp_kurzbz == 'Urlaub' || cell.getData().zeitsperretyp_kurzbz == 'ZVerfueg'){ + button.disabled = true; + } + //TODO(Manu) check if needed + if(this.typesTimeLocks.includes(cell.getData().zeitsperretyp_kurzbz) && (cell.getData().vondatum < this.timeRecordingLockedUntil)){ + button.disabled = true; + } + container.append(button); + + return container; + }, + frozen: true + }, + ] + }; + this.tabulatorEvents = [ + { + event: 'tableBuilt', + handler: async() => { + await this.$p.loadCategory(['global', 'person', 'zeitsperren', 'ui', 'abschlusspruefung']); + + let cm = this.$refs.table.tabulator.columnManager; + + cm.getColumnByField('bezeichnung').component.updateDefinition({ + title: this.$p.t('person', 'grund') + }); + cm.getColumnByField('beschreibung').component.updateDefinition({ + title: this.$p.t('global', 'bezeichnung') + }); + cm.getColumnByField('vondatum').component.updateDefinition({ + title: this.$p.t('ui', 'von') + }); + cm.getColumnByField('bisdatum').component.updateDefinition({ + title: this.$p.t('global', 'bis') + }); + cm.getColumnByField('vonstunde').component.updateDefinition({ + title: this.$p.t('zeitsperren', 'stunde_von') + }); + cm.getColumnByField('bisstunde').component.updateDefinition({ + title: this.$p.t('zeitsperren', 'stunde_bis') + }); + cm.getColumnByField('vertretung').component.updateDefinition({ + title: this.$p.t('person', 'vertretung') + }); + + cm.getColumnByField('erreichbarkeit_beschreibung').component.updateDefinition({ + title: this.$p.t('person', 'erreichbarkeit') + }); + cm.getColumnByField('freigabeamum').component.updateDefinition({ + title: this.$p.t('abschlusspruefung', 'freigabe') + }); + + /* cm.getColumnByField('actions').component.updateDefinition({ + title: this.$p.t('global', 'aktionen') + });*/ + + } + } + ]; + }); + + this.$api + .call(ApiTimelocks.getTypenZeitsperren()) + .then(result => { + this.listTypenZeitsperren = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + this.$api + .call(ApiTimelocks.getTypenErreichbarkeit()) + .then(result => { + this.listTypenErreichbarkeit = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + this.$api + .call(ApiTimelocks.getStunden()) + .then(result => { + this.listStunden = result.data; + }) + .catch(this.$fhcAlert.handleSystemError); + + }, + template: /* html */` +
+

{{$p.t('zeitsperren', 'header_zeitsperren')}} ({{uid}})

+ + +
+
+ + + +
+ Dienstfreistellungen nur in Absprache mit HR Service eintragen! +
+
+ +
+ + + + +
+
+ + +
+
+ +
+
+
+ + +
+
+ +
+ +
+ + + + + +
+ + + +
+ +
+
+ + +
+
+ +
+ + + + +
+ + + +
+ +
+
+ + +
+
+ +
+
+ + + +
+ +
+ + +
+
+ +
+ +
+ +
+ +
+ {{$p.t('alert', 'attention')}}
+ {{$p.t('zeitsperren', 'info_zeitsperrenMoreDays')}} +
+
+ + +
+ + + +
+ ` +}; \ No newline at end of file diff --git a/system/phrasesupdate.php b/system/phrasesupdate.php index 8a159dbdc..1ff99a819 100644 --- a/system/phrasesupdate.php +++ b/system/phrasesupdate.php @@ -60788,6 +60788,468 @@ I have been informed that I am under no obligation to consent to the transmissio ) ), //**************************** Pruefungsprotokolle end + //// ### ZEITSPERREN START + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'eheschliessung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Eigene Eheschließung oder Verpartnerung (3 Tage)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Own marriage or civil partnership (3 days)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'geburt', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Geburt eines Kindes der Ehefrau/Lebensgefährtin (2 Tage)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Birth of a child of the wife/partner (2 days)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'heirat', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Eheschließung oder Verpartnerung eines Kindes/eigener Geschwister (1 Tag)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Marriage or civil partnership of a child or own sibling (1 day)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'sponsion', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Teilnahme an eigener Sponsion/Promotion (1 Tag)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Participation in own graduation/promotion ceremony (1 day)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'erkrankung_lebensbedr', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Lebensbedrohliche Erkrankung Partner/Kinder/Eltern (3 Tage)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Life-threatening illness of partner/children/parents (3 days)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'ableben', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Ableben Partner/Kinder/Elternteil (3 Tage)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Death of partner/children/parent (3 days)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'bestattung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Teilnahme an Bestattung Geschwister/Schwiegereltern/eigener Großeltern (1 Tag)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Attendance at the funeral of siblings/parents-in-law/own grandparents (1 day)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'umzug', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Wohnungswechsel in eigenen Haushalt (2 Tage)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Moving to your own home (2 days)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'bundesheer', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Einberufung Bundesheer', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Conscription of the Austrian Armed Forces', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'volksschultag', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'erster Volksschultag (1 Tag)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'First day of primary school (1 day)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'error_VonDatumGroesserAlsBisDatum', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'VonDatum ist größer als BisDatum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'The start date is greater than the end date.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'error_zeitraumAuffallendHoch', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Achtung, eingegebener Zeitraum ist auffallend hoch. \nWollen Sie die Daten dennoch speichern?', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Attention, the entered time period is unusually long. Do you still want to save the data?', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'stunde', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Stunde(inklusive)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Hour(inclusive)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'stunde_von', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Stunde(von)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'hour(from)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'stunde_bis', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Stunde(bis)', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'hour(to)', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'addZeitsperre', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zeitsperre hinzufügen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'add Timelock', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'saveZeitsperre', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zeitsperre speichern', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'save Timelock', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'noAdmin', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Für diese Aktion benötigen Sie Administratorenrechte', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'You need administrator rights to perform this action.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'ui', + 'phrase' => 'noPermission', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Sie haben keine Berechtigung, für einen anderen User einen Datensatz anzulegen, zu bearbeiten oder zu löschen', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'You do not have permission to create, edit, or delete a record for another user.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'info_zeitsperrenMoreDays', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es werden alle eingegebenen Tage bei der Berechnung berücksichtigt. Daher müssen mehrtägige Zeitsperren an Unterbrechungen wie Wochenenden oder Feiertagen unterteilt werden!', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'All entered days will be included in the calculation. Therefore, multi-day time blocks must be broken down by breaks such as weekends or holidays!', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'header_zeitsperren', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Meine Zeitsperren', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'My timelocks', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'error_existingZeitaufzeichnung', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Es existiert mindestens eine Zeitaufzeichnung im angegebenen Zeitraum', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'At least one time recording exists within the specified period.', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + array( + 'app' => 'core', + 'category' => 'zeitsperren', + 'phrase' => 'error_existingZeitsperre', + 'insertvon' => 'system', + 'phrases' => array( + array( + 'sprache' => 'German', + 'text' => 'Zeitsperre vom Typ {typ} vorhanden', + 'description' => '', + 'insertvon' => 'system' + ), + array( + 'sprache' => 'English', + 'text' => 'Time lock of type {type} exists', + 'description' => '', + 'insertvon' => 'system' + ) + ) + ), + /// ### ZEITSPERREN END );