Compare commits

...

99 Commits

Author SHA1 Message Date
adisposkofh eb8b4986d7 fixed issue with sidenav covering search results/usermenu 2026-04-20 11:36:53 +02:00
Andreas Österreicher 0ff29ba6af Merge branch 'epic-56039/LV-Evaluierung' 2026-04-16 13:22:40 +02:00
Harald Bamberger ba543448ae Merge branch 'bug-76260/StudVW_Messages_TinyMCE_Cursor_Jumping_TextInput' 2026-04-16 11:26:32 +02:00
Harald Bamberger f121f9b5a2 deactivate pagination - since potentially msg threads are not shown completely 2026-04-16 11:23:57 +02:00
Harald Bamberger 88b22f5490 revert to v-if to not render element when not necessary and use Vue.nextTick for dependent code 2026-04-15 17:02:58 +02:00
Harald Bamberger 4b7ee9abe1 Merge branch 'feature-70376/Lohnguide' 2026-04-15 15:52:12 +02:00
Cristina d499619cf3 Added phrase endedatumMussInZukunftLiegen 2026-04-15 13:39:06 +02:00
Cristina f489153ff3 Merge branch 'master' into epic-56039/LV-Evaluierung 2026-04-15 12:25:05 +02:00
Cristina 9b79a07fa2 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-04-15 12:24:44 +02:00
Harald Bamberger 6ce14a25d7 Merge branch 'master' into feature-70376/Lohnguide 2026-04-15 11:49:26 +02:00
Werner Masik c701d92779 fix db_update 2026-04-15 11:15:10 +02:00
Werner Masik 73e03ba901 Gehaltstyp Überstundenpauschale und Sachbezug PKW 2026-04-15 10:44:03 +02:00
ma0068 95a7797ae9 delete unused apicall for mode modal
add editor.setContent to function getReplyData
readd loadReplyData and use v-show for visibleDiv for mode inSamePage
2026-04-14 15:46:34 +02:00
Andreas Österreicher 3a91b12f31 Merge branch 'epic-56039/LV-Evaluierung' 2026-04-13 10:39:00 +02:00
ma0068 843894405e changes for NewDiv
remove Watcher for formData Fields
add predefault settings for tabulator fields
add setContent for Editor
change text for closing window/tab
show alertSuccess for sending Message just in case of inSamePage
2026-04-10 13:03:24 +02:00
ma0068 8fddbc3a32 delete watcher for formData fields, add setContent for loading Vorlage 2026-04-10 09:15:34 +02:00
Harald Bamberger b2538075ee use STV_TAGS_ENABLED config when preparing sql statement for students list to query tags only if enabled 2026-04-07 10:35:06 +02:00
Werner Masik 5c463c0866 add vordienstzeit to lohnguide 2026-04-06 22:25:13 +02:00
Werner Masik 423bbd95a6 add vordienstzeit to lohnguide 2026-04-06 22:25:12 +02:00
Cristina 386cc779bf Merge branch 'master' into epic-56039/LV-Evaluierung 2026-04-02 15:49:20 +02:00
Cristina 08c6d58a50 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-04-02 14:24:39 +02:00
Cristina 3f53c5feba Added: method getKFLByUID to get Kompetenzfeldleitung by UID 2026-04-02 14:23:59 +02:00
Andreas Österreicher 2c057aad58 Updated Startup Dump to Final 3.3 Version 2026-04-01 13:00:50 +02:00
kindlm c2ce831bca Merge remote-tracking branch 'origin/master' 2026-03-26 11:43:59 +01:00
kindlm 21c1f13b28 Spalte "faktiv" (Foebis-Aktiv) im FAS 2026-03-26 11:43:20 +01:00
Andreas Österreicher e0079bb812 Merge branch 'feature-71665/mc4_vorlage' 2026-03-26 09:27:00 +01:00
Andreas Österreicher 966d1d10f6 Merge branch 'feature-71566/Studienordnung_Anpassungen_fuer_Programme_und_Lehrgaenge' 2026-03-26 09:05:31 +01:00
Andreas Österreicher 76936ad74f Merge branch 'feature-75703/BIS_Personalmeldung_Lehrgaenge' 2026-03-26 08:31:13 +01:00
Harald Bamberger 6fbb09eb6e group or clause 2026-03-25 16:32:43 +01:00
Harald Bamberger cfe1307018 Merge branch 'feature-68530/Dashboard_Cleanup_Admin' 2026-03-25 15:37:34 +01:00
Harald Bamberger 5139c3e44e use array_replace_recursive instead of array_merge_recursive to prevent two scalar values being merged to an array 2026-03-25 15:15:05 +01:00
Harald Bamberger 627a52e3d1 Merge branch 'master' into feature-70376/Lohnguide 2026-03-25 09:36:27 +01:00
chfhtw 1951cd6fa8 split/rename dashboard api factories 2026-03-24 16:08:02 +01:00
chfhtw e3093bdf3f get magic funktionen (Mitarbeiter, Student) as dashboard presets 2026-03-24 15:15:36 +01:00
chfhtw b11d8d056a get access rights from permissionlib 2026-03-24 15:15:12 +01:00
chfhtw 3a4015eced dashboard useroverwrite: remove doubles in other funktionen 2026-03-24 15:15:04 +01:00
chfhtw aeb5d40840 rename api endpoints 2026-03-24 15:14:39 +01:00
Alexei Karpenko 49c712a5b6 Personalmeldung sws: rounding to 2 decimals 2026-03-24 13:57:23 +01:00
Harald Bamberger 46817b846a fix e.g. long lines of underscores in cms content 2026-03-24 13:29:20 +01:00
Harald Bamberger 8c75608eaf add menu entry for Dashboard Admin 2026-03-24 13:21:50 +01:00
chfhtw 2720ed9ffb timezone from global object 2026-03-24 11:00:09 +01:00
chfhtw 2fc392c084 refactor dashboards Preset->addWidgets to (single) Preset->addWidget 2026-03-23 16:05:22 +01:00
chfhtw ca630e94ae remove debug line 2026-03-23 15:46:13 +01:00
chfhtw 9cff50fa3b extract preset logic from dashboard admin api 2026-03-23 15:44:42 +01:00
chfhtw 3d7a6b1ad3 dashboard user api: empty -> check for false/null 2026-03-23 15:42:28 +01:00
chfhtw f15fd40636 dashboardlib bug: array <=> stdclass 2026-03-23 15:41:32 +01:00
chfhtw 054cf2f258 correct form validation & typo in api dashboard widget 2026-03-23 15:06:25 +01:00
chfhtw dc067a619b make widgets resizeable in dashboard admin 2026-03-23 14:07:31 +01:00
chfhtw 2a762fa4ab add renderers & timezone to dashboard admin for calendar widget 2026-03-23 13:22:30 +01:00
chfhtw ccade6ae0e rename dashboard admin controller and views 2026-03-23 11:47:28 +01:00
chfhtw 6971aed030 parsing happens in backend not frontend 2026-03-23 11:46:45 +01:00
chfhtw 60e556b2a8 wrong case 2026-03-23 11:33:45 +01:00
chfhtw 42fbbc5257 remove unused file 2026-03-23 11:28:31 +01:00
chfhtw d01dedb79c remove unused file 2026-03-23 11:23:01 +01:00
chfhtw 1972b461e7 replace controllers/dashboard/Config.php with controllers/api/frontend/v1/dashboard/User.php & controllers/api/frontend/v1/dashboard/DashboardAdmin.php 2026-03-23 11:21:15 +01:00
chfhtw e957926a4d replace controllers/dashboard/Widget.php with controllers/api/frontend/v1/dashboard/Widget.php 2026-03-23 10:57:43 +01:00
chfhtw bac2c13da3 viewData is mandatory so we dont need to load it if its not set 2026-03-23 10:44:39 +01:00
ma0068 b90c26412a DB update: new Organisationseinheittyp Programm 2026-03-20 13:06:16 +01:00
chfhtw 65c7ad2aac use correct error handling in FhcApi in case of success 2026-03-20 12:29:01 +01:00
chfhtw 126a2d3b7b add deepToRaw function to helpers/ObjectUtils 2026-03-20 11:23:35 +01:00
Harald Bamberger 60734f708e Merge branch 'master' into feature-68530/Dashboard_Cleanup_Admin 2026-03-19 16:20:06 +01:00
Harald Bamberger 9e6c15a10d Merge branch 'bug-76010/StudVW_Archivieren_Ausbildungsvertrag_Aufgenommene' 2026-03-19 15:41:44 +01:00
Harald Bamberger 14a8e2f001 Funktionen fett schreiben, die schon presets hinterlegt haben, demo aus views und Controller namen entfernen, preview hinzufuegen 2026-03-18 15:48:57 +01:00
ma0068 7603f8f12b Bugfix: use null instead of empty string, provide kuerzel 2026-03-18 14:32:09 +01:00
Harald Bamberger 059b13938e Merge branch 'master' into feature-70376/Lohnguide 2026-03-18 11:46:27 +01:00
Harald Bamberger a4f2502fe6 dashboard admin: funktionen sortieren, allgemein/general wieder hinzufuegen 2026-03-18 10:58:05 +01:00
Harald Bamberger 7c1762d467 Merge branch 'master' into feature-68530/Dashboard_Cleanup_Admin 2026-03-18 09:20:53 +01:00
Andreas Österreicher 96745525f1 Merge branch 'feature-71530/Error_beim_Archivieren_von_Diplomasupplement_STUDVW_Neu' 2026-03-16 11:28:56 +01:00
Andreas Österreicher 8d6e04ea77 Merge branch 'feature-61164/AbgabetoolQualityGates' 2026-03-16 11:09:52 +01:00
Andreas Österreicher d9e5acb52c Merge branch 'master' into feature-61164/AbgabetoolQualityGates 2026-03-16 11:08:44 +01:00
Johann Hoffmann 6ec32b0ca3 update tabulator persistence id 2026-03-16 11:05:48 +01:00
Johann Hoffmann 4778bb82c3 assistenz action buttons lefthand alignment & lower minwidth 2026-03-16 10:56:51 +01:00
Andreas Österreicher e20ff52f5b Merge branch 'feature-75417/pep_finetuning' 2026-03-16 09:08:10 +01:00
Andreas Österreicher df05af98d2 FHB - Markierung von Outgoing ohne Endedatum 2026-03-16 09:01:33 +01:00
Johann Hoffmann 5ef1dccfc9 added missing phrase c4noZuordnungBetreuerStudent 2026-03-12 14:58:35 +01:00
Johann Hoffmann 6d28b8986d Merge remote-tracking branch 'origin/master' into feature-61164/AbgabetoolQualityGates
# Conflicts:
#	public/js/components/Stv/Studentenverwaltung/Details/Projektarbeit/Projektbetreuer.js
2026-03-12 09:47:54 +01:00
Johann Hoffmann b43f1ec920 AbgabetoolAssistenz download latest uploaded file action button; UX changes Projektarbeit Tab Stv; fix stv form input bug after invalidation for selects; 2026-03-11 17:00:56 +01:00
Alexei Karpenko c3d20bb181 Personalmeldung Lehrgaenge: distributed sws among Lehrgang types (Zertifikat, Master etc...) 2026-03-11 12:37:28 +01:00
Werner Masik 6b816def31 add lohnguide to vertragsbestandteil SQL 2026-03-05 15:34:51 +01:00
Werner Masik 5fbcf588ed fix vertragsbestandteil lohnguide 2026-03-05 14:29:56 +01:00
Alexei Karpenko fc4e79c1f5 Personalmeldung: include Lehrgaenge in Lehre in legacy script 2026-03-04 11:12:58 +01:00
Werner Masik 41b2a6d1d4 added db migration for lohnguide 2026-03-04 10:53:30 +01:00
Werner Masik e054f1222b basic model and factory for lohnguide 2026-03-03 11:50:45 +01:00
Johann Hoffmann 56a6aa993e getMitarbeiterProjektarbeiten safeguard in case a person without any assigned betreuungen opens the page for some reason to avoid nasty confusing sql error messages from querying with empty parameters 2026-03-03 10:52:12 +01:00
Johann Hoffmann db75cd2f62 also skip email loop/relevant abgaben loop when every occurance is filtered out to avoid empty notification emails; 2026-03-03 10:42:47 +01:00
Cristina c57eb1b8de Adapted method getLvLeitung: filter Dummy and allow only active Benutzer/Person 2026-03-02 11:00:05 +01:00
ma0048 f3986688f2 selfoverview:
nur aktive kategorien anzeigen
uebersetzungen hinzugefuegt
2026-02-26 12:50:37 +01:00
ma0048 f1dbc6ab7d mc4 vorlage hinzugefuegt 2026-02-25 14:46:15 +01:00
Cristina d1015956d1 Merge branch 'master' into epic-56039/LV-Evaluierung 2026-02-25 10:54:29 +01:00
Cristina 726fce9fac Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-02-25 10:52:50 +01:00
Alexei Karpenko f068b56083 PlausiIssueProducer: removed debugging output and die 2026-02-18 22:30:29 +01:00
Alexei Karpenko fa7a125727 Studierendenverwaltung archivieren: error message when no studentlehrverband found 2026-02-18 21:18:57 +01:00
Cristina 27a91de5f6 Added app lvevaluierung to system.tbl_app 2026-01-22 15:51:05 +01:00
Cristina 7ccc26c878 Added lvevaluierung phrasen for STGL Übersichtsseite 2026-01-22 15:41:25 +01:00
Cristina 9ebc847e8e Added lvevaluierung phrasen for Lektoren Übersichtsseite 2026-01-22 14:59:00 +01:00
Cristina 511b04c1f8 Merge branch 'master' into epic-56039/LV-Evaluierung 2026-01-21 18:01:55 +01:00
Cristina ec90d35e02 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-01-21 18:01:24 +01:00
ma0068 4f104523ff - include directive primevue.tooltip
- refactor phrases to avoid timing problem with loading phrases of alert
2026-01-08 16:02:29 +01:00
ma0068 02153e469f Dashboard Admin Cleanup
- refactoring Api: FHC-API controller for Edit/Update, widgets and presets
- delete dashboard with Prompt
- phrases
2025-12-19 11:39:31 +01:00
73 changed files with 9741 additions and 1281 deletions
+8 -1
View File
@@ -208,7 +208,14 @@ $config['navigation_header'] = array(
'expand' => true,
'sort' => 30,
'requiredPermissions' => 'lehre/anrechnungszeitfenster:rw'
)
),
'dashboardadmin' => array(
'link' => site_url('dashboard/Admin'),
'description' => 'Dashboard Admin',
'expand' => true,
'sort' => 40,
'requiredPermissions' => 'dashboard/admin:r'
)
)
)
)
@@ -511,10 +511,11 @@ class Abgabe extends FHCAPI_Controller
return $projektarbeit->projektarbeit_id;
};
$projektarbeiten_ids = array_map($mapFunc, $projektarbeiten->retval);
$ret = $this->ProjektarbeitModel->getProjektarbeitenAbgabetermine($projektarbeiten_ids);
$projektabgaben = $this->getDataOrTerminateWithError($ret, 'general');
if(count($projektarbeiten_ids) > 0) {
$ret = $this->ProjektarbeitModel->getProjektarbeitenAbgabetermine($projektarbeiten_ids);
$projektabgaben = $this->getDataOrTerminateWithError($ret, 'general');
}
forEach($projektarbeiten->retval as $pa) {
@@ -846,9 +847,10 @@ class Abgabe extends FHCAPI_Controller
private function getProjektbetreuerEmailByProjektarbeitID($projektarbeit_id) {
$this->load->model('education/Projektarbeit_model', 'ProjektarbeitModel');
$result = $this->ProjektarbeitModel->getProjektbetreuerEmail($projektarbeit_id);
$email = $this->getDataOrTerminateWithError($result, 'general');
return $email[0]->uid ? $email[0]->uid.'@'.DOMAIN : $email[0]->private_email;
if(count($result->retval) > 0) {
$email = getData($result);
return $email[0]->uid ? $email[0]->uid.'@'.DOMAIN : $email[0]->private_email;
} else return '';
}
@@ -208,7 +208,6 @@ class Documents extends FHCAPI_Controller
$this->load->model('system/Vorlage_model', 'VorlageModel');
$result = $this->VorlageModel->load($xsl);
$this->addMeta("ress", $result);
$vorlage = current($this->getDataOrTerminateWithError($result));
if (!$vorlage)
show_404();
@@ -221,7 +220,7 @@ class Documents extends FHCAPI_Controller
'gedruckt' => true,
'insertamum' => date('c'),
'insertvon' => getAuthUID(),
'uid' => $this->input->post_get('uid') ?: '',
'uid' => $this->input->post_get('uid') ?: null,
'archiv' => true,
'signiert' => !!$sign_user,
'stud_selfservice' => $vorlage->stud_selfservice
@@ -251,6 +250,9 @@ class Documents extends FHCAPI_Controller
'studiensemester_kurzbz' => $ss,
'student_uid' => $akteData['uid']
]);
if (!hasData($result)) $this->terminateWithError($this->p->t("stv", "error_noLehrverbandAssigned"));
$res = current($this->getDataOrTerminateWithError($result));
$studiengang_kz = $res->studiengang_kz;
@@ -332,6 +334,7 @@ class Documents extends FHCAPI_Controller
if ($prestudent_id) {
$this->load->model('crm/prestudent_model', 'PrestudentModel');
$this->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addSelect('tbl_prestudent.*, UPPER(typ || kurzbz) AS kuerzel');
$result = $this->PrestudentModel->load($prestudent_id);
$prestudent = current($this->getDataOrTerminateWithError($result));
@@ -0,0 +1,121 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about addresses
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class Board extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'list' => 'dashboard/admin:r',
'create' => 'dashboard/admin:rw',
'update' => 'dashboard/admin:rw',
'delete' => 'dashboard/admin:rw'
]);
// Models
$this->load->model('dashboard/Dashboard_model', 'DashboardModel');
}
public function list()
{
$result = $this->DashboardModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
public function create()
{
$dashboard_kurzbz = $this->input->post('dashboard_kurzbz');
$result = $this->DashboardModel->insert([
'dashboard_kurzbz' => $dashboard_kurzbz
]);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
public function update()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard_id', 'Dashboard ID', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_id = $this->input->post('dashboard_id');
$dashboard_kurzbz = $this->input->post('dashboard_kurzbz');
$beschreibung = $this->input->post('beschreibung');
$result = $this->DashboardModel->update([
'dashboard_id' => $dashboard_id
], [
'dashboard_kurzbz' => $dashboard_kurzbz,
'beschreibung' => $beschreibung
]);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
public function delete()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard_id', 'Dashboard ID', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_id = $this->input->post('dashboard_id');
//delete all presets
$this->load->model('dashboard/Dashboard_Preset_model', 'DashboardPresetModel');
$result = $this->DashboardPresetModel->delete([
'dashboard_id' => $dashboard_id
]);
$this->getDataOrTerminateWithError($result);
//delete all widgets
$this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel');
$result = $this->DashboardWidgetModel->delete([
'dashboard_id' => $dashboard_id
]);
$this->getDataOrTerminateWithError($result);
$result = $this->DashboardModel->delete($dashboard_id);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($result);
}
}
@@ -0,0 +1,200 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about addresses
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class Preset extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'list' => 'dashboard/admin:r',
'getBatch' => 'dashboard/admin:r',
'addWidget' => 'dashboard/admin:rw',
'removeWidget' => 'dashboard/admin:rw'
]);
// Load language phrases
$this->loadPhrases([
'ui'
]);
// Libraries
$this->load->library('dashboard/DashboardLib');
// Models
$this->load->model('ressource/Funktion_model', 'FunktionModel');
}
public function list($dashboard_kurzbz)
{
$sql = "
WITH
dashboard_presets AS (
SELECT
*
FROM
dashboard.tbl_dashboard_preset dp
JOIN
dashboard.tbl_dashboard d ON d.dashboard_id = dp.dashboard_id
WHERE
d.dashboard_kurzbz = {$this->db->escape($dashboard_kurzbz)}
),
general AS (
SELECT
'general' AS funktion_kurzbz,
'Allgemein' AS beschreibung
)
(
SELECT
f.funktion_kurzbz,
f.beschreibung,
COUNT(p.preset_id) AS has_preset
FROM
general f
LEFT JOIN
dashboard_presets p ON p.funktion_kurzbz IS NULL
GROUP BY
f.funktion_kurzbz, f.beschreibung
)
UNION ALL
(
SELECT
f.funktion_kurzbz,
f.beschreibung,
COUNT(p.preset_id) AS has_preset
FROM
public.tbl_funktion f
LEFT JOIN
dashboard_presets p ON p.funktion_kurzbz = f.funktion_kurzbz
GROUP BY
f.funktion_kurzbz, f.beschreibung
ORDER BY
f.beschreibung ASC
)
";
$result = $this->FunktionModel->execReadOnlyQuery($sql);
$funktionen = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($funktionen);
}
public function getBatch()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('db', 'Dashboard', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$db = $this->input->post('db');
$funktionen = $this->input->post('funktionen') ?: [];
$result = [];
foreach ($funktionen as $funktion) {
$conf = $this->dashboardlib->getPreset($db, $funktion);
if ($conf) {
$preset = json_decode($conf->preset, true);
if (!isset($preset[$funktion]) || !isset($preset[$funktion]['widgets']))
$result[$funktion] = [];
else
$result[$funktion] = $preset[$funktion]['widgets'];
} else {
$result[$funktion] = [];
}
}
return $this->terminateWithSuccess($result);
}
public function addWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard', 'Dashboard', 'required');
$this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required');
$this->form_validation->set_rules('widget[widget]', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_kurzbz = $this->input->post('dashboard');
$funktion_kurzbz = $this->input->post('funktion_kurzbz');
$widget = $this->input->post('widget');
if (!isset($widget['widgetid']))
$widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz);
$preset = $this->dashboardlib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz);
$preset_decoded = json_decode($preset->preset, true);
$this->dashboardlib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, [$widget]);
$preset->preset = json_encode($preset_decoded);
$result = $this->dashboardlib->insertOrUpdatePreset($preset);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($widget['widgetid']);
}
public function removeWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('db', 'Dashboard', 'required');
$this->form_validation->set_rules('funktion_kurzbz', 'Funktion', 'required');
$this->form_validation->set_rules('widgetid', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$dashboard_kurzbz = $this->input->post('db');
$funktion_kurzbz = $this->input->post('funktion_kurzbz');
$widgetid = $this->input->post('widgetid');
$preset = $this->dashboardlib->getPreset($dashboard_kurzbz, $funktion_kurzbz);
if (!$preset)
show_404();
$preset_decoded = json_decode($preset->preset, true);
if (!$this->dashboardlib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid))
show_404();
$preset->preset = json_encode($preset_decoded);
$result = $this->dashboardlib->insertOrUpdatePreset($preset);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess(array('msg' => $this->p->t('dashboard', 'success_savePreset')));
}
}
@@ -0,0 +1,159 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about the users dashboard
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class User extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'get' => 'dashboard/benutzer:r',
'addWidget' => 'dashboard/benutzer:rw',
'removeWidget' => 'dashboard/benutzer:rw'
]);
// Libraries
$this->load->library('dashboard/DashboardLib');
// Models
$this->load->model('ressource/Funktion_model', 'FunktionModel');
}
public function get($dashboard_kurzbz)
{
$dashboard = $this->dashboardlib->getDashboardByKurzbz($dashboard_kurzbz);
if (!$dashboard)
show_404();
$uid = $this->authlib->getAuthObj()->username;
/*$mergedconfig = $this->dashboardlib->getMergedConfig($dashboard->dashboard_id, $uid);
$this->terminateWithSuccess([
'general' => call_user_func_array(
'array_merge_recursive',
$mergedconfig
)
]);*/
$defaultconfig = $this->dashboardlib->getDefaultConfig($dashboard->dashboard_id);
$userconfig = $this->dashboardlib->getUserConfig($dashboard->dashboard_id, $uid);
$defaultconfig_squashed = $defaultconfig ? call_user_func_array('array_replace_recursive', $defaultconfig) : [];
$userconfig_squashed = $userconfig ? call_user_func_array('array_replace_recursive', $userconfig) : [];
$mergedconfig = array_replace_recursive($defaultconfig_squashed, $userconfig_squashed);
$this->terminateWithSuccess([
DashboardLib::SECTION_IF_FUNKTION_KURZBZ_IS_NULL => $mergedconfig
]);
}
public function addWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard', 'Dashboard', 'required');
$this->form_validation->set_rules('widget[widget]', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$widget = $this->input->post('widget');
$dashboard_kurzbz = $this->input->post('dashboard');
$uid = $this->authlib->getAuthObj()->username;
if (!isset($widget['widgetid']))
$widget['widgetid'] = $this->dashboardlib->generateWidgetId($dashboard_kurzbz);
$override = $this->dashboardlib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid);
$override_decoded = json_decode($override->override, true);
if (!isset($override_decoded['general']) || !is_array($override_decoded['general']))
$override_decoded['general'] = [];
if (!isset($override_decoded['general']['widgets']))
$override_decoded['general']['widgets'] = [];
$override_decoded['general']['widgets'][$widget['widgetid']] = $widget;
// NOTE(chris): remove doubles in other funktionen
foreach ($override_decoded as $funktion => $array) {
if ($funktion == 'general')
continue;
if (isset($array['widgets']) && isset($array['widgets'][$widget['widgetid']]))
unset($override_decoded[$funktion]['widgets'][$widget['widgetid']]);
}
$override->override = json_encode($override_decoded);
$result = $this->dashboardlib->insertOrUpdateOverride($override);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($widget['widgetid']);
}
public function removeWidget()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard', 'Dashboard', 'required');
$this->form_validation->set_rules('widget', 'Widget', 'required');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$widget_id = $this->input->post('widget');
$dashboard_kurzbz = $this->input->post('dashboard');
$uid = $this->authlib->getAuthObj()->username;
$override = $this->dashboardlib->getOverride($dashboard_kurzbz, $uid);
if (!$override)
show_404();
$override_decoded = json_decode($override->override, true);
foreach (array_keys($override_decoded) as $k) {
if (!isset($override_decoded[$k]["widgets"])) {
unset($override_decoded[$k]);
continue;
}
if (isset($override_decoded[$k]["widgets"][$widget_id])) {
unset($override_decoded[$k]["widgets"][$widget_id]);
}
if (!$override_decoded[$k]["widgets"]) {
unset($override_decoded[$k]);
}
}
$override->override = json_encode($override_decoded);
$result = $this->dashboardlib->insertOrUpdateOverride($override);
$this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess();
}
}
@@ -0,0 +1,137 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
* This controller operates between (interface) the JS (GUI) and the back-end
* Provides data to the ajax get calls about the users dashboard
* This controller works with JSON calls on the HTTP GET or POST and the output is always JSON
*/
class Widget extends FHCAPI_Controller
{
public function __construct()
{
parent::__construct([
'get' => ['dashboard/benutzer:r', 'dashboard/admin:r'],
'list' => 'dashboard/admin:r',
'listAllowed' => ['dashboard/benutzer:rw', 'dashboard/admin:r'],
'setAllowed' => 'dashboard/admin:rw'
]);
// Libraries
$this->load->library('dashboard/DashboardLib');
// Models
$this->load->model('dashboard/Widget_model', 'WidgetModel');
}
public function get($id)
{
$result = $this->WidgetModel->load($id);
$widget = $this->getDataOrTerminateWithError($result);
if (!$widget)
return $this->terminateWithSuccess([
"widget_id" => 0,
"widget_kurzbz" => "notfound",
"arguments" => [
"className" => 'alert-danger',
"title" => 'Widget Not Found',
"msg" => 'The widget with the id ' . $id . ' could not be found'
],
"setup" => [
"name" => 'Widget Not Found',
"file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'),
"width" => 1,
"height" => 1
]
]);
$widget = current($widget);
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
$this->terminateWithSuccess($widget);
}
public function list($dashboard)
{
$result = $this->WidgetModel->getWithAllowedForDashboard($dashboard);
$widgets = $this->getDataOrTerminateWithError($result);
$widgets = array_map(function ($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $widgets);
$this->terminateWithSuccess($widgets);
}
public function listAllowed($dashboard)
{
$result = $this->WidgetModel->getForDashboard($dashboard);
$widgets = $this->getDataOrTerminateWithError($result);
$widgets = array_map(function ($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $widgets);
$this->terminateWithSuccess($widgets);
}
public function setAllowed()
{
$this->load->library('form_validation');
$this->form_validation->set_rules('dashboard_id', 'Dashboard', 'required');
$this->form_validation->set_rules('widget_id', 'Widget', 'required');
$this->form_validation->set_rules('allowed', 'Allowed', 'is_bool');
if (!$this->form_validation->run())
$this->terminateWithValidationErrors($this->form_validation->error_array());
$data = [
'dashboard_id' => $this->input->post('dashboard_id'),
'widget_id' => $this->input->post('widget_id')
];
$this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel');
if ($this->input->post('allowed'))
$result = $this->DashboardWidgetModel->insert($data);
else
$result = $this->DashboardWidgetModel->delete($data);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
}
@@ -42,14 +42,22 @@ class Messages extends FHCAPI_Controller
]);
}
public function getMessages($id, $type_id, $size, $page)
public function getMessages($id, $type_id, $size=null, $page=null)
{
if($type_id != 'person_id'){
$id = $this->_getPersonId($id, $type_id);
}
$offset = $size * ($page - 1);
$limit = $size;
if(!(is_null($size) && is_null($page)))
{
$offset = $size * ($page - 1);
$limit = $size;
}
else
{
$offset = null;
$limit = null;
}
$result = $this->MessageModel->getMessagesForTable($id, $offset, $limit);
@@ -626,7 +626,7 @@ class Students extends FHCAPI_Controller
$this->addFilter($studiensemester_kurzbz);
$result = $this->PrestudentModel->loadWhere($where);
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
@@ -851,40 +851,44 @@ class Students extends FHCAPI_Controller
$stdsemEsc = $studiensemester_kurzbz ? $this->PrestudentModel->escape($studiensemester_kurzbz) : 'NULL';
$this->load->config('stv');
$tags = $this->config->item('stv_prestudent_tags');
$whereTags = '';
if (is_array($tags) && !isEmptyArray($tags)) {
$tags = array_keys($tags);
if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED)
{
$tags = $this->config->item('stv_prestudent_tags');
foreach ($tags as $key => $tag) {
$tags[$key] = $this->db->escape($tag);
$whereTags = '';
if (is_array($tags) && !isEmptyArray($tags)) {
$tags = array_keys($tags);
foreach ($tags as $key => $tag) {
$tags[$key] = $this->db->escape($tag);
}
$whereTags = " AND nt.typ_kurzbz IN (" . implode(",", $tags) . ")";
}
$whereTags = " AND nt.typ_kurzbz IN (" . implode(",", $tags) . ")";
$subQueryTag = "
(
SELECT
tag.prestudent_id,
COALESCE(json_agg(tag ORDER BY tag.done), '[]'::json) AS tags
FROM (
SELECT DISTINCT ON (n.notiz_id)
n.notiz_id AS id,
nt.typ_kurzbz,
array_to_json(nt.bezeichnung_mehrsprachig)->>0 AS beschreibung,
n.text AS notiz,
nt.style,
n.erledigt AS done,
nz.prestudent_id
FROM public.tbl_notizzuordnung AS nz
JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id
JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz "
. $whereTags .
"
) AS tag
GROUP BY tag.prestudent_id
) AS tag_data_agg
";
}
$subQueryTag = "
(
SELECT
tag.prestudent_id,
COALESCE(json_agg(tag ORDER BY tag.done), '[]'::json) AS tags
FROM (
SELECT DISTINCT ON (n.notiz_id)
n.notiz_id AS id,
nt.typ_kurzbz,
array_to_json(nt.bezeichnung_mehrsprachig)->>0 AS beschreibung,
n.text AS notiz,
nt.style,
n.erledigt AS done,
nz.prestudent_id
FROM public.tbl_notizzuordnung AS nz
JOIN public.tbl_notiz AS n ON nz.notiz_id = n.notiz_id
JOIN public.tbl_notiz_typ AS nt ON n.typ = nt.typ_kurzbz "
. $whereTags .
"
) AS tag
GROUP BY tag.prestudent_id
) AS tag_data_agg
";
$this->PrestudentModel->addJoin('public.tbl_studiengang stg', 'studiengang_kz', 'LEFT');
$this->PrestudentModel->addJoin('public.tbl_person p', 'person_id');
@@ -907,11 +911,17 @@ class Students extends FHCAPI_Controller
AND ps.studiensemester_kurzbz=public.get_stdsem_prestudent(tbl_prestudent.prestudent_id, ' . $stdsemEsc . ')
AND ps.ausbildungssemester=public.get_absem_prestudent(tbl_prestudent.prestudent_id, ' . $stdsemEsc . ')', 'LEFT');
$this->PrestudentModel->addJoin($subQueryTag, 'tag_data_agg.prestudent_id = tbl_prestudent.prestudent_id', 'LEFT');
if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED)
{
$this->PrestudentModel->addJoin($subQueryTag, 'tag_data_agg.prestudent_id = tbl_prestudent.prestudent_id', 'LEFT');
}
$this->PrestudentModel->addSelect("b.uid");
$this->PrestudentModel->addSelect('tag_data_agg.tags');
if(defined('STV_TAGS_ENABLED') && STV_TAGS_ENABLED)
{
$this->PrestudentModel->addSelect('tag_data_agg.tags');
}
$this->PrestudentModel->addSelect('titelpre');
$this->PrestudentModel->addSelect('nachname');
$this->PrestudentModel->addSelect('vorname');
@@ -0,0 +1,52 @@
<?php
/**
* 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 <https://www.gnu.org/licenses/>.
*/
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*/
class Admin extends Auth_Controller
{
/**
* Constructor
*/
public function __construct()
{
// Set required permissions
parent::__construct(
array(
'index' => 'dashboard/admin:rw',
'preview' => 'dashboard/admin:r',
)
);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function index()
{
$this->load->view('dashboard/admin.php', []);
}
public function preview($dashboard_kurzbz = 'CIS')
{
$this->load->view('dashboard/preview.php', [
'dashboard_kurzbz' => $dashboard_kurzbz
]);
}
}
-76
View File
@@ -1,76 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
class Api extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => 'dashboard/admin:rw',
'getNews' => 'dashboard/benutzer:r',
'getAmpeln' => 'dashboard/benutzer:r',
)
);
$this->load->library('AuthLib', null, 'AuthLib');
$this->_setAuthUID();
}
public function index()
{
echo 'Dashboard API Controller';
}
/**
* Get News.
*/
public function getNews()
{
$limit = $this->input->get('limit');
$this->load->model('content/News_model', 'NewsModel');
$result = $this->NewsModel->getAll($limit);
if (hasData($result))
{
$this->outputJson(getData($result), REST_Controller::HTTP_OK);
}
else
{
$this->terminateWithJsonError('fehler entdeckt');
}
}
/**
* Get Ampeln.
*/
public function getAmpeln()
{
$this->load->model('content/Ampel_model', 'AmpelModel');
$result = $this->AmpelModel->getByUser($this->_uid);
if (hasData($result))
{
$this->outputJson(getData($result), REST_Controller::HTTP_OK);
}
else
{
$this->terminateWithJsonError('fehler entdeckt');
}
}
/**
* Retrieve the UID of the logged user and checks if it is valid
*/
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid) show_error('User authentification failed');
}
}
@@ -1,216 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
/**
* Description of Config
*
* @author bambi
*/
class Config extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => 'dashboard/benutzer:r',
'dummy' => 'dashboard/benutzer:r',
'genWidgetId' => 'dashboard/benutzer:rw',
'addWidgetsToPreset' => 'dashboard/admin:rw',
'removeWidgetFromPreset' => 'dashboard/admin:rw',
'addWidgetsToUserOverride' => 'dashboard/benutzer:rw',
'removeWidgetFromUserOverride' => 'dashboard/benutzer:rw',
'funktionen' => 'dashboard/admin:r',
'preset' => 'dashboard/admin:r',
'presetBatch' => 'dashboard/admin:r'
)
);
$this->load->library('dashboard/DashboardLib', null, 'DashboardLib');
$this->load->library('AuthLib', null, 'AuthLib');
$this->load->model('ressource/Funktion_model', 'FunktionModel');
}
public function index()
{
$dashboard_kurzbz = $this->input->get('db');
$uid = $this->AuthLib->getAuthObj()->username;
$dashboard = $this->DashboardLib->getDashboardByKurzbz($dashboard_kurzbz);
if(!$dashboard) {
http_response_code(404);
$this->terminateWithJsonError(array(
'error' => 'Dashboard ' . $dashboard_kurzbz . ' not found.'
));
}
$mergedconfig = $this->DashboardLib->getMergedConfig($dashboard->dashboard_id, $uid);
$this->outputJsonSuccess($mergedconfig);
}
public function genWidgetId()
{
$dashboard_kurzbz = $this->input->get('db');
$widgetid = $this->DashboardLib->generateWidgetId($dashboard_kurzbz);
$this->outputJsonSuccess(array(
'widgetid' => $widgetid
));
}
public function addWidgetsToPreset()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$preset = $this->DashboardLib->getPresetOrCreateEmptyPreset($dashboard_kurzbz, $funktion_kurzbz);
$preset_decoded = json_decode($preset->preset, true);
$this->DashboardLib->addWidgetsToWidgets($preset_decoded, $dashboard_kurzbz, $funktion_kurzbz, $input->widgets);
$preset->preset = json_encode($preset_decoded);
$result = $this->DashboardLib->insertOrUpdatePreset($preset);
if (isError($result)) {
http_response_code(500);
$this->terminateWithJsonError('preset could not be saved');
}
$this->outputJsonSuccess(array('msg' => 'preset successfully stored.', 'data' => $preset_decoded));
}
public function removeWidgetFromPreset()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$widgetid = $input->widgetid;
$preset = $this->DashboardLib->getPreset($dashboard_kurzbz, $funktion_kurzbz);
if ($preset === null) {
http_response_code(404);
$this->terminateWithJsonError('preset for dashboard ' . $dashboard_kurzbz . ' and funktion ' . $funktion_kurzbz . ' not found.');
}
$preset_decoded = json_decode($preset->preset, true);
if (!$this->DashboardLib->removeWidgetFromWidgets($preset_decoded, $funktion_kurzbz, $widgetid))
{
http_response_code(404);
$this->terminateWithJsonError('widgetid ' . $widgetid . ' not found');
}
$preset->preset = json_encode($preset_decoded);
$result = $this->DashboardLib->insertOrUpdatePreset($preset);
if (isError($result))
{
http_response_code(500);
$this->terminateWithJsonError('failed to remove widget');
}
$this->outputJsonSuccess(array('msg' => 'preset successfully updated.'));
}
public function addWidgetsToUserOverride()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$uid = $this->AuthLib->getAuthObj()->username;
$override = $this->DashboardLib->getOverrideOrCreateEmptyOverride($dashboard_kurzbz, $uid);
$override_decoded = json_decode($override->override, true);
$this->DashboardLib->addWidgetsToWidgets($override_decoded, $dashboard_kurzbz, $funktion_kurzbz, $input->widgets);
$override->override = json_encode($override_decoded);
$result = $this->DashboardLib->insertOrUpdateOverride($override);
if (isError($result)) {
http_response_code(500);
$this->terminateWithJsonError('override could not be saved');
}
$this->outputJsonSuccess(array('msg' => 'override successfully stored.', 'data' => $override_decoded));
}
public function removeWidgetFromUserOverride()
{
$input = json_decode($this->input->raw_input_stream);
$dashboard_kurzbz = $input->db;
$funktion_kurzbz = $input->funktion_kurzbz;
$uid = $this->AuthLib->getAuthObj()->username;
$widgetid = $input->widgetid;
$override = $this->DashboardLib->getOverride($dashboard_kurzbz, $uid);
if (empty($override)) {
http_response_code(404);
$this->terminateWithJsonError('userconfig for dashboard ' . $dashboard_kurzbz . ' not found.');
}
$override_decoded = json_decode($override->override, true);
if (!$this->DashboardLib->removeWidgetFromWidgets($override_decoded, $funktion_kurzbz, $widgetid))
{
http_response_code(404);
$this->terminateWithJsonError('widgetid ' . $widgetid . ' not found');
}
$override->override = json_encode($override_decoded);
$result = $this->DashboardLib->insertOrUpdateOverride($override, $uid);
if (isError($result))
{
http_response_code(500);
$this->terminateWithJsonError('failed to remove widget');
}
$this->outputJsonSuccess(array('msg' => 'override successfully updated.'));
}
public function funktionen()
{
$funktionen = $this->FunktionModel->load();
if (isError($funktionen)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($funktionen)
]);
}
return $this->outputJsonSuccess(getData($funktionen) ?: []);
}
public function preset()
{
$db = $this->input->get('db');
$funktion = $this->input->get('funktion');
$conf = $this->DashboardLib->getPreset($db, $funktion);
if (!$conf)
return $this->outputJsonSuccess(['widgets' => [$funktion => []]]);
return $this->outputJsonSuccess(json_decode($conf->preset, true));
}
public function presetBatch()
{
$db = $this->input->get('db');
$funktionen = $this->input->get('funktionen');
$result = [];
foreach ($funktionen as $funktion) {
$conf = $this->DashboardLib->getPreset($db, $funktion);
if ($conf)
{
$preset = json_decode($conf->preset, true);
if (!isset($preset[$funktion]) || !isset($preset[$funktion]['widgets']))
$result[$funktion] = [];
else
$result[$funktion] = $preset[$funktion]['widgets'];
}
else
$result[$funktion] = [];
}
return $this->outputJsonSuccess($result);
}
}
@@ -1,86 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
/**
* Description of Widget
*
* @author chris
*/
class Dashboard extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => 'dashboard/admin:r',
'create' => 'dashboard/admin:rw',
'update' => 'dashboard/admin:rw',
'delete' => 'dashboard/admin:rw'
)
);
$this->load->library('dashboard/DashboardLib', null, 'DashboardLib');
$this->load->model('dashboard/Dashboard_model', 'DashboardModel');
}
public function index()
{
$result = $this->DashboardModel->load();
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
public function create()
{
$input = $this->getPostJSON();
$result = $this->DashboardModel->insert($input);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
public function update()
{
$input = $this->getPostJSON();
$result = $this->DashboardModel->update($input->dashboard_id, $input);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
public function delete()
{
$input = $this->getPostJSON();
$result = $this->DashboardModel->delete($input->dashboard_id);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result) ?: []);
}
}
@@ -1,58 +0,0 @@
<?php
if (! defined('BASEPATH')) exit('No direct script access allowed');
/**
*/
class DashboardDemo extends Auth_Controller
{
private $_uid; // uid of the logged user
/**
* Constructor
*/
public function __construct()
{
// Set required permissions
parent::__construct(
array(
'index' => 'dashboard/benutzer:r',
'admin' => 'dashboard/admin:rw'
)
);
$this->load->library('AuthLib');
$this->load->library('WidgetLib');
$this->_setAuthUID(); // sets property uid
$this->setControllerId(); // sets the controller id
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function index()
{
$this->load->view('dashboard/dashboard_demo.php', []);
}
// -----------------------------------------------------------------------------------------------------------------
// Public methods
public function admin()
{
$this->load->view('dashboard/dashboard_demo_admin.php', []);
}
// -----------------------------------------------------------------------------------------------------------------
// Private methods
/**
* Retrieve the UID of the logged user and checks if it is valid
*/
private function _setAuthUID()
{
$this->_uid = getAuthUID();
if (!$this->_uid) show_error('User authentification failed');
}
}
@@ -1,134 +0,0 @@
<?php
defined('BASEPATH') || exit('No direct script access allowed');
/**
* Description of Widget
*
* @author chris
*/
class Widget extends Auth_Controller
{
public function __construct()
{
parent::__construct(
array(
'index' => ['dashboard/benutzer:r', 'dashboard/admin:r'],
'getAll' => 'dashboard/admin:r',
'getWidgetsForDashboard' => ['dashboard/benutzer:rw', 'dashboard/admin:r'],
'setAllowed' => 'dashboard/admin:rw'
)
);
$this->load->library('dashboard/DashboardLib', null, 'DashboardLib');
$this->load->model('dashboard/Widget_model', 'WidgetModel');
$this->load->model('dashboard/Dashboard_Widget_model', 'DashboardWidgetModel');
}
public function index()
{
$widget_id = $this->input->get('id');
$widget = $this->WidgetModel->load($widget_id);
if (isError($widget) || !getData($widget))
return $this->outputJsonSuccess([
"widget_id" => 0,
"widget_kurzbz" => "notfound",
"arguments" => [
"className" => 'alert-danger',
"title" => 'Widget Not Found',
"msg" => 'The widget with the id ' . $widget_id . ' could not be found'
],
"setup" => [
"name" => 'Widget Not Found',
"file" => absoluteJsImportUrl('public/js/components/DashboardWidget/Default.js'),
"width" => 1,
"height" => 1
]
]);
$widget = current(getData($widget));
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $this->outputJsonSuccess($widget);
}
public function getAll()
{
$dashboard_id = $this->input->get('dashboard_id');
$result = $this->WidgetModel->getWithAllowedForDashboard($dashboard_id);
if (isError($result))
return $this->outputJsonError(getError($result));
$tmpwidgets = getData($result) ?: [];
$widgets = array_map(function($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $tmpwidgets);
$this->outputJsonSuccess($widgets);
}
public function getWidgetsForDashboard()
{
$db = $this->input->get('db');
$result = $this->WidgetModel->getForDashboard($db);
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
$tmpwidgets = getData($result) ?: [];
$widgets = array_map(function($widget) {
$widget->arguments = json_decode($widget->arguments);
$tmpsetup = json_decode($widget->setup);
$tmpsetup->file = absoluteJsImportUrl($tmpsetup->file);
$widget->setup = $tmpsetup;
return $widget;
}, $tmpwidgets);
$this->outputJsonSuccess($widgets);
}
public function setAllowed()
{
$input = $this->getPostJSON();
$dashboard_id = $input->dashboard_id;
$widget_id = $input->widget_id;
$action = $input->action;
if ($action == 'add') {
$result = $this->DashboardWidgetModel->insert([
'dashboard_id' => $dashboard_id,
'widget_id' => $widget_id
]);
} elseif ($action == 'delete') {
$result = $this->DashboardWidgetModel->delete([
'dashboard_id' => $dashboard_id,
'widget_id' => $widget_id
]);
} else {
http_response_code(404); // TODO(chris): 400?
$this->terminateWithJsonError([
'error' => 'action value invalid'
]);
}
if (isError($result)) {
http_response_code(404);
$this->terminateWithJsonError([
'error' => getError($result)
]);
}
return $this->outputJsonSuccess(getData($result));
}
}
@@ -495,6 +495,10 @@ class AbgabetoolJob extends JOB_Controller
// get all new or changed termine in interval
$result = $this->_ci->PaabgabeModel->findAbgabenNewOrUpdatedSince($interval, $relevantTypes);
$retval = getData($result);
if(!$retval) {
$this->_ci->logInfo("Keine Emails an Betreuer über neue oder veränderte Termine versandt");
return;
}
// group changed/new abgaben for projektarbeiten
$projektarbeiten = [];
@@ -557,6 +561,8 @@ class AbgabetoolJob extends JOB_Controller
$anredeFillString = $data->anrede == "Herr" ? "r" : "";
$fullFormattedNameString = $data->first;
$relevantCounter = 0; // workaround to check if a betreuer needs to have any notification about relevant
// abgaben at all to avoid sending empty emails since we filter on certain conditions
forEach($tupelArr as $tupel) {
$projektarbeit_id = $tupel[0];
$betreuerRow = $tupel[1];
@@ -575,6 +581,8 @@ class AbgabetoolJob extends JOB_Controller
continue;
}
$relevantCounter++;
// format the Student Name
$s = $relevantAbgaben[0];
$nameParts = [];
@@ -633,6 +641,11 @@ class AbgabetoolJob extends JOB_Controller
// done with building the change list, now send it
$betreuerRow = $tupelArr[0][1];
if($relevantCounter == 0) {
$this->_ci->logInfo('No Relevant Abgaben to notify Betreuer PersonID: "'.$betreuerRow->person_id.'".');
continue;
}
$path = $this->_ci->config->item('URL_MITARBEITER');
$url = CIS_ROOT.$path;
+16 -1
View File
@@ -50,6 +50,7 @@ class PermissionLib
const LOGINAS_PERSONIDS_BLACKLIST = 'permission_loginas_personids_blacklist';
private $_ci; // CI instance
private $access_rights; // current users access rights
private static $bb; // benutzerberechtigung
/**
@@ -61,6 +62,8 @@ class PermissionLib
// Loads CI instance
$this->_ci =& get_instance();
$this->access_rights = null;
$this->_ci->config->load('permission'); // Loads permission configuration
// If it's NOT called from command line
@@ -69,8 +72,10 @@ class PermissionLib
// API Caller rights initialization
$authObj = $this->_ci->authlib->getAuthObj();
self::$bb = new benutzerberechtigung();
if ($authObj)
if ($authObj) {
self::$bb->getBerechtigungen($authObj->{AuthLib::AO_USERNAME});
$this->access_rights = self::$bb->berechtigungen;
}
}
}
@@ -340,6 +345,16 @@ class PermissionLib
}
}
/**
* Returns the access rights for the current user
*
* @return array|null
*/
public function getAccessRights()
{
return $this->access_rights;
}
//------------------------------------------------------------------------------------------------------------------
// Private methods
@@ -49,7 +49,7 @@ class DashboardLib
public function getMergedConfig($dashboard_id, $uid)
{
$defaultconfig = $this->getDefaultConfig($dashboard_id, $uid);
$defaultconfig = $this->getDefaultConfig($dashboard_id);
$userconfig = $this->getUserConfig($dashboard_id, $uid);
$mergedconfig = array_replace_recursive($defaultconfig, $userconfig);
@@ -57,14 +57,31 @@ class DashboardLib
return $mergedconfig;
}
public function getDefaultConfig($dashboard_id, $uid)
public function getDefaultConfig($dashboard_id)
{
$res_presets = $this->_ci->DashboardPresetModel->getPresets($dashboard_id, $uid);
$funktion_kurzbzs = [];
$rights = $this->_ci->permissionlib->getAccessRights();
if ($rights)
$funktion_kurzbzs = array_unique(array_map(function ($right) {
return $right->funktion_kurzbz;
}, $rights));
$this->_ci->DashboardPresetModel->db
->group_start()
->where_in('funktion_kurzbz', $funktion_kurzbzs)
->or_where('funktion_kurzbz IS NULL')
->group_end();
$this->_ci->DashboardPresetModel->addOrder('funktion_kurzbz', 'DESC');
$result = $this->_ci->DashboardPresetModel->loadWhere([
'dashboard_id' => $dashboard_id
]);
$defaultconfig = array();
if (hasData($res_presets))
if (hasData($result))
{
$presets = getData($res_presets);
$presets = getData($result);
foreach ($presets as $presetobj)
{
$preset = json_decode($presetobj->preset, true);
@@ -137,8 +154,10 @@ class DashboardLib
$dashboard = $this->getDashboardByKurzbz($dashboard_kurzbz);
$funktion_kurzbz = ($section === self::SECTION_IF_FUNKTION_KURZBZ_IS_NULL) ? null : $section;
$result = $this->_ci->DashboardPresetModel
->getPresetByDashboardAndFunktion($dashboard->dashboard_id, $funktion_kurzbz);
$result = $this->_ci->DashboardPresetModel->loadWhere([
'dashboard_id' => $dashboard->dashboard_id,
'funktion_kurzbz' => $funktion_kurzbz
]);
if (hasData($result))
{
@@ -195,11 +214,11 @@ class DashboardLib
{
foreach ($addwigets as $widget)
{
if(!isset($widget->widgetid))
if(!isset($widget['widgetid']))
{
$widget->widgetid = $this->generateWidgetId($dashboard_kurzbz);
$widget['widgetid'] = $this->generateWidgetId($dashboard_kurzbz);
}
$this->addWidgetToWidgets($widgets, $section, $widget, $widget->widgetid);
$this->addWidgetToWidgets($widgets, $section, $widget, $widget['widgetid']);
}
}
@@ -3,6 +3,7 @@ namespace vertragsbestandteil;
use Exception;
use vertragsbestandteil\VertragsbestandteilStunden;
use vertragsbestandteil\VertragsbestandteilLohnguide;
/**
* Description of VertragsbestandteilFactory
@@ -22,6 +23,7 @@ class VertragsbestandteilFactory
const VERTRAGSBESTANDTEIL_URLAUBSANSPRUCH = 'urlaubsanspruch';
const VERTRAGSBESTANDTEIL_ZEITAUFZEICHNUNG = 'zeitaufzeichnung';
const VERTRAGSBESTANDTEIL_LEHRE = 'lehre';
const VERTRAGSBESTANDTEIL_LOHNGUIDE = 'lohnguide';
public static function getVertragsbestandteil($data, $fromdb=false)
{
@@ -69,6 +71,11 @@ class VertragsbestandteilFactory
$vertragsbestandteil = new VertragsbestandteilZeitaufzeichnung();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$vertragsbestandteil = new VertragsbestandteilLohnguide();
$vertragsbestandteil->hydrateByStdClass($data, $fromdb);
break;
default:
throw new Exception('Unknown vertragsbestandteiltyp_kurzbz '
@@ -127,6 +134,12 @@ class VertragsbestandteilFactory
$vertragsbestandteildbmodel = $CI->VertragsbestandteilZeitaufzeichnung_model;
break;
case self::VERTRAGSBESTANDTEIL_LOHNGUIDE:
$CI->load->model('vertragsbestandteil/VertragsbestandteilLohnguide_model',
'VertragsbestandteilLohnguide_model');
$vertragsbestandteildbmodel = $CI->VertragsbestandteilLohnguide_model;
break;
default:
throw new Exception('Unknown vertragsbestandteil_kurzbz '
. $vertragsbestandteil_kurzbz);
@@ -10,6 +10,7 @@ require_once __DIR__ . '/VertragsbestandteilKuendigungsfrist.php';
require_once __DIR__ . '/VertragsbestandteilUrlaubsanspruch.php';
require_once __DIR__ . '/VertragsbestandteilFreitext.php';
require_once __DIR__ . '/VertragsbestandteilKarenz.php';
require_once __DIR__ . '/VertragsbestandteilLohnguide.php';
require_once __DIR__ . '/VertragsbestandteilFactory.php';
require_once __DIR__ . '/OverlapChecker.php';
@@ -0,0 +1,155 @@
<?php
namespace vertragsbestandteil;
use vertragsbestandteil\Vertragsbestandteil;
use vertragsbestandteil\VertragsbestandteilFactory;
class VertragsbestandteilLohnguide extends Vertragsbestandteil
{
protected $stellenbezeichnung;
protected $vordienstzeit;
protected $fachrichtung_kurzbz;
protected $modellstelle_kurzbz;
protected $kommentar_person;
protected $kommentar_modellstelle;
public function __construct()
{
parent::__construct();
$this->setVertragsbestandteiltyp_kurzbz(
VertragsbestandteilFactory::VERTRAGSBESTANDTEIL_LOHNGUIDE);
}
public function getStellenbezeichnung()
{
return $this->stellenbezeichnung;
}
public function setStellenbezeichnung($stellenbezeichnung): self
{
$this->markDirty('stellenbezeichnung', $this->stellenbezeichnung, $stellenbezeichnung);
$this->stellenbezeichnung = $stellenbezeichnung;
return $this;
}
public function getVordienstzeit()
{
return $this->vordienstzeit;
}
public function setVordienstzeit($vordienstzeit): self
{
$this->markDirty('vordienstzeit', $this->vordienstzeit, $vordienstzeit);
$this->vordienstzeit = $vordienstzeit;
return $this;
}
public function getFachrichtung_kurzbz()
{
return $this->fachrichtung_kurzbz;
}
public function setFachrichtung_kurzbz($fachrichtung_kurzbz): self
{
$this->markDirty('fachrichtung_kurzbz', $this->fachrichtung_kurzbz, $fachrichtung_kurzbz);
$this->fachrichtung_kurzbz = $fachrichtung_kurzbz;
return $this;
}
public function getModellstelle_kurzbz()
{
return $this->modellstelle_kurzbz;
}
public function setModellstelle_kurzbz($modellstelle_kurzbz): self
{
$this->markDirty('modellstelle_kurzbz', $this->modellstelle_kurzbz, $modellstelle_kurzbz);
$this->modellstelle_kurzbz = $modellstelle_kurzbz;
return $this;
}
public function getKommentar_person()
{
return $this->kommentar_person;
}
public function setKommentar_person($kommentar_person): self
{
$this->markDirty('kommentar_person', $this->kommentar_person, $kommentar_person);
$this->kommentar_person = $kommentar_person;
return $this;
}
public function getKommentar_modellstelle()
{
return $this->kommentar_modellstelle;
}
public function setKommentar_modellstelle($kommentar_modellstelle): self
{
$this->markDirty('kommentar_modellstelle', $this->kommentar_modellstelle, $kommentar_modellstelle);
$this->kommentar_modellstelle = $kommentar_modellstelle;
return $this;
}
public function hydrateByStdClass($data, $fromdb=false)
{
parent::hydrateByStdClass($data, $fromdb);
$this->fromdb = $fromdb;
isset($data->fachrichtung_kurzbz) && $this->setFachrichtung_kurzbz($data->fachrichtung_kurzbz);
isset($data->stellenbezeichnung) && $this->setStellenbezeichnung($data->stellenbezeichnung);
isset($data->vordienstzeit) && $this->setVordienstzeit($data->vordienstzeit);
isset($data->modellstelle_kurzbz) && $this->setModellstelle_kurzbz($data->modellstelle_kurzbz);
isset($data->kommentar_person) && $this->setKommentar_person($data->kommentar_person);
isset($data->kommentar_modellstelle) && $this->setKommentar_modellstelle($data->kommentar_modellstelle);
$this->fromdb = false;
}
public function toStdClass(): \stdClass
{
$tmp = array(
'vertragsbestandteil_id' => $this->getVertragsbestandteil_id(),
'stellenbezeichnung' => $this->getStellenbezeichnung(),
'vordienstzeit' => $this->getVordienstzeit(),
'fachrichtung_kurzbz' => $this->getFachrichtung_kurzbz(),
'modellstelle_kurzbz' => $this->getModellstelle_kurzbz(),
'kommentar_person' => $this->getKommentar_person(),
'kommentar_modellstelle' => $this->getKommentar_modellstelle(),
);
$tmp = array_filter($tmp, function($k) {
return in_array($k, $this->modifiedcolumns);
}, ARRAY_FILTER_USE_KEY);
return (object) $tmp;
}
public function __toString()
{
$txt = <<<EOTXT
modellstelle_kurzbz: {$this->getModellstelle_kurzbz()}
EOTXT;
return parent::__toString() . $txt;
}
/* public function validate()
{
if( !(filter_var($this->tage, FILTER_VALIDATE_INT,
array(
'options' => array(
'min_range' => 1,
'max_range' => 50
)
)
)) ) {
$this->validationerrors[] = 'Urlaubsanspruch muss eine Tagesanzahl im Bereich 1 bis 50 sein.';
}
return parent::validate();
} */
}
@@ -11,57 +11,4 @@ class Dashboard_Preset_model extends DB_Model
$this->dbTable = 'dashboard.tbl_dashboard_preset';
$this->pk = 'preset_id';
}
/**
* Get Presets of given uid.
* @param integer dashboard_id
* @param string $uid
* @return array
*/
public function getPresets($dashboard_id, $uid)
{
// TODO: get Funktionen for uid and load all preset for all funktionen for uid
//return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz'=> null));
$sql = <<<EOSQL
SELECT
*
FROM
dashboard.tbl_dashboard_preset
WHERE
dashboard_id = ?
AND (
funktion_kurzbz IN (
SELECT
DISTINCT funktion_kurzbz
FROM
public.tbl_benutzerfunktion
WHERE
uid = ?
AND
NOW()::date
BETWEEN
COALESCE(datum_von, '1970-01-01')
AND
COALESCE(datum_bis, '2170-12-31')
)
OR
funktion_kurzbz IS NULL
)
ORDER BY
funktion_kurzbz DESC
EOSQL;
return $this->execQuery($sql, array($dashboard_id, $uid));
}
/**
* Get Preset by Dashboard and Funktion
* @param integer dashboard_id
* @param string funktion_kurzbz
* @return array
*/
public function getPresetByDashboardAndFunktion($dashboard_id, $funktion_kurzbz)
{
return $this->loadWhere(array('dashboard_id' => $dashboard_id, 'funktion_kurzbz' => $funktion_kurzbz));
}
}
@@ -402,14 +402,17 @@ class Lehrveranstaltung_model extends DB_Model
SELECT
vorname, nachname, mitarbeiter_uid, lehrfunktion_kurzbz
FROM
lehre.tbl_lehreinheit
lehre.tbl_lehreinheit le
JOIN lehre.tbl_lehreinheitmitarbeiter lema USING (lehreinheit_id)
JOIN public.tbl_benutzer b ON b.uid = lema.mitarbeiter_uid
JOIN public.tbl_person p using (person_id)
WHERE
tbl_lehreinheit.lehrveranstaltung_id= ?
AND tbl_lehreinheit.studiensemester_kurzbz = ?
le.lehrveranstaltung_id= ?
AND le.studiensemester_kurzbz = ?
AND lehrfunktion_kurzbz = 'LV-Leitung'
AND lema.mitarbeiter_uid NOT like '_Dummy%'
AND b.aktiv = TRUE
AND p.aktiv = TRUE
ORDER BY
lema.insertamum DESC
LIMIT 1
@@ -261,6 +261,42 @@ class Benutzerfunktion_model extends DB_Model
}
/**
* Get active Kompetenzfeldleitung bei UID.
*
* @param $uid
* @return array|stdClass|null
*/
public function getKFLByUID($uid)
{
$query = '
SELECT
bf.uid,
bf.oe_kurzbz,
oe.organisationseinheittyp_kurzbz
FROM
public.tbl_benutzerfunktion bf
JOIN public.tbl_organisationseinheit oe USING (oe_kurzbz)
JOIN public.tbl_benutzer b USING (uid)
WHERE
b.uid = ?
AND b.aktiv = TRUE
AND funktion_kurzbz = \'Leitung\'
AND organisationseinheittyp_kurzbz = \'Kompetenzfeld\'
AND (datum_von IS NULL OR datum_von <= now())
AND (datum_bis IS NULL OR datum_bis >= now())
';
$parameters_array = array();
if (is_string($uid))
{
$parameters_array[] = $uid;
}
return $this->execQuery($query, $parameters_array);
}
public function insertBenutzerfunktion($Json)
{
unset($Json['benutzerfunktion_id']);
+6 -4
View File
@@ -242,6 +242,7 @@ class Message_model extends DB_Model
*/
public function getMessagesForTable($person_id, $offset, $limit)
{
$limitoffset = (!is_null($offset) && !is_null($limit)) ? 'limit ? offset ?' : '';
$sql = <<<EOSQL
with filtered_messages as (
select
@@ -310,11 +311,12 @@ class Message_model extends DB_Model
public.tbl_person pr on pr.person_id = fm.recipient_id
order by
m.insertamum DESC
limit ?
offset ?;
{$limitoffset}
EOSQL;
$parametersArray = array($person_id, $person_id, $limit, $offset);
$parametersArray = $limitoffset
? array($person_id, $person_id, $limit, $offset)
: array($person_id, $person_id);
$count = 0;
$data = $this->execQuery($sql, $parametersArray);
@@ -325,7 +327,7 @@ EOSQL;
$data = getData($data);
if($data)
{
$count = ceil($data[0]->total_msgs / $limit);
$count = is_null($limit) ? 1 : ceil($data[0]->total_msgs / $limit);
}
return success(['data' => $data, 'count' => $count]);
@@ -0,0 +1,11 @@
<?php
class VertragsbestandteilLohnguide_model extends DB_Model
{
public function __construct()
{
parent::__construct();
$this->dbTable = 'hr.tbl_vertragsbestandteil_lohnguide';
$this->pk = 'vertragsbestandteil_id';
}
}
@@ -37,7 +37,8 @@ class Vertragsbestandteil_model extends DB_Model
kf.arbeitgeber_frist, kf.arbeitnehmer_frist,
s.wochenstunden, s.teilzeittyp_kurzbz,
u.tage,
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice
z.zeitaufzeichnung, z.azgrelevant, z.homeoffice,
lg.stellenbezeichnung, lg.vordienstzeit, lg.fachrichtung_kurzbz, lg.modellstelle_kurzbz, lg.kommentar_person, lg.kommentar_modellstelle
FROM
hr.tbl_vertragsbestandteil v
LEFT JOIN
@@ -63,6 +64,8 @@ class Vertragsbestandteil_model extends DB_Model
hr.tbl_vertragsbestandteil_urlaubsanspruch u USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_zeitaufzeichnung z USING(vertragsbestandteil_id)
LEFT JOIN
hr.tbl_vertragsbestandteil_lohnguide lg USING(vertragsbestandteil_id)
EOSQL;
return $sql;
}
@@ -8,9 +8,15 @@ $this->load->view(
'axios027' => true,
'restclient' => true,
'vue3' => true,
'customJSModules' => ['public/js/apps/Dashboard.js'],
'primevue3' => true,
'vuedatepicker11' => true,
'customJSs' => [
'vendor/moment/luxonjs/luxon.min.js'
],
'customJSModules' => ['public/js/apps/Dashboard/Admin.js'],
'customCSSs' => [
'public/css/components/dashboard.css'
'public/css/components/dashboard.css',
'public/css/components/primevue.css',
],
'navigationcomponent' => true
)
@@ -25,7 +31,7 @@ $this->load->view(
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
</div>
<core-dashboard dashboard="CIS" apiurl="<?= site_url('dashboard'); ?>"></core-dashboard>
<dashboard-admin></dashboard-admin>
</div>
</div>
@@ -8,7 +8,12 @@ $this->load->view(
'axios027' => true,
'restclient' => true,
'vue3' => true,
'customJSModules' => ['public/js/apps/DashboardAdmin.js'],
'vuedatepicker11' => true,
'primevue3' => true,
'customJSs' => [
'vendor/moment/luxonjs/luxon.min.js'
],
'customJSModules' => ['public/js/apps/Dashboard/Preview.js'],
'customCSSs' => [
'public/css/components/dashboard.css'
],
@@ -23,9 +28,9 @@ $this->load->view(
<div id="content">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1>
<h1 class="h2">Dashboard <?= $dashboard_kurzbz ?></h1>
</div>
<dashboard-admin dashboard="CIS" apiurl="<?= site_url('dashboard'); ?>"></dashboard-admin>
<core-dashboard dashboard="<?= $dashboard_kurzbz ?>"></core-dashboard>
</div>
</div>
@@ -802,6 +802,10 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#fgm" />
<splitter class="tree-splitter"/>
<treecol id="student-prestudent-tree-rolle-faktiv" label="F-Aktiv" flex="1" hidden="true" persist="hidden, width, ordinal"
class="sortDirectionIndicator"
sort="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#faktiv" />
<splitter class="tree-splitter"/>
</treecols>
<template>
@@ -828,6 +832,7 @@ echo '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>';
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#updateamum"/>
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#updatevon"/>
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#fgm"/>
<treecell properties="Aktiv_rdf:http://www.technikum-wien.at/prestudentrolle/rdf#aktiv rdf:http://www.technikum-wien.at/prestudentrolle/rdf#stichtagsaktiv" label="rdf:http://www.technikum-wien.at/prestudentrolle/rdf#faktiv"/>
</treerow>
</treeitem>
</treechildren>
+2
View File
@@ -3550,9 +3550,11 @@ function StudentZeugnisDokumentArchivieren()
case 'microcredentialzertifikat_1':
case 'microcredentialzertifikat_2':
case 'microcredentialzertifikat_3':
case 'microcredentialzertifikat_4':
case 'microcredential_1':
case 'microcredential_2':
case 'microcredential_3':
case 'microcredential_4':
xml = 'microcredential.xml.php';
break;
+162 -7
View File
@@ -552,9 +552,40 @@ class lehreinheitmitarbeiter extends basis_db
$beginn = new DateTime($beginn);
$ende = new DateTime($ende);
// get relevant Studiensemester
$studiensemester_kurzbz_arr = [];
$qry = '
SELECT
studiensemester_kurzbz
FROM
public.tbl_studiensemester
WHERE
start BETWEEN
'. $this->db_add_param($beginn->format('Y-m-d')). ' AND
'. $this->db_add_param($ende->format('Y-m-d'));
if ($this->db_query($qry))
{
while($row = $this->db_fetch_object())
{
$studiensemester_kurzbz_arr[] = $row->studiensemester_kurzbz;
}
}
else
{
$this->errormsg = 'Fehler bei der Datenbankabfrage';
return false;
}
$lehrgaengeDistr = $this->_getLehrgaengeForDistribution($studiensemester_kurzbz_arr);
if (!is_array($studiensemester_kurzbz_arr) || empty($studiensemester_kurzbz_arr)) return true;
$qry = '
WITH semester_sws_tbl AS (
SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden, stg.studiengang_kz
SELECT DISTINCT lehreinheit_id, studiensemester_kurzbz, lema.semesterstunden,
stg.studiengang_kz, stg.melde_studiengang_kz, stg.lgartcode
FROM lehre.tbl_lehreinheitmitarbeiter lema
JOIN lehre.tbl_lehreinheit USING (lehreinheit_id)
JOIN lehre.tbl_lehrveranstaltung lv USING (lehrveranstaltung_id)
@@ -564,38 +595,103 @@ class lehreinheitmitarbeiter extends basis_db
JOIN public.tbl_studiengang stg ON stg.studiengang_kz = sto.studiengang_kz
JOIN public.tbl_studiensemester ss USING (studiensemester_kurzbz)
WHERE mitarbeiter_uid = '. $this->db_add_param($uid). '
AND (
ss.start BETWEEN
'. $this->db_add_param($beginn->format('Y-m-d')). ' AND
'. $this->db_add_param($ende->format('Y-m-d')). ')
AND ss.studiensemester_kurzbz IN ('.$this->implode4SQL($studiensemester_kurzbz_arr).')
-- nur lehre, die bisgemeldet wird
AND lema.bismelden
-- keine lehreinheiten ohne semesterstunden
AND lema.semesterstunden != 0
AND lema.semesterstunden != 0
)
SELECT
studiengang_kz,
studiensemester_kurzbz,
melde_studiengang_kz,
lgartcode,
sum(semesterstunden) AS summe,
round(sum(semesterstunden) / 15, 2) AS sws
FROM
semester_sws_tbl
GROUP BY
studiengang_kz,
studiensemester_kurzbz
studiensemester_kurzbz,
melde_studiengang_kz,
lgartcode
ORDER BY
studiengang_kz;
';
if ($this->db_query($qry))
{
$additionalLehrgaenge = [];
while($row = $this->db_fetch_object())
{
$obj = new StdClass();
$obj->studiengang_kz = $row->studiengang_kz;
$obj->studiensemester_kurzbz = $row->studiensemester_kurzbz;
$obj->melde_studiengang_kz = $row->melde_studiengang_kz;
$obj->lgartcode = $row->lgartcode;
$obj->sws = $row->sws;
if (isset($lehrgaengeDistr[$uid][$row->studiensemester_kurzbz]))
{
$lehrgaenge = $lehrgaengeDistr[$uid][$row->studiensemester_kurzbz];
foreach ($lehrgaenge as $lehreinheit_id => $lehrgangKzArr)
{
// wenn lehrgang gefunden, zusammenhängende Lehrgaenge holen und sws aufteilen
if (array_key_exists($row->studiengang_kz, $lehrgangKzArr))
{
foreach ($lehrgangKzArr as $studiengang_kz => $lehrgang)
{
// check: nur eine Studiengangsverknüpfung pro Mitarbeiter, Semester, und Referenzstudiengang
if (
$studiengang_kz == $row->studiengang_kz
|| isset(
$additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz][$studiengang_kz]
)
) continue;
// Lehrgang erstellen
$lg = new StdClass();
$lg->mitarbeiter_uid = $uid;
$lg->melde_studiengang_kz = $lehrgang->melde_studiengang_kz;
$lg->lgartcode = $lehrgang->lgartcode;
$lg->studiengang_kz = $lehrgang->studiengang_kz;
$lg->studiensemester_kurzbz = $lehrgang->studiensemester_kurzbz;
$lg->summe = $row->summe;
$lg->sws = $row->sws;
// Lehrgang, der mit Ursprungsstudiengang aufgrund lehreinheit "verknüpft" ist, hinzufügen
$additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz][$studiengang_kz] = $lg;
}
}
}
// ignorieren, wenn für den Studiengang keine verknüpften Lehrgaenge hat
if (isset($additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz]))
{
$addLehrgaenge = $additionalLehrgaenge[$uid][$row->studiensemester_kurzbz][$row->studiengang_kz];
// sws Durchschnitt über alle verknuepften Lehrgaenge berechnet
$summeSws = $row->summe/(count($addLehrgaenge) + 1);
$sws = $row->sws/(count($addLehrgaenge) + 1);
// neue sws zuweisen
$obj->summe = $summeSws;
$obj->sws = $sws;
foreach ($addLehrgaenge as $conn_ws_studiengang_kz => $lehrgang)
{
// sws fuer jeden verknuepften Lehrgang zuweisen
$lehrgang->summe = $summeSws;
$lehrgang->sws = $sws;
// neue lehrgang sws hinzufuegen
$this->result [] = $lehrgang;
}
}
}
$this->result []= $obj;
}
return true;
@@ -655,4 +751,63 @@ class lehreinheitmitarbeiter extends basis_db
return false;
}
/**
* Get "connected" Lehrgaenge for equal sws distribution.
* @param $studiensemester_kurzbz_arr all semester for which Lehrgaenge should be retrieved
* @return object success or error
*/
private function _getLehrgaengeForDistribution($studiensemester_kurzbz_arr)
{
if (!is_array($studiensemester_kurzbz_arr) || empty($studiensemester_kurzbz_arr)) return [];
$qry = "
WITH gruppen AS (
SELECT
mitarbeiter_uid, lehreinheit_id, lehrveranstaltung_id, studiensemester_kurzbz, sem.start, sem.ende,
lehreinheitgruppe_id, studiengang_kz, melde_studiengang_kz, lgartcode
FROM
lehre.tbl_lehreinheitmitarbeiter lema
JOIN lehre.tbl_lehreinheit le USING (lehreinheit_id)
JOIN lehre.tbl_lehreinheitgruppe legr USING (lehreinheit_id)
JOIN public.tbl_studiengang stg USING (studiengang_kz)
JOIN public.tbl_studiensemester sem USING (studiensemester_kurzbz)
WHERE
bismelden
AND stg.melderelevant
AND stg.typ = 'l'
AND le.studiensemester_kurzbz IN (".$this->implode4SQL($studiensemester_kurzbz_arr).")
)
SELECT
DISTINCT mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode
FROM
gruppen gr
GROUP BY
mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode
ORDER BY
mitarbeiter_uid, studiensemester_kurzbz, lehreinheit_id, studiengang_kz, melde_studiengang_kz, lgartcode";
$lehrgaengeDistributions = [];
if($this->db_query($qry))
{
while($row = $this->db_fetch_object())
{
// group by properties
$lehrgaengeDistributions
[$row->mitarbeiter_uid]
[$row->studiensemester_kurzbz]
[$row->lehreinheit_id]
[$row->studiengang_kz]
= $row;
}
}
else
{
$this->errormsg = 'Fehler bei der Datenbankabfrage';
return false;
}
return $lehrgaengeDistributions;
}
}
+2
View File
@@ -270,6 +270,8 @@ class LehreListHelper
} else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von > $stsemdatumvon || $row->von == '')) {
// if bis datum is not yet known but von is available already
$zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')';
} else if ($row->bisio_id != '' && $row->status != 'Incoming' && ($row->von <= $stsemdatumvon || $row->von == '') && ($row->bis == '' || $row->bis > date('Y-m-d'))){
$zusatz .= '(o)(ab '.$datum->formatDatum($row->von, 'd.m.Y').')';
}
+1
View File
@@ -706,6 +706,7 @@ class prestudent extends person
$rolle->bestaetigtam = $row->bestaetigtam;
$rolle->bestaetigtvon = $row->bestaetigtvon;
$rolle->fgm = $row->fgm;
$rolle->faktiv = $this->db_parse_bool($row->faktiv);
$rolle->anmerkung_status = $row->anmerkung;
$rolle->bewerbung_abgeschicktamum = $row->bewerbung_abgeschicktamum;
$rolle->rt_stufe = $row->rt_stufe;
+11 -1
View File
@@ -147,6 +147,8 @@ html {
--fhc-cis-menu-lvl-5-color-hover: var(--fhc-text);
--fhc-cis-grade-positive: var(--fhc-success);
--fhc-cis-grade-negative: var(--fhc-danger);
--fhc-offcanvas-zindex: 1045;
}
#themeSwitch i{
@@ -375,7 +377,7 @@ html {
/* searchbar */
#nav-search {
background-color: var(--fhc-primary);
z-index: 1;
z-index: calc(var(--fhc-offcanvas-zindex) + 1) !important;
}
#nav-search.me-3 {
margin: 0 !important;
@@ -413,10 +415,18 @@ html {
color: var(--fhc-link) !important;
}
#nav-main {
z-index: var(--fhc-offcanvas-zindex);
}
#nav-main-sticky {
max-height: calc(100vh - var(--fhc-cis-header-height));
}
#nav-user-menu {
z-index: calc(var(--fhc-offcanvas-zindex) + 1);
}
#nav-user-menu img {
object-fit: cover;
height: calc( 3 * var(--fhc-cis-header-py));
+3
View File
@@ -193,3 +193,6 @@
word-break: break-word;
}
.news-list-item p {
word-break: break-word;
}
+46
View File
@@ -0,0 +1,46 @@
/**
* 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 <https://www.gnu.org/licenses/>.
*/
export default {
list() {
return {
method: 'get',
url: 'api/frontend/v1/dashboard/board/list'
};
},
add(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/board/create',
params
};
},
update(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/board/update',
params
};
},
delete(dashboard_id) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/board/delete',
params: { dashboard_id }
};
}
}
+47
View File
@@ -0,0 +1,47 @@
/**
* 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 <https://www.gnu.org/licenses/>.
*/
export default {
list(dashboard_kurzbz) {
return {
method: 'get',
url: 'api/frontend/v1/dashboard/preset/list/'
+ encodeURIComponent(dashboard_kurzbz)
};
},
getBatch(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/preset/getBatch',
params
};
},
addWidget(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/preset/addWidget',
params
};
},
removeWidget(params) {
return {
method: 'post',
url: 'api/frontend/v1/dashboard/preset/removeWidget',
params
};
}
};
+45
View File
@@ -0,0 +1,45 @@
/**
* 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 <https://www.gnu.org/licenses/>.
*/
export default {
get(dashboard) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/user/get/' + dashboard
};
},
addWidget(dashboard, widget) {
return {
method: 'post',
url: '/api/frontend/v1/dashboard/user/addWidget',
params: {
dashboard,
widget
}
};
},
removeWidget(dashboard, widget) {
return {
method: 'post',
url: '/api/frontend/v1/dashboard/user/removeWidget',
params: {
dashboard,
widget
}
};
}
};
+46
View File
@@ -0,0 +1,46 @@
/**
* 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 <https://www.gnu.org/licenses/>.
*/
export default {
get(widget) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/widget/get/' + widget
};
},
list(dashboard) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/widget/list/' + dashboard
};
},
listAllowed(dashboard) {
return {
method: 'get',
url: '/api/frontend/v1/dashboard/widget/listAllowed/' + dashboard
};
},
setAllowed(dashboard_id, widget_id, allowed) {
return {
method: 'post',
url: '/api/frontend/v1/dashboard/widget/setAllowed',
params: {
dashboard_id, widget_id, allowed
}
};
}
};
+8 -5
View File
@@ -17,13 +17,16 @@
export default {
getMessages(params) {
let url = 'api/frontend/v1/messages/messages/getMessages'
+ '/' + params.id
+ '/' + params.type;
if(params.size && params.page) {
url += '/' + params.size
+ '/' + params.page;
}
return {
method: 'get',
url: 'api/frontend/v1/messages/messages/getMessages/'
+ params.id + '/'
+ params.type + '/'
+ params.size + '/'
+ params.page
url: url
};
},
getVorlagen(){
+60 -9
View File
@@ -1,16 +1,67 @@
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js';
import { CoreNavigationCmpt } from '../../components/navigation/Navigation.js';
import DashboardAdmin from '../../components/Dashboard/Admin.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
import ApiRenderers from '../../api/factory/renderers.js';
const app = Vue.createApp({
name: 'AdminApp',
data: () => ({
appSideMenuEntries: {}
}),
components: {
CoreNavigationCmpt,
DashboardAdmin
}
name: 'DashboardAdminApp',
data: () => ({
appSideMenuEntries: {},
renderers: null
}),
components: {
CoreNavigationCmpt,
DashboardAdmin
},
provide() {
return {
// TODO(chris): move those two into the components that need it
renderers: Vue.computed(() => this.renderers),
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone
};
},
created() {
this.$api
.call(ApiRenderers.loadRenderers())
.then(res => {
for (let rendertype of Object.keys(res.data)) {
let modalTitle = null;
let modalContent = null;
let calendarEvent = null;
if (res.data[rendertype].modalTitle)
modalTitle = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalTitle)));
if (res.data[rendertype].modalContent)
modalContent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].modalContent)));
if (res.data[rendertype].calendarEvent)
calendarEvent = Vue.markRaw(Vue.defineAsyncComponent(() => import(res.data[rendertype].calendarEvent)));
if (res.data[rendertype].calendarEventStyles) {
var head = document.head;
if (!head.querySelector(`link[href="${res.data[rendertype].calendarEventStyles}"]`)) {
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = res.data[rendertype].calendarEventStyles;
head.appendChild(link);
}
}
if (this.renderers === null) {
this.renderers = {};
}
if (!this.renderers[rendertype]) {
this.renderers[rendertype] = {}
}
this.renderers[rendertype].modalTitle = modalTitle;
this.renderers[rendertype].modalContent = modalContent;
this.renderers[rendertype].calendarEvent = calendarEvent;
}
})
.catch(this.$fhcAlert.handleSystemErrors);
}
});
app.use(PluginsPhrasen);
app.directive('tooltip', primevue.tooltip);
app.mount('#main');
+17
View File
@@ -0,0 +1,17 @@
import {CoreNavigationCmpt} from '../../components/navigation/Navigation.js';
import CoreDashboard from '../../components/Dashboard/Dashboard.js';
import PluginsPhrasen from '../../plugins/Phrasen.js';
const app = Vue.createApp({
name: 'DashboardPreviewApp',
data: () => ({
appSideMenuEntries: {}
}),
components: {
CoreNavigationCmpt,
CoreDashboard
}
});
app.use(PluginsPhrasen);
app.directive('tooltip', primevue.tooltip);
app.mount('#main');
+1 -1
View File
@@ -17,7 +17,7 @@ export default {
</template>
<template v-slot:footer>
<button type="button" class="btn btn-primary" @click="result=true;this.hide()">OK</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{{this.$p.t('ui', 'cancel')}}</button>
</template>
</bs-modal>`
}
@@ -180,7 +180,7 @@ export const AbgabetoolAssistenz = {
// frozen: true,
// width: 40
// },
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', headerFilter: false, headerSort: false, formatter: this.formAction, tooltip:false, minWidth: 150, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4details'))), field: 'details', headerFilter: false, headerSort: false, formatter: this.formAction, tooltip:false, minWidth: 100, cssClass: 'sticky-col'},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4personenkennzeichen'))), headerFilter: true, field: 'pkz', formatter: this.pkzTextFormatter, widthGrow: 1, tooltip: false},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4vorname'))), field: 'student_vorname', headerFilter: true, formatter: this.centeredTextFormatter,widthGrow: 1},
{title: Vue.computed(() => this.$capitalize(this.$p.t('abgabetool/c4nachname'))), field: 'student_nachname', headerFilter: true, formatter: this.centeredTextFormatter, widthGrow: 1},
@@ -226,7 +226,7 @@ export const AbgabetoolAssistenz = {
field: 'qgate2Status', formatter: this.centeredTextFormatter, widthGrow: 1, width: 220, tooltip: false},
],
persistence: false,
persistenceID: "abgabetool_2026_02_26"
persistenceID: "abgabetool_2026_03_16"
},
abgabeTableEventHandlers: [
{
@@ -645,7 +645,7 @@ export const AbgabetoolAssistenz = {
actionButtons.className = "d-flex gap-3"; // you can keep Bootstrap gap if loaded
actionButtons.style.display = "flex";
actionButtons.style.alignItems = "stretch"; // buttons stretch to full height
actionButtons.style.justifyContent = "center";
actionButtons.style.justifyContent = "start";
actionButtons.style.height = "100%"; // full grid cell height
const val = cell.getValue();
@@ -675,8 +675,20 @@ export const AbgabetoolAssistenz = {
createButton('fa fa-timeline', 'abgabetool/c4termineTimeLine', () => this.openTimeline(val))
);
if(val.latestTerminWithUpload) {
actionButtons.append(
createButton('fa fa-download', 'abgabetool/c4downloadLatestAbgabe', () => this.downloadAbgabe(val.latestTerminWithUpload.paabgabe_id, val.student_uid, val.projektarbeit_id))
)
}
return actionButtons;
},
downloadAbgabe(paabgabe_id, student_uid, projektarbeit_id) {
const url = `/api/frontend/v1/Abgabe/getStudentProjektarbeitAbgabeFile?paabgabe_id=${paabgabe_id}&student_uid=${student_uid}&projektarbeit_id=${projektarbeit_id}`;
window.open(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + url)
// this.$api.call(ApiAbgabe.getStudentProjektarbeitAbgabeFile(termin.paabgabe_id, this.projektarbeit.student_uid))
},
undoSelection(cell) {
// checks if cells row is selected and unselects -> imitates columns which dont trigger row selection
@@ -780,6 +792,8 @@ export const AbgabetoolAssistenz = {
// TODO: mehrsprachig englisch
projekt.note_bez = opt.bezeichnung
}
const latestTerminWithUpload = this.findLatestTerminWithUpload(projekt)
return {
...projekt,
@@ -787,6 +801,7 @@ export const AbgabetoolAssistenz = {
details: {
student_uid: projekt.student_uid,
projektarbeit_id: projekt.projektarbeit_id,
latestTerminWithUpload: latestTerminWithUpload ?? null
},
pkz: this.buildPKZ(projekt),
beurteilung: projekt.beurteilungLink ?? null,
@@ -800,6 +815,15 @@ export const AbgabetoolAssistenz = {
}
})
},
findLatestTerminWithUpload(projekt) {
const withAbgabedatumSorted = projekt?.abgabetermine?.filter(t => t.abgabedatum != null)?.sort((a,b) => a < b)
if(withAbgabedatumSorted.length) {
return withAbgabedatumSorted[0]
}
return null
},
createInfoString(data) {
let str = '';
@@ -1413,9 +1437,12 @@ export const AbgabetoolAssistenz = {
<div id="abgabetable" style="max-height:40vw;">
<div class="row">
<div class="col-auto">
<div class="col-auto me-auto">
<h2 tabindex="1">{{$p.t('abgabetool/abgabetoolTitle')}}</h2>
</div>
<div class="col-auto">
<label class="col-form-label">{{$capitalize($p.t('lehre/studiengang'))}}:</label>
</div>
<div class="col-3">
<Dropdown
:placeholder="$capitalize($p.t('lehre/studiengang'))"
@@ -1430,6 +1457,9 @@ export const AbgabetoolAssistenz = {
</template>
</Dropdown>
</div>
<div class="col-auto">
<label class="col-form-label">{{$capitalize($p.t('lehre/note'))}}:</label>
</div>
<div class="col-3">
<Dropdown
:placeholder="$p.t('lehre/note')"
@@ -667,8 +667,10 @@ export const AbgabetoolMitarbeiter = {
setDetailComponent(details){
this.loading=true
const pa = this.projektarbeiten?.retval?.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id)
const projektarbeiten = this.projektarbeiten?.retval ?? this.projektarbeiten
const pa = projektarbeiten.find(projekarbeit => projekarbeit.projektarbeit_id == details.projektarbeit_id)
let paIsBenotet = false
if(pa.note !== undefined && pa.note !== null) {
// check if the note is not defined as a non final projektarbeit note
+66 -34
View File
@@ -3,15 +3,20 @@ import DashboardAdminEdit from "./Admin/Edit.js";
import DashboardAdminWidgets from "./Admin/Widgets.js";
import DashboardAdminPresets from "./Admin/Presets.js";
import ApiDashboardBoard from "../../api/factory/dashboard/board.js";
import ApiDashboardWidget from "../../api/factory/dashboard/widget.js";
export default {
name: 'DashboardAdmin',
components: {
DashboardAdminEdit,
DashboardAdminWidgets,
DashboardAdminPresets
DashboardAdminPresets,
},
provide() {
return {
adminMode: true
adminMode: true,
widgetsSetup: Vue.computed(() => this.dashboards[this.current] ? this.dashboards[this.current].widgetSetup : null)
};
},
data() {
@@ -22,9 +27,6 @@ export default {
};
},
computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
},
dashboard() {
return this.dashboards.find(el => el.dashboard_id == this.current);
}
@@ -35,33 +37,50 @@ export default {
BsPrompt.popup('New Dashboard name').then(
name => {
_name = name;
return axios.post(this.apiurl + '/Dashboard/create', {
const params = {
dashboard_kurzbz: name
})
}
).then(res => {
let newDashboard = {
dashboard_id: res.data.retval,
dashboard_kurzbz: _name,
beschreibung: ''
};
this.dashboards.push(newDashboard);
this.current = newDashboard.dashboard_id;
}).catch(err => err !== undefined ? console.error('ERROR:', err) : 0);
};
return this.$api
.call(ApiDashboardBoard.add(params))
.then(response =>{
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
let newDashboard = {
dashboard_id: response.data,
dashboard_kurzbz: _name,
beschreibung: ''
};
this.dashboards.push(newDashboard);
this.current = newDashboard.dashboard_id;
})
.catch(this.$fhcAlert.handleSystemError);
});
},
dashboardUpdate(dashboard) {
// TODO(chris): Loading or message
axios.post(this.apiurl + '/Dashboard/update', dashboard).then(() => {
let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id);
old.dashboard_kurzbz = dashboard.dashboard_kurzbz;
old.beschreibung = dashboard.beschreibung;
}).catch(err => console.error('ERROR:', err));
return this.$api
.call(ApiDashboardBoard.update(dashboard))
.then(response =>{
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSave'));
let old = this.dashboards.find(el => el.dashboard_id == dashboard.dashboard_id);
old.dashboard_kurzbz = dashboard.dashboard_kurzbz;
old.beschreibung = dashboard.beschreibung;
})
.catch(this.$fhcAlert.handleSystemError);
},
dashboardDelete(dashboard_id) {
axios.post(this.apiurl + '/Dashboard/delete', {dashboard_id}).then(() => {
this.current = -1;
this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id);
}).catch(err => console.error('ERROR:', err));
return this.$api
.call(ApiDashboardBoard.delete(dashboard_id))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successDelete'));
})
.catch(this.$fhcAlert.handleSystemError)
.finally(() => {
this.current = -1;
this.dashboards = this.dashboards.filter(el => el.dashboard_id != dashboard_id);
});
},
assignWidgets(widgets) {
this.widgets = widgets;
@@ -72,22 +91,35 @@ export default {
}
},
created() {
axios.get(this.apiurl + '/Dashboard').then(res => {
this.dashboards = res.data.retval;
}).catch(err => console.error('ERROR:', err));
this.$api
.call(ApiDashboardBoard.list())
.then(result => {
this.dashboards = result.data.retval;
for (const dashboard of this.dashboards) {
this.$api
.call(ApiDashboardWidget.list(dashboard.dashboard_id))
.then(res => {
dashboard.widgetSetup = res.data;
})
.catch(this.$fhcAlert.handleSystemError);
}
})
.catch(this.$fhcAlert.handleSystemError);
},
template: `<div class="dashboard-admin">
<div class="input-group">
<label for="dashbaord-select" class="input-group-text">Dashboard:</label>
<select id="dashbaord-select" class="form-select" v-model="current">
<label for="dashboard-select" class="input-group-text">Dashboard:</label>
<select id="dashboard-select" class="form-select" v-model="current">
<option v-for="dashboard in dashboards" :key="dashboard.dashboard_id" :value="dashboard.dashboard_id">{{dashboard.dashboard_kurzbz}}</option>
</select>
<button class="btn btn-outline-secondary" type="button" @click="dashboardAdd"><i class="fa-solid fa-plus"></i></button>
</div>
<div v-if="dashboard">
<ul class="nav nav-tabs mt-3" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link" id="edit-tab" data-bs-toggle="tab" data-bs-target="#edit" type="button" role="tab" aria-controls="edit" aria-selected="false">Edit</button>
<button class="nav-link" id="edit-tab" data-bs-toggle="tab" data-bs-target="#edit" type="button" role="tab" aria-controls="edit" aria-selected="false">{{this.$p.t('ui', 'bearbeiten')}}</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link active" id="widgets-tab" data-bs-toggle="tab" data-bs-target="#widgets" type="button" role="tab" aria-controls="widgets" aria-selected="true">Widgets</button>
@@ -101,7 +133,7 @@ export default {
<dashboard-admin-edit v-bind="dashboard" :key="dashboard.dashboard_id" @change="dashboardUpdate($event)" @delete="dashboardDelete($event)"></dashboard-admin-edit>
</div>
<div class="tab-pane fade show active" id="widgets" role="tabpanel" aria-labelledby="widgets-tab">
<dashboard-admin-widgets :key="dashboard.dashboard_id" :dashboard_id="dashboard.dashboard_id" :widgets="widgets" @change="test" @assign-widgets="assignWidgets"></dashboard-admin-widgets>
<dashboard-admin-widgets :key="dashboard.dashboard_id" :dashboard_id="dashboard.dashboard_id" :widgets="widgets" @assign-widgets="assignWidgets"></dashboard-admin-widgets>
</div>
<div class="tab-pane fade" id="presets" role="tabpanel" aria-labelledby="presets-tab">
<dashboard-admin-presets :dashboard="dashboard.dashboard_kurzbz" :widgets="widgets"></dashboard-admin-presets>
+4 -3
View File
@@ -18,7 +18,8 @@ export default {
},
methods: {
sendDelete() {
BsConfirm.popup('Sure?').then(() => this.$emit('delete', this.dashboard_id)).catch();
BsConfirm.popup(this.$p.t('ui', 'confirm_delete') + " " + this.$p.t('ui', 'deleteInfo'))
.then(() => this.$emit('delete', this.dashboard_id)).catch();
}
},
template: `<div class="dashboard-admin-edit px-3">
@@ -31,8 +32,8 @@ export default {
<textarea id="dashboard-admin-edit-beschreibung" class="form-control" v-model="desc"></textarea>
</div>
<div>
<button class="btn btn-danger" @click="sendDelete">Delete</button>
<button class="btn btn-primary" @click="$emit('change', {dashboard_id,dashboard_kurzbz:kurzbz,beschreibung:desc})">Update</button>
<button class="btn btn-danger" @click="sendDelete">{{this.$p.t('ui', 'loeschen')}}</button>
<button class="btn btn-primary" @click="$emit('change', {dashboard_id,dashboard_kurzbz:kurzbz,beschreibung:desc})">{{this.$p.t('ui', 'btnAktualisieren')}}</button>
</div>
</div>`
}
+111 -89
View File
@@ -1,6 +1,7 @@
import DashboardSection from "../Section.js";
import DashboardWidgetPicker from "../Widget/Picker.js";
import ObjectUtils from "../../../helpers/ObjectUtils.js";
import ApiDashboardPreset from "../../../api/factory/dashboard/preset.js";
export default {
components: {
@@ -17,9 +18,6 @@ export default {
tmpLoading: ''
}),
computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
},
pickerWidgets() {
return this.widgets.filter(widget => widget.allowed);
}
@@ -28,6 +26,7 @@ export default {
widgetAdd(section_name, widget) {
this.$refs.widgetpicker.getWidget().then(widget_id => {
widget.widget = widget_id;
widget.id = 'loading_' + String((new Date()).valueOf());
delete widget.custom;
widget.preset = 1;
let loading = {...widget};
@@ -36,130 +35,153 @@ export default {
if (section.name == section_name)
section.widgets.push(loading);
});
axios.post(this.apiurl + '/Config/addWidgetsToPreset', {
db: this.dashboard,
const params = {
dashboard: this.dashboard,
funktion_kurzbz: section_name,
widgets: [widget]
}).then(result => {
let newId = Object.keys(result.data.retval.data[section_name].widgets).pop();
widget.id = newId;
widget.custom = 1;
this.sections.forEach(section => {
if (section.name == section_name) {
section.widgets.splice(section.widgets.indexOf(loading),1);
section.widgets.push(widget);
}
});
}).catch(error => {
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
}).catch(() => {});
widget
};
return this.$api
.call(ApiDashboardPreset.addWidget(params))
.then(result => {
let newId = result.data;
widget.id = newId;
widget.custom = 1;
this.sections.forEach(section => {
if (section.name == section_name) {
section.widgets.splice(section.widgets.indexOf(loading),1);
section.widgets.push(widget);
}
});
this.funktionen.forEach(funktion => {
if(funktion.funktion_kurzbz === section_name && funktion.has_preset < 1) {
funktion.has_preset = 1;
}
});
})
.catch(this.$fhcAlert.handleSystemError);
})
.catch(() => {});
},
widgetUpdate(section_name, payload) {
payload = payload[section_name];
for (var k in payload) {
for (var i in this.sections) {
if (this.sections[i].name == section_name) {
for (var wid in this.sections[i].widgets) {
if (this.sections[i].widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(this.sections[i].widgets[wid], payload[k]);
// NOTE(chris): remove internal props
for (var prop in {_x:1,_y:1,_w:1,_h:1,index:1,id:1})
if (payload[k][prop])
delete payload[k][prop];
break;
}
}
const section = this.sections.find(section => section.name == section_name);
for (var wid in section.widgets) {
if (section.widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(section.widgets[wid], payload[k]);
// NOTE(chris): remove internal props
for (var prop of ['_x', '_y', '_w', '_h', 'index', 'id'])
if (payload[k][prop])
delete payload[k][prop];
break;
}
}
payload[k].widgetid = k;
delete payload[k].custom;
}
axios.post(this.apiurl + '/Config/addWidgetsToPreset', {
db: this.dashboard,
funktion_kurzbz: section_name,
widgets: payload
}).then(() => {
this.sections.forEach(section => {
if (section.name == section_name) {
section.widgets.forEach((widget, i) => {
if (payload[widget.id]) {
payload[widget.id].id = widget.id;
payload[widget.id].index = widget.index;
section.widgets[i] = payload[widget.id];
section.widgets[i].custom = 1;
}
});
}
});
}).catch(error => {
// TODO(chris): revert placement on failure
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
this.$api
.call(Object.entries(payload).map(([key, widget]) => [
key,
ApiDashboardPreset.addWidget({
dashboard: this.dashboard,
funktion_kurzbz: section_name,
widget
})
]))
.then(result => {
this.sections.forEach(section => {
if (section.name == section_name) {
section.widgets.forEach((widget, i) => {
if (payload[widget.id]) {
payload[widget.id].id = widget.id;
payload[widget.id].index = widget.index;
section.widgets[i] = payload[widget.id];
section.widgets[i].custom = 1;
}
});
}
});
})
.catch(this.$fhcAlert.handleSystemError);
},
widgetRemove(section_name, id) {
axios.post(this.apiurl + '/Config/removeWidgetFromPreset', {
const params = {
db: this.dashboard,
funktion_kurzbz: section_name,
widgetid: id
}).then(() => {
this.sections.forEach(section => {
if (section.name == section_name)
section.widgets = section.widgets.filter(widget => widget.id != id);
});
}).catch(error => {
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
};
return this.$api
.call(ApiDashboardPreset.removeWidget(params))
.then(result => {
this.sections.forEach(section => {
if (section.name == section_name)
section.widgets = section.widgets.filter(widget => widget.id != id);
});
})
.catch(this.$fhcAlert.handleSystemError);
},
loadSections(evt) {
let funktionen = Array.from(evt.target.querySelectorAll("option:checked"),e=>e.value);
this.sections = [];
this.tmpLoading = funktionen.join('###');
axios.get(this.apiurl + '/Config/presetBatch', {params: {
const params = {
db: this.dashboard,
funktionen
}}).then(res => {
if (this.tmpLoading !== funktionen.join('###'))
return; // NOTE(chris): prevent race condition
for (var section in res.data.retval) {
let widgets = [];
for (var wid in res.data.retval[section]) {
res.data.retval[section][wid].id = wid;
res.data.retval[section][wid].custom = 1;
widgets.push(res.data.retval[section][wid]);
};
return this.$api
.call(ApiDashboardPreset.getBatch(params))
.then(result => {
if (this.tmpLoading !== funktionen.join('###'))
return; // NOTE(chris): prevent race condition
for (var section in result.data) {
let widgets = [];
for (var wid in result.data[section]) {
result.data[section][wid].id = wid;
result.data[section][wid].custom = 1;
widgets.push(result.data[section][wid]);
}
this.sections.push({
name: section,
widgets
});
}
this.sections.push({
name: section,
widgets
});
}
}).catch(err => console.error('ERROR:', err));
})
.catch(this.$fhcAlert.handleSystemError);
},
loadFunktionen() {
this.$api
.call(ApiDashboardPreset.list(this.dashboard))
.then(result => {
this.funktionen = result.data;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
created() {
axios.get(this.apiurl + '/Config/funktionen').then(res => {
this.funktionen = {general: 'GENERAL'};
res.data.retval.forEach(funktion => {
this.funktionen[funktion.funktion_kurzbz] = funktion.beschreibung;
});
}).catch(err => console.error('ERROR:', err));
this.loadFunktionen();
},
watch: {
dashboard() {
// TODO(chris): this should be done without a watcher
this.loadSections({target:this.$refs.funktionenList});
this.loadFunktionen();
}
},
template: `<div class="dashboard-admin-presets">
<div class="row">
<div class="col-3">
<select ref="funktionenList" style="height:30em" class="form-control" multiple @input="loadSections">
<option v-for="name,id in funktionen" :key="id" :value="id">{{ name }}</option>
<option
v-for="funktion in funktionen"
:key="funktion.funktion_kurzbz"
:value="funktion.funktion_kurzbz"
:class="(funktion.has_preset > 0) ? 'fw-bold' : ''"
>{{ funktion.beschreibung }}</option>
</select>
</div>
<div class="col-9">
+13 -20
View File
@@ -1,3 +1,5 @@
import ApiDashboardWidget from "../../../api/factory/dashboard/widget.js";
export default {
emits: [
"change",
@@ -7,34 +9,25 @@ export default {
dashboard_id: Number,
widgets: Array
},
computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
}
},
methods: {
sendChange(widget_id) {
let allow = !this.widgets.find(el => el.widget_id == widget_id).allowed;
axios.post(this.apiurl + '/Widget/setAllowed', {
dashboard_id: this.dashboard_id,
widget_id,
action: allow ? 'add' : 'delete'
}).catch(err => console.error('ERROR: ' + err));
this.$api
.call(ApiDashboardWidget.setAllowed(this.dashboard_id, widget_id, allow))
.catch(this.$fhcAlert.handleSystemError);
}
},
created() {
axios.get(this.apiurl + '/Widget/getAll', {
params:{
dashboard_id: this.dashboard_id
}
}).then(
result => {
this.$emit('assignWidgets', result.data.retval.map(el => ({
this.$api
.call(ApiDashboardWidget.list(this.dashboard_id))
.then(result => {
this.$emit('assignWidgets', result.data.map(el => ({
...el,
...{setup:JSON.parse(el.setup),arguments:JSON.parse(el.arguments),allowed:!!el.allowed}
allowed: !!el.allowed
})));
}
).catch(err => console.error('ERROR:', err));
})
.catch(this.$fhcAlert.handleSystemError);
},
template: `
<div class="dashboard-admin-widgets">
+105 -138
View File
@@ -2,7 +2,8 @@ import DashboardSection from "./Section.js";
import DashboardWidgetPicker from "./Widget/Picker.js";
import ObjectUtils from "../../helpers/ObjectUtils.js";
import ApiDashboard from '../../api/factory/cis/dashboard.js';
import ApiDashboardWidget from '../../api/factory/dashboard/widget.js';
import ApiDashboardUser from '../../api/factory/dashboard/user.js';
export default {
name: 'Dashboard',
@@ -20,181 +21,147 @@ export default {
type: Object,
required: true,
validator(value) {
return value && value.name && value.uid && value.timezone
return value && value.name && value.timezone
}
}
},
data() {
return {
sections: [],
widgets: null,
editMode: false,
viewDataInternal: this.viewData
widgets: [],
originalWidgets: {},
widgetsSetup: null,
editMode: false
}
},
provide() {
return {
editMode: Vue.computed(()=>this.editMode),
widgetsSetup: Vue.computed(() => this.widgets),
widgetsSetup: Vue.computed(() => this.widgetsSetup),
timezone: Vue.computed(() => this.viewData.timezone)
}
},
computed: {
apiurl() {
return FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard';
}
},
methods: {
widgetAdd(section_name, widget) {
if (this.widgets === null) {
axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {params:{
db: this.dashboard
}}).then(res => {
res.data.retval.forEach(widget => {
widget.arguments = JSON.parse(widget.arguments);
widget.setup = JSON.parse(widget.setup);
});
this.widgets = res.data.retval;
}).catch(err => console.error('ERROR:', err));
}
this.$refs.widgetpicker.getWidget().then(widget_id => {
widget.widget = widget_id;
widget.id = 'loading_' + String((new Date()).valueOf());
let loading = {...widget};
loading.loading = true;
this.sections.forEach(section => {
if (section.name == section_name)
section.widgets.push(loading);
});
axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', {
db: this.dashboard,
funktion_kurzbz: section_name,
widgets: [widget]
}).then(result => {
let newId = Object.keys(result.data.retval.data[section_name].widgets).pop();
widget.id = newId;
this.sections.forEach(section => {
if (section.name == section_name) {
section.widgets.splice(section.widgets.indexOf(loading),1);
section.widgets.push(widget);
}
});
}).catch(error => {
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
}).catch(() => {});
// TODO(chris): remove section_name? (change order of params => get rid of it)
this.$refs.widgetpicker
.getWidget()
.then(widget_id => {
widget.widget = widget_id;
widget.id = 'loading_' + String((new Date()).valueOf());
let loading = { ...widget };
loading.loading = true;
this.widgets.push(loading);
this.$api
.call(ApiDashboardUser.addWidget(this.dashboard, widget))
.then(result => {
widget.id = result.data;
this.widgets.splice(this.widgets.indexOf(loading), 1);
this.widgets.push(widget);
this.originalWidgets[widget.id] = structuredClone(ObjectUtils.deepToRaw(widget));
})
.catch(this.$fhcAlert.handleSystemError);
})
.catch(() => {});
},
widgetUpdate(section_name, payload) {
payload = payload[section_name];
for (var k in payload) {
for (var i in this.sections) {
if (this.sections[i].name == section_name) {
for (var wid in this.sections[i].widgets) {
if (this.sections[i].widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(this.sections[i].widgets[wid], payload[k]);
// NOTE(chris): remove internal props
for (var prop in {_x:1,_y:1,_w:1,_h:1,index:1,id:1,preset:1})
if (payload[k][prop])
delete payload[k][prop];
break;
}
}
for (var wid in this.widgets) {
if (this.widgets[wid].id == k) {
payload[k] = ObjectUtils.mergeDeep(this.widgets[wid], payload[k]);
// NOTE(chris): remove internal props
for (var prop of ['_x','_y','_w','_h','index','id','preset'])
if (payload[k][prop])
delete payload[k][prop];
break;
}
}
payload[k].widgetid = k;
}
axios.post(this.apiurl + '/Config/addWidgetsToUserOverride', {
db: this.dashboard,
funktion_kurzbz: section_name,
widgets: payload
}).then(() => {
this.sections.forEach(section => {
if (section.name == section_name) {
section.widgets.forEach((widget, i) => {
if (payload[widget.id]) {
payload[widget.id].id = widget.id;
payload[widget.id].index = widget.index;
section.widgets[i] = payload[widget.id];
this.$api
.call(Object.entries(payload).map(([key, widget]) => [
key,
ApiDashboardUser.addWidget(this.dashboard, widget)
]))
.then(result => {
const failed = result
.filter(o => o.status == 'rejected')
.map(o => o.reason.config.errorHeader);
this.widgets.forEach((widget, i) => {
if (failed.includes(widget.id)) {
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
/** NOTE(chris): if you wanna hide or unhide a
* preset and it fails: switch around the hidden
* value to revert it properly (checkboxes can't
* really handle it otherwise)
*/
if (payload[widget.id].hidden !== undefined) {
this.widgets[i].hidden = payload[widget.id].hidden;
this.$nextTick(() => {
this.widgets[i] = structuredClone(ObjectUtils.deepToRaw(this.originalWidgets[widget.id]));
});
}
});
}
});
}).catch(error => {
// TODO(chris): revert placement on failure
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
} else if (payload[widget.id]) {
payload[widget.id].id = widget.id;
payload[widget.id].index = widget.index;
this.widgets[i] = payload[widget.id];
this.originalWidgets[widget.id] = structuredClone(ObjectUtils.deepToRaw(this.widgets[i]));
}
});
})
.catch(this.$fhcAlert.handleSystemError);
},
widgetRemove(section_name, id) {
axios.post(this.apiurl + '/Config/removeWidgetFromUserOverride', {
db: this.dashboard,
funktion_kurzbz: section_name,
widgetid: id
}).then(() => {
this.sections.forEach(section => {
if (section.name == section_name)
section.widgets = section.widgets.filter(widget => widget.id != id);
});
}).catch(error => {
console.error('ERROR: ', error);
alert('ERROR: ' + error.response.data.retval);
});
this.$api
.call(ApiDashboardUser.removeWidget(this.dashboard, id))
.then(() => {
this.widgets = this.widgets.filter(widget => widget.id != id);
})
.catch(this.$fhcAlert.handleSystemError);
}
},
created() {
this.$p.loadCategory('dashboard');
axios.get(this.apiurl + '/Widget/getWidgetsForDashboard', {
params: {
db: this.dashboard
}
}).then(res => {
this.widgets = res.data.retval;
}).catch(err => console.error('ERROR:', err));
axios.get(this.apiurl + '/Config', {params:{
db: this.dashboard
}}).then(res => {
for (var name in res.data.retval) {
let widgets = [];
let remove = [];
for (var wid in res.data.retval[name].widgets) {
res.data.retval[name].widgets[wid].id = wid;
if (res.data.retval[name].widgets[wid].custom || res.data.retval[name].widgets[wid].preset)
widgets.push(res.data.retval[name].widgets[wid]);
else
this.$api
.call(ApiDashboardWidget.listAllowed(this.dashboard))
.then(res => {
this.widgetsSetup = res.data;
})
.catch(this.$fhcAlert.handleSystemError);
this.$api
.call(ApiDashboardUser.get(this.dashboard))
.then(res => {
const widgets = [];
const remove = [];
for (var wid in res.data.general.widgets) {
let widget = res.data.general.widgets[wid];
widget.id = wid;
if (widget.custom || widget.preset) {
widgets.push(widget);
this.originalWidgets[wid] = structuredClone(widget);
} else {
remove.push(wid);
}
}
this.sections.push({
name: name,
widgets: widgets
});
remove.forEach(wid => this.widgetRemove(name, wid));
}
this.sections = this.sections.sort((section1, section2) => {
if(section1.name == 'custom')
return 1;
if (section2.name == 'custom')
return -1;
return section2.widgets.length - section1.widgets.length;
});
}).catch(err => console.error('ERROR:', err));
},
async beforeMount() {
if (!this.viewData.name || !this.viewData.uid) {
const res = await this.$api.call(ApiDashboard.getViewData());
this.viewDataInternal = res.data
}
remove.forEach(wid => this.widgetRemove('general', wid));
this.widgets = widgets;
})
.catch(this.$fhcAlert.handleSystemError);
},
template: `
<div class="core-dashboard">
<h3 v-show="viewDataInternal?.name">
{{ $p.t('global/personalGreeting', [ viewDataInternal?.name ]) }}
<h3>
{{ $p.t('global/personalGreeting', [ viewData?.name ]) }}
<button style="margin-left: 8px;" class="btn" @click="editMode = !editMode" aria-label="edit dashboard" v-tooltip="{showDelay:1000,value:'edit dashboard'}"><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
</h3>
<dashboard-section v-for="(section, index) in sections" :key="section.name" :seperator="index" :name="section.name" :widgets="section.widgets" @widgetAdd="widgetAdd" @widgetUpdate="widgetUpdate" @widgetRemove="widgetRemove"></dashboard-section>
<dashboard-widget-picker ref="widgetpicker" :widgets="widgets"></dashboard-widget-picker>
<dashboard-section :seperator="0" name="general" :widgets="widgets" @widgetAdd="widgetAdd" @widgetUpdate="widgetUpdate" @widgetRemove="widgetRemove"></dashboard-section>
<dashboard-widget-picker ref="widgetpicker" :widgets="widgetsSetup"></dashboard-widget-picker>
</div>`
}
+17 -3
View File
@@ -1,5 +1,5 @@
import BsModal from "../Bootstrap/Modal.js";
import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js";
import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
import HeightTransition from "../Tranistion/HeightTransition.js";
export default {
@@ -70,6 +70,14 @@ export default {
ready() {
return this.component && this.arguments !== null;
},
visible: {
get() {
return !this.hidden;
},
set(value) {
this.$emit('remove', this.hidden);
}
}
},
methods: {
unpin(){
@@ -142,8 +150,14 @@ export default {
this.isLoading = false;
},
},
setup() {
const { actions } = useCachedWidgetLoader();
return {
loadWidget: actions.load
};
},
async created() {
this.widget = await CachedWidgetLoader.loadWidget(this.id);
this.widget = await this.loadWidget(this.id);
let component = (await import(this.widget.setup.file)).default;
this.$options.components["widget" + this.widget.widget_id] = component;
this.component = "widget" + this.widget.widget_id;
@@ -185,7 +199,7 @@ export default {
</a>
<Transition>
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
<input class="form-check-input ms-0" type="checkbox" role="switch" aria-label="toggle widget" id="flexSwitchCheckChecked" :checked="!hidden" @input="$emit('remove', hidden)">
<input class="form-check-input ms-0" type="checkbox" role="switch" aria-label="toggle widget" id="flexSwitchCheckChecked" v-model="visible" :value="true">
</div>
</Transition>
</div>
+14 -7
View File
@@ -1,7 +1,7 @@
import BsConfirm from "../Bootstrap/Confirm.js";
import DropGrid from '../Drop/Grid.js'
import DashboardItem from "./Item.js";
import CachedWidgetLoader from "../../composables/Dashboard/CachedWidgetLoader.js";
import { useCachedWidgetLoader } from "../../composables/Dashboard/CachedWidgetLoader.js";
import WidgetIcon from "./Widget/WidgetIcon.js"
export default {
@@ -125,23 +125,23 @@ export default {
},
checkResizeLimit(item, w, h) {
// NOTE(chris): widgets needs to be loaded for this to work
let widget = CachedWidgetLoader.getWidget(item.widget);
let widget = this.widgetState[item.widget];
if (widget) {
let minmaxW = widget.setup.width;
let minmaxW = { ...widget.setup.width };
if (minmaxW.max)
minmaxW.min = minmaxW.min || 1;
else
minmaxW = {min:minmaxW,max:minmaxW};
minmaxW = { min: minmaxW, max: minmaxW };
if (w < minmaxW.min)
w = minmaxW.min;
if (w > minmaxW.max)
w = minmaxW.max;
let minmaxH = widget.setup.height;
let minmaxH = { ...widget.setup.height };
if (minmaxH.max)
minmaxH.min = minmaxH.min || 1;
else
minmaxH = {min:minmaxH,max:minmaxH};
minmaxH = { min: minmaxH, max: minmaxH };
if (h < minmaxH.min)
h = minmaxH.min;
if (h > minmaxH.max)
@@ -151,7 +151,7 @@ export default {
},
removeWidget(item, revert) {
if (item.custom) {
BsConfirm.popup('Are you sure you want to delete this widget?').then(() => this.$emit('widgetRemove', this.name, item.id));
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', this.name, item.id));
} else {
let update = {};
update[item.id] = { hidden: !revert };
@@ -199,6 +199,13 @@ export default {
this.$emit('widgetUpdate', this.name, payload);
}
},
setup() {
const { state: widgetState } = useCachedWidgetLoader();
return {
widgetState
};
},
mounted() {
let self = this;
let cont = self.$refs.container;
+4 -3
View File
@@ -170,6 +170,7 @@ export default {
return this.$attrs.modelValue;
},
set(v) {
this.clearValidationForThisName()
if (!this.$attrs.hasOwnProperty('modelValue'))
this.modelValueDummy = v;
this.$emit('update:modelValue', v);
@@ -242,9 +243,9 @@ export default {
template: `
<component :is="!hasContainer ? 'FhcFragment' : 'div'" class="position-relative" :class="autoContainerClass">
<label v-if="label && lcType != 'radio' && lcType != 'checkbox'" :class="!noAutoClass && 'form-label'" :for="idCmp">{{label}}</label>
<input v-if="tag == 'input'" :type="lcType" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidationForThisName(); $emit('input', $event)">
<textarea v-else-if="tag == 'textarea'" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidationForThisName(); $emit('input', $event)"></textarea>
<select v-else-if="tag == 'select'" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidationForThisName(); $emit('input', $event)">
<input v-if="tag == 'input'" :type="lcType" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="$emit('input', $event)">
<textarea v-else-if="tag == 'textarea'" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="$emit('input', $event)"></textarea>
<select v-else-if="tag == 'select'" ref="input" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="$emit('input', $event)">
<slot></slot>
</select>
<component
@@ -133,6 +133,7 @@ export default {
return this.$api
.call(ApiMessages.getDataVorlage(vorlage_kurzbz))
.then(response => {
this.editor.setContent(response.data.text);
this.formData.body = response.data.text;
this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError);
@@ -203,24 +204,6 @@ export default {
},
},
watch: {
'formData.body': {
handler(newVal) {
const tinymcsVal = this.editor.getContent();
if (newVal && tinymcsVal != newVal) {
//Inhalt des Editors aktualisieren
this.editor.setContent(newVal);
}
}
},
'formData.vorlage_kurzbz': {
handler(newVal){
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getDataVorlage(newVal);
}
}
},
messageId: {
immediate: true,
handler: async function (newMessageId) {
@@ -231,6 +214,7 @@ export default {
this.replyData = result.data;
if (this.replyData.length > 0) {
this.editor.setContent(this.replyData[0].replyBody);
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = newMessageId;
@@ -290,19 +274,6 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
//case of reply
if(this.messageId) {
this.$api
.call(ApiMessages.getReplyData(this.messageId))
.then(result => {
this.replyData = result.data;
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = this.messageId;
})
.catch(this.$fhcAlert.handleSystemError);
}
},
async mounted() {
this.initTinyMCE();
@@ -64,7 +64,16 @@ export default {
target: this.$refs.editor.$refs.input, //Important: not selector: to enable multiple import of component
//height: 800,
//plugins: ['lists'],
toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify',
toolbar: 'styleselect | bold italic underline | alignleft aligncenter alignright alignjustify | link',
plugins: 'link',
link_context_toolbar: true,
automatic_uploads: true,
default_link_target: "_blank",
link_title: true,
target_list: [
{ title: 'New tab', value: '_blank' },
{ title: 'Same tab', value: '_self' }
],
style_formats: [
{title: 'Blocks', block: 'div'},
{title: 'Paragraph', block: 'p'},
@@ -98,7 +107,8 @@ export default {
return this.$api
.call(ApiMessages.sendMessage(this.typeId, data))
.then(response => {
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent'));
if(this.openMode == "inSamePage")
this.$fhcAlert.alertSuccess(this.$p.t('ui', 'successSent'));
this.hideTemplate();
this.resetForm();
this.messageSent = true;
@@ -114,19 +124,17 @@ export default {
return this.$api
.call(ApiMessages.getDataVorlage(vorlage_kurzbz))
.then(response => {
this.editor.setContent(response.data.text);
this.formData.body = response.data.text;
this.formData.subject = response.data.subject;
}).catch(this.$fhcAlert.handleSystemError);
},
getPreviewText(){
console.log("subj" + this.formData.subject);
const data = new FormData();
data.append('data', JSON.stringify(this.formData.body));
data.append('ids', JSON.stringify(this.id));
console.log("subj" + this.formData.subject);
return this.$api
.call(ApiMessages.getPreviewText(
this.typeId, data))
@@ -195,6 +203,7 @@ export default {
.call(ApiMessages.getReplyData(messageId))
.then(result => {
this.replyData = result.data;
this.editor.setContent(this.replyData[0].replyBody);
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = messageId;
@@ -202,27 +211,6 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
}
},
watch: {
'formData.body': {
handler(newVal) {
const tinymcsVal = this.editor.getContent();
if (newVal && tinymcsVal != newVal) {
//Inhalt des Editors aktualisieren
this.editor.setContent(newVal);
}
}
},
'formData.vorlage_kurzbz': {
handler(newVal){
if (newVal && newVal != null) {
this.formData.subject = newVal;
return this.getDataVorlage(newVal);
}
}
},
},
created(){
const missingparamsmsgs = [];
if(!this.typeId)
@@ -291,17 +279,8 @@ export default {
.catch(this.$fhcAlert.handleSystemError);
//case of reply
if(this.messageId != null) {
if(this.messageId) {
this.loadReplyData(this.messageId);
/* this.$api
.call(ApiMessages.getReplyData(this.messageId))
.then(result => {
this.replyData = result.data;
this.formData.subject = this.replyData[0].replySubject;
this.formData.body = this.replyData[0].replyBody;
this.formData.relationmessage_id = this.messageId;
})
.catch(this.$fhcAlert.handleSystemError);*/
}
},
@@ -499,10 +478,10 @@ export default {
<div class="row">
<div class="col-6" style="border-right: 1px">
You can safely close this window.
You can safely close this window/tab.
</div>
<div class="col-6">
Sie können dieses Fenster schließen.
Fenster/Reiter kann geschlossen werden!
</div>
</div>
</div>
@@ -65,7 +65,14 @@ export default {
buildTreemap(messages) {
if (!messages || !messages.data || messages.data.length === 0)
{
return {data: [], last_page: 0};
if(this.tabulatorOptions.pagination)
{
return {data: [], last_page: 0};
}
else
{
return [];
}
}
const last_page = messages.meta.count;
@@ -106,7 +113,15 @@ export default {
// to avoid endless loop
if (iteration > messages.length) break;
}
return {data: messageNested, last_page: last_page};
if(this.tabulatorOptions.pagination)
{
return {data: messageNested, last_page: last_page};
}
else
{
return messageNested;
}
},
loadAjaxCall(url, config, params){
return this.$api.call(
@@ -252,7 +267,7 @@ export default {
frozen: true
}
],
pagination: true,
pagination: false,
paginationMode: "remote",
paginationSize: 15,
paginationInitialPage: 1,
+8 -6
View File
@@ -82,14 +82,16 @@ export default {
this.$refs.modalMsg.show();
}
else if (this.openMode == "inSamePage"){
console.log("in same Page");
this.isVisibleDiv = true;
if(messageId)
this.$refs.templateNewDivMessage.loadReplyData(messageId);
else
this.$refs.templateNewDivMessage.resetForm();
this.$refs.templateNewDivMessage.showTemplate();
this.$nextTick(() => {
if(messageId)
this.$refs.templateNewDivMessage.loadReplyData(messageId);
else
this.$refs.templateNewDivMessage.resetForm();
this.$refs.templateNewDivMessage.showTemplate();
});
}
else
console.log("no valid openMode");
@@ -74,6 +74,16 @@ export default {
],
'abschlussdokument_lehrgaenge.xml.php': [
'AbschlussdokumentLehrgaenge'
],
'microcredential.xml.php' : [
'microcredentialzertifikat_1',
'microcredentialzertifikat_2',
'microcredentialzertifikat_3',
'microcredentialzertifikat_4',
'microcredential_1',
'microcredential_2',
'microcredential_3',
'microcredential_4',
]
},
documentDropdownObject: {}
@@ -14,6 +14,9 @@ export default {
inject: {
defaultSemester: {
from: 'defaultSemester'
},
currentSemester: {
from: 'currentSemester'
}
},
computed: {
@@ -95,8 +98,9 @@ export default {
this.formData.themenbereich = null;
this.formData.projekttyp_kurzbz = null;
this.formData.firma = null;
this.formData.lehrveranstaltung_id = null;
this.formData.lehreinheit_id = null;
// dont reset these form fields for UX reasons
// this.formData.lehrveranstaltung_id = null;
// this.formData.lehreinheit_id = null;
this.formData.beginn = null;
this.formData.ende = null;
this.formData.freigegeben = true;
@@ -109,7 +113,7 @@ export default {
getFormData(newProjektarbeit, studiensemester_kurzbz, additional_lehrveranstaltung_id) {
this.additional_lehrveranstaltung_id = additional_lehrveranstaltung_id;
this.studiensemester = studiensemester_kurzbz || this.defaultSemester;
this.studiensemester = studiensemester_kurzbz || this.currentSemester;
this.newProjektarbeit = newProjektarbeit;
this.$api
@@ -121,7 +121,6 @@ export default {
height: 'auto',
minHeight: '100',
selectableRows: true,
selectableRows: 1,
index: 'betreuer_id',
persistence:{
columns: true, //persist column layout
@@ -1,29 +1,36 @@
let __widgets = {};
let __widgetsStarted = {};
let __path = FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + '/dashboard/Widget';
import ApiWidget from "../../api/factory/dashboard/widget.js";
export default {
getWidget(id) {
return __widgets[id];
},
loadWidget(id) {
if (__widgets[id])
return Promise.resolve(__widgets[id]);
if (__widgetsStarted[id])
return __widgetsStarted[id];
if (!__path)
return Promise.reject('Widget could not be loaded because there is no path yet!');
const promises = Vue.ref([]);
const stateRef = Vue.ref([]);
const state = Vue.readonly(stateRef);
__widgetsStarted[id] = new Promise((resolve, reject) => {
axios.get(__path, {params:{id}}).then(res => {
__widgets[id] = res.data.retval;
__widgetsStarted[id] = undefined;
resolve(__widgets[id]);
}).catch(error => reject(error.response.data.retval.error));
});
return __widgetsStarted[id];
},
setPath(path) {
__path = path;
export function useCachedWidgetLoader() {
const $api = Vue.inject('$api');
const $fhcAlert = Vue.inject('$fhcAlert');
function load(id) {
if (state.value[id])
return Promise.resolve(state.value[id]);
if (!promises.value[id])
promises.value[id] = new Promise((resolve, reject) => {
$api
.call(ApiWidget.get(id))
.then(res => {
stateRef.value[id] = res.data;
promises.value[id] = undefined;
resolve(state.value[id]);
})
.catch($fhcAlert.handleSystemError);
});
return promises.value[id];
}
return {
state,
actions: {
load
}
};
}
+70 -29
View File
@@ -1,31 +1,72 @@
export default {
/**
* Performs a deep merge of objects and returns new object. Does not modify
* objects (immutable) and merges arrays via concatenation.
*
* @param {...object} objects - Objects to merge
* @returns {object} New object with merged key/values
*/
mergeDeep(...objects) {
const isObject = obj => obj && typeof obj === 'object';
return objects.reduce((prev, obj) => {
Object.keys(obj).forEach(key => {
const pVal = prev[key];
const oVal = obj[key];
if (Array.isArray(pVal) && Array.isArray(oVal)) {
prev[key] = pVal.concat(...oVal);
}
else if (isObject(pVal) && isObject(oVal)) {
prev[key] = this.mergeDeep(pVal, oVal);
}
else {
prev[key] = oVal;
}
});
/**
* Performs a deep merge of objects and returns new object. Does not modify
* objects (immutable) and merges arrays via concatenation.
*
* @param {...object} objects - Objects to merge
* @returns {object} New object with merged key/values
*/
function mergeDeep(...objects) {
const isObject = obj => obj && typeof obj === 'object';
return objects.reduce((prev, obj) => {
Object.keys(obj).forEach(key => {
const pVal = prev[key];
const oVal = obj[key];
return prev;
}, {});
}
if (Array.isArray(pVal) && Array.isArray(oVal)) {
prev[key] = pVal.concat(...oVal);
}
else if (isObject(pVal) && isObject(oVal)) {
prev[key] = this.mergeDeep(pVal, oVal);
}
else {
prev[key] = oVal;
}
});
return prev;
}, {});
}
/**
* Extends VUEs toRaw() function to nested Proxies
* @see https://www.reddit.com/r/javascript/comments/10gzynk/deep_cloning_objects_in_javascript_the_modern_way/
*
* @param object sourceObj - Object to transform
* @returns object
*/
function deepToRaw(sourceObj) {
const objectIterator = input => {
if (Array.isArray(input))
return input.map(objectIterator);
if (Vue.isRef(input) || Vue.isReactive(input) || Vue.isProxy(input))
return objectIterator(Vue.toRaw(input));
if (input && typeof input === 'object') {
/** use custom handling of 'Date' objects to avoid data loss if treating it like any other object.
* reminder:
* typeof (new Date()) ==> 'object'
* Object.keys(new Date()) ==> []
*/
if (input instanceof Date)
return input;
return Object.keys(input).reduce((acc, key) => {
acc[key] = objectIterator(input[key]);
return acc;
}, {});
}
return input;
};
return objectIterator(sourceObj);
}
export {
mergeDeep,
deepToRaw
}
export default {
mergeDeep,
deepToRaw
}
+8 -8
View File
@@ -430,16 +430,16 @@ export default {
fhcApiAxios.interceptors.response.use(
response => {
if (response.config?.errorHandling == 'off'
|| response.config?.errorHandling === false
|| response.config?.errorHandling == 'fail')
const errorConfig = get_error_handler(response.config);
if (!errorConfig.success)
return clean_return_value(response);
// NOTE(chris): loop through errors
if (response.data.errors)
response.data.errors = response.data.errors.filter(
err => (response.config[err.type + 'ErrorHandler'] || app.config.globalProperties.$api._defaultErrorHandlers[err.type])(err, response.config)
);
const errors = popHandleableErrors(errorConfig, response.data.errors);
for (var type in errors) {
errorConfig.handler[type](errors[type]);
}
return clean_return_value(response);
},
+1
View File
@@ -144,6 +144,7 @@ foreach($ps->result as $row)
<ROLLE:stichtagsaktiv><![CDATA['.$stichtagsaktiv.']]></ROLLE:stichtagsaktiv>
<ROLLE:aktiv><![CDATA['.$aktiv.']]></ROLLE:aktiv>
<ROLLE:fgm><![CDATA['.$row->fgm.']]></ROLLE:fgm>
<ROLLE:faktiv><![CDATA['.($row->faktiv?'Ja':'Nein').']]></ROLLE:faktiv>
</RDF:Description>
</RDF:li>
';
+5998 -107
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -92,6 +92,8 @@ require_once('dbupdate_3.4/68744_StV_settings.php');
require_once('dbupdate_3.4/62889_reihungstest_ueberwachung_mit_constructor.php');
require_once('dbupdate_3.4/71399_dashboard_update_widget_paths.php');
require_once('dbupdate_3.4/71645_studvw_messagetab_ladezeit.php');
require_once('dbupdate_3.4/71566_studienordnungsdokument_neuer_organisationseinheitstyp_programm.php');
require_once('dbupdate_3.4/70376_lohnguide.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -239,6 +241,11 @@ $tabellen=array(
"hr.tbl_valorisierung_instanz" => array("updateamum", "oe_kurzbz", "valorisierungsdatum", "valorisierung_kurzbz", "beschreibung", "ausgewaehlt", "updatevon", "valorisierung_instanz_id"),
"hr.tbl_valorisierung_instanz_methode" => array("valorisierung_instanz_id", "valorisierung_methode_kurzbz", "beschreibung", "valorisierung_methode_parameter"),
"hr.tbl_valorisierung_methode" => array("beschreibung", "valorisierung_methode_kurzbz"),
"hr.tbl_lohnguide_jobfamilie" => array("jobfamilie_kurzbz", "bezeichnung", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_modellfunktion" => array("modellfunktion_kurzbz", "bezeichnung", "jobfamilie_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_modellstelle" => array("modellstelle_kurzbz", "bezeichnung", "grade", "modellfunktion_kurzbz", "aktiv", "sort", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_lohnguide_fachrichtung" => array("fachrichtung_kurzbz", "bezeichnung", "aktiv", "insertvon", "insertamum", "updatevon", "updateamum"),
"hr.tbl_vertragsbestandteil_lohnguide" => array("vertragsbestandteil_id", "stellenbezeichnung", "vordienstzeit", "fachrichtung_kurzbz", "modellstelle_kurzbz", "kommentar_person", "kommentar_modellstelle"),
"lehre.tbl_abschlussbeurteilung" => array("abschlussbeurteilung_kurzbz","bezeichnung","bezeichnung_english","sort"),
"lehre.tbl_abschlusspruefung" => array("abschlusspruefung_id","student_uid","vorsitz","pruefer1","pruefer2","pruefer3","abschlussbeurteilung_kurzbz","akadgrad_id","pruefungstyp_kurzbz","datum","uhrzeit","sponsion","anmerkung","updateamum","updatevon","insertamum","insertvon","ext_id","note","protokoll","endezeit","pruefungsantritt_kurzbz","freigabedatum"),
"lehre.tbl_abschlusspruefung_antritt" => array("pruefungsantritt_kurzbz","bezeichnung","bezeichnung_english","sort"),
+14 -1
View File
@@ -1,6 +1,19 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if ($result = @$db->db_query("SELECT 1 FROM system.tbl_app WHERE app='lvevaluierung' LIMIT 1"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO system.tbl_app (app) VALUES ('lvevaluierung');";
if(!$db->db_query($qry))
echo '<strong>system.tbl_app: '.$db->db_last_error().'</strong><br>';
else
echo ' system.tbl_app: lvevaluierung hinzugefügt<br>';
}
}
//Add column evaluierung to lehre.tbl_lehrveranstaltung
if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1"))
{
@@ -12,4 +25,4 @@ if(!@$db->db_query("SELECT evaluierung FROM lehre.tbl_lehrveranstaltung LIMIT 1"
echo '<strong>lehre.tbl_lehrveranstaltung '.$db->db_last_error().'</strong><br>';
else
echo '<br>Spalte evaluierung zu Tabelle lehre.tbl_lehrveranstaltung hinzugefügt';
}
}
+407
View File
@@ -0,0 +1,407 @@
<?php
if (! defined('DB_NAME')) exit('No direct script access allowed');
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_jobfamilie' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_jobfamilie (
jobfamilie_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(64) NOT NULL,
aktiv boolean DEFAULT FALSE,
sort smallint,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_jobfamilie_pkey PRIMARY KEY (jobfamilie_kurzbz)
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_jobfamilie TO vilesci;
INSERT INTO hr.tbl_lohnguide_jobfamilie(jobfamilie_kurzbz, bezeichnung,aktiv, sort, insertvon, insertamum) VALUES
('FÜHRUNG','Führung',true,1,'system',NOW()),
('ALLGEMEIN','Allgemein',true,2,'system',NOW()),
('TECHNIK','Technik',true,3,'system',NOW()),
('IT','IT',true,4,'system',NOW()),
('PRODUKTION','Produktion',true,5,'system',NOW()),
('HANDW_IH_LOG','Handwerk, Instandhaltung + Logistik',true,6,'system',NOW())
ON CONFLICT (jobfamilie_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Jobfamilie: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_jobfamilie wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_modellfunktion' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_modellfunktion (
modellfunktion_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(64) NOT NULL,
jobfamilie_kurzbz character varying(32) NOT NULL,
aktiv boolean DEFAULT FALSE,
sort smallint,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_modellfunktion_pkey PRIMARY KEY (modellfunktion_kurzbz),
CONSTRAINT tbl_lohnguide_modellfunktion_jobfamilie_fk FOREIGN KEY (jobfamilie_kurzbz) REFERENCES hr.tbl_lohnguide_jobfamilie (jobfamilie_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_modellfunktion TO vilesci;
INSERT INTO hr.tbl_lohnguide_modellfunktion(modellfunktion_kurzbz, bezeichnung, jobfamilie_kurzbz, aktiv, sort, insertvon, insertamum) VALUES
('FÜHRUNG_I','Führung I','FÜHRUNG',true,1,'system',NOW()),
('FÜHRUNG_II','Führung I','FÜHRUNG',true,2,'system',NOW()),
('FÜHRUNG_III','Führung I','FÜHRUNG',true,3,'system',NOW()),
('GF','Geschäftsführung','FÜHRUNG',true,4,'system',NOW()),
/* Allgemein */
('FK_ALLGM','Fachkraft Allgemein','ALLGEMEIN',true,5,'system',NOW()),
('SFK_ALLGM','Spezial-Fachkraft Allgemein','ALLGEMEIN',true,6,'system',NOW()),
('SP_ALLGM','Spezialist:in Allgemein','ALLGEMEIN',true,7,'system',NOW()),
('EXP_ALLGM','Expert:in Allgemein','ALLGEMEIN',true,8,'system',NOW()),
('TOP_EXP_ALLGM','Top-Expert:in Allgemein','ALLGEMEIN',true,9,'system',NOW()),
/* Technik */
('FK_TECH','Fachkraft Technik','TECHNIK',true,10,'system',NOW()),
('SFK_TECH','Spezial-Fachkraft Technik','TECHNIK',true,11,'system',NOW()),
('SP_TECH','Spezialist:in Technik','TECHNIK',true,12,'system',NOW()),
('EXP_TECH','Expert:in Technik','TECHNIK',true,13,'system',NOW()),
('TOP_EXP_TECH','Top-Expert:in Technik','TECHNIK',true,14,'system',NOW()),
/* IT */
('FK_IT','Fachkraft IT','IT',true,15,'system',NOW()),
('SFK_IT','Spezial-Fachkraft IT','IT',true,16,'system',NOW()),
('SP_IT','Spezialist:in IT','IT',true,17,'system',NOW()),
('EXP_IT','Expert:in IT','IT',true,18,'system',NOW()),
('TOP_EXP_IT','Top-Expert:in IT','IT',true,19,'system',NOW()),
/* Produktion */
('HK_PROD','Hilfskraft Produktion','PRODUKTION',true,20,'system',NOW()),
('FK_PROD','Fachkraft Produktion','PRODUKTION',true,21,'system',NOW()),
('SFK_PROD','Spezial-Fachkraft Produktion','PRODUKTION',true,22,'system',NOW()),
('SP_PROD','Spezialist:in Produktion','PRODUKTION',true,23,'system',NOW()),
/* Handwerk, Instandhaltung, Logistik */
('HK_HIL','Hilfskraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,24,'system',NOW()),
('FK_HIL','Fachkraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,25,'system',NOW()),
('SFK_HIL','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,26,'system',NOW()),
('SP_HIL','Spezialist:in Handwerk, Instandhaltung + Logistik','HANDW_IH_LOG',true,27,'system',NOW())
ON CONFLICT (modellfunktion_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Modellfunktion: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_modellfunktion wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_modellstelle' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_modellstelle (
modellstelle_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(128) NOT NULL,
grade int NOT NULL,
modellfunktion_kurzbz character varying(32) NOT NULL,
aktiv boolean DEFAULT FALSE,
sort smallint,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_modellstelle_pkey PRIMARY KEY (modellstelle_kurzbz),
CONSTRAINT tbl_lohnguide_modellstelle_modellfunktion_fk FOREIGN KEY (modellfunktion_kurzbz) REFERENCES hr.tbl_lohnguide_modellfunktion (modellfunktion_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_modellstelle TO vilesci;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('111','Führung III 1/5',9,'FÜHRUNG_III',true,13,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('112','Führung III 2/5',10,'FÜHRUNG_III',true,14,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('113','Führung III 3/5',11,'FÜHRUNG_III',true,15,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('114','Führung III 4/5',12,'FÜHRUNG_III',true,16,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('115','Führung III 5/5',13,'FÜHRUNG_III',true,17,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('121','Führung II 1/4',14,'FÜHRUNG_II',true,7,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('122a','Führung II 2a/4',15,'FÜHRUNG_II',true,8,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('122b','Führung II 2b/4',15,'FÜHRUNG_II',true,9,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('123a','Führung II 3a/4',16,'FÜHRUNG_II',true,10,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('123b','Führung II 3b/4',16,'FÜHRUNG_II',true,11,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('124','Führung II 4/4',17,'FÜHRUNG_II',true,12,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('131','Führung I 1/4',18,'FÜHRUNG_I',true,1,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('132a','Führung I 2a/4',19,'FÜHRUNG_I',true,2,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('132b','Führung I 2b/4',19,'FÜHRUNG_I',true,3,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('133a','Führung I 3a/4',20,'FÜHRUNG_I',true,4,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('133b','Führung I 3b/4',20,'FÜHRUNG_I',true,5,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('134','Führung I 4/4',21,'FÜHRUNG_I',true,6,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- GF
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('141','Geschäftsführung 1/5',22,'GF',true,18,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('142a','Geschäftsführung 2a/5',23,'GF',true,19,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('142b','Geschäftsführung 2b/5',23,'GF',true,20,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('143a','Geschäftsführung 3a/5',24,'GF',true,21,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('143b','Geschäftsführung 3b/5',24,'GF',true,22,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('144a','Geschäftsführung 4a/5',25,'GF',true,23,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('144b','Geschäftsführung 4b/5',25,'GF',true,24,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('145','Geschäftsführung 5/5',26,'GF',true,25,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Allgemein
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('211','Fachkraft Allgemein 1/3',4,'FK_ALLGM',true,26,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('212a','Fachkraft Allgemein 2a/3',5,'FK_ALLGM',true,27,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('212b','Fachkraft Allgemein 2b/3',5,'FK_ALLGM',true,28,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('213','Fachkraft Allgemein 3/3',6,'FK_ALLGM',true,29,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('221','Spezial-Fachkraft Allgemein 1/4', 7,'SFK_ALLGM',true,30,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('222a','Spezial-Fachkraft Allgemein 2a/4',8,'SFK_ALLGM',true,31,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('222b','Spezial-Fachkraft Allgemein 2b/4',8,'SFK_ALLGM',true,32,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('223a','Spezial-Fachkraft Allgemein 3a/4',9,'SFK_ALLGM',true,33,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('223b','Spezial-Fachkraft Allgemein 3b/4',9,'SFK_ALLGM',true,34,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('224','Spezial-Fachkraft Allgemein 4/4',10,'SFK_ALLGM',true,35,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('231','Spezialist:in Allgemein 1/4',11,'SP_ALLGM',true,36,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('232a','Spezialist:in Allgemein 2a/4',12,'SP_ALLGM',true,37,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('232b','Spezialist:in Allgemein 2b/4',12,'SP_ALLGM',true,38,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('233a','Spezialist:in Allgemein 3a/4',13,'SP_ALLGM',true,39,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('233b','Spezialist:in Allgemein 3b/4',13,'SP_ALLGM',true,40,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('234','Spezialist:in Allgemein 4/4',14,'SP_ALLGM',true,41,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('241','Expert:in Allgemein 1/4',15,'EXP_ALLGM',true,42,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('242a','Expert:in Allgemein 2a/4',16,'EXP_ALLGM',true,43,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('242b','Expert:in Allgemein 2b/4',16,'EXP_ALLGM',true,44,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('243a','Expert:in Allgemein 3a/4',17,'EXP_ALLGM',true,45,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('243b','Expert:in Allgemein 3b/4',17,'EXP_ALLGM',true,46,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('244','Expert:in Allgemein 4/4',18,'EXP_ALLGM',true,47,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('251','Top-Expert:in Allgemein 1/1',19,'TOP_EXP_ALLGM',true,48,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Technik
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('311','Fachkraft Technik 1/3',4,'FK_TECH',true,49,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('312a','Fachkraft Technik 2a/3',5,'FK_TECH',true,50,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('312b','Fachkraft Technik 2b/3',5,'FK_TECH',true,51,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('313','Fachkraft Technik 3/3',6,'FK_TECH',true,52,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('321','Spezial-Fachkraft Technik 1/4',7,'SFK_TECH',true,53,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('322a','Spezial-Fachkraft Technik 2a/4',8,'SFK_TECH',true,54,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('322b','Spezial-Fachkraft Technik 2b/4',8,'SFK_TECH',true,55,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('323a','Spezial-Fachkraft Technik 3a/4',9,'SFK_TECH',true,56,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('323b','Spezial-Fachkraft Technik 3b/4',9,'SFK_TECH',true,57,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('324','Spezial-Fachkraft Technik 4/4',10,'SFK_TECH',true,58,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('331','Spezialist:in Technik 1/4',11,'SP_TECH',true,59,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('332a','Spezialist:in Technik 2a/4',12,'SP_TECH',true,60,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('332b','Spezialist:in Technik 2b/4',12,'SP_TECH',true,61,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('333a','Spezialist:in Technik 3a/4',13,'SP_TECH',true,62,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('333b','Spezialist:in Technik 3b/4',13,'SP_TECH',true,63,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('334','Spezialist:in Technik 4/4',14,'SP_TECH',true,64,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('341','Expert:in Technik 1/4',15,'EXP_TECH',true,65,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('342a','Expert:in Technik 2a/4',16,'EXP_TECH',true,66,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('342b','Expert:in Technik 2b/4',16,'EXP_TECH',true,67,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('343a','Expert:in Technik 3a/4',17,'EXP_TECH',true,68,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('343b','Expert:in Technik 3b/4',17,'EXP_TECH',true,69,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('344','Expert:in Technik 4/4',18,'EXP_TECH',true,70,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('351','Top-Expert:in Technik 1/1',19,'TOP_EXP_TECH',true,71,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- IT
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('411','Fachkraft IT 1/2',5,'FK_IT',true,72,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('412','Fachkraft IT 2/2',6,'FK_IT',true,73,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('421','Spezial-Fachkraft IT 1/4',7,'SFK_IT',true,74,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('422','Spezial-Fachkraft IT 2/4',8,'SFK_IT',true,75,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('423','Spezial-Fachkraft IT 3/4',9,'SFK_IT',true,76,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('424','Spezial-Fachkraft IT 4/4',10,'SFK_IT',true,77,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('431','Spezialist:in IT 1/4',11,'SP_IT',true,78,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('432a','Spezialist:in IT 2a/4',12,'SP_IT',true,79,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('432b','Spezialist:in IT 2b/4',12,'SP_IT',true,80,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('433a','Spezialist:in IT 3a/4',13,'SP_IT',true,81,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('433b','Spezialist:in IT 3b/4',13,'SP_IT',true,82,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('434','Spezialist:in IT 4/4',14,'SP_IT',true,83,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('441','Expert:in IT 1/4',15,'EXP_IT',true,84,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('442','Expert:in IT 2/4',16,'EXP_IT',true,85,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('443','Expert:in IT 3/4',17,'EXP_IT',true,86,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('444','Expert:in IT 4/4',18,'EXP_IT',true,87,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('451','Top-Expert:in IT 1/1',19,'TOP_EXP_IT',true,88,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Produktion
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('511','Hilfskraft Produktion 1/4',1,'HK_PROD',true,89,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('512','Hilfskraft Produktion 2/4',2,'HK_PROD',true,90,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('513','Hilfskraft Produktion 3/4',3,'HK_PROD',true,91,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('514','Hilfskraft Produktion 4/4',4,'HK_PROD',true,92,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('521','Fachkraft Produktion 1/2',5,'FK_PROD',true,93,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('522','Fachkraft Produktion 2/2',6,'FK_PROD',true,94,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('531','Spezial-Fachkraft Produktion 1/4',7,'SFK_PROD',true,95,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('532','Spezial-Fachkraft Produktion 2/4',8,'SFK_PROD',true,96,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('533','Spezial-Fachkraft Produktion 3/4',9,'SFK_PROD',true,97,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('534','Spezial-Fachkraft Produktion 4/4',10,'SFK_PROD',true,98,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('541','Spezialist:in Produktion 1/4',11,'SP_PROD',true,99,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('542a','Spezialist:in Produktion 2a/4',12,'SP_PROD',true,100,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('542b','Spezialist:in Produktion 2b/4',12,'SP_PROD',true,101,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('543a','Spezialist:in Produktion 3a/4',13,'SP_PROD',true,102,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('543b','Spezialist:in Produktion 3b/4',13,'SP_PROD',true,103,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('544','Spezialist:in Produktion 4/4',14,'SP_PROD',true,104,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
-- Handwerk, Logistik, ..
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('611','Hilfskraft Handwerk, Instandhaltung + Logistik 1/4',1,'HK_HIL',true,105,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('612','Hilfskraft Handwerk, Instandhaltung + Logistik 2/4',2,'HK_HIL',true,106,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('613','Hilfskraft Handwerk, Instandhaltung + Logistik 3/4',3,'HK_HIL',true,107,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('614','Hilfskraft Handwerk, Instandhaltung + Logistik 4/4',4,'HK_HIL',true,108,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('621','Fachkraft Handwerk, Instandhaltung + Logistik 1/2',5,'FK_HIL',true,109,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('622','Fachkraft Handwerk, Instandhaltung + Logistik 2/2',6,'FK_HIL',true,110,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('631','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 1/4',7,'SFK_HIL',true,111,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('632','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 2/4',8,'SFK_HIL',true,112,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('633','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 3/4',9,'SFK_HIL',true,113,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
INSERT INTO hr.tbl_lohnguide_modellstelle(modellstelle_kurzbz, bezeichnung, grade, modellfunktion_kurzbz, aktiv, sort, insertvon, insertamum) VALUES('634','Spezial-Fachkraft Handwerk, Instandhaltung + Logistik 4/4',10,'SFK_HIL',true,114,'system',NOW()) ON CONFLICT (modellstelle_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Modellstelle: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_modellstelle wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_lohnguide_fachrichtung' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_lohnguide_fachrichtung (
fachrichtung_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(32) NOT NULL,
aktiv boolean DEFAULT FALSE,
insertvon character varying(32) NOT NULL,
insertamum timestamp without time zone DEFAULT now() NOT NULL,
updatevon character varying(32),
updateamum timestamp without time zone,
CONSTRAINT tbl_lohnguide_fachrichtung_pkey PRIMARY KEY (fachrichtung_kurzbz)
);
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_lohnguide_fachrichtung TO vilesci;
INSERT INTO hr.tbl_lohnguide_fachrichtung(fachrichtung_kurzbz,bezeichnung,aktiv,insertvon,insertamum) VALUES
('FA00','Keine Berücksichtigung',true,'system',NOW()),
('FA01','Administration allgemein',true,'system',NOW()),
('FA02','Dienste Infrastruktur',true,'system',NOW()),
('FA03','Finanzwesen & Controlling',true,'system',NOW()),
('FA04','IT',true,'system',NOW()),
('FA05','Logistik',true,'system',NOW()),
('FA06','Marketing & Digitales Marketing',true,'system',NOW()),
('FA07','Produktion',true,'system',NOW()),
('FA08','Technik',true,'system',NOW()),
('FA09','Verkauf',true,'system',NOW())
ON CONFLICT (fachrichtung_kurzbz) DO NOTHING;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide Fachrichtung: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_lohnguide_fachrichtung wurde neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.tables WHERE table_name='tbl_vertragsbestandteil_lohnguide' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
CREATE TABLE IF NOT EXISTS hr.tbl_vertragsbestandteil_lohnguide (
vertragsbestandteil_id integer NOT NULL,
vordienstzeit int,
stellenbezeichnung varchar(255),
fachrichtung_kurzbz character varying(32) NOT NULL,
modellstelle_kurzbz character varying(32) NOT NULL,
kommentar_person varchar(255),
kommentar_modellstelle varchar(255),
CONSTRAINT tbl_vertragsbestandteil_lohnguide_pk PRIMARY KEY (vertragsbestandteil_id),
CONSTRAINT tbl_vertragsbestandteil_fk FOREIGN KEY (vertragsbestandteil_id) REFERENCES hr.tbl_vertragsbestandteil (vertragsbestandteil_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT tbl_vertragsbestandteil_lohnguide_fachrichtung_fk FOREIGN KEY (fachrichtung_kurzbz) REFERENCES hr.tbl_lohnguide_fachrichtung (fachrichtung_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT tbl_vertragsbestandteil_modellstelle_fachrichtung_fk FOREIGN KEY (modellstelle_kurzbz) REFERENCES hr.tbl_lohnguide_modellstelle (modellstelle_kurzbz) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE
);
COMMENT ON TABLE hr.tbl_vertragsbestandteil_lohnguide IS E'Zuordnung für EU-Entgelttransparenzrichtlinie';
GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE hr.tbl_vertragsbestandteil_lohnguide TO vilesci;
";
if (! $db->db_query($qry))
echo '<strong>Vertragsbestandteil Lohnguide: ' . $db->db_last_error() . '</strong><br>';
else
echo 'hr.tbl_vertragsbestandteil_lohnguide wurde neu erstellt<br>';
}
}
if($result = $db->db_query("SELECT 1 FROM hr.tbl_vertragsbestandteiltyp WHERE vertragsbestandteiltyp_kurzbz = 'lohnguide'"))
{
if($db->db_num_rows($result) === 0)
{
$qry = "insert into hr.tbl_vertragsbestandteiltyp (vertragsbestandteiltyp_kurzbz,bezeichnung,ueberlappend) values('lohnguide','Lohnguide',false)";
if(!$db->db_query($qry))
echo '<strong>Public Tabelle person: '.$db->db_last_error().'</strong><br>';
else
echo "<br>Vertragsbestandteiltyp 'lohnguide' hinzugefuegt";
}
}
if ($result = $db->db_query("SELECT * FROM information_schema.columns WHERE column_name='vordienstzeit' AND table_name='tbl_vertragsbestandteil_lohnguide' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
ALTER TABLE
hr.tbl_vertragsbestandteil_lohnguide
ADD COLUMN
vordienstzeit int;
";
if (! $db->db_query($qry))
echo '<strong>Lohnguide: ' . $db->db_last_error() . '</strong><br>';
else
echo 'Spalte vordienstzeit wurde in hr.tbl_vertragsbestandteil_lohnguide neu erstellt<br>';
}
}
if ($result = $db->db_query("SELECT * FROM hr.tbl_gehaltstyp WHERE gehaltstyp_kurzbz='ueberstundenpauschale'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
INSERT INTO hr.tbl_gehaltstyp
(gehaltstyp_kurzbz, bezeichnung, valorisierung, sort, aktiv, lvexport)
VALUES
('ueberstundenpauschale','Überstundenpauschale', true, 8, true, true);
";
if (! $db->db_query($qry))
echo '<strong>Gehaltstyp: ' . $db->db_last_error() . '</strong><br>';
else
echo 'Gehaltstyp "Überstundenpauschale" erstellt.<br />';
}
}
if ($result = $db->db_query("SELECT * FROM hr.tbl_gehaltstyp WHERE gehaltstyp_kurzbz='sachbezug_pkw'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
INSERT INTO hr.tbl_gehaltstyp
(gehaltstyp_kurzbz, bezeichnung, valorisierung, sort, aktiv, lvexport)
VALUES
('sachbezug_pkw','Sachbezug PKW', true, 9, true, true);
";
if (! $db->db_query($qry))
echo '<strong>Gehaltstyp: ' . $db->db_last_error() . '</strong><br>';
else
echo 'Gehaltstyp "Sachbezug PKW" erstellt.<br />';
}
}
@@ -0,0 +1,15 @@
<?php
// neuen Organisationseinheittyp (Programm) als Zeile hinzufügen
if($result = @$db->db_query("SELECT 1 FROM public.tbl_organisationseinheittyp WHERE organisationseinheittyp_kurzbz= 'Programm';"))
{
if($db->db_num_rows($result) == 0)
{
$qry = "INSERT INTO public.tbl_organisationseinheittyp(organisationseinheittyp_kurzbz, beschreibung, bezeichnung) VALUES ('Programm', 'Programm', 'Programm');";
if(!$db->db_query($qry))
echo '<strong>public.tbl_organisationseinheittyp: '.$db->db_last_error().'</strong><br>';
else
echo '<br>public.tbl_organisationseinheittyp: Zeile Programm hinzugefuegt!<br>';
}
}
+1233 -4
View File
File diff suppressed because it is too large Load Diff
+16 -7
View File
@@ -875,7 +875,6 @@ function _getLehrecontainer($sws_proStg_arr)
$sws_proStg_arr = array_filter($sws_proStg_arr, function ($obj) {
return
!in_array($obj->studiengang_kz, BIS_EXCLUDE_STG) &&
$obj->studiengang_kz > 0 &&
$obj->studiengang_kz < 10000;
});
}
@@ -886,13 +885,17 @@ function _getLehrecontainer($sws_proStg_arr)
{
$is_sommersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'SS';
$is_wintersemester = substr($sws_proStg->studiensemester_kurzbz, 0, 2) == 'WS';
$is_lehrgang = isset($sws_proStg->lgartcode);
$kennzeichen_name = $is_lehrgang ? 'LehrgangNr' : 'StgKz';
// Lehreobjekt generieren
if (empty($lehre_arr) || !lehre_stg_exists($sws_proStg->studiengang_kz, $lehre_arr))
{
$lehre_obj = new StdClass();
$lehre_obj->StgKz = setLeadingZero(intval($sws_proStg->studiengang_kz), 4);
$lehre_obj->{$kennzeichen_name} = $sws_proStg->melde_studiengang_kz;
//~ $lehre_obj->StgKz = setLeadingZero(intval($sws_proStg->studiengang_kz), 4);
$lehre_obj->SommersemesterSWS = $is_sommersemester ? $sws_proStg->sws : 0.00;
$lehre_obj->WintersemesterSWS = $is_wintersemester ? $sws_proStg->sws : 0.00;
@@ -1020,9 +1023,14 @@ function _generateXML($person_arr)
foreach ($person->lehre_arr as $lehre)
{
$xml .= '<Lehre>';
$xml .= '<StgKz><![CDATA['. $lehre->StgKz. ']]></StgKz>';
$xml .= '<SommersemesterSWS><![CDATA['. $lehre->SommersemesterSWS. ']]></SommersemesterSWS>';
$xml .= '<WintersemesterSWS><![CDATA['. $lehre->WintersemesterSWS. ']]></WintersemesterSWS>';
if (isset($lehre->LehrgangNr))
$xml .= '<LehrgangNr><![CDATA['. $lehre->LehrgangNr. ']]></LehrgangNr>';
else
$xml .= '<StgKz><![CDATA['. $lehre->StgKz. ']]></StgKz>';
$xml .= '<SommersemesterSWS><![CDATA['. number_format($lehre->SommersemesterSWS, 2, '.', ''). ']]></SommersemesterSWS>';
$xml .= '<WintersemesterSWS><![CDATA['. number_format($lehre->WintersemesterSWS, 2, '.', ''). ']]></WintersemesterSWS>';
$xml .= '</Lehre>';
}
@@ -1211,7 +1219,7 @@ function _outputHTML($person_arr)
{
echo '
<tr>
<td>'. $lehre->StgKz. '</td>
<td>'. (isset($lehre->LehrgangNr) ? $lehre->LehrgangNr : $lehre->StgKz). '</td>
<td>'. $lehre->SommersemesterSWS. '</td>
<td>'. $lehre->WintersemesterSWS. '</td>
</tr>';
@@ -1359,7 +1367,8 @@ function lehre_stg_exists($studiengang_kz, $lehre_arr)
{
foreach($lehre_arr as $row)
{
if($row->StgKz == $studiengang_kz)
$kennzeichenName = $row->LehrgangNr ?? $row->StgKz;
if(isset($row->{$kennzeichenName}) && $row->{$kennzeichenName} == $melde_studiengang_kz)
return true;
}
return false;