Compare commits

...
Author SHA1 Message Date
Harald Bamberger 47716f1aff use new endpoint in frontend, skip next semester spezialgruppen 2026-10-09 16:51:17 +02:00
Harald Bamberger 682ad7c0e8 further progress 2026-10-09 16:07:28 +02:00
Harald Bamberger 8b68e60ae8 work in progress 2026-10-09 15:14:51 +02:00
Manuela Thamer bdf8b3c609 add comments, use array_filter of studentlehrverband 2026-09-09 11:58:03 +02:00
Manuela Thamer f616d70a5d extend logic for approximating FAS result of getting list LvTermine
- extend timeInterval for followup semester
- refactor function applyLoadUeberSemesterHaelfte
- use ausbildungssemester of student for semesterFilter in function getStundenplanQuery
- use old logic of studentlehrverbaende to avoid error for arrayfilter studentlehrverbaende
2026-09-08 16:11:45 +02:00
Manuela Thamer e70449046e Merge branch 'master' into bug-78491/StudVw_Lvv_LvTermine 2026-08-31 14:58:32 +02:00
Manuela Thamer 53e64cc83f capitalize table headers 2026-08-27 17:18:05 +02:00
Manuela Thamer 4f8ffa1228 button group for lvplan dev and prod 2026-08-27 15:46:25 +02:00
Harald Bamberger 6d641bbd06 pass in uid by reference 2026-08-24 13:56:55 +02:00
Harald Bamberger 3455aed3f5 Merge branch 'bhdev_master_stundenplan' 2026-08-24 13:46:09 +02:00
Harald Bamberger f84d12277d remove debug code 2026-08-24 07:31:47 +02:00
Harald Bamberger 9bf5bc2ad9 more generic bugfix, to use studentlehrverband only with date_range for the same studiensemester_kurzbz 2026-08-24 07:26:12 +02:00
Harald Bamberger 27765606e3 bufix akadGrad by sex if exists 2026-08-20 19:31:37 +02:00
Manuela Thamer a826bd7bc7 merge master in bug-78491/StudVw_Lvv_LvTermine 2026-08-20 17:05:07 +02:00
Harald Bamberger 75d2cabdbe bugfix lv-plan 2026-08-20 15:10:26 +02:00
Harald Bamberger bae9d6d9a0 Merge branch 'feature-78257/StudVW_Und_Tags_Finetuning' 2026-08-19 17:11:12 +02:00
Harald Bamberger 43d6a882ce add padding to tags buttons, remove orphanded template closing tag 2026-08-19 17:07:25 +02:00
Harald Bamberger 7f3b4100ab Merge branch 'master' into feature-78257/StudVW_Und_Tags_Finetuning 2026-08-19 15:07:07 +02:00
Harald Bamberger 9f54ecce54 Merge branch 'epic/LVEvaluierung-Neu' 2026-08-19 13:59:00 +02:00
Cristina 7af0f4a478 Merge remote-tracking branch 'origin/epic/LVEvaluierung-Neu' into epic/LVEvaluierung-Neu 2026-08-19 13:35:25 +02:00
Cristina 0f843db33d Merge branch 'master' into epic/LVEvaluierung-Neu 2026-08-19 13:34:53 +02:00
Cristina 3e59648d5b Merge branch 'master' into epic/LVEvaluierung-Neu 2026-08-19 12:55:38 +02:00
Cristina 5923991848 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-08-19 12:55:03 +02:00
Harald Bamberger 55a6ec9d0d fix handling of grundbetrag and betrag_valorisiert 2026-08-19 11:48:22 +02:00
chfhtw 78505589c8 Bugfix: racecondition renderers 2026-08-19 09:45:15 +02:00
Harald Bamberger 0e985113ec Merge branch 'feature-77666/Details_anzeigen_der_Valorisierung_pro_Person' 2026-08-19 08:20:42 +02:00
Harald Bamberger 8d9113d5c8 Merge branch 'master' into feature-77666/Details_anzeigen_der_Valorisierung_pro_Person 2026-08-19 08:06:16 +02:00
Harald Bamberger 7ce81ad4bd Merge branch 'feature-78292/dv_gehaltsbestandteil_ersteinstufung' 2026-08-19 07:57:07 +02:00
Harald Bamberger ea56ec8daa change wording 2026-08-19 07:50:50 +02:00
Harald Bamberger bf5ee2f64b Merge branch 'feature-78139/CIS4_Dashboard_ausgeblendete_Widgets' 2026-08-18 17:51:40 +02:00
Harald Bamberger 55d6d90fa6 Merge branch 'master' into feature-78139/CIS4_Dashboard_ausgeblendete_Widgets 2026-08-18 17:47:13 +02:00
Harald Bamberger 6adc55a843 Merge branch 'feature/78128-quick-copy-full-name-and-title' 2026-08-18 17:39:44 +02:00
Harald Bamberger 52c1fba17b Merge branch 'bugfix/78127-degree-programs-page-localization-ineffective' 2026-08-18 16:53:49 +02:00
Cristina 5e15d4b219 Add Distinct to getLeitung in Studiengang_model.php
Avoid multiple records for same active STGL.
e.g. double entries
2026-08-18 16:51:39 +02:00
Harald Bamberger 00954f680b Merge branch 'bugfix/78392-missing-searchbar-is-mobile-injectable' 2026-08-18 16:48:43 +02:00
Harald Bamberger 92d27fc242 Merge branch 'master' into feature/78128-quick-copy-full-name-and-title 2026-08-18 14:52:41 +02:00
Harald Bamberger f6bb27cdb0 Merge branch 'master' into bugfix/78127-degree-programs-page-localization-ineffective 2026-08-18 14:49:16 +02:00
Harald Bamberger 558963cd74 Merge branch 'master' into feature-78139/CIS4_Dashboard_ausgeblendete_Widgets 2026-08-18 14:42:44 +02:00
Harald Bamberger 0b5c0f27a5 Merge branch 'bugfix/77738-persisting-lv-menu' 2026-08-14 11:58:42 +02:00
adisposkofh f846bb221a refetching lv menu after a different calendar event is selected 2026-08-14 11:47:31 +02:00
Werner Masik d8355ba3f5 added Funktionsänderung to gehaltsanpassungtyp 2026-08-13 14:56:04 +02:00
ma0068 afb8011347 bugfix manual rebuilding of Tag ZGV Missing 2026-08-13 12:57:49 +02:00
Harald Bamberger 6d5a4531e1 use tbl_studentlehrverband.semester instead of tbl_student.semester 2026-08-12 16:58:21 +02:00
Werner Masik d85a3cb08f extract DB migration for gehaltsanpassungtyp into separate file 2026-08-12 15:18:35 +02:00
ma0068 15beaa9be8 Merge branch 'feature-78257/StudVW_Und_Tags_Finetuning' of github.com:FH-Complete/FHC-Core into feature-78257/StudVW_Und_Tags_Finetuning 2026-08-12 12:42:10 +02:00
ma0068 574057da88 add masterzgv to CoremissingZgvLib 2026-08-12 12:41:39 +02:00
Harald Bamberger c81b5338de Merge branch 'master' into feature-78292/dv_gehaltsbestandteil_ersteinstufung 2026-08-12 11:39:49 +02:00
Harald Bamberger c433ff5c3a remove public/vendor symlink since created by composer install 2026-08-12 11:39:16 +02:00
Harald Bamberger a5797a0bde Merge branch 'master' into feature-78257/StudVW_Und_Tags_Finetuning 2026-08-11 18:00:17 +02:00
adisposkofh 772b8c603c fixed issue with ineffective localization of courses in 'degree programs' page 2026-08-11 15:50:24 +02:00
Cristina 37d5f2da5d Merge branch 'master' into epic/LVEvaluierung-Neu 2026-08-11 15:45:27 +02:00
Cristina 27c39cfdca Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-08-11 15:45:08 +02:00
Werner Masik d66fe72cc9 add field for salary adaption type (Ersteinstufung, indiv. Erhöhung, ..) 2026-08-11 11:18:36 +02:00
adisposkofh 59ac56374e removed redundant response meta used for dev 2026-08-11 09:44:41 +02:00
adisposkofh a397460e0e Merge branch 'master' into feature/78128-quick-copy-full-name-and-title 2026-08-11 09:43:27 +02:00
adisposkofh 108f91ea1a quick copy option on full name and title in profile and other lv plan 2026-08-10 17:09:31 +02:00
adisposkofh dc5a846c10 defaulting searchbar isMobile injectable to false when not provided 2026-08-10 16:24:58 +02:00
ma0068 06895510e0 add reloadList if language is changed 2026-08-10 14:25:07 +02:00
ma0068 83b06b6c50 - Redesign Tags
- use Slot in Filter Component
- use Phrase  for relaoding Tag in Detail Header
2026-08-10 14:04:45 +02:00
Werner Masik bd99f3f927 add db schema for gehaltsanpassungtyp 2026-08-10 12:20:30 +02:00
Cristina eeb1529b64 Merge branch 'master' into epic/LVEvaluierung-Neu 2026-08-10 12:07:45 +02:00
Cristina 98c3707802 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-08-10 12:07:01 +02:00
Cristina e5c8807f17 Merge branch 'master' of https://github.com/FH-Complete/FHC-Core 2026-08-05 15:13:35 +02:00
chfhtw 2f86a1b017 Changing the edit button into a toggle 2026-08-04 10:46:08 +02:00
chfhtw 75ddbdf7c1 Recovery items on top 2026-08-04 10:24:29 +02:00
chfhtw d824334538 Hiding widgets should look like deleting and recovering is moved to the "add widget" popup 2026-08-04 09:42:18 +02:00
chfhtw 13515b2522 Bugfix: after a drag and drop operation, when resizing the page, the moved item stays in the drop position even if it has had a position in the resized layout 2026-08-04 09:40:47 +02:00
Alexei Karpenko c5c29df6cc Gehaltsbestandteil, Gehaltsbestandteil_model: added gehaltstyp_bezeichnung 2026-07-14 20:51:41 +02:00
ma0068 6324c3d467 add subquery for showing lvs of stundenplan in dependance of new parameter, deleting unused part of old parameter 2026-03-16 09:08:52 +01:00
ma0068 377c53cf60 add sql part 2026-03-12 14:12:53 +01:00
35 changed files with 1128 additions and 451 deletions
@@ -114,6 +114,7 @@ class LvPlan extends FHCAPI_Controller
// fetching lvplan events
$result = $this->stundenplanlib->getEventsUser($start_date, $end_date, $uid);
$lvplanEvents = $this->getDataOrTerminateWithError($result);
// fetching moodle events
@@ -62,6 +62,8 @@ class OtherLvPlan extends FHCAPI_Controller
"foto" => $profileData->data->foto,
"vorname" => $profileData->data->vorname,
"nachname" => $profileData->data->nachname,
"titel" => $profileData->data->titel,
"postnomen" => $profileData->data->postnomen,
],
];
@@ -11,6 +11,7 @@ class LvTermine extends FHCAPI_Controller
parent::__construct([
'getStundenplan' => ['admin:r', 'assistenz:r'],
'getStudiensemester' => ['admin:r', 'assistenz:r'],
'getStundenplanForStudiensemester' => ['admin:r', 'assistenz:r'],
]);
// Load Libraries
@@ -32,37 +33,147 @@ class LvTermine extends FHCAPI_Controller
$this->load->model('person/Benutzergruppe_model', 'BenutzergruppeModel');
}
//TODO Build own lib or combine with Controller Stundenplan.php
//here use of logic of Stundenplan.php, extended with parameters uid, grouping, and used dbTable
public function getStundenplan($uid, $start_date = null, $end_date = null, $dbStundenplanTable = "stundenplan", $groupConsecutiveHours = false)
{
$this->getStundenplanbh($uid, '2026-02-04', '2027-02-03', $dbStundenplanTable, $groupConsecutiveHours);
}
public function getStundenplanForStudiensemester($uid, $studiensemester_kurzbz, $dbStundenplanTable = "stundenplan", $groupConsecutiveHours = false)
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$resstudiensemester = getData($this->StudiensemesterModel->getStartEndeFromStudiensemester($studiensemester_kurzbz));
$resnextstudiensemester = getData($this->StudiensemesterModel->getNextFrom($studiensemester_kurzbz));
$studiensemester = ($resstudiensemester && count($resstudiensemester) > 0) ? $resstudiensemester[0] : null;
$studiensemester->studiensemester_kurzbz = $studiensemester_kurzbz;
$nextstudiensemester = ($resnextstudiensemester && count($resnextstudiensemester) > 0) ? $resnextstudiensemester[0] : null;
$this->addMeta('bhsemester', $studiensemester);
$this->addMeta('bhnextsemester', $nextstudiensemester);
$this->getStundenplanbh($uid, $studiensemester, $nextstudiensemester, $dbStundenplanTable, $groupConsecutiveHours);
}
//TODO Build own lib or combine with Controller Stundenplan.php
//here use of logic of Stundenplan.php, extended with parameters uid, grouping, and used dbTable
public function getStundenplanbh($uid, $studiensemester, $nextstudiensemester, $dbStundenplanTable = "stundenplan", $groupConsecutiveHours = false)
{
$start_date = $studiensemester->start;
$end_date = $nextstudiensemester->ende;
$this->load->library('StundenplanLib');
$ci = get_instance();
$student_uid = $uid;
$semester_range = $this->studienSemesterErmitteln($start_date, $end_date);
$semester_range = $this->stundenplanlib->studienSemesterErmitteln($start_date, $end_date);
if (isError($semester_range))
return $semester_range;
$semester_range = getData($semester_range);
$ci->addMeta('semester_range', $semester_range);
$first_semester = $semester_range[0];
$this->sortStudienSemester($semester_range);
$this->applyLoadUeberSemesterHaelfte($semester_range);
$function_error = $this->stundenplanlib->applyLoadUeberSemesterHaelfte($semester_range);
$ci->addMeta('nach_apply_semester_range', $semester_range);
if ($function_error)
return $function_error;
$benutzer_gruppen = $this->fetchBenutzerGruppenFromStudiensemester($semester_range, $student_uid);
$student_lehrverband = $this->fetchStudentlehrverbandFromStudiensemester($semester_range, $student_uid);
$benutzer_gruppen = $this->fetchBenutzerGruppenFromStudiensemester($student_uid, $semester_range, $nextstudiensemester->studiensemester_kurzbz);
if (isError($benutzer_gruppen))
return $benutzer_gruppen;
$benutzer_gruppen = getData($benutzer_gruppen);
$ci->addMeta('spezialgruppen', $benutzer_gruppen);
// getting the student_lehrverbaende of the student in the different studiensemester
if(!$groupConsecutiveHours)
$stundenplan_query = $this->StundenplanModel->getStundenplanQuery(
$start_date,
$end_date,
$semester_range,
$benutzer_gruppen,
$student_lehrverband
);
else
$stundenplan_query = $this->StundenplanModel->getStundenplanQuery(
$start_date,
$end_date,
$semester_range,
$benutzer_gruppen,
$student_lehrverband,
true,
$dbStundenplanTable
);
$ci->addMeta('bhsemtest', print_r($semester_range, true));
/*
$semester_range = array(
'SS2026' => array(
'WS2025' => (object) array(
'start' => '2026-02-04',
'ende' => '2026-05-19'
),
'SS2026' => (object) array(
'start' => '2026-02-04',
'ende' => '2026-11-15'
),
'WS2026' => (object) array(
'start' => '2026-09-01',
'ende' => '2027-02-03'
)
)
);
*/
$student_lehrverband = $this->fetchStudentlehrverbandFromStudiensemester($student_uid, $semester_range, $studiensemester->studiensemester_kurzbz, $nextstudiensemester->studiensemester_kurzbz);
//get semester
if (isError($student_lehrverband))
return $student_lehrverband;
$student_lehrverband = getData($student_lehrverband);
/*
$student_lehrverband = array (
'SS2026' =>
array (
0 =>
(object) array(
'verbandsgruppe' => 'BEE1A',
'studiengang_kz' => 476,
'semester' => 1,
'verband' => 'A',
'gruppe' => ' ',
'studiensemester_kurzbz' => 'WS2025',
),
1 =>
(object) array(
'verbandsgruppe' => 'BEE2A',
'studiengang_kz' => 476,
'semester' => 2,
'verband' => 'A',
'gruppe' => ' ',
'studiensemester_kurzbz' => 'SS2026',
),
2 =>
(object) array(
'verbandsgruppe' => 'BEE3A',
'studiengang_kz' => 476,
'semester' => 3,
'verband' => 'A',
'gruppe' => ' ',
'studiensemester_kurzbz' => 'SS2026',
),
),
);
*/
$ci->addMeta('studentlehrverbaende', $student_lehrverband);
$this->load->model('crm/Student_model', 'StudentModel');
$this->StudentModel->addSelect('ausbildungssemester');
$this->StudentModel->addJoin('public.tbl_prestudentstatus ps', 'prestudent_id');
$result = $this->StudentModel->loadWhere(array(
"student_uid"=>$student_uid,
'studiensemester_kurzbz' => $first_semester,
));
if (isError($result))
$semesterStud = null;
if(hasData($result)){
$result = getData($result);
$data = current($result);
$semesterStud = $data->ausbildungssemester;
}
$stundenplan_query = $this->StundenplanModel->getStundenplanQuery(
$start_date,
$end_date, //will be overwritten in model if $ende_semProlonged
$semester_range,
$benutzer_gruppen,
$student_lehrverband,
$dbStundenplanTable,
$groupConsecutiveHours,
null,
null
);
$ci->addMeta('bhstdplanquery', $stundenplan_query);
if(!$stundenplan_query)
{
@@ -84,47 +195,23 @@ class LvTermine extends FHCAPI_Controller
public function getStudiensemester()
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
$this->StudiensemesterModel->addOrder('studienjahr_kurzbz', 'DESC');
$result = $this->StudiensemesterModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
$this->StudiensemesterModel->addOrder('studienjahr_kurzbz', 'DESC');
$result = $this->StudiensemesterModel->load();
$data = $this->getDataOrTerminateWithError($result);
$this->terminateWithSuccess($data);
}
//copied from Stundenplan.php
private function studienSemesterErmitteln($start_date, $end_date)
{
// gets all studiensemester from the student from start_date to end_date
$semester_range = $this->StudiensemesterModel->getByDateRange($start_date, $end_date);
$semester_range = array_map(
function ($sem) {
return $sem->studiensemester_kurzbz;
},
$this->getDataOrTerminateWithError($semester_range)
);
// TODO(Manu) following 5 private functions are copybased of StundenplanLib,
// just applyLoadUeberSemesterHaelfte is different
// after approval: change to public in StundenplanLib and use it here?
// if no studiensemester is found for the given timespan, get the nearest studiensemester
if(count($semester_range) == 0)
{
$aktuelle_studiensemester = $this->StudiensemesterModel->getNearest();
$aktuelle_studiensemester = $this->getDataOrTerminateWithError($aktuelle_studiensemester);
if (count($aktuelle_studiensemester) == 0) {
$this->terminateWithError("No aktuelles semester");
}
$aktuelle_studiensemester = current($aktuelle_studiensemester)->studiensemester_kurzbz;
// push aktuelles semester in active semester array
array_push($semester_range, $aktuelle_studiensemester);
}
return $semester_range;
}
//copied from Stundenplan.php
private function sortStudienSemester(&$semester_range)
{
private function sortStudienSemester(&$semester_range){
usort(
$semester_range,
function ($first, $second) {
function($first, $second)
{
$sem_first = null;
$year_first = null;
$match_first = null;
@@ -133,8 +220,8 @@ class LvTermine extends FHCAPI_Controller
$year_second = null;
$match_second = null;
preg_match('/([WS]+)([0-9]+)/', $first, $match_first);
preg_match('/([WS]+)([0-9]+)/', $second, $match_second);
preg_match('/([WS]+)([0-9]+)/',$first,$match_first);
preg_match('/([WS]+)([0-9]+)/',$second,$match_second);
$sem_first = $match_first[1];
$year_first = intval($match_first[2]);
@@ -146,15 +233,15 @@ class LvTermine extends FHCAPI_Controller
{
return -1;
}
elseif($year_first > $year_second)
else if($year_first > $year_second)
{
return 1;
}
elseif($year_first == $year_second && $sem_first > $sem_second)
else if($year_first == $year_second && $sem_first > $sem_second)
{
return 1;
}
elseif($year_first == $year_second && $sem_first < $sem_second)
else if($year_first == $year_second && $sem_first < $sem_second)
{
return -1;
}
@@ -163,9 +250,151 @@ class LvTermine extends FHCAPI_Controller
);
}
//copied from Stundenplan.php
private function fetchBenutzerGruppenFromStudiensemester($student_uid, $semester_range, $skip_semester_kurzbz)
{
$this->load->model('person/Benutzergruppe_model', 'BenutzergruppeModel');
$benutzer_gruppen = [];
// for each studiensemester fetch the benutzer gruppen and add them to an associate $bentuzer_gruppen array
/*
[
['WS2023'] => [['gruppe1_SS2023','gruppe2_SS2023'],['gruppe1_WS2023','gruppe2_WS2023']],
['SS2024'] => [['gruppe1_WS2023','gruppe2_WS2023'],['gruppe1_SS2024','gruppe2_SS2024']],
['WS2024'] => [['gruppe1_SS2024','gruppe2_SS2024'],['gruppe1_WS2024','gruppe2_WS2024']],
]
*/
foreach($semester_range as $semester_key => $semester_array)
{
$benutzer_gruppen[$semester_key] = [];
// each semester could have ajoint semesters that need to be checked
foreach($semester_array as $semester=>$semester_date_range)
{
if($semester === $skip_semester_kurzbz)
{
continue;
}
// for each active semester query the benutzer_gruppen associated to the semester
$benutzer_query = $this->BenutzergruppeModel->execReadOnlyQuery("
SELECT * FROM tbl_benutzergruppe where uid = ? AND studiensemester_kurzbz = ?",[$student_uid, $semester]);
if(isError($benutzer_query)){
return error(getData($benutzer_query));
}
$benutzer_query_result = getData($benutzer_query)??[];
array_push(
$benutzer_gruppen[$semester_key],
array_map(
function($item)
{
return "'".$item->gruppe_kurzbz. "'";
},
$benutzer_query_result
)
);
}
}
// merge the gruppen of each studiensemester together for the original studiensemester
/*
[
['WS2023'] => ['gruppe1_SS2023','gruppe2_SS2023','gruppe1_WS2023','gruppe2_WS2023'],
['SS2024'] => ['gruppe1_WS2023','gruppe2_WS2023','gruppe1_SS2024','gruppe2_SS2024'],
['WS2024'] => ['gruppe1_SS2024','gruppe2_SS2024','gruppe1_WS2024','gruppe2_WS2024'],
]
*/
$benutzer_gruppen = array_map(
function($gruppe)
{
$merged_gruppe = [];
foreach($gruppe as $gruppen_array)
{
$merged_gruppe = array_merge($merged_gruppe, $gruppen_array);
}
return $merged_gruppe;
},
$benutzer_gruppen
);
return success($benutzer_gruppen);
}
private function fetchStudentlehrverbandFromStudiensemester($student_uid, $semester_range, $studiensemester_kurzbz, $nextstudiensemester_kurzbz)
{
$this->load->model('person/Benutzergruppe_model', 'BenutzergruppeModel');
$student_lehrverband = [];
// for each studiensemester fetch the studentlehrverbaende and add them to an associate $student_lehrverband array
/*
[
['WS2023'] => [ [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""] ] ],
['SS2024'] => [ [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""] ], [ ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""] ] ],
['WS2024'] => [ [ ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""] ], [ ['stg_kz'=>298,'semester'=>3,'verband'=>"A",'gruppe'=>""] ] ],
]
*/
foreach($semester_range as $semester_key => $semester_array)
{
$student_lehrverband[$semester_key] = [];
foreach($semester_array as $semester=>$semester_date_range)
{
// for each active semester query the student_lehrverband associated to the semester
$lehrverband_query = $this->BenutzergruppeModel->execReadOnlyQuery("
SELECT vbd.*, UPPER(s.typ || s.kurzbz) as stg FROM tbl_studentlehrverband vbd join tbl_studiengang s USING(studiengang_kz) where vbd.student_uid = ? AND vbd.studiensemester_kurzbz = ?", [$student_uid, $semester]);
if(isError($lehrverband_query)){
return error(getData($lehrverband_query));
}
$lehrverband_query_result = getData($lehrverband_query)??[];
$converted_studentLehrverband= array_map(
function ($item) use($nextstudiensemester_kurzbz, $studiensemester_kurzbz)
{
$result = new stdClass();
$result->verbandsgruppe = $item->stg . $item->semester . trim($item->verband) . trim($item->gruppe);
$result->studiengang_kz = $item->studiengang_kz;
$result->semester = $item->semester;
$result->verband = $item->verband;
$result->gruppe = $item->gruppe;
$result->studiensemester_kurzbz = $item->studiensemester_kurzbz;
$result->query_studiensemester_kurzbz = ($item->studiensemester_kurzbz === $nextstudiensemester_kurzbz) ? $studiensemester_kurzbz : $item->studiensemester_kurzbz;
return $result;
},
$lehrverband_query_result);
array_push($student_lehrverband[$semester_key], $converted_studentLehrverband);
}
}
// merge the studentlehrverband of each studiensemester together for the original studiensemester
/*
[
['WS2023'] => [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""] ],
['SS2024'] => [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""], ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""] ],
['WS2024'] => [ ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""], ['stg_kz'=>298,'semester'=>3,'verband'=>"A",'gruppe'=>""] ],
]
*/
$student_lehrverband = array_map(
function($studentlehrverband)
{
$merged_studentlehrverband = [];
foreach($studentlehrverband as $studentlehrverband_array)
{
$merged_studentlehrverband = array_merge($merged_studentlehrverband, $studentlehrverband_array);
}
return $merged_studentlehrverband;
},
$student_lehrverband
);
return success($student_lehrverband);
}
//note: this is different to the version in StundenplanLib, changes apply just to LVTermine in StudVW
private function applyLoadUeberSemesterHaelfte(&$semester_range)
{
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
/*
@var($semester_collection)
convert the array of studiensemester into an associative array with the studiensemester as the key
@@ -185,9 +414,13 @@ class LvTermine extends FHCAPI_Controller
foreach($semester_range as $studiensemester)
{
$previous_studiensemester = $this->StudiensemesterModel->getPreviousFrom($studiensemester);
$previous_studiensemester = $this->getDataOrTerminateWithError($previous_studiensemester);
if(isError($previous_studiensemester))
{
return error(getData($previous_studiensemester));
}
$previous_studiensemester = getData($previous_studiensemester);
if (count($previous_studiensemester) == 0) {
$this->terminateWithError("No previous semester");
return error('no previous semester');
}
$previous_studiensemester = current($previous_studiensemester)->studiensemester_kurzbz;
$semester_collection[$studiensemester] = [$previous_studiensemester, $studiensemester];
@@ -237,25 +470,33 @@ class LvTermine extends FHCAPI_Controller
foreach($semester_collection as $semester_original => $semester_adjoint)
{
$semester_start_ende = $this->StudiensemesterModel->getStartEndeFromStudiensemester($semester_original);
$semester_start_ende = current($this->getDataOrTerminateWithError($semester_start_ende));
if(isError($semester_start_ende))
{
return error(getData($semester_start_ende));
}
$semester_start_ende = current(getData($semester_start_ende));
// initialize empty arrays to add key value pairs
$studienSemesterDateRanges[$semester_original] = [];
// check if the studiensemester is a summer semester and add 1 month to bridge the school summer break
$match = null;
preg_match("/^(SS)([0-9]+)/", $semester_original, $match);
preg_match("/^(SS)([0-9]+)/",$semester_original,$match);
if(count($match) >0)
{
$one_month = new DateInterval('P1M');
$one_day = DateInterval::createFromDateString('1 days');
$summer_studiensemester_end_date = DateTime::createFromFormat('Y-m-d', $semester_start_ende->ende);
$summer_studiensemester_end_date = DateTime::createFromFormat('Y-m-d',$semester_start_ende->ende);
$summer_studiensemester_end_date->add($one_month);
$summer_studiensemester_end_date->sub($one_day);
$semester_start_ende->ende = date_format($summer_studiensemester_end_date, 'Y-m-d');
$semester_start_ende->ende = date_format($summer_studiensemester_end_date,'Y-m-d');
}
if (defined('LVPLAN_LOAD_UEBER_SEMESTERHAELFTE') && LVPLAN_LOAD_UEBER_SEMESTERHAELFTE === true)
//TODO(Manu) config not taken into account
if (defined('LVPLAN_LOAD_UEBER_SEMESTERHAELFTE') && LVPLAN_LOAD_UEBER_SEMESTERHAELFTE)
{
$this->addMeta('LVPLAN_LOAD_UEBER_SEMESTERHAELFTE_TRUE', $semester_range);
foreach($semester_adjoint as $adjoint)
{
$studienSemesterDateRanges[$semester_original][$adjoint]=$semester_start_ende;
@@ -263,139 +504,106 @@ class LvTermine extends FHCAPI_Controller
}
else
{
$this->addMeta('LVPLAN_LOAD_UEBER_SEMESTERHAELFTE_FALSE', $semester_range);
//TODO: half of a DateInterval might not be correctly calculated
// calculate the half of the studiensemester
$studiensemester_start_date = DateTime::createFromFormat('Y-m-d', $semester_start_ende->start);
$studiensemester_end_date = DateTime::createFromFormat('Y-m-d', $semester_start_ende->ende);
$studiensemester_start_date = DateTime::createFromFormat('Y-m-d',$semester_start_ende->start);
$studiensemester_end_date = DateTime::createFromFormat('Y-m-d',$semester_start_ende->ende);
$studiensemester_time_difference = $studiensemester_start_date->diff($studiensemester_end_date);
$half_dateNumber = ceil($studiensemester_time_difference->d/2)+ceil(($studiensemester_time_difference->m*30)/2);
$half_dateInterval = new DateInterval('P'.strval($half_dateNumber) .'D');
$studiensemester_half = date_format($studiensemester_start_date->add($half_dateInterval), 'Y-m-d');
$studiensemester_half = date_format($studiensemester_start_date->add($half_dateInterval),'Y-m-d');
$first_half = new stdClass();
$first_half->start = $semester_start_ende->start;
$first_half->ende = $studiensemester_half;
$studienSemesterDateRanges[$semester_original][$semester_adjoint[0]] = $first_half;
$studienSemesterDateRanges[$semester_original][$semester_adjoint[1]] = $semester_start_ende;
$studiensemester_start_date = DateTime::createFromFormat(
'Y-m-d',
$semester_start_ende->start
);
$studiensemester_end_date = DateTime::createFromFormat(
'Y-m-d',
$semester_start_ende->ende
);
$studiensemester_time_difference = $studiensemester_start_date->diff(
$studiensemester_end_date
);
$half_dateNumber =
ceil($studiensemester_time_difference->d / 2) +
ceil(($studiensemester_time_difference->m * 30) / 2);
$half_dateInterval = new DateInterval(
'P' . strval($half_dateNumber) . 'D'
);
$second_semester_end_date = clone $studiensemester_end_date;
$second_semester_end_date->add($half_dateInterval);
$second_semester_date_range = clone $semester_start_ende;
$second_semester_date_range->ende = $second_semester_end_date->format('Y-m-d');
// 2nd Adjacent-Semester: old time intervall plus half next semester
$studienSemesterDateRanges[$semester_original][$semester_adjoint[1]] = $second_semester_date_range;
}
$semester_range = $studienSemesterDateRanges;
}
}
//copied from Stundenplan.php, extended with $student_uid
private function fetchBenutzerGruppenFromStudiensemester($semester_range, $student_uid)
private function studienSemesterErmitteln($start_date, $end_date)
{
//$student_uid = getAuthUID();
$benutzer_gruppen = [];
// for each studiensemester fetch the benutzer gruppen and add them to an associate $bentuzer_gruppen array
/*
[
['WS2023'] => [['gruppe1_SS2023','gruppe2_SS2023'],['gruppe1_WS2023','gruppe2_WS2023']],
['SS2024'] => [['gruppe1_WS2023','gruppe2_WS2023'],['gruppe1_SS2024','gruppe2_SS2024']],
['WS2024'] => [['gruppe1_SS2024','gruppe2_SS2024'],['gruppe1_WS2024','gruppe2_WS2024']],
]
*/
foreach($semester_range as $semester_key => $semester_array)
{
$benutzer_gruppen[$semester_key] = [];
// each semester could have ajoint semesters that need to be checked
foreach($semester_array as $semester => $semester_date_range)
{
// for each active semester query the benutzer_gruppen associated to the semester
$benutzer_query = $this->BenutzergruppeModel->execReadOnlyQuery("
SELECT * FROM tbl_benutzergruppe where uid = ? AND studiensemester_kurzbz = ?", [$student_uid, $semester]);
$benutzer_query_result = $this->getDataOrTerminateWithError($benutzer_query);
array_push(
$benutzer_gruppen[$semester_key],
array_map(
function ($item) {
return "'".$item->gruppe_kurzbz. "'";
},
$benutzer_query_result
)
);
}
}
$this->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
// merge the gruppen of each studiensemester together for the original studiensemester
/*
[
['WS2023'] => ['gruppe1_SS2023','gruppe2_SS2023','gruppe1_WS2023','gruppe2_WS2023'],
['SS2024'] => ['gruppe1_WS2023','gruppe2_WS2023','gruppe1_SS2024','gruppe2_SS2024'],
['WS2024'] => ['gruppe1_SS2024','gruppe2_SS2024','gruppe1_WS2024','gruppe2_WS2024'],
]
*/
$benutzer_gruppen = array_map(
function ($gruppe) {
$merged_gruppe = [];
foreach($gruppe as $gruppen_array)
{
$merged_gruppe = array_merge($merged_gruppe, $gruppen_array);
}
return $merged_gruppe;
// gets all studiensemester from the student from start_date to end_date
//$semester_range = $this->StudiensemesterModel->getByDateRange($start_date, $end_date);
$semester_range = $this->StudiensemesterModel->getContainingOrNearestByDateRange($start_date, $end_date);
if (isError($semester_range))
return $semester_range;
$semester_range = array_map(
function ($sem) {
return $sem->studiensemester_kurzbz;
},
$benutzer_gruppen
getData($semester_range) ?: []
);
return $benutzer_gruppen;
}
//copied from Stundenplan.php, extended with $student_uid
private function fetchStudentlehrverbandFromStudiensemester($semester_range, $student_uid)
{
//$student_uid = getAuthUID();
$student_lehrverband = [];
// for each studiensemester fetch the studentlehrverbaende and add them to an associate $student_lehrverband array
/*
[
['WS2023'] => [ [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""] ] ],
['SS2024'] => [ [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""] ], [ ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""] ] ],
['WS2024'] => [ [ ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""] ], [ ['stg_kz'=>298,'semester'=>3,'verband'=>"A",'gruppe'=>""] ] ],
]
*/
foreach($semester_range as $semester_key => $semester_array)
// if no studiensemester is found for the given timespan, get the nearest studiensemester
if (count($semester_range) == 0)
{
$student_lehrverband[$semester_key] = [];
foreach($semester_array as $semester => $semester_date_range)
{
// for each active semester query the student_lehrverband associated to the semester
$lehrverband_query = $this->BenutzergruppeModel->execReadOnlyQuery("
SELECT * FROM tbl_studentlehrverband where student_uid = ? AND studiensemester_kurzbz = ?", [$student_uid, $semester]);
$lehrverband_query_result = $this->getDataOrTerminateWithError($lehrverband_query);
array_push($student_lehrverband[$semester_key], array_map(
function ($item) {
$result = new stdClass();
$result->studiengang_kz = $item->studiengang_kz;
$result->semester = $item->semester;
$result->verband = $item->verband;
$result->gruppe = $item->gruppe;
return $result;
},
$lehrverband_query_result));
$aktuelle_studiensemester = $this->StudiensemesterModel->getNearest();
if (isError($aktuelle_studiensemester))
return $aktuelle_studiensemester;
$aktuelle_studiensemester = getData($aktuelle_studiensemester);
if (count($aktuelle_studiensemester) == 0) {
return error("No aktuelles semester");
}
$aktuelle_studiensemester = current($aktuelle_studiensemester)->studiensemester_kurzbz;
// push aktuelles semester in active semester array
array_push($semester_range, $aktuelle_studiensemester);
}
// merge the studentlehrverband of each studiensemester together for the original studiensemester
/*
[
['WS2023'] => [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""] ],
['SS2024'] => [ ['stg_kz'=>298,'semester'=>1,'verband'=>"A",'gruppe'=>""], ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""] ],
['WS2024'] => [ ['stg_kz'=>298,'semester'=>2,'verband'=>"A",'gruppe'=>""], ['stg_kz'=>298,'semester'=>3,'verband'=>"A",'gruppe'=>""] ],
]
*/
$student_lehrverband = array_map(
function ($studentlehrverband) {
$merged_studentlehrverband = [];
foreach($studentlehrverband as $studentlehrverband_array)
{
$merged_studentlehrverband = array_merge($merged_studentlehrverband, $studentlehrverband_array);
}
return $merged_studentlehrverband;
},
$student_lehrverband
);
//auf jeden Fall Semester 2 dazunehmen, wenn nur eines vorhanden ist für Anzeige der ersten Hälfte des folgenden Semesters?
if (count($semester_range) == 1)
{
$nextStudiensemester = $this->StudiensemesterModel->getNextFrom($semester_range[0]);
if (isError($nextStudiensemester))
return $nextStudiensemester;
return $student_lehrverband;
$nextStudiensemester = getData($nextStudiensemester);
if (count($nextStudiensemester) == 0) {
return error("No aktuelles semester");
}
$nextStudiensemester = current($nextStudiensemester)->studiensemester_kurzbz;
// push aktuelles semester in active semester array
array_push($semester_range, $nextStudiensemester);
}
return success($semester_range);
}
}
+23 -5
View File
@@ -88,19 +88,34 @@ class StundenplanLib
$this->_ci->addMeta('nach_apply_semester_range', $semester_range);
if ($function_error)
return $function_error;
$this->_ci->addMeta('bhsemtest', print_r($semester_range, true));
$semester_range = array(
'SS2026' => array(
'WS2025' => (object) array(
'start' => '2026-02-04',
'ende' => '2026-05-19'
),
'SS2026' => (object) array(
'start' => '2026-02-04',
'ende' => '2026-11-15'
),
)
);
// getting the gruppen_kurzbz of the student in the different studiensemester
$benutzer_gruppen = $this->fetchBenutzerGruppenFromStudiensemester($student_uid, $semester_range);
if (isError($benutzer_gruppen))
return $benutzer_gruppen;
$benutzer_gruppen = getData($benutzer_gruppen);
$this->_ci->addMeta('spezialgruppen', $benutzer_gruppen);
// getting the student_lehrverbaende of the student in the different studiensemester
$student_lehrverband = $this->fetchStudentlehrverbandFromStudiensemester($student_uid, $semester_range);
if (isError($student_lehrverband))
return $student_lehrverband;
$student_lehrverband = getData($student_lehrverband);
$this->_ci->addMeta('studentlehrvebaende', $student_lehrverband);
$stundenplan_query = $this->_ci->StundenplanModel->getStundenplanQuery(
$start,
$end,
@@ -632,6 +647,7 @@ class StundenplanLib
$result->semester = $item->semester;
$result->verband = $item->verband;
$result->gruppe = $item->gruppe;
$result->studiensemester_kurzbz = $item->studiensemester_kurzbz;
return $result;
},
$lehrverband_query_result);
@@ -667,8 +683,9 @@ class StundenplanLib
return success($student_lehrverband);
}
private function applyLoadUeberSemesterHaelfte(&$semester_range)
public function applyLoadUeberSemesterHaelfte(&$semester_range)
{
$this->_ci = &get_instance();
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
/*
@@ -796,8 +813,9 @@ class StundenplanLib
}
}
private function studienSemesterErmitteln($start_date, $end_date)
public function studienSemesterErmitteln($start_date, $end_date)
{
$this->_ci = &get_instance();
$this->_ci->load->model('organisation/Studiensemester_model', 'StudiensemesterModel');
// gets all studiensemester from the student from start_date to end_date
+1 -1
View File
@@ -77,7 +77,7 @@ class StudentListLib
CASE
WHEN pls.status_kurzbz IN ('Interessent','Aufgenommener','Bewerber','Wartender')
THEN pls.ausbildungssemester
ELSE s.semester
ELSE v.semester
END AS semester_berechnet
");
$this->addSelect(
@@ -28,10 +28,21 @@ class CoreMissingZgvTagLib
$this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id');
$this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id');
$this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$this->ci->PrestudentModel->db->group_start();
$this->ci->PrestudentModel->db->where('zgvdatum', null);
$this->ci->PrestudentModel->db->where('typ', 'b');
$this->ci->PrestudentModel->db->or_group_start();
$this->ci->PrestudentModel->db->where('zgvmadatum', null);
$this->ci->PrestudentModel->db->where('typ', 'm');
$this->ci->PrestudentModel->db->group_end();
$this->ci->PrestudentModel->db->group_end();
$result = $this->ci->PrestudentModel-> loadWhere(array(
'bn.aktiv' => true, //check if necessary
'zgvdatum' => null,
'typ' => 'b',
'bn.aktiv' => true,
'studiensemester_kurzbz' => $semester
));
@@ -60,16 +71,36 @@ class CoreMissingZgvTagLib
$semester = $params['studiensemester_kurzbz'];
$prestudent_id = $params['id'];
$this->ci->PrestudentModel->addSelect('prestudent_id');
$this->ci->PrestudentModel->addSelect('start as von');
$this->ci->PrestudentModel->addSelect('ende as bis');
$this->ci->PrestudentModel->addJoin('public.tbl_prestudentstatus', 'prestudent_id');
//add studiensemester also here for manual rebuild
$this->ci->PrestudentModel->addJoin('public.tbl_studiensemester', 'studiensemester_kurzbz');
$this->ci->PrestudentModel->addJoin('public.tbl_benutzer bn', 'person_id');
$this->ci->PrestudentModel->addJoin('public.tbl_studiengang', 'studiengang_kz');
$result = $this->ci->PrestudentModel->loadWhere(array(
'bn.aktiv' => true, //check if necessary
'zgvdatum' => null,
'typ' => 'b',
$this->ci->PrestudentModel->db->group_start();
$this->ci->PrestudentModel->db->where('zgvdatum', null);
$this->ci->PrestudentModel->db->where('typ', 'b');
$this->ci->PrestudentModel->db->or_group_start();
$this->ci->PrestudentModel->db->where('zgvmadatum', null);
$this->ci->PrestudentModel->db->where('typ', 'm');
$this->ci->PrestudentModel->db->group_end();
$this->ci->PrestudentModel->db->group_end();
$this->ci->PrestudentModel->db->limit(1);
$result = $this->ci->PrestudentModel-> loadWhere(array(
'bn.aktiv' => true,
'studiensemester_kurzbz' => $semester,
'prestudent_id' => $prestudent_id
));
if(hasData($result))
{
return $result;
@@ -13,6 +13,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
protected $dienstverhaeltnis_id;
protected $vertragsbestandteil_id;
protected $gehaltstyp_kurzbz;
protected $gehaltsanpassungtyp_kurzbz = '';
protected $von;
protected $bis;
protected $anmerkung;
@@ -21,6 +22,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
protected $valorisierungssperre;
protected $valorisierung;
protected $auszahlungen;
protected $gehaltstyp_bezeichnung;
protected $insertamum;
protected $insertvon;
@@ -39,6 +41,10 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
isset($data->dienstverhaeltnis_id) && $this->setDienstverhaeltnis_id($data->dienstverhaeltnis_id);
isset($data->vertragsbestandteil_id) && $this->setVertragsbestandteil_id($data->vertragsbestandteil_id);
isset($data->gehaltstyp_kurzbz) && $this->setGehaltstyp_kurzbz($data->gehaltstyp_kurzbz);
//isset($data->gehaltsanpassungtyp_kurzbz) && $this->setGehaltsanpassungtyp_kurzbz($data->gehaltsanpassungtyp_kurzbz);
if (!empty($data->gehaltsanpassungtyp_kurzbz)) {
$this->setGehaltsanpassungtyp_kurzbz($data->gehaltsanpassungtyp_kurzbz);
}
isset($data->von) && $this->setVon($data->von);
isset($data->bis) && $this->setBis($data->bis);
isset($data->anmerkung) && $this->setAnmerkung($data->anmerkung);
@@ -47,7 +53,8 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
isset($data->valorisierungssperre) && $this->setValorisierungssperre($data->valorisierungssperre);
isset($data->valorisierung) && $this->setValorisierung($data->valorisierung);
isset($data->auszahlungen) && $this->setAuszahlungen($data->auszahlungen);
isset($data->gehaltstyp_bezeichnung) && $this->setGehaltstyp_bezeichnung($data->gehaltstyp_bezeichnung);
isset($data->insertamum) && $this->setInsertamum($data->insertamum);
isset($data->insertvon) && $this->setInsertvon($data->insertvon);
isset($data->updateamum) && $this->setUpdateamum($data->updateamum);
@@ -75,6 +82,11 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
return $this->gehaltstyp_kurzbz;
}
public function getGehaltsanpassungtyp_kurzbz()
{
return $this->gehaltsanpassungtyp_kurzbz;
}
public function getVon()
{
return $this->von;
@@ -178,6 +190,13 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
return $this;
}
public function setGehaltsanpassungtyp_kurzbz($gehaltsanpassungtyp_kurzbz)
{
$this->markDirty('gehaltsanpassungtyp_kurzbz', $this->gehaltsanpassungtyp_kurzbz, $gehaltsanpassungtyp_kurzbz);
$this->gehaltsanpassungtyp_kurzbz = $gehaltsanpassungtyp_kurzbz;
return $this;
}
public function setVon($von)
{
$this->markDirty('von', $this->von, $von);
@@ -206,6 +225,10 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
$grundbetrag = number_format($grundbetrag, 2, '.', '');
}
$this->markDirty('grundbetrag', $this->grundbetrag, $grundbetrag);
if(in_array('grundbetrag', $this->modifiedcolumns))
{
$this->setBetrag_valorisiert($grundbetrag);
}
$this->grundbetrag = $grundbetrag;
return $this;
}
@@ -242,6 +265,13 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
return $this;
}
public function setGehaltstyp_bezeichnung($gehaltstyp_bezeichnung)
{
$this->markDirty('gehaltstyp_bezeichnung', $this->gehaltstyp_bezeichnung, $gehaltstyp_bezeichnung);
$this->gehaltstyp_bezeichnung = $gehaltstyp_bezeichnung;
return $this;
}
public function setInsertamum($insertamum)
{
$this->markDirty('insertamum', $this->insertamum, $insertamum);
@@ -284,6 +314,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
'dienstverhaeltnis_id' => $this->getDienstverhaeltnis_id(),
'vertragsbestandteil_id' => $this->getVertragsbestandteil_id(),
'gehaltstyp_kurzbz' => $this->getGehaltstyp_kurzbz(),
'gehaltsanpassungtyp_kurzbz' => $this->getGehaltsanpassungtyp_kurzbz(),
'von' => $this->getVon(),
'bis' => $this->getBis(),
'anmerkung' => $this->getAnmerkung(),
@@ -312,6 +343,7 @@ class Gehaltsbestandteil extends AbstractBestandteil implements \JsonSerializabl
dienstverhaeltnis_id: {$this->getDienstverhaeltnis_id()}
vertragsbestandteil_id: {$this->getVertragsbestandteil_id()}
gehaltstyp_kurzbz: {$this->getGehaltstyp_kurzbz()}
gehaltsanpassungtyp_kurzbz: {$this->getGehaltsanpassungtyp_kurzbz()}
von: {$this->getVon()}
bis: {$this->getBis()}
anmerkung: {$this->getAnmerkung()}
+1 -1
View File
@@ -204,7 +204,7 @@ class Student_model extends DB_Model
Events::trigger(
'generate_student_uid',
function ($value) use ($uid) {
function ($value) use (&$uid) {
$uid = $value;
},
$stgkzl,
@@ -457,6 +457,7 @@ class Studiengang_model extends DB_Model
*/
public function getLeitung($studiengang_kz = null)
{
$this->addDistinct();
$this->addSelect('uid, studiengang_kz, oe_kurzbz, vorname, nachname, email, titelpre, titelpost, alias');
$this->addJoin('public.tbl_benutzerfunktion', 'oe_kurzbz');
$this->addJoin('public.tbl_benutzer', 'uid');
@@ -193,7 +193,8 @@ class Stundenplan_model extends DB_Model
*/
public function stundenplanGruppierungConsecutive($stundenplanViewQuery)
{
$query_result = $this->execReadOnlyQuery("
$query_result = $this->execReadOnlyQuery(
$stundenplanViewQuery . "
SELECT
distinct lehrveranstaltung_id,
datum,
@@ -247,7 +248,7 @@ class Stundenplan_model extends DB_Model
anmerkung_lehreinheit,gruppe, verband, semester,stg_kurzbz,
CONCAT(p.nachname, ' ', p.vorname) as lektorname
FROM (".$stundenplanViewQuery.") sp
FROM stundenplaneintraege sp
JOIN lehre.tbl_stunde ON lehre.tbl_stunde.stunde = sp.stunde
LEFT JOIN public.tbl_benutzer bn ON bn.uid = sp.uid
LEFT JOIN public.tbl_person p ON p.person_id = bn.person_id
@@ -280,7 +281,7 @@ class Stundenplan_model extends DB_Model
/**
* queries Stundenplan but for a whole lva, irrespective of who is requesting it
*
*
* @return void
*/
public function getStundenplanLVA($start_date, $end_date, $lv_id) {
@@ -528,7 +529,96 @@ class Stundenplan_model extends DB_Model
return $this->execReadOnlyQuery($qry, $qry_params);
}
protected function genLehrverbandClauses($semester, $studentlehrverbaende)
{
$lvbdclauses = array();
$studiengang_kz = null;
foreach($semester as $sem=>$semester_date_range)
{
foreach($semester_date_range as $sem_date => $sem_date_range)
{
if(!array_key_exists($sem,$studentlehrverbaende) || count($studentlehrverbaende[$sem]) == 0)
{
continue;
}
$studlvbds = array_filter(
$studentlehrverbaende[$sem],
function($value) use ($sem_date) {
return $value->studiensemester_kurzbz === $sem_date;
}
);
foreach($studlvbds as $key=>$lehrverband)
{
$studiengang_kz = $lehrverband->studiengang_kz;
$lvbdclauses[] = <<<EOCLAUSE
(
{$this->escape($lehrverband->verbandsgruppe)} like sp.verbandsgruppe || '%'
and sp.datum between {$this->escape($sem_date_range->start)} and {$this->escape($sem_date_range->ende)}
and (sp.studiensemester_kurzbz is null or sp.studiensemester_kurzbz = {$this->escape($lehrverband->query_studiensemester_kurzbz)})
)
EOCLAUSE;
}
}
}
$sql = implode(PHP_EOL . " OR" . PHP_EOL, $lvbdclauses);
if($sql)
{
$sql = <<<EOSQL
(
sp.studiengang_kz = {$this->escape($studiengang_kz)}
and
sp.gruppe_kurzbz is null
and
(
{$sql}
)
)
EOSQL;
}
return $sql;
}
protected function genSpezialgruppenClauses($semester, $gruppen)
{
$spezgrpclauses = array();
foreach($semester as $sem => $semester_date_range)
{
foreach($semester_date_range as $sem_date => $sem_date_range)
{
// if there are not groups for the semester skip the iteration step
if(!array_key_exists($sem_date,$gruppen) || count($gruppen[$sem_date]) == 0)
{
continue;
}
// converts the array of gruppen strings into a sql IN (_,_,_) chain
$groups = implode(', ', $gruppen[$sem_date]);
$spezgrpclauses[] = <<<EOSPEZGRP
(
sp.gruppe_kurzbz IN ({$groups})
AND sp.datum BETWEEN {$this->escape($sem_date_range->start)} AND {$this->escape($sem_date_range->ende)}
)
EOSPEZGRP;
}
}
$sql = implode(PHP_EOL . " OR" . PHP_EOL, $spezgrpclauses);
if($sql)
{
$sql = <<<EOSQL
(
{$sql}
)
EOSQL;
}
return $sql;
}
/**
* NO STANDALONE FUNCTION - Generates a SQL query string to fetch 'stundenplan' events for a specific student within the current semester.
*
@@ -537,8 +627,8 @@ class Stundenplan_model extends DB_Model
*
* @return mixed
*/
public function getStundenplanQuery($start_date, $end_date, $semester, $gruppen, $studentlehrverbaende, $isLvList=false, $db_stpl_table='stundenplan'){
public function getStundenplanQuery($start_date, $end_date, $semester, $gruppen, $studentlehrverbaende, $db_stpl_table='stundenplan', $showLvsStundenplan=false, $endDateNextSemHalf=null, $semesterFilter=null)
{
// helper function to check if either $gruppen or $studentlehrverbaende are empty for each semester
$emptyCheck = function($toBeCheckedArray) use ($semester){
$result = true;
@@ -552,87 +642,42 @@ class Stundenplan_model extends DB_Model
return $result;
};
// if both the gruppen and the studentlehrverbaende are empty we early return
// if both the gruppen and the studentlehrverbaende are empty we early return
if($emptyCheck($gruppen) && $emptyCheck($studentlehrverbaende))
{
return false;
}
$query =
"select sp.*
from lehre.vw_".$db_stpl_table." sp
WHERE
sp.datum >= ".$this->escape($start_date)."
AND sp.datum <= ".$this->escape($end_date);
// adds the AND sql chain only if both $gruppen and $studentlehrverbaende are not empty
if(!$emptyCheck($gruppen) || !$emptyCheck($studentlehrverbaende))
{
$query .= " AND ( ";
}
$lvbdclauses = $this->genLehrverbandClauses($semester, $studentlehrverbaende);
$spezgrpclauses = $this->genSpezialgruppenClauses($semester, $gruppen);
$chainingor = ($lvbdclauses && $spezgrpclauses) ? 'OR' : '';
foreach($semester as $sem => $semester_date_range)
{
$query = <<<EOSQL
with preprocessedsp as (
select
(UPPER(sp.stg_typ || sp.stg_kurzbz)) || sp.semester || sp.verband || sp.gruppe as verbandsgruppe,
sp.*,
le.studiensemester_kurzbz
from
lehre.vw_stundenplan sp
join
lehre.tbl_lehreinheit le on sp.lehreinheit_id = le.lehreinheit_id
where
sp.datum >= {$this->escape($start_date)}
and sp.datum <= {$this->escape($end_date)}
),
stundenplaneintraege as (
select
sp.*
from
preprocessedsp sp
where
{$lvbdclauses}
{$chainingor}
{$spezgrpclauses}
)
foreach($semester_date_range as $sem_date => $sem_date_range)
{
// if there are not groups for the semester skip the iteration step
if(!array_key_exists($sem_date,$gruppen) || count($gruppen[$sem_date]) == 0)
{
continue;
}
// converts the array of gruppen strings into a sql IN (_,_,_) chain
$query .="(sp.gruppe_kurzbz IN (" .implode(',',$gruppen[$sem_date]).") AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende)." )";
$query .="OR";
}
}
// if there are no studentlehrverbaende and the gruppen are not empty, we can remove the last OR added after the groups
if($emptyCheck($studentlehrverbaende) && !$emptyCheck($gruppen))
{
$query = substr($query, 0, -2);
}
//Condition for showLVList FHC4
if(!$isLvList)
$stringGroupLv = "AND gruppe_kurzbz is null";
else
$stringGroupLv ="";
foreach($semester as $sem=>$semester_date_range)
{
foreach($semester_date_range as $sem_date => $sem_date_range)
{
if(!array_key_exists($sem_date,$studentlehrverbaende) || count($studentlehrverbaende[$sem_date]) == 0)
{
continue;
}
foreach($studentlehrverbaende[$sem_date] as $key=>$lehrverband)
{
$query .= "(((sp.studiengang_kz = ".$this->escape($lehrverband->studiengang_kz)." AND sp.semester = ".$this->escape($lehrverband->semester)." AND sp.verband = ".$this->escape($lehrverband->verband)." AND sp.gruppe = ".$this->escape($lehrverband->gruppe)." AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende).")";
// Eintraege fuer den ganzen Verband
$query .= "OR (sp.studiengang_kz = ".$this->escape($lehrverband->studiengang_kz)." AND sp.semester = ".$this->escape($lehrverband->semester)." AND sp.verband = ".$this->escape($lehrverband->verband)." AND (sp.gruppe is null OR sp.gruppe='') AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)." AND ".$this->escape($sem_date_range->ende).")";
// Eintraege fuer das ganze Semester
$query .= "OR (sp.studiengang_kz = ".$this->escape($lehrverband->studiengang_kz)." AND sp.semester = ".$this->escape($lehrverband->semester)." AND (sp.verband is null OR sp.verband='') AND sp.datum BETWEEN ".$this->escape($sem_date_range->start)
." AND ".$this->escape($sem_date_range->ende).")) AND gruppe_kurzbz is null)";
$query .="OR";
}
}
}
// if the studentlehrverbaende is not empty we can remove the last OR that was added to the query
if(!$emptyCheck($studentlehrverbaende))
{
$query = substr($query, 0, -2);
}
// closes the AND sql chain only if it was opened previously
if(!$emptyCheck($gruppen) || !$emptyCheck($studentlehrverbaende))
{
$query .= ")";
}
EOSQL;
return $query;
}
@@ -724,4 +769,4 @@ class Stundenplan_model extends DB_Model
return $this->execQuery($qry, [$lehrveranstaltung_id, $studiensemester_kurzbz]);
}
}
}
@@ -41,6 +41,7 @@ with gbt as (
gb.anmerkung,
gb.dienstverhaeltnis_id,
gb.gehaltstyp_kurzbz,
gb.gehaltsanpassungtyp_kurzbz,
gb.valorisierungssperre,
gb.valorisierung,
gb.grundbetrag as grund_betrag_decrypted,
@@ -76,11 +77,13 @@ select
gbt.anmerkung,
gbt.dienstverhaeltnis_id,
gbt.gehaltstyp_kurzbz,
gbt.gehaltsanpassungtyp_kurzbz,
gbt.valorisierungssperre,
gbt.valorisierung,
gbt.grund_betrag_decrypted,
gbt.betrag_val_decrypted,
gt.bezeichnung as gehaltstyp_bezeichnung,
ga.bezeichnung as gehaltsanpassungtyp_bezeichnung,
vb.vertragsbestandteiltyp_kurzbz,
bf.funktion_kurzbz,
bf.oe_kurzbz,
@@ -93,6 +96,8 @@ from
gbt
LEFT JOIN
hr.tbl_gehaltstyp gt using(gehaltstyp_kurzbz)
LEFT JOIN
hr.tbl_gehaltsanpassungtyp ga using(gehaltsanpassungtyp_kurzbz)
LEFT JOIN
hr.tbl_vertragsbestandteil vb using(vertragsbestandteil_id)
LEFT JOIN
@@ -177,7 +182,8 @@ LEFT JOIN
$stichtagclause .= ')';
}
$this->addSelect('*');
$this->addSelect('tbl_gehaltsbestandteil.*, tbl_gehaltstyp.bezeichnung AS gehaltstyp_bezeichnung');
$this->addJoin('hr.tbl_gehaltstyp', 'gehaltstyp_kurzbz');
$where = <<<EOSQL
dienstverhaeltnis_id = {$this->escape($dienstverhaeltnis_id)}
{$stichtagclause}
@@ -202,6 +208,7 @@ SELECT
g.dienstverhaeltnis_id,
g.vertragsbestandteil_id,
g.gehaltstyp_kurzbz,
g.gehaltsanpassungtyp_kurzbz,
g.von,
g.bis,
g.anmerkung,
@@ -213,9 +220,11 @@ SELECT
g.updateamum,
g.updatevon,
g.valorisierung,
g.auszahlungen
g.auszahlungen,
gt.bezeichnung AS gehaltstyp_bezeichnung
FROM
hr.tbl_gehaltsbestandteil g
hr.tbl_gehaltsbestandteil g
JOIN hr.tbl_gehaltstyp gt USING (gehaltstyp_kurzbz)
LEFT JOIN
hr.tbl_valorisierung_historie vh ON vh.gehaltsbestandteil_id = g.gehaltsbestandteil_id AND vh.valorisierungsdatum = (
SELECT
@@ -263,7 +272,7 @@ EOSQL;
// Note: replaced gb.betrag_valorisiert with vh.betrag_valorisiert!
$qry = "
SELECT
gb.gehaltsbestandteil_id,gb.dienstverhaeltnis_id,gb.vertragsbestandteil_id,gb.gehaltstyp_kurzbz,
gb.gehaltsbestandteil_id,gb.dienstverhaeltnis_id,gb.vertragsbestandteil_id,gb.gehaltstyp_kurzbz,gb.gehaltsanpassungtyp_kurzbz,
gb.von,gb.bis,gb.anmerkung,gb.grundbetrag as grundbetrag,gb.valorisierungssperre,gb.insertamum,
gb.insertvon,gb.updateamum,gb.updatevon,gb.valorisierung,gb.auszahlungen,
vh.valorisierungsdatum, vh.betrag_valorisiert as betrag_valorisiert
+13
View File
@@ -62,3 +62,16 @@
top:13%;"
z-index: 104; font-size: 1rem; width: 2.5rem; height: 2.5rem; opacity:0; transition: opacity 0.2s; top:13%;"
}*/
.tag-hover:hover {
background-color: #e9ecef;
}
.green {
background-color: #e9ecef;
}
.btn-hover {
--bs-btn-hover-bg: #e9ecef; /* Neue Hintergrundfarbe beim Hover */
--bs-btn-hover-border-color: #e9ecef; /* Neue Rahmenfarbe beim Hover */
}
+5
View File
@@ -145,3 +145,8 @@
float: right;
margin-top: 3px;
}
.btn-hover {
--bs-btn-hover-bg: #e9ecef;
--bs-btn-hover-border-color: #e9ecef;
}
+9
View File
@@ -26,6 +26,15 @@ export default {
+ encodeURIComponent(true)
};
},
getCourselistByStudiensemester(student_uid, studiensemester_kurzbz, stundenplan) {
return {
method: 'get',
url: 'api/frontend/v1/stv/LvTermine/getStundenplanForStudiensemester/' + encodeURIComponent(student_uid) + '/'
+ encodeURIComponent(studiensemester_kurzbz) + '/'
+ encodeURIComponent(stundenplan) + '/'
+ encodeURIComponent(true)
};
},
exportCalendar(student_uid, stundenplan)
{
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'content/statistik/termine.xls.php?student_uid=' + encodeURIComponent(student_uid) + '&db_stpl_table='+encodeURIComponent(stundenplan);
+3
View File
@@ -174,6 +174,9 @@ export default {
>
{{ $p.t('lehre/noLvFound') }}
</div>
<div v-else-if="!renderers || !renderers[event.type]" class="placeholder-glow">
<span class="placeholder col-12"></span>
</div>
<div
v-else
:class="'event-type-' + event.type + ' ' + mode + 'PageContainer'"
+3
View File
@@ -79,6 +79,9 @@ export default {
>
{{ $p.t('lehre/noLvFound') }}
</div>
<div v-else-if="!renderers || !renderers[event.type]" class="placeholder-glow">
<span class="placeholder col-12"></span>
</div>
<component
v-else-if="mode == 'eventheader'"
:is="renderers[event.type]?.modalTitle"
+27 -8
View File
@@ -32,7 +32,7 @@ export default {
listGroup: [],
rangeIntervalFirst: null,
otherPersonData: {
fullName: "",
fullNameWithTitle: "",
photo: "",
},
timezone: FHC_JS_DATA_STORAGE_OBJECT.timezone,
@@ -210,10 +210,17 @@ export default {
this.isOtherPersonMitarbeiter =
!!viewData?.user_data?.is_mitarbeiter;
this.isOtherPersonStudent = !!viewData?.user_data?.is_student;
this.otherPersonData.fullName =
viewData?.user_data?.vorname +
" " +
viewData?.user_data?.nachname;
let fullNameWithTitleFragments = [
viewData?.user_data?.titel,
viewData?.user_data?.vorname,
viewData?.user_data?.nachname,
viewData?.user_data?.postnomen,
];
fullNameWithTitleFragments = fullNameWithTitleFragments.filter(
(fragment) => !!fragment,
);
this.otherPersonData.fullNameWithTitle =
fullNameWithTitleFragments.join(" ");
this.otherPersonData.photo = viewData?.user_data?.foto;
},
async redirectToMyLvPlanIfAuthUid() {
@@ -225,12 +232,21 @@ export default {
this.$router.push({ name: "MyLvPlan" });
}
},
copyFullNameWithTitleToClipboard() {
navigator.clipboard.writeText(
this.otherPersonData.fullNameWithTitle,
);
this.$fhcAlert.alertSuccess(
this.$p.t("profil/name_title_clipboard_copy_confirmation"),
);
},
},
async created() {
await this.redirectToMyLvPlanIfAuthUid();
await this.fetchViewData();
await this.$p.loadCategory(["profil"]);
},
template: `
template: /*html*/ `
<div class="d-flex flex-column h-100">
<h2>
<div class="d-flex flex-row justify-content-between align-items-center">
@@ -238,8 +254,11 @@ export default {
{{ $p.t('lehre/stundenplan') + (studiensemester_kurzbz ? " " + studiensemester_kurzbz : "") }}
</span>
<div @click="this.$router.push({name: 'ProfilView', params: {uid: propsViewData.otherUid}})" type="button" class="d-flex flex-row align-items-center gap-3">
<span v-if="otherPersonData.fullName?.length">
{{ otherPersonData.fullName }}
<span v-if="otherPersonData.fullNameWithTitle?.length">
{{ otherPersonData.fullNameWithTitle }}
<span @click.stop="copyFullNameWithTitleToClipboard()" :title="$p.t('LvPlan/copy')" class="fs-4 h-100">
<i class="fa-regular fa-copy"></i>
</span>
</span>
<img v-if="otherPersonData.photo?.length" alt="profile picture" class=" img-thumbnail " style=" max-height:60px; " :src="get_image_base64_src"/>
</div>
@@ -67,6 +67,21 @@ export default {
return res;
},
},
methods: {
copyFullNameWithTitleToClipboard() {
let fragments = [
this.$props.data.Titel,
this.$props.data.Vorname,
this.$props.data.Nachname,
this.$props.data.Postnomen,
];
fragments = fragments.filter((fragment) => !!fragment);
navigator.clipboard.writeText(fragments.join(" "));
this.$fhcAlert.alertSuccess(
this.$p.t("profil/name_title_clipboard_copy_confirmation"),
);
},
},
template: /*html*/ `
<div class="card h-100">
@@ -124,6 +139,16 @@ export default {
<span class="form-underline-content">{{wert?wert:'-'}} </span>
</div>
</div>
<a
@click="copyFullNameWithTitleToClipboard()"
href="#"
class="col-12 fhc-link-color d-flex flex-row gap-2 align-items-center justify-content-center mt-3"
>
<span>
<i class="fa-regular fa-copy ms-2 text-decoration-none"></i>
</span>
<span>{{ $p.t("profil/name_title_copy") }}</span>
</a>
</div>
</div>
</div>
@@ -51,6 +51,16 @@ export default {
return numberPadding(this.event.ende.getHours()) + ":" + numberPadding(this.event.ende.getMinutes());
}
},
watch: {
event: {
async handler() {
if (this.$props.event.type == "lehreinheit") {
await this.getLvMenu();
}
},
deep: true,
},
},
methods: {
mehtodNumberPadding: function (number) {
return numberPadding(number);
@@ -58,21 +68,31 @@ export default {
methodFormatDate: function (d) {
return formatDate(d);
},
async getLvMenu() {
const semesterResponse = await this.$api.call(
ApiLvPlan.getLehreinheitStudiensemester(
Array.isArray(this.event.lehreinheit_id)
? this.event.lehreinheit_id[0]
: this.event.lehreinheit_id,
),
);
if (semesterResponse.meta.status !== "success") return;
const studiensemesterKurzbz = semesterResponse.data;
const lvMenuResponse = await this.$api.call(
ApiAddons.getLvMenu(
this.event.lehrveranstaltung_id,
studiensemesterKurzbz,
),
);
if (lvMenuResponse.meta.status !== "success") return;
this.lvMenu = lvMenuResponse.data;
},
},
created() {
if (this.event.type == 'lehreinheit') {
this.$api
.call(ApiLvPlan.getLehreinheitStudiensemester(Array.isArray(this.event.lehreinheit_id) ? this.event.lehreinheit_id[0] : this.event.lehreinheit_id))
.then(res => res.data)
.then(studiensemester_kurzbz => this.$api.call(
ApiAddons.getLvMenu(
this.event.lehrveranstaltung_id,
studiensemester_kurzbz
)
))
.then(res => {
this.lvMenu = res.data;
});
async created() {
if (this.$props.event.type == 'lehreinheit') {
await this.getLvMenu();
}
},
template: `
+2 -2
View File
@@ -381,13 +381,13 @@ export default {
<template v-for="lehrveranstaltung in lehrveranstaltungen" :key="lehrveranstaltung.lehrveranstaltung_id">
<div class="card" v-if="Array.isArray(lehrveranstaltung.lehrveranstaltungen) && lehrveranstaltung.lehrveranstaltungen.length >0" >
<div class="card-header">
<h5 class=" card-title">{{isGermanLanguage == 'German' ? lehrveranstaltung.bezeichnung : lehrveranstaltung.bezeichnung_english }}</h5>
<h5 class=" card-title">{{isGermanLanguage ? lehrveranstaltung.bezeichnung : lehrveranstaltung.bezeichnung_english }}</h5>
<h6 class=" card-subtitle">{{lehrveranstaltung.lehrform_kurzbz}}</h6>
</div>
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="d-flex list-group-item" v-for="lv in lehrveranstaltung.lehrveranstaltungen">
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lv)">{{isGermanLanguage == 'German' ? lv.bezeichnung : lv.bezeichnung_english}}</a>
<a class="fhc-link-color d-block me-auto" href="#" @click="openLvUebersicht(lv)">{{isGermanLanguage ? lv.bezeichnung : lv.bezeichnung_english}}</a>
<p>{{lv.lehrform_kurzbz}}</p>
</li>
</ul>
+49 -20
View File
@@ -62,6 +62,9 @@ export default {
return [type, result];
}));
},
hiddenWidgets() {
return this.widgets.filter(widget => widget.hidden);
}
},
methods: {
@@ -69,6 +72,9 @@ export default {
this.$refs.widgetpicker
.getWidget()
.then(widget_id => {
if (widget_id.hidden)
this.widgetUnhide(widget_id, widget.place);
widget.widget = widget_id;
// NOTE(chris): min size
widget.place = Object.fromEntries(Object.entries(widget.place).map(([key, value]) => {
@@ -131,17 +137,6 @@ export default {
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]));
});
}
} else if (payload[widget.id]) {
payload[widget.id].id = widget.id;
payload[widget.id].index = widget.index;
@@ -160,6 +155,31 @@ export default {
})
.catch(this.$fhcAlert.handleSystemError);
},
widgetUnhide(orig, place) {
place = Object.fromEntries(Object.entries(place).map(([key, value]) => {
value.w = this.sizeLimits[orig.widget].width.min;
value.h = this.sizeLimits[orig.widget].height.min;
// NOTE(chris): unpin pinned widgets because they got a new placement
value.pinned = false;
return [key, value];
}));
// NOTE(chris): clear placement in other screensizes
Object.keys(orig.place).forEach(key => {
if (!place[key]) {
place[key] = {
x: undefined,
y: undefined,
pinned: false
};
}
});
let payload = {};
payload[orig.id] = {
hidden: false,
place
};
return this.widgetUpdate(payload);
},
async fetchViewData() {
let viewDataResult = await this.$api.call(ApiDashboard.getViewData());
const viewData = viewDataResult.data;
@@ -204,15 +224,23 @@ export default {
},
template: /* html */`
<div class="core-dashboard">
<h3>
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
<button
class="btn ms-2"
aria-label="edit dashboard"
v-tooltip="{ showDelay: 1000, value: $p.t('dashboard/edit') }"
@click="editMode = !editMode"
><i class="fa-solid fa-gear" aria-hidden="true"></i></button>
</h3>
<header class="d-flex justify-content-between align-items-baseline">
<h3>
{{ userFirstName ? $p.t('global/personalGreeting', [ userFirstName ]) : '' }}
</h3>
<div class="form-check form-check-reverse form-switch">
<label class="form-check-label" for="switchEditMode">
{{ $p.t('dashboard/edit') }}
</label>
<input
class="form-check-input"
type="checkbox"
role="switch"
id="switchEditMode"
v-model="editMode"
>
</div>
</header>
<dashboard-section
name="general"
:widgets="widgets"
@@ -223,6 +251,7 @@ export default {
<dashboard-widget-picker
ref="widgetpicker"
:widgets="widgetsSetup"
:hidden-widgets="hiddenWidgets"
></dashboard-widget-picker>
</div>`
}
+2 -26
View File
@@ -36,7 +36,6 @@ export default {
"width",
"height",
"custom",
"hidden",
"editMode",
"loading", // widget got added and is waiting for backend to save in db
"item_data",
@@ -115,14 +114,6 @@ export default {
},
ready() {
return this.component && this.arguments !== null;
},
visible: {
get() {
return !this.hidden;
},
set(value) {
this.$emit('remove', this.hidden);
}
}
},
methods: {
@@ -202,10 +193,8 @@ export default {
},
template: /*html*/ `
<article
v-if="!hidden || editMode"
class="dashboard-item card overflow-hidden h-100 position-relative"
:class="{
'hidden-widget': hidden,
[arguments?.className]: arguments && arguments.className
}"
>
@@ -299,9 +288,9 @@ export default {
>
<i class="fa-solid fa-gear" aria-hidden="true"></i>
</a>
<!-- delete button -->
<!-- delete/hide button -->
<a
v-if="custom && editMode"
v-if="editMode"
href="#"
class="col-auto px-1"
:aria-label="$p.t('dashboard/widget_delete')"
@@ -310,19 +299,6 @@ export default {
>
<i class="fa-solid fa-trash" aria-hidden="true"></i>
</a>
<!-- hide button -->
<Transition>
<div v-if="!custom && editMode" class="col-auto px-1 form-switch">
<input
type="checkbox"
role="switch"
v-model="visible"
class="form-check-input ms-0"
:value="true"
:aria-label="$p.t('dashboard/widget_toggle_visibility')"
>
</div>
</Transition>
</header>
<!-- TODO Manu rename/remove-->
<div v-if="ready" class="card-body overflow-hidden p-0">
+3 -16
View File
@@ -124,8 +124,6 @@ export default {
return { ...item, ...placement, weight };
});
if (this.editModeIsActive)
return placedItems;
return placedItems.filter(item => !item.hidden);
}
},
@@ -141,22 +139,12 @@ export default {
handleConfigClosed() {
this.configOpened = false
},
removeWidget(item, revert) {
removeWidget(item) {
if (item.custom) {
BsConfirm.popup(this.$p.t('dashboard', 'alert_deleteWidget')).then(() => this.$emit('widgetRemove', item.id, this.name));
} else {
let update = {};
update[item.id] = { hidden: !revert };
if (!revert) {
// NOTE(chris): move to last line
update[item.id].place = [];
let y = this.gridHeight;
if (this.additionalRow)
y--;
update[item.id].place[this.gridWidth] = { x: 0, y };
}
update[item.id] = { hidden: true };
this.updatePreset(update);
}
},
@@ -251,13 +239,12 @@ export default {
:loading="item.loading"
:config="item.config"
:custom="item.custom"
:hidden="item.hidden"
:editMode="editModeIsActive"
:place="item.place[gridWidth]"
:widget-template="indexedWidgetsTemplates[item.widget]"
:source="adminMode ? null : item.source || 'custom'"
@change="saveConfig($event, item)"
@remove="removeWidget(item, $event)"
@remove="removeWidget(item)"
@config-opened="handleConfigOpened"
@config-closed="handleConfigClosed"
@pin-item="updatePositions"
+44 -11
View File
@@ -7,11 +7,21 @@ export default {
WidgetIcon,
},
props: [
"widgets"
"widgets",
"hiddenWidgets"
],
data: () => ({
callbacks: {}
}),
computed: {
hasAnyWidgets() {
if (!this.widgets)
return false;
if (!this.widgets.length && !this.hiddenWidgets?.length)
return false;
return true;
}
},
methods: {
getWidget() {
return new Promise((resolve,reject) => {
@@ -37,23 +47,46 @@ export default {
<bs-modal
ref="modal"
class="fade"
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': widgets && widgets.length > 0 }"
:dialog-class="{ 'modal-fullscreen-sm-down': 1, 'modal-xl': hasAnyWidgets }"
@hiddenBsModal="close"
>
<template v-slot:title>{{ $p.t('dashboard/createWidget') }}</template>
<template v-slot:default>
<div v-if="widgets" class="row g-2">
<div v-if="!widgets.length">
{{ $p.t('dashboard/noWidgetsAvailable') }}
</div>
<template v-if="widgets && hiddenWidgets">
<div
v-for="widget in widgets"
:key="widget.widget_id"
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
v-if="!widgets.length && !hiddenWidgets.length"
class="row g-2"
>
<widget-icon @select="pick" :widget="widget"></widget-icon>
<div>{{ $p.t('dashboard/noWidgetsAvailable') }}</div>
</div>
</div>
<template v-else>
<div
v-if="hiddenWidgets.length"
class="row g-2"
:class="widgets.length ? 'border-bottom pb-3 mb-3' : ''"
>
<div
v-for="widget in hiddenWidgets"
:key="widget.widget_id"
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
>
<widget-icon @select="pick(widget)" :widget="widgets.find(w => w.widget_id == widget.widget)"></widget-icon>
</div>
</div>
<div
v-if="widgets.length"
class="row g-2"
>
<div
v-for="widget in widgets"
:key="widget.widget_id"
class="widget-icon-container col-sm-6 col-md-4 col-lg-3 col-xl-2"
>
<widget-icon @select="pick" :widget="widget"></widget-icon>
</div>
</div>
</template>
</template>
<div v-else class="text-center">
<i class="fa-solid fa-spinner fa-pulse fa-3x"></i>
</div>
@@ -400,32 +400,37 @@ export default {
<div v-if="headerData.length == 1">
<div v-if="!isLoading" class="d-flex align-items-center gap-3">
<h2 class="h4">
<h2 class="h4 mt-2">
{{headerData[0].titelpre}}
{{headerData[0].vorname}}
{{headerData[0].nachname}}
<span v-if="headerData[0].titelpost">, </span>
{{headerData[0].titelpost}}
</h2>
<core-tag ref="tagComponent"
v-if="tagsEnabled"
:endpoint="tagEndpoint"
:values="prestudentIds"
@added="addedTag"
@deleted="deletedTag"
@updated="updatedTag"
zuordnung_typ="prestudent_id"
></core-tag>
<div
role="button"
v-if="tagsEnabled"
@click="rebuildPrestudentTags"
class="btn btn-outline btn-light mb-1"
:title="'Automatische Tags fuer ' + currentSemester + ' neu laden'"
>
<i class="fa-solid fa-refresh pe-1"></i>
<span>{{currentSemester}}</span>
<div class="d-flex border rounded align-items-center ps-1">
<core-tag ref="tagComponent"
v-if="tagsEnabled"
:endpoint="tagEndpoint"
:values="prestudentIds"
:show-hover="true"
@added="addedTag"
@deleted="deletedTag"
@updated="updatedTag"
zuordnung_typ="prestudent_id"
></core-tag>
<div
role="button"
v-if="tagsEnabled"
@click="rebuildPrestudentTags"
class="btn btn-outline btn-sm btn-hover m-1"
:title="$p.t('tag','rebuild_tags') + ' ' + currentSemester"
>
<i class="fa-solid fa-refresh pe-1"></i>
<span>{{currentSemester}}</span>
</div>
</div>
<h6 v-if="headerData[0].unruly" class="badge" :class="'bg-unruly rounded-0'"><strong>unruly</strong></h6>
</div>
<div v-else class="d-flex align-items-center gap-3">
+13
View File
@@ -233,6 +233,14 @@ export default {
if (value == oldValue)
return;
/** NOTE(chris): tempPositionUpdates depends on the cols not to be
* updated. So if they do we reset to get a clean value. In case the
* drag and drop operation is still motion on the next tick this
* will be correct again and if there is no such operation we clean
* up.
*/
this.tempPositionUpdates = null;
this.reinitGrid();
},
rows: {
@@ -246,6 +254,11 @@ export default {
},
indexedItems: {
handler(value) {
/** NOTE(chris): there was an update of the items, so in case
* it came from a drag and drop operation we need to clean up
*/
this.tempPositionUpdates = null;
this.reinitGrid();
},
immediate: true,
@@ -553,8 +553,12 @@ export default {
}
if (!this.formData.akadgrad_id && this.arrAkadGrad.length > 0) {
if(this.student.geschlecht == 'w')
this.formData.akadgrad_id = this.arrAkadGrad[1].akadgrad_id;
const akadGradByGeschlecht = this.arrAkadGrad.find((item) => {
return item.geschlecht === this.student.geschlecht;
});
if(akadGradByGeschlecht)
this.formData.akadgrad_id = akadGradByGeschlecht.akadgrad_id;
else
this.formData.akadgrad_id = this.arrAkadGrad[0].akadgrad_id;
}
@@ -1,6 +1,7 @@
import {CoreFilterCmpt} from "../../../../filter/Filter.js";
import FormInput from "../../../../Form/Input.js";
import FormForm from '../../../../Form/Form.js';
import { capitalize } from '../../../../../helpers/StringHelpers.js';
export default {
name: "TblCourseList",
@@ -30,7 +31,7 @@ export default {
tabulatorEvents: [],
listStudiensemester: [],
dataSem: {},
showStundenplanDev: false
stundenplanEnvironment: 'prod'
};
},
computed: {
@@ -46,14 +47,15 @@ export default {
return link;
},
dbStundenplanTable: function (){
return this.showStundenplanDev ? 'stundenplandev' : 'stundenplan';
return this.stundenplanEnvironment === 'dev' ? 'stundenplandev' : 'stundenplan';
},
},
methods: {
initTabulatorOptions(){
this.tabulatorOptions = {
ajaxURL: 'dummy',
ajaxRequestFunc: () => this.$api.call(this.endpoint.getCourselist(this.id, this.dataSem.start, this.dataSem.ende, this.dbStundenplanTable)),
//ajaxRequestFunc: () => this.$api.call(this.endpoint.getCourselist(this.id, this.dataSem.start, this.dataSem.ende, this.dbStundenplanTable)),
ajaxRequestFunc: () => this.$api.call(this.endpoint.getCourselistByStudiensemester(this.id, this.currentSemester, this.dbStundenplanTable)),
ajaxResponse: (url, params, response) => {
return response.data;
},
@@ -112,15 +114,15 @@ export default {
}
};
setHeader('lehrveranstaltung_id', this.$p.t('lehre', 'lehrveranstaltung_id'));
setHeader('lehreinheit_id', this.$p.t('global', 'lehreinheit_id'));
setHeader('datum', this.$p.t('global', 'datum'));
setHeader('beginn', this.$p.t('ui', 'dateFrom'));
setHeader('ende', this.$p.t('ui', 'dateTo'));
setHeader('gruppen_kuerzel', this.$p.t('global', 'gruppen'));
setHeader('ort_kurzbz', this.$p.t('global', 'ortLocation'));
setHeader('lektorname', this.$p.t('lehre', 'lektor'));
setHeader('lehrfach_bez', this.$p.t('global', 'lehrfach'));
setHeader('lehrveranstaltung_id', capitalize(this.$p.t('lehre','lehrveranstaltung_id')));
setHeader('lehreinheit_id', capitalize(this.$p.t('global','lehreinheit_id')));
setHeader('datum', capitalize(this.$p.t('global','datum')));
setHeader('beginn', capitalize(this.$p.t('ui','dateFrom')));
setHeader('ende', capitalize(this.$p.t('ui','dateTo')));
setHeader('gruppen_kuerzel', capitalize(this.$p.t('global','gruppen')));
setHeader('ort_kurzbz', capitalize(this.$p.t('global','ortLocation')));
setHeader('lektorname', capitalize(this.$p.t('lehre','lektor')));
setHeader('lehrfach_bez', capitalize(this.$p.t('global','lehrfach')));
}
}
];
@@ -135,7 +137,6 @@ export default {
this.$refs.table.reloadTable();
},
switchStundenplan(){
this.showStundenplanDev = !this.showStundenplanDev;
this.reload();
}
},
@@ -172,18 +173,45 @@ export default {
reload
:reload-btn-infotext="this.$p.t('table', 'reload')"
>
<template #actions>
<template #actions>
<button
class="btn btn-outline-secondary"
class="btn btn-outline-secondary me-2"
@click="exportToExcel">
{{$p.t('ui', 'export')}}
</button>
<button
class="btn btn-outline-secondary"
@click="switchStundenplan">
<span v-if="!showStundenplanDev">{{$p.t('lehre', 'stundenplan')}}</span>
<span v-else>{{$p.t('lehre', 'stundenplanDev')}}</span>
</button>
<div class="btn-group" role="group">
<input
type="radio"
class="btn-check"
name="btnradio"
id="btnradio_prod"
value="prod"
v-model="stundenplanEnvironment"
@change="switchStundenplan()"
>
<label
class="btn btn-outline-secondary"
for="btnradio_prod"
>
{{$p.t('lehre', 'btn_stundenplanProd')}}
</label>
<input
type="radio"
class="btn-check"
name="btnradio"
id="btnradio_dev"
value="dev"
v-model="stundenplanEnvironment"
@change="switchStundenplan()"
>
<label
class="btn btn-outline-secondary"
for="btnradio_dev"
>
{{$p.t('lehre', 'btn_stundenplanDev')}}
</label>
</div>
</template>
</core-filter-cmpt>
</div>
@@ -317,6 +317,7 @@ export default {
"$p.user_language.value"(n, o) {
if (n !== o && o !== undefined && this.$refs.table.tableBuilt) {
this.translateTabulator();
this.$reloadList();
}
},
"tagFilterState.selectedOptions": {
@@ -842,35 +843,39 @@ export default {
</div>
</template>
<template #actions>
<core-tag ref="tagComponent"
v-if="tagsEnabled"
:endpoint="tagEndpoint"
:values="selectedColumnValues"
@added="addedTag"
@deleted="deletedTag"
@updated="updatedTag"
zuordnung_typ="prestudent_id"
></core-tag>
<template #tags>
<div class="d-flex border rounded align-items-center ps-1">
<core-tag ref="tagComponent"
v-if="tagsEnabled"
:endpoint="tagEndpoint"
:values="selectedColumnValues"
@added="addedTag"
@deleted="deletedTag"
@updated="updatedTag"
zuordnung_typ="prestudent_id"
show-hover
></core-tag>
<button
v-if="!selected.length"
class="btn btn-outline btn-light mb-1"
@click="rebuild(selected)"
:title="$p.t('tag','rebuild_tags') + ' Stg ' + currentSemester"
>
<i class="fa-solid fa-refresh pe-1"></i> STG
</button>
<button
v-else
class="btn btn-outline btn-light mb-1"
@click="rebuild(selected)"
:title="$p.t('tag','rebuild_tags') + ' ' + $p.t('ui','selection') + ' ' + currentSemester"
>
<i class="fa-solid fa-refresh pe-1"></i> {{selected.length}}
</button>
<button
v-if="!selected.length"
class="btn btn-outline btn-sm m-1 btn-hover"
@click="rebuild(selected)"
:title="$p.t('tag','rebuild_tags') + ' Stg ' + currentSemester"
>
<i class="fa-solid fa-refresh pe-1"></i> STG
</button>
<button
v-else
class="btn btn-outline btn-sm m-1 btn-hover"
@click="rebuild(selected)"
:title="$p.t('tag','rebuild_tags') + ' ' + $p.t('ui','selection') + ' ' + currentSemester"
>
<i class="fa-solid fa-refresh pe-1"></i> {{selected.length}}
</button>
</div>
</template>
<template v-if="filter.length || headerFilterActive">
<template v-if="filter.length || headerFilterActive">
<div class="d-flex justify-content-center align-items-center gap-2 ps-4 position-absolute start-50 translate-middle-x">
<p class="text-danger mb-0">
<strong>{{$p.t('filter','filterActive')}}</strong>
@@ -884,8 +889,6 @@ export default {
<span class="fa-solid fa-filter-circle-xmark"></span>
</button>
</div>
</template>
</template>
<template #filter>
@@ -895,6 +898,7 @@ export default {
</div>
</div>
</template>
</core-filter-cmpt>
</div>
<list-new ref="new" :studiengang-kz="studiengangKz" :studiensemester-kurzbz="studiensemesterKurzbz"></list-new>
+6 -1
View File
@@ -28,6 +28,10 @@ export default {
confirmLimit: {
type: Number,
default: 20
},
showHover: {
type: Boolean,
default: false
}
},
data() {
@@ -228,7 +232,8 @@ export default {
<span :title="values.length === 0 ? 'Bitte Zeilen markieren' : ''">
<button @mouseover="showList = true"
:disabled="!values || values.length === 0"
class="btn btn-sm">
class="btn btn-sm"
:class="{'btn-hover': showHover}">
<i class="fa-solid fa-tag fa-xl"></i>
</button>
</span>
+2 -1
View File
@@ -1094,7 +1094,7 @@ export const CoreFilterCmpt = {
<div :id="'filterCollapsables' + idExtra">
<div class="d-flex flex-row justify-content-between flex-wrap">
<div v-if="newBtnShow || reload || $slots.search || $slots.actions" class="d-flex gap-2 align-items-baseline flex-wrap">
<div v-if="newBtnShow || reload || $slots.search || $slots.actions || $slots.tags" class="d-flex gap-2 align-items-baseline flex-wrap">
<button v-if="newBtnShow" class="btn btn-primary" :class="newBtnClass" :title="newBtnLabel ? undefined : 'New'" :aria-label="newBtnLabel ? undefined : 'New'" @click="$emit('click:new', $event)" :disabled="newBtnDisabled">
<span class="fa-solid fa-plus" aria-hidden="true"></span>
{{ newBtnLabel }}
@@ -1107,6 +1107,7 @@ export const CoreFilterCmpt = {
<span v-else id="selected-info-text"> Mit {{ selectedData.length }} ausgewählten:</span>
</span>
<slot name="actions" v-bind="{selected: tabulatorHasSelector ? selectedData : []}"></slot>
<slot name="tags"></slot>
<slot name="search"></slot>
</div>
<div class="d-flex gap-1 align-items-baseline flex-grow-1 justify-content-end">
+1 -1
View File
@@ -40,7 +40,7 @@ export default {
query: Vue.computed(() => this.lastQuery),
};
},
inject: ["isMobile"],
inject: { isMobile: { default: false } },
data: function () {
return {
searchtimer: null,
+1
View File
@@ -100,6 +100,7 @@ require_once('dbupdate_3.4/68530_Dashboard_Cleanup.php');
require_once('dbupdate_3.4/75959_StudVw_Automatische_Tags.php');
require_once('dbupdate_3.4/76160_lvv_favorites.php');
require_once('dbupdate_3.4/77080_tabulator_presets_table.php');
require_once('dbupdate_3.4/78292_gehaltsanpassungtyp.php');
// *** Pruefung und hinzufuegen der neuen Attribute und Tabellen
echo '<H2>Pruefe Tabellen und Attribute!</H2>';
@@ -0,0 +1,47 @@
<?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_gehaltsanpassungtyp' AND table_schema='hr'"))
{
if ($db->db_num_rows($result) == 0)
{
$qry = "
ALTER TABLE
hr.tbl_gehaltsbestandteil
ADD COLUMN IF NOT EXISTS
gehaltsanpassungtyp_kurzbz character varying(32);
COMMENT ON COLUMN hr.tbl_gehaltsbestandteil.gehaltsanpassungtyp_kurzbz IS E'Ersteinstufung, indiv. Erhöhung, etc.';
CREATE TABLE IF NOT EXISTS hr.tbl_gehaltsanpassungtyp
(
gehaltsanpassungtyp_kurzbz character varying(32) NOT NULL,
bezeichnung varchar(256),
sort smallint,
aktiv boolean NOT NULL DEFAULT true,
CONSTRAINT tbl_gehaltsanpassungtyp_pk PRIMARY KEY (gehaltsanpassungtyp_kurzbz)
);
COMMENT ON TABLE hr.tbl_gehaltsanpassungtyp IS E'Key-Table of Salary Adaption Types';
ALTER TABLE hr.tbl_gehaltsbestandteil DROP CONSTRAINT IF EXISTS tbl_gehaltsanpassungtyp_fk;
ALTER TABLE hr.tbl_gehaltsbestandteil ADD CONSTRAINT tbl_gehaltsanpassungtyp_fk FOREIGN KEY (gehaltsanpassungtyp_kurzbz)
REFERENCES hr.tbl_gehaltsanpassungtyp (gehaltsanpassungtyp_kurzbz) MATCH FULL
ON DELETE RESTRICT ON UPDATE CASCADE;
GRANT SELECT, UPDATE, INSERT, DELETE ON hr.tbl_gehaltsanpassungtyp TO vilesci;
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('ersteinstufung','Ersteinstufung', 1, true);
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('individuelle_erhoehung','Indiv. Gehaltserhöhung', 2, true);
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('strukturelle_anpassung','Strukturelle Anpassung', 3, true);
INSERT INTO hr.tbl_gehaltsanpassungtyp(gehaltsanpassungtyp_kurzbz, bezeichnung, sort, aktiv) VALUES('funktionsaenderung','Funktionsänderung', 4, true);
";
if (! $db->db_query($qry))
echo '<strong>Gehaltsanpassungtyp: ' . $db->db_last_error() . '</strong><br>';
else
echo 'Tabelle und Feld für Gehaltsanpassungtyp wurden neu erstellt<br>';
}
}
+100
View File
@@ -29742,6 +29742,46 @@ array(
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'name_title_clipboard_copy_confirmation',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Name mit Titel in die Zwischenablage kopiert.',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Full name with title copied to clipboard.',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
'phrase' => 'name_title_copy',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Name mit Titel kopieren',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Copy full name with title',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'profil',
@@ -60852,6 +60892,26 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
array(
'app' => 'core',
'category' => 'LvPlan',
'phrase' => 'copy',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'Kopieren',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'Copy',
'description' => '',
'insertvon' => 'system'
)
)
),
// ### LvPlanStgOrg END ###
// DMS-Link Phrasen Start
array(
@@ -62351,6 +62411,46 @@ I have been informed that I am under no obligation to consent to the transmissio
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'btn_stundenplanDev',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Plan (Planungs-Daten)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'LV-Plan (developing data)',
'description' => '',
'insertvon' => 'system'
)
)
),
array(
'app' => 'core',
'category' => 'lehre',
'phrase' => 'btn_stundenplanProd',
'insertvon' => 'system',
'phrases' => array(
array(
'sprache' => 'German',
'text' => 'LV-Plan (Produktiv Daten)',
'description' => '',
'insertvon' => 'system'
),
array(
'sprache' => 'English',
'text' => 'LV-Plan (produktive data',
'description' => '',
'insertvon' => 'system'
)
)
),
);